summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans-Christoph Steiner <hans@eds.org>2014-10-16 22:51:43 -0400
committerHans-Christoph Steiner <hans@eds.org>2014-10-16 22:51:43 -0400
commit9f67c0520ea0d5f11a190197cdf746c512db4ce4 (patch)
treec88a33f01f20a3d13a09594f114fffacebd0d1a4
parentee20336e9c78d2e3782c8d096b9ab4f6ca8ce95f (diff)
parent569c6676a6ddb0ff73821d7693b5e18ddef809b9 (diff)
Merge tag 'upstream/3.2.0'
Upstream version 3.2.0 # gpg: Signature made Thu 16 Oct 2014 10:51:39 PM EDT using RSA key ID 374BBE81 # gpg: Good signature from "Hans-Christoph Steiner <hans@guardianproject.info>" # gpg: aka "Hans-Christoph Steiner <hans@eds.org>" # gpg: aka "Hans-Christoph Steiner <hans@at.or.at>" # gpg: aka "[jpeg image of size 5408]"
-rw-r--r--.gitignore7
-rw-r--r--Makefile.in40
-rw-r--r--Makefile.msc313
-rw-r--r--Makefile.vxworks4
-rw-r--r--README.md (renamed from README)49
-rw-r--r--VERSION2
-rw-r--r--addopcodes.awk2
-rw-r--r--autoconf/INSTALL370
-rw-r--r--autoconf/Makefile.am19
-rw-r--r--autoconf/README32
-rw-r--r--autoconf/README.first57
-rwxr-xr-xautoconf/config.guess1530
-rwxr-xr-xautoconf/config.sub1773
-rw-r--r--autoconf/configure.ac108
-rwxr-xr-xautoconf/depcomp708
-rwxr-xr-xautoconf/install-sh527
-rw-r--r--autoconf/ltmain.sh9655
-rwxr-xr-xautoconf/missing331
-rw-r--r--autoconf/tea/Makefile.in439
-rw-r--r--autoconf/tea/README36
-rw-r--r--autoconf/tea/aclocal.m49
-rw-r--r--autoconf/tea/configure.in200
-rw-r--r--autoconf/tea/doc/sqlite3.n15
-rw-r--r--autoconf/tea/license.terms6
-rw-r--r--autoconf/tea/pkgIndex.tcl.in7
-rw-r--r--autoconf/tea/tclconfig/install-sh528
-rw-r--r--autoconf/tea/tclconfig/tcl.m44165
-rw-r--r--autoconf/tea/win/makefile.vc414
-rw-r--r--autoconf/tea/win/nmakehlp.c694
-rw-r--r--autoconf/tea/win/rules.vc711
-rwxr-xr-xconfigure314
-rw-r--r--configure.ac18
-rw-r--r--ext/fts1/fts1.c7
-rw-r--r--ext/fts1/fulltext.c29
-rw-r--r--ext/fts2/fts2.c5
-rw-r--r--ext/fts2/fts2_hash.c2
-rw-r--r--ext/fts2/fts2_porter.c3
-rw-r--r--ext/fts2/fts2_tokenizer.c2
-rw-r--r--ext/fts2/fts2_tokenizer1.c3
-rw-r--r--ext/fts3/fts3.c759
-rw-r--r--ext/fts3/fts3Int.h46
-rw-r--r--ext/fts3/fts3_aux.c129
-rw-r--r--ext/fts3/fts3_expr.c221
-rw-r--r--ext/fts3/fts3_hash.c4
-rw-r--r--ext/fts3/fts3_porter.c74
-rw-r--r--ext/fts3/fts3_snippet.c3
-rw-r--r--ext/fts3/fts3_test.c49
-rw-r--r--ext/fts3/fts3_unicode.c16
-rw-r--r--ext/fts3/fts3_unicode2.c53
-rw-r--r--ext/fts3/fts3_write.c506
-rw-r--r--ext/fts3/tool/fts3view.c15
-rw-r--r--ext/fts3/unicode/mkunicode.tcl15
-rw-r--r--ext/icu/icu.c7
-rw-r--r--ext/misc/amatch.c8
-rw-r--r--ext/misc/closure.c16
-rw-r--r--ext/misc/compress.c107
-rw-r--r--ext/misc/fileio.c100
-rw-r--r--ext/misc/fuzzer.c13
-rw-r--r--ext/misc/ieee754.c4
-rw-r--r--ext/misc/nextchar.c88
-rw-r--r--ext/misc/percentile.c219
-rw-r--r--ext/misc/regexp.c6
-rw-r--r--ext/misc/spellfix.c48
-rw-r--r--ext/misc/totype.c512
-rw-r--r--ext/misc/vfslog.c759
-rw-r--r--ext/misc/vtshim.c551
-rw-r--r--ext/rtree/rtree.c1575
-rw-r--r--ext/rtree/rtree1.test41
-rw-r--r--ext/rtree/rtree6.test40
-rw-r--r--ext/rtree/rtree8.test1
-rw-r--r--ext/rtree/rtreeB.test2
-rw-r--r--ext/rtree/rtreeC.test273
-rw-r--r--ext/rtree/rtreeD.test57
-rw-r--r--ext/rtree/rtreeE.test129
-rw-r--r--ext/rtree/sqlite3rtree.h67
-rw-r--r--magic.txt6
-rw-r--r--main.mk83
-rw-r--r--manifest987
-rw-r--r--manifest.uuid2
-rw-r--r--mkdll.sh49
-rw-r--r--mkextu.sh13
-rw-r--r--mkextw.sh22
-rw-r--r--mkopcodec.awk20
-rw-r--r--mkopcodeh.awk126
-rw-r--r--mptest/config01.test46
-rw-r--r--mptest/config02.test123
-rw-r--r--mptest/crash01.test102
-rw-r--r--mptest/crash02.subtest53
-rw-r--r--mptest/mptest.c1401
-rw-r--r--mptest/multiwrite01.test405
-rw-r--r--sqlcipher-1.1.8-testkey.dbbin2048 -> 1013760 bytes
-rw-r--r--sqlcipher-2.0-be-testkey.dbbin2048 -> 1024000 bytes
-rwxr-xr-xsqlcipher-2.0-beta-testkey.dbbin2048 -> 1035264 bytes
-rw-r--r--sqlcipher-2.0-le-testkey.dbbin2048 -> 1024000 bytes
-rw-r--r--sqlcipher-3.0-testkey.dbbin0 -> 1024000 bytes
-rw-r--r--sqlcipher.194
-rw-r--r--sqlcipher.pc.in6
-rw-r--r--sqlcipher.xcodeproj/project.pbxproj53
-rw-r--r--src/alter.c16
-rw-r--r--src/analyze.c1788
-rw-r--r--src/attach.c61
-rw-r--r--src/backup.c17
-rw-r--r--src/btree.c759
-rw-r--r--src/btree.h16
-rw-r--r--src/btreeInt.h57
-rw-r--r--src/build.c968
-rw-r--r--src/callback.c11
-rw-r--r--src/crypto.c109
-rw-r--r--src/crypto.h26
-rw-r--r--src/crypto_cc.c11
-rw-r--r--src/crypto_impl.c456
-rw-r--r--src/crypto_libtomcrypt.c121
-rw-r--r--src/crypto_openssl.c4
-rw-r--r--src/ctime.c13
-rw-r--r--src/date.c17
-rw-r--r--src/delete.c435
-rw-r--r--src/expr.c1296
-rw-r--r--src/fkey.c334
-rw-r--r--src/func.c116
-rw-r--r--src/global.c15
-rw-r--r--src/hash.c2
-rw-r--r--src/insert.c1145
-rw-r--r--src/legacy.c3
-rw-r--r--src/loadext.c29
-rw-r--r--src/main.c210
-rw-r--r--src/malloc.c8
-rw-r--r--src/mem1.c62
-rw-r--r--src/mem2.c16
-rw-r--r--src/mem5.c49
-rw-r--r--src/memjournal.c6
-rw-r--r--src/mutex.c2
-rw-r--r--src/mutex_noop.c2
-rw-r--r--src/mutex_unix.c8
-rw-r--r--src/mutex_w32.c167
-rw-r--r--src/os.c16
-rw-r--r--src/os.h79
-rw-r--r--src/os_setup.h57
-rw-r--r--src/os_unix.c287
-rw-r--r--src/os_win.c1391
-rw-r--r--src/os_win.h67
-rw-r--r--src/pager.c378
-rw-r--r--src/pager.h21
-rw-r--r--src/parse.y295
-rw-r--r--src/pcache.c34
-rw-r--r--src/pcache1.c59
-rw-r--r--src/pragma.c1347
-rw-r--r--src/prepare.c22
-rw-r--r--src/printf.c310
-rw-r--r--src/random.c65
-rw-r--r--src/resolve.c250
-rw-r--r--src/rowset.c6
-rw-r--r--src/select.c1569
-rw-r--r--src/shell.c1580
-rw-r--r--src/sqlcipher.h2
-rw-r--r--src/sqlite.h.in502
-rw-r--r--src/sqlite3.rc30
-rw-r--r--src/sqlite3ext.h3
-rw-r--r--src/sqliteInt.h1018
-rw-r--r--src/status.c10
-rw-r--r--src/tclsqlite.c153
-rw-r--r--src/test1.c340
-rw-r--r--src/test2.c88
-rw-r--r--src/test5.c1
-rw-r--r--src/test6.c21
-rw-r--r--src/test7.c18
-rw-r--r--src/test8.c3
-rw-r--r--src/test_autoext.c54
-rw-r--r--src/test_btree.c2
-rw-r--r--src/test_config.c30
-rw-r--r--src/test_demovfs.c2
-rw-r--r--src/test_fs.c4
-rw-r--r--src/test_func.c144
-rw-r--r--src/test_init.c1
-rw-r--r--src/test_intarray.h3
-rw-r--r--src/test_loadext.c6
-rw-r--r--src/test_malloc.c33
-rw-r--r--src/test_multiplex.c33
-rw-r--r--src/test_multiplex.h4
-rw-r--r--src/test_osinst.c7
-rw-r--r--src/test_quota.c40
-rw-r--r--src/test_quota.h6
-rw-r--r--src/test_rtree.c201
-rw-r--r--src/test_schema.c5
-rw-r--r--src/test_server.c26
-rw-r--r--src/test_stat.c5
-rw-r--r--src/test_syscall.c49
-rw-r--r--src/test_vfs.c110
-rw-r--r--src/test_vfstrace.c7
-rw-r--r--src/tokenize.c27
-rw-r--r--src/trigger.c35
-rw-r--r--src/update.c311
-rw-r--r--src/utf.c33
-rw-r--r--src/util.c213
-rw-r--r--src/vacuum.c38
-rw-r--r--src/vdbe.c2767
-rw-r--r--src/vdbe.h92
-rw-r--r--src/vdbeInt.h131
-rw-r--r--src/vdbeapi.c185
-rw-r--r--src/vdbeaux.c1407
-rw-r--r--src/vdbeblob.c81
-rw-r--r--src/vdbemem.c699
-rw-r--r--src/vdbesort.c111
-rw-r--r--src/vdbetrace.c27
-rw-r--r--src/vtab.c12
-rw-r--r--src/wal.c12
-rw-r--r--src/walker.c25
-rw-r--r--src/where.c6317
-rw-r--r--src/whereInt.h461
-rw-r--r--test/all.test3
-rw-r--r--test/alter.test55
-rw-r--r--test/alter4.test26
-rw-r--r--test/amatch1.test117
-rw-r--r--test/analyze.test34
-rw-r--r--test/analyze3.test92
-rw-r--r--test/analyze4.test2
-rw-r--r--test/analyze5.test82
-rw-r--r--test/analyze6.test38
-rw-r--r--test/analyze7.test41
-rw-r--r--test/analyze8.test36
-rw-r--r--test/analyze9.test1091
-rw-r--r--test/analyzeA.test167
-rw-r--r--test/analyzeB.test683
-rw-r--r--test/analyzeC.test167
-rw-r--r--test/async5.test1
-rw-r--r--test/atof1.test2
-rw-r--r--test/attach2.test3
-rw-r--r--test/attach3.test1
-rw-r--r--test/auth.test48
-rw-r--r--test/auth2.test2
-rw-r--r--test/autoinc.test2
-rw-r--r--test/autoindex1.test57
-rw-r--r--test/autoindex2.test271
-rw-r--r--test/autoindex3.test58
-rw-r--r--test/autovacuum.test2
-rw-r--r--test/backcompat.test90
-rw-r--r--test/backup4.test1
-rw-r--r--test/between.test24
-rw-r--r--test/bigfile2.test2
-rw-r--r--test/boundary3.tcl17
-rw-r--r--test/btreefault.test1
-rw-r--r--test/capi2.test6
-rw-r--r--test/capi3.test60
-rw-r--r--test/capi3c.test36
-rw-r--r--test/capi3d.test44
-rw-r--r--test/capi3e.test8
-rw-r--r--test/check.test41
-rw-r--r--test/close.test1
-rw-r--r--test/closure01.test100
-rw-r--r--test/collate1.test30
-rw-r--r--test/collate2.test34
-rw-r--r--test/collate4.test18
-rw-r--r--test/conflict.test40
-rw-r--r--test/conflict2.test855
-rw-r--r--test/conflict3.test356
-rw-r--r--test/contrib01.test90
-rw-r--r--test/corrupt.test4
-rw-r--r--test/corrupt2.test4
-rw-r--r--test/corrupt3.test4
-rw-r--r--test/corrupt4.test4
-rw-r--r--test/corrupt5.test4
-rw-r--r--test/corrupt6.test4
-rw-r--r--test/corrupt7.test4
-rw-r--r--test/corrupt8.test4
-rw-r--r--test/corrupt9.test4
-rw-r--r--test/corruptA.test4
-rw-r--r--test/corruptB.test4
-rw-r--r--test/corruptC.test10
-rw-r--r--test/corruptD.test4
-rw-r--r--test/corruptE.test4
-rw-r--r--test/corruptF.test5
-rw-r--r--test/corruptG.test76
-rw-r--r--test/corruptH.test178
-rw-r--r--test/corruptI.test105
-rw-r--r--test/cost.test292
-rw-r--r--test/count.test10
-rw-r--r--test/crash8.test116
-rw-r--r--test/crypto.test427
-rw-r--r--test/date.test23
-rw-r--r--test/dbstatus.test16
-rw-r--r--test/default.test35
-rw-r--r--test/descidx1.test4
-rw-r--r--test/distinct.test29
-rw-r--r--test/e_createtable.test209
-rw-r--r--test/e_delete.test7
-rw-r--r--test/e_droptrigger.test2
-rw-r--r--test/e_dropview.test2
-rw-r--r--test/e_expr.test98
-rw-r--r--test/e_fkey.test206
-rw-r--r--test/e_insert.test26
-rw-r--r--test/e_reindex.test12
-rw-r--r--test/e_select.test130
-rw-r--r--test/e_select2.test34
-rw-r--r--test/e_update.test18
-rw-r--r--test/e_uri.test2
-rw-r--r--test/e_vacuum.test2
-rw-r--r--test/eqp.test415
-rw-r--r--test/errmsg.test6
-rw-r--r--test/exclusive.test1
-rw-r--r--test/exclusive2.test1
-rw-r--r--test/extension01.test83
-rw-r--r--test/fallocate.test1
-rw-r--r--test/filefmt.test1
-rw-r--r--test/fkey1.test3
-rw-r--r--test/fkey2.test150
-rw-r--r--test/fkey3.test22
-rw-r--r--test/fkey5.test53
-rw-r--r--test/fkey6.test175
-rw-r--r--test/fkey7.test54
-rw-r--r--test/fkey_malloc.test5
-rw-r--r--test/fts3aa.test2
-rw-r--r--test/fts3ab.test1
-rw-r--r--test/fts3ag.test1
-rw-r--r--test/fts3ao.test29
-rw-r--r--test/fts3atoken.test2
-rw-r--r--test/fts3auto.test1
-rw-r--r--test/fts3aux1.test35
-rw-r--r--test/fts3aux2.test144
-rw-r--r--test/fts3corrupt.test1
-rw-r--r--test/fts3d.test31
-rw-r--r--test/fts3defer2.test2
-rw-r--r--test/fts3defer3.test84
-rw-r--r--test/fts3expr.test18
-rw-r--r--test/fts3expr3.test4
-rw-r--r--test/fts3expr4.test57
-rw-r--r--test/fts3join.test66
-rw-r--r--test/fts3malloc.test5
-rw-r--r--test/fts3matchinfo.test8
-rw-r--r--test/fts3near.test1
-rw-r--r--test/fts3prefix2.test1
-rw-r--r--test/fts3query.test17
-rw-r--r--test/fts3shared.test106
-rw-r--r--test/fts3snippet.test71
-rw-r--r--test/fts3sort.test1
-rw-r--r--test/fts3tok1.test3
-rw-r--r--test/fts3tok_err.test2
-rw-r--r--test/fts3varint.test118
-rw-r--r--test/fts4aa.test1552
-rw-r--r--test/fts4check.test28
-rw-r--r--test/fts4content.test1
-rw-r--r--test/fts4docid.test116
-rw-r--r--test/fts4growth.test437
-rw-r--r--test/fts4growth2.test93
-rw-r--r--test/fts4incr.test75
-rw-r--r--test/fts4merge4.test102
-rw-r--r--test/fts4noti.test233
-rw-r--r--test/fts4unicode.test200
-rw-r--r--test/func.test82
-rw-r--r--test/func3.test117
-rw-r--r--test/func4.test758
-rw-r--r--test/func5.test63
-rw-r--r--test/fuzz.test2
-rw-r--r--test/fuzz3.test3
-rw-r--r--test/fuzzer1.test17
-rw-r--r--test/genesis.tcl1560
-rw-r--r--test/hexlit.test114
-rw-r--r--test/hook.test52
-rw-r--r--test/in.test2
-rw-r--r--test/in4.test177
-rw-r--r--test/incrblob2.test6
-rw-r--r--test/incrblob3.test1
-rw-r--r--test/incrblob4.test1
-rw-r--r--test/incrblob_err.test1
-rw-r--r--test/incrblobfault.test1
-rw-r--r--test/incrvacuum3.test1
-rw-r--r--test/index.test21
-rw-r--r--test/index3.test2
-rw-r--r--test/index4.test2
-rw-r--r--test/index6.test271
-rw-r--r--test/index7.test251
-rw-r--r--test/indexedby.test121
-rw-r--r--test/insert.test35
-rw-r--r--test/insert4.test20
-rw-r--r--test/instr.test45
-rw-r--r--test/intpkey.test9
-rw-r--r--test/io.test1
-rw-r--r--test/ioerr.test2
-rw-r--r--test/ioerr6.test1
-rw-r--r--test/join.test46
-rw-r--r--test/keyword1.test3
-rw-r--r--test/lastinsert.test11
-rw-r--r--test/like.test112
-rw-r--r--test/limit.test20
-rw-r--r--test/loadext.test21
-rw-r--r--test/loadext2.test28
-rw-r--r--test/lock7.test1
-rw-r--r--test/malloc.test1
-rw-r--r--test/malloc5.test2
-rw-r--r--test/mallocA.test50
-rw-r--r--test/mallocK.test66
-rw-r--r--test/mallocL.test43
-rw-r--r--test/malloc_common.tcl29
-rw-r--r--test/memdb.test2
-rw-r--r--test/memsubsys1.test2
-rw-r--r--test/misc1.test31
-rw-r--r--test/misc3.test4
-rw-r--r--test/misc7.test16
-rw-r--r--test/misuse.test61
-rw-r--r--test/mmap3.test98
-rw-r--r--test/mmapfault.test76
-rw-r--r--test/multiplex.test14
-rw-r--r--test/nolock.test185
-rw-r--r--test/notify3.test1
-rw-r--r--test/notnull.test52
-rw-r--r--test/orderby1.test155
-rw-r--r--test/orderby5.test130
-rw-r--r--test/orderby6.test183
-rw-r--r--test/orderby7.test106
-rw-r--r--test/pager1.test5
-rw-r--r--test/pager4.test99
-rw-r--r--test/pagerfault.test2
-rw-r--r--test/pagerfault2.test1
-rw-r--r--test/pagerfault3.test1
-rw-r--r--test/pcache.test1
-rw-r--r--test/percentile.test209
-rw-r--r--test/permutations.test126
-rw-r--r--test/pragma.test75
-rw-r--r--test/pragma2.test89
-rw-r--r--test/printf2.test99
-rw-r--r--test/progress.test4
-rw-r--r--test/queryonly.test72
-rw-r--r--test/quota.test2
-rw-r--r--test/quota2.test2
-rw-r--r--test/rdonly.test6
-rw-r--r--test/releasetest.tcl7
-rw-r--r--test/resolver01.test173
-rw-r--r--test/rollback.test2
-rw-r--r--test/rowid.test4
-rw-r--r--test/run-wordcount.sh78
-rw-r--r--test/savepoint.test2
-rw-r--r--test/schema5.test8
-rw-r--r--test/securedel.test1
-rw-r--r--test/securedel2.test1
-rw-r--r--test/select1.test2
-rw-r--r--test/select4.test36
-rw-r--r--test/select7.test30
-rw-r--r--test/select9.test18
-rw-r--r--test/selectA.test82
-rw-r--r--test/selectF.test49
-rw-r--r--test/shared3.test35
-rw-r--r--test/shared7.test3
-rw-r--r--test/shared8.test1
-rw-r--r--test/sharedlock.test31
-rw-r--r--test/shell1.test126
-rw-r--r--test/shell2.test20
-rw-r--r--test/shell3.test4
-rw-r--r--test/shell4.test4
-rw-r--r--test/shell5.test197
-rw-r--r--test/show_speedtest1_rtree.tcl57
-rw-r--r--test/skipscan1.test250
-rw-r--r--test/skipscan2.test205
-rw-r--r--test/skipscan5.test186
-rw-r--r--test/softheap1.test23
-rw-r--r--test/speedtest1.c1391
-rw-r--r--test/spellfix.test66
-rw-r--r--test/stat.test47
-rw-r--r--test/subquery.test7
-rw-r--r--test/syscall.test1
-rw-r--r--test/table.test50
-rw-r--r--test/tableopts.test76
-rw-r--r--test/temptrigger.test75
-rw-r--r--test/tester.tcl304
-rw-r--r--test/tkt-2a5629202f.test7
-rw-r--r--test/tkt-385a5b56b9.test8
-rw-r--r--test/tkt-3a77c9714e.test1
-rw-r--r--test/tkt-3fe897352e.test6
-rw-r--r--test/tkt-4a03edc4c8.test2
-rw-r--r--test/tkt-4c86b126f2.test49
-rw-r--r--test/tkt-4ef7e3cfca.test68
-rw-r--r--test/tkt-78e04e52ea.test20
-rw-r--r--test/tkt-7a31705a7e6.test1
-rw-r--r--test/tkt-7bbfb7d442.test2
-rw-r--r--test/tkt-80e031a00f.test4
-rw-r--r--test/tkt-868145d012.test64
-rw-r--r--test/tkt-8c63ff0ec.test48
-rw-r--r--test/tkt-94c04eaadb.test1
-rw-r--r--test/tkt-9a8b09f8e6.test323
-rw-r--r--test/tkt-9f2eb3abac.test79
-rw-r--r--test/tkt-a8a0d2996a.test93
-rw-r--r--test/tkt-b1d3a2e531.test2
-rw-r--r--test/tkt-b75a9ca6b0.test77
-rw-r--r--test/tkt-c48d99d690.test1
-rw-r--r--test/tkt-cbd054fa6b.test45
-rw-r--r--test/tkt-d11f09d36e.test1
-rw-r--r--test/tkt-f3e5abed55.test1
-rw-r--r--test/tkt-f67b41381a.test53
-rw-r--r--test/tkt-f973c7ac31.test1
-rw-r--r--test/tkt1567.test33
-rw-r--r--test/tkt2822.test5
-rw-r--r--test/tkt3442.test6
-rw-r--r--test/tkt35xx.test2
-rw-r--r--test/tkt3918.test1
-rw-r--r--test/tkt3929.test1
-rw-r--r--test/tpch01.test192
-rw-r--r--test/trace2.test2
-rw-r--r--test/trans2.test4
-rw-r--r--test/transitive1.test234
-rw-r--r--test/trigger2.test12
-rw-r--r--test/triggerC.test2
-rw-r--r--test/triggerE.test112
-rw-r--r--test/unique.test16
-rw-r--r--test/unique2.test78
-rw-r--r--test/unixexcl.test4
-rw-r--r--test/unordered.test30
-rw-r--r--test/update.test6
-rw-r--r--test/uri.test2
-rw-r--r--test/veryquick.test1
-rw-r--r--test/view.test13
-rw-r--r--test/vtab1.test50
-rw-r--r--test/vtab6.test4
-rw-r--r--test/vtab_shared.test48
-rw-r--r--test/wal.test16
-rw-r--r--test/wal2.test9
-rw-r--r--test/wal6.test166
-rw-r--r--test/wal64k.test51
-rw-r--r--test/wal8.test1
-rw-r--r--test/walcksum.test1
-rw-r--r--test/walcrash.test1
-rw-r--r--test/walcrash2.test1
-rw-r--r--test/walcrash3.test1
-rw-r--r--test/walfault.test1
-rw-r--r--test/walro.test16
-rw-r--r--test/walshared.test1
-rw-r--r--test/where.test90
-rw-r--r--test/where2.test150
-rw-r--r--test/where3.test132
-rw-r--r--test/where4.test4
-rw-r--r--test/where7.test10
-rw-r--r--test/where8.test25
-rw-r--r--test/where9.test105
-rw-r--r--test/whereA.test6
-rw-r--r--test/whereC.test1
-rw-r--r--test/whereD.test86
-rw-r--r--test/whereE.test8
-rw-r--r--test/whereF.test15
-rw-r--r--test/whereG.test233
-rw-r--r--test/whereH.test139
-rw-r--r--test/whereI.test92
-rw-r--r--test/whereJ.test375
-rw-r--r--test/wild001.test311
-rw-r--r--test/win32heap.test74
-rw-r--r--test/win32lock.test50
-rw-r--r--test/win32longpath.test110
-rw-r--r--test/with1.test831
-rw-r--r--test/with2.test418
-rw-r--r--test/withM.test77
-rw-r--r--test/without_rowid1.test281
-rw-r--r--test/without_rowid2.test125
-rw-r--r--test/without_rowid3.test2084
-rw-r--r--test/without_rowid4.test764
-rw-r--r--test/without_rowid5.test201
-rw-r--r--test/wordcount.c545
-rw-r--r--test/zerodamage.test2
-rwxr-xr-xtool/build-all-msvc.bat80
-rw-r--r--tool/fast_vacuum.c234
-rw-r--r--tool/lemon.c297
-rw-r--r--tool/logest.c170
-rw-r--r--tool/mkautoconfamal.sh83
-rw-r--r--tool/mkkeywordhash.c10
-rw-r--r--tool/mkpragmatab.tcl434
-rw-r--r--tool/mksqlite3c-noext.tcl2
-rw-r--r--tool/mksqlite3c.tcl17
-rw-r--r--tool/mksqlite3internalh.tcl2
-rw-r--r--tool/mkvsix.tcl234
-rw-r--r--tool/omittest.tcl1
-rw-r--r--tool/pagesig.c92
-rw-r--r--tool/showdb.c279
-rw-r--r--tool/showjournal.c19
-rw-r--r--tool/showstat4.c157
-rw-r--r--tool/showwal.c48
-rw-r--r--tool/spaceanal.tcl125
-rw-r--r--tool/vdbe-compress.tcl12
-rw-r--r--tool/vdbe_profile.tcl82
-rw-r--r--tool/warnings.sh6
-rw-r--r--tool/win/sqlite.vsixbin32816 -> 32825 bytes
574 files changed, 81805 insertions, 16786 deletions
diff --git a/.gitignore b/.gitignore
index 0af1a93..f68644e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ xcuserdata/*
/config.log
/config.status
/keywordhash.h
-/mkkeywordhash
+/mkkeywordhash*
/opcodes.c
/opcodes.h
/parse.c
@@ -21,8 +21,8 @@ xcuserdata/*
/parse.h.temp
/parse.out
/tsrc
-/testfixture
-/lemon
+/testfixture*
+/lemon*
/libtool
/libsqlite3.la
/libtclsqlite3.la
@@ -37,3 +37,4 @@ xcuserdata/*
/sqlcipher.pc
/sqlite3ext.h
/libsqlcipher.la
+/.DS_Store
diff --git a/Makefile.in b/Makefile.in
index 76fb83e..ca493be 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -259,8 +259,10 @@ SRC = \
$(TOP)/src/os.c \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
$(TOP)/src/os_unix.c \
$(TOP)/src/os_win.c \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.c \
$(TOP)/src/pager.h \
$(TOP)/src/parse.y \
@@ -301,7 +303,8 @@ SRC = \
$(TOP)/src/wal.c \
$(TOP)/src/wal.h \
$(TOP)/src/walker.c \
- $(TOP)/src/where.c
+ $(TOP)/src/where.c \
+ $(TOP)/src/whereInt.h
# Source code for extensions
#
@@ -410,11 +413,14 @@ TESTSRC = \
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/closure.c \
+ $(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/fuzzer.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/nextchar.c \
+ $(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
+ $(TOP)/ext/misc/totype.c \
$(TOP)/ext/misc/wholenumber.c
# Source code to the library files needed by the test fixture
@@ -474,6 +480,8 @@ HDR = \
opcodes.h \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.h \
$(TOP)/src/pcache.h \
parse.h \
@@ -483,6 +491,7 @@ HDR = \
$(TOP)/src/sqliteLimit.h \
$(TOP)/src/vdbe.h \
$(TOP)/src/vdbeInt.h \
+ $(TOP)/src/whereInt.h \
config.h
# Header files used by extensions
@@ -550,7 +559,7 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
mkdir tsrc
cp -f $(SRC) tsrc
rm tsrc/sqlite.h.in tsrc/parse.y
- $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new
+ $(TCLSH_CMD) $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new
mv vdbe.new tsrc/vdbe.c
touch .target_source
@@ -963,6 +972,30 @@ sqlite3_analyzer.c: sqlite3.c $(TOP)/src/test_stat.c $(TOP)/src/tclsqlite.c $(TO
sqlite3_analyzer$(TEXE): sqlite3_analyzer.c
$(LTLINK) sqlite3_analyzer.c -o $@ $(LIBTCL) $(TLIBS)
+showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS)
+
+showstat4$(TEXE): $(TOP)/tool/showstat4.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/tool/showstat4.c sqlite3.lo $(TLIBS)
+
+showjournal$(TEXE): $(TOP)/tool/showjournal.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/tool/showjournal.c sqlite3.lo $(TLIBS)
+
+showwal$(TEXE): $(TOP)/tool/showwal.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/tool/showwal.c sqlite3.lo $(TLIBS)
+
+rollback-test$(TEXE): $(TOP)/tool/rollback-test.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/tool/rollback-test.c sqlite3.lo $(TLIBS)
+
+LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
+ $(LTLINK) -I. -o $@ $(TOP)/tool/logest.c
+
+wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.c
+ $(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.c $(TLIBS)
+
+speedtest1$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
+ $(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
+
# Standard install and cleanup targets
#
lib_install: libsqlcipher.la
@@ -997,6 +1030,9 @@ clean:
rm -rf tsrc .target_source
rm -f tclsqlcipher$(TEXE)
rm -f testfixture$(TEXE) test.db
+ rm -f LogEst$(TEXE) fts3view$(TEXE) rollback-test$(TEXE) showdb$(TEXE)
+ rm -f showjournal$(TEXE) showstat4$(TEXE) showwal$(TEXE) speedtest1$(TEXE)
+ rm -f wordcount$(TEXE)
rm -f sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
rm -f sqlite3.c
rm -f sqlite3rc.h
diff --git a/Makefile.msc b/Makefile.msc
index 57d7065..3a9fa29 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -1,6 +1,9 @@
#
# nmake Makefile for SQLite
#
+###############################################################################
+############################## START OF OPTIONS ###############################
+###############################################################################
# The toplevel directory of the source tree. This is the directory
# that contains this "Makefile.msc".
@@ -13,6 +16,20 @@ TOP = .
USE_AMALGAMATION = 1
!ENDIF
+# Set this non-0 to use the library paths and other options necessary for
+# Windows Phone 8.1.
+#
+!IFNDEF USE_WP81_OPTS
+USE_WP81_OPTS = 0
+!ENDIF
+
+# Set this non-0 to split the SQLite amalgamation file into chunks to
+# be used for debugging with Visual Studio.
+#
+!IFNDEF SPLIT_AMALGAMATION
+SPLIT_AMALGAMATION = 0
+!ENDIF
+
# Set this non-0 to use the International Components for Unicode (ICU).
#
!IFNDEF USE_ICU
@@ -25,6 +42,13 @@ USE_ICU = 0
USE_CRT_DLL = 0
!ENDIF
+# Set this non-0 to generate assembly code listings for the source code
+# files.
+#
+!IFNDEF USE_LISTINGS
+USE_LISTINGS = 0
+!ENDIF
+
# Set this non-0 to attempt setting the native compiler automatically
# for cross-compiling the command line tools needed during the compilation
# process.
@@ -94,6 +118,33 @@ WIN32HEAP = 0
DEBUG = 0
!ENDIF
+# Enable use of available compiler optimizations? Normally, this should be
+# non-zero. Setting this to zero, thus disabling all compiler optimizations,
+# can be useful for testing.
+#
+!IFNDEF OPTIMIZATIONS
+OPTIMIZATIONS = 2
+!ENDIF
+
+# These are the "standard" SQLite compilation options used when compiling for
+# the Windows platform.
+#
+!IFNDEF OPT_FEATURE_FLAGS
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
+OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
+!ENDIF
+
+###############################################################################
+############################### END OF OPTIONS ################################
+###############################################################################
+
+# This assumes that MSVC is always installed in 32-bit Program Files directory
+# and sets the variable for use in locating other 32-bit installs accordingly.
+#
+PROGRAMFILES_X86 = $(VCINSTALLDIR)\..\..
+PROGRAMFILES_X86 = $(PROGRAMFILES_X86:\\=\)
+
# Check for the predefined command macro CC. This should point to the compiler
# binary for the target platform. If it is not defined, simply define it to
# the legacy default value 'cl.exe'.
@@ -118,6 +169,15 @@ LD = link.exe
RC = rc.exe
!ENDIF
+# Check for the MSVC runtime library path macro. Othertise, this value will
+# default to the 'lib' directory underneath the MSVC installation directory.
+#
+!IFNDEF CRTLIBPATH
+CRTLIBPATH = $(VCINSTALLDIR)\lib
+!ENDIF
+
+CRTLIBPATH = $(CRTLIBPATH:\\=\)
+
# Check for the command macro NCC. This should point to the compiler binary
# for the platform the compilation process is taking place on. If it is not
# defined, simply define it to have the same value as the CC macro. When
@@ -140,14 +200,14 @@ RC = rc.exe
!IFDEF NCC
NCC = $(NCC:\\=\)
!ELSEIF $(XCOMPILE)!=0
-NCC = "$(VCINSTALLDIR)\bin\cl.exe"
+NCC = "$(VCINSTALLDIR)\bin\$(CC)"
NCC = $(NCC:\\=\)
!ELSE
NCC = $(CC)
!ENDIF
-# Check for the MSVC runtime library path macro. Othertise, this
-# value will default to the 'lib' directory underneath the MSVC
+# Check for the MSVC native runtime library path macro. Othertise,
+# this value will default to the 'lib' directory underneath the MSVC
# installation directory.
#
!IFNDEF NCRTLIBPATH
@@ -173,6 +233,13 @@ NSDKLIBPATH = $(NSDKLIBPATH:\\=\)
#
BCC = $(NCC) -W3
+# Check if assembly code listings should be generated for the source
+# code files to be compiled.
+#
+!IF $(USE_LISTINGS)!=0
+BCC = $(BCC) -FAcs
+!ENDIF
+
# Check if the native library paths should be used when compiling
# the command line tools used during the compilation process. If
# so, set the necessary macro now.
@@ -185,9 +252,16 @@ NLTLIBPATHS = "/LIBPATH:$(NCRTLIBPATH)" "/LIBPATH:$(NSDKLIBPATH)"
# will run on the target platform. (BCC and TCC are usually the
# same unless your are cross-compiling.)
#
-TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src -fp:precise
+TCC = $(CC) -W3 -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise
RCC = $(RC) -DSQLITE_OS_WIN=1 -I$(TOP) -I$(TOP)\src
+# Check if assembly code listings should be generated for the source
+# code files to be compiled.
+#
+!IF $(USE_LISTINGS)!=0
+TCC = $(TCC) -FAcs
+!ENDIF
+
# When compiling the library for use in the WinRT environment,
# the following compile-time options must be used as well to
# disable use of Win32 APIs that are not available and to enable
@@ -234,6 +308,17 @@ TCC = $(TCC) -I$(TOP)\ext\rtree
RCC = $(RCC) -I$(TOP)\ext\rtree
!ENDIF
+# The mksqlite3c.tcl script accepts some options on the command
+# line. When compiling with debugging enabled, some of these
+# options are necessary in order to allow debugging symbols to
+# work correctly with Visual Studio when using the amalgamation.
+#
+!IF $(DEBUG)>0
+MKSQLITE3C_ARGS = --linemacros
+!ELSE
+MKSQLITE3C_ARGS =
+!ENDIF
+
# 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.
@@ -259,7 +344,6 @@ TCC = $(TCC) -DSQLITE_ENABLE_IOTRACE
RCC = $(RCC) -DSQLITE_ENABLE_IOTRACE
!ENDIF
-#
# Prevent warnings about "insecure" MSVC runtime library functions
# being used.
#
@@ -267,28 +351,24 @@ TCC = $(TCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
BCC = $(BCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
RCC = $(RCC) -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS
-#
# Prevent warnings about "deprecated" POSIX functions being used.
#
TCC = $(TCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
BCC = $(BCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
RCC = $(RCC) -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS
-#
# Use the SQLite debugging heap subsystem?
#
!IF $(MEMDEBUG)!=0
TCC = $(TCC) -DSQLITE_MEMDEBUG=1
RCC = $(RCC) -DSQLITE_MEMDEBUG=1
-#
# Use native Win32 heap subsystem instead of malloc/free?
#
!ELSEIF $(WIN32HEAP)!=0
TCC = $(TCC) -DSQLITE_WIN32_MALLOC=1
RCC = $(RCC) -DSQLITE_WIN32_MALLOC=1
-#
# Validate the heap on every call into the native Win32 heap subsystem?
#
!IF $(DEBUG)>2
@@ -343,11 +423,15 @@ TCLSH_CMD = tclsh85
# Compiler options needed for programs that use the readline() library.
#
+!IFNDEF READLINE_FLAGS
READLINE_FLAGS = -DHAVE_READLINE=0
+!ENDIF
# The library that programs using readline() must link against.
#
+!IFNDEF LIBREADLINE
LIBREADLINE =
+!ENDIF
# Should the database engine be compiled threadsafe
#
@@ -379,41 +463,57 @@ RCC = $(RCC) -DSQLITE_TEMP_STORE=1
# The same set of OMIT and ENABLE flags should be passed to the
# LEMON parser generator and the mkkeywordhash tool as well.
-# BEGIN standard options
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_FTS3=1
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_RTREE=1
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_ENABLE_COLUMN_METADATA=1
-# END standard options
-
-# BEGIN required Windows option
-OPT_FEATURE_FLAGS = $(OPT_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
-# END required Windows option
+# These are the required SQLite compilation options used when compiling for
+# the Windows platform.
+#
+REQ_FEATURE_FLAGS = $(REQ_FEATURE_FLAGS) -DSQLITE_MAX_TRIGGER_DEPTH=100
-TCC = $(TCC) $(OPT_FEATURE_FLAGS)
-RCC = $(RCC) $(OPT_FEATURE_FLAGS)
+# Add the required and optional SQLite compilation options into the command
+# lines used to invoke the MSVC code and resource compilers.
+#
+TCC = $(TCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS)
+RCC = $(RCC) $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS)
-# Add in any optional parameters specified on the make commane line
-# ie. make "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1".
+# Add in any optional parameters specified on the commane line, e.g.
+# nmake /f Makefile.msc all "OPTS=-DSQLITE_ENABLE_FOO=1 -DSQLITE_OMIT_FOO=1"
+#
TCC = $(TCC) $(OPTS)
RCC = $(RCC) $(OPTS)
-# If symbols are enabled, enable PDBs.
-# If debugging is enabled, disable all optimizations and enable PDBs.
+# If compiling for debugging, add some defines.
+#
!IF $(DEBUG)>0
-TCC = $(TCC) -Od -D_DEBUG
-BCC = $(BCC) -Od -D_DEBUG
+TCC = $(TCC) -D_DEBUG
+BCC = $(BCC) -D_DEBUG
RCC = $(RCC) -D_DEBUG
-!ELSE
+!ENDIF
+
+# If optimizations are enabled or disabled (either implicitly or
+# explicitly), add the necessary flags.
+#
+!IF $(DEBUG)>0 || $(OPTIMIZATIONS)==0
+TCC = $(TCC) -Od
+BCC = $(BCC) -Od
+!ELSEIF $(OPTIMIZATIONS)>=3
+TCC = $(TCC) -Ox
+BCC = $(BCC) -Ox
+!ELSEIF $(OPTIMIZATIONS)==2
TCC = $(TCC) -O2
BCC = $(BCC) -O2
+!ELSEIF $(OPTIMIZATIONS)==1
+TCC = $(TCC) -O1
+BCC = $(BCC) -O1
!ENDIF
+# If symbols are enabled (or compiling for debugging), enable PDBs.
+#
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
TCC = $(TCC) -Zi
BCC = $(BCC) -Zi
!ENDIF
# If ICU support is enabled, add the compiler options for it.
+#
!IF $(USE_ICU)!=0
TCC = $(TCC) -DSQLITE_ENABLE_ICU=1
RCC = $(RCC) -DSQLITE_ENABLE_ICU=1
@@ -425,6 +525,7 @@ RCC = $(RCC) -I$(ICUINCDIR)
# Command line prefixes for compiling code, compiling resources,
# linking, etc.
+#
LTCOMPILE = $(TCC) -Fo$@
LTRCOMPILE = $(RCC) -r
LTLIB = lib.exe
@@ -445,34 +546,82 @@ LTLIBOPTS = /MACHINE:$(PLATFORM)
#
!IF $(FOR_WINRT)!=0
LTLINKOPTS = $(LTLINKOPTS) /APPCONTAINER
+!IF "$(VISUALSTUDIOVERSION)"=="12.0"
+!IFNDEF STORELIBPATH
+!IF "$(PLATFORM)"=="x86"
+STORELIBPATH = $(CRTLIBPATH)\store
+!ELSEIF "$(PLATFORM)"=="x64"
+STORELIBPATH = $(CRTLIBPATH)\store\amd64
+!ELSEIF "$(PLATFORM)"=="ARM"
+STORELIBPATH = $(CRTLIBPATH)\store\arm
+!ELSE
+STORELIBPATH = $(CRTLIBPATH)\store
+!ENDIF
+!ENDIF
+STORELIBPATH = $(STORELIBPATH:\\=\)
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(STORELIBPATH)"
+!ENDIF
+!ENDIF
+
+# When compiling for Windows Phone 8.1, an extra library path is
+# required.
+#
+!IF $(USE_WP81_OPTS)!=0
+!IFNDEF WP81LIBPATH
+!IF "$(PLATFORM)"=="x86"
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
+!ELSEIF "$(PLATFORM)"=="ARM"
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\ARM
+!ELSE
+WP81LIBPATH = $(PROGRAMFILES_X86)\Windows Phone Kits\8.1\lib\x86
+!ENDIF
+!ENDIF
+!ENDIF
+
+# When compiling for Windows Phone 8.1, some extra linker options
+# are also required.
+#
+!IF $(USE_WP81_OPTS)!=0
+!IFDEF WP81LIBPATH
+LTLINKOPTS = $(LTLINKOPTS) "/LIBPATH:$(WP81LIBPATH)"
+!ENDIF
+LTLINKOPTS = $(LTLINKOPTS) /DYNAMICBASE
+LTLINKOPTS = $(LTLINKOPTS) WindowsPhoneCore.lib RuntimeObject.lib PhoneAppModelHost.lib
+LTLINKOPTS = $(LTLINKOPTS) /NODEFAULTLIB:kernel32.lib /NODEFAULTLIB:ole32.lib
!ENDIF
# If either debugging or symbols are enabled, enable PDBs.
+#
!IF $(DEBUG)>0 || $(SYMBOLS)!=0
LDFLAGS = /DEBUG
!ENDIF
# Start with the Tcl related linker options.
+#
!IF $(NO_TCL)==0
LTLIBPATHS = /LIBPATH:$(TCLLIBDIR)
LTLIBS = $(LIBTCL)
!ENDIF
# If ICU support is enabled, add the linker options for it.
+#
!IF $(USE_ICU)!=0
LTLIBPATHS = $(LTLIBPATHS) /LIBPATH:$(ICULIBDIR)
LTLIBS = $(LTLIBS) $(LIBICU)
!ENDIF
# nawk compatible awk.
+#
+!IFNDEF NAWK
NAWK = gawk.exe
+!ENDIF
# You should not have to change anything below this line
###############################################################################
# Object files for the SQLite library (non-amalgamation).
#
-LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
+LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
backup.lo bitvec.lo btmutex.lo btree.lo build.lo \
callback.lo complete.lo ctime.lo date.lo delete.lo \
expr.lo fault.lo fkey.lo \
@@ -485,11 +634,11 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
memjournal.lo \
mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \
notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \
- pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
+ pager.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \
random.lo resolve.lo rowset.lo rtree.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 vdbemem.lo vdbesort.lo \
+ vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo wal.lo walker.lo where.lo utf.lo vtab.lo
# Object files for the amalgamation.
@@ -560,8 +709,10 @@ SRC = \
$(TOP)\src\os.c \
$(TOP)\src\os.h \
$(TOP)\src\os_common.h \
+ $(TOP)\src\os_setup.h \
$(TOP)\src\os_unix.c \
$(TOP)\src\os_win.c \
+ $(TOP)\src\os_win.h \
$(TOP)\src\pager.c \
$(TOP)\src\pager.h \
$(TOP)\src\parse.y \
@@ -602,7 +753,8 @@ SRC = \
$(TOP)\src\wal.c \
$(TOP)\src\wal.h \
$(TOP)\src\walker.c \
- $(TOP)\src\where.c
+ $(TOP)\src\where.c \
+ $(TOP)\src\whereInt.h
# Source code for extensions
#
@@ -710,11 +862,14 @@ TESTSRC = \
TESTEXT = \
$(TOP)\ext\misc\amatch.c \
$(TOP)\ext\misc\closure.c \
+ $(TOP)\ext\misc\fileio.c \
$(TOP)\ext\misc\fuzzer.c \
$(TOP)\ext\misc\ieee754.c \
$(TOP)\ext\misc\nextchar.c \
+ $(TOP)\ext\misc\percentile.c \
$(TOP)\ext\misc\regexp.c \
$(TOP)\ext\misc\spellfix.c \
+ $(TOP)\ext\misc\totype.c \
$(TOP)\ext\misc\wholenumber.c
@@ -778,6 +933,8 @@ HDR = \
opcodes.h \
$(TOP)\src\os.h \
$(TOP)\src\os_common.h \
+ $(TOP)\src\os_setup.h \
+ $(TOP)\src\os_win.h \
$(TOP)\src\pager.h \
$(TOP)\src\pcache.h \
parse.h \
@@ -786,7 +943,8 @@ HDR = \
$(TOP)\src\sqliteInt.h \
$(TOP)\src\sqliteLimit.h \
$(TOP)\src\vdbe.h \
- $(TOP)\src\vdbeInt.h
+ $(TOP)\src\vdbeInt.h \
+ $(TOP)\src\whereInt.h
# Header files used by extensions
#
@@ -841,22 +999,31 @@ mptester.exe: $(TOP)\mptest\mptest.c libsqlite3.lib $(LIBRESOBJS) sqlite3.h
-mkdir tsrc
for %i in ($(SRC)) do copy /Y %i tsrc
del /Q tsrc\sqlite.h.in tsrc\parse.y
- $(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl < tsrc\vdbe.c > vdbe.new
+ $(TCLSH_CMD) $(TOP)\tool\vdbe-compress.tcl $(OPTS) < tsrc\vdbe.c > vdbe.new
move vdbe.new tsrc\vdbe.c
echo > .target_source
sqlite3.c: .target_source $(TOP)\tool\mksqlite3c.tcl
- $(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl
+ $(TCLSH_CMD) $(TOP)\tool\mksqlite3c.tcl $(MKSQLITE3C_ARGS)
copy tsrc\shell.c .
copy tsrc\sqlite3ext.h .
sqlite3-all.c: sqlite3.c $(TOP)\tool\split-sqlite3c.tcl
$(TCLSH_CMD) $(TOP)\tool\split-sqlite3c.tcl
+# Set the source code file to be used by executables and libraries when
+# they need the amalgamation.
+#
+!IF $(SPLIT_AMALGAMATION)!=0
+SQLITE3C = sqlite3-all.c
+!ELSE
+SQLITE3C = sqlite3.c
+!ENDIF
+
# Rule to build the amalgamation
#
-sqlite3.lo: sqlite3.c
- $(LTCOMPILE) -c sqlite3.c
+sqlite3.lo: $(SQLITE3C)
+ $(LTCOMPILE) -c $(SQLITE3C)
# Rules to build the LEMON compiler generator
#
@@ -864,7 +1031,7 @@ lempar.c: $(TOP)\src\lempar.c
copy $(TOP)\src\lempar.c .
lemon.exe: $(TOP)\tool\lemon.c lempar.c
- $(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLIBPATHS)
+ $(BCC) -Daccess=_access -Fe$@ $(TOP)\tool\lemon.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
# Rules to build individual *.lo files from generated *.c files. This
# applies to:
@@ -1124,15 +1291,15 @@ parse.h: parse.c
parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\addopcodes.awk
del /Q parse.y parse.h parse.h.temp
copy $(TOP)\src\parse.y .
- .\lemon.exe $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
+ .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y
move parse.h parse.h.temp
$(NAWK) -f $(TOP)\addopcodes.awk parse.h.temp > parse.h
sqlite3.h: $(TOP)\src\sqlite.h.in $(TOP)\manifest.uuid $(TOP)\VERSION
- $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP) > sqlite3.h
+ $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > sqlite3.h
mkkeywordhash.exe: $(TOP)\tool\mkkeywordhash.c
- $(BCC) -Fe$@ $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLIBPATHS)
+ $(BCC) -Fe$@ $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(OPTS) $(TOP)\tool\mkkeywordhash.c /link $(NLTLINKOPTS) $(NLTLIBPATHS)
keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
.\mkkeywordhash.exe > keywordhash.h
@@ -1216,7 +1383,7 @@ TESTFIXTURE_FLAGS = -DTCLSH=1 -DSQLITE_TEST=1 -DSQLITE_CRASH_TEST=1
TESTFIXTURE_FLAGS = $(TESTFIXTURE_FLAGS) -DSQLITE_SERVER=1 -DSQLITE_PRIVATE="" -DSQLITE_CORE
TESTFIXTURE_SRC0 = $(TESTEXT) $(TESTSRC2) libsqlite3.lib
-TESTFIXTURE_SRC1 = $(TESTEXT) sqlite3.c
+TESTFIXTURE_SRC1 = $(TESTEXT) $(SQLITE3C)
!IF $(USE_AMALGAMATION)==0
TESTFIXTURE_SRC = $(TESTSRC) $(TOP)\src\tclsqlite.c $(TESTFIXTURE_SRC0)
!ELSE
@@ -1229,6 +1396,9 @@ testfixture.exe: $(TESTFIXTURE_SRC) $(LIBRESOBJS) $(HDR)
$(TESTFIXTURE_SRC) \
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+extensiontest: testfixture.exe testloadext.dll
+ .\testfixture.exe $(TOP)\test\loadext.test
+
fulltest: testfixture.exe sqlite3.exe
.\testfixture.exe $(TOP)\test\all.test
@@ -1238,11 +1408,14 @@ soaktest: testfixture.exe sqlite3.exe
fulltestonly: testfixture.exe sqlite3.exe
.\testfixture.exe $(TOP)\test\full.test
+queryplantest: testfixture.exe sqlite3.exe
+ .\testfixture.exe $(TOP)\test\permutations.test queryplanner
+
test: testfixture.exe sqlite3.exe
.\testfixture.exe $(TOP)\test\veryquick.test
-sqlite3_analyzer.c: sqlite3.c $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
- copy sqlite3.c + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
+sqlite3_analyzer.c: $(SQLITE3C) $(TOP)\src\test_stat.c $(TOP)\src\tclsqlite.c $(TOP)\tool\spaceanal.tcl
+ copy $(SQLITE3C) + $(TOP)\src\test_stat.c + $(TOP)\src\tclsqlite.c $@
echo static const char *tclsh_main_loop(void){ >> $@
echo static const char *zMainloop = >> $@
$(NAWK) -f $(TOP)\tool\tostr.awk $(TOP)\tool\spaceanal.tcl >> $@
@@ -1252,12 +1425,54 @@ sqlite3_analyzer.exe: sqlite3_analyzer.c $(LIBRESOBJS)
$(LTLINK) -DBUILD_sqlite -DTCLSH=2 -I$(TCLINCDIR) sqlite3_analyzer.c \
/link $(LTLINKOPTS) $(LTLIBPATHS) $(LIBRESOBJS) $(LTLIBS) $(TLIBS)
+testloadext.lo: $(TOP)\src\test_loadext.c
+ $(LTCOMPILE) -c $(TOP)\src\test_loadext.c
+
+testloadext.dll: testloadext.lo
+ $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo
+
+showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\tool\showdb.c $(SQLITE3C)
+
+showstat4.exe: $(TOP)\tool\showstat4.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\tool\showstat4.c $(SQLITE3C)
+
+showjournal.exe: $(TOP)\tool\showjournal.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\tool\showjournal.c $(SQLITE3C)
+
+showwal.exe: $(TOP)\tool\showwal.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\tool\showwal.c $(SQLITE3C)
+
+fts3view.exe: $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\ext\fts3\tool\fts3view.c $(SQLITE3C)
+
+rollback-test.exe: $(TOP)\tool\rollback-test.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\tool\rollback-test.c $(SQLITE3C)
+
+LogEst.exe: $(TOP)\tool\logest.c sqlite3.h
+ $(LTLINK) -Fe$@ $(TOP)\tool\LogEst.c
+
+wordcount.exe: $(TOP)\test\wordcount.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\test\wordcount.c $(SQLITE3C)
+
+speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C)
+ $(LTLINK) -DSQLITE_OMIT_LOAD_EXTENSION -Fe$@ \
+ $(TOP)\test\speedtest1.c $(SQLITE3C)
+
clean:
del /Q *.lo *.ilk *.lib *.obj *.pdb sqlite3.exe libsqlite3.lib
- del /Q *.da *.bb *.bbg gmon.out
+ del /Q *.cod *.da *.bb *.bbg gmon.out
del /Q sqlite3.h opcodes.c opcodes.h
- del /Q lemon.exe lempar.c parse.*
- del /Q mkkeywordhash.exe keywordhash.h
+ del /Q lemon.* lempar.c parse.*
+ del /Q mkkeywordhash.* keywordhash.h
+ del /Q notasharedlib.*
-rmdir /Q/S .deps
-rmdir /Q/S .libs
-rmdir /Q/S quota2a
@@ -1266,9 +1481,13 @@ clean:
-rmdir /Q/S tsrc
del /Q .target_source
del /Q tclsqlite3.exe tclsqlite3.exp
+ del /Q testloadext.dll testloadext.exp
del /Q testfixture.exe testfixture.exp test.db
+ del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe
+ del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe
+ del /Q wordcount.exe
del /Q sqlite3.dll sqlite3.lib sqlite3.exp sqlite3.def
- del /Q sqlite3.c
+ del /Q sqlite3.c sqlite3-*.c
del /Q sqlite3rc.h
del /Q shell.c sqlite3ext.h
del /Q sqlite3_analyzer.exe sqlite3_analyzer.exp sqlite3_analyzer.c
diff --git a/Makefile.vxworks b/Makefile.vxworks
index c9058da..0d9c27f 100644
--- a/Makefile.vxworks
+++ b/Makefile.vxworks
@@ -262,8 +262,10 @@ SRC = \
$(TOP)/src/os.c \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
$(TOP)/src/os_unix.c \
$(TOP)/src/os_win.c \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.c \
$(TOP)/src/pager.h \
$(TOP)/src/parse.y \
@@ -416,6 +418,8 @@ HDR = \
opcodes.h \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.h \
$(TOP)/src/pcache.h \
parse.h \
diff --git a/README b/README.md
index 0378dad..a0f4cea 100644
--- a/README
+++ b/README.md
@@ -1,4 +1,4 @@
-== SQLCipher ==
+## SQLCipher
SQLCipher is an SQLite extension that provides transparent 256-bit AES encryption of
database files. Pages are encrypted before being written to disk and are decrypted
@@ -11,7 +11,7 @@ SQLCipher was initially developed by Stephen Lombardo at Zetetic LLC
(sjlombardo@zetetic.net) as the encrypted database layer for Strip,
an iPhone data vault and password manager (http://getstrip.com).
-[Features]
+## Features
- Fast performance with as little as 5-15% overhead for encryption on many operations
- 100% of data in the database file is encrypted
@@ -20,7 +20,7 @@ an iPhone data vault and password manager (http://getstrip.com).
- Algorithms provided by the peer reviewed OpenSSL crypto library.
- Configurable crypto providers
-[Compiling]
+## Compiling
Building SQLCipher is almost the same as compiling a regular version of
SQLite with two small exceptions:
@@ -30,57 +30,56 @@ SQLite with two small exceptions:
Example Static linking (replace /opt/local/lib with the path to libcrypto.a)
- $ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
- LDFLAGS="/opt/local/lib/libcrypto.a"
- $ make
+ $ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
+ LDFLAGS="/opt/local/lib/libcrypto.a"
+ $ make
Example Dynamic linking
- $ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
- LDFLAGS="-lcrypto"
- $ make
+ $ ./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" \
+ LDFLAGS="-lcrypto"
+ $ make
-[Encrypting a database]
+## Encrypting a database
To specify an encryption passphrase for the database via the SQL interface you
use a pragma. The passphrase you enter is passed through PBKDF2 key derivation to
obtain the encryption key for the database
- PRAGMA key = 'passphrase';
+ PRAGMA key = 'passphrase';
Alternately, you can specify an exact byte sequence using a blob literal. If you
use this method it is your responsibility to ensure that the data you provide a
64 character hex string, which will be converted directly to 32 bytes (256 bits) of
key data without key derivation.
- PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
To encrypt a database programatically you can use the sqlite3_key function.
The data provided in pKey is converted to an encryption key according to the
same rules as PRAGMA key.
-
- int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
+ int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
PRAGMA key or sqlite3_key should be called as the first operation when a database is open.
-[Changing a database key]
+## Changing a database key
To change the encryption passphrase for an existing database you may use the rekey pragma
after you've supplied the correct database password;
- PRAGMA key = 'passphrase'; -- start with the existing database passphrase
- PRAGMA rekey = 'new-passphrase'; -- rekey will reencrypt with the new passphrase
+ PRAGMA key = 'passphrase'; -- start with the existing database passphrase
+ PRAGMA rekey = 'new-passphrase'; -- rekey will reencrypt with the new passphrase
The hexrekey pragma may be used to rekey to a specific binary value
- PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA rekey = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
This can be accomplished programtically by using sqlite3_rekey;
- sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
+ sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey)
-[Support]
+## Support
The primary avenue for support and discussions is the SQLCipher users mailing list:
@@ -97,7 +96,7 @@ posts about SQLCipher as we do not monitor them frequently.
If you are using SQLCipher in your own software please let us know at
support@zetetic.net!
-[License]
+## License
Copyright (c) 2008, ZETETIC LLC
All rights reserved.
@@ -124,11 +123,11 @@ 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.
-== End SQLCipher ==
+## End SQLCipher
This directory contains source code to
- SQLite: An Embeddable SQL Database Engine
+ 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
@@ -138,7 +137,7 @@ 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"
+ 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
@@ -164,4 +163,4 @@ AWK.
Contacts:
- http://www.sqlite.org/
+ http://www.sqlite.org/
diff --git a/VERSION b/VERSION
index 9a4c437..b916322 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.7.17
+3.8.6 \ No newline at end of file
diff --git a/addopcodes.awk b/addopcodes.awk
index c90e1dd..dcd31ef 100644
--- a/addopcodes.awk
+++ b/addopcodes.awk
@@ -28,7 +28,7 @@ END {
printf "#define TK_%-29s %4d\n", "COLUMN", ++max
printf "#define TK_%-29s %4d\n", "AGG_FUNCTION", ++max
printf "#define TK_%-29s %4d\n", "AGG_COLUMN", ++max
- printf "#define TK_%-29s %4d\n", "CONST_FUNC", ++max
printf "#define TK_%-29s %4d\n", "UMINUS", ++max
printf "#define TK_%-29s %4d\n", "UPLUS", ++max
+ printf "#define TK_%-29s %4d\n", "REGISTER", ++max
}
diff --git a/autoconf/INSTALL b/autoconf/INSTALL
new file mode 100644
index 0000000..a1e89e1
--- /dev/null
+++ b/autoconf/INSTALL
@@ -0,0 +1,370 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation,
+Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package. Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the `make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior `make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type `make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide `make
+ distcheck', which can by used by developers to test that all other
+ targets like `make install' and `make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. This
+is known as a "VPATH" build.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'. Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated. The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the `DESTDIR' variable. For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names. The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of `make' will be. For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ HP-UX `make' updates targets which have the same time stamps as
+their prerequisites, which makes it generally unusable when shipped
+generated files such as `configure' are involved. Use GNU `make'
+instead.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file. The option `-nodtk' can be used as
+a workaround. If GNU CC is not installed, it is therefore recommended
+to try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+ Print a summary of the options unique to this package's
+ `configure', and exit. The `short' variant lists options used
+ only in the top level, while the `recursive' variant lists options
+ also present in any nested packages.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names::
+ for more details, including other options available for fine-tuning
+ the installation locations.
+
+`--no-create'
+`-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am
new file mode 100644
index 0000000..6fc4f33
--- /dev/null
+++ b/autoconf/Makefile.am
@@ -0,0 +1,19 @@
+
+AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
+
+lib_LTLIBRARIES = libsqlite3.la
+libsqlite3_la_SOURCES = sqlite3.c
+libsqlite3_la_LDFLAGS = -no-undefined -version-info 8:6:8
+
+bin_PROGRAMS = sqlite3
+sqlite3_SOURCES = shell.c sqlite3.h
+sqlite3_LDADD = $(top_builddir)/libsqlite3.la @READLINE_LIBS@
+sqlite3_DEPENDENCIES = $(top_builddir)/libsqlite3.la
+
+include_HEADERS = sqlite3.h sqlite3ext.h
+
+EXTRA_DIST = sqlite3.1 tea
+pkgconfigdir = ${libdir}/pkgconfig
+pkgconfig_DATA = sqlite3.pc
+
+man_MANS = sqlite3.1
diff --git a/autoconf/README b/autoconf/README
new file mode 100644
index 0000000..dd8cca2
--- /dev/null
+++ b/autoconf/README
@@ -0,0 +1,32 @@
+
+This package contains:
+
+ * the SQLite library amalgamation (single file) source code distribution,
+ * the shell.c file used to build the sqlite3 shell too, and
+ * the sqlite3.h and sqlite3ext.h header files required to link programs
+ and sqlite extensions against the installed libary.
+ * autoconf/automake installation infrastucture.
+
+The generic installation instructions for autoconf/automake are found
+in the INSTALL file.
+
+The following SQLite specific boolean options are supported:
+
+ --enable-readline use readline in shell tool [default=yes]
+ --enable-threadsafe build a thread-safe library [default=yes]
+ --enable-dynamic-extensions support loadable extensions [default=yes]
+
+The default value for the CFLAGS variable (options passed to the C
+compiler) includes debugging symbols in the build, resulting in larger
+binaries than are necessary. Override it on the configure command
+line like this:
+
+ $ CFLAGS="-Os" ./configure
+
+to produce a smaller installation footprint.
+
+Other SQLite compilation parameters can also be set using CFLAGS. For
+example:
+
+ $ CFLAGS="-Os -DSQLITE_OMIT_TRIGGERS" ./configure
+
diff --git a/autoconf/README.first b/autoconf/README.first
new file mode 100644
index 0000000..6676228
--- /dev/null
+++ b/autoconf/README.first
@@ -0,0 +1,57 @@
+
+This file describes how to use the files in this directory to create a new
+version of the "autoconf-amalgamation" package.
+
+1. The following files should have executable permission:
+
+ chmod 755 install-sh
+ chmod 755 missing
+ chmod 755 depcomp
+ chmod 755 config.sub
+ chmod 755 config.guess
+
+2. Copy new versions of the following SQLite files into this directory:
+
+ sqlite3.c
+ sqlite3.h
+ sqlite3ext.h
+ sqlite3.1
+ sqlite3.pc.in
+ shell.c
+
+3. Update the SQLite version number in the AC_INIT macro in file
+ configure.ac:
+
+ AC_INIT(sqlite, 3.6.3, http://www.sqlite.org)
+
+4. Run the following commands to push the version number change through
+ to the generated files.
+
+ aclocal
+ autoconf
+ automake
+
+5. Create the tclsqlite3.c file in the tea/generic directory. As follows:
+
+ mkdir -p tea/generic
+ echo "#ifdef USE_SYSTEM_SQLITE" > tea/generic/tclsqlite3.c
+ echo "# include <sqlite3.h>" >> tea/generic/tclsqlite3.c
+ echo "#else" >> tea/generic/tclsqlite3.c
+ echo "#include \"../../sqlite3.c\"" >> tea/generic/tclsqlite3.c
+ echo "#endif" >> tea/generic/tclsqlite3.c
+ cat ../src/tclsqlite.c >> tea/generic/tclsqlite3.c
+
+6. Update the SQLite version in the AC_INIT macro in file tea/configure.in:
+
+ AC_INIT([sqlite], [3.6.3])
+
+7. From the 'tea' directory, run the following commands:
+
+ autoconf
+ rm -rf autom4te.cache
+
+8. Run "./configure && make dist". This builds a distribution package
+ named something like "sqlite-3.6.3.tar.gz". Rename to
+ "sqlite-amalgamation-3.6.3.tar.gz" and use.
+
+
diff --git a/autoconf/config.guess b/autoconf/config.guess
new file mode 100755
index 0000000..d622a44
--- /dev/null
+++ b/autoconf/config.guess
@@ -0,0 +1,1530 @@
+#! /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, 2007, 2008, 2009, 2010,
+# 2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-02-10'
+
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# 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. Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a 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.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+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, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+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
+
+# 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 tuples: *-*-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 -q __ELF__
+ 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'`
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ 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 ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ 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:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-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:*:[4567])
+ 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 -q __LP64__
+ 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:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
+ amd64)
+ echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_PROCESSOR}-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*:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-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 ;;
+ 8664:Windows_NT:*)
+ echo x86_64-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 ;;
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 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 -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo ${UNAME_MACHINE}-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo ${UNAME_MACHINE}-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ 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-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ 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.[02]*:*)
+ 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 i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-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; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ 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; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; 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.[02]*:*)
+ 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 ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ 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
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ 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 ;;
+ NEO-?:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ 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 ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
+ 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://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+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/autoconf/config.sub b/autoconf/config.sub
new file mode 100755
index 0000000..c894da4
--- /dev/null
+++ b/autoconf/config.sub
@@ -0,0 +1,1773 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+# 2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-02-10'
+
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# 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 GNU 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.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# 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, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+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-android* | linux-dietlibc | linux-newlib* | \
+ linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
+ *)
+ 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 | -microblaze)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -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 \
+ | aarch64 | aarch64_be \
+ | 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 \
+ | be32 | be64 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | epiphany \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | open8 \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
+ | pyramid \
+ | rl78 | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | 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 \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | we32k \
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-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-* \
+ | aarch64-* | aarch64_be-* \
+ | 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-* \
+ | be32-* | be64-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
+ | 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-* \
+ | hexagon-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | le32-* | le64-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | 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-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ | pyramid-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | 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-* | sv1-* | sx?-* \
+ | tahoe-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # 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
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ 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 | 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
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ 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*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
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze)
+ basic_machine=microblaze-xilinx
+ ;;
+ 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
+ ;;
+ 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-/'`
+ ;;
+ msys)
+ basic_machine=i386-pc
+ os=-msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
+ 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
+ ;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
+ 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
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ 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 | ppcbe) basic_machine=powerpc-unknown
+ ;;
+ ppc-* | ppcbe-*)
+ 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
+ ;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ 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
+ ;;
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
+ ;;
+ 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
+ ;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ z80-*-coff)
+ basic_machine=z80-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[24]aeb | 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.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -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* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -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* | -cegcc* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-android* \
+ | -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* | -es*)
+ # 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
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -nacl*)
+ ;;
+ -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
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ 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
+ ;;
+ 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
+ ;;
+ -cnk*|-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/autoconf/configure.ac b/autoconf/configure.ac
new file mode 100644
index 0000000..46b6563
--- /dev/null
+++ b/autoconf/configure.ac
@@ -0,0 +1,108 @@
+
+#-----------------------------------------------------------------------
+# Supports the following non-standard switches.
+#
+# --enable-threadsafe
+# --enable-readline
+# --enable-dynamic-extensions
+#
+
+AC_PREREQ(2.61)
+AC_INIT(sqlite, 3.7.5, http://www.sqlite.org)
+AC_CONFIG_SRCDIR([sqlite3.c])
+
+# Use automake.
+AM_INIT_AUTOMAKE([foreign])
+
+AC_SYS_LARGEFILE
+
+# Check for required programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+AC_PROG_LIBTOOL
+AC_PROG_MKDIR_P
+
+# Check for library functions that SQLite can optionally use.
+AC_CHECK_FUNCS([fdatasync usleep fullfsync localtime_r gmtime_r])
+AC_FUNC_STRERROR_R
+
+AC_CONFIG_FILES([Makefile sqlite3.pc])
+AC_SUBST(BUILD_CFLAGS)
+
+#-----------------------------------------------------------------------
+# --enable-readline
+#
+AC_ARG_ENABLE(readline, [AS_HELP_STRING(
+ [--enable-readline],
+ [use readline in shell tool (yes, no) [default=yes]])],
+ [], [enable_readline=yes])
+if test x"$enable_readline" != xno ; then
+ sLIBS=$LIBS
+ LIBS=""
+ AC_SEARCH_LIBS(tgetent, curses ncurses ncursesw, [], [])
+ AC_SEARCH_LIBS(readline, readline, [], [enable_readline=no])
+ AC_CHECK_FUNCS(readline, [], [])
+ READLINE_LIBS=$LIBS
+ LIBS=$sLIBS
+fi
+AC_SUBST(READLINE_LIBS)
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-threadsafe
+#
+AC_ARG_ENABLE(threadsafe, [AS_HELP_STRING(
+ [--enable-threadsafe], [build a thread-safe library [default=yes]])],
+ [], [enable_threadsafe=yes])
+THREADSAFE_FLAGS=-DSQLITE_THREADSAFE=0
+if test x"$enable_threadsafe" != "xno"; then
+ THREADSAFE_FLAGS="-D_REENTRANT=1 -DSQLITE_THREADSAFE=1"
+ AC_SEARCH_LIBS(pthread_create, pthread)
+fi
+AC_SUBST(THREADSAFE_FLAGS)
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# --enable-dynamic-extensions
+#
+AC_ARG_ENABLE(dynamic-extensions, [AS_HELP_STRING(
+ [--enable-dynamic-extensions], [support loadable extensions [default=yes]])],
+ [], [enable_dynamic_extensions=yes])
+if test x"$enable_dynamic_extensions" != "xno"; then
+ AC_SEARCH_LIBS(dlopen, dl)
+else
+ DYNAMIC_EXTENSION_FLAGS=-DSQLITE_OMIT_LOAD_EXTENSION=1
+fi
+AC_MSG_CHECKING([for whether to support dynamic extensions])
+AC_MSG_RESULT($enable_dynamic_extensions)
+AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
+#-----------------------------------------------------------------------
+
+AC_CHECK_FUNCS(posix_fallocate)
+
+#-----------------------------------------------------------------------
+# UPDATE: Maybe it's better if users just set CFLAGS before invoking
+# configure. This option doesn't really add much...
+#
+# --enable-tempstore
+#
+# AC_ARG_ENABLE(tempstore, [AS_HELP_STRING(
+# [--enable-tempstore],
+# [in-memory temporary tables (never, no, yes, always) [default=no]])],
+# [], [enable_tempstore=no])
+# AC_MSG_CHECKING([for whether or not to store temp tables in-memory])
+# case "$enable_tempstore" in
+# never ) TEMP_STORE=0 ;;
+# no ) TEMP_STORE=1 ;;
+# always ) TEMP_STORE=3 ;;
+# yes ) TEMP_STORE=3 ;;
+# * )
+# TEMP_STORE=1
+# enable_tempstore=yes
+# ;;
+# esac
+# AC_MSG_RESULT($enable_tempstore)
+# AC_SUBST(TEMP_STORE)
+#-----------------------------------------------------------------------
+
+AC_OUTPUT
diff --git a/autoconf/depcomp b/autoconf/depcomp
new file mode 100755
index 0000000..25a39e6
--- /dev/null
+++ b/autoconf/depcomp
@@ -0,0 +1,708 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2012-03-27.16; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
+# 2011, 2012 Free Software Foundation, Inc.
+
+# This program 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, 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, see <http://www.gnu.org/licenses/>.
+
+# 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 Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try '$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by 'PROGRAMS ARGS'.
+ object Object file output by 'PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputting dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+# A tabulation character.
+tab=' '
+# A newline character.
+nl='
+'
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u='sed s,\\\\,/,g'
+ depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+ # This is just like msvc7 but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u='sed s,\\\\,/,g'
+ depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+ # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
+ gccflag=-qmakedep=gcc,-MF
+ depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' "$nl" < "$tmpdepfile" |
+## Some versions of gcc put a space before the ':'. On the theory
+## that the space means something, we add a space to the output as
+## well. hp depmode also adds that space, but also prefixes the VPATH
+## to the object. Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like '#:fec' to the end of the
+ # dependency line.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr "$nl" ' ' >> "$depfile"
+ echo >> "$depfile"
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+xlc)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts '$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form 'foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # '$object: dependent.h' and one to simply 'dependent.h:'.
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
+ # However on
+ # $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using '\':
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+ # tcc 0.9.26 (FIXME still under development at the moment of writing)
+ # will emit a similar output, but also prepend the continuation lines
+ # with horizontal tabulation characters.
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form 'foo.o: dependent.h',
+ # or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # '$object: dependent.h' and one to simply 'dependent.h:'.
+ sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \
+ < "$tmpdepfile" > "$depfile"
+ sed '
+ s/[ '"$tab"'][ '"$tab"']*/ /g
+ s/^ *//
+ s/ *\\*$//
+ s/^[^:]*: *//
+ /^$/d
+ /:$/d
+ s/$/ :/
+ ' < "$tmpdepfile" >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add 'dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in 'foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mechanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+msvc7)
+ if test "$libtool" = yes; then
+ showIncludes=-Wc,-showIncludes
+ else
+ showIncludes=-showIncludes
+ fi
+ "$@" $showIncludes > "$tmpdepfile"
+ stat=$?
+ grep -v '^Note: including file: ' "$tmpdepfile"
+ if test "$stat" = 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ # The first sed program below extracts the file names and escapes
+ # backslashes for cygpath. The second sed program outputs the file
+ # name when reading, but also accumulates all include files in the
+ # hold buffer in order to output them again at the end. This only
+ # works with sed implementations that can handle large buffers.
+ sed < "$tmpdepfile" -n '
+/^Note: including file: *\(.*\)/ {
+ s//\1/
+ s/\\/\\\\/g
+ p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+ s/.*/'"$tab"'/
+ G
+ p
+}' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvc7msys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove '-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for ':'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' "$nl" < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ # makedepend may prepend the VPATH from the source file name to the object.
+ # No need to regex-escape $object, excess matching of '.' is harmless.
+ sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove '-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+ echo "$tab" >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/autoconf/install-sh b/autoconf/install-sh
new file mode 100755
index 0000000..a9244eb
--- /dev/null
+++ b/autoconf/install-sh
@@ -0,0 +1,527 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2011-01-19.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# 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.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -t) dst_arg=$2
+ # Protect names problematic for `test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ # Protect names problematic for `test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ do_exit='(exit $ret); exit $ret'
+ trap "ret=129; $do_exit" 1
+ trap "ret=130; $do_exit" 2
+ trap "ret=141; $do_exit" 13
+ trap "ret=143; $do_exit" 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names problematic for `test' and other utilities.
+ case $src in
+ -* | [=\(\)!]) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+ dst=$dst_arg
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # 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 $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/autoconf/ltmain.sh b/autoconf/ltmain.sh
new file mode 100644
index 0000000..6a2f116
--- /dev/null
+++ b/autoconf/ltmain.sh
@@ -0,0 +1,9655 @@
+
+# libtool (GNU libtool) 2.4.2
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
+# 2007, 2008, 2009, 2010, 2011 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
+# --no-quiet, --no-silent
+# print informational messages (default)
+# --no-warn don't display warning messages
+# --tag=TAG use configuration variables from tag TAG
+# -v, --verbose print more informational messages than default
+# --no-verbose don't print the extra informational messages
+# --version print version information
+# -h, --help, --help-all print short, long, or detailed 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. When passed as first option,
+# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that.
+# 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.4.2
+# automake: $automake_version
+# autoconf: $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+# GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+# General help using GNU software: <http://www.gnu.org/gethelp/>.
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION=2.4.2
+TIMESTAMP=""
+package_revision=1.3337
+
+# 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
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# NLS nuisances: We save the old values to restore during execute mode.
+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
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
+
+$lt_unset CDPATH
+
+
+# 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"
+
+
+
+: ${CP="cp -f"}
+test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${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 file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+} # func_dirname may be replaced by extended shell implementation
+
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "${1}" | $SED "$basename"`
+} # func_basename may be replaced by extended shell implementation
+
+
+# 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 "${1}" | $SED -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 "${1}" | $SED -e "$basename"`
+} # func_dirname_and_basename may be replaced by extended shell implementation
+
+
+# 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 "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+ esac
+} # func_stripname may be replaced by extended shell implementation
+
+
+# These SED scripts presuppose an absolute path with a trailing slash.
+pathcar='s,^/\([^/]*\).*$,\1,'
+pathcdr='s,^/[^/]*,,'
+removedotparts=':dotsl
+ s@/\./@/@g
+ t dotsl
+ s,/\.$,/,'
+collapseslashes='s@/\{1,\}@/@g'
+finalslash='s,/*$,/,'
+
+# func_normal_abspath PATH
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+# value returned in "$func_normal_abspath_result"
+func_normal_abspath ()
+{
+ # Start from root dir and reassemble the path.
+ func_normal_abspath_result=
+ func_normal_abspath_tpath=$1
+ func_normal_abspath_altnamespace=
+ case $func_normal_abspath_tpath in
+ "")
+ # Empty path, that just means $cwd.
+ func_stripname '' '/' "`pwd`"
+ func_normal_abspath_result=$func_stripname_result
+ return
+ ;;
+ # The next three entries are used to spot a run of precisely
+ # two leading slashes without using negated character classes;
+ # we take advantage of case's first-match behaviour.
+ ///*)
+ # Unusual form of absolute path, do nothing.
+ ;;
+ //*)
+ # Not necessarily an ordinary path; POSIX reserves leading '//'
+ # and for example Cygwin uses it to access remote file shares
+ # over CIFS/SMB, so we conserve a leading double slash if found.
+ func_normal_abspath_altnamespace=/
+ ;;
+ /*)
+ # Absolute path, do nothing.
+ ;;
+ *)
+ # Relative path, prepend $cwd.
+ func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+ ;;
+ esac
+ # Cancel out all the simple stuff to save iterations. We also want
+ # the path to end with a slash for ease of parsing, so make sure
+ # there is one (and only one) here.
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"`
+ while :; do
+ # Processed it all yet?
+ if test "$func_normal_abspath_tpath" = / ; then
+ # If we ascended to the root using ".." the result may be empty now.
+ if test -z "$func_normal_abspath_result" ; then
+ func_normal_abspath_result=/
+ fi
+ break
+ fi
+ func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$pathcar"`
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$pathcdr"`
+ # Figure out what to do with it
+ case $func_normal_abspath_tcomponent in
+ "")
+ # Trailing empty path component, ignore it.
+ ;;
+ ..)
+ # Parent dir; strip last assembled component from result.
+ func_dirname "$func_normal_abspath_result"
+ func_normal_abspath_result=$func_dirname_result
+ ;;
+ *)
+ # Actual path component, append it.
+ func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent
+ ;;
+ esac
+ done
+ # Restore leading double-slash if one was found on entry.
+ func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+# func_relative_path SRCDIR DSTDIR
+# generates a relative path from SRCDIR to DSTDIR, with a trailing
+# slash if non-empty, suitable for immediately appending a filename
+# without needing to append a separator.
+# value returned in "$func_relative_path_result"
+func_relative_path ()
+{
+ func_relative_path_result=
+ func_normal_abspath "$1"
+ func_relative_path_tlibdir=$func_normal_abspath_result
+ func_normal_abspath "$2"
+ func_relative_path_tbindir=$func_normal_abspath_result
+
+ # Ascend the tree starting from libdir
+ while :; do
+ # check if we have found a prefix of bindir
+ case $func_relative_path_tbindir in
+ $func_relative_path_tlibdir)
+ # found an exact match
+ func_relative_path_tcancelled=
+ break
+ ;;
+ $func_relative_path_tlibdir*)
+ # found a matching prefix
+ func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+ func_relative_path_tcancelled=$func_stripname_result
+ if test -z "$func_relative_path_result"; then
+ func_relative_path_result=.
+ fi
+ break
+ ;;
+ *)
+ func_dirname $func_relative_path_tlibdir
+ func_relative_path_tlibdir=${func_dirname_result}
+ if test "x$func_relative_path_tlibdir" = x ; then
+ # Have to descend all the way to the root!
+ func_relative_path_result=../$func_relative_path_result
+ func_relative_path_tcancelled=$func_relative_path_tbindir
+ break
+ fi
+ func_relative_path_result=../$func_relative_path_result
+ ;;
+ esac
+ done
+
+ # Now calculate path; take care to avoid doubling-up slashes.
+ func_stripname '' '/' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ func_stripname '/' '/' "$func_relative_path_tcancelled"
+ if test "x$func_stripname_result" != x ; then
+ func_relative_path_result=${func_relative_path_result}/${func_stripname_result}
+ fi
+
+ # Normalisation. If bindir is libdir, return empty string,
+ # else relative path ending with a slash; either way, target
+ # file name can be directly appended.
+ if test ! -z "$func_relative_path_result"; then
+ func_stripname './' '' "$func_relative_path_result/"
+ func_relative_path_result=$func_stripname_result
+ fi
+}
+
+# The name of this program:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+
+# 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=${PATH_SEPARATOR-:}
+ 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'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s,[].[^$\\*\/],\\&,g'
+
+# Sed substitution that converts a w32 file name or path
+# which contains forward slashes, into one that contains
+# (escaped) backslashes. A very naive implementation.
+lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|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: ${opt_mode+$opt_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_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+ $opt_warning && $ECHO "$progname: ${opt_mode+$opt_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 "$my_directory_path" | $SED -e "$dirname"`
+ done
+ my_dir_list=`$ECHO "$my_dir_list" | $SED '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 "$my_tmpdir"
+}
+
+
+# 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 "$1" | $SED "$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 "$1" | $SED \
+ -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_tr_sh
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result. All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+ case $1 in
+ [0-9]* | *[!a-zA-Z0-9_]*)
+ func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'`
+ ;;
+ * )
+ func_tr_sh_result=$1
+ ;;
+ esac
+}
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $opt_debug
+
+ $SED -n '/(C)/!b go
+ :more
+ /\./!{
+ N
+ s/\n# / /
+ b more
+ }
+ :go
+ /^# '$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 ()
+{
+ $opt_debug
+
+ $SED -n '/^# Usage:/,/^# *.*--help/ {
+ s/^# //
+ s/^# *$//
+ s/\$progname/'$progname'/
+ p
+ }' < "$progpath"
+ echo
+ $ECHO "run \`$progname --help | more' for full usage"
+ exit $?
+}
+
+# func_help [NOEXIT]
+# Echo long help message to standard output and exit,
+# unless 'noexit' is passed as argument.
+func_help ()
+{
+ $opt_debug
+
+ $SED -n '/^# Usage:/,/# Report bugs to/ {
+ :print
+ 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-automake} --version) 2>/dev/null |$SED 1q`"'/
+ s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/
+ p
+ d
+ }
+ /^# .* home page:/b print
+ /^# General help using/b print
+ ' < "$progpath"
+ ret=$?
+ if test -z "$1"; then
+ exit $ret
+ fi
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ $opt_debug
+
+ func_error "missing argument for $1."
+ exit_cmd=exit
+}
+
+
+# func_split_short_opt shortopt
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+func_split_short_opt ()
+{
+ my_sed_short_opt='1s/^\(..\).*$/\1/;q'
+ my_sed_short_rest='1s/^..\(.*\)$/\1/;q'
+
+ func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"`
+ func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"`
+} # func_split_short_opt may be replaced by extended shell implementation
+
+
+# func_split_long_opt longopt
+# Set func_split_long_opt_name and func_split_long_opt_arg shell
+# variables after splitting LONGOPT at the `=' sign.
+func_split_long_opt ()
+{
+ my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
+ my_sed_long_arg='1s/^--[^=]*=//'
+
+ func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"`
+ func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"`
+} # func_split_long_opt may be replaced by extended shell implementation
+
+exit_cmd=:
+
+
+
+
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+nonopt=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+# 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_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "${1}=\$${1}\${2}"
+} # func_append may be replaced by extended shell implementation
+
+# func_append_quoted var value
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+func_append_quoted ()
+{
+ func_quote_for_eval "${2}"
+ eval "${1}=\$${1}\\ \$func_quote_for_eval_result"
+} # func_append_quoted may be replaced by extended shell implementation
+
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "${@}"`
+} # func_arith may be replaced by extended shell implementation
+
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len`
+} # func_len may be replaced by extended shell implementation
+
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
+} # func_lo2o may be replaced by extended shell implementation
+
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
+} # func_xform may be replaced by extended shell implementation
+
+
+# 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
+}
+
+# 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
+}
+
+
+# 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
+
+
+
+# Option defaults:
+opt_debug=:
+opt_dry_run=false
+opt_config=false
+opt_preserve_dup_deps=false
+opt_features=false
+opt_finish=false
+opt_help=false
+opt_help_all=false
+opt_silent=:
+opt_warning=:
+opt_verbose=:
+opt_silent=false
+opt_verbose=false
+
+
+# Parse options once, thoroughly. This comes as soon as possible in the
+# script to make things like `--version' happen as quickly as we can.
+{
+ # this just eases exit handling
+ while test $# -gt 0; do
+ opt="$1"
+ shift
+ case $opt in
+ --debug|-x) opt_debug='set -x'
+ func_echo "enabling shell trace mode"
+ $opt_debug
+ ;;
+ --dry-run|--dryrun|-n)
+ opt_dry_run=:
+ ;;
+ --config)
+ opt_config=:
+func_config
+ ;;
+ --dlopen|-dlopen)
+ optarg="$1"
+ opt_dlopen="${opt_dlopen+$opt_dlopen
+}$optarg"
+ shift
+ ;;
+ --preserve-dup-deps)
+ opt_preserve_dup_deps=:
+ ;;
+ --features)
+ opt_features=:
+func_features
+ ;;
+ --finish)
+ opt_finish=:
+set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ --help)
+ opt_help=:
+ ;;
+ --help-all)
+ opt_help_all=:
+opt_help=': help-all'
+ ;;
+ --mode)
+ test $# = 0 && func_missing_arg $opt && break
+ optarg="$1"
+ opt_mode="$optarg"
+case $optarg 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
+ shift
+ ;;
+ --no-silent|--no-quiet)
+ opt_silent=false
+func_append preserve_args " $opt"
+ ;;
+ --no-warning|--no-warn)
+ opt_warning=false
+func_append preserve_args " $opt"
+ ;;
+ --no-verbose)
+ opt_verbose=false
+func_append preserve_args " $opt"
+ ;;
+ --silent|--quiet)
+ opt_silent=:
+func_append preserve_args " $opt"
+ opt_verbose=false
+ ;;
+ --verbose|-v)
+ opt_verbose=:
+func_append preserve_args " $opt"
+opt_silent=false
+ ;;
+ --tag)
+ test $# = 0 && func_missing_arg $opt && break
+ optarg="$1"
+ opt_tag="$optarg"
+func_append preserve_args " $opt $optarg"
+func_enable_tag "$optarg"
+ shift
+ ;;
+
+ -\?|-h) func_usage ;;
+ --help) func_help ;;
+ --version) func_version ;;
+
+ # Separate optargs to long options:
+ --*=*)
+ func_split_long_opt "$opt"
+ set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate non-argument short options:
+ -\?*|-h*|-n*|-v*)
+ func_split_short_opt "$opt"
+ set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ --) break ;;
+ -*) func_fatal_help "unrecognized option \`$opt'" ;;
+ *) set dummy "$opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+ # Validate options:
+
+ # save first non-option argument
+ if test "$#" -gt 0; then
+ nonopt="$opt"
+ shift
+ fi
+
+ # preserve --debug
+ test "$opt_debug" = : || func_append preserve_args " --debug"
+
+ case $host in
+ *cygwin* | *mingw* | *pw32* | *cegcc*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+ ;;
+ esac
+
+ $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
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$opt_dlopen" && test "$opt_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=$opt_mode' for more information."
+ }
+
+
+ # Bail if the options were screwed
+ $exit_cmd $EXIT_FAILURE
+}
+
+
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+# 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 ()
+{
+ test -f "$1" &&
+ $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 -f "$1" && 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_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+}
+
+# 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_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot. Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+ func_resolve_sysroot_result=$1
+ case $func_resolve_sysroot_result in
+ =*)
+ func_stripname '=' '' "$func_resolve_sysroot_result"
+ func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+ ;;
+ esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+ case "$lt_sysroot:$1" in
+ ?*:"$lt_sysroot"*)
+ func_stripname "$lt_sysroot" '' "$1"
+ func_replace_sysroot_result="=$func_stripname_result"
+ ;;
+ *)
+ # Including no sysroot.
+ func_replace_sysroot_result=$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_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ 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 "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+ # 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_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case "$@ " in
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+ # 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}"
+ }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+ $opt_debug
+ func_convert_core_file_wine_to_w32_result="$1"
+ if test -n "$1"; then
+ # Unfortunately, winepath does not exit with a non-zero error code, so we
+ # are forced to check the contents of stdout. On the other hand, if the
+ # command is not found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both error code of
+ # zero AND non-empty stdout, which explains the odd construction:
+ func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then
+ func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+ $SED -e "$lt_sed_naive_backslashify"`
+ else
+ func_convert_core_file_wine_to_w32_result=
+ fi
+ fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+ $opt_debug
+ # unfortunately, winepath doesn't convert paths, only file names
+ func_convert_core_path_wine_to_w32_result=""
+ if test -n "$1"; then
+ oldIFS=$IFS
+ IFS=:
+ for func_convert_core_path_wine_to_w32_f in $1; do
+ IFS=$oldIFS
+ func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+ if test -n "$func_convert_core_file_wine_to_w32_result" ; then
+ if test -z "$func_convert_core_path_wine_to_w32_result"; then
+ func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result"
+ else
+ func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+ fi
+ fi
+ done
+ IFS=$oldIFS
+ fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+ $opt_debug
+ if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+ func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+ if test "$?" -ne 0; then
+ # on failure, ensure result is empty
+ func_cygpath_result=
+ fi
+ else
+ func_cygpath_result=
+ func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'"
+ fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format. Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+ $opt_debug
+ # awkward: cmd appends spaces to result
+ func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+ $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+ $opt_debug
+ if test -z "$2" && test -n "$1" ; then
+ func_error "Could not determine host file name corresponding to"
+ func_error " \`$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_file_result="$1"
+ fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+ $opt_debug
+ if test -z "$4" && test -n "$3"; then
+ func_error "Could not determine the host path corresponding to"
+ func_error " \`$3'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This is a deliberately simplistic "conversion" and
+ # should not be "improved". See libtool.info.
+ if test "x$1" != "x$2"; then
+ lt_replace_pathsep_chars="s|$1|$2|g"
+ func_to_host_path_result=`echo "$3" |
+ $SED -e "$lt_replace_pathsep_chars"`
+ else
+ func_to_host_path_result="$3"
+ fi
+ fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+ $opt_debug
+ case $4 in
+ $1 ) func_to_host_path_result="$3$func_to_host_path_result"
+ ;;
+ esac
+ case $4 in
+ $2 ) func_append func_to_host_path_result "$3"
+ ;;
+ esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via `$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+ $opt_debug
+ $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result. If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+ $opt_debug
+ case ,$2, in
+ *,"$to_tool_file_cmd",*)
+ func_to_tool_file_result=$1
+ ;;
+ *)
+ $to_tool_file_cmd "$1"
+ func_to_tool_file_result=$func_to_host_file_result
+ ;;
+ esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+ func_to_host_file_result="$1"
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+ $opt_debug
+ func_to_host_file_result="$1"
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_to_host_file_result="$func_convert_core_msys_to_w32_result"
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+ $opt_debug
+ func_to_host_file_result="$1"
+ if test -n "$1"; then
+ # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+ # LT_CYGPATH in this case.
+ func_to_host_file_result=`cygpath -m "$1"`
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format. Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+ $opt_debug
+ func_to_host_file_result="$1"
+ if test -n "$1"; then
+ func_convert_core_file_wine_to_w32 "$1"
+ func_to_host_file_result="$func_convert_core_file_wine_to_w32_result"
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+ $opt_debug
+ func_to_host_file_result="$1"
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_msys_to_w32_result"
+ func_to_host_file_result="$func_cygpath_result"
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set. Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+ $opt_debug
+ func_to_host_file_result="$1"
+ if test -n "$1"; then
+ # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+ func_convert_core_file_wine_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+ func_to_host_file_result="$func_cygpath_result"
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via `$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format. If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+# file name conversion function : func_convert_file_X_to_Y ()
+# path conversion function : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same. If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+ $opt_debug
+ if test -z "$to_host_path_cmd"; then
+ func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+ to_host_path_cmd="func_convert_path_${func_stripname_result}"
+ fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+ $opt_debug
+ func_init_to_host_path_cmd
+ $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+ func_to_host_path_result="$1"
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+ $opt_debug
+ func_to_host_path_result="$1"
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from ARG. MSYS
+ # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+ # and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result="$func_convert_core_msys_to_w32_result"
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+ $opt_debug
+ func_to_host_path_result="$1"
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format. Requires a wine environment and
+# a working winepath. Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+ $opt_debug
+ func_to_host_path_result="$1"
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result="$func_convert_core_path_wine_to_w32_result"
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+ $opt_debug
+ func_to_host_path_result="$1"
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+ func_to_host_path_result="$func_cygpath_result"
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set. Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+ $opt_debug
+ func_to_host_path_result="$1"
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+ func_to_host_path_result="$func_cygpath_result"
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# 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)
+ func_append pie_flag " $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ func_append 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_append_quoted lastarg "$arg"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ func_append 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_append_quoted base_compile "$lastarg"
+ 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 | *.go | *.obj | *.sx | *.cu | *.cup)
+ 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* | cegcc*)
+ 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 "$srcfile" | $SED 's%^.*/%%; 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
+ func_append removelist " $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ func_append removelist " $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+ srcfile=$func_to_tool_file_result
+ 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
+ func_append 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
+ func_append command " -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ func_append 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 "$opt_mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $opt_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 build PIC objects only
+ -prefer-non-pic try to build 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
+ -Wc,FLAG pass FLAG directly to the compiler
+
+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-dir 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
+ -bindir BINDIR specify path to binaries directory (for systems where
+ libraries must be found in the PATH setting at runtime)
+ -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
+ -Wc,FLAG
+ -Xcompiler FLAG pass linker-specific FLAG directly to the compiler
+ -Wl,FLAG
+ -Xlinker FLAG pass linker-specific FLAG directly to the linker
+ -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
+
+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 \`$opt_mode'"
+ ;;
+ esac
+
+ echo
+ $ECHO "Try \`$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+ if test "$opt_help" = :; then
+ func_mode_help
+ else
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ func_mode_help
+ done
+ } | sed -n '1p; 2,$s/^Usage:/ or: /p'
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ echo
+ func_mode_help
+ done
+ } |
+ sed '1d
+ /^When reporting/,/^Report/{
+ H
+ d
+ }
+ $x
+ /information about other modes/d
+ /more detailed .*MODE/d
+ s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+ fi
+ exit $?
+fi
+
+
+# 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 $opt_dlopen; do
+ test -f "$file" \
+ || func_fatal_help "\`$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # 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
+ func_append 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
+ -* | *.la | *.lo ) ;;
+ *)
+ # 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_append_quoted args "$file"
+ 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 "$opt_mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $opt_debug
+ libs=
+ libdirs=
+ admincmds=
+
+ for opt in "$nonopt" ${1+"$@"}
+ do
+ if test -d "$opt"; then
+ func_append libdirs " $opt"
+
+ elif test -f "$opt"; then
+ if func_lalib_unsafe_p "$opt"; then
+ func_append libs " $opt"
+ else
+ func_warning "\`$opt' is not a valid libtool archive"
+ fi
+
+ else
+ func_fatal_error "invalid argument \`$opt'"
+ fi
+ done
+
+ if test -n "$libs"; then
+ if test -n "$lt_sysroot"; then
+ sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+ sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+ else
+ sysroot_cmd=
+ fi
+
+ # Remove sysroot references
+ if $opt_dry_run; then
+ for lib in $libs; do
+ echo "removing references to $lt_sysroot and \`=' prefixes from $lib"
+ done
+ else
+ tmpdir=`func_mktempdir`
+ for lib in $libs; do
+ sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+ > $tmpdir/tmp-la
+ mv -f $tmpdir/tmp-la $lib
+ done
+ ${RM}r "$tmpdir"
+ fi
+ fi
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ 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" || func_append admincmds "
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_silent && exit $EXIT_SUCCESS
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ echo "----------------------------------------------------------------------"
+ 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 "----------------------------------------------------------------------"
+ fi
+ exit $EXIT_SUCCESS
+}
+
+test "$opt_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.
+ case $nonopt in *shtool*) :;; *) false;; esac; 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"
+ func_append install_prog "$func_quote_for_eval_result"
+ install_shared_prog=$install_prog
+ case " $install_prog " in
+ *[\\\ /]cp\ *) install_cp=: ;;
+ *) install_cp=false ;;
+ esac
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=no
+ stripme=
+ no_mode=:
+ for arg
+ do
+ arg2=
+ if test -n "$dest"; then
+ func_append files " $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=yes ;;
+ -f)
+ if $install_cp; then :; else
+ prev=$arg
+ fi
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ if test "x$prev" = x-m && test -n "$install_override_mode"; then
+ arg2=$install_override_mode
+ no_mode=false
+ fi
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ func_append install_prog " $func_quote_for_eval_result"
+ if test -n "$arg2"; then
+ func_quote_for_eval "$arg2"
+ fi
+ func_append install_shared_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 -n "$install_override_mode" && $no_mode; then
+ if $install_cp; then :; else
+ func_quote_for_eval "$install_override_mode"
+ func_append install_shared_prog " -m $func_quote_for_eval_result"
+ fi
+ fi
+
+ 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.
+ func_append staticlibs " $file"
+ ;;
+
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # 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 "*) ;;
+ *) func_append current_libdirs " $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append future_libdirs " $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir="$func_dirname_result"
+ func_append dir "$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "$destdir" | $SED -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 "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "$relink_command" | $SED "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_shared_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme="$stripme"
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ 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" && func_append 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 "$lib" | $SED '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 "$relink_command" | $SED '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 "$file$stripped_ext" | $SED "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_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $tool_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 "$opt_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
+
+#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#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 "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+ func_verbose "extracting global C symbols from \`$func_to_tool_file_result'"
+ $opt_dry_run || eval "$NM $func_to_tool_file_result | $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* | *cegcc* )
+ 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* | *cegcc* )
+ 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"
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ # if an import library, we need to obtain dlname
+ if func_win32_import_lib_p "$dlprefile"; then
+ func_tr_sh "$dlprefile"
+ eval "curr_lafile=\$libfile_$func_tr_sh_result"
+ dlprefile_dlbasename=""
+ if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+ # Use subshell, to avoid clobbering current variable values
+ dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+ if test -n "$dlprefile_dlname" ; then
+ func_basename "$dlprefile_dlname"
+ dlprefile_dlbasename="$func_basename_result"
+ else
+ # no lafile. user explicitly requested -dlpreopen <import library>.
+ $sharedlib_from_linklib_cmd "$dlprefile"
+ dlprefile_dlbasename=$sharedlib_from_linklib_result
+ fi
+ fi
+ $opt_dry_run || {
+ if test -n "$dlprefile_dlbasename" ; then
+ eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+ else
+ func_warning "Could not compute DLL name from $name"
+ eval '$ECHO ": $name " >> "$nlist"'
+ fi
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ }
+ else # not an import lib
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ fi
+ ;;
+ *)
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ ;;
+ esac
+ 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;
+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) ;;
+ *) func_append 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* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "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 "$compile_command" | $SED "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "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.
+# Despite the name, also deal with 64 bit binaries.
+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
+ # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+ $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_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+ $opt_debug
+ sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+ $opt_debug
+ match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+ $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+ $SED '/^Contents of section '"$match_literal"':/{
+ # Place marker at beginning of archive member dllname section
+ s/.*/====MARK====/
+ p
+ d
+ }
+ # These lines can sometimes be longer than 43 characters, but
+ # are always uninteresting
+ /:[ ]*file format pe[i]\{,1\}-/d
+ /^In archive [^:]*:/d
+ # Ensure marker is printed
+ /^====MARK====/p
+ # Remove all lines with less than 43 characters
+ /^.\{43\}/!d
+ # From remaining lines, remove first 43 characters
+ s/^.\{43\}//' |
+ $SED -n '
+ # Join marker and all lines until next marker into a single line
+ /^====MARK====/ b para
+ H
+ $ b para
+ b
+ :para
+ x
+ s/\n//g
+ # Remove the marker
+ s/^====MARK====//
+ # Remove trailing dots and whitespace
+ s/[\. \t]*$//
+ # Print
+ /./p' |
+ # we now have a list, one entry per line, of the stringified
+ # contents of the appropriate section of all members of the
+ # archive which possess that section. Heuristic: eliminate
+ # all those which have a first or second character that is
+ # a '.' (that is, objdump's representation of an unprintable
+ # character.) This should work for all archives with less than
+ # 0x302f exports -- but will fail for DLLs whose name actually
+ # begins with a literal '.' or a single character followed by
+ # a '.'.
+ #
+ # Of those that remain, print the first one.
+ $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+ $opt_debug
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+ test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+ $opt_debug
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+ test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+ $opt_debug
+ if func_cygming_gnu_implib_p "$1" ; then
+ # binutils import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+ elif func_cygming_ms_implib_p "$1" ; then
+ # ms-generated import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+ else
+ # unknown
+ sharedlib_from_linklib_result=""
+ fi
+}
+
+
+# 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"
+ if test "$lock_old_archive_extraction" = yes; then
+ lockfile=$f_ex_an_ar_oldlib.lock
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ fi
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+ 'stat=$?; rm -f "$lockfile"; exit $stat'
+ if test "$lock_old_archive_extraction" = yes; then
+ $opt_dry_run || rm -f "$lockfile"
+ fi
+ 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 | sort | $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 | sort | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# 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 variables
+# 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 $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=${1-no}
+
+ $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.
+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
+ file=\"\$0\""
+
+ qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+ $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+ ECHO=\"$qECHO\"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ which is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options which match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=\$0
+ shift
+ for lt_opt
+ do
+ case \"\$lt_opt\" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+ test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+ lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+ cat \"\$lt_dump_D/\$lt_dump_F\"
+ exit 0
+ ;;
+ --lt-*)
+ \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n \"\$lt_option_debug\"; then
+ echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\"
+ lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case \" \$* \" in
+ *\\ --lt-*)
+ for lt_wr_arg
+ do
+ case \$lt_wr_arg in
+ --lt-*) ;;
+ *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core \${1+\"\$@\"}
+}
+
+ # Parse options
+ func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"\$file\" | $SED '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 \"\$file\" | $SED '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 \"\$file\" | $SED '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 \"\$thisdir\" | $SED '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"
+
+ # fixup the dll searchpath if we need to.
+ #
+ # Fix the DLL searchpath if we need to. Do this before prepending
+ # to shlibpath, because on Windows, both are PATH and uninstalled
+ # libraries must come first.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ # 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 \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+ func_exec_program \${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\
+"
+}
+
+
+# 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.
+*/
+EOF
+ cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#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>
+
+/* declarations of non-ANSI functions */
+#if defined(__MINGW32__)
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined(__CYGWIN__)
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined (other platforms) ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined(_MSC_VER)
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+# define S_IXUSR _S_IEXEC
+# ifndef _INTPTR_T_DEFINED
+# define _INTPTR_T_DEFINED
+# define intptr_t int
+# endif
+#elif defined(__MINGW32__)
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+#elif defined(__CYGWIN__)
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined (other platforms) ... */
+#endif
+
+#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
+
+/* path handling portability macros */
+#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 */
+
+#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)
+
+#if defined(LT_DEBUGWRAPPER)
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+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_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+ cat <<EOF
+volatile const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_path "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_path "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test "$fast_install" = yes; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ intptr_t rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ newargz = XMALLOC (char *, argc + 1);
+
+ /* very simple arg parsing; don't want to rely on getopt
+ * also, copy all non cwrapper options to newargz, except
+ * argz[0], which is handled differently
+ */
+ newargc=0;
+ 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"
+ lt_dump_script (stdout);
+ return 0;
+ }
+ if (strcmp (argv[i], debug_opt) == 0)
+ {
+ lt_debug = 1;
+ continue;
+ }
+ if (strcmp (argv[i], ltwrapper_option_prefix) == 0)
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal (__FILE__, __LINE__,
+ "unrecognized %s option: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+EOF
+ cat <<EOF
+ /* The GNU banner must be the first non-error debug message */
+ lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n");
+EOF
+ cat <<"EOF"
+ lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (before symlink chase) at: %s\n",
+ tmp_pathspec);
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (after symlink chase) at: %s\n",
+ actual_cwrapper_path);
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) libtool target name: %s\n",
+ target_name);
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must
+ be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+ because on Windows, both *_VARNAMEs are PATH but uninstalled
+ libraries must come first. */
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+ lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+ nonnull (lt_argv_zero));
+ for (i = 0; i < newargc; i++)
+ {
+ lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+ i, nonnull (newargz[i]));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ newargz = prepare_spawn (newargz);
+ rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) failed to launch target \"%s\": %s\n",
+ lt_argv_zero, nonnull (strerror (errno)));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, 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 (__FILE__, __LINE__, "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;
+
+ lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+ nonempty (path));
+ 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;
+
+ lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+ nonempty (path));
+ 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;
+
+ lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+ nonempty (wrapper));
+
+ 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 (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ 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 (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ 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)
+ {
+ lt_debugprintf (__FILE__, __LINE__,
+ "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
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "error accessing file \"%s\": %s",
+ tmp_pathspec, nonnull (strerror (errno)));
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "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;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ if (lt_debug)
+ {
+ (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+ }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+ int line, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+ va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+ return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+ return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_setenv) setting '%s' to '%s'\n",
+ nonnull (name), nonnull (value));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ int len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ int orig_value_len = strlen (orig_value);
+ int add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ int len = strlen (new_value);
+ while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[len-1] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+EOF
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+ Note that spawn() does not by itself call the command interpreter
+ (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+ ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&v);
+ v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+ }) ? "cmd.exe" : "command.com").
+ Instead it simply concatenates the arguments, separated by ' ', and calls
+ CreateProcess(). We must quote the arguments since Win32 CreateProcess()
+ interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+ special way:
+ - Space and tab are interpreted as delimiters. They are not treated as
+ delimiters if they are surrounded by double quotes: "...".
+ - Unescaped double quotes are removed from the input. Their only effect is
+ that within double quotes, space and tab are treated like normal
+ characters.
+ - Backslashes not followed by double quotes are not special.
+ - But 2*n+1 backslashes followed by a double quote become
+ n backslashes followed by a double quote (n >= 0):
+ \" -> "
+ \\\" -> \"
+ \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+ size_t argc;
+ char **new_argv;
+ size_t i;
+
+ /* Count number of arguments. */
+ for (argc = 0; argv[argc] != NULL; argc++)
+ ;
+
+ /* Allocate new argument vector. */
+ new_argv = XMALLOC (char *, argc + 1);
+
+ /* Put quoted arguments into the new argument vector. */
+ for (i = 0; i < argc; i++)
+ {
+ const char *string = argv[i];
+
+ if (string[0] == '\0')
+ new_argv[i] = xstrdup ("\"\"");
+ else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+ {
+ int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+ size_t length;
+ unsigned int backslashes;
+ const char *s;
+ char *quoted_string;
+ char *p;
+
+ length = 0;
+ backslashes = 0;
+ if (quote_around)
+ length++;
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ length += backslashes + 1;
+ length++;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ length += backslashes + 1;
+
+ quoted_string = XMALLOC (char, length + 1);
+
+ p = quoted_string;
+ backslashes = 0;
+ if (quote_around)
+ *p++ = '"';
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ {
+ unsigned int j;
+ for (j = backslashes + 1; j > 0; j--)
+ *p++ = '\\';
+ }
+ *p++ = c;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ {
+ unsigned int j;
+ for (j = backslashes; j > 0; j--)
+ *p++ = '\\';
+ *p++ = '"';
+ }
+ *p = '\0';
+
+ new_argv[i] = quoted_string;
+ }
+ else
+ new_argv[i] = (char *) string;
+ }
+ new_argv[argc] = NULL;
+
+ return new_argv;
+}
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+ func_emit_wrapper yes |
+ $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/ fputs ("\1", f);/p
+g
+D'
+ cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+ $opt_debug
+ case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+ *import*) : ;;
+ *) false ;;
+ esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $opt_debug
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # 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
+ bindir=
+ 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
+ bindir)
+ bindir="$arg"
+ prev=
+ continue
+ ;;
+ 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
+ func_append dlfiles " $arg"
+ else
+ func_append 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 "*) ;;
+ *) func_append 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
+# func_append 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
+ func_append 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.
+ func_append 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 "*) ;;
+ *) func_append rpath " $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) func_append xrpath " $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds="$arg"
+ prev=
+ continue
+ ;;
+ weak)
+ func_append weak_libs " $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ func_append linker_flags " $qarg"
+ func_append 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
+ ;;
+
+ -bindir)
+ prev=bindir
+ 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"
+ if test -z "$func_stripname_result"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between \`-L' and \`$1'"
+ else
+ func_fatal_error "need path for \`-L' option"
+ fi
+ fi
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_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 "* | *" $arg "*)
+ # Will only happen for absolute or sysroot arguments
+ ;;
+ *)
+ # Preserve sysroot, but never include relative directories
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+ *) func_append deplibs " -L$dir" ;;
+ esac
+ func_append lib_search_path " $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) func_append dllsearchpath ":$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append 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* | *-cegcc* | *-*-haiku*)
+ # 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
+ func_append 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
+ func_append 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|--sysroot)
+ func_append 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|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) func_append 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* | *-cegcc*)
+ # 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_stripname '=' '' "$dir"
+ dir=$lt_sysroot$func_stripname_result
+ ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append 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"
+ func_append arg " $func_quote_for_eval_result"
+ func_append 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"
+ func_append arg " $wl$func_quote_for_eval_result"
+ func_append compiler_flags " $wl$func_quote_for_eval_result"
+ func_append 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"
+ ;;
+
+ # Flags to be passed through unchanged, with rationale:
+ # -64, -mips[0-9] enable 64-bit mode for the SGI compiler
+ # -r[0-9][0-9]* specify processor for the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+ # +DA*, +DD* enable 64-bit mode for the HP compiler
+ # -q* compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+ # -F/path path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* profiling flags for GCC
+ # @file GCC response files
+ # -tp=* Portland pgcc target processor selection
+ # --sysroot=* for sysroot support
+ # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+ -O*|-flto*|-fwhopr*|-fuse-linker-plugin)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ func_append compiler_flags " $arg"
+ continue
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ *.$objext)
+ # A standard object.
+ func_append 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
+ func_append 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.
+ func_append 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.
+ func_append deplibs " $arg"
+ func_append old_deplibs " $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ func_resolve_sysroot "$arg"
+ if test "$prev" = dlfiles; then
+ # This library was specified with -dlopen.
+ func_append dlfiles " $func_resolve_sysroot_result"
+ prev=
+ elif test "$prev" = dlprefiles; then
+ # The library was specified with -dlpreopen.
+ func_append dlprefiles " $func_resolve_sysroot_result"
+ prev=
+ else
+ func_append deplibs " $func_resolve_sysroot_result"
+ 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 \"\${$shlibpath_var}\" \| \$SED \'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"
+ func_to_tool_file "$output_objdir/"
+ tool_output_objdir=$func_to_tool_file_result
+ # 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_preserve_dup_deps ; then
+ case "$libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append 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 "*) func_append specialdeplibs " $pre_post_deps" ;;
+ esac
+ func_append 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=
+ func_resolve_sysroot "$lib"
+ case $lib in
+ *.la) func_source "$func_resolve_sysroot_result" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ func_basename "$deplib"
+ deplib_base=$func_basename_result
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) func_append 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|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append compiler_flags " $deplib"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append 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 "*) ;;
+ * ) func_append 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"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_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"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ *)
+ func_warning "\`-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test "$pass" = link; then
+ func_stripname '-R' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ lib=$func_resolve_sysroot_result
+ ;;
+ *.$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 \"$deplib\"" 2>/dev/null | $SED 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.
+ func_append newdlprefiles " $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append 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 "$inherited_linker_flags" | $SED '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 "*) ;;
+ *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO " $dependency_libs" | $SED '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" && func_append dlfiles " $dlopen"
+ test -n "$dlpreopen" && func_append 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.
+ func_append convenience " $ladir/$objdir/$old_library"
+ func_append 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_preserve_dup_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ if test -n "$old_library" &&
+ { test "$prefer_static_libs" = yes ||
+ test "$prefer_static_libs,$installed" = "built,no"; }; then
+ linklib=$old_library
+ else
+ for l in $old_library $library_names; do
+ linklib="$l"
+ done
+ fi
+ 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.
+ func_append dlprefiles " $lib $dependency_libs"
+ else
+ func_append 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 "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ #func_warning "library \`$lib' was moved."
+ dir="$ladir"
+ absdir="$abs_ladir"
+ libdir="$abs_ladir"
+ else
+ dir="$lt_sysroot$libdir"
+ absdir="$lt_sysroot$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
+ func_append notinst_path " $abs_ladir"
+ else
+ dir="$ladir/$objdir"
+ absdir="$abs_ladir/$objdir"
+ # Remove this search path later
+ func_append 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
+ case "$host" in
+ # special handling for platforms with PE-DLLs.
+ *cygwin* | *mingw* | *cegcc* )
+ # Linker will automatically link against shared library if both
+ # static and shared are present. Therefore, ensure we extract
+ # symbols from the import library if a shared library is present
+ # (otherwise, the dlopen module name will be incorrect). We do
+ # this by putting the import library name into $newdlprefiles.
+ # We recover the dlopen module name by 'saving' the la file
+ # name in a special purpose variable, and (later) extracting the
+ # dlname from the la file.
+ if test -n "$dlname"; then
+ func_tr_sh "$dir/$linklib"
+ eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+ func_append newdlprefiles " $dir/$linklib"
+ else
+ func_append 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" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ fi
+ ;;
+ * )
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ func_append 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" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ func_append newdlprefiles " $dir/$dlname"
+ else
+ func_append newdlprefiles " $dir/$linklib"
+ fi
+ ;;
+ esac
+ 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
+ func_append 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"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_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_preserve_dup_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append 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:"*) ;;
+ *) func_append 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 "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append 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* | *cegcc*)
+ # No point in relinking DLLs because paths are not encoded
+ func_append notinst_deplibs " $lib"
+ need_relink=no
+ ;;
+ *)
+ if test "$installed" = no; then
+ func_append 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 "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append 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* | *cegcc*)
+ 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 "$opt_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$absdir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append 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:"*) ;;
+ *) func_append 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:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test "$linkmode" = prog || test "$opt_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:"*) ;;
+ *) func_append 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
+ [\\/]*)
+ func_append 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 "*) ;;
+ *) func_append xrpath " $temp_xrpath";;
+ esac;;
+ *) func_append temp_deplibs " $libdir";;
+ esac
+ done
+ dependency_libs="$temp_deplibs"
+ fi
+
+ func_append 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"
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result";;
+ *) func_resolve_sysroot "$deplib" ;;
+ esac
+ if $opt_preserve_dup_deps ; then
+ case "$tmp_libs " in
+ *" $func_resolve_sysroot_result "*)
+ func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+ esac
+ fi
+ func_append tmp_libs " $func_resolve_sysroot_result"
+ done
+
+ if test "$link_all_deplibs" != no; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ path=
+ case $deplib in
+ -L*) path="$deplib" ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ deplib=$func_resolve_sysroot_result
+ 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
+ func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+ func_append 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 " $new_inherited_linker_flags" | $SED '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 "*) ;;
+ *) func_append 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 "*) ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append 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
+ func_append 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"
+ func_append 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!"
+ func_append 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
+ # correct linux to gnu/linux during the next big refactor
+ 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|qnx|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) # correct to gnu/linux during the next big refactor
+ 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.
+ func_append 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"
+ func_append libobjs " $symfileobj"
+ test "X$libobjs" = "X " && libobjs=
+
+ if test "$opt_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 | *.gcno)
+ ;;
+ $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
+ func_append 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
+ func_append oldlibs " $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+ # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "$dependency_libs " | $SED "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
+ func_replace_sysroot "$libdir"
+ func_append temp_xrpath " -R$func_replace_sysroot_result"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append 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 "*) ;;
+ *) func_append 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 "*) ;;
+ *) func_append dlprefiles " $lib" ;;
+ esac
+ done
+
+ if test "$build_libtool_libs" = yes; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ func_append 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
+ func_append 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 "*)
+ func_append 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
+ func_append 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
+ ;;
+ *)
+ func_append 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 "*)
+ func_append 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
+ func_append 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
+ ;;
+ *)
+ func_append 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 "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ if test -n "$file_magic_glob"; then
+ libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+ else
+ libnameglob=$libname
+ fi
+ test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ if test "$want_nocaseglob" = yes; then
+ shopt -s nocaseglob
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ $nocaseglob
+ else
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ fi
+ 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 "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ func_append 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.
+ func_append 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 "*)
+ func_append 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 \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ func_append 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.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=""
+ tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; 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 " $tmp_deplibs" | $SED "s,$i,,"`
+ done
+ fi
+ case $tmp_deplibs in
+ *[!\ \ ]*)
+ 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
+ ;;
+ esac
+ ;;
+ 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 " $newdeplibs" | $SED '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 " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO " $deplibs" | $SED '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 "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append 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
+ # Remove ${wl} instances when linking with ld.
+ # FIXME: should test the right _cmds variable.
+ case $archive_cmds in
+ *\$LD\ *) wl= ;;
+ esac
+ if test "$hardcode_into_libs" = yes; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath="$finalize_rpath"
+ test "$opt_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
+ func_replace_sysroot "$libdir"
+ libdir=$func_replace_sysroot_result
+ 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"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append dep_rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append 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 "dep_rpath=\"$hardcode_libdir_flag_spec\""
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append 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 "$opt_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
+ func_append linknames " $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$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"
+ func_append delfiles " $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ 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 cmd1 in $cmds; do
+ IFS="$save_ifs"
+ # Take the normal branch if the nm_file_list_spec branch
+ # doesn't work or if tool conversion is not needed.
+ case $nm_file_list_spec~$to_tool_file_cmd in
+ *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+ try_normal_branch=yes
+ eval cmd=\"$cmd1\"
+ func_len " $cmd"
+ len=$func_len_result
+ ;;
+ *)
+ try_normal_branch=no
+ ;;
+ esac
+ if test "$try_normal_branch" = yes \
+ && { test "$len" -lt "$max_cmd_len" \
+ || test "$max_cmd_len" -le -1; }
+ then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ elif test -n "$nm_file_list_spec"; then
+ func_basename "$output"
+ output_la=$func_basename_result
+ save_libobjs=$libobjs
+ save_output=$output
+ output=${output_objdir}/${output_la}.nm
+ func_to_tool_file "$output"
+ libobjs=$nm_file_list_spec$func_to_tool_file_result
+ func_append delfiles " $output"
+ func_verbose "creating $NM input file list: $output"
+ for obj in $save_libobjs; do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > "$output"
+ eval cmd=\"$cmd1\"
+ func_show_eval "$cmd" 'exit $?'
+ output=$save_output
+ libobjs=$save_libobjs
+ 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 "$include_expsyms" | $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
+ func_append 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 "*) ;;
+ *)
+ func_append 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"
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ func_append 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\"
+ func_append linker_flags " $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test "$opt_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
+ func_basename "$output"
+ output_la=$func_basename_result
+
+ # 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
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ echo ')' >> $output
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$func_to_tool_file_result
+ 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
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+ 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.
+ reload_objs=$objlist
+ eval concat_cmds=\"$reload_cmds\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$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~
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\${concat_cmds}$reload_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+ fi
+ func_append 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 "$opt_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 "$include_expsyms" | $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
+ func_append 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"
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append 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 "$opt_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 "$opt_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 "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ else
+ gentop="$output_objdir/${obj}x"
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # If we're not building shared, we need to use non_pic_objs
+ test "$build_libtool_libs" != yes && libobjs="$non_pic_objects"
+
+ # Create the old-style object.
+ reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $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 " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED '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])
+ func_append compile_command " ${wl}-bind_at_load"
+ func_append finalize_command " ${wl}-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED '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 "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ compile_deplibs="$new_libs"
+
+
+ func_append compile_command " $compile_deplibs"
+ func_append 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 "*) ;;
+ *) func_append 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"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) func_append dllsearchpath ":$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append 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"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append 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 "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$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
+ *cegcc* | *mingw32ce*)
+ # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+ wrappers_required=no
+ ;;
+ *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 "$compile_command" | $SED '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=$?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # 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
+ func_append 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
+ func_append 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 "$link_command" | $SED '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 $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ 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 "$compile_var$compile_command$compile_rpath" | $SED '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 "$link_command" | $SED '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 $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output_objdir/$outputname"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # 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 "$relink_command" | $SED "$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
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $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
+ func_append oldobjs " $symfileobj"
+ fi
+ fi
+ addlibs="$old_convenience"
+ fi
+
+ if test -n "$addlibs"; then
+ gentop="$output_objdir/${outputname}x"
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $addlibs
+ func_append 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"
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append 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"
+ func_append 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"
+ func_append oldobjs " $gentop/$newobj"
+ ;;
+ *) func_append oldobjs " $obj" ;;
+ esac
+ done
+ fi
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+ 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
+ elif test -n "$archiver_list_spec"; then
+ func_verbose "using command file archive linking..."
+ for obj in $oldobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > $output_objdir/$libname.libcmd
+ func_to_tool_file "$output_objdir/$libname.libcmd"
+ oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+ 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 "$relink_command" | $SED "$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"
+ func_resolve_sysroot "$deplib"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ -L*)
+ func_stripname -L '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -L$func_replace_sysroot_result"
+ ;;
+ -R*)
+ func_stripname -R '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -R$func_replace_sysroot_result"
+ ;;
+ *) func_append newdependency_libs " $deplib" ;;
+ 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"
+ func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ *) func_append 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"
+ func_append newdlprefiles " ${lt_sysroot:+=}$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
+ func_append newdlfiles " $abs"
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlprefiles " $abs"
+ done
+ dlprefiles="$newdlprefiles"
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ # In fact, it would be nice if we could use this code for all target
+ # systems that can't hard-code library paths into their executables
+ # and that have no shared library path variable independent of PATH,
+ # but it turns out we can't easily determine that from inspecting
+ # libtool variables, so we have to hard-code the OSs to which it
+ # applies here; at the moment, that means platforms that use the PE
+ # object format with DLL files. See the long comment at the top of
+ # tests/bindir.at for full details.
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ # If a -bindir argument was supplied, place the dll there.
+ if test "x$bindir" != x ;
+ then
+ func_relative_path "$install_libdir" "$bindir"
+ tdlname=$func_relative_path_result$dlname
+ else
+ # Otherwise fall back on heuristic.
+ tdlname=../bin/$dlname
+ fi
+ ;;
+ esac
+ $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 "$opt_mode" = link || test "$opt_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) func_append RM " $arg"; rmforce=yes ;;
+ -*) func_append RM " $arg" ;;
+ *) func_append files " $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ if test "X$dir" = X.; then
+ odir="$objdir"
+ else
+ odir="$dir/$objdir"
+ fi
+ func_basename "$file"
+ name="$func_basename_result"
+ test "$opt_mode" = uninstall && odir="$dir"
+
+ # Remember odir for removal later, being careful to avoid duplicates
+ if test "$opt_mode" = clean; then
+ case " $rmdirs " in
+ *" $odir "*) ;;
+ *) func_append rmdirs " $odir" ;;
+ 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
+ func_append rmfiles " $odir/$n"
+ done
+ test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+ case "$opt_mode" in
+ clean)
+ case " $library_names " in
+ *" $dlname "*) ;;
+ *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+ esac
+ test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${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
+ func_append 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
+ func_append rmfiles " $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test "$opt_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
+ func_append 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
+ func_append 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
+ func_append rmfiles " $odir/$name $odir/${name}S.${objext}"
+ if test "$fast_install" = yes && test -n "$relink_command"; then
+ func_append rmfiles " $odir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name" ; then
+ func_append rmfiles " $odir/lt-${noexename}.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+
+ # 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 "$opt_mode" = uninstall || test "$opt_mode" = clean; } &&
+ func_mode_uninstall ${1+"$@"}
+
+test -z "$opt_mode" && {
+ help="$generic_help"
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode \`$opt_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/autoconf/missing b/autoconf/missing
new file mode 100755
index 0000000..86a8fc3
--- /dev/null
+++ b/autoconf/missing
@@ -0,0 +1,331 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2012-01-06.13; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program 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, 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, see <http://www.gnu.org/licenses/>.
+
+# 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.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ autom4te touch the output file, or create a stub one
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+# Now exit if we have it, but it failed. Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program). This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+ lex*|yacc*)
+ # Not GNU programs, they don't have --version.
+ ;;
+
+ *)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ # Could not run --version or --help. This is probably someone
+ # running `$TOOL --version' or `$TOOL --help' to check whether
+ # $TOOL exists and not knowing $TOOL uses missing.
+ exit 1
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case $f in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te*)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison*|yacc*)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if test $# -ne 1; then
+ eval LASTARG=\${$#}
+ case $LASTARG in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f y.tab.h; then
+ echo >y.tab.h
+ fi
+ if test ! -f y.tab.c; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex*|flex*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if test $# -ne 1; then
+ eval LASTARG=\${$#}
+ case $LASTARG in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f lex.yy.c; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit $?
+ fi
+ ;;
+
+ makeinfo*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ # The file to touch is that specified with -o ...
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -z "$file"; then
+ # ... or it is the one specified with @setfilename ...
+ infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '
+ /^@setfilename/{
+ s/.* \([^ ]*\) *$/\1/
+ p
+ q
+ }' $infile`
+ # ... or it is derived from the source name (dir/f.texi becomes f.info)
+ test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+ fi
+ # If the file does not exist, the user really needs makeinfo;
+ # let's fail without touching anything.
+ test -f $file || exit 1
+ touch $file
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/autoconf/tea/Makefile.in b/autoconf/tea/Makefile.in
new file mode 100644
index 0000000..08b1a44
--- /dev/null
+++ b/autoconf/tea/Makefile.in
@@ -0,0 +1,439 @@
+# Makefile.in --
+#
+# This file is a Makefile for Sample TEA Extension. If it has the name
+# "Makefile.in" then it is a template for a Makefile; to generate the
+# actual Makefile, run "./configure", which is a configuration script
+# generated by the "autoconf" program (constructs like "@foo@" will get
+# replaced in the actual Makefile.
+#
+# Copyright (c) 1999 Scriptics Corporation.
+# Copyright (c) 2002-2005 ActiveState Corporation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: Makefile.in,v 1.59 2005/07/26 19:17:02 mdejong Exp $
+
+#========================================================================
+# Add additional lines to handle any additional AC_SUBST cases that
+# have been added in a customized configure script.
+#========================================================================
+
+#SAMPLE_NEW_VAR = @SAMPLE_NEW_VAR@
+
+#========================================================================
+# Nothing of the variables below this line should need to be changed.
+# Please check the TARGETS section below to make sure the make targets
+# are correct.
+#========================================================================
+
+#========================================================================
+# The names of the source files is defined in the configure script.
+# The object files are used for linking into the final library.
+# This will be used when a dist target is added to the Makefile.
+# It is not important to specify the directory, as long as it is the
+# $(srcdir) or in the generic, win or unix subdirectory.
+#========================================================================
+
+PKG_SOURCES = @PKG_SOURCES@
+PKG_OBJECTS = @PKG_OBJECTS@
+
+PKG_STUB_SOURCES = @PKG_STUB_SOURCES@
+PKG_STUB_OBJECTS = @PKG_STUB_OBJECTS@
+
+#========================================================================
+# PKG_TCL_SOURCES identifies Tcl runtime files that are associated with
+# this package that need to be installed, if any.
+#========================================================================
+
+PKG_TCL_SOURCES = @PKG_TCL_SOURCES@
+
+#========================================================================
+# This is a list of public header files to be installed, if any.
+#========================================================================
+
+PKG_HEADERS = @PKG_HEADERS@
+
+#========================================================================
+# "PKG_LIB_FILE" refers to the library (dynamic or static as per
+# configuration options) composed of the named objects.
+#========================================================================
+
+PKG_LIB_FILE = @PKG_LIB_FILE@
+PKG_STUB_LIB_FILE = @PKG_STUB_LIB_FILE@
+
+lib_BINARIES = $(PKG_LIB_FILE)
+BINARIES = $(lib_BINARIES)
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+libdir = @libdir@
+datadir = @datadir@
+mandir = @mandir@
+includedir = @includedir@
+
+DESTDIR =
+
+PKG_DIR = $(PACKAGE_NAME)$(PACKAGE_VERSION)
+pkgdatadir = $(datadir)/$(PKG_DIR)
+pkglibdir = $(libdir)/$(PKG_DIR)
+pkgincludedir = $(includedir)/$(PKG_DIR)
+
+top_builddir = .
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+CC = @CC@
+CFLAGS_DEFAULT = @CFLAGS_DEFAULT@
+CFLAGS_WARNING = @CFLAGS_WARNING@
+CLEANFILES = @CLEANFILES@
+EXEEXT = @EXEEXT@
+LDFLAGS_DEFAULT = @LDFLAGS_DEFAULT@
+MAKE_LIB = @MAKE_LIB@
+MAKE_SHARED_LIB = @MAKE_SHARED_LIB@
+MAKE_STATIC_LIB = @MAKE_STATIC_LIB@
+MAKE_STUB_LIB = @MAKE_STUB_LIB@
+OBJEXT = @OBJEXT@
+RANLIB = @RANLIB@
+RANLIB_STUB = @RANLIB_STUB@
+SHLIB_CFLAGS = @SHLIB_CFLAGS@
+SHLIB_LD = @SHLIB_LD@
+SHLIB_LD_LIBS = @SHLIB_LD_LIBS@
+STLIB_LD = @STLIB_LD@
+#TCL_DEFS = @TCL_DEFS@
+TCL_BIN_DIR = @TCL_BIN_DIR@
+TCL_SRC_DIR = @TCL_SRC_DIR@
+#TK_BIN_DIR = @TK_BIN_DIR@
+#TK_SRC_DIR = @TK_SRC_DIR@
+
+# This is no longer necessary even for packages that use private Tcl headers
+#TCL_TOP_DIR_NATIVE = @TCL_TOP_DIR_NATIVE@
+# Not used, but retained for reference of what libs Tcl required
+#TCL_LIBS = @TCL_LIBS@
+
+#========================================================================
+# TCLLIBPATH seeds the auto_path in Tcl's init.tcl so we can test our
+# package without installing. The other environment variables allow us
+# to test against an uninstalled Tcl. Add special env vars that you
+# require for testing here (like TCLX_LIBRARY).
+#========================================================================
+
+EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR)
+#EXTRA_PATH = $(top_builddir):$(TCL_BIN_DIR):$(TK_BIN_DIR)
+TCLLIBPATH = $(top_builddir)
+TCLSH_ENV = TCL_LIBRARY=`@CYGPATH@ $(TCL_SRC_DIR)/library` \
+ @LD_LIBRARY_PATH_VAR@="$(EXTRA_PATH):$(@LD_LIBRARY_PATH_VAR@)" \
+ PATH="$(EXTRA_PATH):$(PATH)" \
+ TCLLIBPATH="$(TCLLIBPATH)"
+# TK_LIBRARY=`@CYGPATH@ $(TK_SRC_DIR)/library`
+
+TCLSH_PROG = @TCLSH_PROG@
+TCLSH = $(TCLSH_ENV) $(TCLSH_PROG)
+
+#WISH_PROG = @WISH_PROG@
+#WISH = $(TCLSH_ENV) $(WISH_PROG)
+
+
+SHARED_BUILD = @SHARED_BUILD@
+
+INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ -I$(srcdir)/..
+#INCLUDES = @PKG_INCLUDES@ @TCL_INCLUDES@ @TK_INCLUDES@ @TK_XINCLUDES@
+
+PKG_CFLAGS = @PKG_CFLAGS@
+
+# TCL_DEFS is not strictly need here, but if you remove it, then you
+# must make sure that configure.in checks for the necessary components
+# that your library may use. TCL_DEFS can actually be a problem if
+# you do not compile with a similar machine setup as the Tcl core was
+# compiled with.
+#DEFS = $(TCL_DEFS) @DEFS@ $(PKG_CFLAGS)
+DEFS = @DEFS@ $(PKG_CFLAGS)
+
+CONFIG_CLEAN_FILES = Makefile pkgIndex.tcl
+
+CPPFLAGS = @CPPFLAGS@
+LIBS = @PKG_LIBS@ @LIBS@
+AR = @AR@
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+#========================================================================
+# Start of user-definable TARGETS section
+#========================================================================
+
+#========================================================================
+# TEA TARGETS. Please note that the "libraries:" target refers to platform
+# independent files, and the "binaries:" target inclues executable programs and
+# platform-dependent libraries. Modify these targets so that they install
+# the various pieces of your package. The make and install rules
+# for the BINARIES that you specified above have already been done.
+#========================================================================
+
+all: binaries libraries doc
+
+#========================================================================
+# The binaries target builds executable programs, Windows .dll's, unix
+# shared/static libraries, and any other platform-dependent files.
+# The list of targets to build for "binaries:" is specified at the top
+# of the Makefile, in the "BINARIES" variable.
+#========================================================================
+
+binaries: $(BINARIES)
+
+libraries:
+
+
+#========================================================================
+# Your doc target should differentiate from doc builds (by the developer)
+# and doc installs (see install-doc), which just install the docs on the
+# end user machine when building from source.
+#========================================================================
+
+doc:
+ @echo "If you have documentation to create, place the commands to"
+ @echo "build the docs in the 'doc:' target. For example:"
+ @echo " xml2nroff sample.xml > sample.n"
+ @echo " xml2html sample.xml > sample.html"
+
+install: all install-binaries install-libraries install-doc
+
+install-binaries: binaries install-lib-binaries install-bin-binaries
+
+#========================================================================
+# This rule installs platform-independent files, such as header files.
+# The list=...; for p in $$list handles the empty list case x-platform.
+#========================================================================
+
+install-libraries: libraries
+ @mkdir -p $(DESTDIR)$(includedir)
+ @echo "Installing header files in $(DESTDIR)$(includedir)"
+ @list='$(PKG_HEADERS)'; for i in $$list; do \
+ echo "Installing $(srcdir)/$$i" ; \
+ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir) ; \
+ done;
+
+#========================================================================
+# Install documentation. Unix manpages should go in the $(mandir)
+# directory.
+#========================================================================
+
+install-doc: doc
+ @mkdir -p $(DESTDIR)$(mandir)/mann
+ @echo "Installing documentation in $(DESTDIR)$(mandir)"
+ @list='$(srcdir)/doc/*.n'; for i in $$list; do \
+ echo "Installing $$i"; \
+ rm -f $(DESTDIR)$(mandir)/mann/`basename $$i`; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/mann ; \
+ done
+
+test: binaries libraries
+ @echo "SQLite TEA distribution does not include tests"
+
+shell: binaries libraries
+ @$(TCLSH) $(SCRIPT)
+
+gdb:
+ $(TCLSH_ENV) gdb $(TCLSH_PROG) $(SCRIPT)
+
+depend:
+
+#========================================================================
+# $(PKG_LIB_FILE) should be listed as part of the BINARIES variable
+# mentioned above. That will ensure that this target is built when you
+# run "make binaries".
+#
+# The $(PKG_OBJECTS) objects are created and linked into the final
+# library. In most cases these object files will correspond to the
+# source files above.
+#========================================================================
+
+$(PKG_LIB_FILE): $(PKG_OBJECTS)
+ -rm -f $(PKG_LIB_FILE)
+ ${MAKE_LIB}
+ $(RANLIB) $(PKG_LIB_FILE)
+
+$(PKG_STUB_LIB_FILE): $(PKG_STUB_OBJECTS)
+ -rm -f $(PKG_STUB_LIB_FILE)
+ ${MAKE_STUB_LIB}
+ $(RANLIB_STUB) $(PKG_STUB_LIB_FILE)
+
+#========================================================================
+# We need to enumerate the list of .c to .o lines here.
+#
+# In the following lines, $(srcdir) refers to the toplevel directory
+# containing your extension. If your sources are in a subdirectory,
+# you will have to modify the paths to reflect this:
+#
+# sample.$(OBJEXT): $(srcdir)/generic/sample.c
+# $(COMPILE) -c `@CYGPATH@ $(srcdir)/generic/sample.c` -o $@
+#
+# Setting the VPATH variable to a list of paths will cause the makefile
+# to look into these paths when resolving .c to .obj dependencies.
+# As necessary, add $(srcdir):$(srcdir)/compat:....
+#========================================================================
+
+VPATH = $(srcdir):$(srcdir)/generic:$(srcdir)/unix:$(srcdir)/win
+
+.c.@OBJEXT@:
+ $(COMPILE) -c `@CYGPATH@ $<` -o $@
+
+#========================================================================
+# Distribution creation
+# You may need to tweak this target to make it work correctly.
+#========================================================================
+
+#COMPRESS = tar cvf $(PKG_DIR).tar $(PKG_DIR); compress $(PKG_DIR).tar
+COMPRESS = gtar zcvf $(PKG_DIR).tar.gz $(PKG_DIR)
+DIST_ROOT = /tmp/dist
+DIST_DIR = $(DIST_ROOT)/$(PKG_DIR)
+
+dist-clean:
+ rm -rf $(DIST_DIR) $(DIST_ROOT)/$(PKG_DIR).tar.*
+
+dist: dist-clean
+ mkdir -p $(DIST_DIR)
+ cp -p $(srcdir)/README* $(srcdir)/license* \
+ $(srcdir)/aclocal.m4 $(srcdir)/configure $(srcdir)/*.in \
+ $(DIST_DIR)/
+ chmod 664 $(DIST_DIR)/Makefile.in $(DIST_DIR)/aclocal.m4
+ chmod 775 $(DIST_DIR)/configure $(DIST_DIR)/configure.in
+
+ for i in $(srcdir)/*.[ch]; do \
+ if [ -f $$i ]; then \
+ cp -p $$i $(DIST_DIR)/ ; \
+ fi; \
+ done;
+
+ mkdir $(DIST_DIR)/tclconfig
+ cp $(srcdir)/tclconfig/install-sh $(srcdir)/tclconfig/tcl.m4 \
+ $(DIST_DIR)/tclconfig/
+ chmod 664 $(DIST_DIR)/tclconfig/tcl.m4
+ chmod +x $(DIST_DIR)/tclconfig/install-sh
+
+ list='demos doc generic library mac tests unix win'; \
+ for p in $$list; do \
+ if test -d $(srcdir)/$$p ; then \
+ mkdir $(DIST_DIR)/$$p; \
+ cp -p $(srcdir)/$$p/*.* $(DIST_DIR)/$$p/; \
+ fi; \
+ done
+
+ (cd $(DIST_ROOT); $(COMPRESS);)
+
+#========================================================================
+# End of user-definable section
+#========================================================================
+
+#========================================================================
+# Don't modify the file to clean here. Instead, set the "CLEANFILES"
+# variable in configure.in
+#========================================================================
+
+clean:
+ -test -z "$(BINARIES)" || rm -f $(BINARIES)
+ -rm -f *.$(OBJEXT) core *.core
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean: clean
+ -rm -f *.tab.c
+ -rm -f $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log config.status
+
+#========================================================================
+# Install binary object libraries. On Windows this includes both .dll and
+# .lib files. Because the .lib files are not explicitly listed anywhere,
+# we need to deduce their existence from the .dll file of the same name.
+# Library files go into the lib directory.
+# In addition, this will generate the pkgIndex.tcl
+# file in the install location (assuming it can find a usable tclsh shell)
+#
+# You should not have to modify this target.
+#========================================================================
+
+install-lib-binaries: binaries
+ @mkdir -p $(DESTDIR)$(pkglibdir)
+ @list='$(lib_BINARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(pkglibdir)/$$p; \
+ stub=`echo $$p|sed -e "s/.*\(stub\).*/\1/"`; \
+ if test "x$$stub" = "xstub"; then \
+ echo " $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p"; \
+ $(RANLIB_STUB) $(DESTDIR)$(pkglibdir)/$$p; \
+ else \
+ echo " $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p"; \
+ $(RANLIB) $(DESTDIR)$(pkglibdir)/$$p; \
+ fi; \
+ ext=`echo $$p|sed -e "s/.*\.//"`; \
+ if test "x$$ext" = "xdll"; then \
+ lib=`basename $$p|sed -e 's/.[^.]*$$//'`.lib; \
+ if test -f $$lib; then \
+ echo " $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib"; \
+ $(INSTALL_DATA) $$lib $(DESTDIR)$(pkglibdir)/$$lib; \
+ fi; \
+ fi; \
+ fi; \
+ done
+ @list='$(PKG_TCL_SOURCES)'; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ destp=`basename $$p`; \
+ echo " Install $$destp $(DESTDIR)$(pkglibdir)/$$destp"; \
+ $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkglibdir)/$$destp; \
+ fi; \
+ done
+ @if test "x$(SHARED_BUILD)" = "x1"; then \
+ echo " Install pkgIndex.tcl $(DESTDIR)$(pkglibdir)"; \
+ $(INSTALL_DATA) pkgIndex.tcl $(DESTDIR)$(pkglibdir); \
+ fi
+
+#========================================================================
+# Install binary executables (e.g. .exe files and dependent .dll files)
+# This is for files that must go in the bin directory (located next to
+# wish and tclsh), like dependent .dll files on Windows.
+#
+# You should not have to modify this target, except to define bin_BINARIES
+# above if necessary.
+#========================================================================
+
+install-bin-binaries: binaries
+ @mkdir -p $(DESTDIR)$(bindir)
+ @list='$(bin_BINARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/$$p; \
+ fi; \
+ done
+
+.SUFFIXES: .c .$(OBJEXT)
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) \
+ && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+uninstall-binaries:
+ list='$(lib_BINARIES)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+ done
+ list='$(PKG_TCL_SOURCES)'; for p in $$list; do \
+ p=`basename $$p`; \
+ rm -f $(DESTDIR)$(pkglibdir)/$$p; \
+ done
+ list='$(bin_BINARIES)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/$$p; \
+ done
+
+.PHONY: all binaries clean depend distclean doc install libraries test
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/autoconf/tea/README b/autoconf/tea/README
new file mode 100644
index 0000000..99dc8b8
--- /dev/null
+++ b/autoconf/tea/README
@@ -0,0 +1,36 @@
+This is the SQLite extension for Tcl using the Tcl Extension
+Architecture (TEA). For additional information on SQLite see
+
+ http://www.sqlite.org/
+
+
+UNIX BUILD
+==========
+
+Building under most UNIX systems is easy, just run the configure script
+and then run make. For more information about the build process, see
+the tcl/unix/README file in the Tcl src dist. The following minimal
+example will install the extension in the /opt/tcl directory.
+
+ $ cd sqlite-*-tea
+ $ ./configure --prefix=/opt/tcl
+ $ make
+ $ make install
+
+WINDOWS BUILD
+=============
+
+The recommended method to build extensions under windows is to use the
+Msys + Mingw build process. This provides a Unix-style build while
+generating native Windows binaries. Using the Msys + Mingw build tools
+means that you can use the same configure script as per the Unix build
+to create a Makefile. See the tcl/win/README file for the URL of
+the Msys + Mingw download.
+
+If you have VC++ then you may wish to use the files in the win
+subdirectory and build the extension using just VC++. These files have
+been designed to be as generic as possible but will require some
+additional maintenance by the project developer to synchronise with
+the TEA configure.in and Makefile.in files. Instructions for using the
+VC++ makefile are written in the first part of the Makefile.vc
+file.
diff --git a/autoconf/tea/aclocal.m4 b/autoconf/tea/aclocal.m4
new file mode 100644
index 0000000..0b05739
--- /dev/null
+++ b/autoconf/tea/aclocal.m4
@@ -0,0 +1,9 @@
+#
+# Include the TEA standard macro set
+#
+
+builtin(include,tclconfig/tcl.m4)
+
+#
+# Add here whatever m4 macros you want to define for your package
+#
diff --git a/autoconf/tea/configure.in b/autoconf/tea/configure.in
new file mode 100644
index 0000000..ec9c565
--- /dev/null
+++ b/autoconf/tea/configure.in
@@ -0,0 +1,200 @@
+#!/bin/bash -norc
+dnl This file is an input file used by the GNU "autoconf" program to
+dnl generate the file "configure", which is run during Tcl installation
+dnl to configure the system for the local environment.
+#
+# RCS: @(#) $Id: configure.in,v 1.43 2005/07/26 19:17:05 mdejong Exp $
+
+#-----------------------------------------------------------------------
+# Sample configure.in for Tcl Extensions. The only places you should
+# need to modify this file are marked by the string __CHANGE__
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
+# __CHANGE__
+# Set your package name and version numbers here.
+#
+# This initializes the environment with PACKAGE_NAME and PACKAGE_VERSION
+# set as provided. These will also be added as -D defs in your Makefile
+# so you can encode the package version directly into the source files.
+#-----------------------------------------------------------------------
+
+AC_INIT([sqlite], [3.7.4])
+
+#--------------------------------------------------------------------
+# Call TEA_INIT as the first TEA_ macro to set up initial vars.
+# This will define a ${TEA_PLATFORM} variable == "unix" or "windows"
+# as well as PKG_LIB_FILE and PKG_STUB_LIB_FILE.
+#--------------------------------------------------------------------
+
+TEA_INIT([3.9])
+
+AC_CONFIG_AUX_DIR(tclconfig)
+
+#--------------------------------------------------------------------
+# Load the tclConfig.sh file
+#--------------------------------------------------------------------
+
+TEA_PATH_TCLCONFIG
+TEA_LOAD_TCLCONFIG
+
+#--------------------------------------------------------------------
+# Load the tkConfig.sh file if necessary (Tk extension)
+#--------------------------------------------------------------------
+
+#TEA_PATH_TKCONFIG
+#TEA_LOAD_TKCONFIG
+
+#-----------------------------------------------------------------------
+# Handle the --prefix=... option by defaulting to what Tcl gave.
+# Must be called after TEA_LOAD_TCLCONFIG and before TEA_SETUP_COMPILER.
+#-----------------------------------------------------------------------
+
+TEA_PREFIX
+
+#-----------------------------------------------------------------------
+# Standard compiler checks.
+# This sets up CC by using the CC env var, or looks for gcc otherwise.
+# This also calls AC_PROG_CC, AC_PROG_INSTALL and a few others to create
+# the basic setup necessary to compile executables.
+#-----------------------------------------------------------------------
+
+TEA_SETUP_COMPILER
+
+#-----------------------------------------------------------------------
+# __CHANGE__
+# Specify the C source files to compile in TEA_ADD_SOURCES,
+# public headers that need to be installed in TEA_ADD_HEADERS,
+# stub library C source files to compile in TEA_ADD_STUB_SOURCES,
+# and runtime Tcl library files in TEA_ADD_TCL_SOURCES.
+# This defines PKG(_STUB)_SOURCES, PKG(_STUB)_OBJECTS, PKG_HEADERS
+# and PKG_TCL_SOURCES.
+#-----------------------------------------------------------------------
+
+TEA_ADD_SOURCES([tclsqlite3.c])
+TEA_ADD_HEADERS([])
+TEA_ADD_INCLUDES([-I\"`\${CYGPATH} \${srcdir}/generic`\"])
+TEA_ADD_LIBS([])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_FTS3=1])
+TEA_ADD_CFLAGS([-DSQLITE_3_SUFFIX_ONLY=1])
+TEA_ADD_CFLAGS([-DSQLITE_ENABLE_RTREE=1])
+TEA_ADD_CFLAGS([-DSQLITE_OMIT_DEPRECATED=1])
+TEA_ADD_STUB_SOURCES([])
+TEA_ADD_TCL_SOURCES([])
+
+#--------------------------------------------------------------------
+# The --with-system-sqlite causes the TCL bindings to SQLite to use
+# the system shared library for SQLite rather than statically linking
+# against its own private copy. This is dangerous and leads to
+# undersirable dependences and is not recommended.
+# Patchs from rmax.
+#--------------------------------------------------------------------
+AC_ARG_WITH([system-sqlite],
+ [AC_HELP_STRING([--with-system-sqlite],
+ [use a system-supplied libsqlite3 instead of the bundled one])],
+ [], [with_system_sqlite=no])
+if test x$with_system_sqlite != xno; then
+ AC_CHECK_HEADER([sqlite3.h],
+ [AC_CHECK_LIB([sqlite3],[sqlite3_initialize],
+ [AC_DEFINE(USE_SYSTEM_SQLITE)
+ LIBS="$LIBS -lsqlite3"])])
+fi
+
+#--------------------------------------------------------------------
+# __CHANGE__
+# Choose which headers you need. Extension authors should try very
+# hard to only rely on the Tcl public header files. Internal headers
+# contain private data structures and are subject to change without
+# notice.
+# This MUST be called after TEA_LOAD_TCLCONFIG / TEA_LOAD_TKCONFIG
+#--------------------------------------------------------------------
+
+TEA_PUBLIC_TCL_HEADERS
+#TEA_PRIVATE_TCL_HEADERS
+
+#TEA_PUBLIC_TK_HEADERS
+#TEA_PRIVATE_TK_HEADERS
+#TEA_PATH_X
+
+#--------------------------------------------------------------------
+# Check whether --enable-threads or --disable-threads was given.
+# This auto-enables if Tcl was compiled threaded.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_THREADS
+if test "${TCL_THREADS}" = "1" ; then
+ AC_DEFINE(SQLITE_THREADSAFE, 1, [Trigger sqlite threadsafe build])
+ # Not automatically added by Tcl because its assumed Tcl links to them,
+ # but it may not if it isn't really a threaded build.
+ TEA_ADD_LIBS([$THREADS_LIBS])
+else
+ AC_DEFINE(SQLITE_THREADSAFE, 0, [Trigger sqlite non-threadsafe build])
+fi
+
+#--------------------------------------------------------------------
+# The statement below defines a collection of symbols related to
+# building as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_SHARED
+
+#--------------------------------------------------------------------
+# This macro figures out what flags to use with the compiler/linker
+# when building shared/static debug/optimized objects. This information
+# can be taken from the tclConfig.sh file, but this figures it all out.
+#--------------------------------------------------------------------
+
+TEA_CONFIG_CFLAGS
+
+#--------------------------------------------------------------------
+# Set the default compiler switches based on the --enable-symbols option.
+#--------------------------------------------------------------------
+
+TEA_ENABLE_SYMBOLS
+
+#--------------------------------------------------------------------
+# Everyone should be linking against the Tcl stub library. If you
+# can't for some reason, remove this definition. If you aren't using
+# stubs, you also need to modify the SHLIB_LD_LIBS setting below to
+# link against the non-stubbed Tcl library. Add Tk too if necessary.
+#--------------------------------------------------------------------
+
+AC_DEFINE(USE_TCL_STUBS, 1, [Use Tcl stubs])
+#AC_DEFINE(USE_TK_STUBS, 1, [Use Tk stubs])
+
+
+#--------------------------------------------------------------------
+# Redefine fdatasync as fsync on systems that lack fdatasync
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(fdatasync, , AC_DEFINE(fdatasync, fsync))
+
+AC_FUNC_STRERROR_R
+
+
+#--------------------------------------------------------------------
+# This macro generates a line to use when building a library. It
+# depends on values set by the TEA_ENABLE_SHARED, TEA_ENABLE_SYMBOLS,
+# and TEA_LOAD_TCLCONFIG macros above.
+#--------------------------------------------------------------------
+
+TEA_MAKE_LIB
+
+#--------------------------------------------------------------------
+# Determine the name of the tclsh and/or wish executables in the
+# Tcl and Tk build directories or the location they were installed
+# into. These paths are used to support running test cases only,
+# the Makefile should not be making use of these paths to generate
+# a pkgIndex.tcl file or anything else at extension build time.
+#--------------------------------------------------------------------
+
+TEA_PROG_TCLSH
+#TEA_PROG_WISH
+
+#--------------------------------------------------------------------
+# Finally, substitute all of the various values into the Makefile.
+# You may alternatively have a special pkgIndex.tcl.in or other files
+# which require substituting th AC variables in. Include these here.
+#--------------------------------------------------------------------
+
+AC_OUTPUT([Makefile pkgIndex.tcl])
diff --git a/autoconf/tea/doc/sqlite3.n b/autoconf/tea/doc/sqlite3.n
new file mode 100644
index 0000000..13913e5
--- /dev/null
+++ b/autoconf/tea/doc/sqlite3.n
@@ -0,0 +1,15 @@
+.TH sqlite3 n 4.1 "Tcl-Extensions"
+.HS sqlite3 tcl
+.BS
+.SH NAME
+sqlite3 \- an interface to the SQLite3 database engine
+.SH SYNOPSIS
+\fBsqlite3\fI command_name ?filename?\fR
+.br
+.SH DESCRIPTION
+SQLite3 is a self-contains, zero-configuration, transactional SQL database
+engine. This extension provides an easy to use interface for accessing
+SQLite database files from Tcl.
+.PP
+For full documentation see \fIhttp://www.sqlite.org/\fR and
+in particular \fIhttp://www.sqlite.org/tclsqlite.html\fR.
diff --git a/autoconf/tea/license.terms b/autoconf/tea/license.terms
new file mode 100644
index 0000000..723c4cd
--- /dev/null
+++ b/autoconf/tea/license.terms
@@ -0,0 +1,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.
diff --git a/autoconf/tea/pkgIndex.tcl.in b/autoconf/tea/pkgIndex.tcl.in
new file mode 100644
index 0000000..bc585f7
--- /dev/null
+++ b/autoconf/tea/pkgIndex.tcl.in
@@ -0,0 +1,7 @@
+#
+# Tcl package index file
+#
+# Note sqlite*3* init specifically
+#
+package ifneeded sqlite3 @PACKAGE_VERSION@ \
+ [list load [file join $dir @PKG_LIB_FILE@] Sqlite3]
diff --git a/autoconf/tea/tclconfig/install-sh b/autoconf/tea/tclconfig/install-sh
new file mode 100644
index 0000000..7c34c3f
--- /dev/null
+++ b/autoconf/tea/tclconfig/install-sh
@@ -0,0 +1,528 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2011-04-20.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# 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.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -S $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -S) stripcmd="$stripprog $2"
+ shift;;
+
+ -t) dst_arg=$2
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ do_exit='(exit $ret); exit $ret'
+ trap "ret=129; $do_exit" 1
+ trap "ret=130; $do_exit" 2
+ trap "ret=141; $do_exit" 13
+ trap "ret=143; $do_exit" 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dst_arg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ -*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # 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 $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/autoconf/tea/tclconfig/tcl.m4 b/autoconf/tea/tclconfig/tcl.m4
new file mode 100644
index 0000000..66214e7
--- /dev/null
+++ b/autoconf/tea/tclconfig/tcl.m4
@@ -0,0 +1,4165 @@
+# tcl.m4 --
+#
+# This file provides a set of autoconf macros to help TEA-enable
+# a Tcl extension.
+#
+# Copyright (c) 1999-2000 Ajuba Solutions.
+# Copyright (c) 2002-2005 ActiveState Corporation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+AC_PREREQ(2.57)
+
+dnl TEA extensions pass us the version of TEA they think they
+dnl are compatible with (must be set in TEA_INIT below)
+dnl TEA_VERSION="3.9"
+
+# Possible values for key variables defined:
+#
+# TEA_WINDOWINGSYSTEM - win32 aqua x11 (mirrors 'tk windowingsystem')
+# TEA_PLATFORM - windows unix
+#
+
+#------------------------------------------------------------------------
+# TEA_PATH_TCLCONFIG --
+#
+# Locate the tclConfig.sh file and perform a sanity check on
+# the Tcl compile flags
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-tcl=...
+#
+# Defines the following vars:
+# TCL_BIN_DIR Full path to the directory containing
+# the tclConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TCLCONFIG], [
+ dnl TEA specific: Make sure we are initialized
+ AC_REQUIRE([TEA_INIT])
+ #
+ # Ok, lets find the tcl configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tcl
+ #
+
+ if test x"${no_tcl}" = x ; then
+ # we reset no_tcl in case something fails here
+ no_tcl=true
+ AC_ARG_WITH(tcl,
+ AC_HELP_STRING([--with-tcl],
+ [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
+ case "${with_tclconfig}" in
+ */tclConfig.sh )
+ if test -f "${with_tclconfig}"; then
+ AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself])
+ with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`"
+ fi ;;
+ esac
+ 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 "${TEA_PLATFORM}" = "windows" \
+ -a -f "$i/win/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ if test -f "$i/unix/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/unix; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # on Darwin, check in Framework installation locations
+ if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+ `ls -d /Library/Frameworks 2>/dev/null` \
+ `ls -d /Network/Library/Frameworks 2>/dev/null` \
+ `ls -d /System/Library/Frameworks 2>/dev/null` \
+ ; do
+ if test -f "$i/Tcl.framework/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/Tcl.framework; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # TEA specific: on Windows, check in common installation locations
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/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 common install locations
+ if test x"${ac_cv_c_tclconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 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` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ `ls -d /usr/lib/tcl8.6 2>/dev/null` \
+ `ls -d /usr/lib/tcl8.5 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 "${TEA_PLATFORM}" = "windows" \
+ -a -f "$i/win/tclConfig.sh" ; then
+ ac_cv_c_tclconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ 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
+ TCL_BIN_DIR="# no Tcl configs found"
+ AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh])
+ else
+ no_tcl=
+ TCL_BIN_DIR="${ac_cv_c_tclconfig}"
+ AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_TKCONFIG --
+#
+# Locate the tkConfig.sh file
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-tk=...
+#
+# Defines the following vars:
+# TK_BIN_DIR Full path to the directory containing
+# the tkConfig.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_TKCONFIG], [
+ #
+ # Ok, lets find the tk configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-tk
+ #
+
+ if test x"${no_tk}" = x ; then
+ # we reset no_tk in case something fails here
+ no_tk=true
+ AC_ARG_WITH(tk,
+ AC_HELP_STRING([--with-tk],
+ [directory containing tk configuration (tkConfig.sh)]),
+ with_tkconfig="${withval}")
+ AC_MSG_CHECKING([for Tk configuration])
+ AC_CACHE_VAL(ac_cv_c_tkconfig,[
+
+ # First check to see if --with-tkconfig was specified.
+ if test x"${with_tkconfig}" != x ; then
+ case "${with_tkconfig}" in
+ */tkConfig.sh )
+ if test -f "${with_tkconfig}"; then
+ AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself])
+ with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`"
+ fi ;;
+ esac
+ if test -f "${with_tkconfig}/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`"
+ else
+ AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh])
+ fi
+ fi
+
+ # then check for a private Tk library
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ../tk \
+ `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../tk \
+ `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \
+ ../../../tk \
+ `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a -f "$i/win/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ if test -f "$i/unix/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/unix; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # on Darwin, check in Framework installation locations
+ if test "`uname -s`" = "Darwin" -a x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d ~/Library/Frameworks 2>/dev/null` \
+ `ls -d /Library/Frameworks 2>/dev/null` \
+ `ls -d /Network/Library/Frameworks 2>/dev/null` \
+ `ls -d /System/Library/Frameworks 2>/dev/null` \
+ ; do
+ if test -f "$i/Tk.framework/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/Tk.framework; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 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` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ ; do
+ if test -f "$i/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # TEA specific: on Windows, check in common installation locations
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a x"${ac_cv_c_tkconfig}" = x ; then
+ for i in `ls -d C:/Tcl/lib 2>/dev/null` \
+ `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \
+ ; do
+ if test -f "$i/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i; pwd)`"
+ break
+ fi
+ done
+ fi
+
+ # check in a few other private locations
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ for i in \
+ ${srcdir}/../tk \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do
+ if test "${TEA_PLATFORM}" = "windows" \
+ -a -f "$i/win/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/win; pwd)`"
+ break
+ fi
+ if test -f "$i/unix/tkConfig.sh" ; then
+ ac_cv_c_tkconfig="`(cd $i/unix; pwd)`"
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_tkconfig}" = x ; then
+ TK_BIN_DIR="# no Tk configs found"
+ AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh])
+ else
+ no_tk=
+ TK_BIN_DIR="${ac_cv_c_tkconfig}"
+ AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TCLCONFIG --
+#
+# Load the tclConfig.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# TCL_BIN_DIR
+#
+# Results:
+#
+# Substitutes the following vars:
+# TCL_BIN_DIR
+# TCL_SRC_DIR
+# TCL_LIB_FILE
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TCLCONFIG], [
+ 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([could not find ${TCL_BIN_DIR}/tclConfig.sh])
+ fi
+
+ # eval is required to do the TCL_DBGX substitution
+ eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\""
+ eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\""
+
+ # 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}"
+ elif test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use the libraries
+ # from the framework at the given location so that linking works
+ # against Tcl.framework installed in an arbitrary location.
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ if test -f "${TCL_BIN_DIR}/${TCL_LIB_FILE}"; then
+ for i in "`cd "${TCL_BIN_DIR}"; pwd`" \
+ "`cd "${TCL_BIN_DIR}"/../..; pwd`"; do
+ if test "`basename "$i"`" = "${TCL_LIB_FILE}.framework"; then
+ TCL_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TCL_LIB_FILE}"
+ break
+ fi
+ done
+ fi
+ if test -f "${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"; then
+ TCL_STUB_LIB_SPEC="-L`echo "${TCL_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TCL_STUB_LIB_FLAG}"
+ TCL_STUB_LIB_PATH="${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}"
+ fi
+ ;;
+ esac
+ fi
+
+ # eval is required to do the TCL_DBGX substitution
+ eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\""
+ eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\""
+ 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_PATCH_LEVEL)
+ AC_SUBST(TCL_BIN_DIR)
+ AC_SUBST(TCL_SRC_DIR)
+
+ 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)
+
+ AC_MSG_CHECKING([platform])
+ hold_cc=$CC; CC="$TCL_CC"
+ AC_TRY_COMPILE(,[
+ #ifdef _WIN32
+ #error win32
+ #endif
+ ], TEA_PLATFORM="unix",
+ TEA_PLATFORM="windows"
+ )
+ CC=$hold_cc
+ AC_MSG_RESULT($TEA_PLATFORM)
+
+ # The BUILD_$pkg is to define the correct extern storage class
+ # handling when making this package
+ AC_DEFINE_UNQUOTED(BUILD_${PACKAGE_NAME}, [],
+ [Building extension source?])
+ # Do this here as we have fully defined TEA_PLATFORM now
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ EXEEXT=".exe"
+ CLEANFILES="$CLEANFILES *.lib *.dll *.pdb *.exp"
+ fi
+
+ # TEA specific:
+ AC_SUBST(CLEANFILES)
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(TCL_DEFS)
+ AC_SUBST(TCL_EXTRA_CFLAGS)
+ AC_SUBST(TCL_LD_FLAGS)
+ AC_SUBST(TCL_SHLIB_LD_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_TKCONFIG --
+#
+# Load the tkConfig.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# TK_BIN_DIR
+#
+# Results:
+#
+# Sets the following vars that should be in tkConfig.sh:
+# TK_BIN_DIR
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_TKCONFIG], [
+ AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh])
+
+ if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then
+ AC_MSG_RESULT([loading])
+ . "${TK_BIN_DIR}/tkConfig.sh"
+ else
+ AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh])
+ fi
+
+ # eval is required to do the TK_DBGX substitution
+ eval "TK_LIB_FILE=\"${TK_LIB_FILE}\""
+ eval "TK_STUB_LIB_FILE=\"${TK_STUB_LIB_FILE}\""
+
+ # If the TK_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 TK_LIB_SPEC will be set to the value
+ # of TK_BUILD_LIB_SPEC. An extension should make use of TK_LIB_SPEC
+ # instead of TK_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ if test -f "${TK_BIN_DIR}/Makefile" ; then
+ TK_LIB_SPEC="${TK_BUILD_LIB_SPEC}"
+ TK_STUB_LIB_SPEC="${TK_BUILD_STUB_LIB_SPEC}"
+ TK_STUB_LIB_PATH="${TK_BUILD_STUB_LIB_PATH}"
+ elif test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use the libraries
+ # from the framework at the given location so that linking works
+ # against Tk.framework installed in an arbitrary location.
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ if test -f "${TK_BIN_DIR}/${TK_LIB_FILE}"; then
+ for i in "`cd "${TK_BIN_DIR}"; pwd`" \
+ "`cd "${TK_BIN_DIR}"/../..; pwd`"; do
+ if test "`basename "$i"`" = "${TK_LIB_FILE}.framework"; then
+ TK_LIB_SPEC="-F`dirname "$i" | sed -e 's/ /\\\\ /g'` -framework ${TK_LIB_FILE}"
+ break
+ fi
+ done
+ fi
+ if test -f "${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"; then
+ TK_STUB_LIB_SPEC="-L` echo "${TK_BIN_DIR}" | sed -e 's/ /\\\\ /g'` ${TK_STUB_LIB_FLAG}"
+ TK_STUB_LIB_PATH="${TK_BIN_DIR}/${TK_STUB_LIB_FILE}"
+ fi
+ ;;
+ esac
+ fi
+
+ # eval is required to do the TK_DBGX substitution
+ eval "TK_LIB_FLAG=\"${TK_LIB_FLAG}\""
+ eval "TK_LIB_SPEC=\"${TK_LIB_SPEC}\""
+ eval "TK_STUB_LIB_FLAG=\"${TK_STUB_LIB_FLAG}\""
+ eval "TK_STUB_LIB_SPEC=\"${TK_STUB_LIB_SPEC}\""
+
+ # TEA specific: Ensure windowingsystem is defined
+ if test "${TEA_PLATFORM}" = "unix" ; then
+ case ${TK_DEFS} in
+ *MAC_OSX_TK*)
+ AC_DEFINE(MAC_OSX_TK, 1, [Are we building against Mac OS X TkAqua?])
+ TEA_WINDOWINGSYSTEM="aqua"
+ ;;
+ *)
+ TEA_WINDOWINGSYSTEM="x11"
+ ;;
+ esac
+ elif test "${TEA_PLATFORM}" = "windows" ; then
+ TEA_WINDOWINGSYSTEM="win32"
+ fi
+
+ AC_SUBST(TK_VERSION)
+ AC_SUBST(TK_BIN_DIR)
+ AC_SUBST(TK_SRC_DIR)
+
+ AC_SUBST(TK_LIB_FILE)
+ AC_SUBST(TK_LIB_FLAG)
+ AC_SUBST(TK_LIB_SPEC)
+
+ AC_SUBST(TK_STUB_LIB_FILE)
+ AC_SUBST(TK_STUB_LIB_FLAG)
+ AC_SUBST(TK_STUB_LIB_SPEC)
+
+ # TEA specific:
+ AC_SUBST(TK_LIBS)
+ AC_SUBST(TK_XINCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_TCLSH
+# Determine the fully qualified path name of the tclsh executable
+# in the Tcl build directory or the tclsh installed in a bin
+# directory. This macro will correctly determine the name
+# of the tclsh executable even if tclsh has not yet been
+# built in the build directory. The tclsh found is always
+# associated with a tclConfig.sh file. This tclsh should be used
+# only for running extension test cases. It should never be
+# or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments:
+# none
+#
+# Results:
+# Substitutes the following vars:
+# TCLSH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_TCLSH], [
+ AC_MSG_CHECKING([for tclsh])
+ if test -f "${TCL_BIN_DIR}/Makefile" ; then
+ # tclConfig.sh is in Tcl build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+ else
+ TCLSH_PROG="${TCL_BIN_DIR}/tclsh"
+ fi
+ else
+ # tclConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT}"
+ else
+ TCLSH_PROG="tclsh${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_DBGX}"
+ fi
+ list="`ls -d ${TCL_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${TCLSH_PROG}" ; then
+ REAL_TCL_BIN_DIR="`cd "$i"; pwd`/"
+ break
+ fi
+ done
+ TCLSH_PROG="${REAL_TCL_BIN_DIR}${TCLSH_PROG}"
+ fi
+ AC_MSG_RESULT([${TCLSH_PROG}])
+ AC_SUBST(TCLSH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_PROG_WISH
+# Determine the fully qualified path name of the wish executable
+# in the Tk build directory or the wish installed in a bin
+# directory. This macro will correctly determine the name
+# of the wish executable even if wish has not yet been
+# built in the build directory. The wish found is always
+# associated with a tkConfig.sh file. This wish should be used
+# only for running extension test cases. It should never be
+# or generation of files (like pkgIndex.tcl) at build time.
+#
+# Arguments:
+# none
+#
+# Results:
+# Substitutes the following vars:
+# WISH_PROG
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PROG_WISH], [
+ AC_MSG_CHECKING([for wish])
+ if test -f "${TK_BIN_DIR}/Makefile" ; then
+ # tkConfig.sh is in Tk build directory
+ if test "${TEA_PLATFORM}" = "windows"; then
+ WISH_PROG="${TK_BIN_DIR}/wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+ else
+ WISH_PROG="${TK_BIN_DIR}/wish"
+ fi
+ else
+ # tkConfig.sh is in install location
+ if test "${TEA_PLATFORM}" = "windows"; then
+ WISH_PROG="wish${TK_MAJOR_VERSION}${TK_MINOR_VERSION}${TK_DBGX}${EXEEXT}"
+ else
+ WISH_PROG="wish${TK_MAJOR_VERSION}.${TK_MINOR_VERSION}${TK_DBGX}"
+ fi
+ list="`ls -d ${TK_BIN_DIR}/../bin 2>/dev/null` \
+ `ls -d ${TK_BIN_DIR}/.. 2>/dev/null` \
+ `ls -d ${TK_PREFIX}/bin 2>/dev/null`"
+ for i in $list ; do
+ if test -f "$i/${WISH_PROG}" ; then
+ REAL_TK_BIN_DIR="`cd "$i"; pwd`/"
+ break
+ fi
+ done
+ WISH_PROG="${REAL_TK_BIN_DIR}${WISH_PROG}"
+ fi
+ AC_MSG_RESULT([${WISH_PROG}])
+ AC_SUBST(WISH_PROG)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SHARED --
+#
+# Allows the building of shared libraries
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-shared=yes|no
+#
+# Defines the following vars:
+# STATIC_BUILD Used for building import/export libraries
+# on Windows.
+#
+# Sets the following vars:
+# SHARED_BUILD Value of 1 or 0
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SHARED], [
+ AC_MSG_CHECKING([how to build libraries])
+ AC_ARG_ENABLE(shared,
+ AC_HELP_STRING([--enable-shared],
+ [build and link with shared libraries (default: on)]),
+ [tcl_ok=$enableval], [tcl_ok=yes])
+
+ if test "${enable_shared+set}" = set; then
+ enableval="$enable_shared"
+ tcl_ok=$enableval
+ else
+ tcl_ok=yes
+ fi
+
+ if test "$tcl_ok" = "yes" ; then
+ AC_MSG_RESULT([shared])
+ SHARED_BUILD=1
+ else
+ AC_MSG_RESULT([static])
+ SHARED_BUILD=0
+ AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?])
+ fi
+ AC_SUBST(SHARED_BUILD)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_THREADS --
+#
+# Specify if thread support should be enabled. If "yes" is specified
+# as an arg (optional), threads are enabled by default, "no" means
+# threads are disabled. "yes" is the default.
+#
+# TCL_THREADS is checked so that if you are compiling an extension
+# against a threaded core, your extension must be compiled threaded
+# as well.
+#
+# Note that it is legal to have a thread enabled extension run in a
+# threaded or non-threaded Tcl core, but a non-threaded extension may
+# only run in a non-threaded Tcl core.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-threads
+#
+# Sets the following vars:
+# THREADS_LIBS Thread library(s)
+#
+# Defines the following vars:
+# TCL_THREADS
+# _REENTRANT
+# _THREAD_SAFE
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_THREADS], [
+ AC_ARG_ENABLE(threads,
+ AC_HELP_STRING([--enable-threads],
+ [build with threads]),
+ [tcl_ok=$enableval], [tcl_ok=yes])
+
+ if test "${enable_threads+set}" = set; then
+ enableval="$enable_threads"
+ tcl_ok=$enableval
+ else
+ tcl_ok=yes
+ fi
+
+ if test "$tcl_ok" = "yes" -o "${TCL_THREADS}" = 1; then
+ TCL_THREADS=1
+
+ if test "${TEA_PLATFORM}" != "windows" ; then
+ # We are always OK on Windows, so check what this platform wants:
+
+ # USE_THREAD_ALLOC tells us to try the special thread-based
+ # allocator that significantly reduces lock contention
+ AC_DEFINE(USE_THREAD_ALLOC, 1,
+ [Do we want to use the threaded memory allocator?])
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ if test "`uname -s`" = "SunOS" ; then
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+ fi
+ AC_DEFINE(_THREAD_SAFE, 1, [Do we want the thread-safe OS API?])
+ AC_CHECK_LIB(pthread,pthread_mutex_init,tcl_ok=yes,tcl_ok=no)
+ if test "$tcl_ok" = "no"; then
+ # Check a little harder for __pthread_mutex_init in the same
+ # library, as some systems hide it there until pthread.h is
+ # defined. We could alternatively do an AC_TRY_COMPILE with
+ # pthread.h, but that will work with libpthread really doesn't
+ # exist, like AIX 4.2. [Bug: 4359]
+ AC_CHECK_LIB(pthread, __pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ fi
+
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -lpthread"
+ else
+ AC_CHECK_LIB(pthreads, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -lpthreads"
+ else
+ AC_CHECK_LIB(c, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "no"; then
+ AC_CHECK_LIB(c_r, pthread_mutex_init,
+ tcl_ok=yes, tcl_ok=no)
+ if test "$tcl_ok" = "yes"; then
+ # The space is needed
+ THREADS_LIBS=" -pthread"
+ else
+ TCL_THREADS=0
+ AC_MSG_WARN([Do not know how to find pthread lib on your system - thread support disabled])
+ fi
+ fi
+ fi
+ fi
+ fi
+ else
+ TCL_THREADS=0
+ fi
+ # Do checking message here to not mess up interleaved configure output
+ AC_MSG_CHECKING([for building with threads])
+ if test "${TCL_THREADS}" = 1; then
+ AC_DEFINE(TCL_THREADS, 1, [Are we building with threads enabled?])
+ AC_MSG_RESULT([yes (default)])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ # TCL_THREADS sanity checking. See if our request for building with
+ # threads is the same as the way Tcl was built. If not, warn the user.
+ case ${TCL_DEFS} in
+ *THREADS=1*)
+ if test "${TCL_THREADS}" = "0"; then
+ AC_MSG_WARN([
+ Building ${PACKAGE_NAME} without threads enabled, but building against Tcl
+ that IS thread-enabled. It is recommended to use --enable-threads.])
+ fi
+ ;;
+ *)
+ if test "${TCL_THREADS}" = "1"; then
+ AC_MSG_WARN([
+ --enable-threads requested, but building against a Tcl that is NOT
+ thread-enabled. This is an OK configuration that will also run in
+ a thread-enabled core.])
+ fi
+ ;;
+ esac
+ AC_SUBST(TCL_THREADS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_SYMBOLS --
+#
+# Specify if debugging symbols should be used.
+# Memory (TCL_MEM_DEBUG) debugging can also be enabled.
+#
+# Arguments:
+# none
+#
+# TEA varies from core Tcl in that C|LDFLAGS_DEFAULT receives
+# the value of C|LDFLAGS_OPTIMIZE|DEBUG already substituted.
+# Requires the following vars to be set in the Makefile:
+# CFLAGS_DEFAULT
+# LDFLAGS_DEFAULT
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-symbols
+#
+# Defines the following vars:
+# CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true
+# Sets to "$(CFLAGS_OPTIMIZE) -DNDEBUG" if false
+# LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true
+# Sets to $(LDFLAGS_OPTIMIZE) if false
+# DBGX Formerly used as debug library extension;
+# always blank now.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_SYMBOLS], [
+ dnl TEA specific: Make sure we are initialized
+ AC_REQUIRE([TEA_CONFIG_CFLAGS])
+ AC_MSG_CHECKING([for build with symbols])
+ AC_ARG_ENABLE(symbols,
+ AC_HELP_STRING([--enable-symbols],
+ [build with debugging symbols (default: off)]),
+ [tcl_ok=$enableval], [tcl_ok=no])
+ DBGX=""
+ if test "$tcl_ok" = "no"; then
+ CFLAGS_DEFAULT="${CFLAGS_OPTIMIZE} -DNDEBUG"
+ LDFLAGS_DEFAULT="${LDFLAGS_OPTIMIZE}"
+ AC_MSG_RESULT([no])
+ else
+ CFLAGS_DEFAULT="${CFLAGS_DEBUG}"
+ LDFLAGS_DEFAULT="${LDFLAGS_DEBUG}"
+ if test "$tcl_ok" = "yes"; then
+ AC_MSG_RESULT([yes (standard debugging)])
+ fi
+ fi
+ # TEA specific:
+ if test "${TEA_PLATFORM}" != "windows" ; then
+ LDFLAGS_DEFAULT="${LDFLAGS}"
+ fi
+ AC_SUBST(CFLAGS_DEFAULT)
+ AC_SUBST(LDFLAGS_DEFAULT)
+ AC_SUBST(TCL_DBGX)
+
+ if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then
+ AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?])
+ fi
+
+ if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then
+ if test "$tcl_ok" = "all"; then
+ AC_MSG_RESULT([enabled symbols mem debugging])
+ else
+ AC_MSG_RESULT([enabled $tcl_ok debugging])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_ENABLE_LANGINFO --
+#
+# Allows use of modern nl_langinfo check for better l10n.
+# This is only relevant for Unix.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --enable-langinfo=yes|no (default is yes)
+#
+# Defines the following vars:
+# HAVE_LANGINFO Triggers use of nl_langinfo if defined.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_ENABLE_LANGINFO], [
+ AC_ARG_ENABLE(langinfo,
+ AC_HELP_STRING([--enable-langinfo],
+ [use nl_langinfo if possible to determine encoding at startup, otherwise use old heuristic (default: on)]),
+ [langinfo_ok=$enableval], [langinfo_ok=yes])
+
+ HAVE_LANGINFO=0
+ if test "$langinfo_ok" = "yes"; then
+ AC_CHECK_HEADER(langinfo.h,[langinfo_ok=yes],[langinfo_ok=no])
+ fi
+ AC_MSG_CHECKING([whether to use nl_langinfo])
+ if test "$langinfo_ok" = "yes"; then
+ AC_CACHE_VAL(tcl_cv_langinfo_h, [
+ AC_TRY_COMPILE([#include <langinfo.h>], [nl_langinfo(CODESET);],
+ [tcl_cv_langinfo_h=yes],[tcl_cv_langinfo_h=no])])
+ AC_MSG_RESULT([$tcl_cv_langinfo_h])
+ if test $tcl_cv_langinfo_h = yes; then
+ AC_DEFINE(HAVE_LANGINFO, 1, [Do we have nl_langinfo()?])
+ fi
+ else
+ AC_MSG_RESULT([$langinfo_ok])
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_SYSTEM
+#
+# Determine what the system is (some things cannot be easily checked
+# on a feature-driven basis, alas). This can usually be done via the
+# "uname" command.
+#
+# Arguments:
+# none
+#
+# Results:
+# Defines the following var:
+#
+# system - System/platform/version identification code.
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_SYSTEM], [
+ AC_CACHE_CHECK([system version], tcl_cv_sys_version, [
+ # TEA specific:
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ tcl_cv_sys_version=windows
+ else
+ tcl_cv_sys_version=`uname -s`-`uname -r`
+ if test "$?" -ne 0 ; then
+ AC_MSG_WARN([can't find uname command])
+ tcl_cv_sys_version=unknown
+ else
+ if test "`uname -s`" = "AIX" ; then
+ tcl_cv_sys_version=AIX-`uname -v`.`uname -r`
+ fi
+ fi
+ fi
+ ])
+ system=$tcl_cv_sys_version
+])
+
+#--------------------------------------------------------------------
+# TEA_CONFIG_CFLAGS
+#
+# Try to determine the proper flags to pass to the compiler
+# for building shared libraries and other such nonsense.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines and substitutes the following vars:
+#
+# DL_OBJS, DL_LIBS - removed for TEA, only needed by core.
+# LDFLAGS - Flags to pass to the compiler when linking object
+# files into an executable application binary such
+# as tclsh.
+# LD_SEARCH_FLAGS-Flags to pass to ld, such as "-R /usr/local/tcl/lib",
+# that tell the run-time dynamic linker where to look
+# for shared libraries such as libtcl.so. Depends on
+# the variable LIB_RUNTIME_DIR in the Makefile. Could
+# be the same as CC_SEARCH_FLAGS if ${CC} is used to link.
+# CC_SEARCH_FLAGS-Flags to pass to ${CC}, such as "-Wl,-rpath,/usr/local/tcl/lib",
+# that tell the run-time dynamic linker where to look
+# for shared libraries such as libtcl.so. Depends on
+# the variable LIB_RUNTIME_DIR in the Makefile.
+# SHLIB_CFLAGS - Flags to pass to cc when compiling the components
+# of a shared library (may request position-independent
+# code, among other things).
+# SHLIB_LD - Base command to use for combining object files
+# into a shared library.
+# SHLIB_LD_LIBS - Dependent libraries for the linker to scan when
+# creating shared libraries. This symbol typically
+# goes at the end of the "ld" commands that build
+# shared libraries. The value of the symbol defaults to
+# "${LIBS}" if all of the dependent libraries should
+# be specified when creating a shared library. If
+# dependent libraries should not be specified (as on
+# SunOS 4.x, where they cause the link to fail, or in
+# general if Tcl and Tk aren't themselves shared
+# libraries), then this symbol has an empty string
+# as its value.
+# SHLIB_SUFFIX - Suffix to use for the names of dynamically loadable
+# extensions. An empty string means we don't know how
+# to use shared libraries on this platform.
+# LIB_SUFFIX - Specifies everything that comes after the "libfoo"
+# in a static or shared library name, using the $PACKAGE_VERSION variable
+# to put the version in the right place. This is used
+# by platforms that need non-standard library names.
+# Examples: ${PACKAGE_VERSION}.so.1.1 on NetBSD, since it needs
+# to have a version after the .so, and ${PACKAGE_VERSION}.a
+# on AIX, since a shared library needs to have
+# a .a extension whereas shared objects for loadable
+# extensions have a .so extension. Defaults to
+# ${PACKAGE_VERSION}${SHLIB_SUFFIX}.
+# CFLAGS_DEBUG -
+# Flags used when running the compiler in debug mode
+# CFLAGS_OPTIMIZE -
+# Flags used when running the compiler in optimize mode
+# CFLAGS - Additional CFLAGS added as necessary (usually 64-bit)
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_CONFIG_CFLAGS], [
+ dnl TEA specific: Make sure we are initialized
+ AC_REQUIRE([TEA_INIT])
+
+ # Step 0.a: Enable 64 bit support?
+
+ AC_MSG_CHECKING([if 64bit support is requested])
+ AC_ARG_ENABLE(64bit,
+ AC_HELP_STRING([--enable-64bit],
+ [enable 64bit support (default: off)]),
+ [do64bit=$enableval], [do64bit=no])
+ AC_MSG_RESULT([$do64bit])
+
+ # Step 0.b: Enable Solaris 64 bit VIS support?
+
+ AC_MSG_CHECKING([if 64bit Sparc VIS support is requested])
+ AC_ARG_ENABLE(64bit-vis,
+ AC_HELP_STRING([--enable-64bit-vis],
+ [enable 64bit Sparc VIS support (default: off)]),
+ [do64bitVIS=$enableval], [do64bitVIS=no])
+ AC_MSG_RESULT([$do64bitVIS])
+ # Force 64bit on with VIS
+ AS_IF([test "$do64bitVIS" = "yes"], [do64bit=yes])
+
+ # Step 0.c: Check if visibility support is available. Do this here so
+ # that platform specific alternatives can be used below if this fails.
+
+ AC_CACHE_CHECK([if compiler supports visibility "hidden"],
+ tcl_cv_cc_visibility_hidden, [
+ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -Werror"
+ AC_TRY_LINK([
+ extern __attribute__((__visibility__("hidden"))) void f(void);
+ void f(void) {}], [f();], tcl_cv_cc_visibility_hidden=yes,
+ tcl_cv_cc_visibility_hidden=no)
+ CFLAGS=$hold_cflags])
+ AS_IF([test $tcl_cv_cc_visibility_hidden = yes], [
+ AC_DEFINE(MODULE_SCOPE,
+ [extern __attribute__((__visibility__("hidden")))],
+ [Compiler support for module scope symbols])
+ AC_DEFINE(HAVE_HIDDEN, [1], [Compiler support for module scope symbols])
+ ])
+
+ # Step 0.d: Disable -rpath support?
+
+ AC_MSG_CHECKING([if rpath support is requested])
+ AC_ARG_ENABLE(rpath,
+ AC_HELP_STRING([--disable-rpath],
+ [disable rpath support (default: on)]),
+ [doRpath=$enableval], [doRpath=yes])
+ AC_MSG_RESULT([$doRpath])
+
+ # TEA specific: Cross-compiling options for Windows/CE builds?
+
+ AS_IF([test "${TEA_PLATFORM}" = windows], [
+ AC_MSG_CHECKING([if Windows/CE build is requested])
+ AC_ARG_ENABLE(wince,
+ AC_HELP_STRING([--enable-wince],
+ [enable Win/CE support (where applicable)]),
+ [doWince=$enableval], [doWince=no])
+ AC_MSG_RESULT([$doWince])
+ ])
+
+ # Set the variable "system" to hold the name and version number
+ # for the system.
+
+ TEA_CONFIG_SYSTEM
+
+ # Require ranlib early so we can override it in special cases below.
+
+ AC_REQUIRE([AC_PROG_RANLIB])
+
+ # Set configuration options based on system name and version.
+ # This is similar to Tcl's unix/tcl.m4 except that we've added a
+ # "windows" case and removed some core-only vars.
+
+ do64bit_ok=no
+ # default to '{$LIBS}' and set to "" on per-platform necessary basis
+ SHLIB_LD_LIBS='${LIBS}'
+ # When ld needs options to work in 64-bit mode, put them in
+ # LDFLAGS_ARCH so they eventually end up in LDFLAGS even if [load]
+ # is disabled by the user. [Bug 1016796]
+ LDFLAGS_ARCH=""
+ UNSHARED_LIB_SUFFIX=""
+ # TEA specific: use PACKAGE_VERSION instead of VERSION
+ TCL_TRIM_DOTS='`echo ${PACKAGE_VERSION} | tr -d .`'
+ ECHO_VERSION='`echo ${PACKAGE_VERSION}`'
+ TCL_LIB_VERSIONS_OK=ok
+ CFLAGS_DEBUG=-g
+ AS_IF([test "$GCC" = yes], [
+ CFLAGS_OPTIMIZE=-O2
+ CFLAGS_WARNING="-Wall"
+ ], [
+ CFLAGS_OPTIMIZE=-O
+ CFLAGS_WARNING=""
+ ])
+ AC_CHECK_TOOL(AR, ar)
+ STLIB_LD='${AR} cr'
+ LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH"
+ AS_IF([test "x$SHLIB_VERSION" = x],[SHLIB_VERSION="1.0"])
+ case $system in
+ # TEA specific:
+ windows)
+ # This is a 2-stage check to make sure we have the 64-bit SDK
+ # We have to know where the SDK is installed.
+ # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs
+ # MACHINE is IX86 for LINK, but this is used by the manifest,
+ # which requires x86|amd64|ia64.
+ MACHINE="X86"
+ if test "$do64bit" != "no" ; then
+ if test "x${MSSDK}x" = "xx" ; then
+ MSSDK="C:/Progra~1/Microsoft Platform SDK"
+ fi
+ MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'`
+ PATH64=""
+ case "$do64bit" in
+ amd64|x64|yes)
+ MACHINE="AMD64" ; # default to AMD64 64-bit build
+ PATH64="${MSSDK}/Bin/Win64/x86/AMD64"
+ ;;
+ ia64)
+ MACHINE="IA64"
+ PATH64="${MSSDK}/Bin/Win64"
+ ;;
+ esac
+ if test "$GCC" != "yes" -a ! -d "${PATH64}" ; then
+ AC_MSG_WARN([Could not find 64-bit $MACHINE SDK to enable 64bit mode])
+ AC_MSG_WARN([Ensure latest Platform SDK is installed])
+ do64bit="no"
+ else
+ AC_MSG_RESULT([ Using 64-bit $MACHINE mode])
+ do64bit_ok="yes"
+ fi
+ fi
+
+ if test "$doWince" != "no" ; then
+ if test "$do64bit" != "no" ; then
+ AC_MSG_ERROR([Windows/CE and 64-bit builds incompatible])
+ fi
+ if test "$GCC" = "yes" ; then
+ AC_MSG_ERROR([Windows/CE and GCC builds incompatible])
+ fi
+ TEA_PATH_CELIB
+ # Set defaults for common evc4/PPC2003 setup
+ # Currently Tcl requires 300+, possibly 420+ for sockets
+ CEVERSION=420; # could be 211 300 301 400 420 ...
+ TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ...
+ ARCH=ARM; # could be ARM MIPS X86EM ...
+ PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002"
+ if test "$doWince" != "yes"; then
+ # If !yes then the user specified something
+ # Reset ARCH to allow user to skip specifying it
+ ARCH=
+ eval `echo $doWince | awk -F, '{ \
+ if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \
+ if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \
+ if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \
+ if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \
+ if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \
+ }'`
+ if test "x${ARCH}" = "x" ; then
+ ARCH=$TARGETCPU;
+ fi
+ fi
+ OSVERSION=WCE$CEVERSION;
+ if test "x${WCEROOT}" = "x" ; then
+ WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0"
+ if test ! -d "${WCEROOT}" ; then
+ WCEROOT="C:/Program Files/Microsoft eMbedded Tools"
+ fi
+ fi
+ if test "x${SDKROOT}" = "x" ; then
+ SDKROOT="C:/Program Files/Windows CE Tools"
+ if test ! -d "${SDKROOT}" ; then
+ SDKROOT="C:/Windows CE Tools"
+ fi
+ fi
+ WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'`
+ SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'`
+ if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" \
+ -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then
+ AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]])
+ doWince="no"
+ else
+ # We could PATH_NOSPACE these, but that's not important,
+ # as long as we quote them when used.
+ CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include"
+ if test -d "${CEINCLUDE}/${TARGETCPU}" ; then
+ CEINCLUDE="${CEINCLUDE}/${TARGETCPU}"
+ fi
+ CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"
+ fi
+ fi
+
+ if test "$GCC" != "yes" ; then
+ if test "${SHARED_BUILD}" = "0" ; then
+ runtime=-MT
+ else
+ runtime=-MD
+ fi
+
+ if test "$do64bit" != "no" ; then
+ # All this magic is necessary for the Win64 SDK RC1 - hobbs
+ CC="\"${PATH64}/cl.exe\""
+ CFLAGS="${CFLAGS} -I\"${MSSDK}/Include\" -I\"${MSSDK}/Include/crt\" -I\"${MSSDK}/Include/crt/sys\""
+ RC="\"${MSSDK}/bin/rc.exe\""
+ lflags="-nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\""
+ LINKBIN="\"${PATH64}/link.exe\""
+ CFLAGS_DEBUG="-nologo -Zi -Od -W3 ${runtime}d"
+ CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+ # Avoid 'unresolved external symbol __security_cookie'
+ # errors, c.f. http://support.microsoft.com/?id=894573
+ TEA_ADD_LIBS([bufferoverflowU.lib])
+ elif test "$doWince" != "no" ; then
+ CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin"
+ if test "${TARGETCPU}" = "X86"; then
+ CC="\"${CEBINROOT}/cl.exe\""
+ else
+ CC="\"${CEBINROOT}/cl${ARCH}.exe\""
+ fi
+ CFLAGS="$CFLAGS -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\""
+ RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\""
+ arch=`echo ${ARCH} | awk '{print tolower([$]0)}'`
+ defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _WINDOWS"
+ if test "${SHARED_BUILD}" = "1" ; then
+ # Static CE builds require static celib as well
+ defs="${defs} _DLL"
+ fi
+ for i in $defs ; do
+ AC_DEFINE_UNQUOTED($i, 1, [WinCE def ]$i)
+ done
+ AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION, [_WIN32_WCE version])
+ AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION, [UNDER_CE version])
+ CFLAGS_DEBUG="-nologo -Zi -Od"
+ CFLAGS_OPTIMIZE="-nologo -Ox"
+ lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'`
+ lflags="-MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo"
+ LINKBIN="\"${CEBINROOT}/link.exe\""
+ AC_SUBST(CELIB_DIR)
+ else
+ RC="rc"
+ lflags="-nologo"
+ LINKBIN="link"
+ CFLAGS_DEBUG="-nologo -Z7 -Od -W3 -WX ${runtime}d"
+ CFLAGS_OPTIMIZE="-nologo -O2 -W2 ${runtime}"
+ fi
+ fi
+
+ if test "$GCC" = "yes"; then
+ # mingw gcc mode
+ AC_CHECK_TOOL(RC, windres)
+ CFLAGS_DEBUG="-g"
+ CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+ SHLIB_LD='${CC} -shared'
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ LDFLAGS_CONSOLE="-wl,--subsystem,console ${lflags}"
+ LDFLAGS_WINDOW="-wl,--subsystem,windows ${lflags}"
+
+ AC_CACHE_CHECK(for cross-compile version of gcc,
+ ac_cv_cross,
+ AC_TRY_COMPILE([
+ #ifdef _WIN32
+ #error cross-compiler
+ #endif
+ ], [],
+ ac_cv_cross=yes,
+ ac_cv_cross=no)
+ )
+ if test "$ac_cv_cross" = "yes"; then
+ case "$do64bit" in
+ amd64|x64|yes)
+ CC="x86_64-w64-mingw32-gcc"
+ LD="x86_64-w64-mingw32-ld"
+ AR="x86_64-w64-mingw32-ar"
+ RANLIB="x86_64-w64-mingw32-ranlib"
+ RC="x86_64-w64-mingw32-windres"
+ ;;
+ *)
+ CC="i686-w64-mingw32-gcc"
+ LD="i686-w64-mingw32-ld"
+ AR="i686-w64-mingw32-ar"
+ RANLIB="i686-w64-mingw32-ranlib"
+ RC="i686-w64-mingw32-windres"
+ ;;
+ esac
+ fi
+
+ else
+ SHLIB_LD="${LINKBIN} -dll ${lflags}"
+ # link -lib only works when -lib is the first arg
+ STLIB_LD="${LINKBIN} -lib ${lflags}"
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.lib'
+ PATHTYPE=-w
+ # For information on what debugtype is most useful, see:
+ # http://msdn.microsoft.com/library/en-us/dnvc60/html/gendepdebug.asp
+ # and also
+ # http://msdn2.microsoft.com/en-us/library/y0zzbyt4%28VS.80%29.aspx
+ # This essentially turns it all on.
+ LDFLAGS_DEBUG="-debug -debugtype:cv"
+ LDFLAGS_OPTIMIZE="-release"
+ if test "$doWince" != "no" ; then
+ LDFLAGS_CONSOLE="-link ${lflags}"
+ LDFLAGS_WINDOW=${LDFLAGS_CONSOLE}
+ else
+ LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}"
+ LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}"
+ fi
+ fi
+
+ SHLIB_SUFFIX=".dll"
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.dll'
+
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ AIX-*)
+ AS_IF([test "${TCL_THREADS}" = "1" -a "$GCC" != "yes"], [
+ # AIX requires the _r compiler when gcc isn't being used
+ case "${CC}" in
+ *_r|*_r\ *)
+ # ok ...
+ ;;
+ *)
+ # Make sure only first arg gets _r
+ CC=`echo "$CC" | sed -e 's/^\([[^ ]]*\)/\1_r/'`
+ ;;
+ esac
+ AC_MSG_RESULT([Using $CC for compiling with threads])
+ ])
+ LIBS="$LIBS -lc"
+ SHLIB_CFLAGS=""
+ SHLIB_SUFFIX=".so"
+
+ LD_LIBRARY_PATH_VAR="LIBPATH"
+
+ # Check to enable 64-bit flags for compiler/linker
+ AS_IF([test "$do64bit" = yes], [
+ AS_IF([test "$GCC" = yes], [
+ AC_MSG_WARN([64bit mode not supported with GCC on $system])
+ ], [
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -q64"
+ LDFLAGS_ARCH="-q64"
+ RANLIB="${RANLIB} -X64"
+ AR="${AR} -X64"
+ SHLIB_LD_FLAGS="-b64"
+ ])
+ ])
+
+ AS_IF([test "`uname -m`" = ia64], [
+ # AIX-5 uses ELF style dynamic libraries on IA-64, but not PPC
+ SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+ AS_IF([test "$GCC" = yes], [
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ ], [
+ CC_SEARCH_FLAGS='-R${LIB_RUNTIME_DIR}'
+ ])
+ LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ ], [
+ AS_IF([test "$GCC" = yes], [
+ SHLIB_LD='${CC} -shared -Wl,-bexpall'
+ ], [
+ SHLIB_LD="/bin/ld -bhalt:4 -bM:SRE -bexpall -H512 -T512 -bnoentry"
+ LDFLAGS="$LDFLAGS -brtl"
+ ])
+ SHLIB_LD="${SHLIB_LD} ${SHLIB_LD_FLAGS}"
+ CC_SEARCH_FLAGS='-L${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ])
+ ;;
+ BeOS*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD='${CC} -nostart'
+ SHLIB_SUFFIX=".so"
+
+ #-----------------------------------------------------------
+ # Check for inet_ntoa in -lbind, for BeOS (which also needs
+ # -lsocket, even if the network functions are in -lnet which
+ # is always linked to, for compatibility.
+ #-----------------------------------------------------------
+ AC_CHECK_LIB(bind, inet_ntoa, [LIBS="$LIBS -lbind -lsocket"])
+ ;;
+ BSD/OS-4.*)
+ SHLIB_CFLAGS="-export-dynamic -fPIC"
+ SHLIB_LD='${CC} -shared'
+ SHLIB_SUFFIX=".so"
+ LDFLAGS="$LDFLAGS -export-dynamic"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ CYGWIN_*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD='${CC} -shared'
+ SHLIB_SUFFIX=".dll"
+ EXEEXT=".exe"
+ do64bit_ok=yes
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ Haiku*)
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_SUFFIX=".so"
+ SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS}'
+ AC_CHECK_LIB(network, inet_ntoa, [LIBS="$LIBS -lnetwork"])
+ ;;
+ HP-UX-*.11.*)
+ # Use updated header definitions where possible
+ AC_DEFINE(_XOPEN_SOURCE_EXTENDED, 1, [Do we want to use the XOPEN network library?])
+ # TEA specific: Needed by Tcl, but not most extensions
+ #AC_DEFINE(_XOPEN_SOURCE, 1, [Do we want to use the XOPEN network library?])
+ #LIBS="$LIBS -lxnet" # Use the XOPEN network library
+
+ AS_IF([test "`uname -m`" = ia64], [
+ SHLIB_SUFFIX=".so"
+ # Use newer C++ library for C++ extensions
+ #if test "$GCC" != "yes" ; then
+ # CPPFLAGS="-AA"
+ #fi
+ ], [
+ SHLIB_SUFFIX=".sl"
+ ])
+ AC_CHECK_LIB(dld, shl_load, tcl_ok=yes, tcl_ok=no)
+ AS_IF([test "$tcl_ok" = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-E"
+ CC_SEARCH_FLAGS='-Wl,+s,+b,${LIB_RUNTIME_DIR}:.'
+ LD_SEARCH_FLAGS='+s +b ${LIB_RUNTIME_DIR}:.'
+ LD_LIBRARY_PATH_VAR="SHLIB_PATH"
+ ])
+ AS_IF([test "$GCC" = yes], [
+ SHLIB_LD='${CC} -shared'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ], [
+ CFLAGS="$CFLAGS -z"
+ # Users may want PA-RISC 1.1/2.0 portable code - needs HP cc
+ #CFLAGS="$CFLAGS +DAportable"
+ SHLIB_CFLAGS="+z"
+ SHLIB_LD="ld -b"
+ ])
+
+ # Check to enable 64-bit flags for compiler/linker
+ AS_IF([test "$do64bit" = "yes"], [
+ AS_IF([test "$GCC" = yes], [
+ case `${CC} -dumpmachine` in
+ hppa64*)
+ # 64-bit gcc in use. Fix flags for GNU ld.
+ do64bit_ok=yes
+ SHLIB_LD='${CC} -shared'
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ;;
+ *)
+ AC_MSG_WARN([64bit mode not supported with GCC on $system])
+ ;;
+ esac
+ ], [
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS +DD64"
+ LDFLAGS_ARCH="+DD64"
+ ])
+ ]) ;;
+ IRIX-6.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -n32 -shared -rdata_shared"
+ SHLIB_SUFFIX=".so"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+ AS_IF([test "$GCC" = yes], [
+ CFLAGS="$CFLAGS -mabi=n32"
+ LDFLAGS="$LDFLAGS -mabi=n32"
+ ], [
+ case $system in
+ IRIX-6.3)
+ # Use to build 6.2 compatible binaries on 6.3.
+ CFLAGS="$CFLAGS -n32 -D_OLD_TERMIOS"
+ ;;
+ *)
+ CFLAGS="$CFLAGS -n32"
+ ;;
+ esac
+ LDFLAGS="$LDFLAGS -n32"
+ ])
+ ;;
+ IRIX64-6.*)
+ SHLIB_CFLAGS=""
+ SHLIB_LD="ld -n32 -shared -rdata_shared"
+ SHLIB_SUFFIX=".so"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+
+ # Check to enable 64-bit flags for compiler/linker
+
+ AS_IF([test "$do64bit" = yes], [
+ AS_IF([test "$GCC" = yes], [
+ AC_MSG_WARN([64bit mode not supported by gcc])
+ ], [
+ do64bit_ok=yes
+ SHLIB_LD="ld -64 -shared -rdata_shared"
+ CFLAGS="$CFLAGS -64"
+ LDFLAGS_ARCH="-64"
+ ])
+ ])
+ ;;
+ Linux*|GNU*|NetBSD-Debian)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_SUFFIX=".so"
+
+ # TEA specific:
+ CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer"
+
+ # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+ SHLIB_LD='${CC} -shared ${CFLAGS} ${LDFLAGS_DEFAULT}'
+ LDFLAGS="$LDFLAGS -Wl,--export-dynamic"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ AS_IF([test "`uname -m`" = "alpha"], [CFLAGS="$CFLAGS -mieee"])
+ AS_IF([test $do64bit = yes], [
+ AC_CACHE_CHECK([if compiler accepts -m64 flag], tcl_cv_cc_m64, [
+ hold_cflags=$CFLAGS
+ CFLAGS="$CFLAGS -m64"
+ AC_TRY_LINK(,, tcl_cv_cc_m64=yes, tcl_cv_cc_m64=no)
+ CFLAGS=$hold_cflags])
+ AS_IF([test $tcl_cv_cc_m64 = yes], [
+ CFLAGS="$CFLAGS -m64"
+ do64bit_ok=yes
+ ])
+ ])
+
+ # The combo of gcc + glibc has a bug related to inlining of
+ # functions like strtod(). The -fno-builtin flag should address
+ # this problem but it does not work. The -fno-inline flag is kind
+ # of overkill but it works. Disable inlining only when one of the
+ # files in compat/*.c is being linked in.
+
+ AS_IF([test x"${USE_COMPAT}" != x],[CFLAGS="$CFLAGS -fno-inline"])
+ ;;
+ Lynx*)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_SUFFIX=".so"
+ CFLAGS_OPTIMIZE=-02
+ SHLIB_LD='${CC} -shared'
+ LD_FLAGS="-Wl,--export-dynamic"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ ;;
+ OpenBSD-*)
+ arch=`arch -s`
+ case "$arch" in
+ vax)
+ SHLIB_SUFFIX=""
+ SHARED_LIB_SUFFIX=""
+ LDFLAGS=""
+ ;;
+ *)
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+ SHLIB_SUFFIX=".so"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.so.${SHLIB_VERSION}'
+ LDFLAGS="-Wl,-export-dynamic"
+ ;;
+ esac
+ case "$arch" in
+ vax)
+ CFLAGS_OPTIMIZE="-O1"
+ ;;
+ *)
+ CFLAGS_OPTIMIZE="-O2"
+ ;;
+ esac
+ AS_IF([test "${TCL_THREADS}" = "1"], [
+ # On OpenBSD: Compile with -pthread
+ # Don't link with -lpthread
+ LIBS=`echo $LIBS | sed s/-lpthread//`
+ CFLAGS="$CFLAGS -pthread"
+ ])
+ # OpenBSD doesn't do version numbers with dots.
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ NetBSD-*)
+ # NetBSD has ELF and can use 'cc -shared' to build shared libs
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD='${CC} -shared ${SHLIB_CFLAGS}'
+ SHLIB_SUFFIX=".so"
+ LDFLAGS="$LDFLAGS -export-dynamic"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ AS_IF([test "${TCL_THREADS}" = "1"], [
+ # The -pthread needs to go in the CFLAGS, not LIBS
+ LIBS=`echo $LIBS | sed s/-pthread//`
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
+ ])
+ ;;
+ FreeBSD-*)
+ # This configuration from FreeBSD Ports.
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="${CC} -shared"
+ TCL_SHLIB_LD_EXTRAS="-Wl,-soname=\$[@]"
+ SHLIB_SUFFIX=".so"
+ LDFLAGS=""
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'])
+ AS_IF([test "${TCL_THREADS}" = "1"], [
+ # The -pthread needs to go in the LDFLAGS, not LIBS
+ LIBS=`echo $LIBS | sed s/-pthread//`
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ LDFLAGS="$LDFLAGS $PTHREAD_LIBS"])
+ # Version numbers are dot-stripped by system policy.
+ TCL_TRIM_DOTS=`echo ${PACKAGE_VERSION} | tr -d .`
+ UNSHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}.a'
+ SHARED_LIB_SUFFIX='${TCL_TRIM_DOTS}\$\{DBGX\}.so.1'
+ TCL_LIB_VERSIONS_OK=nodots
+ ;;
+ Darwin-*)
+ CFLAGS_OPTIMIZE="-Os"
+ SHLIB_CFLAGS="-fno-common"
+ # To avoid discrepancies between what headers configure sees during
+ # preprocessing tests and compiling tests, move any -isysroot and
+ # -mmacosx-version-min flags from CFLAGS to CPPFLAGS:
+ CPPFLAGS="${CPPFLAGS} `echo " ${CFLAGS}" | \
+ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+ if ([$]i~/^(isysroot|mmacosx-version-min)/) print "-"[$]i}'`"
+ CFLAGS="`echo " ${CFLAGS}" | \
+ awk 'BEGIN {FS=" +-";ORS=" "}; {for (i=2;i<=NF;i++) \
+ if (!([$]i~/^(isysroot|mmacosx-version-min)/)) print "-"[$]i}'`"
+ AS_IF([test $do64bit = yes], [
+ case `arch` in
+ ppc)
+ AC_CACHE_CHECK([if compiler accepts -arch ppc64 flag],
+ tcl_cv_cc_arch_ppc64, [
+ hold_cflags=$CFLAGS
+ CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+ AC_TRY_LINK(,, tcl_cv_cc_arch_ppc64=yes,
+ tcl_cv_cc_arch_ppc64=no)
+ CFLAGS=$hold_cflags])
+ AS_IF([test $tcl_cv_cc_arch_ppc64 = yes], [
+ CFLAGS="$CFLAGS -arch ppc64 -mpowerpc64 -mcpu=G5"
+ do64bit_ok=yes
+ ]);;
+ i386)
+ AC_CACHE_CHECK([if compiler accepts -arch x86_64 flag],
+ tcl_cv_cc_arch_x86_64, [
+ hold_cflags=$CFLAGS
+ CFLAGS="$CFLAGS -arch x86_64"
+ AC_TRY_LINK(,, tcl_cv_cc_arch_x86_64=yes,
+ tcl_cv_cc_arch_x86_64=no)
+ CFLAGS=$hold_cflags])
+ AS_IF([test $tcl_cv_cc_arch_x86_64 = yes], [
+ CFLAGS="$CFLAGS -arch x86_64"
+ do64bit_ok=yes
+ ]);;
+ *)
+ AC_MSG_WARN([Don't know how enable 64-bit on architecture `arch`]);;
+ esac
+ ], [
+ # Check for combined 32-bit and 64-bit fat build
+ AS_IF([echo "$CFLAGS " |grep -E -q -- '-arch (ppc64|x86_64) ' \
+ && echo "$CFLAGS " |grep -E -q -- '-arch (ppc|i386) '], [
+ fat_32_64=yes])
+ ])
+ # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+ SHLIB_LD='${CC} -dynamiclib ${CFLAGS} ${LDFLAGS_DEFAULT}'
+ AC_CACHE_CHECK([if ld accepts -single_module flag], tcl_cv_ld_single_module, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -dynamiclib -Wl,-single_module"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_single_module=yes, tcl_cv_ld_single_module=no)
+ LDFLAGS=$hold_ldflags])
+ AS_IF([test $tcl_cv_ld_single_module = yes], [
+ SHLIB_LD="${SHLIB_LD} -Wl,-single_module"
+ ])
+ # TEA specific: link shlib with current and compatibility version flags
+ vers=`echo ${PACKAGE_VERSION} | sed -e 's/^\([[0-9]]\{1,5\}\)\(\(\.[[0-9]]\{1,3\}\)\{0,2\}\).*$/\1\2/p' -e d`
+ SHLIB_LD="${SHLIB_LD} -current_version ${vers:-0} -compatibility_version ${vers:-0}"
+ SHLIB_SUFFIX=".dylib"
+ # Don't use -prebind when building for Mac OS X 10.4 or later only:
+ AS_IF([test "`echo "${MACOSX_DEPLOYMENT_TARGET}" | awk -F '10\\.' '{print int([$]2)}'`" -lt 4 -a \
+ "`echo "${CPPFLAGS}" | awk -F '-mmacosx-version-min=10\\.' '{print int([$]2)}'`" -lt 4], [
+ LDFLAGS="$LDFLAGS -prebind"])
+ LDFLAGS="$LDFLAGS -headerpad_max_install_names"
+ AC_CACHE_CHECK([if ld accepts -search_paths_first flag],
+ tcl_cv_ld_search_paths_first, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_search_paths_first=yes,
+ tcl_cv_ld_search_paths_first=no)
+ LDFLAGS=$hold_ldflags])
+ AS_IF([test $tcl_cv_ld_search_paths_first = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-search_paths_first"
+ ])
+ AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [
+ AC_DEFINE(MODULE_SCOPE, [__private_extern__],
+ [Compiler support for module scope symbols])
+ tcl_cv_cc_visibility_hidden=yes
+ ])
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH"
+ # TEA specific: for combined 32 & 64 bit fat builds of Tk
+ # extensions, verify that 64-bit build is possible.
+ AS_IF([test "$fat_32_64" = yes && test -n "${TK_BIN_DIR}"], [
+ AS_IF([test "${TEA_WINDOWINGSYSTEM}" = x11], [
+ AC_CACHE_CHECK([for 64-bit X11], tcl_cv_lib_x11_64, [
+ for v in CFLAGS CPPFLAGS LDFLAGS; do
+ eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+ done
+ CPPFLAGS="$CPPFLAGS -I/usr/X11R6/include"
+ LDFLAGS="$LDFLAGS -L/usr/X11R6/lib -lX11"
+ AC_TRY_LINK([#include <X11/Xlib.h>], [XrmInitialize();],
+ tcl_cv_lib_x11_64=yes, tcl_cv_lib_x11_64=no)
+ for v in CFLAGS CPPFLAGS LDFLAGS; do
+ eval $v'="$hold_'$v'"'
+ done])
+ ])
+ AS_IF([test "${TEA_WINDOWINGSYSTEM}" = aqua], [
+ AC_CACHE_CHECK([for 64-bit Tk], tcl_cv_lib_tk_64, [
+ for v in CFLAGS CPPFLAGS LDFLAGS; do
+ eval 'hold_'$v'="$'$v'";'$v'="`echo "$'$v' "|sed -e "s/-arch ppc / /g" -e "s/-arch i386 / /g"`"'
+ done
+ CPPFLAGS="$CPPFLAGS -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 ${TCL_INCLUDES} ${TK_INCLUDES}"
+ LDFLAGS="$LDFLAGS ${TCL_STUB_LIB_SPEC} ${TK_STUB_LIB_SPEC}"
+ AC_TRY_LINK([#include <tk.h>], [Tk_InitStubs(NULL, "", 0);],
+ tcl_cv_lib_tk_64=yes, tcl_cv_lib_tk_64=no)
+ for v in CFLAGS CPPFLAGS LDFLAGS; do
+ eval $v'="$hold_'$v'"'
+ done])
+ ])
+ # remove 64-bit arch flags from CFLAGS et al. if configuration
+ # does not support 64-bit.
+ AS_IF([test "$tcl_cv_lib_tk_64" = no -o "$tcl_cv_lib_x11_64" = no], [
+ AC_MSG_NOTICE([Removing 64-bit architectures from compiler & linker flags])
+ for v in CFLAGS CPPFLAGS LDFLAGS; do
+ eval $v'="`echo "$'$v' "|sed -e "s/-arch ppc64 / /g" -e "s/-arch x86_64 / /g"`"'
+ done])
+ ])
+ ;;
+ OS/390-*)
+ CFLAGS_OPTIMIZE="" # Optimizer is buggy
+ AC_DEFINE(_OE_SOCKETS, 1, # needed in sys/socket.h
+ [Should OS/390 do the right thing with sockets?])
+ ;;
+ OSF1-V*)
+ # Digital OSF/1
+ SHLIB_CFLAGS=""
+ AS_IF([test "$SHARED_BUILD" = 1], [
+ SHLIB_LD='ld -shared -expect_unresolved "*"'
+ ], [
+ SHLIB_LD='ld -non_shared -expect_unresolved "*"'
+ ])
+ SHLIB_SUFFIX=".so"
+ AS_IF([test $doRpath = yes], [
+ CC_SEARCH_FLAGS='-Wl,-rpath,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-rpath ${LIB_RUNTIME_DIR}'])
+ AS_IF([test "$GCC" = yes], [CFLAGS="$CFLAGS -mieee"], [
+ CFLAGS="$CFLAGS -DHAVE_TZSET -std1 -ieee"])
+ # see pthread_intro(3) for pthread support on osf1, k.furukawa
+ AS_IF([test "${TCL_THREADS}" = 1], [
+ CFLAGS="$CFLAGS -DHAVE_PTHREAD_ATTR_SETSTACKSIZE"
+ CFLAGS="$CFLAGS -DTCL_THREAD_STACK_MIN=PTHREAD_STACK_MIN*64"
+ LIBS=`echo $LIBS | sed s/-lpthreads//`
+ AS_IF([test "$GCC" = yes], [
+ LIBS="$LIBS -lpthread -lmach -lexc"
+ ], [
+ CFLAGS="$CFLAGS -pthread"
+ LDFLAGS="$LDFLAGS -pthread"
+ ])
+ ])
+ ;;
+ QNX-6*)
+ # QNX RTP
+ # This may work for all QNX, but it was only reported for v6.
+ SHLIB_CFLAGS="-fPIC"
+ SHLIB_LD="ld -Bshareable -x"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ SCO_SV-3.2*)
+ AS_IF([test "$GCC" = yes], [
+ SHLIB_CFLAGS="-fPIC -melf"
+ LDFLAGS="$LDFLAGS -melf -Wl,-Bexport"
+ ], [
+ SHLIB_CFLAGS="-Kpic -belf"
+ LDFLAGS="$LDFLAGS -belf -Wl,-Bexport"
+ ])
+ SHLIB_LD="ld -G"
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ SunOS-5.[[0-6]])
+ # Careful to not let 5.10+ fall into this case
+
+ # Note: If _REENTRANT isn't defined, then Solaris
+ # won't define thread-safe library routines.
+
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+
+ SHLIB_CFLAGS="-KPIC"
+ SHLIB_SUFFIX=".so"
+ AS_IF([test "$GCC" = yes], [
+ SHLIB_LD='${CC} -shared'
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ], [
+ SHLIB_LD="/usr/ccs/bin/ld -G -z text"
+ CC_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ ])
+ ;;
+ SunOS-5*)
+ # Note: If _REENTRANT isn't defined, then Solaris
+ # won't define thread-safe library routines.
+
+ AC_DEFINE(_REENTRANT, 1, [Do we want the reentrant OS API?])
+ AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1,
+ [Do we really want to follow the standard? Yes we do!])
+
+ SHLIB_CFLAGS="-KPIC"
+
+ # Check to enable 64-bit flags for compiler/linker
+ AS_IF([test "$do64bit" = yes], [
+ arch=`isainfo`
+ AS_IF([test "$arch" = "sparcv9 sparc"], [
+ AS_IF([test "$GCC" = yes], [
+ AS_IF([test "`${CC} -dumpversion | awk -F. '{print [$]1}'`" -lt 3], [
+ AC_MSG_WARN([64bit mode not supported with GCC < 3.2 on $system])
+ ], [
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -m64 -mcpu=v9"
+ LDFLAGS="$LDFLAGS -m64 -mcpu=v9"
+ SHLIB_CFLAGS="-fPIC"
+ ])
+ ], [
+ do64bit_ok=yes
+ AS_IF([test "$do64bitVIS" = yes], [
+ CFLAGS="$CFLAGS -xarch=v9a"
+ LDFLAGS_ARCH="-xarch=v9a"
+ ], [
+ CFLAGS="$CFLAGS -xarch=v9"
+ LDFLAGS_ARCH="-xarch=v9"
+ ])
+ # Solaris 64 uses this as well
+ #LD_LIBRARY_PATH_VAR="LD_LIBRARY_PATH_64"
+ ])
+ ], [AS_IF([test "$arch" = "amd64 i386"], [
+ AS_IF([test "$GCC" = yes], [
+ case $system in
+ SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*)
+ do64bit_ok=yes
+ CFLAGS="$CFLAGS -m64"
+ LDFLAGS="$LDFLAGS -m64";;
+ *)
+ AC_MSG_WARN([64bit mode not supported with GCC on $system]);;
+ esac
+ ], [
+ do64bit_ok=yes
+ case $system in
+ SunOS-5.1[[1-9]]*|SunOS-5.[[2-9]][[0-9]]*)
+ CFLAGS="$CFLAGS -m64"
+ LDFLAGS="$LDFLAGS -m64";;
+ *)
+ CFLAGS="$CFLAGS -xarch=amd64"
+ LDFLAGS="$LDFLAGS -xarch=amd64";;
+ esac
+ ])
+ ], [AC_MSG_WARN([64bit mode not supported for $arch])])])
+ ])
+
+ SHLIB_SUFFIX=".so"
+ AS_IF([test "$GCC" = yes], [
+ SHLIB_LD='${CC} -shared'
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS=${CC_SEARCH_FLAGS}
+ AS_IF([test "$do64bit_ok" = yes], [
+ AS_IF([test "$arch" = "sparcv9 sparc"], [
+ # We need to specify -static-libgcc or we need to
+ # add the path to the sparv9 libgcc.
+ # JH: static-libgcc is necessary for core Tcl, but may
+ # not be necessary for extensions.
+ SHLIB_LD="$SHLIB_LD -m64 -mcpu=v9 -static-libgcc"
+ # for finding sparcv9 libgcc, get the regular libgcc
+ # path, remove so name and append 'sparcv9'
+ #v9gcclibdir="`gcc -print-file-name=libgcc_s.so` | ..."
+ #CC_SEARCH_FLAGS="${CC_SEARCH_FLAGS},-R,$v9gcclibdir"
+ ], [AS_IF([test "$arch" = "amd64 i386"], [
+ # JH: static-libgcc is necessary for core Tcl, but may
+ # not be necessary for extensions.
+ SHLIB_LD="$SHLIB_LD -m64 -static-libgcc"
+ ])])
+ ])
+ ], [
+ case $system in
+ SunOS-5.[[1-9]][[0-9]]*)
+ # TEA specific: use LDFLAGS_DEFAULT instead of LDFLAGS
+ SHLIB_LD='${CC} -G -z text ${LDFLAGS_DEFAULT}';;
+ *)
+ SHLIB_LD='/usr/ccs/bin/ld -G -z text';;
+ esac
+ CC_SEARCH_FLAGS='-Wl,-R,${LIB_RUNTIME_DIR}'
+ LD_SEARCH_FLAGS='-R ${LIB_RUNTIME_DIR}'
+ ])
+ ;;
+ UNIX_SV* | UnixWare-5*)
+ SHLIB_CFLAGS="-KPIC"
+ SHLIB_LD='${CC} -G'
+ SHLIB_LD_LIBS=""
+ SHLIB_SUFFIX=".so"
+ # Some UNIX_SV* systems (unixware 1.1.2 for example) have linkers
+ # that don't grok the -Bexport option. Test that it does.
+ AC_CACHE_CHECK([for ld accepts -Bexport flag], tcl_cv_ld_Bexport, [
+ hold_ldflags=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-Bexport"
+ AC_TRY_LINK(, [int i;], tcl_cv_ld_Bexport=yes, tcl_cv_ld_Bexport=no)
+ LDFLAGS=$hold_ldflags])
+ AS_IF([test $tcl_cv_ld_Bexport = yes], [
+ LDFLAGS="$LDFLAGS -Wl,-Bexport"
+ ])
+ CC_SEARCH_FLAGS=""
+ LD_SEARCH_FLAGS=""
+ ;;
+ esac
+
+ AS_IF([test "$do64bit" = yes -a "$do64bit_ok" = no], [
+ AC_MSG_WARN([64bit support being disabled -- don't know magic for this platform])
+ ])
+
+dnl # Add any CPPFLAGS set in the environment to our CFLAGS, but delay doing so
+dnl # until the end of configure, as configure's compile and link tests use
+dnl # both CPPFLAGS and CFLAGS (unlike our compile and link) but configure's
+dnl # preprocessing tests use only CPPFLAGS.
+ AC_CONFIG_COMMANDS_PRE([CFLAGS="${CFLAGS} ${CPPFLAGS}"; CPPFLAGS=""])
+
+ # Add in the arch flags late to ensure it wasn't removed.
+ # Not necessary in TEA, but this is aligned with core
+ LDFLAGS="$LDFLAGS $LDFLAGS_ARCH"
+
+ # If we're running gcc, then change the C flags for compiling shared
+ # libraries to the right flags for gcc, instead of those for the
+ # standard manufacturer compiler.
+
+ AS_IF([test "$GCC" = yes], [
+ case $system in
+ AIX-*) ;;
+ BSD/OS*) ;;
+ CYGWIN_*|MINGW32_*) ;;
+ IRIX*) ;;
+ NetBSD-*|FreeBSD-*|OpenBSD-*) ;;
+ Darwin-*) ;;
+ SCO_SV-3.2*) ;;
+ windows) ;;
+ *) SHLIB_CFLAGS="-fPIC" ;;
+ esac])
+
+ AS_IF([test "$tcl_cv_cc_visibility_hidden" != yes], [
+ AC_DEFINE(MODULE_SCOPE, [extern],
+ [No Compiler support for module scope symbols])
+ ])
+
+ AS_IF([test "$SHARED_LIB_SUFFIX" = ""], [
+ # TEA specific: use PACKAGE_VERSION instead of VERSION
+ SHARED_LIB_SUFFIX='${PACKAGE_VERSION}${SHLIB_SUFFIX}'])
+ AS_IF([test "$UNSHARED_LIB_SUFFIX" = ""], [
+ # TEA specific: use PACKAGE_VERSION instead of VERSION
+ UNSHARED_LIB_SUFFIX='${PACKAGE_VERSION}.a'])
+
+ if test "${GCC}" = "yes" -a ${SHLIB_SUFFIX} = ".dll"; then
+ AC_CACHE_CHECK(for SEH support in compiler,
+ tcl_cv_seh,
+ AC_TRY_RUN([
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+ int main(int argc, char** argv) {
+ int a, b = 0;
+ __try {
+ a = 666 / b;
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER) {
+ return 0;
+ }
+ return 1;
+ }
+ ],
+ tcl_cv_seh=yes,
+ tcl_cv_seh=no,
+ tcl_cv_seh=no)
+ )
+ if test "$tcl_cv_seh" = "no" ; then
+ AC_DEFINE(HAVE_NO_SEH, 1,
+ [Defined when mingw does not support SEH])
+ fi
+
+ #
+ # Check to see if the excpt.h include file provided contains the
+ # definition for EXCEPTION_DISPOSITION; if not, which is the case
+ # with Cygwin's version as of 2002-04-10, define it to be int,
+ # sufficient for getting the current code to work.
+ #
+ AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files,
+ tcl_cv_eh_disposition,
+ AC_TRY_COMPILE([
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+ ],[
+ EXCEPTION_DISPOSITION x;
+ ],
+ tcl_cv_eh_disposition=yes,
+ tcl_cv_eh_disposition=no)
+ )
+ if test "$tcl_cv_eh_disposition" = "no" ; then
+ AC_DEFINE(EXCEPTION_DISPOSITION, int,
+ [Defined when cygwin/mingw does not support EXCEPTION DISPOSITION])
+ fi
+
+ # Check to see if winnt.h defines CHAR, SHORT, and LONG
+ # even if VOID has already been #defined. The win32api
+ # used by mingw and cygwin is known to do this.
+
+ AC_CACHE_CHECK(for winnt.h that ignores VOID define,
+ tcl_cv_winnt_ignore_void,
+ AC_TRY_COMPILE([
+#define VOID void
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+ ], [
+ CHAR c;
+ SHORT s;
+ LONG l;
+ ],
+ tcl_cv_winnt_ignore_void=yes,
+ tcl_cv_winnt_ignore_void=no)
+ )
+ if test "$tcl_cv_winnt_ignore_void" = "yes" ; then
+ AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1,
+ [Defined when cygwin/mingw ignores VOID define in winnt.h])
+ fi
+ fi
+
+ # See if the compiler supports casting to a union type.
+ # This is used to stop gcc from printing a compiler
+ # warning when initializing a union member.
+
+ AC_CACHE_CHECK(for cast to union support,
+ tcl_cv_cast_to_union,
+ AC_TRY_COMPILE([],
+ [
+ union foo { int i; double d; };
+ union foo f = (union foo) (int) 0;
+ ],
+ tcl_cv_cast_to_union=yes,
+ tcl_cv_cast_to_union=no)
+ )
+ if test "$tcl_cv_cast_to_union" = "yes"; then
+ AC_DEFINE(HAVE_CAST_TO_UNION, 1,
+ [Defined when compiler supports casting to union type.])
+ fi
+
+ AC_SUBST(CFLAGS_DEBUG)
+ AC_SUBST(CFLAGS_OPTIMIZE)
+ AC_SUBST(CFLAGS_WARNING)
+
+ AC_SUBST(STLIB_LD)
+ AC_SUBST(SHLIB_LD)
+
+ AC_SUBST(SHLIB_LD_LIBS)
+ AC_SUBST(SHLIB_CFLAGS)
+
+ AC_SUBST(LD_LIBRARY_PATH_VAR)
+
+ # These must be called after we do the basic CFLAGS checks and
+ # verify any possible 64-bit or similar switches are necessary
+ TEA_TCL_EARLY_FLAGS
+ TEA_TCL_64BIT_FLAGS
+])
+
+#--------------------------------------------------------------------
+# TEA_SERIAL_PORT
+#
+# Determine which interface to use to talk to the serial port.
+# Note that #include lines must begin in leftmost column for
+# some compilers to recognize them as preprocessor directives,
+# and some build environments have stdin not pointing at a
+# pseudo-terminal (usually /dev/null instead.)
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines only one of the following vars:
+# HAVE_SYS_MODEM_H
+# USE_TERMIOS
+# USE_TERMIO
+# USE_SGTTY
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_SERIAL_PORT], [
+ AC_CHECK_HEADERS(sys/modem.h)
+ AC_CACHE_CHECK([termios vs. termio vs. sgtty], tcl_cv_api_serial, [
+ AC_TRY_RUN([
+#include <termios.h>
+
+int main() {
+ struct termios t;
+ if (tcgetattr(0, &t) == 0) {
+ cfsetospeed(&t, 0);
+ t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <termio.h>
+
+int main() {
+ struct termio t;
+ if (ioctl(0, TCGETA, &t) == 0) {
+ t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <sgtty.h>
+
+int main() {
+ struct sgttyb t;
+ if (ioctl(0, TIOCGETP, &t) == 0) {
+ t.sg_ospeed = 0;
+ t.sg_flags |= ODDP | EVENP | RAW;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no ; then
+ AC_TRY_RUN([
+#include <termios.h>
+#include <errno.h>
+
+int main() {
+ struct termios t;
+ if (tcgetattr(0, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ cfsetospeed(&t, 0);
+ t.c_cflag |= PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=termios, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no; then
+ AC_TRY_RUN([
+#include <termio.h>
+#include <errno.h>
+
+int main() {
+ struct termio t;
+ if (ioctl(0, TCGETA, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ t.c_cflag |= CBAUD | PARENB | PARODD | CSIZE | CSTOPB;
+ return 0;
+ }
+ return 1;
+ }], tcl_cv_api_serial=termio, tcl_cv_api_serial=no, tcl_cv_api_serial=no)
+ fi
+ if test $tcl_cv_api_serial = no; then
+ AC_TRY_RUN([
+#include <sgtty.h>
+#include <errno.h>
+
+int main() {
+ struct sgttyb t;
+ if (ioctl(0, TIOCGETP, &t) == 0
+ || errno == ENOTTY || errno == ENXIO || errno == EINVAL) {
+ t.sg_ospeed = 0;
+ t.sg_flags |= ODDP | EVENP | RAW;
+ return 0;
+ }
+ return 1;
+}], tcl_cv_api_serial=sgtty, tcl_cv_api_serial=none, tcl_cv_api_serial=none)
+ fi])
+ case $tcl_cv_api_serial in
+ termios) AC_DEFINE(USE_TERMIOS, 1, [Use the termios API for serial lines]);;
+ termio) AC_DEFINE(USE_TERMIO, 1, [Use the termio API for serial lines]);;
+ sgtty) AC_DEFINE(USE_SGTTY, 1, [Use the sgtty API for serial lines]);;
+ esac
+])
+
+#--------------------------------------------------------------------
+# TEA_MISSING_POSIX_HEADERS
+#
+# Supply substitutes for missing POSIX header files. Special
+# notes:
+# - stdlib.h doesn't define strtol, strtoul, or
+# strtod in some versions of SunOS
+# - some versions of string.h don't declare procedures such
+# as strstr
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# NO_DIRENT_H
+# NO_ERRNO_H
+# NO_VALUES_H
+# HAVE_LIMITS_H or NO_LIMITS_H
+# NO_STDLIB_H
+# NO_STRING_H
+# NO_SYS_WAIT_H
+# NO_DLFCN_H
+# HAVE_SYS_PARAM_H
+#
+# HAVE_STRING_H ?
+#
+# tkUnixPort.h checks for HAVE_LIMITS_H, so do both HAVE and
+# CHECK on limits.h
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_MISSING_POSIX_HEADERS], [
+ AC_CACHE_CHECK([dirent.h], tcl_cv_dirent_h, [
+ AC_TRY_LINK([#include <sys/types.h>
+#include <dirent.h>], [
+#ifndef _POSIX_SOURCE
+# ifdef __Lynx__
+ /*
+ * Generate compilation error to make the test fail: Lynx headers
+ * are only valid if really in the POSIX environment.
+ */
+
+ missing_procedure();
+# endif
+#endif
+DIR *d;
+struct dirent *entryPtr;
+char *p;
+d = opendir("foobar");
+entryPtr = readdir(d);
+p = entryPtr->d_name;
+closedir(d);
+], tcl_cv_dirent_h=yes, tcl_cv_dirent_h=no)])
+
+ if test $tcl_cv_dirent_h = no; then
+ AC_DEFINE(NO_DIRENT_H, 1, [Do we have <dirent.h>?])
+ fi
+
+ # TEA specific:
+ AC_CHECK_HEADER(errno.h, , [AC_DEFINE(NO_ERRNO_H, 1, [Do we have <errno.h>?])])
+ AC_CHECK_HEADER(float.h, , [AC_DEFINE(NO_FLOAT_H, 1, [Do we have <float.h>?])])
+ AC_CHECK_HEADER(values.h, , [AC_DEFINE(NO_VALUES_H, 1, [Do we have <values.h>?])])
+ AC_CHECK_HEADER(limits.h,
+ [AC_DEFINE(HAVE_LIMITS_H, 1, [Do we have <limits.h>?])],
+ [AC_DEFINE(NO_LIMITS_H, 1, [Do we have <limits.h>?])])
+ AC_CHECK_HEADER(stdlib.h, tcl_ok=1, tcl_ok=0)
+ AC_EGREP_HEADER(strtol, stdlib.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strtoul, stdlib.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strtod, stdlib.h, , tcl_ok=0)
+ if test $tcl_ok = 0; then
+ AC_DEFINE(NO_STDLIB_H, 1, [Do we have <stdlib.h>?])
+ fi
+ AC_CHECK_HEADER(string.h, tcl_ok=1, tcl_ok=0)
+ AC_EGREP_HEADER(strstr, string.h, , tcl_ok=0)
+ AC_EGREP_HEADER(strerror, string.h, , tcl_ok=0)
+
+ # See also memmove check below for a place where NO_STRING_H can be
+ # set and why.
+
+ if test $tcl_ok = 0; then
+ AC_DEFINE(NO_STRING_H, 1, [Do we have <string.h>?])
+ fi
+
+ AC_CHECK_HEADER(sys/wait.h, , [AC_DEFINE(NO_SYS_WAIT_H, 1, [Do we have <sys/wait.h>?])])
+ AC_CHECK_HEADER(dlfcn.h, , [AC_DEFINE(NO_DLFCN_H, 1, [Do we have <dlfcn.h>?])])
+
+ # OS/390 lacks sys/param.h (and doesn't need it, by chance).
+ AC_HAVE_HEADERS(sys/param.h)
+])
+
+#--------------------------------------------------------------------
+# TEA_PATH_X
+#
+# Locate the X11 header files and the X11 library archive. Try
+# the ac_path_x macro first, but if it doesn't find the X stuff
+# (e.g. because there's no xmkmf program) then check through
+# a list of possible directories. Under some conditions the
+# autoconf macro will return an include directory that contains
+# no include files, so double-check its result just to be safe.
+#
+# This should be called after TEA_CONFIG_CFLAGS as setting the
+# LIBS line can confuse some configure macro magic.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets the following vars:
+# XINCLUDES
+# XLIBSW
+# PKG_LIBS (appends to)
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_X], [
+ if test "${TEA_WINDOWINGSYSTEM}" = "x11" ; then
+ TEA_PATH_UNIX_X
+ fi
+])
+
+AC_DEFUN([TEA_PATH_UNIX_X], [
+ AC_PATH_X
+ not_really_there=""
+ if test "$no_x" = ""; then
+ if test "$x_includes" = ""; then
+ AC_TRY_CPP([#include <X11/Xlib.h>], , not_really_there="yes")
+ else
+ if test ! -r $x_includes/X11/Xlib.h; then
+ not_really_there="yes"
+ fi
+ fi
+ fi
+ if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
+ AC_MSG_CHECKING([for X11 header files])
+ found_xincludes="no"
+ AC_TRY_CPP([#include <X11/Xlib.h>], found_xincludes="yes", found_xincludes="no")
+ if test "$found_xincludes" = "no"; then
+ dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
+ for i in $dirs ; do
+ if test -r $i/X11/Xlib.h; then
+ AC_MSG_RESULT([$i])
+ XINCLUDES=" -I$i"
+ found_xincludes="yes"
+ break
+ fi
+ done
+ fi
+ else
+ if test "$x_includes" != ""; then
+ XINCLUDES="-I$x_includes"
+ found_xincludes="yes"
+ fi
+ fi
+ if test "$found_xincludes" = "no"; then
+ AC_MSG_RESULT([couldn't find any!])
+ fi
+
+ if test "$no_x" = yes; then
+ AC_MSG_CHECKING([for X11 libraries])
+ XLIBSW=nope
+ dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib"
+ for i in $dirs ; do
+ if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl -o -r $i/libX11.dylib; then
+ AC_MSG_RESULT([$i])
+ XLIBSW="-L$i -lX11"
+ x_libraries="$i"
+ break
+ fi
+ done
+ else
+ if test "$x_libraries" = ""; then
+ XLIBSW=-lX11
+ else
+ XLIBSW="-L$x_libraries -lX11"
+ fi
+ fi
+ if test "$XLIBSW" = nope ; then
+ AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow)
+ fi
+ if test "$XLIBSW" = nope ; then
+ AC_MSG_RESULT([could not find any! Using -lX11.])
+ XLIBSW=-lX11
+ fi
+ # TEA specific:
+ if test x"${XLIBSW}" != x ; then
+ PKG_LIBS="${PKG_LIBS} ${XLIBSW}"
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BLOCKING_STYLE
+#
+# The statements below check for systems where POSIX-style
+# non-blocking I/O (O_NONBLOCK) doesn't work or is unimplemented.
+# On these systems (mostly older ones), use the old BSD-style
+# FIONBIO approach instead.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# HAVE_SYS_IOCTL_H
+# HAVE_SYS_FILIO_H
+# USE_FIONBIO
+# O_NONBLOCK
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BLOCKING_STYLE], [
+ AC_CHECK_HEADERS(sys/ioctl.h)
+ AC_CHECK_HEADERS(sys/filio.h)
+ TEA_CONFIG_SYSTEM
+ AC_MSG_CHECKING([FIONBIO vs. O_NONBLOCK for nonblocking I/O])
+ case $system in
+ OSF*)
+ AC_DEFINE(USE_FIONBIO, 1, [Should we use FIONBIO?])
+ AC_MSG_RESULT([FIONBIO])
+ ;;
+ *)
+ AC_MSG_RESULT([O_NONBLOCK])
+ ;;
+ esac
+])
+
+#--------------------------------------------------------------------
+# TEA_TIME_HANDLER
+#
+# Checks how the system deals with time.h, what time structures
+# are used on the system, and what fields the structures have.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines some of the following vars:
+# USE_DELTA_FOR_TZ
+# HAVE_TM_GMTOFF
+# HAVE_TM_TZADJ
+# HAVE_TIMEZONE_VAR
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TIME_HANDLER], [
+ AC_CHECK_HEADERS(sys/time.h)
+ AC_HEADER_TIME
+ AC_STRUCT_TIMEZONE
+
+ AC_CHECK_FUNCS(gmtime_r localtime_r)
+
+ AC_CACHE_CHECK([tm_tzadj in struct tm], tcl_cv_member_tm_tzadj, [
+ AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_tzadj;],
+ tcl_cv_member_tm_tzadj=yes, tcl_cv_member_tm_tzadj=no)])
+ if test $tcl_cv_member_tm_tzadj = yes ; then
+ AC_DEFINE(HAVE_TM_TZADJ, 1, [Should we use the tm_tzadj field of struct tm?])
+ fi
+
+ AC_CACHE_CHECK([tm_gmtoff in struct tm], tcl_cv_member_tm_gmtoff, [
+ AC_TRY_COMPILE([#include <time.h>], [struct tm tm; tm.tm_gmtoff;],
+ tcl_cv_member_tm_gmtoff=yes, tcl_cv_member_tm_gmtoff=no)])
+ if test $tcl_cv_member_tm_gmtoff = yes ; then
+ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Should we use the tm_gmtoff field of struct tm?])
+ fi
+
+ #
+ # Its important to include time.h in this check, as some systems
+ # (like convex) have timezone functions, etc.
+ #
+ AC_CACHE_CHECK([long timezone variable], tcl_cv_timezone_long, [
+ AC_TRY_COMPILE([#include <time.h>],
+ [extern long timezone;
+ timezone += 1;
+ exit (0);],
+ tcl_cv_timezone_long=yes, tcl_cv_timezone_long=no)])
+ if test $tcl_cv_timezone_long = yes ; then
+ AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+ else
+ #
+ # On some systems (eg IRIX 6.2), timezone is a time_t and not a long.
+ #
+ AC_CACHE_CHECK([time_t timezone variable], tcl_cv_timezone_time, [
+ AC_TRY_COMPILE([#include <time.h>],
+ [extern time_t timezone;
+ timezone += 1;
+ exit (0);],
+ tcl_cv_timezone_time=yes, tcl_cv_timezone_time=no)])
+ if test $tcl_cv_timezone_time = yes ; then
+ AC_DEFINE(HAVE_TIMEZONE_VAR, 1, [Should we use the global timezone variable?])
+ fi
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_BUGGY_STRTOD
+#
+# Under Solaris 2.4, strtod returns the wrong value for the
+# terminating character under some conditions. Check for this
+# and if the problem exists use a substitute procedure
+# "fixstrtod" (provided by Tcl) that corrects the error.
+# Also, on Compaq's Tru64 Unix 5.0,
+# strtod(" ") returns 0.0 instead of a failure to convert.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Might defines some of the following vars:
+# strtod (=fixstrtod)
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_BUGGY_STRTOD], [
+ AC_CHECK_FUNC(strtod, tcl_strtod=1, tcl_strtod=0)
+ if test "$tcl_strtod" = 1; then
+ AC_CACHE_CHECK([for Solaris2.4/Tru64 strtod bugs], tcl_cv_strtod_buggy,[
+ AC_TRY_RUN([
+ extern double strtod();
+ int main() {
+ char *infString="Inf", *nanString="NaN", *spaceString=" ";
+ char *term;
+ double value;
+ value = strtod(infString, &term);
+ if ((term != infString) && (term[-1] == 0)) {
+ exit(1);
+ }
+ value = strtod(nanString, &term);
+ if ((term != nanString) && (term[-1] == 0)) {
+ exit(1);
+ }
+ value = strtod(spaceString, &term);
+ if (term == (spaceString+1)) {
+ exit(1);
+ }
+ exit(0);
+ }], tcl_cv_strtod_buggy=ok, tcl_cv_strtod_buggy=buggy,
+ tcl_cv_strtod_buggy=buggy)])
+ if test "$tcl_cv_strtod_buggy" = buggy; then
+ AC_LIBOBJ([fixstrtod])
+ USE_COMPAT=1
+ AC_DEFINE(strtod, fixstrtod, [Do we want to use the strtod() in compat?])
+ fi
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_LINK_LIBS
+#
+# Search for the libraries needed to link the Tcl shell.
+# Things like the math library (-lm) and socket stuff (-lsocket vs.
+# -lnsl) are dealt with here.
+#
+# Arguments:
+# Requires the following vars to be set in the Makefile:
+# DL_LIBS (not in TEA, only needed in core)
+# LIBS
+# MATH_LIBS
+#
+# Results:
+#
+# Substitutes the following vars:
+# TCL_LIBS
+# MATH_LIBS
+#
+# Might append to the following vars:
+# LIBS
+#
+# Might define the following vars:
+# HAVE_NET_ERRNO_H
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_LINK_LIBS], [
+ #--------------------------------------------------------------------
+ # On a few very rare systems, all of the libm.a stuff is
+ # already in libc.a. Set compiler flags accordingly.
+ # Also, Linux requires the "ieee" library for math to work
+ # right (and it must appear before "-lm").
+ #--------------------------------------------------------------------
+
+ AC_CHECK_FUNC(sin, MATH_LIBS="", MATH_LIBS="-lm")
+ AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+ #--------------------------------------------------------------------
+ # Interactive UNIX requires -linet instead of -lsocket, plus it
+ # needs net/errno.h to define the socket-related error codes.
+ #--------------------------------------------------------------------
+
+ AC_CHECK_LIB(inet, main, [LIBS="$LIBS -linet"])
+ AC_CHECK_HEADER(net/errno.h, [
+ AC_DEFINE(HAVE_NET_ERRNO_H, 1, [Do we have <net/errno.h>?])])
+
+ #--------------------------------------------------------------------
+ # Check for the existence of the -lsocket and -lnsl libraries.
+ # The order here is important, so that they end up in the right
+ # order in the command line generated by make. Here are some
+ # special considerations:
+ # 1. Use "connect" and "accept" to check for -lsocket, and
+ # "gethostbyname" to check for -lnsl.
+ # 2. Use each function name only once: can't redo a check because
+ # autoconf caches the results of the last check and won't redo it.
+ # 3. Use -lnsl and -lsocket only if they supply procedures that
+ # aren't already present in the normal libraries. This is because
+ # IRIX 5.2 has libraries, but they aren't needed and they're
+ # bogus: they goof up name resolution if used.
+ # 4. On some SVR4 systems, can't use -lsocket without -lnsl too.
+ # To get around this problem, check for both libraries together
+ # if -lsocket doesn't work by itself.
+ #--------------------------------------------------------------------
+
+ tcl_checkBoth=0
+ AC_CHECK_FUNC(connect, tcl_checkSocket=0, tcl_checkSocket=1)
+ if test "$tcl_checkSocket" = 1; then
+ AC_CHECK_FUNC(setsockopt, , [AC_CHECK_LIB(socket, setsockopt,
+ LIBS="$LIBS -lsocket", tcl_checkBoth=1)])
+ fi
+ if test "$tcl_checkBoth" = 1; then
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lsocket -lnsl"
+ AC_CHECK_FUNC(accept, tcl_checkNsl=0, [LIBS=$tk_oldLibs])
+ fi
+ AC_CHECK_FUNC(gethostbyname, , [AC_CHECK_LIB(nsl, gethostbyname,
+ [LIBS="$LIBS -lnsl"])])
+
+ # TEA specific: Don't perform the eval of the libraries here because
+ # DL_LIBS won't be set until we call TEA_CONFIG_CFLAGS
+
+ TCL_LIBS='${DL_LIBS} ${LIBS} ${MATH_LIBS}'
+ AC_SUBST(TCL_LIBS)
+ AC_SUBST(MATH_LIBS)
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_EARLY_FLAGS
+#
+# Check for what flags are needed to be passed so the correct OS
+# features are available.
+#
+# Arguments:
+# None
+#
+# Results:
+#
+# Might define the following vars:
+# _ISOC99_SOURCE
+# _LARGEFILE64_SOURCE
+# _LARGEFILE_SOURCE64
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_EARLY_FLAG],[
+ AC_CACHE_VAL([tcl_cv_flag_]translit($1,[A-Z],[a-z]),
+ AC_TRY_COMPILE([$2], $3, [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no,
+ AC_TRY_COMPILE([[#define ]$1[ 1
+]$2], $3,
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=yes,
+ [tcl_cv_flag_]translit($1,[A-Z],[a-z])=no)))
+ if test ["x${tcl_cv_flag_]translit($1,[A-Z],[a-z])[}" = "xyes"] ; then
+ AC_DEFINE($1, 1, [Add the ]$1[ flag when building])
+ tcl_flags="$tcl_flags $1"
+ fi
+])
+
+AC_DEFUN([TEA_TCL_EARLY_FLAGS],[
+ AC_MSG_CHECKING([for required early compiler flags])
+ tcl_flags=""
+ TEA_TCL_EARLY_FLAG(_ISOC99_SOURCE,[#include <stdlib.h>],
+ [char *p = (char *)strtoll; char *q = (char *)strtoull;])
+ TEA_TCL_EARLY_FLAG(_LARGEFILE64_SOURCE,[#include <sys/stat.h>],
+ [struct stat64 buf; int i = stat64("/", &buf);])
+ TEA_TCL_EARLY_FLAG(_LARGEFILE_SOURCE64,[#include <sys/stat.h>],
+ [char *p = (char *)open64;])
+ if test "x${tcl_flags}" = "x" ; then
+ AC_MSG_RESULT([none])
+ else
+ AC_MSG_RESULT([${tcl_flags}])
+ fi
+])
+
+#--------------------------------------------------------------------
+# TEA_TCL_64BIT_FLAGS
+#
+# Check for what is defined in the way of 64-bit features.
+#
+# Arguments:
+# None
+#
+# Results:
+#
+# Might define the following vars:
+# TCL_WIDE_INT_IS_LONG
+# TCL_WIDE_INT_TYPE
+# HAVE_STRUCT_DIRENT64
+# HAVE_STRUCT_STAT64
+# HAVE_TYPE_OFF64_T
+#--------------------------------------------------------------------
+
+AC_DEFUN([TEA_TCL_64BIT_FLAGS], [
+ AC_MSG_CHECKING([for 64-bit integer type])
+ AC_CACHE_VAL(tcl_cv_type_64bit,[
+ tcl_cv_type_64bit=none
+ # See if the compiler knows natively about __int64
+ AC_TRY_COMPILE(,[__int64 value = (__int64) 0;],
+ tcl_type_64bit=__int64, tcl_type_64bit="long long")
+ # See if we should use long anyway Note that we substitute in the
+ # type that is our current guess for a 64-bit type inside this check
+ # program, so it should be modified only carefully...
+ AC_TRY_COMPILE(,[switch (0) {
+ case 1: case (sizeof(]${tcl_type_64bit}[)==sizeof(long)): ;
+ }],tcl_cv_type_64bit=${tcl_type_64bit})])
+ if test "${tcl_cv_type_64bit}" = none ; then
+ AC_DEFINE(TCL_WIDE_INT_IS_LONG, 1, [Are wide integers to be implemented with C 'long's?])
+ AC_MSG_RESULT([using long])
+ elif test "${tcl_cv_type_64bit}" = "__int64" \
+ -a "${TEA_PLATFORM}" = "windows" ; then
+ # TEA specific: We actually want to use the default tcl.h checks in
+ # this case to handle both TCL_WIDE_INT_TYPE and TCL_LL_MODIFIER*
+ AC_MSG_RESULT([using Tcl header defaults])
+ else
+ AC_DEFINE_UNQUOTED(TCL_WIDE_INT_TYPE,${tcl_cv_type_64bit},
+ [What type should be used to define wide integers?])
+ AC_MSG_RESULT([${tcl_cv_type_64bit}])
+
+ # Now check for auxiliary declarations
+ AC_CACHE_CHECK([for struct dirent64], tcl_cv_struct_dirent64,[
+ AC_TRY_COMPILE([#include <sys/types.h>
+#include <dirent.h>],[struct dirent64 p;],
+ tcl_cv_struct_dirent64=yes,tcl_cv_struct_dirent64=no)])
+ if test "x${tcl_cv_struct_dirent64}" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_DIRENT64, 1, [Is 'struct dirent64' in <sys/types.h>?])
+ fi
+
+ AC_CACHE_CHECK([for struct stat64], tcl_cv_struct_stat64,[
+ AC_TRY_COMPILE([#include <sys/stat.h>],[struct stat64 p;
+],
+ tcl_cv_struct_stat64=yes,tcl_cv_struct_stat64=no)])
+ if test "x${tcl_cv_struct_stat64}" = "xyes" ; then
+ AC_DEFINE(HAVE_STRUCT_STAT64, 1, [Is 'struct stat64' in <sys/stat.h>?])
+ fi
+
+ AC_CHECK_FUNCS(open64 lseek64)
+ AC_MSG_CHECKING([for off64_t])
+ AC_CACHE_VAL(tcl_cv_type_off64_t,[
+ AC_TRY_COMPILE([#include <sys/types.h>],[off64_t offset;
+],
+ tcl_cv_type_off64_t=yes,tcl_cv_type_off64_t=no)])
+ dnl Define HAVE_TYPE_OFF64_T only when the off64_t type and the
+ dnl functions lseek64 and open64 are defined.
+ if test "x${tcl_cv_type_off64_t}" = "xyes" && \
+ test "x${ac_cv_func_lseek64}" = "xyes" && \
+ test "x${ac_cv_func_open64}" = "xyes" ; then
+ AC_DEFINE(HAVE_TYPE_OFF64_T, 1, [Is off64_t in <sys/types.h>?])
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ fi
+])
+
+##
+## Here ends the standard Tcl configuration bits and starts the
+## TEA specific functions
+##
+
+#------------------------------------------------------------------------
+# TEA_INIT --
+#
+# Init various Tcl Extension Architecture (TEA) variables.
+# This should be the first called TEA_* macro.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Defines and substs the following vars:
+# CYGPATH
+# EXEEXT
+# Defines only:
+# TEA_VERSION
+# TEA_INITED
+# TEA_PLATFORM (windows or unix)
+#
+# "cygpath" is used on windows to generate native path names for include
+# files. These variables should only be used with the compiler and linker
+# since they generate native path names.
+#
+# EXEEXT
+# Select the executable extension based on the host type. This
+# is a lightweight replacement for AC_EXEEXT that doesn't require
+# a compiler.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_INIT], [
+ # TEA extensions pass this us the version of TEA they think they
+ # are compatible with.
+ TEA_VERSION="3.9"
+
+ AC_MSG_CHECKING([for correct TEA configuration])
+ if test x"${PACKAGE_NAME}" = x ; then
+ AC_MSG_ERROR([
+The PACKAGE_NAME variable must be defined by your TEA configure.in])
+ fi
+ if test x"$1" = x ; then
+ AC_MSG_ERROR([
+TEA version not specified.])
+ elif test "$1" != "${TEA_VERSION}" ; then
+ AC_MSG_RESULT([warning: requested TEA version "$1", have "${TEA_VERSION}"])
+ else
+ AC_MSG_RESULT([ok (TEA ${TEA_VERSION})])
+ fi
+
+ # If the user did not set CFLAGS, set it now to keep macros
+ # like AC_PROG_CC and AC_TRY_COMPILE from adding "-g -O2".
+ if test "${CFLAGS+set}" != "set" ; then
+ CFLAGS=""
+ fi
+
+ case "`uname -s`" in
+ *win32*|*WIN32*|*MINGW32_*)
+ AC_CHECK_PROG(CYGPATH, cygpath, cygpath -w, echo)
+ EXEEXT=".exe"
+ TEA_PLATFORM="windows"
+ ;;
+ *CYGWIN_*)
+ CYGPATH=echo
+ EXEEXT=".exe"
+ # TEA_PLATFORM is determined later in LOAD_TCLCONFIG
+ ;;
+ *)
+ CYGPATH=echo
+ # Maybe we are cross-compiling....
+ case ${host_alias} in
+ *mingw32*)
+ EXEEXT=".exe"
+ TEA_PLATFORM="windows"
+ ;;
+ *)
+ EXEEXT=""
+ TEA_PLATFORM="unix"
+ ;;
+ esac
+ ;;
+ esac
+
+ # Check if exec_prefix is set. If not use fall back to prefix.
+ # Note when adjusted, so that TEA_PREFIX can correct for this.
+ # This is needed for recursive configures, since autoconf propagates
+ # $prefix, but not $exec_prefix (doh!).
+ if test x$exec_prefix = xNONE ; then
+ exec_prefix_default=yes
+ exec_prefix=$prefix
+ fi
+
+ AC_MSG_NOTICE([configuring ${PACKAGE_NAME} ${PACKAGE_VERSION}])
+
+ AC_SUBST(EXEEXT)
+ AC_SUBST(CYGPATH)
+
+ # This package name must be replaced statically for AC_SUBST to work
+ AC_SUBST(PKG_LIB_FILE)
+ # Substitute STUB_LIB_FILE in case package creates a stub library too.
+ AC_SUBST(PKG_STUB_LIB_FILE)
+
+ # We AC_SUBST these here to ensure they are subst'ed,
+ # in case the user doesn't call TEA_ADD_...
+ AC_SUBST(PKG_STUB_SOURCES)
+ AC_SUBST(PKG_STUB_OBJECTS)
+ AC_SUBST(PKG_TCL_SOURCES)
+ AC_SUBST(PKG_HEADERS)
+ AC_SUBST(PKG_INCLUDES)
+ AC_SUBST(PKG_LIBS)
+ AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_SOURCES --
+#
+# Specify one or more source files. Users should check for
+# the right platform before adding to their list.
+# It is not important to specify the directory, as long as it is
+# in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_SOURCES
+# PKG_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ case $i in
+ [\$]*)
+ # allow $-var names
+ PKG_SOURCES="$PKG_SOURCES $i"
+ PKG_OBJECTS="$PKG_OBJECTS $i"
+ ;;
+ *)
+ # check for existence - allows for generic/win/unix VPATH
+ # To add more dirs here (like 'src'), you have to update VPATH
+ # in Makefile.in as well
+ if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+ -a ! -f "${srcdir}/macosx/$i" \
+ ; then
+ AC_MSG_ERROR([could not find source file '$i'])
+ fi
+ PKG_SOURCES="$PKG_SOURCES $i"
+ # this assumes it is in a VPATH dir
+ i=`basename $i`
+ # handle user calling this before or after TEA_SETUP_COMPILER
+ if test x"${OBJEXT}" != x ; then
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+ else
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+ fi
+ PKG_OBJECTS="$PKG_OBJECTS $j"
+ ;;
+ esac
+ done
+ AC_SUBST(PKG_SOURCES)
+ AC_SUBST(PKG_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_STUB_SOURCES --
+#
+# Specify one or more source files. Users should check for
+# the right platform before adding to their list.
+# It is not important to specify the directory, as long as it is
+# in the generic, win or unix subdirectory of $(srcdir).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_STUB_SOURCES
+# PKG_STUB_OBJECTS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_STUB_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence - allows for generic/win/unix VPATH
+ if test ! -f "${srcdir}/$i" -a ! -f "${srcdir}/generic/$i" \
+ -a ! -f "${srcdir}/win/$i" -a ! -f "${srcdir}/unix/$i" \
+ -a ! -f "${srcdir}/macosx/$i" \
+ ; then
+ AC_MSG_ERROR([could not find stub source file '$i'])
+ fi
+ PKG_STUB_SOURCES="$PKG_STUB_SOURCES $i"
+ # this assumes it is in a VPATH dir
+ i=`basename $i`
+ # handle user calling this before or after TEA_SETUP_COMPILER
+ if test x"${OBJEXT}" != x ; then
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.${OBJEXT}"
+ else
+ j="`echo $i | sed -e 's/\.[[^.]]*$//'`.\${OBJEXT}"
+ fi
+ PKG_STUB_OBJECTS="$PKG_STUB_OBJECTS $j"
+ done
+ AC_SUBST(PKG_STUB_SOURCES)
+ AC_SUBST(PKG_STUB_OBJECTS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_TCL_SOURCES --
+#
+# Specify one or more Tcl source files. These should be platform
+# independent runtime files.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_TCL_SOURCES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_TCL_SOURCES], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence, be strict because it is installed
+ if test ! -f "${srcdir}/$i" ; then
+ AC_MSG_ERROR([could not find tcl source file '${srcdir}/$i'])
+ fi
+ PKG_TCL_SOURCES="$PKG_TCL_SOURCES $i"
+ done
+ AC_SUBST(PKG_TCL_SOURCES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_HEADERS --
+#
+# Specify one or more source headers. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_HEADERS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_HEADERS], [
+ vars="$@"
+ for i in $vars; do
+ # check for existence, be strict because it is installed
+ if test ! -f "${srcdir}/$i" ; then
+ AC_MSG_ERROR([could not find header file '${srcdir}/$i'])
+ fi
+ PKG_HEADERS="$PKG_HEADERS $i"
+ done
+ AC_SUBST(PKG_HEADERS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_INCLUDES --
+#
+# Specify one or more include dirs. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_INCLUDES
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_INCLUDES], [
+ vars="$@"
+ for i in $vars; do
+ PKG_INCLUDES="$PKG_INCLUDES $i"
+ done
+ AC_SUBST(PKG_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_LIBS --
+#
+# Specify one or more libraries. Users should check for
+# the right platform before adding to their list. For Windows,
+# libraries provided in "foo.lib" format will be converted to
+# "-lfoo" when using GCC (mingw).
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_LIBS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_LIBS], [
+ vars="$@"
+ for i in $vars; do
+ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" = "yes" ; then
+ # Convert foo.lib to -lfoo for GCC. No-op if not *.lib
+ i=`echo "$i" | sed -e 's/^\([[^-]].*\)\.lib[$]/-l\1/i'`
+ fi
+ PKG_LIBS="$PKG_LIBS $i"
+ done
+ AC_SUBST(PKG_LIBS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_CFLAGS --
+#
+# Specify one or more CFLAGS. Users should check for
+# the right platform before adding to their list.
+#
+# Arguments:
+# one or more file names
+#
+# Results:
+#
+# Defines and substs the following vars:
+# PKG_CFLAGS
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_CFLAGS], [
+ PKG_CFLAGS="$PKG_CFLAGS $@"
+ AC_SUBST(PKG_CFLAGS)
+])
+
+#------------------------------------------------------------------------
+# TEA_ADD_CLEANFILES --
+#
+# Specify one or more CLEANFILES.
+#
+# Arguments:
+# one or more file names to clean target
+#
+# Results:
+#
+# Appends to CLEANFILES, already defined for subst in LOAD_TCLCONFIG
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_ADD_CLEANFILES], [
+ CLEANFILES="$CLEANFILES $@"
+])
+
+#------------------------------------------------------------------------
+# TEA_PREFIX --
+#
+# Handle the --prefix=... option by defaulting to what Tcl gave
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# If --prefix or --exec-prefix was not specified, $prefix and
+# $exec_prefix will be set to the values given to Tcl when it was
+# configured.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_PREFIX], [
+ if test "${prefix}" = "NONE"; then
+ prefix_default=yes
+ if test x"${TCL_PREFIX}" != x; then
+ AC_MSG_NOTICE([--prefix defaulting to TCL_PREFIX ${TCL_PREFIX}])
+ prefix=${TCL_PREFIX}
+ else
+ AC_MSG_NOTICE([--prefix defaulting to /usr/local])
+ prefix=/usr/local
+ fi
+ fi
+ if test "${exec_prefix}" = "NONE" -a x"${prefix_default}" = x"yes" \
+ -o x"${exec_prefix_default}" = x"yes" ; then
+ if test x"${TCL_EXEC_PREFIX}" != x; then
+ AC_MSG_NOTICE([--exec-prefix defaulting to TCL_EXEC_PREFIX ${TCL_EXEC_PREFIX}])
+ exec_prefix=${TCL_EXEC_PREFIX}
+ else
+ AC_MSG_NOTICE([--exec-prefix defaulting to ${prefix}])
+ exec_prefix=$prefix
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER_CC --
+#
+# Do compiler checks the way we want. This is just a replacement
+# for AC_PROG_CC in TEA configure.in files to make them cleaner.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER_CC], [
+ # Don't put any macros that use the compiler (e.g. AC_TRY_COMPILE)
+ # in this macro, they need to go into TEA_SETUP_COMPILER instead.
+
+ AC_PROG_CC
+ AC_PROG_CPP
+
+ INSTALL="\$(SHELL) \$(srcdir)/tclconfig/install-sh -c"
+ AC_SUBST(INSTALL)
+ INSTALL_DATA="\${INSTALL} -m 644"
+ AC_SUBST(INSTALL_DATA)
+ INSTALL_PROGRAM="\${INSTALL}"
+ AC_SUBST(INSTALL_PROGRAM)
+ INSTALL_SCRIPT="\${INSTALL}"
+ AC_SUBST(INSTALL_SCRIPT)
+
+ #--------------------------------------------------------------------
+ # Checks to see if the make program sets the $MAKE variable.
+ #--------------------------------------------------------------------
+
+ AC_PROG_MAKE_SET
+
+ #--------------------------------------------------------------------
+ # Find ranlib
+ #--------------------------------------------------------------------
+
+ AC_CHECK_TOOL(RANLIB, ranlib)
+
+ #--------------------------------------------------------------------
+ # Determines the correct binary file extension (.o, .obj, .exe etc.)
+ #--------------------------------------------------------------------
+
+ AC_OBJEXT
+ AC_EXEEXT
+])
+
+#------------------------------------------------------------------------
+# TEA_SETUP_COMPILER --
+#
+# Do compiler checks that use the compiler. This must go after
+# TEA_SETUP_COMPILER_CC, which does the actual compiler check.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Sets up CC var and other standard bits we need to make executables.
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_SETUP_COMPILER], [
+ # Any macros that use the compiler (e.g. AC_TRY_COMPILE) have to go here.
+ AC_REQUIRE([TEA_SETUP_COMPILER_CC])
+
+ #------------------------------------------------------------------------
+ # If we're using GCC, see if the compiler understands -pipe. If so, use it.
+ # It makes compiling go faster. (This is only a performance feature.)
+ #------------------------------------------------------------------------
+
+ if test -z "$no_pipe" -a -n "$GCC"; then
+ AC_CACHE_CHECK([if the compiler understands -pipe],
+ tcl_cv_cc_pipe, [
+ hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -pipe"
+ AC_TRY_COMPILE(,, tcl_cv_cc_pipe=yes, tcl_cv_cc_pipe=no)
+ CFLAGS=$hold_cflags])
+ if test $tcl_cv_cc_pipe = yes; then
+ CFLAGS="$CFLAGS -pipe"
+ fi
+ fi
+
+ #--------------------------------------------------------------------
+ # Common compiler flag setup
+ #--------------------------------------------------------------------
+
+ AC_C_BIGENDIAN
+ if test "${TEA_PLATFORM}" = "unix" ; then
+ TEA_TCL_LINK_LIBS
+ TEA_MISSING_POSIX_HEADERS
+ # Let the user call this, because if it triggers, they will
+ # need a compat/strtod.c that is correct. Users can also
+ # use Tcl_GetDouble(FromObj) instead.
+ #TEA_BUGGY_STRTOD
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_MAKE_LIB --
+#
+# Generate a line that can be used to build a shared/unshared library
+# in a platform independent manner.
+#
+# Arguments:
+# none
+#
+# Requires:
+#
+# Results:
+#
+# Defines the following vars:
+# CFLAGS - Done late here to note disturb other AC macros
+# MAKE_LIB - Command to execute to build the Tcl library;
+# differs depending on whether or not Tcl is being
+# compiled as a shared library.
+# MAKE_SHARED_LIB Makefile rule for building a shared library
+# MAKE_STATIC_LIB Makefile rule for building a static library
+# MAKE_STUB_LIB Makefile rule for building a stub library
+# VC_MANIFEST_EMBED_DLL Makefile rule for embedded VC manifest in DLL
+# VC_MANIFEST_EMBED_EXE Makefile rule for embedded VC manifest in EXE
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_MAKE_LIB], [
+ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes"; then
+ MAKE_STATIC_LIB="\${STLIB_LD} -out:\[$]@ \$(PKG_OBJECTS)"
+ MAKE_SHARED_LIB="\${SHLIB_LD} \${SHLIB_LD_LIBS} \${LDFLAGS_DEFAULT} -out:\[$]@ \$(PKG_OBJECTS)"
+ AC_EGREP_CPP([manifest needed], [
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+print("manifest needed")
+#endif
+ ], [
+ # Could do a CHECK_PROG for mt, but should always be with MSVC8+
+ VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;2 ; fi"
+ VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest -outputresource:\[$]@\;1 ; fi"
+ MAKE_SHARED_LIB="${MAKE_SHARED_LIB} ; ${VC_MANIFEST_EMBED_DLL}"
+ TEA_ADD_CLEANFILES([*.manifest])
+ ])
+ MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@ \$(PKG_STUB_OBJECTS)"
+ else
+ MAKE_STATIC_LIB="\${STLIB_LD} \[$]@ \$(PKG_OBJECTS)"
+ MAKE_SHARED_LIB="\${SHLIB_LD} -o \[$]@ \$(PKG_OBJECTS) \${SHLIB_LD_LIBS}"
+ MAKE_STUB_LIB="\${STLIB_LD} \[$]@ \$(PKG_STUB_OBJECTS)"
+ fi
+
+ if test "${SHARED_BUILD}" = "1" ; then
+ MAKE_LIB="${MAKE_SHARED_LIB} "
+ else
+ MAKE_LIB="${MAKE_STATIC_LIB} "
+ fi
+
+ #--------------------------------------------------------------------
+ # Shared libraries and static libraries have different names.
+ # Use the double eval to make sure any variables in the suffix is
+ # substituted. (@@@ Might not be necessary anymore)
+ #--------------------------------------------------------------------
+
+ if test "${TEA_PLATFORM}" = "windows" ; then
+ if test "${SHARED_BUILD}" = "1" ; then
+ # We force the unresolved linking of symbols that are really in
+ # the private libraries of Tcl and Tk.
+ if test x"${TK_BIN_DIR}" != x ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TK_BIN_DIR}/${TK_STUB_LIB_FILE}`\""
+ fi
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} \"`${CYGPATH} ${TCL_BIN_DIR}/${TCL_STUB_LIB_FILE}`\""
+ if test "$GCC" = "yes"; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} -static-libgcc"
+ fi
+ eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ else
+ eval eval "PKG_LIB_FILE=${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ if test "$GCC" = "yes"; then
+ PKG_LIB_FILE=lib${PKG_LIB_FILE}
+ fi
+ fi
+ # Some packages build their own stubs libraries
+ eval eval "PKG_STUB_LIB_FILE=${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ if test "$GCC" = "yes"; then
+ PKG_STUB_LIB_FILE=lib${PKG_STUB_LIB_FILE}
+ fi
+ # These aren't needed on Windows (either MSVC or gcc)
+ RANLIB=:
+ RANLIB_STUB=:
+ else
+ RANLIB_STUB="${RANLIB}"
+ if test "${SHARED_BUILD}" = "1" ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TCL_STUB_LIB_SPEC}"
+ if test x"${TK_BIN_DIR}" != x ; then
+ SHLIB_LD_LIBS="${SHLIB_LD_LIBS} ${TK_STUB_LIB_SPEC}"
+ fi
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${SHARED_LIB_SUFFIX}"
+ RANLIB=:
+ else
+ eval eval "PKG_LIB_FILE=lib${PACKAGE_NAME}${UNSHARED_LIB_SUFFIX}"
+ fi
+ # Some packages build their own stubs libraries
+ eval eval "PKG_STUB_LIB_FILE=lib${PACKAGE_NAME}stub${UNSHARED_LIB_SUFFIX}"
+ fi
+
+ # These are escaped so that only CFLAGS is picked up at configure time.
+ # The other values will be substituted at make time.
+ CFLAGS="${CFLAGS} \${CFLAGS_DEFAULT} \${CFLAGS_WARNING}"
+ if test "${SHARED_BUILD}" = "1" ; then
+ CFLAGS="${CFLAGS} \${SHLIB_CFLAGS}"
+ fi
+
+ AC_SUBST(MAKE_LIB)
+ AC_SUBST(MAKE_SHARED_LIB)
+ AC_SUBST(MAKE_STATIC_LIB)
+ AC_SUBST(MAKE_STUB_LIB)
+ AC_SUBST(RANLIB_STUB)
+ AC_SUBST(VC_MANIFEST_EMBED_DLL)
+ AC_SUBST(VC_MANIFEST_EMBED_EXE)
+])
+
+#------------------------------------------------------------------------
+# TEA_LIB_SPEC --
+#
+# Compute the name of an existing object library located in libdir
+# from the given base name and produce the appropriate linker flags.
+#
+# Arguments:
+# basename The base name of the library without version
+# numbers, extensions, or "lib" prefixes.
+# extra_dir Extra directory in which to search for the
+# library. This location is used first, then
+# $prefix/$exec-prefix, then some defaults.
+#
+# Requires:
+# TEA_INIT and TEA_PREFIX must be called first.
+#
+# Results:
+#
+# Defines the following vars:
+# ${basename}_LIB_NAME The computed library name.
+# ${basename}_LIB_SPEC The computed linker flags.
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LIB_SPEC], [
+ AC_MSG_CHECKING([for $1 library])
+
+ # Look in exec-prefix for the library (defined by TEA_PREFIX).
+
+ tea_lib_name_dir="${exec_prefix}/lib"
+
+ # Or in a user-specified location.
+
+ if test x"$2" != x ; then
+ tea_extra_lib_dir=$2
+ else
+ tea_extra_lib_dir=NONE
+ fi
+
+ for i in \
+ `ls -dr ${tea_extra_lib_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr ${tea_extra_lib_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr ${tea_lib_name_dir}/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr ${tea_lib_name_dir}/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr /usr/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr /usr/lib/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr /usr/lib64/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr /usr/lib64/lib$1[[0-9]]* 2>/dev/null ` \
+ `ls -dr /usr/local/lib/$1[[0-9]]*.lib 2>/dev/null ` \
+ `ls -dr /usr/local/lib/lib$1[[0-9]]* 2>/dev/null ` ; do
+ if test -f "$i" ; then
+ tea_lib_name_dir=`dirname $i`
+ $1_LIB_NAME=`basename $i`
+ $1_LIB_PATH_NAME=$i
+ break
+ fi
+ done
+
+ if test "${TEA_PLATFORM}" = "windows"; then
+ $1_LIB_SPEC=\"`${CYGPATH} ${$1_LIB_PATH_NAME} 2>/dev/null`\"
+ else
+ # Strip off the leading "lib" and trailing ".a" or ".so"
+
+ tea_lib_name_lib=`echo ${$1_LIB_NAME}|sed -e 's/^lib//' -e 's/\.[[^.]]*$//' -e 's/\.so.*//'`
+ $1_LIB_SPEC="-L${tea_lib_name_dir} -l${tea_lib_name_lib}"
+ fi
+
+ if test "x${$1_LIB_NAME}" = x ; then
+ AC_MSG_ERROR([not found])
+ else
+ AC_MSG_RESULT([${$1_LIB_SPEC}])
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TCL_HEADERS --
+#
+# Locate the private Tcl include files
+#
+# Arguments:
+#
+# Requires:
+# TCL_SRC_DIR Assumes that TEA_LOAD_TCLCONFIG has
+# already been called.
+#
+# Results:
+#
+# Substitutes the following vars:
+# TCL_TOP_DIR_NATIVE
+# TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TCL_HEADERS], [
+ # Allow for --with-tclinclude to take effect and define ${ac_cv_c_tclh}
+ AC_REQUIRE([TEA_PUBLIC_TCL_HEADERS])
+ AC_MSG_CHECKING([for Tcl private include files])
+
+ TCL_SRC_DIR_NATIVE=`${CYGPATH} ${TCL_SRC_DIR}`
+ TCL_TOP_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}\"
+
+ # Check to see if tcl<Plat>Port.h isn't already with the public headers
+ # Don't look for tclInt.h because that resides with tcl.h in the core
+ # sources, but the <plat>Port headers are in a different directory
+ if test "${TEA_PLATFORM}" = "windows" -a \
+ -f "${ac_cv_c_tclh}/tclWinPort.h"; then
+ result="private headers found with public headers"
+ elif test "${TEA_PLATFORM}" = "unix" -a \
+ -f "${ac_cv_c_tclh}/tclUnixPort.h"; then
+ result="private headers found with public headers"
+ else
+ TCL_GENERIC_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/generic\"
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/win\"
+ else
+ TCL_PLATFORM_DIR_NATIVE=\"${TCL_SRC_DIR_NATIVE}/unix\"
+ fi
+ # Overwrite the previous TCL_INCLUDES as this should capture both
+ # public and private headers in the same set.
+ # We want to ensure these are substituted so as not to require
+ # any *_NATIVE vars be defined in the Makefile
+ TCL_INCLUDES="-I${TCL_GENERIC_DIR_NATIVE} -I${TCL_PLATFORM_DIR_NATIVE}"
+ if test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use
+ # the framework's Headers and PrivateHeaders directories
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ if test -d "${TCL_BIN_DIR}/Headers" -a \
+ -d "${TCL_BIN_DIR}/PrivateHeaders"; then
+ TCL_INCLUDES="-I\"${TCL_BIN_DIR}/Headers\" -I\"${TCL_BIN_DIR}/PrivateHeaders\" ${TCL_INCLUDES}"
+ else
+ TCL_INCLUDES="${TCL_INCLUDES} ${TCL_INCLUDE_SPEC} `echo "${TCL_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"
+ fi
+ ;;
+ esac
+ result="Using ${TCL_INCLUDES}"
+ else
+ if test ! -f "${TCL_SRC_DIR}/generic/tclInt.h" ; then
+ AC_MSG_ERROR([Cannot find private header tclInt.h in ${TCL_SRC_DIR}])
+ fi
+ result="Using srcdir found in tclConfig.sh: ${TCL_SRC_DIR}"
+ fi
+ fi
+
+ AC_SUBST(TCL_TOP_DIR_NATIVE)
+
+ AC_SUBST(TCL_INCLUDES)
+ AC_MSG_RESULT([${result}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TCL_HEADERS --
+#
+# Locate the installed public Tcl header files
+#
+# Arguments:
+# None.
+#
+# Requires:
+# CYGPATH must be set
+#
+# Results:
+#
+# Adds a --with-tclinclude switch to configure.
+# Result is cached.
+#
+# Substitutes the following vars:
+# TCL_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TCL_HEADERS], [
+ AC_MSG_CHECKING([for Tcl public headers])
+
+ AC_ARG_WITH(tclinclude, [ --with-tclinclude directory containing the public Tcl header files], with_tclinclude=${withval})
+
+ AC_CACHE_VAL(ac_cv_c_tclh, [
+ # Use the value from --with-tclinclude, if it was given
+
+ if test x"${with_tclinclude}" != x ; then
+ if test -f "${with_tclinclude}/tcl.h" ; then
+ ac_cv_c_tclh=${with_tclinclude}
+ else
+ AC_MSG_ERROR([${with_tclinclude} directory does not contain tcl.h])
+ fi
+ else
+ list=""
+ if test "`uname -s`" = "Darwin"; then
+ # If Tcl was built as a framework, attempt to use
+ # the framework's Headers directory
+ case ${TCL_DEFS} in
+ *TCL_FRAMEWORK*)
+ list="`ls -d ${TCL_BIN_DIR}/Headers 2>/dev/null`"
+ ;;
+ esac
+ fi
+
+ # Look in the source dir only if Tcl is not installed,
+ # and in that situation, look there before installed locations.
+ if test -f "${TCL_BIN_DIR}/Makefile" ; then
+ list="$list `ls -d ${TCL_SRC_DIR}/generic 2>/dev/null`"
+ fi
+
+ # Check order: pkg --prefix location, Tcl's --prefix location,
+ # relative to directory of tclConfig.sh.
+
+ eval "temp_includedir=${includedir}"
+ list="$list \
+ `ls -d ${temp_includedir} 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+ if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+ list="$list /usr/local/include /usr/include"
+ if test x"${TCL_INCLUDE_SPEC}" != x ; then
+ d=`echo "${TCL_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+ list="$list `ls -d ${d} 2>/dev/null`"
+ fi
+ fi
+ for i in $list ; do
+ if test -f "$i/tcl.h" ; then
+ ac_cv_c_tclh=$i
+ break
+ fi
+ done
+ fi
+ ])
+
+ # Print a message based on how we determined the include path
+
+ if test x"${ac_cv_c_tclh}" = x ; then
+ AC_MSG_ERROR([tcl.h not found. Please specify its location with --with-tclinclude])
+ else
+ AC_MSG_RESULT([${ac_cv_c_tclh}])
+ fi
+
+ # Convert to a native path and substitute into the output files.
+
+ INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tclh}`
+
+ TCL_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+ AC_SUBST(TCL_INCLUDES)
+])
+
+#------------------------------------------------------------------------
+# TEA_PRIVATE_TK_HEADERS --
+#
+# Locate the private Tk include files
+#
+# Arguments:
+#
+# Requires:
+# TK_SRC_DIR Assumes that TEA_LOAD_TKCONFIG has
+# already been called.
+#
+# Results:
+#
+# Substitutes the following vars:
+# TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PRIVATE_TK_HEADERS], [
+ # Allow for --with-tkinclude to take effect and define ${ac_cv_c_tkh}
+ AC_REQUIRE([TEA_PUBLIC_TK_HEADERS])
+ AC_MSG_CHECKING([for Tk private include files])
+
+ TK_SRC_DIR_NATIVE=`${CYGPATH} ${TK_SRC_DIR}`
+ TK_TOP_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}\"
+
+ # Check to see if tk<Plat>Port.h isn't already with the public headers
+ # Don't look for tkInt.h because that resides with tk.h in the core
+ # sources, but the <plat>Port headers are in a different directory
+ if test "${TEA_PLATFORM}" = "windows" -a \
+ -f "${ac_cv_c_tkh}/tkWinPort.h"; then
+ result="private headers found with public headers"
+ elif test "${TEA_PLATFORM}" = "unix" -a \
+ -f "${ac_cv_c_tkh}/tkUnixPort.h"; then
+ result="private headers found with public headers"
+ else
+ TK_GENERIC_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/generic\"
+ TK_XLIB_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/xlib\"
+ if test "${TEA_PLATFORM}" = "windows"; then
+ TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/win\"
+ else
+ TK_PLATFORM_DIR_NATIVE=\"${TK_SRC_DIR_NATIVE}/unix\"
+ fi
+ # Overwrite the previous TK_INCLUDES as this should capture both
+ # public and private headers in the same set.
+ # We want to ensure these are substituted so as not to require
+ # any *_NATIVE vars be defined in the Makefile
+ TK_INCLUDES="-I${TK_GENERIC_DIR_NATIVE} -I${TK_PLATFORM_DIR_NATIVE}"
+ # Detect and add ttk subdir
+ if test -d "${TK_SRC_DIR}/generic/ttk"; then
+ TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/generic/ttk\""
+ fi
+ if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then
+ TK_INCLUDES="${TK_INCLUDES} -I\"${TK_XLIB_DIR_NATIVE}\""
+ fi
+ if test "${TEA_WINDOWINGSYSTEM}" = "aqua"; then
+ TK_INCLUDES="${TK_INCLUDES} -I\"${TK_SRC_DIR_NATIVE}/macosx\""
+ fi
+ if test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use
+ # the framework's Headers and PrivateHeaders directories
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ if test -d "${TK_BIN_DIR}/Headers" -a \
+ -d "${TK_BIN_DIR}/PrivateHeaders"; then
+ TK_INCLUDES="-I\"${TK_BIN_DIR}/Headers\" -I\"${TK_BIN_DIR}/PrivateHeaders\" ${TK_INCLUDES}"
+ else
+ TK_INCLUDES="${TK_INCLUDES} ${TK_INCLUDE_SPEC} `echo "${TK_INCLUDE_SPEC}" | sed -e 's/Headers/PrivateHeaders/'`"
+ fi
+ ;;
+ esac
+ result="Using ${TK_INCLUDES}"
+ else
+ if test ! -f "${TK_SRC_DIR}/generic/tkInt.h" ; then
+ AC_MSG_ERROR([Cannot find private header tkInt.h in ${TK_SRC_DIR}])
+ fi
+ result="Using srcdir found in tkConfig.sh: ${TK_SRC_DIR}"
+ fi
+ fi
+
+ AC_SUBST(TK_TOP_DIR_NATIVE)
+ AC_SUBST(TK_XLIB_DIR_NATIVE)
+
+ AC_SUBST(TK_INCLUDES)
+ AC_MSG_RESULT([${result}])
+])
+
+#------------------------------------------------------------------------
+# TEA_PUBLIC_TK_HEADERS --
+#
+# Locate the installed public Tk header files
+#
+# Arguments:
+# None.
+#
+# Requires:
+# CYGPATH must be set
+#
+# Results:
+#
+# Adds a --with-tkinclude switch to configure.
+# Result is cached.
+#
+# Substitutes the following vars:
+# TK_INCLUDES
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PUBLIC_TK_HEADERS], [
+ AC_MSG_CHECKING([for Tk public headers])
+
+ AC_ARG_WITH(tkinclude, [ --with-tkinclude directory containing the public Tk header files], with_tkinclude=${withval})
+
+ AC_CACHE_VAL(ac_cv_c_tkh, [
+ # Use the value from --with-tkinclude, if it was given
+
+ if test x"${with_tkinclude}" != x ; then
+ if test -f "${with_tkinclude}/tk.h" ; then
+ ac_cv_c_tkh=${with_tkinclude}
+ else
+ AC_MSG_ERROR([${with_tkinclude} directory does not contain tk.h])
+ fi
+ else
+ list=""
+ if test "`uname -s`" = "Darwin"; then
+ # If Tk was built as a framework, attempt to use
+ # the framework's Headers directory.
+ case ${TK_DEFS} in
+ *TK_FRAMEWORK*)
+ list="`ls -d ${TK_BIN_DIR}/Headers 2>/dev/null`"
+ ;;
+ esac
+ fi
+
+ # Look in the source dir only if Tk is not installed,
+ # and in that situation, look there before installed locations.
+ if test -f "${TK_BIN_DIR}/Makefile" ; then
+ list="$list `ls -d ${TK_SRC_DIR}/generic 2>/dev/null`"
+ fi
+
+ # Check order: pkg --prefix location, Tk's --prefix location,
+ # relative to directory of tkConfig.sh, Tcl's --prefix location,
+ # relative to directory of tclConfig.sh.
+
+ eval "temp_includedir=${includedir}"
+ list="$list \
+ `ls -d ${temp_includedir} 2>/dev/null` \
+ `ls -d ${TK_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TK_BIN_DIR}/../include 2>/dev/null` \
+ `ls -d ${TCL_PREFIX}/include 2>/dev/null` \
+ `ls -d ${TCL_BIN_DIR}/../include 2>/dev/null`"
+ if test "${TEA_PLATFORM}" != "windows" -o "$GCC" = "yes"; then
+ list="$list /usr/local/include /usr/include"
+ if test x"${TK_INCLUDE_SPEC}" != x ; then
+ d=`echo "${TK_INCLUDE_SPEC}" | sed -e 's/^-I//'`
+ list="$list `ls -d ${d} 2>/dev/null`"
+ fi
+ fi
+ for i in $list ; do
+ if test -f "$i/tk.h" ; then
+ ac_cv_c_tkh=$i
+ break
+ fi
+ done
+ fi
+ ])
+
+ # Print a message based on how we determined the include path
+
+ if test x"${ac_cv_c_tkh}" = x ; then
+ AC_MSG_ERROR([tk.h not found. Please specify its location with --with-tkinclude])
+ else
+ AC_MSG_RESULT([${ac_cv_c_tkh}])
+ fi
+
+ # Convert to a native path and substitute into the output files.
+
+ INCLUDE_DIR_NATIVE=`${CYGPATH} ${ac_cv_c_tkh}`
+
+ TK_INCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+
+ AC_SUBST(TK_INCLUDES)
+
+ if test "${TEA_WINDOWINGSYSTEM}" != "x11"; then
+ # On Windows and Aqua, we need the X compat headers
+ AC_MSG_CHECKING([for X11 header files])
+ if test ! -r "${INCLUDE_DIR_NATIVE}/X11/Xlib.h"; then
+ INCLUDE_DIR_NATIVE="`${CYGPATH} ${TK_SRC_DIR}/xlib`"
+ TK_XINCLUDES=-I\"${INCLUDE_DIR_NATIVE}\"
+ AC_SUBST(TK_XINCLUDES)
+ fi
+ AC_MSG_RESULT([${INCLUDE_DIR_NATIVE}])
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_PATH_CONFIG --
+#
+# Locate the ${1}Config.sh file and perform a sanity check on
+# the ${1} compile flags. These are used by packages like
+# [incr Tk] that load *Config.sh files from more than Tcl and Tk.
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-$1=...
+#
+# Defines the following vars:
+# $1_BIN_DIR Full path to the directory containing
+# the $1Config.sh file
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CONFIG], [
+ #
+ # Ok, lets find the $1 configuration
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-$1
+ #
+
+ if test x"${no_$1}" = x ; then
+ # we reset no_$1 in case something fails here
+ no_$1=true
+ AC_ARG_WITH($1, [ --with-$1 directory containing $1 configuration ($1Config.sh)], with_$1config=${withval})
+ AC_MSG_CHECKING([for $1 configuration])
+ AC_CACHE_VAL(ac_cv_c_$1config,[
+
+ # First check to see if --with-$1 was specified.
+ if test x"${with_$1config}" != x ; then
+ case ${with_$1config} in
+ */$1Config.sh )
+ if test -f ${with_$1config}; then
+ AC_MSG_WARN([--with-$1 argument should refer to directory containing $1Config.sh, not to $1Config.sh itself])
+ with_$1config=`echo ${with_$1config} | sed 's!/$1Config\.sh$!!'`
+ fi;;
+ esac
+ if test -f "${with_$1config}/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd ${with_$1config}; pwd)`
+ else
+ AC_MSG_ERROR([${with_$1config} directory doesn't contain $1Config.sh])
+ fi
+ fi
+
+ # then check for a private $1 installation
+ if test x"${ac_cv_c_$1config}" = x ; then
+ for i in \
+ ../$1 \
+ `ls -dr ../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ../../$1 \
+ `ls -dr ../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ../../../$1 \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ../../../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ${srcdir}/../$1 \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]*.[[0-9]]* 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]][[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]] 2>/dev/null` \
+ `ls -dr ${srcdir}/../$1*[[0-9]].[[0-9]]* 2>/dev/null` \
+ ; do
+ if test -f "$i/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i; pwd)`
+ break
+ fi
+ if test -f "$i/unix/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i/unix; pwd)`
+ break
+ fi
+ done
+ fi
+
+ # check in a few common install locations
+ if test x"${ac_cv_c_$1config}" = x ; then
+ for i in `ls -d ${libdir} 2>/dev/null` \
+ `ls -d ${exec_prefix}/lib 2>/dev/null` \
+ `ls -d ${prefix}/lib 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` \
+ `ls -d /usr/lib64 2>/dev/null` \
+ ; do
+ if test -f "$i/$1Config.sh" ; then
+ ac_cv_c_$1config=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+
+ if test x"${ac_cv_c_$1config}" = x ; then
+ $1_BIN_DIR="# no $1 configs found"
+ AC_MSG_WARN([Cannot find $1 configuration definitions])
+ exit 0
+ else
+ no_$1=
+ $1_BIN_DIR=${ac_cv_c_$1config}
+ AC_MSG_RESULT([found $$1_BIN_DIR/$1Config.sh])
+ fi
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_CONFIG --
+#
+# Load the $1Config.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# $1_BIN_DIR
+#
+# Results:
+#
+# Substitutes the following vars:
+# $1_SRC_DIR
+# $1_LIB_FILE
+# $1_LIB_SPEC
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_LOAD_CONFIG], [
+ AC_MSG_CHECKING([for existence of ${$1_BIN_DIR}/$1Config.sh])
+
+ if test -f "${$1_BIN_DIR}/$1Config.sh" ; then
+ AC_MSG_RESULT([loading])
+ . "${$1_BIN_DIR}/$1Config.sh"
+ else
+ AC_MSG_RESULT([file not found])
+ fi
+
+ #
+ # If the $1_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 $1_LIB_SPEC will be set to the value
+ # of $1_BUILD_LIB_SPEC. An extension should make use of $1_LIB_SPEC
+ # instead of $1_BUILD_LIB_SPEC since it will work with both an
+ # installed and uninstalled version of Tcl.
+ #
+
+ if test -f "${$1_BIN_DIR}/Makefile" ; then
+ AC_MSG_WARN([Found Makefile - using build library specs for $1])
+ $1_LIB_SPEC=${$1_BUILD_LIB_SPEC}
+ $1_STUB_LIB_SPEC=${$1_BUILD_STUB_LIB_SPEC}
+ $1_STUB_LIB_PATH=${$1_BUILD_STUB_LIB_PATH}
+ $1_INCLUDE_SPEC=${$1_BUILD_INCLUDE_SPEC}
+ $1_LIBRARY_PATH=${$1_LIBRARY_PATH}
+ fi
+
+ AC_SUBST($1_VERSION)
+ AC_SUBST($1_BIN_DIR)
+ AC_SUBST($1_SRC_DIR)
+
+ AC_SUBST($1_LIB_FILE)
+ AC_SUBST($1_LIB_SPEC)
+
+ AC_SUBST($1_STUB_LIB_FILE)
+ AC_SUBST($1_STUB_LIB_SPEC)
+ AC_SUBST($1_STUB_LIB_PATH)
+
+ # Allow the caller to prevent this auto-check by specifying any 2nd arg
+ AS_IF([test "x$2" = x], [
+ # Check both upper and lower-case variants
+ # If a dev wanted non-stubs libs, this function could take an option
+ # to not use _STUB in the paths below
+ AS_IF([test "x${$1_STUB_LIB_SPEC}" = x],
+ [TEA_LOAD_CONFIG_LIB(translit($1,[a-z],[A-Z])_STUB)],
+ [TEA_LOAD_CONFIG_LIB($1_STUB)])
+ ])
+])
+
+#------------------------------------------------------------------------
+# TEA_LOAD_CONFIG_LIB --
+#
+# Helper function to load correct library from another extension's
+# ${PACKAGE}Config.sh.
+#
+# Results:
+# Adds to LIBS the appropriate extension library
+#------------------------------------------------------------------------
+AC_DEFUN([TEA_LOAD_CONFIG_LIB], [
+ AC_MSG_CHECKING([For $1 library for LIBS])
+ # This simplifies the use of stub libraries by automatically adding
+ # the stub lib to your path. Normally this would add to SHLIB_LD_LIBS,
+ # but this is called before CONFIG_CFLAGS. More importantly, this adds
+ # to PKG_LIBS, which becomes LIBS, and that is only used by SHLIB_LD.
+ if test "x${$1_LIB_SPEC}" != "x" ; then
+ if test "${TEA_PLATFORM}" = "windows" -a "$GCC" != "yes" ; then
+ TEA_ADD_LIBS([\"`${CYGPATH} ${$1_LIB_PATH}`\"])
+ AC_MSG_RESULT([using $1_LIB_PATH ${$1_LIB_PATH}])
+ else
+ TEA_ADD_LIBS([${$1_LIB_SPEC}])
+ AC_MSG_RESULT([using $1_LIB_SPEC ${$1_LIB_SPEC}])
+ fi
+ else
+ AC_MSG_RESULT([file not found])
+ fi
+])
+
+#------------------------------------------------------------------------
+# TEA_EXPORT_CONFIG --
+#
+# Define the data to insert into the ${PACKAGE}Config.sh file
+#
+# Arguments:
+#
+# Requires the following vars to be set:
+# $1
+#
+# Results:
+# Substitutes the following vars:
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_EXPORT_CONFIG], [
+ #--------------------------------------------------------------------
+ # These are for $1Config.sh
+ #--------------------------------------------------------------------
+
+ # pkglibdir must be a fully qualified path and (not ${exec_prefix}/lib)
+ eval pkglibdir="[$]{libdir}/$1${PACKAGE_VERSION}"
+ if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ eval $1_LIB_FLAG="-l$1${PACKAGE_VERSION}${DBGX}"
+ eval $1_STUB_LIB_FLAG="-l$1stub${PACKAGE_VERSION}${DBGX}"
+ else
+ eval $1_LIB_FLAG="-l$1`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
+ eval $1_STUB_LIB_FLAG="-l$1stub`echo ${PACKAGE_VERSION} | tr -d .`${DBGX}"
+ fi
+ $1_BUILD_LIB_SPEC="-L`pwd` ${$1_LIB_FLAG}"
+ $1_LIB_SPEC="-L${pkglibdir} ${$1_LIB_FLAG}"
+ $1_BUILD_STUB_LIB_SPEC="-L`pwd` [$]{$1_STUB_LIB_FLAG}"
+ $1_STUB_LIB_SPEC="-L${pkglibdir} [$]{$1_STUB_LIB_FLAG}"
+ $1_BUILD_STUB_LIB_PATH="`pwd`/[$]{PKG_STUB_LIB_FILE}"
+ $1_STUB_LIB_PATH="${pkglibdir}/[$]{PKG_STUB_LIB_FILE}"
+
+ AC_SUBST($1_BUILD_LIB_SPEC)
+ AC_SUBST($1_LIB_SPEC)
+ AC_SUBST($1_BUILD_STUB_LIB_SPEC)
+ AC_SUBST($1_STUB_LIB_SPEC)
+ AC_SUBST($1_BUILD_STUB_LIB_PATH)
+ AC_SUBST($1_STUB_LIB_PATH)
+
+ AC_SUBST(MAJOR_VERSION)
+ AC_SUBST(MINOR_VERSION)
+ AC_SUBST(PATCHLEVEL)
+])
+
+
+#------------------------------------------------------------------------
+# TEA_PATH_CELIB --
+#
+# Locate Keuchel's celib emulation layer for targeting Win/CE
+#
+# Arguments:
+# none
+#
+# Results:
+#
+# Adds the following arguments to configure:
+# --with-celib=...
+#
+# Defines the following vars:
+# CELIB_DIR Full path to the directory containing
+# the include and platform lib files
+#------------------------------------------------------------------------
+
+AC_DEFUN([TEA_PATH_CELIB], [
+ # First, look for one uninstalled.
+ # the alternative search directory is invoked by --with-celib
+
+ if test x"${no_celib}" = x ; then
+ # we reset no_celib in case something fails here
+ no_celib=true
+ AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], with_celibconfig=${withval})
+ AC_MSG_CHECKING([for Windows/CE celib directory])
+ AC_CACHE_VAL(ac_cv_c_celibconfig,[
+ # First check to see if --with-celibconfig was specified.
+ if test x"${with_celibconfig}" != x ; then
+ if test -d "${with_celibconfig}/inc" ; then
+ ac_cv_c_celibconfig=`(cd ${with_celibconfig}; pwd)`
+ else
+ AC_MSG_ERROR([${with_celibconfig} directory doesn't contain inc directory])
+ fi
+ fi
+
+ # then check for a celib library
+ if test x"${ac_cv_c_celibconfig}" = x ; then
+ for i in \
+ ../celib-palm-3.0 \
+ ../celib \
+ ../../celib-palm-3.0 \
+ ../../celib \
+ `ls -dr ../celib-*3.[[0-9]]* 2>/dev/null` \
+ ${srcdir}/../celib-palm-3.0 \
+ ${srcdir}/../celib \
+ `ls -dr ${srcdir}/../celib-*3.[[0-9]]* 2>/dev/null` \
+ ; do
+ if test -d "$i/inc" ; then
+ ac_cv_c_celibconfig=`(cd $i; pwd)`
+ break
+ fi
+ done
+ fi
+ ])
+ if test x"${ac_cv_c_celibconfig}" = x ; then
+ AC_MSG_ERROR([Cannot find celib support library directory])
+ else
+ no_celib=
+ CELIB_DIR=${ac_cv_c_celibconfig}
+ CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'`
+ AC_MSG_RESULT([found $CELIB_DIR])
+ fi
+ fi
+])
+
+
+# Local Variables:
+# mode: autoconf
+# End:
diff --git a/autoconf/tea/win/makefile.vc b/autoconf/tea/win/makefile.vc
new file mode 100644
index 0000000..a5e4627
--- /dev/null
+++ b/autoconf/tea/win/makefile.vc
@@ -0,0 +1,414 @@
+# makefile.vc -- -*- Makefile -*-
+#
+# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
+#
+# This makefile is based upon the Tcl 8.4 Makefile.vc and modified to
+# make it suitable as a general package makefile. Look for the word EDIT
+# which marks sections that may need modification. As a minumum you will
+# need to change the PROJECT, DOTVERSION and DLLOBJS variables to values
+# relevant to your package.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# Copyright (c) 1995-1996 Sun Microsystems, Inc.
+# Copyright (c) 1998-2000 Ajuba Solutions.
+# Copyright (c) 2001 ActiveState Corporation.
+# Copyright (c) 2001-2002 David Gravereaux.
+# Copyright (c) 2003 Pat Thoyts
+#
+#-------------------------------------------------------------------------
+# RCS: @(#)$Id: makefile.vc,v 1.4 2004/07/26 08:22:05 patthoyts Exp $
+#-------------------------------------------------------------------------
+
+!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
+MSG = ^
+You will need to run vcvars32.bat from Developer Studio, first, to setup^
+the environment. Jump to this line to read the new instructions.
+!error $(MSG)
+!endif
+
+#------------------------------------------------------------------------------
+# HOW TO USE this makefile:
+#
+# 1) It is now necessary to have %MSVCDir% set in the environment. This is
+# used as a check to see if vcvars32.bat had been run prior to running
+# nmake or during the installation of Microsoft Visual C++, MSVCDir had
+# been set globally and the PATH adjusted. Either way is valid.
+#
+# You'll need to run vcvars32.bat contained in the MsDev's vc(98)/bin
+# directory to setup the proper environment, if needed, for your current
+# setup. This is a needed bootstrap requirement and allows the swapping of
+# different environments to be easier.
+#
+# 2) To use the Platform SDK (not expressly needed), run setenv.bat after
+# vcvars32.bat according to the instructions for it. This can also turn on
+# the 64-bit compiler, if your SDK has it.
+#
+# 3) Targets are:
+# all -- Builds everything.
+# <project> -- Builds the project (eg: nmake sample)
+# test -- Builds and runs the test suite.
+# install -- Installs the built binaries and libraries to $(INSTALLDIR)
+# in an appropriate subdirectory.
+# clean/realclean/distclean -- varying levels of cleaning.
+#
+# 4) Macros usable on the commandline:
+# INSTALLDIR=<path>
+# Sets where to install Tcl from the built binaries.
+# C:\Progra~1\Tcl is assumed when not specified.
+#
+# OPTS=static,msvcrt,staticpkg,threads,symbols,profile,loimpact,none
+# Sets special options for the core. The default is for none.
+# Any combination of the above may be used (comma separated).
+# 'none' will over-ride everything to nothing.
+#
+# static = Builds a static library of the core instead of a
+# dll. The shell will be static (and large), as well.
+# msvcrt = Effects the static option only to switch it from
+# using libcmt(d) as the C runtime [by default] to
+# msvcrt(d). This is useful for static embedding
+# support.
+# staticpkg = Effects the static option only to switch
+# tclshXX.exe to have the dde and reg extension linked
+# inside it.
+# threads = Turns on full multithreading support.
+# thrdalloc = Use the thread allocator (shared global free pool).
+# symbols = Adds symbols for step debugging.
+# profile = Adds profiling hooks. Map file is assumed.
+# loimpact = Adds a flag for how NT treats the heap to keep memory
+# in use, low. This is said to impact alloc performance.
+#
+# STATS=memdbg,compdbg,none
+# Sets optional memory and bytecode compiler debugging code added
+# to the core. The default is for none. Any combination of the
+# above may be used (comma separated). 'none' will over-ride
+# everything to nothing.
+#
+# memdbg = Enables the debugging memory allocator.
+# compdbg = Enables byte compilation logging.
+#
+# MACHINE=(IX86|IA64|ALPHA)
+# Set the machine type used for the compiler, linker, and
+# resource compiler. This hook is needed to tell the tools
+# when alternate platforms are requested. IX86 is the default
+# when not specified.
+#
+# TMP_DIR=<path>
+# OUT_DIR=<path>
+# Hooks to allow the intermediate and output directories to be
+# changed. $(OUT_DIR) is assumed to be
+# $(BINROOT)\(Release|Debug) based on if symbols are requested.
+# $(TMP_DIR) will de $(OUT_DIR)\<buildtype> by default.
+#
+# TESTPAT=<file>
+# Reads the tests requested to be run from this file.
+#
+# CFG_ENCODING=encoding
+# name of encoding for configuration information. Defaults
+# to cp1252
+#
+# 5) Examples:
+#
+# Basic syntax of calling nmake looks like this:
+# nmake [-nologo] -f makefile.vc [target|macrodef [target|macrodef] [...]]
+#
+# Standard (no frills)
+# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
+# Setting environment for using Microsoft Visual C++ tools.
+# c:\tcl_src\win\>nmake -f makefile.vc all
+# c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
+#
+# Building for Win64
+# c:\tcl_src\win\>c:\progra~1\micros~1\vc98\bin\vcvars32.bat
+# Setting environment for using Microsoft Visual C++ tools.
+# c:\tcl_src\win\>c:\progra~1\platfo~1\setenv.bat /pre64 /RETAIL
+# Targeting Windows pre64 RETAIL
+# c:\tcl_src\win\>nmake -f makefile.vc MACHINE=IA64
+#
+#------------------------------------------------------------------------------
+#==============================================================================
+###############################################################################
+#------------------------------------------------------------------------------
+
+!if !exist("makefile.vc")
+MSG = ^
+You must run this makefile only from the directory it is in.^
+Please `cd` to its location first.
+!error $(MSG)
+!endif
+
+#-------------------------------------------------------------------------
+# Project specific information (EDIT)
+#
+# You should edit this with the name and version of your project. This
+# information is used to generate the name of the package library and
+# it's install location.
+#
+# For example, the sample extension is going to build sample04.dll and
+# would install it into $(INSTALLDIR)\lib\sample04
+#
+# You need to specify the object files that need to be linked into your
+# binary here.
+#
+#-------------------------------------------------------------------------
+
+PROJECT = sqlite3
+!include "rules.vc"
+
+# nmakehelp -V <file> <tag> will search the file for tag, skips until a
+# number and returns all character until a character not in [0-9.ab]
+# is read.
+
+!if [echo REM = This file is generated from Makefile.vc > versions.vc]
+!endif
+# get project version from row "AC_INIT([sqlite], [3.7.14])"
+!if [echo DOTVERSION = \>> versions.vc] \
+ && [nmakehlp -V ..\configure.in AC_INIT >> versions.vc]
+!endif
+!include "versions.vc"
+
+VERSION = $(DOTVERSION:.=)
+STUBPREFIX = $(PROJECT)stub
+
+DLLOBJS = \
+ $(TMP_DIR)\tclsqlite3.obj
+
+#-------------------------------------------------------------------------
+# Target names and paths ( shouldn't need changing )
+#-------------------------------------------------------------------------
+
+BINROOT = .
+ROOT = ..
+
+PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
+
+PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME)
+
+### Make sure we use backslash only.
+PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
+LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+INCLUDE_INSTALL_DIR = $(_TCLDIR)\include
+
+### The following paths CANNOT have spaces in them.
+GENERICDIR = $(ROOT)\generic
+WINDIR = $(ROOT)\win
+LIBDIR = $(ROOT)\library
+DOCDIR = $(ROOT)\doc
+TOOLSDIR = $(ROOT)\tools
+COMPATDIR = $(ROOT)\compat
+
+#---------------------------------------------------------------------
+# Compile flags
+#---------------------------------------------------------------------
+
+!if !$(DEBUG)
+!if $(OPTIMIZING)
+### This cranks the optimization level to maximize speed
+cdebug = -O2 -Op -Gs
+!else
+cdebug =
+!endif
+!else if "$(MACHINE)" == "IA64"
+### Warnings are too many, can't support warnings into errors.
+cdebug = -Z7 -Od -GZ
+!else
+cdebug = -Z7 -WX -Od -GZ
+!endif
+
+### Declarations common to all compiler options
+cflags = -nologo -c -W3 -YX -Fp$(TMP_DIR)^\
+
+!if $(MSVCRT)
+!if $(DEBUG)
+crt = -MDd
+!else
+crt = -MD
+!endif
+!else
+!if $(DEBUG)
+crt = -MTd
+!else
+crt = -MT
+!endif
+!endif
+
+INCLUDES = $(TCL_INCLUDES) -I"$(WINDIR)" -I"$(GENERICDIR)" \
+ -I"$(ROOT)\.."
+BASE_CLFAGS = $(cflags) $(cdebug) $(crt) $(INCLUDES) \
+ -DSQLITE_3_SUFFIX_ONLY=1 -DSQLITE_ENABLE_RTREE=1 \
+ -DSQLITE_ENABLE_FTS3=1 -DSQLITE_OMIT_DEPRECATED=1
+CON_CFLAGS = $(cflags) $(cdebug) $(crt) -DCONSOLE -DSQLITE_ENABLE_FTS3=1
+TCL_CFLAGS = -DBUILD_sqlite -DUSE_TCL_STUBS \
+ -DPACKAGE_VERSION="\"$(DOTVERSION)\"" $(BASE_CLFAGS) \
+ $(OPTDEFINES)
+
+#---------------------------------------------------------------------
+# Link flags
+#---------------------------------------------------------------------
+
+!if $(DEBUG)
+ldebug = -debug:full -debugtype:cv
+!else
+ldebug = -release -opt:ref -opt:icf,3
+!endif
+
+### Declarations common to all linker options
+lflags = -nologo -machine:$(MACHINE) $(ldebug)
+
+!if $(PROFILE)
+lflags = $(lflags) -profile
+!endif
+
+!if $(ALIGN98_HACK) && !$(STATIC_BUILD)
+### Align sections for PE size savings.
+lflags = $(lflags) -opt:nowin98
+!else if !$(ALIGN98_HACK) && $(STATIC_BUILD)
+### Align sections for speed in loading by choosing the virtual page size.
+lflags = $(lflags) -align:4096
+!endif
+
+!if $(LOIMPACT)
+lflags = $(lflags) -ws:aggressive
+!endif
+
+dlllflags = $(lflags) -dll
+conlflags = $(lflags) -subsystem:console
+guilflags = $(lflags) -subsystem:windows
+baselibs = $(TCLSTUBLIB)
+
+#---------------------------------------------------------------------
+# TclTest flags
+#---------------------------------------------------------------------
+
+!IF "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!ENDIF
+
+#---------------------------------------------------------------------
+# Project specific targets (EDIT)
+#---------------------------------------------------------------------
+
+all: setup $(PROJECT)
+$(PROJECT): setup $(PRJLIB)
+install: install-binaries install-libraries install-docs
+
+# Tests need to ensure we load the right dll file we
+# have to handle the output differently on Win9x.
+#
+!if "$(OS)" == "Windows_NT" || "$(MSVCDIR)" == "IDE"
+test: setup $(PROJECT)
+ set TCL_LIBRARY=$(ROOT)/library
+ $(TCLSH) <<
+load $(PRJLIB:\=/)
+cd "$(ROOT)/tests"
+set argv "$(TESTFLAGS)"
+source all.tcl
+<<
+!else
+test: setup $(PROJECT)
+ echo Please wait while the test results are collected
+ set TCL_LIBRARY=$(ROOT)/library
+ $(TCLSH) << >tests.log
+load $(PRJLIB:\=/)
+cd "$(ROOT)/tests"
+set argv "$(TESTFLAGS)"
+source all.tcl
+<<
+ type tests.log | more
+!endif
+
+setup:
+ @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
+ @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
+
+$(PRJLIB): $(DLLOBJS)
+ $(link32) $(dlllflags) -out:$@ $(baselibs) @<<
+$**
+<<
+ -@del $*.exp
+
+$(PRJSTUBLIB): $(PRJSTUBOBJS)
+ $(lib32) -nologo -out:$@ $(PRJSTUBOBJS)
+
+#---------------------------------------------------------------------
+# Implicit rules
+#---------------------------------------------------------------------
+
+{$(WINDIR)}.c{$(TMP_DIR)}.obj::
+ $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
+ $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
+ $(cc32) $(TCL_CFLAGS) -DBUILD_$(PROJECT) -Fo$(TMP_DIR)\ @<<
+$<
+<<
+
+{$(WINDIR)}.rc{$(TMP_DIR)}.res:
+ $(rc32) -fo $@ -r -i "$(GENERICDIR)" -D__WIN32__ \
+!if $(DEBUG)
+ -d DEBUG \
+!endif
+!if $(TCL_THREADS)
+ -d TCL_THREADS \
+!endif
+!if $(STATIC_BUILD)
+ -d STATIC_BUILD \
+!endif
+ $<
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
+
+#---------------------------------------------------------------------
+# Installation. (EDIT)
+#
+# You may need to modify this section to reflect the final distribution
+# of your files and possibly to generate documentation.
+#
+#---------------------------------------------------------------------
+
+install-binaries:
+ @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+install-libraries:
+ @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
+ @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
+ @echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
+ @type << >"$(SCRIPT_INSTALL_DIR)\pkgIndex.tcl"
+package ifneeded $(PROJECT) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME)] sqlite3]
+<<
+
+install-docs:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if exist $(DOCDIR) $(CPY) $(DOCDIR)\*.n "$(DOC_INSTALL_DIR)"
+
+#---------------------------------------------------------------------
+# Clean up
+#---------------------------------------------------------------------
+
+clean:
+ @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
+ @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
+
+realclean: clean
+ @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+distclean: realclean
+ @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
+ @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
diff --git a/autoconf/tea/win/nmakehlp.c b/autoconf/tea/win/nmakehlp.c
new file mode 100644
index 0000000..2868857
--- /dev/null
+++ b/autoconf/tea/win/nmakehlp.c
@@ -0,0 +1,694 @@
+/*
+ * ----------------------------------------------------------------------------
+ * nmakehlp.c --
+ *
+ * This is used to fix limitations within nmake and the environment.
+ *
+ * Copyright (c) 2002 by David Gravereaux.
+ * Copyright (c) 2006 by Pat Thoyts
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ * ----------------------------------------------------------------------------
+ */
+
+#define _CRT_SECURE_NO_DEPRECATE
+#include <windows.h>
+#define NO_SHLWAPI_GDI
+#define NO_SHLWAPI_STREAM
+#define NO_SHLWAPI_REG
+#include <shlwapi.h>
+#pragma comment (lib, "user32.lib")
+#pragma comment (lib, "kernel32.lib")
+#pragma comment (lib, "shlwapi.lib")
+#include <stdio.h>
+#include <math.h>
+
+/*
+ * This library is required for x64 builds with _some_ versions of MSVC
+ */
+#if defined(_M_IA64) || defined(_M_AMD64)
+#if _MSC_VER >= 1400 && _MSC_VER < 1500
+#pragma comment(lib, "bufferoverflowU")
+#endif
+#endif
+
+/* ISO hack for dumb VC++ */
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#endif
+
+
+
+/* protos */
+
+static int CheckForCompilerFeature(const char *option);
+static int CheckForLinkerFeature(const char *option);
+static int IsIn(const char *string, const char *substring);
+static int SubstituteFile(const char *substs, const char *filename);
+static int QualifyPath(const char *path);
+static const char *GetVersionFromFile(const char *filename, const char *match);
+static DWORD WINAPI ReadFromPipe(LPVOID args);
+
+/* globals */
+
+#define CHUNK 25
+#define STATICBUFFERSIZE 1000
+typedef struct {
+ HANDLE pipe;
+ char buffer[STATICBUFFERSIZE];
+} pipeinfo;
+
+pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
+pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
+
+/*
+ * exitcodes: 0 == no, 1 == yes, 2 == error
+ */
+
+int
+main(
+ int argc,
+ char *argv[])
+{
+ char msg[300];
+ DWORD dwWritten;
+ int chars;
+
+ /*
+ * Make sure children (cl.exe and link.exe) are kept quiet.
+ */
+
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+
+ /*
+ * Make sure the compiler and linker aren't effected by the outside world.
+ */
+
+ SetEnvironmentVariable("CL", "");
+ SetEnvironmentVariable("LINK", "");
+
+ if (argc > 1 && *argv[1] == '-') {
+ switch (*(argv[1]+1)) {
+ case 'c':
+ if (argc != 3) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -c <compiler option>\n"
+ "Tests for whether cl.exe supports an option\n"
+ "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return CheckForCompilerFeature(argv[2]);
+ case 'l':
+ if (argc != 3) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -l <linker option>\n"
+ "Tests for whether link.exe supports an option\n"
+ "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return CheckForLinkerFeature(argv[2]);
+ case 'f':
+ if (argc == 2) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -f <string> <substring>\n"
+ "Find a substring within another\n"
+ "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ } else if (argc == 3) {
+ /*
+ * If the string is blank, there is no match.
+ */
+
+ return 0;
+ } else {
+ return IsIn(argv[2], argv[3]);
+ }
+ case 's':
+ if (argc == 2) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -s <substitutions file> <file>\n"
+ "Perform a set of string map type substutitions on a file\n"
+ "exitcodes: 0\n",
+ argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return SubstituteFile(argv[2], argv[3]);
+ case 'V':
+ if (argc != 4) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -V filename matchstring\n"
+ "Extract a version from a file:\n"
+ "eg: pkgIndex.tcl \"package ifneeded http\"",
+ argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 0;
+ }
+ printf("%s\n", GetVersionFromFile(argv[2], argv[3]));
+ return 0;
+ case 'Q':
+ if (argc != 3) {
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -Q path\n"
+ "Emit the fully qualified path\n"
+ "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
+ &dwWritten, NULL);
+ return 2;
+ }
+ return QualifyPath(argv[2]);
+ }
+ }
+ chars = snprintf(msg, sizeof(msg) - 1,
+ "usage: %s -c|-f|-l|-Q|-s|-V ...\n"
+ "This is a little helper app to equalize shell differences between WinNT and\n"
+ "Win9x and get nmake.exe to accomplish its job.\n",
+ argv[0]);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
+ return 2;
+}
+
+static int
+CheckForCompilerFeature(
+ const char *option)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ DWORD threadID;
+ char msg[300];
+ BOOL ok;
+ HANDLE hProcess, h, pipeThreads[2];
+ char cmdline[100];
+
+ hProcess = GetCurrentProcess();
+
+ ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+ ZeroMemory(&si, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+
+ ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = FALSE;
+
+ /*
+ * Create a non-inheritible pipe.
+ */
+
+ CreatePipe(&Out.pipe, &h, &sa, 0);
+
+ /*
+ * Dupe the write side, make it inheritible, and close the original.
+ */
+
+ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+ /*
+ * Same as above, but for the error side.
+ */
+
+ CreatePipe(&Err.pipe, &h, &sa, 0);
+ DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+ /*
+ * Base command line.
+ */
+
+ lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
+
+ /*
+ * Append our option for testing
+ */
+
+ lstrcat(cmdline, option);
+
+ /*
+ * Filename to compile, which exists, but is nothing and empty.
+ */
+
+ lstrcat(cmdline, " .\\nul");
+
+ ok = CreateProcess(
+ NULL, /* Module name. */
+ cmdline, /* Command line. */
+ NULL, /* Process handle not inheritable. */
+ NULL, /* Thread handle not inheritable. */
+ TRUE, /* yes, inherit handles. */
+ DETACHED_PROCESS, /* No console for you. */
+ NULL, /* Use parent's environment block. */
+ NULL, /* Use parent's starting directory. */
+ &si, /* Pointer to STARTUPINFO structure. */
+ &pi); /* Pointer to PROCESS_INFORMATION structure. */
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ int chars = snprintf(msg, sizeof(msg) - 1,
+ "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
+ FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+ (300-chars), 0);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
+ return 2;
+ }
+
+ /*
+ * Close our references to the write handles that have now been inherited.
+ */
+
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+
+ WaitForInputIdle(pi.hProcess, 5000);
+ CloseHandle(pi.hThread);
+
+ /*
+ * Start the pipe reader threads.
+ */
+
+ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
+ pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
+
+ /*
+ * Block waiting for the process to end.
+ */
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+
+ /*
+ * Wait for our pipe to get done reading, should it be a little slow.
+ */
+
+ WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
+ CloseHandle(pipeThreads[0]);
+ CloseHandle(pipeThreads[1]);
+
+ /*
+ * Look for the commandline warning code in both streams.
+ * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
+ */
+
+ return !(strstr(Out.buffer, "D4002") != NULL
+ || strstr(Err.buffer, "D4002") != NULL
+ || strstr(Out.buffer, "D9002") != NULL
+ || strstr(Err.buffer, "D9002") != NULL
+ || strstr(Out.buffer, "D2021") != NULL
+ || strstr(Err.buffer, "D2021") != NULL);
+}
+
+static int
+CheckForLinkerFeature(
+ const char *option)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ DWORD threadID;
+ char msg[300];
+ BOOL ok;
+ HANDLE hProcess, h, pipeThreads[2];
+ char cmdline[100];
+
+ hProcess = GetCurrentProcess();
+
+ ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+ ZeroMemory(&si, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+
+ ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ /*
+ * Create a non-inheritible pipe.
+ */
+
+ CreatePipe(&Out.pipe, &h, &sa, 0);
+
+ /*
+ * Dupe the write side, make it inheritible, and close the original.
+ */
+
+ DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+ /*
+ * Same as above, but for the error side.
+ */
+
+ CreatePipe(&Err.pipe, &h, &sa, 0);
+ DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+
+ /*
+ * Base command line.
+ */
+
+ lstrcpy(cmdline, "link.exe -nologo ");
+
+ /*
+ * Append our option for testing.
+ */
+
+ lstrcat(cmdline, option);
+
+ ok = CreateProcess(
+ NULL, /* Module name. */
+ cmdline, /* Command line. */
+ NULL, /* Process handle not inheritable. */
+ NULL, /* Thread handle not inheritable. */
+ TRUE, /* yes, inherit handles. */
+ DETACHED_PROCESS, /* No console for you. */
+ NULL, /* Use parent's environment block. */
+ NULL, /* Use parent's starting directory. */
+ &si, /* Pointer to STARTUPINFO structure. */
+ &pi); /* Pointer to PROCESS_INFORMATION structure. */
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ int chars = snprintf(msg, sizeof(msg) - 1,
+ "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
+ FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
+ (300-chars), 0);
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
+ return 2;
+ }
+
+ /*
+ * Close our references to the write handles that have now been inherited.
+ */
+
+ CloseHandle(si.hStdOutput);
+ CloseHandle(si.hStdError);
+
+ WaitForInputIdle(pi.hProcess, 5000);
+ CloseHandle(pi.hThread);
+
+ /*
+ * Start the pipe reader threads.
+ */
+
+ pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
+ pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
+
+ /*
+ * Block waiting for the process to end.
+ */
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+
+ /*
+ * Wait for our pipe to get done reading, should it be a little slow.
+ */
+
+ WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
+ CloseHandle(pipeThreads[0]);
+ CloseHandle(pipeThreads[1]);
+
+ /*
+ * Look for the commandline warning code in the stderr stream.
+ */
+
+ return !(strstr(Out.buffer, "LNK1117") != NULL ||
+ strstr(Err.buffer, "LNK1117") != NULL ||
+ strstr(Out.buffer, "LNK4044") != NULL ||
+ strstr(Err.buffer, "LNK4044") != NULL);
+}
+
+static DWORD WINAPI
+ReadFromPipe(
+ LPVOID args)
+{
+ pipeinfo *pi = (pipeinfo *) args;
+ char *lastBuf = pi->buffer;
+ DWORD dwRead;
+ BOOL ok;
+
+ again:
+ if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
+ CloseHandle(pi->pipe);
+ return (DWORD)-1;
+ }
+ ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
+ if (!ok || dwRead == 0) {
+ CloseHandle(pi->pipe);
+ return 0;
+ }
+ lastBuf += dwRead;
+ goto again;
+
+ return 0; /* makes the compiler happy */
+}
+
+static int
+IsIn(
+ const char *string,
+ const char *substring)
+{
+ return (strstr(string, substring) != NULL);
+}
+
+/*
+ * GetVersionFromFile --
+ * Looks for a match string in a file and then returns the version
+ * following the match where a version is anything acceptable to
+ * package provide or package ifneeded.
+ */
+
+static const char *
+GetVersionFromFile(
+ const char *filename,
+ const char *match)
+{
+ size_t cbBuffer = 100;
+ static char szBuffer[100];
+ char *szResult = NULL;
+ FILE *fp = fopen(filename, "rt");
+
+ if (fp != NULL) {
+ /*
+ * Read data until we see our match string.
+ */
+
+ while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+ LPSTR p, q;
+
+ p = strstr(szBuffer, match);
+ if (p != NULL) {
+ /*
+ * Skip to first digit.
+ */
+
+ while (*p && !isdigit(*p)) {
+ ++p;
+ }
+
+ /*
+ * Find ending whitespace.
+ */
+
+ q = p;
+ while (*q && (isalnum(*q) || *q == '.')) {
+ ++q;
+ }
+
+ memcpy(szBuffer, p, q - p);
+ szBuffer[q-p] = 0;
+ szResult = szBuffer;
+ break;
+ }
+ }
+ fclose(fp);
+ }
+ return szResult;
+}
+
+/*
+ * List helpers for the SubstituteFile function
+ */
+
+typedef struct list_item_t {
+ struct list_item_t *nextPtr;
+ char * key;
+ char * value;
+} list_item_t;
+
+/* insert a list item into the list (list may be null) */
+static list_item_t *
+list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
+{
+ list_item_t *itemPtr = malloc(sizeof(list_item_t));
+ if (itemPtr) {
+ itemPtr->key = strdup(key);
+ itemPtr->value = strdup(value);
+ itemPtr->nextPtr = NULL;
+
+ while(*listPtrPtr) {
+ listPtrPtr = &(*listPtrPtr)->nextPtr;
+ }
+ *listPtrPtr = itemPtr;
+ }
+ return itemPtr;
+}
+
+static void
+list_free(list_item_t **listPtrPtr)
+{
+ list_item_t *tmpPtr, *listPtr = *listPtrPtr;
+ while (listPtr) {
+ tmpPtr = listPtr;
+ listPtr = listPtr->nextPtr;
+ free(tmpPtr->key);
+ free(tmpPtr->value);
+ free(tmpPtr);
+ }
+}
+
+/*
+ * SubstituteFile --
+ * As windows doesn't provide anything useful like sed and it's unreliable
+ * to use the tclsh you are building against (consider x-platform builds -
+ * eg compiling AMD64 target from IX86) we provide a simple substitution
+ * option here to handle autoconf style substitutions.
+ * The substitution file is whitespace and line delimited. The file should
+ * consist of lines matching the regular expression:
+ * \s*\S+\s+\S*$
+ *
+ * Usage is something like:
+ * nmakehlp -S << $** > $@
+ * @PACKAGE_NAME@ $(PACKAGE_NAME)
+ * @PACKAGE_VERSION@ $(PACKAGE_VERSION)
+ * <<
+ */
+
+static int
+SubstituteFile(
+ const char *substitutions,
+ const char *filename)
+{
+ size_t cbBuffer = 1024;
+ static char szBuffer[1024], szCopy[1024];
+ char *szResult = NULL;
+ list_item_t *substPtr = NULL;
+ FILE *fp, *sp;
+
+ fp = fopen(filename, "rt");
+ if (fp != NULL) {
+
+ /*
+ * Build a list of substutitions from the first filename
+ */
+
+ sp = fopen(substitutions, "rt");
+ if (sp != NULL) {
+ while (fgets(szBuffer, cbBuffer, sp) != NULL) {
+ char *ks, *ke, *vs, *ve;
+ ks = szBuffer;
+ while (ks && *ks && isspace(*ks)) ++ks;
+ ke = ks;
+ while (ke && *ke && !isspace(*ke)) ++ke;
+ vs = ke;
+ while (vs && *vs && isspace(*vs)) ++vs;
+ ve = vs;
+ while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
+ *ke = 0, *ve = 0;
+ list_insert(&substPtr, ks, vs);
+ }
+ fclose(sp);
+ }
+
+ /* debug: dump the list */
+#ifdef _DEBUG
+ {
+ int n = 0;
+ list_item_t *p = NULL;
+ for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
+ fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
+ }
+ }
+#endif
+
+ /*
+ * Run the substitutions over each line of the input
+ */
+
+ while (fgets(szBuffer, cbBuffer, fp) != NULL) {
+ list_item_t *p = NULL;
+ for (p = substPtr; p != NULL; p = p->nextPtr) {
+ char *m = strstr(szBuffer, p->key);
+ if (m) {
+ char *cp, *op, *sp;
+ cp = szCopy;
+ op = szBuffer;
+ while (op != m) *cp++ = *op++;
+ sp = p->value;
+ while (sp && *sp) *cp++ = *sp++;
+ op += strlen(p->key);
+ while (*op) *cp++ = *op++;
+ *cp = 0;
+ memcpy(szBuffer, szCopy, sizeof(szCopy));
+ }
+ }
+ printf(szBuffer);
+ }
+
+ list_free(&substPtr);
+ }
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * QualifyPath --
+ *
+ * This composes the current working directory with a provided path
+ * and returns the fully qualified and normalized path.
+ * Mostly needed to setup paths for testing.
+ */
+
+static int
+QualifyPath(
+ const char *szPath)
+{
+ char szCwd[MAX_PATH + 1];
+ char szTmp[MAX_PATH + 1];
+ char *p;
+ GetCurrentDirectory(MAX_PATH, szCwd);
+ while ((p = strchr(szPath, '/')) && *p)
+ *p = '\\';
+ PathCombine(szTmp, szCwd, szPath);
+ PathCanonicalize(szCwd, szTmp);
+ printf("%s\n", szCwd);
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * indent-tabs-mode: t
+ * tab-width: 8
+ * End:
+ */
diff --git a/autoconf/tea/win/rules.vc b/autoconf/tea/win/rules.vc
new file mode 100644
index 0000000..9947105
--- /dev/null
+++ b/autoconf/tea/win/rules.vc
@@ -0,0 +1,711 @@
+#------------------------------------------------------------------------------
+# rules.vc --
+#
+# Microsoft Visual C++ makefile include for decoding the commandline
+# macros. This file does not need editing to build Tcl.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# Copyright (c) 2001-2003 David Gravereaux.
+# Copyright (c) 2003-2008 Patrick Thoyts
+#------------------------------------------------------------------------------
+
+!ifndef _RULES_VC
+_RULES_VC = 1
+
+cc32 = $(CC) # built-in default.
+link32 = link
+lib32 = lib
+rc32 = $(RC) # built-in default.
+
+!ifndef INSTALLDIR
+### Assume the normal default.
+_INSTALLDIR = C:\Program Files\Tcl
+!else
+### Fix the path separators.
+_INSTALLDIR = $(INSTALLDIR:/=\)
+!endif
+
+#----------------------------------------------------------
+# Set the proper copy method to avoid overwrite questions
+# to the user when copying files and selecting the right
+# "delete all" method.
+#----------------------------------------------------------
+
+!if "$(OS)" == "Windows_NT"
+RMDIR = rmdir /S /Q
+ERRNULL = 2>NUL
+!if ![ver | find "4.0" > nul]
+CPY = echo y | xcopy /i >NUL
+COPY = copy >NUL
+!else
+CPY = xcopy /i /y >NUL
+COPY = copy /y >NUL
+!endif
+!else # "$(OS)" != "Windows_NT"
+CPY = xcopy /i >_JUNK.OUT # On Win98 NUL does not work here.
+COPY = copy >_JUNK.OUT # On Win98 NUL does not work here.
+RMDIR = deltree /Y
+NULL = \NUL # Used in testing directory existence
+ERRNULL = >NUL # Win9x shell cannot redirect stderr
+!endif
+MKDIR = mkdir
+
+#------------------------------------------------------------------------------
+# Determine the host and target architectures and compiler version.
+#------------------------------------------------------------------------------
+
+_HASH=^#
+_VC_MANIFEST_EMBED_EXE=
+_VC_MANIFEST_EMBED_DLL=
+VCVER=0
+!if ![echo VCVERSION=_MSC_VER > vercl.x] \
+ && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \
+ && ![echo ARCH=IX86 >> vercl.x] \
+ && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
+ && ![echo ARCH=AMD64 >> vercl.x] \
+ && ![echo $(_HASH)endif >> vercl.x] \
+ && ![cl -nologo -TC -P vercl.x $(ERRNULL)]
+!include vercl.i
+!if ![echo VCVER= ^\> vercl.vc] \
+ && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
+!include vercl.vc
+!endif
+!endif
+!if ![del $(ERRNUL) /q/f vercl.x vercl.i vercl.vc]
+!endif
+
+!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
+NATIVE_ARCH=IX86
+!else
+NATIVE_ARCH=AMD64
+!endif
+
+# Since MSVC8 we must deal with manifest resources.
+!if $(VCVERSION) >= 1400
+_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1
+_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
+!endif
+
+!ifndef MACHINE
+MACHINE=$(ARCH)
+!endif
+
+!ifndef CFG_ENCODING
+CFG_ENCODING = \"cp1252\"
+!endif
+
+!message ===============================================================================
+
+#----------------------------------------------------------
+# build the helper app we need to overcome nmake's limiting
+# environment.
+#----------------------------------------------------------
+
+!if !exist(nmakehlp.exe)
+!if [$(cc32) -nologo nmakehlp.c -link -subsystem:console > nul]
+!endif
+!endif
+
+#----------------------------------------------------------
+# Test for compiler features
+#----------------------------------------------------------
+
+### test for optimizations
+!if [nmakehlp -c -Ot]
+!message *** Compiler has 'Optimizations'
+OPTIMIZING = 1
+!else
+!message *** Compiler does not have 'Optimizations'
+OPTIMIZING = 0
+!endif
+
+OPTIMIZATIONS =
+
+!if [nmakehlp -c -Ot]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -Ot
+!endif
+
+!if [nmakehlp -c -Oi]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -Oi
+!endif
+
+!if [nmakehlp -c -Op]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -Op
+!endif
+
+!if [nmakehlp -c -fp:strict]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -fp:strict
+!endif
+
+!if [nmakehlp -c -Gs]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -Gs
+!endif
+
+!if [nmakehlp -c -GS]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -GS
+!endif
+
+!if [nmakehlp -c -GL]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -GL
+!endif
+
+DEBUGFLAGS =
+
+!if [nmakehlp -c -RTC1]
+DEBUGFLAGS = $(DEBUGFLAGS) -RTC1
+!elseif [nmakehlp -c -GZ]
+DEBUGFLAGS = $(DEBUGFLAGS) -GZ
+!endif
+
+COMPILERFLAGS =-W3 -DUNICODE -D_UNICODE
+
+# In v13 -GL and -YX are incompatible.
+!if [nmakehlp -c -YX]
+!if ![nmakehlp -c -GL]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
+!endif
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for pentium errata
+!if [nmakehlp -c -QI0f]
+!message *** Compiler has 'Pentium 0x0f fix'
+COMPILERFLAGS = $(COMPILERFLAGS) -QI0f
+!else
+!message *** Compiler does not have 'Pentium 0x0f fix'
+!endif
+!endif
+
+!if "$(MACHINE)" == "IA64"
+### test for Itanium errata
+!if [nmakehlp -c -QIA64_Bx]
+!message *** Compiler has 'B-stepping errata workarounds'
+COMPILERFLAGS = $(COMPILERFLAGS) -QIA64_Bx
+!else
+!message *** Compiler does not have 'B-stepping errata workarounds'
+!endif
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for -align:4096, when align:512 will do.
+!if [nmakehlp -l -opt:nowin98]
+!message *** Linker has 'Win98 alignment problem'
+ALIGN98_HACK = 1
+!else
+!message *** Linker does not have 'Win98 alignment problem'
+ALIGN98_HACK = 0
+!endif
+!else
+ALIGN98_HACK = 0
+!endif
+
+LINKERFLAGS =
+
+!if [nmakehlp -l -ltcg]
+LINKERFLAGS =-ltcg
+!endif
+
+#----------------------------------------------------------
+# Decode the options requested.
+#----------------------------------------------------------
+
+!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]
+STATIC_BUILD = 0
+TCL_THREADS = 1
+DEBUG = 0
+SYMBOLS = 0
+PROFILE = 0
+PGO = 0
+MSVCRT = 0
+LOIMPACT = 0
+TCL_USE_STATIC_PACKAGES = 0
+USE_THREAD_ALLOC = 1
+UNCHECKED = 0
+!else
+!if [nmakehlp -f $(OPTS) "static"]
+!message *** Doing static
+STATIC_BUILD = 1
+!else
+STATIC_BUILD = 0
+!endif
+!if [nmakehlp -f $(OPTS) "msvcrt"]
+!message *** Doing msvcrt
+MSVCRT = 1
+!else
+MSVCRT = 0
+!endif
+!if [nmakehlp -f $(OPTS) "staticpkg"]
+!message *** Doing staticpkg
+TCL_USE_STATIC_PACKAGES = 1
+!else
+TCL_USE_STATIC_PACKAGES = 0
+!endif
+!if [nmakehlp -f $(OPTS) "nothreads"]
+!message *** Compile explicitly for non-threaded tcl
+TCL_THREADS = 0
+!else
+TCL_THREADS = 1
+USE_THREAD_ALLOC= 1
+!endif
+!if [nmakehlp -f $(OPTS) "symbols"]
+!message *** Doing symbols
+DEBUG = 1
+!else
+DEBUG = 0
+!endif
+!if [nmakehlp -f $(OPTS) "pdbs"]
+!message *** Doing pdbs
+SYMBOLS = 1
+!else
+SYMBOLS = 0
+!endif
+!if [nmakehlp -f $(OPTS) "profile"]
+!message *** Doing profile
+PROFILE = 1
+!else
+PROFILE = 0
+!endif
+!if [nmakehlp -f $(OPTS) "pgi"]
+!message *** Doing profile guided optimization instrumentation
+PGO = 1
+!elseif [nmakehlp -f $(OPTS) "pgo"]
+!message *** Doing profile guided optimization
+PGO = 2
+!else
+PGO = 0
+!endif
+!if [nmakehlp -f $(OPTS) "loimpact"]
+!message *** Doing loimpact
+LOIMPACT = 1
+!else
+LOIMPACT = 0
+!endif
+!if [nmakehlp -f $(OPTS) "thrdalloc"]
+!message *** Doing thrdalloc
+USE_THREAD_ALLOC = 1
+!endif
+!if [nmakehlp -f $(OPTS) "tclalloc"]
+!message *** Doing tclalloc
+USE_THREAD_ALLOC = 0
+!endif
+!if [nmakehlp -f $(OPTS) "unchecked"]
+!message *** Doing unchecked
+UNCHECKED = 1
+!else
+UNCHECKED = 0
+!endif
+!endif
+
+
+!if !$(STATIC_BUILD)
+# Make sure we don't build overly fat DLLs.
+MSVCRT = 1
+# We shouldn't statically put the extensions inside the shell when dynamic.
+TCL_USE_STATIC_PACKAGES = 0
+!endif
+
+
+#----------------------------------------------------------
+# Figure-out how to name our intermediate and output directories.
+# We wouldn't want different builds to use the same .obj files
+# by accident.
+#----------------------------------------------------------
+
+#----------------------------------------
+# Naming convention:
+# t = full thread support.
+# s = static library (as opposed to an
+# import library)
+# g = linked to the debug enabled C
+# run-time.
+# x = special static build when it
+# links to the dynamic C run-time.
+#----------------------------------------
+SUFX = tsgx
+
+!if $(DEBUG)
+BUILDDIRTOP = Debug
+!else
+BUILDDIRTOP = Release
+!endif
+
+!if "$(MACHINE)" != "IX86"
+BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE)
+!endif
+!if $(VCVER) > 6
+BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER)
+!endif
+
+!if !$(DEBUG) || $(DEBUG) && $(UNCHECKED)
+SUFX = $(SUFX:g=)
+!endif
+
+TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX
+
+!if !$(STATIC_BUILD)
+TMP_DIRFULL = $(TMP_DIRFULL:Static=)
+SUFX = $(SUFX:s=)
+EXT = dll
+!if $(MSVCRT)
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX = $(SUFX:x=)
+!endif
+!else
+TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
+EXT = lib
+!if !$(MSVCRT)
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX = $(SUFX:x=)
+!endif
+!endif
+
+!if !$(TCL_THREADS)
+TMP_DIRFULL = $(TMP_DIRFULL:Threaded=)
+SUFX = $(SUFX:t=)
+!endif
+
+!ifndef TMP_DIR
+TMP_DIR = $(TMP_DIRFULL)
+!ifndef OUT_DIR
+OUT_DIR = .\$(BUILDDIRTOP)
+!endif
+!else
+!ifndef OUT_DIR
+OUT_DIR = $(TMP_DIR)
+!endif
+!endif
+
+
+#----------------------------------------------------------
+# Decode the statistics requested.
+#----------------------------------------------------------
+
+!if "$(STATS)" == "" || [nmakehlp -f "$(STATS)" "none"]
+TCL_MEM_DEBUG = 0
+TCL_COMPILE_DEBUG = 0
+!else
+!if [nmakehlp -f $(STATS) "memdbg"]
+!message *** Doing memdbg
+TCL_MEM_DEBUG = 1
+!else
+TCL_MEM_DEBUG = 0
+!endif
+!if [nmakehlp -f $(STATS) "compdbg"]
+!message *** Doing compdbg
+TCL_COMPILE_DEBUG = 1
+!else
+TCL_COMPILE_DEBUG = 0
+!endif
+!endif
+
+
+#----------------------------------------------------------
+# Decode the checks requested.
+#----------------------------------------------------------
+
+!if "$(CHECKS)" == "" || [nmakehlp -f "$(CHECKS)" "none"]
+TCL_NO_DEPRECATED = 0
+WARNINGS = -W3
+!else
+!if [nmakehlp -f $(CHECKS) "nodep"]
+!message *** Doing nodep check
+TCL_NO_DEPRECATED = 1
+!else
+TCL_NO_DEPRECATED = 0
+!endif
+!if [nmakehlp -f $(CHECKS) "fullwarn"]
+!message *** Doing full warnings check
+WARNINGS = -W4
+!if [nmakehlp -l -warn:3]
+LINKERFLAGS = $(LINKERFLAGS) -warn:3
+!endif
+!else
+WARNINGS = -W3
+!endif
+!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
+!message *** Doing 64bit portability warnings
+WARNINGS = $(WARNINGS) -Wp64
+!endif
+!endif
+
+!if $(PGO) > 1
+!if [nmakehlp -l -ltcg:pgoptimize]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!elseif $(PGO) > 0
+!if [nmakehlp -l -ltcg:pginstrument]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!endif
+
+#----------------------------------------------------------
+# Set our defines now armed with our options.
+#----------------------------------------------------------
+
+OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS
+
+!if $(TCL_MEM_DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG
+!endif
+!if $(TCL_COMPILE_DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
+!endif
+!if $(TCL_THREADS)
+OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1
+!if $(USE_THREAD_ALLOC)
+OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
+!endif
+!endif
+!if $(STATIC_BUILD)
+OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD
+!endif
+!if $(TCL_NO_DEPRECATED)
+OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED
+!endif
+
+!if !$(DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DNDEBUG
+!if $(OPTIMIZING)
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
+!endif
+!endif
+!if $(PROFILE)
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED
+!endif
+!if "$(MACHINE)" == "IA64" || "$(MACHINE)" == "AMD64"
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT
+!endif
+!if $(VCVERSION) < 1300
+OPTDEFINES = $(OPTDEFINES) -DNO_STRTOI64
+!endif
+
+#----------------------------------------------------------
+# Locate the Tcl headers to build against
+#----------------------------------------------------------
+
+!if "$(PROJECT)" == "tcl"
+
+_TCL_H = ..\generic\tcl.h
+
+!else
+
+# If INSTALLDIR set to tcl root dir then reset to the lib dir.
+!if exist("$(_INSTALLDIR)\include\tcl.h")
+_INSTALLDIR=$(_INSTALLDIR)\lib
+!endif
+
+!if !defined(TCLDIR)
+!if exist("$(_INSTALLDIR)\..\include\tcl.h")
+TCLINSTALL = 1
+_TCLDIR = $(_INSTALLDIR)\..
+_TCL_H = $(_INSTALLDIR)\..\include\tcl.h
+TCLDIR = $(_INSTALLDIR)\..
+!else
+MSG=^
+Failed to find tcl.h. Set the TCLDIR macro.
+!error $(MSG)
+!endif
+!else
+_TCLDIR = $(TCLDIR:/=\)
+!if exist("$(_TCLDIR)\include\tcl.h")
+TCLINSTALL = 1
+_TCL_H = $(_TCLDIR)\include\tcl.h
+!elseif exist("$(_TCLDIR)\generic\tcl.h")
+TCLINSTALL = 0
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!else
+MSG =^
+Failed to find tcl.h. The TCLDIR macro does not appear correct.
+!error $(MSG)
+!endif
+!endif
+!endif
+
+#--------------------------------------------------------------
+# Extract various version numbers from tcl headers
+# The generated file is then included in the makefile.
+#--------------------------------------------------------------
+
+!if [echo REM = This file is generated from rules.vc > versions.vc]
+!endif
+!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
+!endif
+
+# If building the tcl core then we need additional package versions
+!if "$(PROJECT)" == "tcl"
+!if [echo PKG_HTTP_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\http\pkgIndex.tcl http >> versions.vc]
+!endif
+!if [echo PKG_TCLTEST_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\tcltest\pkgIndex.tcl tcltest >> versions.vc]
+!endif
+!if [echo PKG_MSGCAT_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\msgcat\pkgIndex.tcl msgcat >> versions.vc]
+!endif
+!if [echo PKG_PLATFORM_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform " >> versions.vc]
+!endif
+!if [echo PKG_SHELL_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\platform\pkgIndex.tcl "platform::shell" >> versions.vc]
+!endif
+!if [echo PKG_DDE_VER = \>> versions.vc] \
+ && [nmakehlp -V ..\library\dde\pkgIndex.tcl "dde " >> versions.vc]
+!endif
+!if [echo PKG_REG_VER =\>> versions.vc] \
+ && [nmakehlp -V ..\library\reg\pkgIndex.tcl registry >> versions.vc]
+!endif
+!endif
+
+!include versions.vc
+
+#--------------------------------------------------------------
+# Setup tcl version dependent stuff headers
+#--------------------------------------------------------------
+
+!if "$(PROJECT)" != "tcl"
+
+TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+
+!if $(TCL_VERSION) < 81
+TCL_DOES_STUBS = 0
+!else
+TCL_DOES_STUBS = 1
+!endif
+
+!if $(TCLINSTALL)
+TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe"
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH = "$(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe"
+!endif
+TCLSTUBLIB = "$(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib"
+TCLIMPLIB = "$(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib"
+TCL_LIBRARY = $(_TCLDIR)\lib
+TCLREGLIB = "$(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib"
+TCLDDELIB = "$(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib"
+COFFBASE = \must\have\tcl\sources\to\build\this\target
+TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
+TCL_INCLUDES = -I"$(_TCLDIR)\include"
+!else
+TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe"
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe"
+!endif
+TCLSTUBLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib"
+TCLIMPLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib"
+TCL_LIBRARY = $(_TCLDIR)\library
+TCLREGLIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib"
+TCLDDELIB = "$(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib"
+COFFBASE = "$(_TCLDIR)\win\coffbase.txt"
+TCLTOOLSDIR = $(_TCLDIR)\tools
+TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
+!endif
+
+!endif
+
+#-------------------------------------------------------------------------
+# Locate the Tk headers to build against
+#-------------------------------------------------------------------------
+
+!if "$(PROJECT)" == "tk"
+_TK_H = ..\generic\tk.h
+_INSTALLDIR = $(_INSTALLDIR)\..
+!endif
+
+!ifdef PROJECT_REQUIRES_TK
+!if !defined(TKDIR)
+!if exist("$(_INSTALLDIR)\..\include\tk.h")
+TKINSTALL = 1
+_TKDIR = $(_INSTALLDIR)\..
+_TK_H = $(_TKDIR)\include\tk.h
+TKDIR = $(_TKDIR)
+!elseif exist("$(_TCLDIR)\include\tk.h")
+TKINSTALL = 1
+_TKDIR = $(_TCLDIR)
+_TK_H = $(_TKDIR)\include\tk.h
+TKDIR = $(_TKDIR)
+!endif
+!else
+_TKDIR = $(TKDIR:/=\)
+!if exist("$(_TKDIR)\include\tk.h")
+TKINSTALL = 1
+_TK_H = $(_TKDIR)\include\tk.h
+!elseif exist("$(_TKDIR)\generic\tk.h")
+TKINSTALL = 0
+_TK_H = $(_TKDIR)\generic\tk.h
+!else
+MSG =^
+Failed to find tk.h. The TKDIR macro does not appear correct.
+!error $(MSG)
+!endif
+!endif
+!endif
+
+#-------------------------------------------------------------------------
+# Extract Tk version numbers
+#-------------------------------------------------------------------------
+
+!if defined(PROJECT_REQUIRES_TK) || "$(PROJECT)" == "tk"
+
+!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!endif
+
+!include versions.vc
+
+TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
+
+!if "$(PROJECT)" != "tk"
+!if $(TKINSTALL)
+WISH = "$(_TKDIR)\bin\wish$(TK_VERSION)$(SUFX).exe"
+TKSTUBLIB = "$(_TKDIR)\lib\tkstub$(TK_VERSION).lib"
+TKIMPLIB = "$(_TKDIR)\lib\tk$(TK_VERSION)$(SUFX).lib"
+TK_INCLUDES = -I"$(_TKDIR)\include"
+!else
+WISH = "$(_TKDIR)\win\$(BUILDDIRTOP)\wish$(TCL_VERSION)$(SUFX).exe"
+TKSTUBLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tkstub$(TCL_VERSION).lib"
+TKIMPLIB = "$(_TKDIR)\win\$(BUILDDIRTOP)\tk$(TCL_VERSION)$(SUFX).lib"
+TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+!endif
+!endif
+
+!endif
+
+#----------------------------------------------------------
+# Display stats being used.
+#----------------------------------------------------------
+
+!message *** Intermediate directory will be '$(TMP_DIR)'
+!message *** Output directory will be '$(OUT_DIR)'
+!message *** Suffix for binaries will be '$(SUFX)'
+!message *** Optional defines are '$(OPTDEFINES)'
+!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
+!message *** Host architecture is $(NATIVE_ARCH)
+!message *** Compiler options '$(COMPILERFLAGS) $(OPTIMIZATIONS) $(DEBUGFLAGS) $(WARNINGS)'
+!message *** Link options '$(LINKERFLAGS)'
+
+!endif
+
diff --git a/configure b/configure
index 69eb324..9f542b4 100755
--- a/configure
+++ b/configure
@@ -1,12 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.68 for sqlcipher 3.7.17.
-#
-#
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
-# Foundation, Inc.
-#
+# Generated by GNU Autoconf 2.62 for sqlcipher 3.8.6.
#
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
@@ -134,6 +128,31 @@ export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
@@ -167,7 +186,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -212,21 +232,25 @@ IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
- # We cannot yet assume a decent shell, so we have to provide a
- # neutralization value for shells without unset; and this also
- # works around shells that cannot unset nonexistent variables.
- # Preserve -v and -x to the replacement shell.
- BASH_ENV=/dev/null
- ENV=/dev/null
- (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
- export CONFIG_SHELL
- case $- in # ((((
- *v*x* | *x*v* ) as_opts=-vx ;;
- *v* ) as_opts=-v ;;
- *x* ) as_opts=-x ;;
- * ) as_opts= ;;
- esac
- exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
fi
if test x$as_have_required = xno; then :
@@ -328,6 +352,14 @@ $as_echo X"$as_dir" |
} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -449,6 +481,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
# 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 sensitive to this).
@@ -483,16 +519,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -504,28 +540,8 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# 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'"
@@ -706,8 +722,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlcipher'
PACKAGE_TARNAME='sqlcipher'
-PACKAGE_VERSION='3.7.17'
-PACKAGE_STRING='sqlcipher 3.7.17'
+PACKAGE_VERSION='3.8.6'
+PACKAGE_STRING='sqlite 3.8.6'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -769,7 +785,6 @@ TCL_SRC_DIR
TCL_BIN_DIR
TCL_VERSION
TARGET_EXEEXT
-SQLITE_OS_OS2
SQLITE_OS_WIN
SQLITE_OS_UNIX
BUILD_EXEEXT
@@ -1353,8 +1368,6 @@ target=$target_alias
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
- $as_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
@@ -1440,7 +1453,7 @@ 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 sqlcipher 3.7.17 to adapt to many kinds of systems.
+\`configure' configures sqlcipher 3.8.6 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1505,7 +1518,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sqlcipher 3.7.17:";;
+ short | recursive ) echo "Configuration of sqlcipher 3.8.6:";;
esac
cat <<\_ACEOF
@@ -1623,20 +1636,16 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sqlcipher configure 3.7.17
-generated by GNU Autoconf 2.68
+sqlcipher configure 3.8.6
+generated by GNU Autoconf 2.62
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 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
fi
-## ------------------------ ##
-## Autoconf initialization. ##
-## ------------------------ ##
-
# ac_fn_c_try_compile LINENO
# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
@@ -1702,7 +1711,7 @@ $as_echo "$ac_try_echo"; } >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
+ test -x conftest$ac_exeext
}; then :
ac_retval=0
else
@@ -2042,8 +2051,8 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by sqlcipher $as_me 3.7.17, which was
-generated by GNU Autoconf 2.68. Invocation command line was
+It was created by sqlcipher $as_me 3.8.4.3, which was
+generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2552,7 +2561,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2592,7 +2601,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2645,7 +2654,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2686,7 +2695,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_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
@@ -2744,7 +2753,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2788,7 +2797,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3234,8 +3243,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
/* 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);
@@ -3342,7 +3350,7 @@ do
for ac_prog in sed gsed; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+ as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
case `"$ac_path_SED" --version 2>&1` in
@@ -3418,7 +3426,7 @@ do
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
@@ -3484,7 +3492,7 @@ do
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
@@ -3551,7 +3559,7 @@ do
for ac_prog in fgrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+ as_fn_executable_p "$ac_path_FGREP" || continue
# Check for GNU ac_path_FGREP and select it if it is found.
# Check for GNU $ac_path_FGREP
case `"$ac_path_FGREP" --version 2>&1` in
@@ -3804,7 +3812,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3848,7 +3856,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3904,13 +3912,13 @@ if ${lt_cv_nm_interface+:} false; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
- (eval echo "\"\$as_me:3907: $ac_compile\"" >&5)
+ (eval echo "\"\$as_me:3923: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
- (eval echo "\"\$as_me:3910: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval echo "\"\$as_me:3926: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
- (eval echo "\"\$as_me:3913: output\"" >&5)
+ (eval echo "\"\$as_me:3929: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@@ -4174,7 +4182,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4214,7 +4222,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OBJDUMP="objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4489,7 +4497,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="${ac_tool_prefix}ar"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4529,7 +4537,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="ar"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4594,7 +4602,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4634,7 +4642,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4693,7 +4701,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4733,7 +4741,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5116,7 +5124,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 5119 "configure"' > conftest.$ac_ext
+ echo '#line 5135 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -5298,7 +5306,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5338,7 +5346,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5390,7 +5398,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5430,7 +5438,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_NMEDIT="nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5482,7 +5490,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5522,7 +5530,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_LIPO="lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5574,7 +5582,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5614,7 +5622,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL="otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5666,7 +5674,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5706,7 +5714,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL64="otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6641,11 +6649,11 @@ else
-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:6644: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6660: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6648: \$? = $ac_status" >&5
+ echo "$as_me:6664: \$? = $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.
@@ -6980,11 +6988,11 @@ else
-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:6983: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6999: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6987: \$? = $ac_status" >&5
+ echo "$as_me:7003: \$? = $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.
@@ -7085,11 +7093,11 @@ else
-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:7088: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7104: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7092: \$? = $ac_status" >&5
+ echo "$as_me:7108: \$? = $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
@@ -7140,11 +7148,11 @@ else
-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:7143: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7159: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7147: \$? = $ac_status" >&5
+ echo "$as_me:7163: \$? = $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
@@ -9520,7 +9528,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9523 "configure"
+#line 9539 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9616,7 +9624,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 9619 "configure"
+#line 9635 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9880,7 +9888,7 @@ case $as_dir/ in #((
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if as_fn_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.
@@ -9954,7 +9962,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -10178,6 +10186,8 @@ _ACEOF
esac
rm -rf conftest*
fi
+
+
fi
@@ -10317,7 +10327,7 @@ USE_AMALGAMATION=1
# if not, then we fall back to plain tclsh.
# TODO: try other versions before falling back?
#
-for ac_prog in tclsh8.5 tclsh
+for ac_prog in tclsh8.6 tclsh8.5 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
@@ -10335,7 +10345,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_TCLSH_CMD="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -10468,7 +10478,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_BUILD_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -10834,21 +10844,12 @@ 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"
- else
- SQLITE_OS_UNIX=0
- SQLITE_OS_WIN=1
- SQLITE_OS_OS2=0
- CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
- fi
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=1
+ CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
else
SQLITE_OS_UNIX=1
SQLITE_OS_WIN=0
- SQLITE_OS_OS2=0
CFLAGS="$CFLAGS -DSQLITE_OS_UNIX=1"
fi
@@ -10857,7 +10858,6 @@ fi
-
##########
# Figure out all the parameters needed to compile against Tcl.
#
@@ -11887,16 +11887,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -11956,28 +11956,16 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# 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'"
@@ -11998,8 +11986,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by sqlcipher $as_me 3.7.17, which was
-generated by GNU Autoconf 2.68. Invocation command line was
+This file was extended by sqlcipher $as_me 3.8.6, which was
+generated by GNU Autoconf 2.62. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -12064,11 +12052,11 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sqlcipher config.status 3.7.17
-configured by $0, generated by GNU Autoconf 2.68,
- with options \\"\$ac_cs_config\\"
+sqlcipher config.status 3.8.6
+configured by $0, generated by GNU Autoconf 2.62,
+ with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -12158,7 +12146,7 @@ fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
- set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
diff --git a/configure.ac b/configure.ac
index f226f14..2f69fc1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,7 +139,7 @@ USE_AMALGAMATION=1
# if not, then we fall back to plain tclsh.
# TODO: try other versions before falling back?
#
-AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.5 tclsh], none)
+AC_CHECK_PROGS(TCLSH_CMD, [tclsh8.6 tclsh8.5 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.
@@ -370,28 +370,18 @@ 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"
- else
- SQLITE_OS_UNIX=0
- SQLITE_OS_WIN=1
- SQLITE_OS_OS2=0
- CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
- fi
+ SQLITE_OS_UNIX=0
+ SQLITE_OS_WIN=1
+ CFLAGS="$CFLAGS -DSQLITE_OS_WIN=1"
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)
##########
diff --git a/ext/fts1/fts1.c b/ext/fts1/fts1.c
index d5429ff..482cf75 100644
--- a/ext/fts1/fts1.c
+++ b/ext/fts1/fts1.c
@@ -3335,8 +3335,11 @@ int sqlite3Fts1Init(sqlite3 *db){
}
#if !SQLITE_CORE
-int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg,
- const sqlite3_api_routines *pApi){
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fts1_init(sqlite3 *db, char **pzErrMsg,
+ const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi)
return sqlite3Fts1Init(db);
}
diff --git a/ext/fts1/fulltext.c b/ext/fts1/fulltext.c
index e6034ba..313ff30 100644
--- a/ext/fts1/fulltext.c
+++ b/ext/fts1/fulltext.c
@@ -852,8 +852,14 @@ static void fulltext_vtab_destroy(fulltext_vtab *v){
** 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){
+static int fulltextConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc,
+ const char * const *argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr
+){
int rc;
fulltext_vtab *v;
sqlite3_tokenizer_module *m = NULL;
@@ -898,8 +904,14 @@ static int fulltextConnect(sqlite3 *db, void *pAux, int argc, char **argv,
return SQLITE_OK;
}
-static int fulltextCreate(sqlite3 *db, void *pAux, int argc, char **argv,
- sqlite3_vtab **ppVTab){
+static int fulltextCreate(
+ sqlite3 *db,
+ void *pAux,
+ int argc,
+ const char * const *argv,
+ sqlite3_vtab **ppVTab,
+ char **pzErr
+){
int rc;
assert( argc>=3 );
@@ -934,7 +946,7 @@ static int fulltextCreate(sqlite3 *db, void *pAux, int argc, char **argv,
"create index %_index on %_term(term, first)");
if( rc!=SQLITE_OK ) return rc;
- return fulltextConnect(db, pAux, argc, argv, ppVTab);
+ return fulltextConnect(db, pAux, argc, argv, ppVTab, pzErr);
}
/* Decide how to handle an SQL query.
@@ -1488,8 +1500,11 @@ int fulltext_init(sqlite3 *db){
}
#if !SQLITE_CORE
-int sqlite3_extension_init(sqlite3 *db, char **pzErrMsg,
- const sqlite3_api_routines *pApi){
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fulltext_init(sqlite3 *db, char **pzErrMsg,
+ const sqlite3_api_routines *pApi){
SQLITE_EXTENSION_INIT2(pApi)
return fulltext_init(db);
}
diff --git a/ext/fts2/fts2.c b/ext/fts2/fts2.c
index f008ce6..0405fb7 100644
--- a/ext/fts2/fts2.c
+++ b/ext/fts2/fts2.c
@@ -6844,7 +6844,10 @@ int sqlite3Fts2Init(sqlite3 *db){
}
#if !SQLITE_CORE
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fts2_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/ext/fts2/fts2_hash.c b/ext/fts2/fts2_hash.c
index f22fcc9..3596dcf 100644
--- a/ext/fts2/fts2_hash.c
+++ b/ext/fts2/fts2_hash.c
@@ -30,6 +30,8 @@
#include <string.h>
#include "sqlite3.h"
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT3
#include "fts2_hash.h"
/*
diff --git a/ext/fts2/fts2_porter.c b/ext/fts2/fts2_porter.c
index 16620b9..881baf7 100644
--- a/ext/fts2/fts2_porter.c
+++ b/ext/fts2/fts2_porter.c
@@ -30,6 +30,9 @@
#include <stdio.h>
#include <string.h>
+#include "sqlite3.h"
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT3
#include "fts2_tokenizer.h"
/*
diff --git a/ext/fts2/fts2_tokenizer.c b/ext/fts2/fts2_tokenizer.c
index a93790c..a3d6a63 100644
--- a/ext/fts2/fts2_tokenizer.c
+++ b/ext/fts2/fts2_tokenizer.c
@@ -28,7 +28,7 @@
#include "sqlite3.h"
#include "sqlite3ext.h"
-SQLITE_EXTENSION_INIT1
+SQLITE_EXTENSION_INIT3
#include "fts2_hash.h"
#include "fts2_tokenizer.h"
diff --git a/ext/fts2/fts2_tokenizer1.c b/ext/fts2/fts2_tokenizer1.c
index 7e13366..fe4f9eb 100644
--- a/ext/fts2/fts2_tokenizer1.c
+++ b/ext/fts2/fts2_tokenizer1.c
@@ -30,6 +30,9 @@
#include <stdio.h>
#include <string.h>
+#include "sqlite3.h"
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT3
#include "fts2_tokenizer.h"
typedef struct simple_tokenizer {
diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c
index c00a13f..4f4b667 100644
--- a/ext/fts3/fts3.c
+++ b/ext/fts3/fts3.c
@@ -330,21 +330,37 @@ int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
return (int) (q - (unsigned char *)p);
}
+#define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \
+ v = (v & mask1) | ( (*ptr++) << shift ); \
+ if( (v & mask2)==0 ){ var = v; return ret; }
+#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \
+ v = (*ptr++); \
+ if( (v & mask2)==0 ){ var = v; return ret; }
+
/*
** 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.
*/
int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
- const unsigned char *q = (const unsigned char *) p;
- sqlite_uint64 x = 0, y = 1;
- while( (*q&0x80)==0x80 && q-(unsigned char *)p<FTS3_VARINT_MAX ){
- x += y * (*q++ & 0x7f);
- y <<= 7;
- }
- x += y * (*q++);
- *v = (sqlite_int64) x;
- return (int) (q - (unsigned char *)p);
+ const char *pStart = p;
+ u32 a;
+ u64 b;
+ int shift;
+
+ GETVARINT_INIT(a, p, 0, 0x00, 0x80, *v, 1);
+ GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *v, 2);
+ GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *v, 3);
+ GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *v, 4);
+ b = (a & 0x0FFFFFFF );
+
+ for(shift=28; shift<=63; shift+=7){
+ u64 c = *p++;
+ b += (c&0x7F) << shift;
+ if( (c & 0x80)==0 ) break;
+ }
+ *v = b;
+ return (int)(p - pStart);
}
/*
@@ -352,10 +368,21 @@ int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
** 32-bit integer before it is returned.
*/
int sqlite3Fts3GetVarint32(const char *p, int *pi){
- sqlite_int64 i;
- int ret = sqlite3Fts3GetVarint(p, &i);
- *pi = (int) i;
- return ret;
+ u32 a;
+
+#ifndef fts3GetVarint32
+ GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1);
+#else
+ a = (*p++);
+ assert( a & 0x80 );
+#endif
+
+ GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *pi, 2);
+ GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3);
+ GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4);
+ a = (a & 0x0FFFFFFF );
+ *pi = (int)(a | ((u32)(*p & 0x0F) << 28));
+ return 5;
}
/*
@@ -1081,6 +1108,8 @@ static int fts3InitVtab(
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
char *zContent = 0; /* content=? parameter (or NULL) */
char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
+ char **azNotindexed = 0; /* The set of notindexed= columns */
+ int nNotindexed = 0; /* Size of azNotindexed[] array */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
@@ -1090,9 +1119,19 @@ static int fts3InitVtab(
nDb = (int)strlen(argv[1]) + 1;
nName = (int)strlen(argv[2]) + 1;
- aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) );
- if( !aCol ) return SQLITE_NOMEM;
- memset((void *)aCol, 0, sizeof(const char *) * (argc-2));
+ nByte = sizeof(const char *) * (argc-2);
+ aCol = (const char **)sqlite3_malloc(nByte);
+ if( aCol ){
+ memset((void*)aCol, 0, nByte);
+ azNotindexed = (char **)sqlite3_malloc(nByte);
+ }
+ if( azNotindexed ){
+ memset(azNotindexed, 0, nByte);
+ }
+ if( !aCol || !azNotindexed ){
+ rc = SQLITE_NOMEM;
+ goto fts3_init_out;
+ }
/* Loop through all of the arguments passed by the user to the FTS3/4
** module (i.e. all the column names and special arguments). This loop
@@ -1131,7 +1170,8 @@ static int fts3InitVtab(
{ "uncompress", 10 }, /* 3 -> UNCOMPRESS */
{ "order", 5 }, /* 4 -> ORDER */
{ "content", 7 }, /* 5 -> CONTENT */
- { "languageid", 10 } /* 6 -> LANGUAGEID */
+ { "languageid", 10 }, /* 6 -> LANGUAGEID */
+ { "notindexed", 10 } /* 7 -> NOTINDEXED */
};
int iOpt;
@@ -1197,6 +1237,11 @@ static int fts3InitVtab(
zLanguageid = zVal;
zVal = 0;
break;
+
+ case 7: /* NOTINDEXED */
+ azNotindexed[nNotindexed++] = zVal;
+ zVal = 0;
+ break;
}
}
sqlite3_free(zVal);
@@ -1268,6 +1313,7 @@ static int fts3InitVtab(
nByte = sizeof(Fts3Table) + /* Fts3Table */
nCol * sizeof(char *) + /* azColumn */
nIndex * sizeof(struct Fts3Index) + /* aIndex */
+ nCol * sizeof(u8) + /* abNotindexed */
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
@@ -1287,7 +1333,7 @@ static int fts3InitVtab(
p->bHasStat = isFts4;
p->bFts4 = isFts4;
p->bDescIdx = bDescIdx;
- p->bAutoincrmerge = 0xff; /* 0xff means setting unknown */
+ p->nAutoincrmerge = 0xff; /* 0xff means setting unknown */
p->zContentTbl = zContent;
p->zLanguageid = zLanguageid;
zContent = 0;
@@ -1301,9 +1347,10 @@ static int fts3InitVtab(
for(i=0; i<nIndex; i++){
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
}
+ p->abNotindexed = (u8 *)&p->aIndex[nIndex];
/* Fill in the zName and zDb fields of the vtab structure. */
- zCsr = (char *)&p->aIndex[nIndex];
+ zCsr = (char *)&p->abNotindexed[nCol];
p->zName = zCsr;
memcpy(zCsr, argv[2], nName);
zCsr += nName;
@@ -1324,7 +1371,28 @@ static int fts3InitVtab(
assert( zCsr <= &((char *)p)[nByte] );
}
- if( (zCompress==0)!=(zUncompress==0) ){
+ /* Fill in the abNotindexed array */
+ for(iCol=0; iCol<nCol; iCol++){
+ int n = (int)strlen(p->azColumn[iCol]);
+ for(i=0; i<nNotindexed; i++){
+ char *zNot = azNotindexed[i];
+ if( zNot && n==(int)strlen(zNot)
+ && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n)
+ ){
+ p->abNotindexed[iCol] = 1;
+ sqlite3_free(zNot);
+ azNotindexed[i] = 0;
+ }
+ }
+ }
+ for(i=0; i<nNotindexed; i++){
+ if( azNotindexed[i] ){
+ *pzErr = sqlite3_mprintf("no such column: %s", azNotindexed[i]);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){
char const *zMiss = (zCompress==0 ? "compress" : "uncompress");
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss);
@@ -1344,10 +1412,7 @@ static int fts3InitVtab(
** addition of a %_stat table so that it can use incremental merge.
*/
if( !isFts4 && !isCreate ){
- int rc2 = SQLITE_OK;
- fts3DbExec(&rc2, db, "SELECT 1 FROM %Q.'%q_stat' WHERE id=2",
- p->zDb, p->zName);
- if( rc2==SQLITE_OK ) p->bHasStat = 1;
+ p->bHasStat = 2;
}
/* Figure out the page-size for the database. This is required in order to
@@ -1365,7 +1430,9 @@ fts3_init_out:
sqlite3_free(zUncompress);
sqlite3_free(zContent);
sqlite3_free(zLanguageid);
+ for(i=0; i<nNotindexed; i++) sqlite3_free(azNotindexed[i]);
sqlite3_free((void *)aCol);
+ sqlite3_free((void *)azNotindexed);
if( rc!=SQLITE_OK ){
if( p ){
fts3DisconnectMethod((sqlite3_vtab *)p);
@@ -1404,6 +1471,19 @@ static int fts3CreateMethod(
return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
}
+/*
+** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support estimatedRows. In that case this function is a no-op.
+*/
+static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
+#if SQLITE_VERSION_NUMBER>=3008002
+ if( sqlite3_libversion_number()>=3008002 ){
+ pIdxInfo->estimatedRows = nRow;
+ }
+#endif
+}
+
/*
** Implementation of the xBestIndex method for FTS3 tables. There
** are three possible strategies, in order of preference:
@@ -1416,23 +1496,40 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts3Table *p = (Fts3Table *)pVTab;
int i; /* Iterator variable */
int iCons = -1; /* Index of constraint to use */
+
int iLangidCons = -1; /* Index of langid=x constraint, if present */
+ int iDocidGe = -1; /* Index of docid>=x constraint, if present */
+ int iDocidLe = -1; /* Index of docid<=x constraint, if present */
+ int iIdx;
/* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient
** strategy is possible.
*/
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
- pInfo->estimatedCost = 500000;
+ pInfo->estimatedCost = 5000000;
for(i=0; i<pInfo->nConstraint; i++){
+ int bDocid; /* True if this constraint is on docid */
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
- if( pCons->usable==0 ) continue;
+ if( pCons->usable==0 ){
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ /* There exists an unusable MATCH constraint. This means that if
+ ** the planner does elect to use the results of this call as part
+ ** of the overall query plan the user will see an "unable to use
+ ** function MATCH in the requested context" error. To discourage
+ ** this, return a very high cost here. */
+ pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
+ pInfo->estimatedCost = 1e50;
+ fts3SetEstimatedRows(pInfo, ((sqlite3_int64)1) << 50);
+ return SQLITE_OK;
+ }
+ continue;
+ }
+
+ bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
- if( iCons<0
- && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
- && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
- ){
+ if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
pInfo->idxNum = FTS3_DOCID_SEARCH;
pInfo->estimatedCost = 1.0;
iCons = i;
@@ -1461,14 +1558,38 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
){
iLangidCons = i;
}
+
+ if( bDocid ){
+ switch( pCons->op ){
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ iDocidGe = i;
+ break;
+
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ iDocidLe = i;
+ break;
+ }
+ }
}
+ iIdx = 1;
if( iCons>=0 ){
- pInfo->aConstraintUsage[iCons].argvIndex = 1;
+ pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
pInfo->aConstraintUsage[iCons].omit = 1;
}
if( iLangidCons>=0 ){
- pInfo->aConstraintUsage[iLangidCons].argvIndex = 2;
+ pInfo->idxNum |= FTS3_HAVE_LANGID;
+ pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
+ }
+ if( iDocidGe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
+ pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
+ }
+ if( iDocidLe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
+ pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
}
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
@@ -1646,10 +1767,10 @@ static int fts3ScanInteriorNode(
/* Load the next term on the node into zBuffer. Use realloc() to expand
** the size of zBuffer if required. */
if( !isFirstTerm ){
- zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
+ zCsr += fts3GetVarint32(zCsr, &nPrefix);
}
isFirstTerm = 0;
- zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
+ zCsr += fts3GetVarint32(zCsr, &nSuffix);
if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
rc = FTS_CORRUPT_VTAB;
@@ -1737,7 +1858,7 @@ static int fts3SelectLeaf(
assert( piLeaf || piLeaf2 );
- sqlite3Fts3GetVarint32(zNode, &iHeight);
+ fts3GetVarint32(zNode, &iHeight);
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
@@ -1939,11 +2060,11 @@ static void fts3PoslistMerge(
int iCol1; /* The current column index in pp1 */
int iCol2; /* The current column index in pp2 */
- if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1);
+ if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1);
else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
else iCol1 = 0;
- if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2);
+ if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2);
else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
else iCol2 = 0;
@@ -2036,11 +2157,11 @@ static int fts3PoslistPhraseMerge(
assert( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
}
if( *p2==POS_COLUMN ){
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
while( 1 ){
@@ -2090,9 +2211,9 @@ static int fts3PoslistPhraseMerge(
if( 0==*p1 || 0==*p2 ) break;
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
/* Advance pointer p1 or p2 (whichever corresponds to the smaller of
@@ -2104,12 +2225,12 @@ static int fts3PoslistPhraseMerge(
fts3ColumnlistCopy(0, &p1);
if( 0==*p1 ) break;
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
}else{
fts3ColumnlistCopy(0, &p2);
if( 0==*p2 ) break;
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
}
@@ -2916,6 +3037,33 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
+** The following are copied from sqliteInt.h.
+**
+** 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.
+*/
+#ifndef SQLITE_AMALGAMATION
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** If the numeric type of argument pVal is "integer", then return it
+** converted to a 64-bit signed integer. Otherwise, return a copy of
+** the second parameter, iDefault.
+*/
+static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){
+ if( pVal ){
+ int eType = sqlite3_value_numeric_type(pVal);
+ if( eType==SQLITE_INTEGER ){
+ return sqlite3_value_int64(pVal);
+ }
+ }
+ return iDefault;
+}
+
+/*
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
** information.
@@ -2940,40 +3088,58 @@ static int fts3FilterMethod(
){
int rc;
char *zSql; /* SQL statement used to access %_content */
+ int eSearch;
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
+ sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */
+ sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */
+ sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */
+ sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */
+ int iIdx;
+
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
- assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
- assert( nVal==0 || nVal==1 || nVal==2 );
- assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
+ eSearch = (idxNum & 0x0000FFFF);
+ assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
+ /* Collect arguments into local variables */
+ iIdx = 0;
+ if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
+ assert( iIdx==nVal );
+
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
+ /* Set the lower and upper bounds on docids to return */
+ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
+ pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
+
if( idxStr ){
pCsr->bDesc = (idxStr[0]=='D');
}else{
pCsr->bDesc = p->bDescIdx;
}
- pCsr->eSearch = (i16)idxNum;
+ pCsr->eSearch = (i16)eSearch;
- if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
- int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
- const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
+ if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){
+ int iCol = eSearch-FTS3_FULLTEXT_SEARCH;
+ const char *zQuery = (const char *)sqlite3_value_text(pCons);
- if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+ if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){
return SQLITE_NOMEM;
}
pCsr->iLangid = 0;
- if( nVal==2 ) pCsr->iLangid = sqlite3_value_int(apVal[1]);
+ if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid);
assert( p->base.zErrMsg==0 );
rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
@@ -2984,11 +3150,7 @@ static int fts3FilterMethod(
return rc;
}
- rc = sqlite3Fts3ReadLock(p);
- if( rc!=SQLITE_OK ) return rc;
-
rc = fts3EvalStart(pCsr);
-
sqlite3Fts3SegmentsClose(p);
if( rc!=SQLITE_OK ) return rc;
pCsr->pNextId = pCsr->aDoclist;
@@ -3000,7 +3162,7 @@ static int fts3FilterMethod(
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
- if( idxNum==FTS3_FULLSCAN_SEARCH ){
+ if( eSearch==FTS3_FULLSCAN_SEARCH ){
zSql = sqlite3_mprintf(
"SELECT %s ORDER BY rowid %s",
p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
@@ -3011,10 +3173,10 @@ static int fts3FilterMethod(
}else{
rc = SQLITE_NOMEM;
}
- }else if( idxNum==FTS3_DOCID_SEARCH ){
+ }else if( eSearch==FTS3_DOCID_SEARCH ){
rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
if( rc==SQLITE_OK ){
- rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons);
}
}
if( rc!=SQLITE_OK ) return rc;
@@ -3142,7 +3304,10 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table*)pVtab;
int rc = sqlite3Fts3PendingTermsFlush(p);
- if( rc==SQLITE_OK && p->bAutoincrmerge==1 && p->nLeafAdd>(nMinMerge/16) ){
+ if( rc==SQLITE_OK
+ && p->nLeafAdd>(nMinMerge/16)
+ && p->nAutoincrmerge && p->nAutoincrmerge!=0xff
+ ){
int mxLevel = 0; /* Maximum relative level value in db */
int A; /* Incr-merge parameter A */
@@ -3150,14 +3315,41 @@ static int fts3SyncMethod(sqlite3_vtab *pVtab){
assert( rc==SQLITE_OK || mxLevel==0 );
A = p->nLeafAdd * mxLevel;
A += (A/2);
- if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, 8);
+ if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, p->nAutoincrmerge);
}
sqlite3Fts3SegmentsClose(p);
return rc;
}
/*
-** Implementation of xBegin() method. This is a no-op.
+** If it is currently unknown whether or not the FTS table has an %_stat
+** table (if p->bHasStat==2), attempt to determine this (set p->bHasStat
+** to 0 or 1). Return SQLITE_OK if successful, or an SQLite error code
+** if an error occurs.
+*/
+static int fts3SetHasStat(Fts3Table *p){
+ int rc = SQLITE_OK;
+ if( p->bHasStat==2 ){
+ const char *zFmt ="SELECT 1 FROM %Q.sqlite_master WHERE tbl_name='%q_stat'";
+ char *zSql = sqlite3_mprintf(zFmt, p->zDb, p->zName);
+ if( zSql ){
+ sqlite3_stmt *pStmt = 0;
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ int bHasStat = (sqlite3_step(pStmt)==SQLITE_ROW);
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) p->bHasStat = bHasStat;
+ }
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ return rc;
+}
+
+/*
+** Implementation of xBegin() method.
*/
static int fts3BeginMethod(sqlite3_vtab *pVtab){
Fts3Table *p = (Fts3Table*)pVtab;
@@ -3168,7 +3360,7 @@ static int fts3BeginMethod(sqlite3_vtab *pVtab){
TESTONLY( p->inTransaction = 1 );
TESTONLY( p->mxSavepoint = -1; );
p->nLeafAdd = 0;
- return SQLITE_OK;
+ return fts3SetHasStat(p);
}
/*
@@ -3417,6 +3609,10 @@ static int fts3RenameMethod(
sqlite3 *db = p->db; /* Database connection */
int rc; /* Return Code */
+ /* At this point it must be known if the %_stat table exists or not.
+ ** So bHasStat may not be 2. */
+ rc = fts3SetHasStat(p);
+
/* As it happens, the pending terms table is always empty here. This is
** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
** always opens a savepoint transaction. And the xSavepoint() method
@@ -3424,7 +3620,9 @@ static int fts3RenameMethod(
** PendingTermsFlush() in in case that changes.
*/
assert( p->nPendingData==0 );
- rc = sqlite3Fts3PendingTermsFlush(p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ }
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
@@ -3552,7 +3750,7 @@ static void hashDestroy(void *p){
*/
void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule);
#endif
#ifdef SQLITE_ENABLE_ICU
@@ -3570,7 +3768,7 @@ int sqlite3Fts3Init(sqlite3 *db){
Fts3Hash *pHash = 0;
const sqlite3_tokenizer_module *pSimple = 0;
const sqlite3_tokenizer_module *pPorter = 0;
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
const sqlite3_tokenizer_module *pUnicode = 0;
#endif
@@ -3579,7 +3777,7 @@ int sqlite3Fts3Init(sqlite3 *db){
sqlite3Fts3IcuTokenizerModule(&pIcu);
#endif
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
sqlite3Fts3UnicodeTokenizer(&pUnicode);
#endif
@@ -3607,7 +3805,7 @@ int sqlite3Fts3Init(sqlite3 *db){
if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
|| sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
|| sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
#endif
#ifdef SQLITE_ENABLE_ICU
@@ -3906,6 +4104,12 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
}
/*
+** Maximum number of tokens a phrase may have to be considered for the
+** incremental doclists strategy.
+*/
+#define MAX_INCR_PHRASE_TOKENS 4
+
+/*
** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
** function has been called successfully on an Fts3Phrase, it may be
@@ -3918,23 +4122,43 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
*/
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
- int rc; /* Error code */
- Fts3PhraseToken *pFirst = &p->aToken[0];
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int rc = SQLITE_OK; /* Error code */
+ int i;
- if( pCsr->bDesc==pTab->bDescIdx
- && bOptOk==1
- && p->nToken==1
- && pFirst->pSegcsr
- && pFirst->pSegcsr->bLookup
- && pFirst->bFirst==0
- ){
+ /* Determine if doclists may be loaded from disk incrementally. This is
+ ** possible if the bOptOk argument is true, the FTS doclists will be
+ ** scanned in forward order, and the phrase consists of
+ ** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
+ ** tokens or prefix tokens that cannot use a prefix-index. */
+ int bHaveIncr = 0;
+ int bIncrOk = (bOptOk
+ && pCsr->bDesc==pTab->bDescIdx
+ && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
+ && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
+#ifdef SQLITE_TEST
+ && pTab->bNoIncrDoclist==0
+#endif
+ );
+ for(i=0; bIncrOk==1 && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){
+ bIncrOk = 0;
+ }
+ if( pToken->pSegcsr ) bHaveIncr = 1;
+ }
+
+ if( bIncrOk && bHaveIncr ){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
- rc = sqlite3Fts3MsrIncrStart(
- pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
+ for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ Fts3MultiSegReader *pSegcsr = pToken->pSegcsr;
+ if( pSegcsr ){
+ rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n);
+ }
+ }
p->bIncr = 1;
-
}else{
/* Load the full doclist for the phrase into memory. */
rc = fts3EvalPhraseLoad(pCsr, p);
@@ -4044,15 +4268,125 @@ void sqlite3Fts3DoclistNext(
}
/*
-** Attempt to move the phrase iterator to point to the next matching docid.
+** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof
+** to true if EOF is reached.
+*/
+static void fts3EvalDlPhraseNext(
+ Fts3Table *pTab,
+ Fts3Doclist *pDL,
+ u8 *pbEof
+){
+ char *pIter; /* Used to iterate through aAll */
+ char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
+
+ if( pDL->pNextDocid ){
+ pIter = pDL->pNextDocid;
+ }else{
+ pIter = pDL->aAll;
+ }
+
+ if( pIter>=pEnd ){
+ /* We have already reached the end of this doclist. EOF. */
+ *pbEof = 1;
+ }else{
+ sqlite3_int64 iDelta;
+ pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
+ if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
+ pDL->iDocid += iDelta;
+ }else{
+ pDL->iDocid -= iDelta;
+ }
+ pDL->pList = pIter;
+ fts3PoslistCopy(0, &pIter);
+ pDL->nList = (int)(pIter - pDL->pList);
+
+ /* pIter now points just past the 0x00 that terminates the position-
+ ** list for document pDL->iDocid. However, if this position-list was
+ ** edited in place by fts3EvalNearTrim(), then pIter may not actually
+ ** point to the start of the next docid value. The following line deals
+ ** with this case by advancing pIter past the zero-padding added by
+ ** fts3EvalNearTrim(). */
+ while( pIter<pEnd && *pIter==0 ) pIter++;
+
+ pDL->pNextDocid = pIter;
+ assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
+ *pbEof = 0;
+ }
+}
+
+/*
+** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext().
+*/
+typedef struct TokenDoclist TokenDoclist;
+struct TokenDoclist {
+ int bIgnore;
+ sqlite3_int64 iDocid;
+ char *pList;
+ int nList;
+};
+
+/*
+** Token pToken is an incrementally loaded token that is part of a
+** multi-token phrase. Advance it to the next matching document in the
+** database and populate output variable *p with the details of the new
+** entry. Or, if the iterator has reached EOF, set *pbEof to true.
+**
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
+*/
+static int incrPhraseTokenNext(
+ Fts3Table *pTab, /* Virtual table handle */
+ Fts3Phrase *pPhrase, /* Phrase to advance token of */
+ int iToken, /* Specific token to advance */
+ TokenDoclist *p, /* OUT: Docid and doclist for new entry */
+ u8 *pbEof /* OUT: True if iterator is at EOF */
+){
+ int rc = SQLITE_OK;
+
+ if( pPhrase->iDoclistToken==iToken ){
+ assert( p->bIgnore==0 );
+ assert( pPhrase->aToken[iToken].pSegcsr==0 );
+ fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof);
+ p->pList = pPhrase->doclist.pList;
+ p->nList = pPhrase->doclist.nList;
+ p->iDocid = pPhrase->doclist.iDocid;
+ }else{
+ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
+ assert( pToken->pDeferred==0 );
+ assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 );
+ if( pToken->pSegcsr ){
+ assert( p->bIgnore==0 );
+ rc = sqlite3Fts3MsrIncrNext(
+ pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList
+ );
+ if( p->pList==0 ) *pbEof = 1;
+ }else{
+ p->bIgnore = 1;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+** The phrase iterator passed as the second argument:
+**
+** * features at least one token that uses an incremental doclist, and
+**
+** * does not contain any deferred tokens.
+**
+** Advance it to the next matching documnent in the database and populate
+** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
+**
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
*/
-static int fts3EvalPhraseNext(
+static int fts3EvalIncrPhraseNext(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Phrase *p, /* Phrase object to advance to next docid */
u8 *pbEof /* OUT: Set to 1 if EOF */
@@ -4060,57 +4394,116 @@ static int fts3EvalPhraseNext(
int rc = SQLITE_OK;
Fts3Doclist *pDL = &p->doclist;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ u8 bEof = 0;
- if( p->bIncr ){
- assert( p->nToken==1 );
- assert( pDL->pNextDocid==0 );
+ /* This is only called if it is guaranteed that the phrase has at least
+ ** one incremental token. In which case the bIncr flag is set. */
+ assert( p->bIncr==1 );
+
+ if( p->nToken==1 && p->bIncr ){
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
- if( rc==SQLITE_OK && !pDL->pList ){
- *pbEof = 1;
+ if( pDL->pList==0 ) bEof = 1;
+ }else{
+ int bDescDoclist = pCsr->bDesc;
+ struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS];
+
+ memset(a, 0, sizeof(a));
+ assert( p->nToken<=MAX_INCR_PHRASE_TOKENS );
+ assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS );
+
+ while( bEof==0 ){
+ int bMaxSet = 0;
+ sqlite3_int64 iMax = 0; /* Largest docid for all iterators */
+ int i; /* Used to iterate through tokens */
+
+ /* Advance the iterator for each token in the phrase once. */
+ for(i=0; rc==SQLITE_OK && i<p->nToken && bEof==0; i++){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){
+ iMax = a[i].iDocid;
+ bMaxSet = 1;
+ }
+ }
+ assert( rc!=SQLITE_OK || a[p->nToken-1].bIgnore==0 );
+ assert( rc!=SQLITE_OK || bMaxSet );
+
+ /* Keep advancing iterators until they all point to the same document */
+ for(i=0; i<p->nToken; i++){
+ while( rc==SQLITE_OK && bEof==0
+ && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
+ ){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
+ iMax = a[i].iDocid;
+ i = 0;
+ }
+ }
+ }
+
+ /* Check if the current entries really are a phrase match */
+ if( bEof==0 ){
+ int nList = 0;
+ int nByte = a[p->nToken-1].nList;
+ char *aDoclist = sqlite3_malloc(nByte+1);
+ if( !aDoclist ) return SQLITE_NOMEM;
+ memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
+
+ for(i=0; i<(p->nToken-1); i++){
+ if( a[i].bIgnore==0 ){
+ char *pL = a[i].pList;
+ char *pR = aDoclist;
+ char *pOut = aDoclist;
+ int nDist = p->nToken-1-i;
+ int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR);
+ if( res==0 ) break;
+ nList = (int)(pOut - aDoclist);
+ }
+ }
+ if( i==(p->nToken-1) ){
+ pDL->iDocid = iMax;
+ pDL->pList = aDoclist;
+ pDL->nList = nList;
+ pDL->bFreeList = 1;
+ break;
+ }
+ sqlite3_free(aDoclist);
+ }
}
+ }
+
+ *pbEof = bEof;
+ return rc;
+}
+
+/*
+** Attempt to move the phrase iterator to point to the next matching docid.
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
+**
+** If there is no "next" entry and no error occurs, then *pbEof is set to
+** 1 before returning. Otherwise, if no error occurs and the iterator is
+** successfully advanced, *pbEof is set to 0.
+*/
+static int fts3EvalPhraseNext(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Phrase *p, /* Phrase object to advance to next docid */
+ u8 *pbEof /* OUT: Set to 1 if EOF */
+){
+ int rc = SQLITE_OK;
+ Fts3Doclist *pDL = &p->doclist;
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+
+ if( p->bIncr ){
+ rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
}else{
- char *pIter; /* Used to iterate through aAll */
- char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
- if( pDL->pNextDocid ){
- pIter = pDL->pNextDocid;
- }else{
- pIter = pDL->aAll;
- }
-
- if( pIter>=pEnd ){
- /* We have already reached the end of this doclist. EOF. */
- *pbEof = 1;
- }else{
- sqlite3_int64 iDelta;
- pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
- if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
- pDL->iDocid += iDelta;
- }else{
- pDL->iDocid -= iDelta;
- }
- pDL->pList = pIter;
- fts3PoslistCopy(0, &pIter);
- pDL->nList = (int)(pIter - pDL->pList);
-
- /* pIter now points just past the 0x00 that terminates the position-
- ** list for document pDL->iDocid. However, if this position-list was
- ** edited in place by fts3EvalNearTrim(), then pIter may not actually
- ** point to the start of the next docid value. The following line deals
- ** with this case by advancing pIter past the zero-padding added by
- ** fts3EvalNearTrim(). */
- while( pIter<pEnd && *pIter==0 ) pIter++;
-
- pDL->pNextDocid = pIter;
- assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
- *pbEof = 0;
- }
+ fts3EvalDlPhraseNext(pTab, pDL, pbEof);
}
return rc;
@@ -4135,7 +4528,6 @@ static int fts3EvalPhraseNext(
static void fts3EvalStartReaders(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expression to initialize phrases in */
- int bOptOk, /* True to enable incremental loading */
int *pRc /* IN/OUT: Error code */
){
if( pExpr && SQLITE_OK==*pRc ){
@@ -4146,10 +4538,10 @@ static void fts3EvalStartReaders(
if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
}
pExpr->bDeferred = (i==nToken);
- *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
+ *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase);
}else{
- fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
- fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pRight, pRc);
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
}
}
@@ -4391,7 +4783,7 @@ static int fts3EvalSelectDeferred(
** overflowing the 32-bit integer it is stored in. */
if( ii<12 ) nLoad4 = nLoad4*4;
- if( ii==0 || pTC->pPhrase->nToken>1 ){
+ if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){
/* Either this is the cheapest token in the entire query, or it is
** part of a multi-token phrase. Either way, the entire doclist will
** (eventually) be loaded into memory. It may as well be now. */
@@ -4471,7 +4863,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
}
#endif
- fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
+ fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc);
return rc;
}
@@ -4954,6 +5346,16 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
pCsr->iPrevId = pExpr->iDocid;
}while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
}
+
+ /* Check if the cursor is past the end of the docid range specified
+ ** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */
+ if( rc==SQLITE_OK && (
+ (pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid)
+ || (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid)
+ )){
+ pCsr->isEof = 1;
+ }
+
return rc;
}
@@ -4977,12 +5379,16 @@ static void fts3EvalRestart(
if( pPhrase ){
fts3EvalInvalidatePoslist(pPhrase);
if( pPhrase->bIncr ){
- assert( pPhrase->nToken==1 );
- assert( pPhrase->aToken[0].pSegcsr );
- sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
+ int i;
+ for(i=0; i<pPhrase->nToken; i++){
+ Fts3PhraseToken *pToken = &pPhrase->aToken[i];
+ assert( pToken->pDeferred==0 );
+ if( pToken->pSegcsr ){
+ sqlite3Fts3MsrIncrRestart(pToken->pSegcsr);
+ }
+ }
*pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
}
-
pPhrase->doclist.pNextDocid = 0;
pPhrase->doclist.iDocid = 0;
}
@@ -5027,7 +5433,7 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
pExpr->aMI[iCol*3 + 2] += (iCnt>0);
if( *p==0x00 ) break;
p++;
- p += sqlite3Fts3GetVarint32(p, &iCol);
+ p += fts3GetVarint32(p, &iCol);
}
}
@@ -5231,15 +5637,23 @@ int sqlite3Fts3EvalPhrasePoslist(
pIter = pPhrase->doclist.pList;
if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
+ int iMul; /* +1 if csr dir matches index dir, else -1 */
int bOr = 0;
u8 bEof = 0;
- Fts3Expr *p;
+ u8 bTreeEof = 0;
+ Fts3Expr *p; /* Used to iterate from pExpr to root */
+ Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
/* Check if this phrase descends from an OR expression node. If not,
** return NULL. Otherwise, the entry that corresponds to docid
- ** pCsr->iPrevId may lie earlier in the doclist buffer. */
+ ** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
+ ** tree that the node is part of has been marked as EOF, but the node
+ ** itself is not EOF, then it may point to an earlier entry. */
+ pNear = pExpr;
for(p=pExpr->pParent; p; p=p->pParent){
if( p->eType==FTSQUERY_OR ) bOr = 1;
+ if( p->eType==FTSQUERY_NEAR ) pNear = p;
+ if( p->bEof ) bTreeEof = 1;
}
if( bOr==0 ) return SQLITE_OK;
@@ -5258,29 +5672,59 @@ int sqlite3Fts3EvalPhrasePoslist(
assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
if( rc!=SQLITE_OK ) return rc;
}
-
- if( pExpr->bEof ){
- pIter = 0;
- iDocid = 0;
+
+ iMul = ((pCsr->bDesc==bDescDoclist) ? 1 : -1);
+ while( bTreeEof==1
+ && pNear->bEof==0
+ && (DOCID_CMP(pNear->iDocid, pCsr->iPrevId) * iMul)<0
+ ){
+ int rc = SQLITE_OK;
+ fts3EvalNextRow(pCsr, pExpr, &rc);
+ if( rc!=SQLITE_OK ) return rc;
+ iDocid = pExpr->iDocid;
+ pIter = pPhrase->doclist.pList;
}
+
bEof = (pPhrase->doclist.nAll==0);
assert( bDescDoclist==0 || bDescDoclist==1 );
assert( pCsr->bDesc==0 || pCsr->bDesc==1 );
- if( pCsr->bDesc==bDescDoclist ){
- int dummy;
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
- sqlite3Fts3DoclistPrev(
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
- &pIter, &iDocid, &dummy, &bEof
- );
- }
- }else{
- while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
- sqlite3Fts3DoclistNext(
- bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
- &pIter, &iDocid, &bEof
- );
+ if( bEof==0 ){
+ if( pCsr->bDesc==bDescDoclist ){
+ int dummy;
+ if( pNear->bEof ){
+ /* This expression is already at EOF. So position it to point to the
+ ** last entry in the doclist at pPhrase->doclist.aAll[]. Variable
+ ** iDocid is already set for this entry, so all that is required is
+ ** to set pIter to point to the first byte of the last position-list
+ ** in the doclist.
+ **
+ ** It would also be correct to set pIter and iDocid to zero. In
+ ** this case, the first call to sqltie3Fts4DoclistPrev() below
+ ** would also move the iterator to point to the last entry in the
+ ** doclist. However, this is expensive, as to do so it has to
+ ** iterate through the entire doclist from start to finish (since
+ ** it does not know the docid for the last entry). */
+ pIter = &pPhrase->doclist.aAll[pPhrase->doclist.nAll-1];
+ fts3ReversePoslist(pPhrase->doclist.aAll, &pIter);
+ }
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
+ sqlite3Fts3DoclistPrev(
+ bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
+ &pIter, &iDocid, &dummy, &bEof
+ );
+ }
+ }else{
+ if( pNear->bEof ){
+ pIter = 0;
+ iDocid = 0;
+ }
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
+ sqlite3Fts3DoclistNext(
+ bDescDoclist, pPhrase->doclist.aAll, pPhrase->doclist.nAll,
+ &pIter, &iDocid, &bEof
+ );
+ }
}
}
@@ -5290,7 +5734,7 @@ int sqlite3Fts3EvalPhrasePoslist(
if( *pIter==0x01 ){
pIter++;
- pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
+ pIter += fts3GetVarint32(pIter, &iThis);
}else{
iThis = 0;
}
@@ -5298,7 +5742,7 @@ int sqlite3Fts3EvalPhrasePoslist(
fts3ColumnlistCopy(0, &pIter);
if( *pIter==0x00 ) return 0;
pIter++;
- pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
+ pIter += fts3GetVarint32(pIter, &iThis);
}
*ppOut = ((iCol==iThis)?pIter:0);
@@ -5339,7 +5783,10 @@ int sqlite3Fts3Corrupt(){
/*
** Initialize API pointer table, if required.
*/
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fts3_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h
index b19064c..b2827b7 100644
--- a/ext/fts3/fts3Int.h
+++ b/ext/fts3/fts3Int.h
@@ -32,7 +32,7 @@
/* If not building as part of the core, include sqlite3ext.h. */
#ifndef SQLITE_CORE
# include "sqlite3ext.h"
-extern const sqlite3_api_routines *sqlite3_api;
+SQLITE_EXTENSION_INIT3
#endif
#include "sqlite3.h"
@@ -40,6 +40,18 @@ extern const sqlite3_api_routines *sqlite3_api;
#include "fts3_hash.h"
/*
+** This constant determines the maximum depth of an FTS expression tree
+** that the library will create and use. FTS uses recursion to perform
+** various operations on the query tree, so the disadvantage of a large
+** limit is that it may allow very large queries to use large amounts
+** of stack space (perhaps causing a stack overflow).
+*/
+#ifndef SQLITE_FTS3_MAX_EXPR_DEPTH
+# define SQLITE_FTS3_MAX_EXPR_DEPTH 12
+#endif
+
+
+/*
** This constant controls how often segments are merged. Once there are
** FTS3_MERGE_COUNT segments of level N, they are merged into a single
** segment of level N+1.
@@ -194,23 +206,24 @@ struct Fts3Table {
const char *zName; /* virtual table name */
int nColumn; /* number of named columns in virtual table */
char **azColumn; /* column names. malloced */
+ u8 *abNotindexed; /* True for 'notindexed' columns */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
char *zContentTbl; /* content=xxx option, or NULL */
char *zLanguageid; /* languageid=xxx option, or NULL */
- u8 bAutoincrmerge; /* True if automerge=1 */
+ int nAutoincrmerge; /* Value configured by 'automerge' */
u32 nLeafAdd; /* Number of leaf blocks added this trans */
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
*/
- sqlite3_stmt *aStmt[37];
+ sqlite3_stmt *aStmt[40];
char *zReadExprlist;
char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */
u8 bFts4; /* True for FTS4, false for FTS3 */
- u8 bHasStat; /* True if %_stat table exists */
+ u8 bHasStat; /* True if %_stat table exists (2==unknown) */
u8 bHasDocsize; /* True if %_docsize table exists */
u8 bDescIdx; /* True if doclists are in reverse order */
u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
@@ -254,6 +267,12 @@ struct Fts3Table {
int inTransaction; /* True after xBegin but before xCommit/xRollback */
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
+
+#ifdef SQLITE_TEST
+ /* True to disable the incremental doclist optimization. This is controled
+ ** by special insert command 'test-no-incr-doclist'. */
+ int bNoIncrDoclist;
+#endif
};
/*
@@ -279,7 +298,8 @@ struct Fts3Cursor {
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
sqlite3_int64 nDoc; /* Documents in table */
-
+ i64 iMinDocid; /* Minimum docid to return */
+ i64 iMaxDocid; /* Maximum docid to return */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
u32 *aMatchinfo; /* Information about most recent match */
int nMatchinfo; /* Number of elements in aMatchinfo[] */
@@ -309,6 +329,15 @@ struct Fts3Cursor {
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
+/*
+** The lower 16-bits of the sqlite3_index_info.idxNum value set by
+** the xBestIndex() method contains the Fts3Cursor.eSearch value described
+** above. The upper 16-bits contain a combination of the following
+** bits, used to describe extra constraints on full-text searches.
+*/
+#define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */
+#define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */
+#define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */
struct Fts3Doclist {
char *aAll; /* Array containing doclist (or NULL) */
@@ -421,7 +450,6 @@ int sqlite3Fts3SegReaderPending(
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
void sqlite3Fts3SegReaderFree(Fts3SegReader *);
int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
-int sqlite3Fts3ReadLock(Fts3Table *);
int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
@@ -496,6 +524,10 @@ struct Fts3MultiSegReader {
int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
+#define fts3GetVarint32(p, piVal) ( \
+ (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
+)
+
/* fts3.c */
int sqlite3Fts3PutVarint(char *, sqlite3_int64);
int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
@@ -553,7 +585,7 @@ int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *);
/* fts3_unicode2.c (functions generated by parsing unicode text files) */
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
int sqlite3FtsUnicodeFold(int, int);
int sqlite3FtsUnicodeIsalnum(int);
int sqlite3FtsUnicodeIsdiacritic(int);
diff --git a/ext/fts3/fts3_aux.c b/ext/fts3/fts3_aux.c
index 9b582fc..c68b1a9 100644
--- a/ext/fts3/fts3_aux.c
+++ b/ext/fts3/fts3_aux.c
@@ -31,6 +31,7 @@ struct Fts3auxCursor {
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
+ int iLangid; /* Language id to query */
int isEof; /* True if cursor is at EOF */
sqlite3_int64 iRowid; /* Current rowid */
@@ -45,7 +46,8 @@ struct Fts3auxCursor {
/*
** Schema of the terms table.
*/
-#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
+#define FTS3_AUX_SCHEMA \
+ "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
/*
** This function does all the work for both the xConnect and xCreate methods.
@@ -92,7 +94,7 @@ static int fts3auxConnectMethod(
}
nFts3 = (int)strlen(zFts3);
- rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
+ rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
@@ -152,6 +154,8 @@ static int fts3auxBestIndexMethod(
int iEq = -1;
int iGe = -1;
int iLe = -1;
+ int iLangid = -1;
+ int iNext = 1; /* Next free argvIndex value */
UNUSED_PARAMETER(pVTab);
@@ -163,36 +167,48 @@ static int fts3auxBestIndexMethod(
pInfo->orderByConsumed = 1;
}
- /* Search for equality and range constraints on the "term" column. */
+ /* Search for equality and range constraints on the "term" column.
+ ** And equality constraints on the hidden "languageid" column. */
for(i=0; i<pInfo->nConstraint; i++){
- if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
+ if( pInfo->aConstraint[i].usable ){
int op = pInfo->aConstraint[i].op;
- if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ int iCol = pInfo->aConstraint[i].iColumn;
+
+ if( iCol==0 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ }
+ if( iCol==4 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
+ }
}
}
if( iEq>=0 ){
pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
- pInfo->aConstraintUsage[iEq].argvIndex = 1;
+ pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
pInfo->estimatedCost = 5;
}else{
pInfo->idxNum = 0;
pInfo->estimatedCost = 20000;
if( iGe>=0 ){
pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
- pInfo->aConstraintUsage[iGe].argvIndex = 1;
+ pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
if( iLe>=0 ){
pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
- pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
+ pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
}
+ if( iLangid>=0 ){
+ pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
+ pInfo->estimatedCost--;
+ }
return SQLITE_OK;
}
@@ -352,7 +368,14 @@ static int fts3auxFilterMethod(
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
- int isScan;
+ int isScan = 0;
+ int iLangVal = 0; /* Language id to query */
+
+ int iEq = -1; /* Index of term=? value in apVal */
+ int iGe = -1; /* Index of term>=? value in apVal */
+ int iLe = -1; /* Index of term<=? value in apVal */
+ int iLangid = -1; /* Index of languageid=? value in apVal */
+ int iNext = 0;
UNUSED_PARAMETER(nVal);
UNUSED_PARAMETER(idxStr);
@@ -362,7 +385,21 @@ static int fts3auxFilterMethod(
|| idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|| idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
);
- isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
+
+ if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
+ iEq = iNext++;
+ }else{
+ isScan = 1;
+ if( idxNum & FTS4AUX_GE_CONSTRAINT ){
+ iGe = iNext++;
+ }
+ if( idxNum & FTS4AUX_LE_CONSTRAINT ){
+ iLe = iNext++;
+ }
+ }
+ if( iNext<nVal ){
+ iLangid = iNext++;
+ }
/* In case this cursor is being reused, close and zero it. */
testcase(pCsr->filter.zTerm);
@@ -374,22 +411,35 @@ static int fts3auxFilterMethod(
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
- if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
+ if( iEq>=0 || iGe>=0 ){
const unsigned char *zStr = sqlite3_value_text(apVal[0]);
+ assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
if( zStr ){
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
}
}
- if( idxNum&FTS4AUX_LE_CONSTRAINT ){
- int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
- pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
- pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
+
+ if( iLe>=0 ){
+ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
+ pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
+
+ if( iLangid>=0 ){
+ iLangVal = sqlite3_value_int(apVal[iLangid]);
+
+ /* If the user specified a negative value for the languageid, use zero
+ ** instead. This works, as the "languageid=?" constraint will also
+ ** be tested by the VDBE layer. The test will always be false (since
+ ** this module will not return a row with a negative languageid), and
+ ** so the overall query will return zero rows. */
+ if( iLangVal<0 ) iLangVal = 0;
+ }
+ pCsr->iLangid = iLangVal;
- rc = sqlite3Fts3SegReaderCursor(pFts3, 0, 0, FTS3_SEGCURSOR_ALL,
+ rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
@@ -413,24 +463,37 @@ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
*/
static int fts3auxColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
assert( p->isEof==0 );
- if( iCol==0 ){ /* Column "term" */
- sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
- }else if( iCol==1 ){ /* Column "col" */
- if( p->iCol ){
- sqlite3_result_int(pContext, p->iCol-1);
- }else{
- sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
- }
- }else if( iCol==2 ){ /* Column "documents" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
- }else{ /* Column "occurrences" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
+ switch( iCol ){
+ case 0: /* term */
+ sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
+ break;
+
+ case 1: /* col */
+ if( p->iCol ){
+ sqlite3_result_int(pCtx, p->iCol-1);
+ }else{
+ sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
+ }
+ break;
+
+ case 2: /* documents */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
+ break;
+
+ case 3: /* occurrences */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
+ break;
+
+ default: /* languageid */
+ assert( iCol==4 );
+ sqlite3_result_int(pCtx, p->iLangid);
+ break;
}
return SQLITE_OK;
diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c
index c046d7d..f5d28cb 100644
--- a/ext/fts3/fts3_expr.c
+++ b/ext/fts3/fts3_expr.c
@@ -155,6 +155,11 @@ int sqlite3Fts3OpenTokenizer(
return rc;
}
+/*
+** Function getNextNode(), which is called by fts3ExprParse(), may itself
+** call fts3ExprParse(). So this forward declaration is required.
+*/
+static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
/*
** Extract the next token from buffer z (length n) using the tokenizer
@@ -180,9 +185,16 @@ static int getNextToken(
int rc;
sqlite3_tokenizer_cursor *pCursor;
Fts3Expr *pRet = 0;
- int nConsumed = 0;
+ int i = 0;
+
+ /* Set variable i to the maximum number of bytes of input to tokenize. */
+ for(i=0; i<n; i++){
+ if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
+ if( z[i]=='*' || z[i]=='"' ) break;
+ }
- rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
+ *pnConsumed = i;
+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
if( rc==SQLITE_OK ){
const char *zToken;
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
@@ -223,13 +235,14 @@ static int getNextToken(
}
}
- nConsumed = iEnd;
+ *pnConsumed = iEnd;
+ }else if( i && rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
}
pModule->xClose(pCursor);
}
- *pnConsumed = nConsumed;
*ppExpr = pRet;
return rc;
}
@@ -370,12 +383,6 @@ no_mem:
}
/*
-** Function getNextNode(), which is called by fts3ExprParse(), may itself
-** call fts3ExprParse(). So this forward declaration is required.
-*/
-static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
-
-/*
** The output variable *ppExpr is populated with an allocated Fts3Expr
** structure, or set to 0 if the end of the input buffer is reached.
**
@@ -471,27 +478,6 @@ static int getNextNode(
}
}
- /* Check for an open bracket. */
- if( sqlite3_fts3_enable_parentheses ){
- if( *zInput=='(' ){
- int nConsumed;
- pParse->nNest++;
- rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
- if( rc==SQLITE_OK && !*ppExpr ){
- rc = SQLITE_DONE;
- }
- *pnConsumed = (int)((zInput - z) + 1 + nConsumed);
- return rc;
- }
-
- /* Check for a close bracket. */
- if( *zInput==')' ){
- pParse->nNest--;
- *pnConsumed = (int)((zInput - z) + 1);
- return SQLITE_DONE;
- }
- }
-
/* See if we are dealing with a quoted phrase. If this is the case, then
** search for the closing quote and pass the whole string to getNextString()
** for processing. This is easy to do, as fts3 has no syntax for escaping
@@ -506,6 +492,21 @@ static int getNextNode(
return getNextString(pParse, &zInput[1], ii-1, ppExpr);
}
+ if( sqlite3_fts3_enable_parentheses ){
+ if( *zInput=='(' ){
+ int nConsumed = 0;
+ pParse->nNest++;
+ rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
+ if( rc==SQLITE_OK && !*ppExpr ){ rc = SQLITE_DONE; }
+ *pnConsumed = (int)(zInput - z) + 1 + nConsumed;
+ return rc;
+ }else if( *zInput==')' ){
+ pParse->nNest--;
+ *pnConsumed = (int)((zInput - z) + 1);
+ *ppExpr = 0;
+ return SQLITE_DONE;
+ }
+ }
/* If control flows to this point, this must be a regular token, or
** the end of the input. Read a regular token using the sqlite3_tokenizer
@@ -624,96 +625,100 @@ static int fts3ExprParse(
while( rc==SQLITE_OK ){
Fts3Expr *p = 0;
int nByte = 0;
+
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
+ assert( nByte>0 || (rc!=SQLITE_OK && p==0) );
if( rc==SQLITE_OK ){
- int isPhrase;
-
- if( !sqlite3_fts3_enable_parentheses
- && p->eType==FTSQUERY_PHRASE && pParse->isNot
- ){
- /* Create an implicit NOT operator. */
- Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
- if( !pNot ){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_NOMEM;
- goto exprparse_out;
- }
- pNot->eType = FTSQUERY_NOT;
- pNot->pRight = p;
- p->pParent = pNot;
- if( pNotBranch ){
- pNot->pLeft = pNotBranch;
- pNotBranch->pParent = pNot;
- }
- pNotBranch = pNot;
- p = pPrev;
- }else{
- int eType = p->eType;
- isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
-
- /* The isRequirePhrase variable is set to true if a phrase or
- ** an expression contained in parenthesis is required. If a
- ** binary operator (AND, OR, NOT or NEAR) is encounted when
- ** isRequirePhrase is set, this is a syntax error.
- */
- if( !isPhrase && isRequirePhrase ){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_ERROR;
- goto exprparse_out;
- }
-
- if( isPhrase && !isRequirePhrase ){
- /* Insert an implicit AND operator. */
- Fts3Expr *pAnd;
- assert( pRet && pPrev );
- pAnd = fts3MallocZero(sizeof(Fts3Expr));
- if( !pAnd ){
+ if( p ){
+ int isPhrase;
+
+ if( !sqlite3_fts3_enable_parentheses
+ && p->eType==FTSQUERY_PHRASE && pParse->isNot
+ ){
+ /* Create an implicit NOT operator. */
+ Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
+ if( !pNot ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
goto exprparse_out;
}
- pAnd->eType = FTSQUERY_AND;
- insertBinaryOperator(&pRet, pPrev, pAnd);
- pPrev = pAnd;
- }
+ pNot->eType = FTSQUERY_NOT;
+ pNot->pRight = p;
+ p->pParent = pNot;
+ if( pNotBranch ){
+ pNot->pLeft = pNotBranch;
+ pNotBranch->pParent = pNot;
+ }
+ pNotBranch = pNot;
+ p = pPrev;
+ }else{
+ int eType = p->eType;
+ isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
+
+ /* The isRequirePhrase variable is set to true if a phrase or
+ ** an expression contained in parenthesis is required. If a
+ ** binary operator (AND, OR, NOT or NEAR) is encounted when
+ ** isRequirePhrase is set, this is a syntax error.
+ */
+ if( !isPhrase && isRequirePhrase ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_ERROR;
+ goto exprparse_out;
+ }
+
+ if( isPhrase && !isRequirePhrase ){
+ /* Insert an implicit AND operator. */
+ Fts3Expr *pAnd;
+ assert( pRet && pPrev );
+ pAnd = fts3MallocZero(sizeof(Fts3Expr));
+ if( !pAnd ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_NOMEM;
+ goto exprparse_out;
+ }
+ pAnd->eType = FTSQUERY_AND;
+ insertBinaryOperator(&pRet, pPrev, pAnd);
+ pPrev = pAnd;
+ }
- /* This test catches attempts to make either operand of a NEAR
- ** operator something other than a phrase. For example, either of
- ** the following:
- **
- ** (bracketed expression) NEAR phrase
- ** phrase NEAR (bracketed expression)
- **
- ** Return an error in either case.
- */
- if( pPrev && (
+ /* This test catches attempts to make either operand of a NEAR
+ ** operator something other than a phrase. For example, either of
+ ** the following:
+ **
+ ** (bracketed expression) NEAR phrase
+ ** phrase NEAR (bracketed expression)
+ **
+ ** Return an error in either case.
+ */
+ if( pPrev && (
(eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE)
|| (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR)
- )){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_ERROR;
- goto exprparse_out;
- }
-
- if( isPhrase ){
- if( pRet ){
- assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
- pPrev->pRight = p;
- p->pParent = pPrev;
+ )){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_ERROR;
+ goto exprparse_out;
+ }
+
+ if( isPhrase ){
+ if( pRet ){
+ assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
+ pPrev->pRight = p;
+ p->pParent = pPrev;
+ }else{
+ pRet = p;
+ }
}else{
- pRet = p;
+ insertBinaryOperator(&pRet, pPrev, p);
}
- }else{
- insertBinaryOperator(&pRet, pPrev, p);
+ isRequirePhrase = !isPhrase;
}
- isRequirePhrase = !isPhrase;
+ pPrev = p;
}
assert( nByte>0 );
}
assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) );
nIn -= nByte;
zIn += nByte;
- pPrev = p;
}
if( rc==SQLITE_DONE && pRet && isRequirePhrase ){
@@ -1000,17 +1005,16 @@ int sqlite3Fts3ExprParse(
Fts3Expr **ppExpr, /* OUT: Parsed query structure */
char **pzErr /* OUT: Error message (sqlite3_malloc) */
){
- static const int MAX_EXPR_DEPTH = 12;
int rc = fts3ExprParseUnbalanced(
pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
);
/* Rebalance the expression. And check that its depth does not exceed
- ** MAX_EXPR_DEPTH. */
+ ** SQLITE_FTS3_MAX_EXPR_DEPTH. */
if( rc==SQLITE_OK && *ppExpr ){
- rc = fts3ExprBalance(ppExpr, MAX_EXPR_DEPTH);
+ rc = fts3ExprBalance(ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
if( rc==SQLITE_OK ){
- rc = fts3ExprCheckDepth(*ppExpr, MAX_EXPR_DEPTH);
+ rc = fts3ExprCheckDepth(*ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
}
}
@@ -1019,7 +1023,8 @@ int sqlite3Fts3ExprParse(
*ppExpr = 0;
if( rc==SQLITE_TOOBIG ){
*pzErr = sqlite3_mprintf(
- "FTS expression tree is too large (maximum depth %d)", MAX_EXPR_DEPTH
+ "FTS expression tree is too large (maximum depth %d)",
+ SQLITE_FTS3_MAX_EXPR_DEPTH
);
rc = SQLITE_ERROR;
}else if( rc==SQLITE_ERROR ){
diff --git a/ext/fts3/fts3_hash.c b/ext/fts3/fts3_hash.c
index 57c59b5..1a32a53 100644
--- a/ext/fts3/fts3_hash.c
+++ b/ext/fts3/fts3_hash.c
@@ -96,13 +96,13 @@ void sqlite3Fts3HashClear(Fts3Hash *pH){
*/
static int fts3StrHash(const void *pKey, int nKey){
const char *z = (const char *)pKey;
- int h = 0;
+ unsigned h = 0;
if( nKey<=0 ) nKey = (int) strlen(z);
while( nKey > 0 ){
h = (h<<3) ^ h ^ *z++;
nKey--;
}
- return h & 0x7fffffff;
+ return (int)(h & 0x7fffffff);
}
static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
if( n1!=n2 ) return 1;
diff --git a/ext/fts3/fts3_porter.c b/ext/fts3/fts3_porter.c
index 579745b..db175ac 100644
--- a/ext/fts3/fts3_porter.c
+++ b/ext/fts3/fts3_porter.c
@@ -403,12 +403,14 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
/* Step 2 */
switch( z[1] ){
case 'a':
- stem(&z, "lanoita", "ate", m_gt_0) ||
- stem(&z, "lanoit", "tion", m_gt_0);
+ if( !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);
+ if( !stem(&z, "icne", "ence", m_gt_0) ){
+ stem(&z, "icna", "ance", m_gt_0);
+ }
break;
case 'e':
stem(&z, "rezi", "ize", m_gt_0);
@@ -417,43 +419,54 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
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);
+ if( !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);
+ if( !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);
+ if( !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);
+ if( !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);
+ if( !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);
+ if( !stem(&z, "laci", "ic", m_gt_0) ){
+ stem(&z, "luf", "", m_gt_0);
+ }
break;
case 's':
stem(&z, "ssen", "", m_gt_0);
@@ -494,9 +507,11 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
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);
+ if( !stem(&z, "tneme", "", m_gt_1)
+ && !stem(&z, "tnem", "", m_gt_1)
+ ){
+ stem(&z, "tne", "", m_gt_1);
+ }
}
}
break;
@@ -515,8 +530,9 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
}
break;
case 't':
- stem(&z, "eta", "", m_gt_1) ||
- stem(&z, "iti", "", m_gt_1);
+ if( !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) ){
diff --git a/ext/fts3/fts3_snippet.c b/ext/fts3/fts3_snippet.c
index d54a787..aa8779f 100644
--- a/ext/fts3/fts3_snippet.c
+++ b/ext/fts3/fts3_snippet.c
@@ -128,7 +128,7 @@ struct StrBuffer {
*/
static void fts3GetDeltaPosition(char **pp, int *piPos){
int iVal;
- *pp += sqlite3Fts3GetVarint32(*pp, &iVal);
+ *pp += fts3GetVarint32(*pp, &iVal);
*piPos += (iVal-2);
}
@@ -504,6 +504,7 @@ static int fts3StringAppend(
pStr->z = zNew;
pStr->nAlloc = nAlloc;
}
+ assert( pStr->z!=0 && (pStr->nAlloc >= pStr->n+nAppend+1) );
/* Append the data to the string buffer. */
memcpy(&pStr->z[pStr->n], zAppend, nAppend);
diff --git a/ext/fts3/fts3_test.c b/ext/fts3/fts3_test.c
index 75ec6bd..36dcc94 100644
--- a/ext/fts3/fts3_test.c
+++ b/ext/fts3/fts3_test.c
@@ -517,6 +517,51 @@ static int fts3_test_tokenizer_cmd(
return TCL_OK;
}
+static int fts3_test_varint_cmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+#ifdef SQLITE_ENABLE_FTS3
+ char aBuf[24];
+ int rc;
+ Tcl_WideInt w, w2;
+ int nByte, nByte2;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "INTEGER");
+ return TCL_ERROR;
+ }
+
+ rc = Tcl_GetWideIntFromObj(interp, objv[1], &w);
+ if( rc!=TCL_OK ) return rc;
+
+ nByte = sqlite3Fts3PutVarint(aBuf, w);
+ nByte2 = sqlite3Fts3GetVarint(aBuf, &w2);
+ if( w!=w2 || nByte!=nByte2 ){
+ char *zErr = sqlite3_mprintf("error testing %lld", w);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+
+ if( w<=2147483647 && w>=0 ){
+ int i;
+ nByte2 = fts3GetVarint32(aBuf, &i);
+ if( (int)w!=i || nByte!=nByte2 ){
+ char *zErr = sqlite3_mprintf("error testing %lld (32-bit)", w);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, zErr, 0);
+ return TCL_ERROR;
+ }
+ }
+
+#endif
+ UNUSED_PARAMETER(clientData);
+ return TCL_OK;
+}
+
/*
** End of tokenizer code.
**************************************************************************/
@@ -529,6 +574,10 @@ int Sqlitetestfts3_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(
interp, "fts3_test_tokenizer", fts3_test_tokenizer_cmd, 0, 0
);
+
+ Tcl_CreateObjCommand(
+ interp, "fts3_test_varint", fts3_test_varint_cmd, 0, 0
+ );
return TCL_OK;
}
#endif /* SQLITE_ENABLE_FTS3 || SQLITE_ENABLE_FTS4 */
diff --git a/ext/fts3/fts3_unicode.c b/ext/fts3/fts3_unicode.c
index 188358e..94fc27b 100644
--- a/ext/fts3/fts3_unicode.c
+++ b/ext/fts3/fts3_unicode.c
@@ -13,7 +13,7 @@
** Implementation of the "unicode" full-text-search tokenizer.
*/
-#ifdef SQLITE_ENABLE_FTS4_UNICODE61
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
#include "fts3Int.h"
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
@@ -231,7 +231,7 @@ static int unicodeCreate(
for(i=0; rc==SQLITE_OK && i<nArg; i++){
const char *z = azArg[i];
- int n = strlen(z);
+ int n = (int)strlen(z);
if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
pNew->bRemoveDiacritic = 1;
@@ -318,7 +318,7 @@ static int unicodeNext(
){
unicode_cursor *pCsr = (unicode_cursor *)pC;
unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
- int iCode;
+ int iCode = 0;
char *zOut;
const unsigned char *z = &pCsr->aInput[pCsr->iOff];
const unsigned char *zStart = z;
@@ -363,11 +363,11 @@ static int unicodeNext(
);
/* Set the output variables and return. */
- pCsr->iOff = (z - pCsr->aInput);
+ pCsr->iOff = (int)(z - pCsr->aInput);
*paToken = pCsr->zToken;
- *pnToken = zOut - pCsr->zToken;
- *piStart = (zStart - pCsr->aInput);
- *piEnd = (zEnd - pCsr->aInput);
+ *pnToken = (int)(zOut - pCsr->zToken);
+ *piStart = (int)(zStart - pCsr->aInput);
+ *piEnd = (int)(zEnd - pCsr->aInput);
*piPos = pCsr->iToken++;
return SQLITE_OK;
}
@@ -390,4 +390,4 @@ void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
-#endif /* ifndef SQLITE_ENABLE_FTS4_UNICODE61 */
+#endif /* ifndef SQLITE_DISABLE_FTS3_UNICODE */
diff --git a/ext/fts3/fts3_unicode2.c b/ext/fts3/fts3_unicode2.c
index 3c24569..20b7a25 100644
--- a/ext/fts3/fts3_unicode2.c
+++ b/ext/fts3/fts3_unicode2.c
@@ -15,7 +15,7 @@
** DO NOT EDIT THIS MACHINE GENERATED FILE.
*/
-#if defined(SQLITE_ENABLE_FTS4_UNICODE61)
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
#include <assert.h>
@@ -39,7 +39,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
** C. It is not possible to represent a range larger than 1023 codepoints
** using this format.
*/
- const static unsigned int aEntry[] = {
+ static const unsigned int aEntry[] = {
0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
@@ -101,28 +101,27 @@ int sqlite3FtsUnicodeIsalnum(int c){
0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
- 0x037FFC02, 0x03E3FC01, 0x03EC7801, 0x03ECA401, 0x03EEC810,
- 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023,
- 0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
- 0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
- 0x04040003, 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E,
- 0x040E7C01, 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01,
- 0x04280403, 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01,
- 0x04294009, 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016,
- 0x04420003, 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004,
- 0x04460003, 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004,
- 0x05BD442E, 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5,
- 0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
- 0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
- 0x075EA401, 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064,
- 0x07C2800F, 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F,
- 0x07C4C03C, 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009,
- 0x07C94002, 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014,
- 0x07CE8025, 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001,
- 0x07D108B6, 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018,
- 0x07D7EC46, 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401,
- 0x38008060, 0x380400F0, 0x3C000001, 0x3FFFF401, 0x40000001,
- 0x43FFF401,
+ 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
+ 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
+ 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
+ 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
+ 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
+ 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
+ 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
+ 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
+ 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
+ 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
+ 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
+ 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
+ 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
+ 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
+ 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
+ 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
+ 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
+ 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
+ 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
+ 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
+ 0x380400F0,
};
static const unsigned int aAscii[4] = {
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
@@ -132,7 +131,7 @@ int sqlite3FtsUnicodeIsalnum(int c){
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
}else if( c<(1<<22) ){
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
- int iRes;
+ int iRes = 0;
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
int iLo = 0;
while( iHi>=iLo ){
@@ -203,7 +202,7 @@ static int remove_diacritic(int c){
}
assert( key>=aDia[iRes] );
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
-};
+}
/*
@@ -363,4 +362,4 @@ int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
return ret;
}
#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
-#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */
+#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
diff --git a/ext/fts3/fts3_write.c b/ext/fts3/fts3_write.c
index 269d1dd..0da08c6 100644
--- a/ext/fts3/fts3_write.c
+++ b/ext/fts3/fts3_write.c
@@ -193,6 +193,7 @@ struct SegmentWriter {
int nSize; /* Size of allocation at aData */
int nData; /* Bytes of data in aData */
char *aData; /* Pointer to block from malloc() */
+ i64 nLeafData; /* Number of bytes of leaf data written */
};
/*
@@ -268,6 +269,10 @@ struct SegmentNode {
#define SQL_SELECT_INDEXES 35
#define SQL_SELECT_MXLEVEL 36
+#define SQL_SELECT_LEVEL_RANGE2 37
+#define SQL_UPDATE_LEVEL_IDX 38
+#define SQL_UPDATE_LEVEL 39
+
/*
** This function is used to obtain an SQLite prepared statement handle
** for the statement identified by the second argument. If successful,
@@ -369,7 +374,18 @@ static int fts3SqlStmt(
/* SQL_SELECT_MXLEVEL
** Return the largest relative level in the FTS index or indexes. */
-/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'"
+/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'",
+
+ /* Return segments in order from oldest to newest.*/
+/* 37 */ "SELECT level, idx, end_block "
+ "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? "
+ "ORDER BY level DESC, idx ASC",
+
+ /* Update statements used while promoting segments */
+/* 38 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=-1,idx=? "
+ "WHERE level=? AND idx=?",
+/* 39 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=? WHERE level=-1"
+
};
int rc = SQLITE_OK;
sqlite3_stmt *pStmt;
@@ -489,37 +505,30 @@ static void fts3SqlExec(
/*
-** This function ensures that the caller has obtained a shared-cache
-** table-lock on the %_content table. This is required before reading
-** data from the fts3 table. If this lock is not acquired first, then
-** the caller may end up holding read-locks on the %_segments and %_segdir
-** tables, but no read-lock on the %_content table. If this happens
-** a second connection will be able to write to the fts3 table, but
-** attempting to commit those writes might return SQLITE_LOCKED or
-** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain
-** write-locks on the %_segments and %_segdir ** tables).
-**
-** We try to avoid this because if FTS3 returns any error when committing
-** a transaction, the whole transaction will be rolled back. And this is
-** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
-** still happen if the user reads data directly from the %_segments or
-** %_segdir tables instead of going through FTS3 though.
+** This function ensures that the caller has obtained an exclusive
+** shared-cache table-lock on the %_segdir table. This is required before
+** writing data to the fts3 table. If this lock is not acquired first, then
+** the caller may end up attempting to take this lock as part of committing
+** a transaction, causing SQLite to return SQLITE_LOCKED or
+** LOCKED_SHAREDCACHEto a COMMIT command.
**
-** This reasoning does not apply to a content=xxx table.
+** It is best to avoid this because if FTS3 returns any error when
+** committing a transaction, the whole transaction will be rolled back.
+** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
+** It can still happen if the user locks the underlying tables directly
+** instead of accessing them via FTS.
*/
-int sqlite3Fts3ReadLock(Fts3Table *p){
- int rc; /* Return code */
- sqlite3_stmt *pStmt; /* Statement used to obtain lock */
-
- if( p->zContentTbl==0 ){
- rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
+static int fts3Writelock(Fts3Table *p){
+ int rc = SQLITE_OK;
+
+ if( p->nPendingData==0 ){
+ sqlite3_stmt *pStmt;
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_null(pStmt, 1);
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
}
- }else{
- rc = SQLITE_OK;
}
return rc;
@@ -907,12 +916,15 @@ static int fts3InsertTerms(
){
int i; /* Iterator variable */
for(i=2; i<p->nColumn+2; i++){
- const char *zText = (const char *)sqlite3_value_text(apVal[i]);
- int rc = fts3PendingTermsAdd(p, iLangid, zText, i-2, &aSz[i-2]);
- if( rc!=SQLITE_OK ){
- return rc;
+ int iCol = i-2;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_value_text(apVal[i]);
+ int rc = fts3PendingTermsAdd(p, iLangid, zText, iCol, &aSz[iCol]);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
- aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
return SQLITE_OK;
}
@@ -1059,9 +1071,12 @@ static void fts3DeleteTerms(
int iLangid = langidFromSelect(p, pSelect);
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pSelect, 0));
for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
- const char *zText = (const char *)sqlite3_column_text(pSelect, i);
- rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[i-1]);
- aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
+ int iCol = i-1;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pSelect, i);
+ rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
+ }
}
if( rc!=SQLITE_OK ){
sqlite3_reset(pSelect);
@@ -1345,8 +1360,8 @@ static int fts3SegReaderNext(
/* Because of the FTS3_NODE_PADDING bytes of padding, the following is
** safe (no risk of overread) even if the node data is corrupted. */
- pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
- pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
+ pNext += fts3GetVarint32(pNext, &nPrefix);
+ pNext += fts3GetVarint32(pNext, &nSuffix);
if( nPrefix<0 || nSuffix<=0
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
){
@@ -1369,7 +1384,7 @@ static int fts3SegReaderNext(
memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix);
pReader->nTerm = nPrefix+nSuffix;
pNext += nSuffix;
- pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist);
+ pNext += fts3GetVarint32(pNext, &pReader->nDoclist);
pReader->aDoclist = pNext;
pReader->pOffsetList = 0;
@@ -1462,7 +1477,7 @@ static int fts3SegReaderNextDocid(
/* The following line of code (and the "p++" below the while() loop) is
** normally all that is required to move pointer p to the desired
** position. The exception is if this node is being loaded from disk
- ** incrementally and pointer "p" now points to the first byte passed
+ ** incrementally and pointer "p" now points to the first byte past
** the populated part of pReader->aNode[].
*/
while( *p | c ) c = *p++ & 0x80;
@@ -1911,6 +1926,7 @@ static int fts3WriteSegdir(
sqlite3_int64 iStartBlock, /* Value for "start_block" field */
sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */
sqlite3_int64 iEndBlock, /* Value for "end_block" field */
+ sqlite3_int64 nLeafData, /* Bytes of leaf data in segment */
char *zRoot, /* Blob value for "root" field */
int nRoot /* Number of bytes in buffer zRoot */
){
@@ -1921,7 +1937,13 @@ static int fts3WriteSegdir(
sqlite3_bind_int(pStmt, 2, iIdx);
sqlite3_bind_int64(pStmt, 3, iStartBlock);
sqlite3_bind_int64(pStmt, 4, iLeafEndBlock);
- sqlite3_bind_int64(pStmt, 5, iEndBlock);
+ if( nLeafData==0 ){
+ sqlite3_bind_int64(pStmt, 5, iEndBlock);
+ }else{
+ char *zEnd = sqlite3_mprintf("%lld %lld", iEndBlock, nLeafData);
+ if( !zEnd ) return SQLITE_NOMEM;
+ sqlite3_bind_text(pStmt, 5, zEnd, -1, sqlite3_free);
+ }
sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC);
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
@@ -2247,6 +2269,9 @@ static int fts3SegWriterAdd(
nDoclist; /* Doclist data */
}
+ /* Increase the total number of bytes written to account for the new entry. */
+ pWriter->nLeafData += nReq;
+
/* If the buffer currently allocated is too small for this entry, realloc
** the buffer to make it large enough.
*/
@@ -2318,13 +2343,13 @@ static int fts3SegWriterFlush(
pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot);
}
if( rc==SQLITE_OK ){
- rc = fts3WriteSegdir(
- p, iLevel, iIdx, pWriter->iFirst, iLastLeaf, iLast, zRoot, nRoot);
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
+ pWriter->iFirst, iLastLeaf, iLast, pWriter->nLeafData, zRoot, nRoot);
}
}else{
/* The entire tree fits on the root node. Write it to the segdir table. */
- rc = fts3WriteSegdir(
- p, iLevel, iIdx, 0, 0, 0, pWriter->aData, pWriter->nData);
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
+ 0, 0, 0, pWriter->nLeafData, pWriter->aData, pWriter->nData);
}
p->nLeafAdd++;
return rc;
@@ -2409,6 +2434,37 @@ static int fts3SegmentMaxLevel(
}
/*
+** iAbsLevel is an absolute level that may be assumed to exist within
+** the database. This function checks if it is the largest level number
+** within its index. Assuming no error occurs, *pbMax is set to 1 if
+** iAbsLevel is indeed the largest level, or 0 otherwise, and SQLITE_OK
+** is returned. If an error occurs, an error code is returned and the
+** final value of *pbMax is undefined.
+*/
+static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
+
+ /* Set pStmt to the compiled version of:
+ **
+ ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
+ **
+ ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
+ */
+ sqlite3_stmt *pStmt;
+ int rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
+ sqlite3_bind_int64(pStmt, 2,
+ ((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
+ );
+
+ *pbMax = 0;
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pbMax = sqlite3_column_type(pStmt, 0)==SQLITE_NULL;
+ }
+ return sqlite3_reset(pStmt);
+}
+
+/*
** Delete all entries in the %_segments table associated with the segment
** opened with seg-reader pSeg. This function does not affect the contents
** of the %_segdir table.
@@ -2530,7 +2586,7 @@ static void fts3ColumnFilter(
break;
}
p = &pList[1];
- p += sqlite3Fts3GetVarint32(p, &iCurrent);
+ p += fts3GetVarint32(p, &iCurrent);
}
if( bZero && &pList[nList]!=pEnd ){
@@ -2849,8 +2905,8 @@ int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp);
while( apSegment[0]->pOffsetList ){
int j; /* Number of segments that share a docid */
- char *pList;
- int nList;
+ char *pList = 0;
+ int nList = 0;
int nByte;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
@@ -2944,6 +3000,140 @@ void sqlite3Fts3SegReaderFinish(
}
/*
+** Decode the "end_block" field, selected by column iCol of the SELECT
+** statement passed as the first argument.
+**
+** The "end_block" field may contain either an integer, or a text field
+** containing the text representation of two non-negative integers separated
+** by one or more space (0x20) characters. In the first case, set *piEndBlock
+** to the integer value and *pnByte to zero before returning. In the second,
+** set *piEndBlock to the first value and *pnByte to the second.
+*/
+static void fts3ReadEndBlockField(
+ sqlite3_stmt *pStmt,
+ int iCol,
+ i64 *piEndBlock,
+ i64 *pnByte
+){
+ const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
+ if( zText ){
+ int i;
+ int iMul = 1;
+ i64 iVal = 0;
+ for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
+ iVal = iVal*10 + (zText[i] - '0');
+ }
+ *piEndBlock = iVal;
+ while( zText[i]==' ' ) i++;
+ iVal = 0;
+ if( zText[i]=='-' ){
+ i++;
+ iMul = -1;
+ }
+ for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
+ iVal = iVal*10 + (zText[i] - '0');
+ }
+ *pnByte = (iVal * (i64)iMul);
+ }
+}
+
+
+/*
+** A segment of size nByte bytes has just been written to absolute level
+** iAbsLevel. Promote any segments that should be promoted as a result.
+*/
+static int fts3PromoteSegments(
+ Fts3Table *p, /* FTS table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level just updated */
+ sqlite3_int64 nByte /* Size of new segment at iAbsLevel */
+){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pRange;
+
+ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE2, &pRange, 0);
+
+ if( rc==SQLITE_OK ){
+ int bOk = 0;
+ i64 iLast = (iAbsLevel/FTS3_SEGDIR_MAXLEVEL + 1) * FTS3_SEGDIR_MAXLEVEL - 1;
+ i64 nLimit = (nByte*3)/2;
+
+ /* Loop through all entries in the %_segdir table corresponding to
+ ** segments in this index on levels greater than iAbsLevel. If there is
+ ** at least one such segment, and it is possible to determine that all
+ ** such segments are smaller than nLimit bytes in size, they will be
+ ** promoted to level iAbsLevel. */
+ sqlite3_bind_int64(pRange, 1, iAbsLevel+1);
+ sqlite3_bind_int64(pRange, 2, iLast);
+ while( SQLITE_ROW==sqlite3_step(pRange) ){
+ i64 nSize = 0, dummy;
+ fts3ReadEndBlockField(pRange, 2, &dummy, &nSize);
+ if( nSize<=0 || nSize>nLimit ){
+ /* If nSize==0, then the %_segdir.end_block field does not not
+ ** contain a size value. This happens if it was written by an
+ ** old version of FTS. In this case it is not possible to determine
+ ** the size of the segment, and so segment promotion does not
+ ** take place. */
+ bOk = 0;
+ break;
+ }
+ bOk = 1;
+ }
+ rc = sqlite3_reset(pRange);
+
+ if( bOk ){
+ int iIdx = 0;
+ sqlite3_stmt *pUpdate1;
+ sqlite3_stmt *pUpdate2;
+
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL, &pUpdate2, 0);
+ }
+
+ if( rc==SQLITE_OK ){
+
+ /* Loop through all %_segdir entries for segments in this index with
+ ** levels equal to or greater than iAbsLevel. As each entry is visited,
+ ** updated it to set (level = -1) and (idx = N), where N is 0 for the
+ ** oldest segment in the range, 1 for the next oldest, and so on.
+ **
+ ** In other words, move all segments being promoted to level -1,
+ ** setting the "idx" fields as appropriate to keep them in the same
+ ** order. The contents of level -1 (which is never used, except
+ ** transiently here), will be moved back to level iAbsLevel below. */
+ sqlite3_bind_int64(pRange, 1, iAbsLevel);
+ while( SQLITE_ROW==sqlite3_step(pRange) ){
+ sqlite3_bind_int(pUpdate1, 1, iIdx++);
+ sqlite3_bind_int(pUpdate1, 2, sqlite3_column_int(pRange, 0));
+ sqlite3_bind_int(pUpdate1, 3, sqlite3_column_int(pRange, 1));
+ sqlite3_step(pUpdate1);
+ rc = sqlite3_reset(pUpdate1);
+ if( rc!=SQLITE_OK ){
+ sqlite3_reset(pRange);
+ break;
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_reset(pRange);
+ }
+
+ /* Move level -1 to level iAbsLevel */
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pUpdate2, 1, iAbsLevel);
+ sqlite3_step(pUpdate2);
+ rc = sqlite3_reset(pUpdate2);
+ }
+ }
+ }
+
+
+ return rc;
+}
+
+/*
** Merge all level iLevel segments in the database into a single
** iLevel+1 segment. Or, if iLevel<0, merge all segments into a
** single segment with a level equal to the numerically largest level
@@ -2967,6 +3157,7 @@ static int fts3SegmentMerge(
Fts3SegFilter filter; /* Segment term filter condition */
Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
int bIgnoreEmpty = 0; /* True to ignore empty segments */
+ i64 iMaxLevel = 0; /* Max level number for this index/langid */
assert( iLevel==FTS3_SEGCURSOR_ALL
|| iLevel==FTS3_SEGCURSOR_PENDING
@@ -2978,6 +3169,11 @@ static int fts3SegmentMerge(
rc = sqlite3Fts3SegReaderCursor(p, iLangid, iIndex, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
+ if( iLevel!=FTS3_SEGCURSOR_PENDING ){
+ rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iMaxLevel);
+ if( rc!=SQLITE_OK ) goto finished;
+ }
+
if( iLevel==FTS3_SEGCURSOR_ALL ){
/* This call is to merge all segments in the database to a single
** segment. The level of the new segment is equal to the numerically
@@ -2987,21 +3183,21 @@ static int fts3SegmentMerge(
rc = SQLITE_DONE;
goto finished;
}
- rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iNewLevel);
+ iNewLevel = iMaxLevel;
bIgnoreEmpty = 1;
- }else if( iLevel==FTS3_SEGCURSOR_PENDING ){
- iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, 0);
- rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, 0, &iIdx);
}else{
/* This call is to merge all segments at level iLevel. find the next
** available segment index at level iLevel+1. The call to
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary. */
- rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx);
+ assert( FTS3_SEGCURSOR_PENDING==-1 );
iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, iLevel+1);
+ rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx);
+ bIgnoreEmpty = (iLevel!=FTS3_SEGCURSOR_PENDING) && (iNewLevel>iMaxLevel);
}
if( rc!=SQLITE_OK ) goto finished;
+
assert( csr.nSegment>0 );
assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
assert( iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL) );
@@ -3018,7 +3214,7 @@ static int fts3SegmentMerge(
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
- assert( pWriter );
+ assert( pWriter || bIgnoreEmpty );
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
rc = fts3DeleteSegdir(
@@ -3026,7 +3222,14 @@ static int fts3SegmentMerge(
);
if( rc!=SQLITE_OK ) goto finished;
}
- rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
+ if( pWriter ){
+ rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
+ if( rc==SQLITE_OK ){
+ if( iLevel==FTS3_SEGCURSOR_PENDING || iNewLevel<iMaxLevel ){
+ rc = fts3PromoteSegments(p, iNewLevel, pWriter->nLeafData);
+ }
+ }
+ }
finished:
fts3SegWriterFree(pWriter);
@@ -3036,7 +3239,7 @@ static int fts3SegmentMerge(
/*
-** Flush the contents of pendingTerms to level 0 segments.
+** Flush the contents of pendingTerms to level 0 segments.
*/
int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
int rc = SQLITE_OK;
@@ -3052,14 +3255,19 @@ int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
** estimate the number of leaf blocks of content to be written
*/
if( rc==SQLITE_OK && p->bHasStat
- && p->bAutoincrmerge==0xff && p->nLeafAdd>0
+ && p->nAutoincrmerge==0xff && p->nLeafAdd>0
){
sqlite3_stmt *pStmt = 0;
rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
rc = sqlite3_step(pStmt);
- p->bAutoincrmerge = (rc==SQLITE_ROW && sqlite3_column_int(pStmt, 0));
+ if( rc==SQLITE_ROW ){
+ p->nAutoincrmerge = sqlite3_column_int(pStmt, 0);
+ if( p->nAutoincrmerge==1 ) p->nAutoincrmerge = 8;
+ }else if( rc==SQLITE_DONE ){
+ p->nAutoincrmerge = 0;
+ }
rc = sqlite3_reset(pStmt);
}
}
@@ -3303,9 +3511,11 @@ static int fts3DoRebuild(Fts3Table *p){
rc = fts3PendingTermsDocid(p, iLangid, sqlite3_column_int64(pStmt, 0));
memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
- const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
- rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
- aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ if( p->abNotindexed[iCol]==0 ){
+ const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
+ rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ }
}
if( p->bHasDocsize ){
fts3InsertDocsize(&rc, p, aSz);
@@ -3425,6 +3635,8 @@ struct IncrmergeWriter {
int iIdx; /* Index of *output* segment in iAbsLevel+1 */
sqlite3_int64 iStart; /* Block number of first allocated block */
sqlite3_int64 iEnd; /* Block number of last allocated block */
+ sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
+ u8 bNoLeafData; /* If true, store 0 for segment size */
NodeWriter aNodeWriter[FTS_MAX_APPENDABLE_HEIGHT];
};
@@ -3493,9 +3705,9 @@ static int nodeReaderNext(NodeReader *p){
p->aNode = 0;
}else{
if( bFirst==0 ){
- p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nPrefix);
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix);
}
- p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
if( rc==SQLITE_OK ){
@@ -3503,7 +3715,7 @@ static int nodeReaderNext(NodeReader *p){
p->term.n = nPrefix+nSuffix;
p->iOff += nSuffix;
if( p->iChild==0 ){
- p->iOff += sqlite3Fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
p->aDoclist = &p->aNode[p->iOff];
p->iOff += p->nDoclist;
}
@@ -3763,8 +3975,8 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
}
+ pWriter->nLeafData += nSpace;
blobGrowBuffer(&pLeaf->block, pLeaf->block.n + nSpace, &rc);
-
if( rc==SQLITE_OK ){
if( pLeaf->block.n==0 ){
pLeaf->block.n = 1;
@@ -3863,6 +4075,7 @@ static void fts3IncrmergeRelease(
pWriter->iStart, /* start_block */
pWriter->aNodeWriter[0].iBlock, /* leaves_end_block */
pWriter->iEnd, /* end_block */
+ (pWriter->bNoLeafData==0 ? pWriter->nLeafData : 0), /* end_block */
pRoot->block.a, pRoot->block.n /* root */
);
}
@@ -3964,7 +4177,11 @@ static int fts3IncrmergeLoad(
if( sqlite3_step(pSelect)==SQLITE_ROW ){
iStart = sqlite3_column_int64(pSelect, 1);
iLeafEnd = sqlite3_column_int64(pSelect, 2);
- iEnd = sqlite3_column_int64(pSelect, 3);
+ fts3ReadEndBlockField(pSelect, 3, &iEnd, &pWriter->nLeafData);
+ if( pWriter->nLeafData<0 ){
+ pWriter->nLeafData = pWriter->nLeafData * -1;
+ }
+ pWriter->bNoLeafData = (pWriter->nLeafData==0);
nRoot = sqlite3_column_bytes(pSelect, 4);
aRoot = sqlite3_column_blob(pSelect, 4);
}else{
@@ -4555,7 +4772,7 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
pHint->n = i;
i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
- i += sqlite3Fts3GetVarint32(&pHint->a[i], pnInput);
+ i += fts3GetVarint32(&pHint->a[i], pnInput);
if( i!=nHint ) return SQLITE_CORRUPT_VTAB;
return SQLITE_OK;
@@ -4565,11 +4782,11 @@ static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
/*
** Attempt an incremental merge that writes nMerge leaf blocks.
**
-** Incremental merges happen nMin segments at a time. The two
-** segments to be merged are the nMin oldest segments (the ones with
-** the smallest indexes) in the highest level that contains at least
-** nMin segments. Multiple merges might occur in an attempt to write the
-** quota of nMerge leaf blocks.
+** Incremental merges happen nMin segments at a time. The segments
+** to be merged are the nMin oldest segments (the ones with the smallest
+** values for the _segdir.idx field) in the highest level that contains
+** at least nMin segments. Multiple merges might occur in an attempt to
+** write the quota of nMerge leaf blocks.
*/
int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
int rc; /* Return code */
@@ -4594,6 +4811,7 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
const i64 nMod = FTS3_SEGDIR_MAXLEVEL * p->nIndex;
sqlite3_stmt *pFindLevel = 0; /* SQL used to determine iAbsLevel */
int bUseHint = 0; /* True if attempting to append */
+ int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */
/* Search the %_segdir table for the absolute level with the smallest
** relative level number that contains at least nMin segments, if any.
@@ -4647,6 +4865,19 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
** to start work on some other level. */
memset(pWriter, 0, nAlloc);
pFilter->flags = FTS3_SEGMENT_REQUIRE_POS;
+
+ if( rc==SQLITE_OK ){
+ rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
+ assert( bUseHint==1 || bUseHint==0 );
+ if( iIdx==0 || (bUseHint && iIdx==1) ){
+ int bIgnore = 0;
+ rc = fts3SegmentIsMaxLevel(p, iAbsLevel+1, &bIgnore);
+ if( bIgnore ){
+ pFilter->flags |= FTS3_SEGMENT_IGNORE_EMPTY;
+ }
+ }
+ }
+
if( rc==SQLITE_OK ){
rc = fts3IncrmergeCsr(p, iAbsLevel, nSeg, pCsr);
}
@@ -4654,16 +4885,12 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
&& SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
&& SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
){
- int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */
- rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
- if( rc==SQLITE_OK ){
- if( bUseHint && iIdx>0 ){
- const char *zKey = pCsr->zTerm;
- int nKey = pCsr->nTerm;
- rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
- }else{
- rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
- }
+ if( bUseHint && iIdx>0 ){
+ const char *zKey = pCsr->zTerm;
+ int nKey = pCsr->nTerm;
+ rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
+ }else{
+ rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
}
if( rc==SQLITE_OK && pWriter->nLeafEst ){
@@ -4685,7 +4912,13 @@ int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
}
}
+ if( nSeg!=0 ){
+ pWriter->nLeafData = pWriter->nLeafData * -1;
+ }
fts3IncrmergeRelease(p, pWriter, &rc);
+ if( nSeg==0 && pWriter->bNoLeafData==0 ){
+ fts3PromoteSegments(p, iAbsLevel+1, pWriter->nLeafData);
+ }
}
sqlite3Fts3SegReaderFinish(pCsr);
@@ -4772,16 +5005,19 @@ static int fts3DoAutoincrmerge(
){
int rc = SQLITE_OK;
sqlite3_stmt *pStmt = 0;
- p->bAutoincrmerge = fts3Getint(&zParam)!=0;
+ p->nAutoincrmerge = fts3Getint(&zParam);
+ if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
+ p->nAutoincrmerge = 8;
+ }
if( !p->bHasStat ){
assert( p->bFts4==0 );
sqlite3Fts3CreateStatTable(&rc, p);
if( rc ) return rc;
}
rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
- if( rc ) return rc;;
+ if( rc ) return rc;
sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
- sqlite3_bind_int(pStmt, 2, p->bAutoincrmerge);
+ sqlite3_bind_int(pStmt, 2, p->nAutoincrmerge);
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
return rc;
@@ -4938,34 +5174,36 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
int iCol;
for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
- const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
- int nText = sqlite3_column_bytes(pStmt, iCol+1);
- sqlite3_tokenizer_cursor *pT = 0;
-
- rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText, &pT);
- while( rc==SQLITE_OK ){
- char const *zToken; /* Buffer containing token */
- int nToken = 0; /* Number of bytes in token */
- int iDum1 = 0, iDum2 = 0; /* Dummy variables */
- int iPos = 0; /* Position of token in zText */
-
- rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos);
- if( rc==SQLITE_OK ){
- int i;
- cksum2 = cksum2 ^ fts3ChecksumEntry(
- zToken, nToken, iLang, 0, iDocid, iCol, iPos
- );
- for(i=1; i<p->nIndex; i++){
- if( p->aIndex[i].nPrefix<=nToken ){
- cksum2 = cksum2 ^ fts3ChecksumEntry(
- zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos
- );
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
+ int nText = sqlite3_column_bytes(pStmt, iCol+1);
+ sqlite3_tokenizer_cursor *pT = 0;
+
+ rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
+ while( rc==SQLITE_OK ){
+ char const *zToken; /* Buffer containing token */
+ int nToken = 0; /* Number of bytes in token */
+ int iDum1 = 0, iDum2 = 0; /* Dummy variables */
+ int iPos = 0; /* Position of token in zText */
+
+ rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos);
+ if( rc==SQLITE_OK ){
+ int i;
+ cksum2 = cksum2 ^ fts3ChecksumEntry(
+ zToken, nToken, iLang, 0, iDocid, iCol, iPos
+ );
+ for(i=1; i<p->nIndex; i++){
+ if( p->aIndex[i].nPrefix<=nToken ){
+ cksum2 = cksum2 ^ fts3ChecksumEntry(
+ zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos
+ );
+ }
}
}
}
+ if( pT ) pModule->xClose(pT);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- if( pT ) pModule->xClose(pT);
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
}
@@ -5049,6 +5287,9 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
+ }else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
+ p->bNoIncrDoclist = atoi(&zVal[21]);
+ rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;
@@ -5108,32 +5349,34 @@ int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
iDocid = sqlite3_column_int64(pCsr->pStmt, 0);
for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){
- const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
- sqlite3_tokenizer_cursor *pTC = 0;
-
- rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
- while( rc==SQLITE_OK ){
- char const *zToken; /* Buffer containing token */
- int nToken = 0; /* Number of bytes in token */
- int iDum1 = 0, iDum2 = 0; /* Dummy variables */
- int iPos = 0; /* Position of token in zText */
-
- rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
- for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
- Fts3PhraseToken *pPT = pDef->pToken;
- if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
- && (pPT->bFirst==0 || iPos==0)
- && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
- && (0==memcmp(zToken, pPT->z, pPT->n))
- ){
- fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ if( p->abNotindexed[i]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
+ sqlite3_tokenizer_cursor *pTC = 0;
+
+ rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
+ while( rc==SQLITE_OK ){
+ char const *zToken; /* Buffer containing token */
+ int nToken = 0; /* Number of bytes in token */
+ int iDum1 = 0, iDum2 = 0; /* Dummy variables */
+ int iPos = 0; /* Position of token in zText */
+
+ rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
+ for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
+ Fts3PhraseToken *pPT = pDef->pToken;
+ if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
+ && (pPT->bFirst==0 || iPos==0)
+ && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
+ && (0==memcmp(zToken, pPT->z, pPT->n))
+ ){
+ fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ }
}
}
+ if( pTC ) pModule->xClose(pTC);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- if( pTC ) pModule->xClose(pTC);
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
-
+
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
if( pDef->pList ){
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
@@ -5265,6 +5508,10 @@ int sqlite3Fts3UpdateMethod(
int nChng = 0; /* Net change in number of documents */
int bInsertDone = 0;
+ /* At this point it must be known if the %_stat table exists or not.
+ ** So bHasStat may not be 2. */
+ assert( p->bHasStat==0 || p->bHasStat==1 );
+
assert( p->pSegments==0 );
assert(
nArg==1 /* DELETE operations */
@@ -5297,6 +5544,9 @@ int sqlite3Fts3UpdateMethod(
aSzIns = &aSzDel[p->nColumn+1];
memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2);
+ rc = fts3Writelock(p);
+ if( rc!=SQLITE_OK ) goto update_out;
+
/* If this is an INSERT operation, or an UPDATE that modifies the rowid
** value, then this operation requires constraint handling.
**
diff --git a/ext/fts3/tool/fts3view.c b/ext/fts3/tool/fts3view.c
index 479ae98..3dc1ba8 100644
--- a/ext/fts3/tool/fts3view.c
+++ b/ext/fts3/tool/fts3view.c
@@ -376,7 +376,7 @@ static void showSegmentStats(sqlite3 *db, const char *zTab){
sqlite3_finalize(pStmt);
nLeaf = nSeg - nIdx;
printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n",
- pgsz-45, n, n*100.0/nLeaf);
+ pgsz-45, n, nLeaf>0 ? n*100.0/nLeaf : 0.0);
pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab);
mxLevel = 0;
@@ -554,7 +554,7 @@ static void decodeSegment(
sqlite3_int64 n;
sqlite3_int64 iDocsz;
int iHeight;
- int i = 0;
+ sqlite3_int64 i = 0;
int cnt = 0;
char zTerm[1000];
@@ -576,12 +576,12 @@ static void decodeSegment(
fprintf(stderr, "term to long\n");
exit(1);
}
- memcpy(zTerm+iPrefix, aData+i, nTerm);
+ memcpy(zTerm+iPrefix, aData+i, (size_t)nTerm);
zTerm[iPrefix+nTerm] = 0;
i += nTerm;
if( iHeight==0 ){
i += getVarint(aData+i, &iDocsz);
- printf("term: %-25s doclist %7lld bytes offset %d\n", zTerm, iDocsz, i);
+ printf("term: %-25s doclist %7lld bytes offset %lld\n", zTerm, iDocsz, i);
i += iDocsz;
}else{
printf("term: %-25s child %lld\n", zTerm, ++iChild);
@@ -749,18 +749,19 @@ static void decodeDoclist(
*/
static void showDoclist(sqlite3 *db, const char *zTab){
const unsigned char *aData;
- sqlite3_int64 offset, nData;
+ sqlite3_int64 offset;
+ int nData;
sqlite3_stmt *pStmt;
offset = atoi64(azExtra[1]);
- nData = atoi64(azExtra[2]);
+ nData = atoi(azExtra[2]);
pStmt = prepareToGetSegment(db, zTab, azExtra[0]);
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
sqlite3_finalize(pStmt);
return;
}
aData = sqlite3_column_blob(pStmt, 0);
- printf("Doclist at %s offset %lld of size %lld bytes:\n",
+ printf("Doclist at %s offset %lld of size %d bytes:\n",
azExtra[0], offset, nData);
if( findOption("raw", 0, 0)!=0 ){
printBlob(aData+offset, nData);
diff --git a/ext/fts3/unicode/mkunicode.tcl b/ext/fts3/unicode/mkunicode.tcl
index 0d58e8a..c3083ee 100644
--- a/ext/fts3/unicode/mkunicode.tcl
+++ b/ext/fts3/unicode/mkunicode.tcl
@@ -160,7 +160,7 @@ proc print_rd {map} {
}
assert( key>=aDia[iRes] );
return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);}
- puts "\};"
+ puts "\}"
}
proc print_isdiacritic {zFunc map} {
@@ -239,7 +239,10 @@ proc an_load_unicodedata_text {zName} {
foreach $lField $fields {}
set iCode [expr "0x$code"]
- set bAlnum [expr {[lsearch {L N} [string range $general_category 0 0]]>=0}]
+ set bAlnum [expr {
+ [lsearch {L N} [string range $general_category 0 0]] >= 0
+ || $general_category=="Co"
+ }]
if { !$bAlnum } { lappend lRet $iCode }
}
@@ -295,7 +298,7 @@ proc an_print_range_array {lRange} {
** using this format.
*/
}]
- puts -nonewline " const static unsigned int aEntry\[\] = \{"
+ puts -nonewline " static const unsigned int aEntry\[\] = \{"
set i 0
foreach range $lRange {
foreach {iFirst nRange} $range {}
@@ -346,7 +349,7 @@ proc print_isalnum {zFunc lRange} {
return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
}else if( c<(1<<22) ){
unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
- int iRes;
+ int iRes = 0;
int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
int iLo = 0;
while( iHi>=iLo ){
@@ -360,7 +363,7 @@ proc print_isalnum {zFunc lRange} {
}
assert( aEntry[0]<key );
assert( key>=aEntry[iRes] );
- return (c >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
}
return 1;}
puts "\}"
@@ -729,7 +732,7 @@ proc print_fileheader {} {
*/
}]
puts ""
- puts "#if !defined(SQLITE_DISABLE_FTS3_UNICODE)"
+ puts "#ifndef SQLITE_DISABLE_FTS3_UNICODE"
puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)"
puts ""
puts "#include <assert.h>"
diff --git a/ext/icu/icu.c b/ext/icu/icu.c
index ae28d70..1ce1e0c 100644
--- a/ext/icu/icu.c
+++ b/ext/icu/icu.c
@@ -22,7 +22,7 @@
** * Implementations of the SQL scalar upper() and lower() functions
** for case mapping.
**
-** * Integration of ICU and SQLite collation seqences.
+** * Integration of ICU and SQLite collation sequences.
**
** * An implementation of the LIKE operator that uses ICU to
** provide case-independent matching.
@@ -488,7 +488,10 @@ int sqlite3IcuInit(sqlite3 *db){
}
#if !SQLITE_CORE
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/ext/misc/amatch.c b/ext/misc/amatch.c
index b613080..d869dbd 100644
--- a/ext/misc/amatch.c
+++ b/ext/misc/amatch.c
@@ -786,6 +786,7 @@ static void amatchFree(amatch_vtab *p){
sqlite3_free(p->zVocabTab);
sqlite3_free(p->zVocabWord);
sqlite3_free(p->zVocabLang);
+ sqlite3_free(p->zSelf);
memset(p, 0, sizeof(*p));
sqlite3_free(p);
}
@@ -948,6 +949,9 @@ static void amatchClearCursor(amatch_cursor *pCur){
pCur->pAllWords = 0;
sqlite3_free(pCur->zInput);
pCur->zInput = 0;
+ sqlite3_free(pCur->zBuf);
+ pCur->zBuf = 0;
+ pCur->nBuf = 0;
pCur->pCost = 0;
pCur->pWord = 0;
pCur->pCurrent = 0;
@@ -1103,7 +1107,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
char *zSql;
if( p->zVocabLang && p->zVocabLang[0] ){
zSql = sqlite3_mprintf(
- "SELECT \"%s\" FROM \"%s\"",
+ "SELECT \"%w\" FROM \"%w\"",
" WHERE \"%w\">=?1 AND \"%w\"=?2"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,
@@ -1111,7 +1115,7 @@ static int amatchNext(sqlite3_vtab_cursor *cur){
);
}else{
zSql = sqlite3_mprintf(
- "SELECT \"%s\" FROM \"%s\""
+ "SELECT \"%w\" FROM \"%w\""
" WHERE \"%w\">=?1"
" ORDER BY 1",
p->zVocabWord, p->zVocabTab,
diff --git a/ext/misc/closure.c b/ext/misc/closure.c
index 213b763..30c812d 100644
--- a/ext/misc/closure.c
+++ b/ext/misc/closure.c
@@ -496,7 +496,7 @@ static const char *closureValueOfKey(const char *zKey, const char *zStr){
/*
** xConnect/xCreate method for the closure module. Arguments are:
**
-** argv[0] -> module name ("approximate_match")
+** argv[0] -> module name ("transitive_closure")
** argv[1] -> database name
** argv[2] -> table name
** argv[3...] -> arguments
@@ -826,11 +826,17 @@ static int closureBestIndex(
int iPlan = 0;
int i;
int idx = 1;
+ int seenMatch = 0;
const struct sqlite3_index_constraint *pConstraint;
closure_vtab *pVtab = (closure_vtab*)pTab;
+ double rCost = 10000000.0;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->iColumn==CLOSURE_COL_ROOT
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ seenMatch = 1;
+ }
if( pConstraint->usable==0 ) continue;
if( (iPlan & 1)==0
&& pConstraint->iColumn==CLOSURE_COL_ROOT
@@ -839,6 +845,7 @@ static int closureBestIndex(
iPlan |= 1;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 100.0;
}
if( (iPlan & 0x0000f0)==0
&& pConstraint->iColumn==CLOSURE_COL_DEPTH
@@ -849,6 +856,7 @@ static int closureBestIndex(
iPlan |= idx<<4;
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) iPlan |= 0x000002;
+ rCost /= 5.0;
}
if( (iPlan & 0x000f00)==0
&& pConstraint->iColumn==CLOSURE_COL_TABLENAME
@@ -857,6 +865,7 @@ static int closureBestIndex(
iPlan |= idx<<8;
pIdxInfo->aConstraintUsage[i].argvIndex = ++idx;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 5.0;
}
if( (iPlan & 0x00f000)==0
&& pConstraint->iColumn==CLOSURE_COL_IDCOLUMN
@@ -891,13 +900,14 @@ static int closureBestIndex(
){
pIdxInfo->orderByConsumed = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
+ pIdxInfo->estimatedCost = rCost;
return SQLITE_OK;
}
/*
-** A virtual table module that implements the "approximate_match".
+** A virtual table module that implements the "transitive_closure".
*/
static sqlite3_module closureModule = {
0, /* iVersion */
diff --git a/ext/misc/compress.c b/ext/misc/compress.c
new file mode 100644
index 0000000..a405911
--- /dev/null
+++ b/ext/misc/compress.c
@@ -0,0 +1,107 @@
+/*
+** 2014-06-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 SQLite extension implements SQL compression functions
+** compress() and uncompress() using ZLIB.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <zlib.h>
+
+/*
+** Implementation of the "compress(X)" SQL function. The input X is
+** compressed using zLib and the output is returned.
+**
+** The output is a BLOB that begins with a variable-length integer that
+** is the input size in bytes (the size of X before compression). The
+** variable-length integer is implemented as 1 to 5 bytes. There are
+** seven bits per integer stored in the lower seven bits of each byte.
+** More significant bits occur first. The most significant bit (0x80)
+** is a flag to indicate the end of the integer.
+*/
+static void compressFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *pIn;
+ unsigned char *pOut;
+ unsigned int nIn;
+ unsigned long int nOut;
+ unsigned char x[8];
+ int i, j;
+
+ pIn = sqlite3_value_blob(argv[0]);
+ nIn = sqlite3_value_bytes(argv[0]);
+ nOut = 13 + nIn + (nIn+999)/1000;
+ pOut = sqlite3_malloc( nOut+5 );
+ for(i=4; i>=0; i--){
+ x[i] = (nIn >> (7*(4-i)))&0x7f;
+ }
+ for(i=0; i<4 && x[i]==0; i++){}
+ for(j=0; i<=4; i++, j++) pOut[j] = x[i];
+ pOut[j-1] |= 0x80;
+ compress(&pOut[j], &nOut, pIn, nIn);
+ sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
+}
+
+/*
+** Implementation of the "uncompress(X)" SQL function. The argument X
+** is a blob which was obtained from compress(Y). The output will be
+** the value Y.
+*/
+static void uncompressFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *pIn;
+ unsigned char *pOut;
+ unsigned int nIn;
+ unsigned long int nOut;
+ int rc;
+ int i;
+
+ pIn = sqlite3_value_blob(argv[0]);
+ nIn = sqlite3_value_bytes(argv[0]);
+ nOut = 0;
+ for(i=0; i<nIn && i<5; i++){
+ nOut = (nOut<<7) | (pIn[i]&0x7f);
+ if( (pIn[i]&0x80)!=0 ){ i++; break; }
+ }
+ pOut = sqlite3_malloc( nOut+1 );
+ rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
+ if( rc==Z_OK ){
+ sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
+ }
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_compress_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
+ compressFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
+ uncompressFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c
new file mode 100644
index 0000000..fbe2d03
--- /dev/null
+++ b/ext/misc/fileio.c
@@ -0,0 +1,100 @@
+/*
+** 2014-06-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 SQLite extension implements SQL functions readfile() and
+** writefile().
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <stdio.h>
+
+/*
+** Implementation of the "readfile(X)" SQL function. The entire content
+** of the file named X is read and returned as a BLOB. NULL is returned
+** if the file does not exist or is unreadable.
+*/
+static void readfileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *zName;
+ FILE *in;
+ long nIn;
+ void *pBuf;
+
+ zName = (const char*)sqlite3_value_text(argv[0]);
+ if( zName==0 ) return;
+ in = fopen(zName, "rb");
+ if( in==0 ) return;
+ fseek(in, 0, SEEK_END);
+ nIn = ftell(in);
+ rewind(in);
+ pBuf = sqlite3_malloc( nIn );
+ if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
+ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
+ }else{
+ sqlite3_free(pBuf);
+ }
+ fclose(in);
+}
+
+/*
+** Implementation of the "writefile(X,Y)" SQL function. The argument Y
+** is written into file X. The number of bytes written is returned. Or
+** NULL is returned if something goes wrong, such as being unable to open
+** file X for writing.
+*/
+static void writefileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ FILE *out;
+ const char *z;
+ sqlite3_int64 rc;
+ const char *zFile;
+
+ zFile = (const char*)sqlite3_value_text(argv[0]);
+ if( zFile==0 ) return;
+ out = fopen(zFile, "wb");
+ if( out==0 ) return;
+ z = (const char*)sqlite3_value_blob(argv[1]);
+ if( z==0 ){
+ rc = 0;
+ }else{
+ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
+ }
+ fclose(out);
+ sqlite3_result_int64(context, rc);
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_fileio_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
+ readfileFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
+ writefileFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/fuzzer.c b/ext/misc/fuzzer.c
index 642b8f9..fe41cda 100644
--- a/ext/misc/fuzzer.c
+++ b/ext/misc/fuzzer.c
@@ -1077,9 +1077,16 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iDistTerm = -1;
int iRulesetTerm = -1;
int i;
+ int seenMatch = 0;
const struct sqlite3_index_constraint *pConstraint;
+ double rCost = 1e12;
+
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->iColumn==0
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ seenMatch = 1;
+ }
if( pConstraint->usable==0 ) continue;
if( (iPlan & 1)==0
&& pConstraint->iColumn==0
@@ -1088,6 +1095,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= 1;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
+ rCost /= 1e6;
}
if( (iPlan & 2)==0
&& pConstraint->iColumn==1
@@ -1096,6 +1104,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
){
iPlan |= 2;
iDistTerm = i;
+ rCost /= 10.0;
}
if( (iPlan & 4)==0
&& pConstraint->iColumn==2
@@ -1104,6 +1113,7 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= 4;
pIdxInfo->aConstraintUsage[i].omit = 1;
iRulesetTerm = i;
+ rCost /= 10.0;
}
}
if( iPlan & 2 ){
@@ -1122,7 +1132,8 @@ static int fuzzerBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
){
pIdxInfo->orderByConsumed = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ if( seenMatch && (iPlan&1)==0 ) rCost = 1e99;
+ pIdxInfo->estimatedCost = rCost;
return SQLITE_OK;
}
diff --git a/ext/misc/ieee754.c b/ext/misc/ieee754.c
index 436b11e..f143893 100644
--- a/ext/misc/ieee754.c
+++ b/ext/misc/ieee754.c
@@ -18,11 +18,11 @@
**
** In the first form, the value X should be a floating-point number.
** The function will return a string of the form 'ieee754(Y,Z)' where
-** Y and Z are integers such that X==Y*pow(w.0,Z).
+** Y and Z are integers such that X==Y*pow(2,Z).
**
** In the second form, Y and Z are integers which are the mantissa and
** base-2 exponent of a new floating point number. The function returns
-** a floating-point value equal to Y*pow(2.0,Z).
+** a floating-point value equal to Y*pow(2,Z).
**
** Examples:
**
diff --git a/ext/misc/nextchar.c b/ext/misc/nextchar.c
index e063043..49dfd24 100644
--- a/ext/misc/nextchar.c
+++ b/ext/misc/nextchar.c
@@ -10,12 +10,22 @@
**
******************************************************************************
**
-** This file contains code to implement the next_char(A,T,F,W) SQL function.
+** This file contains code to implement the next_char(A,T,F,W,C) SQL function.
**
-** The next_char(A,T,F,H) function finds all valid "next" characters for
-** string A given the vocabulary in T.F. The T.F field should be indexed.
-** If the W value exists and is a non-empty string, then it is an SQL
-** expression that limits the entries in T.F that will be considered.
+** The next_char(A,T,F,W,C) function finds all valid "next" characters for
+** string A given the vocabulary in T.F. If the W value exists and is a
+** non-empty string, then it is an SQL expression that limits the entries
+** in T.F that will be considered. If C exists and is a non-empty string,
+** then it is the name of the collating sequence to use for comparison. If
+**
+** Only the first three arguments are required. If the C parameter is
+** omitted or is NULL or is an empty string, then the default collating
+** sequence of T.F is used for comparision. If the W parameter is omitted
+** or is NULL or is an empty string, then no filtering of the output is
+** done.
+**
+** The T.F column should be indexed using collation C or else this routine
+** will be quite slow.
**
** For example, suppose an application has a dictionary like this:
**
@@ -28,6 +38,19 @@
** out) run the following query:
**
** SELECT next_char('cha','dictionary','word');
+**
+** IMPLEMENTATION NOTES:
+**
+** The next_char function is implemented using recursive SQL that makes
+** use of the table name and column name as part of a query. If either
+** the table name or column name are keywords or contain special characters,
+** then they should be escaped. For example:
+**
+** SELECT next_char('cha','[dictionary]','[word]');
+**
+** This also means that the table name can be a subquery:
+**
+** SELECT next_char('cha','(SELECT word AS w FROM dictionary)','w');
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -184,6 +207,9 @@ static void nextCharFunc(
const unsigned char *zTable = sqlite3_value_text(argv[1]);
const unsigned char *zField = sqlite3_value_text(argv[2]);
const unsigned char *zWhere;
+ const unsigned char *zCollName;
+ char *zWhereClause = 0;
+ char *zColl = 0;
char *zSql;
int rc;
@@ -192,25 +218,41 @@ static void nextCharFunc(
c.zPrefix = sqlite3_value_text(argv[0]);
c.nPrefix = sqlite3_value_bytes(argv[0]);
if( zTable==0 || zField==0 || c.zPrefix==0 ) return;
- if( argc<4
- || (zWhere = sqlite3_value_text(argv[3]))==0
- || zWhere[0]==0
+ if( argc>=4
+ && (zWhere = sqlite3_value_text(argv[3]))!=0
+ && zWhere[0]!=0
+ ){
+ zWhereClause = sqlite3_mprintf("AND (%s)", zWhere);
+ if( zWhereClause==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ }else{
+ zWhereClause = "";
+ }
+ if( argc>=5
+ && (zCollName = sqlite3_value_text(argv[4]))!=0
+ && zCollName[0]!=0
){
- zSql = sqlite3_mprintf(
- "SELECT \"%w\" FROM \"%w\""
- " WHERE \"%w\">=(?1 || ?2)"
- " AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
- " ORDER BY 1 ASC LIMIT 1",
- zField, zTable, zField, zField);
+ zColl = sqlite3_mprintf("collate \"%w\"", zCollName);
+ if( zColl==0 ){
+ sqlite3_result_error_nomem(context);
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
+ return;
+ }
}else{
- zSql = sqlite3_mprintf(
- "SELECT \"%w\" FROM \"%w\""
- " WHERE \"%w\">=(?1 || ?2)"
- " AND \"%w\"<=(?1 || char(1114111))" /* 1114111 == 0x10ffff */
- " AND (%s)"
- " ORDER BY 1 ASC LIMIT 1",
- zField, zTable, zField, zField, zWhere);
+ zColl = "";
}
+ zSql = sqlite3_mprintf(
+ "SELECT %s FROM %s"
+ " WHERE %s>=(?1 || ?2) %s"
+ " AND %s<=(?1 || char(1114111)) %s" /* 1114111 == 0x10ffff */
+ " %s"
+ " ORDER BY 1 %s ASC LIMIT 1",
+ zField, zTable, zField, zColl, zField, zColl, zWhereClause, zColl
+ );
+ if( zWhereClause[0] ) sqlite3_free(zWhereClause);
+ if( zColl[0] ) sqlite3_free(zColl);
if( zSql==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -261,5 +303,9 @@ int sqlite3_nextchar_init(
rc = sqlite3_create_function(db, "next_char", 4, SQLITE_UTF8, 0,
nextCharFunc, 0, 0);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "next_char", 5, SQLITE_UTF8, 0,
+ nextCharFunc, 0, 0);
+ }
return rc;
}
diff --git a/ext/misc/percentile.c b/ext/misc/percentile.c
new file mode 100644
index 0000000..74a4c9d
--- /dev/null
+++ b/ext/misc/percentile.c
@@ -0,0 +1,219 @@
+/*
+** 2013-05-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 code to implement the percentile(Y,P) SQL function
+** as described below:
+**
+** (1) The percentile(Y,P) function is an aggregate function taking
+** exactly two arguments.
+**
+** (2) If the P argument to percentile(Y,P) is not the same for every
+** row in the aggregate then an error is thrown. The word "same"
+** in the previous sentence means that the value differ by less
+** than 0.001.
+**
+** (3) If the P argument to percentile(Y,P) evaluates to anything other
+** than a number in the range of 0.0 to 100.0 inclusive then an
+** error is thrown.
+**
+** (4) If any Y argument to percentile(Y,P) evaluates to a value that
+** is not NULL and is not numeric then an error is thrown.
+**
+** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
+** infinity then an error is thrown. (SQLite always interprets NaN
+** values as NULL.)
+**
+** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
+** including CASE WHEN expressions.
+**
+** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
+** one million (1,000,000) rows.
+**
+** (8) If there are no non-NULL values for Y, then percentile(Y,P)
+** returns NULL.
+**
+** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
+** returns the one Y value.
+**
+** (10) If there N non-NULL values of Y where N is two or more and
+** the Y values are ordered from least to greatest and a graph is
+** drawn from 0 to N-1 such that the height of the graph at J is
+** the J-th Y value and such that straight lines are drawn between
+** adjacent Y values, then the percentile(Y,P) function returns
+** the height of the graph at P*(N-1)/100.
+**
+** (11) The percentile(Y,P) function always returns either a floating
+** point number or NULL.
+**
+** (12) The percentile(Y,P) is implemented as a single C99 source-code
+** file that compiles into a shared-library or DLL that can be loaded
+** into SQLite using the sqlite3_load_extension() interface.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* The following object is the session context for a single percentile()
+** function. We have to remember all input Y values until the very end.
+** Those values are accumulated in the Percentile.a[] array.
+*/
+typedef struct Percentile Percentile;
+struct Percentile {
+ unsigned nAlloc; /* Number of slots allocated for a[] */
+ unsigned nUsed; /* Number of slots actually used in a[] */
+ double rPct; /* 1.0 more than the value for P */
+ double *a; /* Array of Y values */
+};
+
+/*
+** Return TRUE if the input floating-point number is an infinity.
+*/
+static int isInfinity(double r){
+ sqlite3_uint64 u;
+ assert( sizeof(u)==sizeof(r) );
+ memcpy(&u, &r, sizeof(u));
+ return ((u>>52)&0x7ff)==0x7ff;
+}
+
+/*
+** Return TRUE if two doubles differ by 0.001 or less
+*/
+static int sameValue(double a, double b){
+ a -= b;
+ return a>=-0.001 && a<=0.001;
+}
+
+/*
+** The "step" function for percentile(Y,P) is called once for each
+** input row.
+*/
+static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
+ Percentile *p;
+ double rPct;
+ int eType;
+ double y;
+ assert( argc==2 );
+
+ /* Requirement 3: P must be a number between 0 and 100 */
+ eType = sqlite3_value_numeric_type(argv[1]);
+ rPct = sqlite3_value_double(argv[1]);
+ if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT) ||
+ ((rPct = sqlite3_value_double(argv[1]))<0.0 || rPct>100.0) ){
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not "
+ "a number between 0.0 and 100.0", -1);
+ return;
+ }
+
+ /* Allocate the session context. */
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
+ if( p==0 ) return;
+
+ /* Remember the P value. Throw an error if the P value is different
+ ** from any prior row, per Requirement (2). */
+ if( p->rPct==0.0 ){
+ p->rPct = rPct+1.0;
+ }else if( !sameValue(p->rPct,rPct+1.0) ){
+ sqlite3_result_error(pCtx, "2nd argument to percentile() is not the "
+ "same for all input rows", -1);
+ return;
+ }
+
+ /* Ignore rows for which Y is NULL */
+ eType = sqlite3_value_type(argv[0]);
+ if( eType==SQLITE_NULL ) return;
+
+ /* If not NULL, then Y must be numeric. Otherwise throw an error.
+ ** Requirement 4 */
+ if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
+ sqlite3_result_error(pCtx, "1st argument to percentile() is not "
+ "numeric", -1);
+ return;
+ }
+
+ /* Throw an error if the Y value is infinity or NaN */
+ y = sqlite3_value_double(argv[0]);
+ if( isInfinity(y) ){
+ sqlite3_result_error(pCtx, "Inf input to percentile()", -1);
+ return;
+ }
+
+ /* Allocate and store the Y */
+ if( p->nUsed>=p->nAlloc ){
+ unsigned n = p->nAlloc*2 + 250;
+ double *a = sqlite3_realloc(p->a, sizeof(double)*n);
+ if( a==0 ){
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ p->nAlloc = n;
+ p->a = a;
+ }
+ p->a[p->nUsed++] = y;
+}
+
+/*
+** Compare to doubles for sorting using qsort()
+*/
+static int doubleCmp(const void *pA, const void *pB){
+ double a = *(double*)pA;
+ double b = *(double*)pB;
+ if( a==b ) return 0;
+ if( a<b ) return -1;
+ return +1;
+}
+
+/*
+** Called to compute the final output of percentile() and to clean
+** up all allocated memory.
+*/
+static void percentFinal(sqlite3_context *pCtx){
+ Percentile *p;
+ unsigned i1, i2;
+ double v1, v2;
+ double ix, vx;
+ p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
+ if( p==0 ) return;
+ if( p->a==0 ) return;
+ if( p->nUsed ){
+ qsort(p->a, p->nUsed, sizeof(double), doubleCmp);
+ ix = (p->rPct-1.0)*(p->nUsed-1)*0.01;
+ i1 = (unsigned)ix;
+ i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
+ v1 = p->a[i1];
+ v2 = p->a[i2];
+ vx = v1 + (v2-v1)*(ix-i1);
+ sqlite3_result_double(pCtx, vx);
+ }
+ sqlite3_free(p->a);
+ memset(p, 0, sizeof(*p));
+}
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_percentile_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "percentile", 2, SQLITE_UTF8, 0,
+ 0, percentStep, percentFinal);
+ return rc;
+}
diff --git a/ext/misc/regexp.c b/ext/misc/regexp.c
index 16fa7d0..7244d52 100644
--- a/ext/misc/regexp.c
+++ b/ext/misc/regexp.c
@@ -713,6 +713,7 @@ static void re_sql_func(
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
+ int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
@@ -728,12 +729,15 @@ static void re_sql_func(
sqlite3_result_error_nomem(context);
return;
}
- sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ setAux = 1;
}
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
if( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
+ if( setAux ){
+ sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
+ }
}
/*
diff --git a/ext/misc/spellfix.c b/ext/misc/spellfix.c
index eb5442e..2e6743e 100644
--- a/ext/misc/spellfix.c
+++ b/ext/misc/spellfix.c
@@ -26,8 +26,8 @@ SQLITE_EXTENSION_INIT1
# define NEVER(X) 0
typedef unsigned char u8;
typedef unsigned short u16;
-# include <ctype.h>
#endif
+#include <ctype.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -1893,7 +1893,7 @@ static int spellfix1Init(
char **pzErr
){
spellfix1_vtab *pNew = 0;
- const char *zModule = argv[0];
+ /* const char *zModule = argv[0]; // not used */
const char *zDbName = argv[1];
const char *zTableName = argv[2];
int nDbName;
@@ -1933,7 +1933,6 @@ static int spellfix1Init(
#define SPELLFIX_COL_COMMAND 11
}
if( rc==SQLITE_OK && isCreate ){
- sqlite3_uint64 r;
spellfix1DbExec(&rc, db,
"CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n"
" id INTEGER PRIMARY KEY,\n"
@@ -1945,11 +1944,10 @@ static int spellfix1Init(
");\n",
zDbName, zTableName
);
- sqlite3_randomness(sizeof(r), &r);
spellfix1DbExec(&rc, db,
- "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_index_%llx\" "
+ "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" "
"ON \"%w_vocab\"(langid,k2);",
- zDbName, zModule, r, zTableName
+ zDbName, zTableName, zTableName
);
}
for(i=3; rc==SQLITE_OK && i<argc; i++){
@@ -2051,6 +2049,7 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
** (D) scope = $scope
** (E) distance < $distance
** (F) distance <= $distance
+** (G) rowid = $rowid
**
** The plan number is a bit mask formed with these bits:
**
@@ -2060,8 +2059,9 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){
** 0x08 (D) is found
** 0x10 (E) is found
** 0x20 (F) is found
+** 0x40 (G) is found
**
-** filter.argv[*] values contains $str, $langid, $top, and $scope,
+** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid
** if specified and in that order.
*/
static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
@@ -2070,6 +2070,7 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int iTopTerm = -1;
int iScopeTerm = -1;
int iDistTerm = -1;
+ int iRowidTerm = -1;
int i;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
@@ -2122,6 +2123,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
iPlan |= pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ? 16 : 32;
iDistTerm = i;
}
+
+ /* Terms of the form: distance < $dist or distance <= $dist */
+ if( (iPlan & 64)==0
+ && pConstraint->iColumn<0
+ && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ
+ ){
+ iPlan |= 64;
+ iRowidTerm = i;
+ }
}
if( iPlan&1 ){
int idx = 2;
@@ -2148,10 +2158,15 @@ static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++;
pIdxInfo->aConstraintUsage[iDistTerm].omit = 1;
}
- pIdxInfo->estimatedCost = (double)10000;
+ pIdxInfo->estimatedCost = 1e5;
+ }else if( (iPlan & 64) ){
+ pIdxInfo->idxNum = 64;
+ pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1;
+ pIdxInfo->estimatedCost = 5;
}else{
pIdxInfo->idxNum = 0;
- pIdxInfo->estimatedCost = (double)10000000;
+ pIdxInfo->estimatedCost = 1e50;
}
return SQLITE_OK;
}
@@ -2465,16 +2480,23 @@ static int spellfix1FilterForFullScan(
int argc,
sqlite3_value **argv
){
- int rc;
+ int rc = SQLITE_OK;
char *zSql;
spellfix1_vtab *pVTab = pCur->pVTab;
spellfix1ResetCursor(pCur);
+ assert( idxNum==0 || idxNum==64 );
zSql = sqlite3_mprintf(
- "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"",
- pVTab->zDbName, pVTab->zTableName);
+ "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s",
+ pVTab->zDbName, pVTab->zTableName,
+ ((idxNum & 64) ? " WHERE rowid=?" : "")
+ );
if( zSql==0 ) return SQLITE_NOMEM;
rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0);
sqlite3_free(zSql);
+ if( rc==SQLITE_OK && (idxNum & 64) ){
+ assert( argc==1 );
+ rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]);
+ }
pCur->nRow = pCur->iRow = 0;
if( rc==SQLITE_OK ){
rc = sqlite3_step(pCur->pFullScan);
@@ -2672,7 +2694,7 @@ static int spellfix1Update(
const char *zCmd =
(const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]);
if( zCmd==0 ){
- pVTab->zErrMsg = sqlite3_mprintf("%s.word may not be NULL",
+ pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word",
p->zTableName);
return SQLITE_CONSTRAINT_NOTNULL;
}
diff --git a/ext/misc/totype.c b/ext/misc/totype.c
new file mode 100644
index 0000000..5dc99f3
--- /dev/null
+++ b/ext/misc/totype.c
@@ -0,0 +1,512 @@
+/*
+** 2013-10-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 SQLite extension implements functions tointeger(X) and toreal(X).
+**
+** If X is an integer, real, or string value that can be
+** losslessly represented as an integer, then tointeger(X)
+** returns the corresponding integer value.
+** If X is an 8-byte BLOB then that blob is interpreted as
+** a signed two-compliment little-endian encoding of an integer
+** and tointeger(X) returns the corresponding integer value.
+** Otherwise tointeger(X) return NULL.
+**
+** If X is an integer, real, or string value that can be
+** convert into a real number, preserving at least 15 digits
+** of precision, then toreal(X) returns the corresponding real value.
+** If X is an 8-byte BLOB then that blob is interpreted as
+** a 64-bit IEEE754 big-endian floating point value
+** and toreal(X) returns the corresponding real value.
+** Otherwise toreal(X) return NULL.
+**
+** Note that tointeger(X) of an 8-byte BLOB assumes a little-endian
+** encoding whereas toreal(X) of an 8-byte BLOB assumes a big-endian
+** encoding.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+/*
+** Determine if this is running on a big-endian or little-endian
+** processor
+*/
+#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
+ || defined(__x86_64) || defined(__x86_64__)
+# define TOTYPE_BIGENDIAN 0
+# define TOTYPE_LITTLEENDIAN 1
+#else
+ const int totype_one = 1;
+# define TOTYPE_BIGENDIAN (*(char *)(&totype_one)==0)
+# define TOTYPE_LITTLEENDIAN (*(char *)(&totype_one)==1)
+#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.
+*/
+#ifndef LARGEST_INT64
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+#endif
+
+#ifndef SMALLEST_INT64
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** Return TRUE if character c is a whitespace character
+*/
+static int totypeIsspace(unsigned char c){
+ return c==' ' || c=='\t' || c=='\n' || c=='\v' || c=='\f' || c=='\r';
+}
+
+/*
+** Return TRUE if character c is a digit
+*/
+static int totypeIsdigit(unsigned char c){
+ return c>='0' && c<='9';
+}
+
+/*
+** Compare the 19-character string zNum against the text representation
+** value 2^63: 9223372036854775808. Return negative, zero, or positive
+** if zNum is less than, equal to, or greater than the string.
+** Note that zNum must contain exactly 19 characters.
+**
+** Unlike memcmp() this routine is guaranteed to return the difference
+** in the values of the last digit if the only difference is in the
+** last digit. So, for example,
+**
+** totypeCompare2pow63("9223372036854775800")
+**
+** will return -8.
+*/
+static int totypeCompare2pow63(const char *zNum){
+ int c = 0;
+ int i;
+ /* 012345678901234567 */
+ const char *pow63 = "922337203685477580";
+ for(i=0; c==0 && i<18; i++){
+ c = (zNum[i]-pow63[i])*10;
+ }
+ if( c==0 ){
+ c = zNum[18] - '8';
+ }
+ return c;
+}
+
+/*
+** Convert zNum to a 64-bit signed integer.
+**
+** If the zNum value is representable as a 64-bit twos-complement
+** integer, then write that value into *pNum and return 0.
+**
+** If zNum is exactly 9223372036854665808, return 2. This special
+** case is broken out because while 9223372036854665808 cannot be a
+** signed 64-bit integer, its negative -9223372036854665808 can be.
+**
+** If zNum is too big for a 64-bit integer and is not
+** 9223372036854665808 or if zNum contains any non-numeric text,
+** then return 1.
+**
+** The string is not necessarily zero-terminated.
+*/
+static int totypeAtoi64(const char *zNum, sqlite3_int64 *pNum, int length){
+ sqlite3_uint64 u = 0;
+ int neg = 0; /* assume positive */
+ int i;
+ int c = 0;
+ int nonNum = 0;
+ const char *zStart;
+ const char *zEnd = zNum + length;
+
+ while( zNum<zEnd && totypeIsspace(*zNum) ) zNum++;
+ if( zNum<zEnd ){
+ if( *zNum=='-' ){
+ neg = 1;
+ zNum++;
+ }else if( *zNum=='+' ){
+ zNum++;
+ }
+ }
+ zStart = zNum;
+ while( zNum<zEnd && zNum[0]=='0' ){ zNum++; } /* Skip leading zeros. */
+ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i++){
+ u = u*10 + c - '0';
+ }
+ if( u>LARGEST_INT64 ){
+ *pNum = SMALLEST_INT64;
+ }else if( neg ){
+ *pNum = -(sqlite3_int64)u;
+ }else{
+ *pNum = (sqlite3_int64)u;
+ }
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19 || nonNum ){
+ /* zNum is empty or contains non-numeric text or is longer
+ ** than 19 digits (thus guaranteeing that it is too large) */
+ return 1;
+ }else if( i<19 ){
+ /* Less than 19 digits, so we know that it fits in 64 bits */
+ assert( u<=LARGEST_INT64 );
+ return 0;
+ }else{
+ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */
+ c = totypeCompare2pow63(zNum);
+ if( c<0 ){
+ /* zNum is less than 9223372036854775808 so it fits */
+ assert( u<=LARGEST_INT64 );
+ return 0;
+ }else if( c>0 ){
+ /* zNum is greater than 9223372036854775808 so it overflows */
+ return 1;
+ }else{
+ /* zNum is exactly 9223372036854775808. Fits if negative. The
+ ** special case 2 overflow if positive */
+ assert( u-1==LARGEST_INT64 );
+ assert( (*pNum)==SMALLEST_INT64 );
+ return neg ? 0 : 2;
+ }
+ }
+}
+
+/*
+** The string z[] is an text representation of a real number.
+** Convert this string to a double and write it into *pResult.
+**
+** The string is not necessarily zero-terminated.
+**
+** Return TRUE if the result is a valid real number (or integer) and FALSE
+** if the string is empty or contains extraneous text. Valid numbers
+** are in one of these formats:
+**
+** [+-]digits[E[+-]digits]
+** [+-]digits.[digits][E[+-]digits]
+** [+-].digits[E[+-]digits]
+**
+** Leading and trailing whitespace is ignored for the purpose of determining
+** validity.
+**
+** If some prefix of the input string is a valid number, this routine
+** returns FALSE but it still converts the prefix and writes the result
+** into *pResult.
+*/
+static int totypeAtoF(const char *z, double *pResult, int length){
+ const char *zEnd = z + length;
+ /* sign * significand * (10 ^ (esign * exponent)) */
+ int sign = 1; /* sign of significand */
+ sqlite3_int64 s = 0; /* significand */
+ int d = 0; /* adjust exponent for shifting decimal point */
+ int esign = 1; /* sign of exponent */
+ int e = 0; /* exponent */
+ int eValid = 1; /* True exponent is either not used or is well-formed */
+ double result;
+ int nDigits = 0;
+ int nonNum = 0;
+
+ *pResult = 0.0; /* Default return value, in case of an error */
+
+ /* skip leading spaces */
+ while( z<zEnd && totypeIsspace(*z) ) z++;
+ if( z>=zEnd ) return 0;
+
+ /* get sign of significand */
+ if( *z=='-' ){
+ sign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+
+ /* skip leading zeroes */
+ while( z<zEnd && z[0]=='0' ) z++, nDigits++;
+
+ /* copy max significant digits to significand */
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ z++, nDigits++;
+ }
+
+ /* skip non-significant significand digits
+ ** (increase exponent by d to shift decimal left) */
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++, d++;
+ if( z>=zEnd ) goto totype_atof_calc;
+
+ /* if decimal point is present */
+ if( *z=='.' ){
+ z++;
+ /* copy digits from after decimal to significand
+ ** (decrease exponent by d to shift decimal right) */
+ while( z<zEnd && totypeIsdigit(*z) && s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ z++, nDigits++, d--;
+ }
+ /* skip non-significant digits */
+ while( z<zEnd && totypeIsdigit(*z) ) z++, nDigits++;
+ }
+ if( z>=zEnd ) goto totype_atof_calc;
+
+ /* if exponent is present */
+ if( *z=='e' || *z=='E' ){
+ z++;
+ eValid = 0;
+ if( z>=zEnd ) goto totype_atof_calc;
+ /* get sign of exponent */
+ if( *z=='-' ){
+ esign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+ /* copy digits to exponent */
+ while( z<zEnd && totypeIsdigit(*z) ){
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
+ z++;
+ eValid = 1;
+ }
+ }
+
+ /* skip trailing spaces */
+ if( nDigits && eValid ){
+ while( z<zEnd && totypeIsspace(*z) ) z++;
+ }
+
+totype_atof_calc:
+ /* adjust exponent by d, and update sign */
+ e = (e*esign) + d;
+ if( e<0 ) {
+ esign = -1;
+ e *= -1;
+ } else {
+ esign = 1;
+ }
+
+ /* if 0 significand */
+ if( !s ) {
+ /* In the IEEE 754 standard, zero is signed.
+ ** Add the sign if we've seen at least one digit */
+ result = (sign<0 && nDigits) ? -(double)0 : (double)0;
+ } else {
+ /* attempt to reduce exponent */
+ if( esign>0 ){
+ while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
+ }else{
+ while( !(s%10) && e>0 ) e--,s/=10;
+ }
+
+ /* adjust the sign of significand */
+ s = sign<0 ? -s : s;
+
+ /* if exponent, scale significand as appropriate
+ ** and store in result. */
+ if( e ){
+ double scale = 1.0;
+ /* attempt to handle extremely small/large numbers better */
+ if( e>307 && e<342 ){
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
+ if( esign<0 ){
+ result = s / scale;
+ result /= 1.0e+308;
+ }else{
+ result = s * scale;
+ result *= 1.0e+308;
+ }
+ }else if( e>=342 ){
+ if( esign<0 ){
+ result = 0.0*s;
+ }else{
+ result = 1e308*1e308*s; /* Infinity */
+ }
+ }else{
+ /* 1.0e+22 is the largest power of 10 than can be
+ ** represented exactly. */
+ while( e%22 ) { scale *= 1.0e+1; e -= 1; }
+ while( e>0 ) { scale *= 1.0e+22; e -= 22; }
+ if( esign<0 ){
+ result = s / scale;
+ }else{
+ result = s * scale;
+ }
+ }
+ } else {
+ result = (double)s;
+ }
+ }
+
+ /* store the result */
+ *pResult = result;
+
+ /* return true if number and no extra non-whitespace chracters after */
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0;
+}
+
+/*
+** tointeger(X): If X is any value (integer, double, blob, or string) that
+** can be losslessly converted into an integer, then make the conversion and
+** return the result. Otherwise, return NULL.
+*/
+static void tointegerFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ (void)argc;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_FLOAT: {
+ double rVal = sqlite3_value_double(argv[0]);
+ sqlite3_int64 iVal = (sqlite3_int64)rVal;
+ if( rVal==(double)iVal ){
+ sqlite3_result_int64(context, iVal);
+ }
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
+ break;
+ }
+ case SQLITE_BLOB: {
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
+ if( zBlob ){
+ int nBlob = sqlite3_value_bytes(argv[0]);
+ if( nBlob==sizeof(sqlite3_int64) ){
+ sqlite3_int64 iVal;
+ if( TOTYPE_BIGENDIAN ){
+ int i;
+ unsigned char zBlobRev[sizeof(sqlite3_int64)];
+ for(i=0; i<sizeof(sqlite3_int64); i++){
+ zBlobRev[i] = zBlob[sizeof(sqlite3_int64)-1-i];
+ }
+ memcpy(&iVal, zBlobRev, sizeof(sqlite3_int64));
+ }else{
+ memcpy(&iVal, zBlob, sizeof(sqlite3_int64));
+ }
+ sqlite3_result_int64(context, iVal);
+ }
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
+ if( zStr ){
+ int nStr = sqlite3_value_bytes(argv[0]);
+ if( nStr && !totypeIsspace(zStr[0]) ){
+ sqlite3_int64 iVal;
+ if( !totypeAtoi64((const char*)zStr, &iVal, nStr) ){
+ sqlite3_result_int64(context, iVal);
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
+ break;
+ }
+ }
+}
+
+/*
+** toreal(X): If X is any value (integer, double, blob, or string) that can
+** be losslessly converted into a real number, then do so and return that
+** real number. Otherwise return NULL.
+*/
+#if defined(_MSC_VER)
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#endif
+static void torealFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( argc==1 );
+ (void)argc;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_FLOAT: {
+ sqlite3_result_double(context, sqlite3_value_double(argv[0]));
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_int64 iVal = sqlite3_value_int64(argv[0]);
+ double rVal = (double)iVal;
+ if( iVal==(sqlite3_int64)rVal ){
+ sqlite3_result_double(context, rVal);
+ }
+ break;
+ }
+ case SQLITE_BLOB: {
+ const unsigned char *zBlob = sqlite3_value_blob(argv[0]);
+ if( zBlob ){
+ int nBlob = sqlite3_value_bytes(argv[0]);
+ if( nBlob==sizeof(double) ){
+ double rVal;
+ if( TOTYPE_LITTLEENDIAN ){
+ int i;
+ unsigned char zBlobRev[sizeof(double)];
+ for(i=0; i<sizeof(double); i++){
+ zBlobRev[i] = zBlob[sizeof(double)-1-i];
+ }
+ memcpy(&rVal, zBlobRev, sizeof(double));
+ }else{
+ memcpy(&rVal, zBlob, sizeof(double));
+ }
+ sqlite3_result_double(context, rVal);
+ }
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const unsigned char *zStr = sqlite3_value_text(argv[0]);
+ if( zStr ){
+ int nStr = sqlite3_value_bytes(argv[0]);
+ if( nStr && !totypeIsspace(zStr[0]) && !totypeIsspace(zStr[nStr-1]) ){
+ double rVal;
+ if( totypeAtoF((const char*)zStr, &rVal, nStr) ){
+ sqlite3_result_double(context, rVal);
+ return;
+ }
+ }
+ }
+ break;
+ }
+ default: {
+ assert( sqlite3_value_type(argv[0])==SQLITE_NULL );
+ break;
+ }
+ }
+}
+#if defined(_MSC_VER)
+#pragma optimize("", on)
+#pragma warning(default: 4748)
+#endif
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_totype_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ rc = sqlite3_create_function(db, "tointeger", 1, SQLITE_UTF8, 0,
+ tointegerFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "toreal", 1, SQLITE_UTF8, 0,
+ torealFunc, 0, 0);
+ }
+ return rc;
+}
diff --git a/ext/misc/vfslog.c b/ext/misc/vfslog.c
new file mode 100644
index 0000000..b55b06f
--- /dev/null
+++ b/ext/misc/vfslog.c
@@ -0,0 +1,759 @@
+/*
+** 2013-10-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 contains the implementation of an SQLite vfs wrapper for
+** unix that generates per-database log files of all disk activity.
+*/
+
+/*
+** This module contains code for a wrapper VFS that causes a log of
+** most VFS calls to be written into a file on disk.
+**
+** Each database connection creates a separate log file in the same
+** directory as the original database and named after the original
+** database. A unique suffix is added to avoid name collisions.
+** Separate log files are used so that concurrent processes do not
+** try to write log operations to the same file at the same instant,
+** resulting in overwritten or comingled log text.
+**
+** Each individual log file records operations by a single database
+** connection on both the original database and its associated rollback
+** journal.
+**
+** The log files are in the comma-separated-value (CSV) format. The
+** log files can be imported into an SQLite database using the ".import"
+** command of the SQLite command-line shell for analysis.
+**
+** One technique for using this module is to append the text of this
+** module to the end of a standard "sqlite3.c" amalgamation file then
+** add the following compile-time options:
+**
+** -DSQLITE_EXTRA_INIT=sqlite3_register_vfslog
+** -DSQLITE_USE_FCNTL_TRACE
+**
+** The first compile-time option causes the sqlite3_register_vfslog()
+** function, defined below, to be invoked when SQLite is initialized.
+** That causes this custom VFS to become the default VFS for all
+** subsequent connections. The SQLITE_USE_FCNTL_TRACE option causes
+** the SQLite core to issue extra sqlite3_file_control() operations
+** with SQLITE_FCNTL_TRACE to give some indication of what is going
+** on in the core.
+*/
+
+#include "sqlite3.h"
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#if SQLITE_OS_UNIX
+# include <unistd.h>
+#endif
+
+/*
+** Forward declaration of objects used by this utility
+*/
+typedef struct VLogLog VLogLog;
+typedef struct VLogVfs VLogVfs;
+typedef struct VLogFile VLogFile;
+
+/* There is a pair (an array of size 2) of the following objects for
+** each database file being logged. The first contains the filename
+** and is used to log I/O with the main database. The second has
+** a NULL filename and is used to log I/O for the journal. Both
+** out pointers are the same.
+*/
+struct VLogLog {
+ VLogLog *pNext; /* Next in a list of all active logs */
+ VLogLog **ppPrev; /* Pointer to this in the list */
+ int nRef; /* Number of references to this object */
+ int nFilename; /* Length of zFilename in bytes */
+ char *zFilename; /* Name of database file. NULL for journal */
+ FILE *out; /* Write information here */
+};
+
+struct VLogVfs {
+ sqlite3_vfs base; /* VFS methods */
+ sqlite3_vfs *pVfs; /* Parent VFS */
+};
+
+struct VLogFile {
+ sqlite3_file base; /* IO methods */
+ sqlite3_file *pReal; /* Underlying file handle */
+ VLogLog *pLog; /* The log file for this file */
+};
+
+#define REALVFS(p) (((VLogVfs*)(p))->pVfs)
+
+/*
+** Methods for VLogFile
+*/
+static int vlogClose(sqlite3_file*);
+static int vlogRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int vlogWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int vlogTruncate(sqlite3_file*, sqlite3_int64 size);
+static int vlogSync(sqlite3_file*, int flags);
+static int vlogFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int vlogLock(sqlite3_file*, int);
+static int vlogUnlock(sqlite3_file*, int);
+static int vlogCheckReservedLock(sqlite3_file*, int *pResOut);
+static int vlogFileControl(sqlite3_file*, int op, void *pArg);
+static int vlogSectorSize(sqlite3_file*);
+static int vlogDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Methods for VLogVfs
+*/
+static int vlogOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int vlogDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int vlogAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int vlogFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+static void *vlogDlOpen(sqlite3_vfs*, const char *zFilename);
+static void vlogDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
+static void vlogDlClose(sqlite3_vfs*, void*);
+static int vlogRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int vlogSleep(sqlite3_vfs*, int microseconds);
+static int vlogCurrentTime(sqlite3_vfs*, double*);
+static int vlogGetLastError(sqlite3_vfs*, int, char *);
+static int vlogCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
+
+static VLogVfs vlog_vfs = {
+ {
+ 1, /* iVersion */
+ 0, /* szOsFile (set by register_vlog()) */
+ 1024, /* mxPathname */
+ 0, /* pNext */
+ "vfslog", /* zName */
+ 0, /* pAppData */
+ vlogOpen, /* xOpen */
+ vlogDelete, /* xDelete */
+ vlogAccess, /* xAccess */
+ vlogFullPathname, /* xFullPathname */
+ vlogDlOpen, /* xDlOpen */
+ vlogDlError, /* xDlError */
+ vlogDlSym, /* xDlSym */
+ vlogDlClose, /* xDlClose */
+ vlogRandomness, /* xRandomness */
+ vlogSleep, /* xSleep */
+ vlogCurrentTime, /* xCurrentTime */
+ vlogGetLastError, /* xGetLastError */
+ vlogCurrentTimeInt64 /* xCurrentTimeInt64 */
+ },
+ 0
+};
+
+static sqlite3_io_methods vlog_io_methods = {
+ 1, /* iVersion */
+ vlogClose, /* xClose */
+ vlogRead, /* xRead */
+ vlogWrite, /* xWrite */
+ vlogTruncate, /* xTruncate */
+ vlogSync, /* xSync */
+ vlogFileSize, /* xFileSize */
+ vlogLock, /* xLock */
+ vlogUnlock, /* xUnlock */
+ vlogCheckReservedLock, /* xCheckReservedLock */
+ vlogFileControl, /* xFileControl */
+ vlogSectorSize, /* xSectorSize */
+ vlogDeviceCharacteristics, /* xDeviceCharacteristics */
+ 0, /* xShmMap */
+ 0, /* xShmLock */
+ 0, /* xShmBarrier */
+ 0 /* xShmUnmap */
+};
+
+#if SQLITE_OS_UNIX && !defined(NO_GETTOD)
+#include <sys/time.h>
+static sqlite3_uint64 vlog_time(){
+ struct timeval sTime;
+ gettimeofday(&sTime, 0);
+ return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
+}
+#elif SQLITE_OS_WIN
+#include <windows.h>
+#include <time.h>
+static sqlite3_uint64 vlog_time(){
+ FILETIME ft;
+ sqlite3_uint64 u64time = 0;
+
+ GetSystemTimeAsFileTime(&ft);
+
+ u64time |= ft.dwHighDateTime;
+ u64time <<= 32;
+ u64time |= ft.dwLowDateTime;
+
+ /* ft is 100-nanosecond intervals, we want microseconds */
+ return u64time /(sqlite3_uint64)10;
+}
+#else
+static sqlite3_uint64 vlog_time(){
+ return 0;
+}
+#endif
+
+
+/*
+** Write a message to the log file
+*/
+static void vlogLogPrint(
+ VLogLog *pLog, /* The log file to write into */
+ sqlite3_int64 tStart, /* Start time of system call */
+ sqlite3_int64 tElapse, /* Elapse time of system call */
+ const char *zOp, /* Type of system call */
+ sqlite3_int64 iArg1, /* First argument */
+ sqlite3_int64 iArg2, /* Second argument */
+ const char *zArg3, /* Third argument */
+ int iRes /* Result */
+){
+ char z1[40], z2[40], z3[2000];
+ if( pLog==0 ) return;
+ if( iArg1>=0 ){
+ sqlite3_snprintf(sizeof(z1), z1, "%lld", iArg1);
+ }else{
+ z1[0] = 0;
+ }
+ if( iArg2>=0 ){
+ sqlite3_snprintf(sizeof(z2), z2, "%lld", iArg2);
+ }else{
+ z2[0] = 0;
+ }
+ if( zArg3 ){
+ sqlite3_snprintf(sizeof(z3), z3, "\"%.*w\"", sizeof(z3)-4, zArg3);
+ }else{
+ z3[0] = 0;
+ }
+ fprintf(pLog->out,"%lld,%lld,%s,%d,%s,%s,%s,%d\n",
+ tStart, tElapse, zOp, pLog->zFilename==0, z1, z2, z3, iRes);
+}
+
+/*
+** List of all active log connections. Protected by the master mutex.
+*/
+static VLogLog *allLogs = 0;
+
+/*
+** Close a VLogLog object
+*/
+static void vlogLogClose(VLogLog *p){
+ if( p ){
+ sqlite3_mutex *pMutex;
+ p->nRef--;
+ if( p->nRef>0 || p->zFilename==0 ) return;
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMutex);
+ *p->ppPrev = p->pNext;
+ if( p->pNext ) p->pNext->ppPrev = p->ppPrev;
+ sqlite3_mutex_leave(pMutex);
+ fclose(p->out);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Open a VLogLog object on the given file
+*/
+static VLogLog *vlogLogOpen(const char *zFilename){
+ int nName = (int)strlen(zFilename);
+ int isJournal = 0;
+ sqlite3_mutex *pMutex;
+ VLogLog *pLog, *pTemp;
+ sqlite3_int64 tNow = 0;
+ if( nName>4 && strcmp(zFilename+nName-4,"-wal")==0 ){
+ return 0; /* Do not log wal files */
+ }else
+ if( nName>8 && strcmp(zFilename+nName-8,"-journal")==0 ){
+ nName -= 8;
+ isJournal = 1;
+ }else if( nName>12
+ && sqlite3_strglob("-mj??????9??", zFilename+nName-12)==0 ){
+ return 0; /* Do not log master journal files */
+ }
+ pTemp = sqlite3_malloc( sizeof(*pLog)*2 + nName + 60 );
+ if( pTemp==0 ) return 0;
+ pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(pMutex);
+ for(pLog=allLogs; pLog; pLog=pLog->pNext){
+ if( pLog->nFilename==nName && !memcmp(pLog->zFilename, zFilename, nName) ){
+ break;
+ }
+ }
+ if( pLog==0 ){
+ pLog = pTemp;
+ pTemp = 0;
+ memset(pLog, 0, sizeof(*pLog)*2);
+ pLog->zFilename = (char*)&pLog[2];
+ tNow = vlog_time();
+ sqlite3_snprintf(nName+60, pLog->zFilename, "%.*s-debuglog-%lld",
+ nName, zFilename, tNow);
+ pLog->out = fopen(pLog->zFilename, "a");
+ if( pLog->out==0 ){
+ sqlite3_mutex_leave(pMutex);
+ sqlite3_free(pLog);
+ return 0;
+ }
+ pLog->nFilename = nName;
+ pLog[1].out = pLog[0].out;
+ pLog->ppPrev = &allLogs;
+ if( allLogs ) allLogs->ppPrev = &pLog->pNext;
+ pLog->pNext = allLogs;
+ allLogs = pLog;
+ }
+ sqlite3_mutex_leave(pMutex);
+ if( pTemp ){
+ sqlite3_free(pTemp);
+ }else{
+#if SQLITE_OS_UNIX
+ char zHost[200];
+ zHost[0] = 0;
+ gethostname(zHost, sizeof(zHost)-1);
+ zHost[sizeof(zHost)-1] = 0;
+ vlogLogPrint(pLog, tNow, 0, "IDENT", getpid(), -1, zHost, 0);
+#endif
+ }
+ if( pLog && isJournal ) pLog++;
+ pLog->nRef++;
+ return pLog;
+}
+
+
+/*
+** Close an vlog-file.
+*/
+static int vlogClose(sqlite3_file *pFile){
+ sqlite3_uint64 tStart, tElapse;
+ int rc = SQLITE_OK;
+ VLogFile *p = (VLogFile *)pFile;
+
+ tStart = vlog_time();
+ if( p->pReal->pMethods ){
+ rc = p->pReal->pMethods->xClose(p->pReal);
+ }
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "CLOSE", -1, -1, 0, rc);
+ vlogLogClose(p->pLog);
+ return rc;
+}
+
+/*
+** Compute signature for a block of content.
+**
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
+** the entire block.
+**
+** For blocks of more than 16 bytes, the signature is a hex dump of the
+** first 8 bytes followed by a 64-bit has of the entire block.
+*/
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
+ unsigned int s0 = 0, s1 = 0;
+ unsigned int *pI;
+ int i;
+ if( n<=16 ){
+ for(i=0; i<n; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
+ }else{
+ pI = (unsigned int*)p;
+ for(i=0; i<n-7; i+=8){
+ s0 += pI[0] + s1;
+ s1 += pI[1] + s0;
+ pI += 2;
+ }
+ for(i=0; i<8; i++) sqlite3_snprintf(3, zCksum+i*2, "%02x", p[i]);
+ sqlite3_snprintf(18, zCksum+i*2, "-%08x%08x", s0, s1);
+ }
+}
+
+/*
+** Convert a big-endian 32-bit integer into a native integer
+*/
+static int bigToNative(const unsigned char *x){
+ return (x[0]<<24) + (x[1]<<16) + (x[2]<<8) + x[3];
+}
+
+/*
+** Read data from an vlog-file.
+*/
+static int vlogRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ char zSig[40];
+
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+ tElapse = vlog_time() - tStart;
+ if( rc==SQLITE_OK ){
+ vlogSignature(zBuf, iAmt, zSig);
+ }else{
+ zSig[0] = 0;
+ }
+ vlogLogPrint(p->pLog, tStart, tElapse, "READ", iAmt, iOfst, zSig, rc);
+ if( rc==SQLITE_OK
+ && p->pLog
+ && p->pLog->zFilename
+ && iOfst<=24
+ && iOfst+iAmt>=28
+ ){
+ unsigned char *x = ((unsigned char*)zBuf)+(24-iOfst);
+ unsigned iCtr, nFree = -1;
+ char *zFree = 0;
+ char zStr[12];
+ iCtr = bigToNative(x);
+ if( iOfst+iAmt>=40 ){
+ zFree = zStr;
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
+ nFree = bigToNative(x+12);
+ }
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-READ", iCtr, nFree, zFree, 0);
+ }
+ return rc;
+}
+
+/*
+** Write data to an vlog-file.
+*/
+static int vlogWrite(
+ sqlite3_file *pFile,
+ const void *z,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ char zSig[40];
+
+ tStart = vlog_time();
+ vlogSignature((unsigned char*)z, iAmt, zSig);
+ rc = p->pReal->pMethods->xWrite(p->pReal, z, iAmt, iOfst);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "WRITE", iAmt, iOfst, zSig, rc);
+ if( rc==SQLITE_OK
+ && p->pLog
+ && p->pLog->zFilename
+ && iOfst<=24
+ && iOfst+iAmt>=28
+ ){
+ unsigned char *x = ((unsigned char*)z)+(24-iOfst);
+ unsigned iCtr, nFree = -1;
+ char *zFree = 0;
+ char zStr[12];
+ iCtr = bigToNative(x);
+ if( iOfst+iAmt>=40 ){
+ zFree = zStr;
+ sqlite3_snprintf(sizeof(zStr), zStr, "%d", bigToNative(x+8));
+ nFree = bigToNative(x+12);
+ }
+ vlogLogPrint(p->pLog, tStart, 0, "CHNGCTR-WRITE", iCtr, nFree, zFree, 0);
+ }
+ return rc;
+}
+
+/*
+** Truncate an vlog-file.
+*/
+static int vlogTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xTruncate(p->pReal, size);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRUNCATE", size, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Sync an vlog-file.
+*/
+static int vlogSync(sqlite3_file *pFile, int flags){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xSync(p->pReal, flags);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "SYNC", flags, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Return the current file-size of an vlog-file.
+*/
+static int vlogFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILESIZE", *pSize, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Lock an vlog-file.
+*/
+static int vlogLock(sqlite3_file *pFile, int eLock){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "LOCK", eLock, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Unlock an vlog-file.
+*/
+static int vlogUnlock(sqlite3_file *pFile, int eLock){
+ int rc;
+ sqlite3_uint64 tStart;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ vlogLogPrint(p->pLog, tStart, 0, "UNLOCK", eLock, -1, 0, 0);
+ rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
+ return rc;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an vlog-file.
+*/
+static int vlogCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "CHECKRESERVEDLOCK",
+ *pResOut, -1, "", rc);
+ return rc;
+}
+
+/*
+** File control method. For custom operations on an vlog-file.
+*/
+static int vlogFileControl(sqlite3_file *pFile, int op, void *pArg){
+ VLogFile *p = (VLogFile *)pFile;
+ sqlite3_uint64 tStart, tElapse;
+ int rc;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
+ if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
+ *(char**)pArg = sqlite3_mprintf("vlog/%z", *(char**)pArg);
+ }
+ tElapse = vlog_time() - tStart;
+ if( op==SQLITE_FCNTL_TRACE ){
+ vlogLogPrint(p->pLog, tStart, tElapse, "TRACE", op, -1, pArg, rc);
+ }else if( op==SQLITE_FCNTL_PRAGMA ){
+ const char **azArg = (const char **)pArg;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, azArg[1], rc);
+ }else if( op==SQLITE_FCNTL_SIZE_HINT ){
+ sqlite3_int64 sz = *(sqlite3_int64*)pArg;
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, sz, 0, rc);
+ }else{
+ vlogLogPrint(p->pLog, tStart, tElapse, "FILECONTROL", op, -1, 0, rc);
+ }
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an vlog-file.
+*/
+static int vlogSectorSize(sqlite3_file *pFile){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xSectorSize(p->pReal);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "SECTORSIZE", -1, -1, 0, rc);
+ return rc;
+}
+
+/*
+** Return the device characteristic flags supported by an vlog-file.
+*/
+static int vlogDeviceCharacteristics(sqlite3_file *pFile){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogFile *p = (VLogFile *)pFile;
+ tStart = vlog_time();
+ rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+ tElapse = vlog_time() - tStart;
+ vlogLogPrint(p->pLog, tStart, tElapse, "DEVCHAR", -1, -1, 0, rc);
+ return rc;
+}
+
+
+/*
+** Open an vlog file handle.
+*/
+static int vlogOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ sqlite3_int64 iArg2;
+ VLogFile *p = (VLogFile*)pFile;
+
+ p->pReal = (sqlite3_file*)&p[1];
+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
+ p->pLog = vlogLogOpen(zName);
+ }else{
+ p->pLog = 0;
+ }
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xOpen(REALVFS(pVfs), zName, p->pReal, flags, pOutFlags);
+ tElapse = vlog_time() - tStart;
+ iArg2 = pOutFlags ? *pOutFlags : -1;
+ vlogLogPrint(p->pLog, tStart, tElapse, "OPEN", flags, iArg2, 0, rc);
+ if( rc==SQLITE_OK ){
+ pFile->pMethods = &vlog_io_methods;
+ }else{
+ if( p->pLog ) vlogLogClose(p->pLog);
+ p->pLog = 0;
+ }
+ 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 vlogDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogLog *pLog;
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xDelete(REALVFS(pVfs), zPath, dirSync);
+ tElapse = vlog_time() - tStart;
+ pLog = vlogLogOpen(zPath);
+ vlogLogPrint(pLog, tStart, tElapse, "DELETE", dirSync, -1, 0, rc);
+ vlogLogClose(pLog);
+ return rc;
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int vlogAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ int rc;
+ sqlite3_uint64 tStart, tElapse;
+ VLogLog *pLog;
+ tStart = vlog_time();
+ rc = REALVFS(pVfs)->xAccess(REALVFS(pVfs), zPath, flags, pResOut);
+ tElapse = vlog_time() - tStart;
+ pLog = vlogLogOpen(zPath);
+ vlogLogPrint(pLog, tStart, tElapse, "ACCESS", flags, *pResOut, 0, rc);
+ vlogLogClose(pLog);
+ return rc;
+}
+
+/*
+** 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 vlogFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ return REALVFS(pVfs)->xFullPathname(REALVFS(pVfs), zPath, nOut, zOut);
+}
+
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *vlogDlOpen(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 vlogDlError(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 (*vlogDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+ return REALVFS(pVfs)->xDlSym(REALVFS(pVfs), p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void vlogDlClose(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 vlogRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ return REALVFS(pVfs)->xRandomness(REALVFS(pVfs), nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int vlogSleep(sqlite3_vfs *pVfs, int nMicro){
+ return REALVFS(pVfs)->xSleep(REALVFS(pVfs), nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int vlogCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ return REALVFS(pVfs)->xCurrentTime(REALVFS(pVfs), pTimeOut);
+}
+
+static int vlogGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return REALVFS(pVfs)->xGetLastError(REALVFS(pVfs), a, b);
+}
+static int vlogCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
+ return REALVFS(pVfs)->xCurrentTimeInt64(REALVFS(pVfs), p);
+}
+
+/*
+** Register debugvfs as the default VFS for this process.
+*/
+int sqlite3_register_vfslog(const char *zArg){
+ vlog_vfs.pVfs = sqlite3_vfs_find(0);
+ vlog_vfs.base.szOsFile = sizeof(VLogFile) + vlog_vfs.pVfs->szOsFile;
+ return sqlite3_vfs_register(&vlog_vfs.base, 1);
+}
diff --git a/ext/misc/vtshim.c b/ext/misc/vtshim.c
new file mode 100644
index 0000000..01348e8
--- /dev/null
+++ b/ext/misc/vtshim.c
@@ -0,0 +1,551 @@
+/*
+** 2013-06-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.
+**
+*************************************************************************
+**
+** A shim that sits between the SQLite virtual table interface and
+** runtimes with garbage collector based memory management.
+*/
+#include "sqlite3ext.h"
+SQLITE_EXTENSION_INIT1
+#include <assert.h>
+#include <string.h>
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+/* Forward references */
+typedef struct vtshim_aux vtshim_aux;
+typedef struct vtshim_vtab vtshim_vtab;
+typedef struct vtshim_cursor vtshim_cursor;
+
+
+/* The vtshim_aux argument is the auxiliary parameter that is passed
+** into sqlite3_create_module_v2().
+*/
+struct vtshim_aux {
+ void *pChildAux; /* pAux for child virtual tables */
+ void (*xChildDestroy)(void*); /* Destructor for pChildAux */
+ sqlite3_module *pMod; /* Methods for child virtual tables */
+ sqlite3 *db; /* The database to which we are attached */
+ char *zName; /* Name of the module */
+ int bDisposed; /* True if disposed */
+ vtshim_vtab *pAllVtab; /* List of all vtshim_vtab objects */
+ sqlite3_module sSelf; /* Methods used by this shim */
+};
+
+/* A vtshim virtual table object */
+struct vtshim_vtab {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3_vtab *pChild; /* Child virtual table */
+ vtshim_aux *pAux; /* Pointer to vtshim_aux object */
+ vtshim_cursor *pAllCur; /* List of all cursors */
+ vtshim_vtab **ppPrev; /* Previous on list */
+ vtshim_vtab *pNext; /* Next on list */
+};
+
+/* A vtshim cursor object */
+struct vtshim_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ sqlite3_vtab_cursor *pChild; /* Cursor generated by the managed subclass */
+ vtshim_cursor **ppPrev; /* Previous on list of all cursors */
+ vtshim_cursor *pNext; /* Next on list of all cursors */
+};
+
+/* Macro used to copy the child vtable error message to outer vtable */
+#define VTSHIM_COPY_ERRMSG() \
+ do { \
+ sqlite3_free(pVtab->base.zErrMsg); \
+ pVtab->base.zErrMsg = sqlite3_mprintf("%s", pVtab->pChild->zErrMsg); \
+ } while (0)
+
+/* Methods for the vtshim module */
+static int vtshimCreate(
+ sqlite3 *db,
+ void *ppAux,
+ int argc,
+ const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
+ vtshim_vtab *pNew;
+ int rc;
+
+ assert( db==pAux->db );
+ if( pAux->bDisposed ){
+ if( pzErr ){
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
+ pAux->zName);
+ }
+ return SQLITE_ERROR;
+ }
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ rc = pAux->pMod->xCreate(db, pAux->pChildAux, argc, argv,
+ &pNew->pChild, pzErr);
+ if( rc ){
+ sqlite3_free(pNew);
+ *ppVtab = 0;
+ }
+ pNew->pAux = pAux;
+ pNew->ppPrev = &pAux->pAllVtab;
+ pNew->pNext = pAux->pAllVtab;
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
+ pAux->pAllVtab = pNew;
+ return rc;
+}
+
+static int vtshimConnect(
+ sqlite3 *db,
+ void *ppAux,
+ int argc,
+ const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ vtshim_aux *pAux = (vtshim_aux*)ppAux;
+ vtshim_vtab *pNew;
+ int rc;
+
+ assert( db==pAux->db );
+ if( pAux->bDisposed ){
+ if( pzErr ){
+ *pzErr = sqlite3_mprintf("virtual table was disposed: \"%s\"",
+ pAux->zName);
+ }
+ return SQLITE_ERROR;
+ }
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ rc = pAux->pMod->xConnect(db, pAux->pChildAux, argc, argv,
+ &pNew->pChild, pzErr);
+ if( rc ){
+ sqlite3_free(pNew);
+ *ppVtab = 0;
+ }
+ pNew->pAux = pAux;
+ pNew->ppPrev = &pAux->pAllVtab;
+ pNew->pNext = pAux->pAllVtab;
+ if( pAux->pAllVtab ) pAux->pAllVtab->ppPrev = &pNew->pNext;
+ pAux->pAllVtab = pNew;
+ return rc;
+}
+
+static int vtshimBestIndex(
+ sqlite3_vtab *pBase,
+ sqlite3_index_info *pIdxInfo
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xBestIndex(pVtab->pChild, pIdxInfo);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimDisconnect(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xDisconnect(pVtab->pChild);
+ }
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
+ *pVtab->ppPrev = pVtab->pNext;
+ sqlite3_free(pVtab);
+ return rc;
+}
+
+static int vtshimDestroy(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xDestroy(pVtab->pChild);
+ }
+ if( pVtab->pNext ) pVtab->pNext->ppPrev = pVtab->ppPrev;
+ *pVtab->ppPrev = pVtab->pNext;
+ sqlite3_free(pVtab);
+ return rc;
+}
+
+static int vtshimOpen(sqlite3_vtab *pBase, sqlite3_vtab_cursor **ppCursor){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ vtshim_cursor *pCur;
+ int rc;
+ *ppCursor = 0;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ rc = pAux->pMod->xOpen(pVtab->pChild, &pCur->pChild);
+ if( rc ){
+ sqlite3_free(pCur);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+ }
+ pCur->pChild->pVtab = pVtab->pChild;
+ *ppCursor = &pCur->base;
+ pCur->ppPrev = &pVtab->pAllCur;
+ if( pVtab->pAllCur ) pVtab->pAllCur->ppPrev = &pCur->pNext;
+ pCur->pNext = pVtab->pAllCur;
+ pVtab->pAllCur = pCur;
+ return SQLITE_OK;
+}
+
+static int vtshimClose(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc = SQLITE_OK;
+ if( !pAux->bDisposed ){
+ rc = pAux->pMod->xClose(pCur->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ }
+ if( pCur->pNext ) pCur->pNext->ppPrev = pCur->ppPrev;
+ *pCur->ppPrev = pCur->pNext;
+ sqlite3_free(pCur);
+ return rc;
+}
+
+static int vtshimFilter(
+ sqlite3_vtab_cursor *pX,
+ int idxNum,
+ const char *idxStr,
+ int argc,
+ sqlite3_value **argv
+){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xFilter(pCur->pChild, idxNum, idxStr, argc, argv);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimNext(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xNext(pCur->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimEof(sqlite3_vtab_cursor *pX){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return 1;
+ rc = pAux->pMod->xEof(pCur->pChild);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+}
+
+static int vtshimColumn(sqlite3_vtab_cursor *pX, sqlite3_context *ctx, int i){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xColumn(pCur->pChild, ctx, i);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRowid(sqlite3_vtab_cursor *pX, sqlite3_int64 *pRowid){
+ vtshim_cursor *pCur = (vtshim_cursor*)pX;
+ vtshim_vtab *pVtab = (vtshim_vtab*)pCur->base.pVtab;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRowid(pCur->pChild, pRowid);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimUpdate(
+ sqlite3_vtab *pBase,
+ int argc,
+ sqlite3_value **argv,
+ sqlite3_int64 *pRowid
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xUpdate(pVtab->pChild, argc, argv, pRowid);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimBegin(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xBegin(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimSync(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xSync(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimCommit(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xCommit(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRollback(sqlite3_vtab *pBase){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRollback(pVtab->pChild);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimFindFunction(
+ sqlite3_vtab *pBase,
+ int nArg,
+ const char *zName,
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),
+ void **ppArg
+){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return 0;
+ rc = pAux->pMod->xFindFunction(pVtab->pChild, nArg, zName, pxFunc, ppArg);
+ VTSHIM_COPY_ERRMSG();
+ return rc;
+}
+
+static int vtshimRename(sqlite3_vtab *pBase, const char *zNewName){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRename(pVtab->pChild, zNewName);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimSavepoint(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xSavepoint(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRelease(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRelease(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+static int vtshimRollbackTo(sqlite3_vtab *pBase, int n){
+ vtshim_vtab *pVtab = (vtshim_vtab*)pBase;
+ vtshim_aux *pAux = pVtab->pAux;
+ int rc;
+ if( pAux->bDisposed ) return SQLITE_ERROR;
+ rc = pAux->pMod->xRollbackTo(pVtab->pChild, n);
+ if( rc!=SQLITE_OK ){
+ VTSHIM_COPY_ERRMSG();
+ }
+ return rc;
+}
+
+/* The destructor function for a disposible module */
+static void vtshimAuxDestructor(void *pXAux){
+ vtshim_aux *pAux = (vtshim_aux*)pXAux;
+ assert( pAux->pAllVtab==0 );
+ if( !pAux->bDisposed && pAux->xChildDestroy ){
+ pAux->xChildDestroy(pAux->pChildAux);
+ pAux->xChildDestroy = 0;
+ }
+ sqlite3_free(pAux->zName);
+ sqlite3_free(pAux->pMod);
+ sqlite3_free(pAux);
+}
+
+static int vtshimCopyModule(
+ const sqlite3_module *pMod, /* Source module to be copied */
+ sqlite3_module **ppMod /* Destination for copied module */
+){
+ sqlite3_module *p;
+ if( !pMod || !ppMod ) return SQLITE_ERROR;
+ p = sqlite3_malloc( sizeof(*p) );
+ if( p==0 ) return SQLITE_NOMEM;
+ memcpy(p, pMod, sizeof(*p));
+ *ppMod = p;
+ return SQLITE_OK;
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void *sqlite3_create_disposable_module(
+ sqlite3 *db, /* SQLite connection to register module with */
+ const char *zName, /* Name of the module */
+ const sqlite3_module *p, /* Methods for the module */
+ void *pClientData, /* Client data for xCreate/xConnect */
+ void(*xDestroy)(void*) /* Module destructor function */
+){
+ vtshim_aux *pAux;
+ sqlite3_module *pMod;
+ int rc;
+ pAux = sqlite3_malloc( sizeof(*pAux) );
+ if( pAux==0 ){
+ if( xDestroy ) xDestroy(pClientData);
+ return 0;
+ }
+ rc = vtshimCopyModule(p, &pMod);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pAux);
+ return 0;
+ }
+ pAux->pChildAux = pClientData;
+ pAux->xChildDestroy = xDestroy;
+ pAux->pMod = pMod;
+ pAux->db = db;
+ pAux->zName = sqlite3_mprintf("%s", zName);
+ pAux->bDisposed = 0;
+ pAux->pAllVtab = 0;
+ pAux->sSelf.iVersion = p->iVersion<=2 ? p->iVersion : 2;
+ pAux->sSelf.xCreate = p->xCreate ? vtshimCreate : 0;
+ pAux->sSelf.xConnect = p->xConnect ? vtshimConnect : 0;
+ pAux->sSelf.xBestIndex = p->xBestIndex ? vtshimBestIndex : 0;
+ pAux->sSelf.xDisconnect = p->xDisconnect ? vtshimDisconnect : 0;
+ pAux->sSelf.xDestroy = p->xDestroy ? vtshimDestroy : 0;
+ pAux->sSelf.xOpen = p->xOpen ? vtshimOpen : 0;
+ pAux->sSelf.xClose = p->xClose ? vtshimClose : 0;
+ pAux->sSelf.xFilter = p->xFilter ? vtshimFilter : 0;
+ pAux->sSelf.xNext = p->xNext ? vtshimNext : 0;
+ pAux->sSelf.xEof = p->xEof ? vtshimEof : 0;
+ pAux->sSelf.xColumn = p->xColumn ? vtshimColumn : 0;
+ pAux->sSelf.xRowid = p->xRowid ? vtshimRowid : 0;
+ pAux->sSelf.xUpdate = p->xUpdate ? vtshimUpdate : 0;
+ pAux->sSelf.xBegin = p->xBegin ? vtshimBegin : 0;
+ pAux->sSelf.xSync = p->xSync ? vtshimSync : 0;
+ pAux->sSelf.xCommit = p->xCommit ? vtshimCommit : 0;
+ pAux->sSelf.xRollback = p->xRollback ? vtshimRollback : 0;
+ pAux->sSelf.xFindFunction = p->xFindFunction ? vtshimFindFunction : 0;
+ pAux->sSelf.xRename = p->xRename ? vtshimRename : 0;
+ if( p->iVersion>=2 ){
+ pAux->sSelf.xSavepoint = p->xSavepoint ? vtshimSavepoint : 0;
+ pAux->sSelf.xRelease = p->xRelease ? vtshimRelease : 0;
+ pAux->sSelf.xRollbackTo = p->xRollbackTo ? vtshimRollbackTo : 0;
+ }else{
+ pAux->sSelf.xSavepoint = 0;
+ pAux->sSelf.xRelease = 0;
+ pAux->sSelf.xRollbackTo = 0;
+ }
+ rc = sqlite3_create_module_v2(db, zName, &pAux->sSelf,
+ pAux, vtshimAuxDestructor);
+ return rc==SQLITE_OK ? (void*)pAux : 0;
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+void sqlite3_dispose_module(void *pX){
+ vtshim_aux *pAux = (vtshim_aux*)pX;
+ if( !pAux->bDisposed ){
+ vtshim_vtab *pVtab;
+ vtshim_cursor *pCur;
+ for(pVtab=pAux->pAllVtab; pVtab; pVtab=pVtab->pNext){
+ for(pCur=pVtab->pAllCur; pCur; pCur=pCur->pNext){
+ pAux->pMod->xClose(pCur->pChild);
+ }
+ pAux->pMod->xDisconnect(pVtab->pChild);
+ }
+ pAux->bDisposed = 1;
+ if( pAux->xChildDestroy ){
+ pAux->xChildDestroy(pAux->pChildAux);
+ pAux->xChildDestroy = 0;
+ }
+ }
+}
+
+
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_vtshim_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ return SQLITE_OK;
+}
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
index 16a316f..8150538 100644
--- a/ext/rtree/rtree.c
+++ b/ext/rtree/rtree.c
@@ -54,48 +54,6 @@
#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
-
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
-# define NDEBUG 1
-#endif
-
#ifndef SQLITE_CORE
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
@@ -105,11 +63,13 @@
#include <string.h>
#include <assert.h>
+#include <stdio.h>
#ifndef SQLITE_AMALGAMATION
#include "sqlite3rtree.h"
typedef sqlite3_int64 i64;
typedef unsigned char u8;
+typedef unsigned short u16;
typedef unsigned int u32;
#endif
@@ -127,6 +87,7 @@ typedef struct RtreeConstraint RtreeConstraint;
typedef struct RtreeMatchArg RtreeMatchArg;
typedef struct RtreeGeomCallback RtreeGeomCallback;
typedef union RtreeCoord RtreeCoord;
+typedef struct RtreeSearchPoint RtreeSearchPoint;
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
#define RTREE_MAX_DIMENSIONS 5
@@ -135,22 +96,33 @@ typedef union RtreeCoord RtreeCoord;
** ever contain very many entries, so a fixed number of buckets is
** used.
*/
-#define HASHSIZE 128
+#define HASHSIZE 97
+
+/* The xBestIndex method of this virtual table requires an estimate of
+** the number of rows in the virtual table to calculate the costs of
+** various strategies. If possible, this estimate is loaded from the
+** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum).
+** Otherwise, if no sqlite_stat1 entry is available, use
+** RTREE_DEFAULT_ROWEST.
+*/
+#define RTREE_DEFAULT_ROWEST 1048576
+#define RTREE_MIN_ROWEST 100
/*
** An rtree virtual-table object.
*/
struct Rtree {
- sqlite3_vtab base;
+ sqlite3_vtab base; /* Base class. Must be first */
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 */
+ u8 nDim; /* Number of dimensions */
+ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
+ u8 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 */
+ i64 nRowEst; /* Estimated number of rows in this table */
/* List of nodes removed during a CondenseTree operation. List is
** linked together via the pointer normally used for hash chains -
@@ -175,10 +147,10 @@ struct Rtree {
sqlite3_stmt *pWriteParent;
sqlite3_stmt *pDeleteParent;
- int eCoordType;
+ RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
};
-/* Possible values for eCoordType: */
+/* Possible values for Rtree.eCoordType: */
#define RTREE_COORD_REAL32 0
#define RTREE_COORD_INT32 1
@@ -190,12 +162,31 @@ struct Rtree {
#ifdef SQLITE_RTREE_INT_ONLY
typedef sqlite3_int64 RtreeDValue; /* High accuracy coordinate */
typedef int RtreeValue; /* Low accuracy coordinate */
+# define RTREE_ZERO 0
#else
typedef double RtreeDValue; /* High accuracy coordinate */
typedef float RtreeValue; /* Low accuracy coordinate */
+# define RTREE_ZERO 0.0
#endif
/*
+** When doing a search of an r-tree, instances of the following structure
+** record intermediate results from the tree walk.
+**
+** The id is always a node-id. For iLevel>=1 the id is the node-id of
+** the node that the RtreeSearchPoint represents. When iLevel==0, however,
+** the id is of the parent node and the cell that RtreeSearchPoint
+** represents is the iCell-th entry in the parent node.
+*/
+struct RtreeSearchPoint {
+ RtreeDValue rScore; /* The score for this node. Smallest goes first. */
+ sqlite3_int64 id; /* Node ID */
+ u8 iLevel; /* 0=entries. 1=leaf node. 2+ for higher */
+ u8 eWithin; /* PARTLY_WITHIN or FULLY_WITHIN */
+ u8 iCell; /* Cell index within the node */
+};
+
+/*
** The minimum number of cells allowed for a node is a third of the
** maximum. In Gutman's notation:
**
@@ -217,21 +208,44 @@ struct Rtree {
*/
#define RTREE_MAX_DEPTH 40
+
+/*
+** Number of entries in the cursor RtreeNode cache. The first entry is
+** used to cache the RtreeNode for RtreeCursor.sPoint. The remaining
+** entries cache the RtreeNode for the first elements of the priority queue.
+*/
+#define RTREE_CACHE_SZ 5
+
/*
** 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 */
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
+ u8 atEOF; /* True if at end of search */
+ u8 bPoint; /* True if sPoint is valid */
int iStrategy; /* Copy of idxNum search parameter */
int nConstraint; /* Number of entries in aConstraint */
RtreeConstraint *aConstraint; /* Search constraints. */
+ int nPointAlloc; /* Number of slots allocated for aPoint[] */
+ int nPoint; /* Number of slots used in aPoint[] */
+ int mxLevel; /* iLevel value for root of the tree */
+ RtreeSearchPoint *aPoint; /* Priority queue for search points */
+ RtreeSearchPoint sPoint; /* Cached next search point */
+ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */
+ u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */
};
+/* Return the Rtree of a RtreeCursor */
+#define RTREE_OF_CURSOR(X) ((Rtree*)((X)->base.pVtab))
+
+/*
+** A coordinate can be either a floating point number or a integer. All
+** coordinates within a single R-Tree are always of the same time.
+*/
union RtreeCoord {
- RtreeValue f;
- int i;
+ RtreeValue f; /* Floating point value */
+ int i; /* Integer value */
+ u32 u; /* Unsigned for byte-order conversions */
};
/*
@@ -256,38 +270,67 @@ union RtreeCoord {
struct RtreeConstraint {
int iCoord; /* Index of constrained coordinate */
int op; /* Constraining operation */
- RtreeDValue rValue; /* Constraint value. */
- int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
- sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */
+ union {
+ RtreeDValue rValue; /* Constraint value. */
+ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*);
+ int (*xQueryFunc)(sqlite3_rtree_query_info*);
+ } u;
+ sqlite3_rtree_query_info *pInfo; /* xGeom and xQueryFunc argument */
};
/* 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
-#define RTREE_MATCH 0x46
+#define RTREE_EQ 0x41 /* A */
+#define RTREE_LE 0x42 /* B */
+#define RTREE_LT 0x43 /* C */
+#define RTREE_GE 0x44 /* D */
+#define RTREE_GT 0x45 /* E */
+#define RTREE_MATCH 0x46 /* F: Old-style sqlite3_rtree_geometry_callback() */
+#define RTREE_QUERY 0x47 /* G: New-style sqlite3_rtree_query_callback() */
+
/*
** An rtree structure node.
*/
struct RtreeNode {
- RtreeNode *pParent; /* Parent node */
- i64 iNode;
- int nRef;
- int isDirty;
- u8 *zData;
- RtreeNode *pNext; /* Next node in this hash chain */
+ RtreeNode *pParent; /* Parent node */
+ i64 iNode; /* The node number */
+ int nRef; /* Number of references to this node */
+ int isDirty; /* True if the node needs to be written to disk */
+ u8 *zData; /* Content of the node, as should be on disk */
+ RtreeNode *pNext; /* Next node in this hash collision chain */
};
+
+/* Return the number of cells in a node */
#define NCELL(pNode) readInt16(&(pNode)->zData[2])
/*
-** Structure to store a deserialized rtree record.
+** A single cell from a node, deserialized
*/
struct RtreeCell {
- i64 iRowid;
- RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
+ i64 iRowid; /* Node or entry ID */
+ RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; /* Bounding box coordinates */
+};
+
+
+/*
+** This object becomes the sqlite3_user_data() for the SQL functions
+** that are created by sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() and which appear on the right of MATCH
+** operators in order to constrain a search.
+**
+** xGeom and xQueryFunc are the callback functions. Exactly one of
+** xGeom and xQueryFunc fields is non-NULL, depending on whether the
+** SQL function was created using sqlite3_rtree_geometry_callback() or
+** sqlite3_rtree_query_callback().
+**
+** This object is deleted automatically by the destructor mechanism in
+** sqlite3_create_function_v2().
+*/
+struct RtreeGeomCallback {
+ int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
+ int (*xQueryFunc)(sqlite3_rtree_query_info*);
+ void (*xDestructor)(void*);
+ void *pContext;
};
@@ -299,29 +342,16 @@ struct RtreeCell {
#define RTREE_GEOMETRY_MAGIC 0x891245AB
/*
-** An instance of this structure must be supplied as a blob argument to
-** the right-hand-side of an SQL MATCH operator used to constrain an
-** r-tree query.
+** An instance of this structure (in the form of a BLOB) is returned by
+** the SQL functions that sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() create, and is read as the right-hand
+** operand to the MATCH operator of an R-Tree.
*/
struct RtreeMatchArg {
- u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
- int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue*, int *);
- void *pContext;
- int nParam;
- RtreeDValue aParam[1];
-};
-
-/*
-** When a geometry callback is created (see sqlite3_rtree_geometry_callback),
-** a single instance of the following structure is allocated. It is used
-** as the context for the user-function created by by s_r_g_c(). The object
-** is eventually deleted by the destructor mechanism provided by
-** sqlite3_create_function_v2() (which is called by s_r_g_c() to create
-** the geometry callback function).
-*/
-struct RtreeGeomCallback {
- int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
- void *pContext;
+ u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
+ RtreeGeomCallback cb; /* Info about the callback functions */
+ int nParam; /* Number of parameters to the SQL function */
+ RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
};
#ifndef MAX
@@ -415,10 +445,7 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){
** 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;
+ return iNode % HASHSIZE;
}
/*
@@ -478,8 +505,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
/*
** Obtain a reference to an r-tree node.
*/
-static int
-nodeAcquire(
+static int nodeAcquire(
Rtree *pRtree, /* R-tree structure */
i64 iNode, /* Node number to load */
RtreeNode *pParent, /* Either the parent node or NULL */
@@ -568,10 +594,10 @@ nodeAcquire(
** Overwrite cell iCell of node pNode with the contents of pCell.
*/
static void nodeOverwriteCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell,
- int iCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node into which the cell is to be written */
+ RtreeCell *pCell, /* The cell to write */
+ int iCell /* Index into pNode into which pCell is written */
){
int ii;
u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
@@ -583,7 +609,7 @@ static void nodeOverwriteCell(
}
/*
-** Remove cell the cell with index iCell from node pNode.
+** Remove 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];
@@ -600,11 +626,10 @@ static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){
**
** If there is not enough free space in pNode, return SQLITE_FULL.
*/
-static int
-nodeInsertCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell
+static int nodeInsertCell(
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* Write new cell into this node */
+ RtreeCell *pCell /* The cell to be inserted */
){
int nCell; /* Current number of cells in pNode */
int nMaxCell; /* Maximum number of cells for pNode */
@@ -625,8 +650,7 @@ nodeInsertCell(
/*
** If the node is dirty, write it out to the database.
*/
-static int
-nodeWrite(Rtree *pRtree, RtreeNode *pNode){
+static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode->isDirty ){
sqlite3_stmt *p = pRtree->pWriteNode;
@@ -651,8 +675,7 @@ nodeWrite(Rtree *pRtree, RtreeNode *pNode){
** 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){
+static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode ){
assert( pNode->nRef>0 );
@@ -680,9 +703,9 @@ nodeRelease(Rtree *pRtree, RtreeNode *pNode){
** an internal node, then the 64-bit integer is a child page number.
*/
static i64 nodeGetRowid(
- Rtree *pRtree,
- RtreeNode *pNode,
- int iCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node from which to extract the ID */
+ int iCell /* The cell index from which to extract the ID */
){
assert( iCell<NCELL(pNode) );
return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]);
@@ -692,11 +715,11 @@ static i64 nodeGetRowid(
** 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 */
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node from which to extract a coordinate */
+ int iCell, /* The index of the cell within the node */
+ int iCoord, /* Which coordinate to extract */
+ RtreeCoord *pCoord /* OUT: Space to write result to */
){
readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
@@ -706,15 +729,20 @@ static void nodeGetCoord(
** to by pCell with the results.
*/
static void nodeGetCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- int iCell,
- RtreeCell *pCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node containing the cell to be read */
+ int iCell, /* Index of the cell within the node */
+ RtreeCell *pCell /* OUT: Write the cell contents here */
){
- int ii;
+ u8 *pData;
+ u8 *pEnd;
+ RtreeCoord *pCoord;
pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
- for(ii=0; ii<pRtree->nDim*2; ii++){
- nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]);
+ pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell);
+ pEnd = pData + pRtree->nDim*8;
+ pCoord = pCell->aCoord;
+ for(; pData<pEnd; pData+=4, pCoord++){
+ readCoord(pData, pCoord);
}
}
@@ -840,10 +868,10 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
if( pCsr->aConstraint ){
int i; /* Used to iterate through constraint array */
for(i=0; i<pCsr->nConstraint; i++){
- sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom;
- if( pGeom ){
- if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser);
- sqlite3_free(pGeom);
+ sqlite3_rtree_query_info *pInfo = pCsr->aConstraint[i].pInfo;
+ if( pInfo ){
+ if( pInfo->xDelUser ) pInfo->xDelUser(pInfo->pUser);
+ sqlite3_free(pInfo);
}
}
sqlite3_free(pCsr->aConstraint);
@@ -856,12 +884,13 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
*/
static int rtreeClose(sqlite3_vtab_cursor *cur){
Rtree *pRtree = (Rtree *)(cur->pVtab);
- int rc;
+ int ii;
RtreeCursor *pCsr = (RtreeCursor *)cur;
freeCursorConstraints(pCsr);
- rc = nodeRelease(pRtree, pCsr->pNode);
+ sqlite3_free(pCsr->aPoint);
+ for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
sqlite3_free(pCsr);
- return rc;
+ return SQLITE_OK;
}
/*
@@ -872,194 +901,164 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
*/
static int rtreeEof(sqlite3_vtab_cursor *cur){
RtreeCursor *pCsr = (RtreeCursor *)cur;
- return (pCsr->pNode==0);
+ return pCsr->atEOF;
}
/*
-** The r-tree constraint passed as the second argument to this function is
-** guaranteed to be a MATCH constraint.
-*/
-static int testRtreeGeom(
- Rtree *pRtree, /* R-Tree object */
- RtreeConstraint *pConstraint, /* MATCH constraint to test */
- RtreeCell *pCell, /* Cell to test */
- int *pbRes /* OUT: Test result */
-){
- int i;
- RtreeDValue aCoord[RTREE_MAX_DIMENSIONS*2];
- int nCoord = pRtree->nDim*2;
-
- assert( pConstraint->op==RTREE_MATCH );
- assert( pConstraint->pGeom );
-
- for(i=0; i<nCoord; i++){
- aCoord[i] = DCOORD(pCell->aCoord[i]);
- }
- return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes);
+** Convert raw bits from the on-disk RTree record into a coordinate value.
+** The on-disk format is big-endian and needs to be converted for little-
+** endian platforms. The on-disk record stores integer coordinates if
+** eInt is true and it stores 32-bit floating point records if eInt is
+** false. a[] is the four bytes of the on-disk record to be decoded.
+** Store the results in "r".
+**
+** There are three versions of this macro, one each for little-endian and
+** big-endian processors and a third generic implementation. The endian-
+** specific implementations are much faster and are preferred if the
+** processor endianness is known at compile-time. The SQLITE_BYTEORDER
+** macro is part of sqliteInt.h and hence the endian-specific
+** implementation will only be used if this module is compiled as part
+** of the amalgamation.
+*/
+#if defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==1234
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ memcpy(&c.u,a,4); \
+ c.u = ((c.u>>24)&0xff)|((c.u>>8)&0xff00)| \
+ ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
+}
+#elif defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==4321
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ memcpy(&c.u,a,4); \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
}
+#else
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ c.u = ((u32)a[0]<<24) + ((u32)a[1]<<16) \
+ +((u32)a[2]<<8) + a[3]; \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
+}
+#endif
-/*
-** Cursor pCursor currently points to a cell in a non-leaf page.
-** Set *pbEof to true if the sub-tree headed by the cell is filtered
-** (excluded) by the constraints in the pCursor->aConstraint[]
-** array, or false otherwise.
-**
-** Return SQLITE_OK if successful or an SQLite error code if an error
-** occurs within a geometry callback.
+/*
+** Check the RTree node or entry given by pCellData and p against the MATCH
+** constraint pConstraint.
*/
-static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
- RtreeCell cell;
- int ii;
- int bRes = 0;
- int rc = SQLITE_OK;
-
- nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
- for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
- RtreeConstraint *p = &pCursor->aConstraint[ii];
- RtreeDValue cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
- RtreeDValue 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 || p->op==RTREE_MATCH
- );
-
- 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;
-
- default: {
- assert( p->op==RTREE_MATCH );
- rc = testRtreeGeom(pRtree, p, &cell, &bRes);
- bRes = !bRes;
- break;
- }
+static int rtreeCallbackConstraint(
+ RtreeConstraint *pConstraint, /* The constraint to test */
+ int eInt, /* True if RTree holding integer coordinates */
+ u8 *pCellData, /* Raw cell content */
+ RtreeSearchPoint *pSearch, /* Container of this cell */
+ sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */
+ int *peWithin /* OUT: visibility of the cell */
+){
+ int i; /* Loop counter */
+ sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
+ int nCoord = pInfo->nCoord; /* No. of coordinates */
+ int rc; /* Callback return code */
+ sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */
+
+ assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY );
+ assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 );
+
+ if( pConstraint->op==RTREE_QUERY && pSearch->iLevel==1 ){
+ pInfo->iRowid = readInt64(pCellData);
+ }
+ pCellData += 8;
+ for(i=0; i<nCoord; i++, pCellData += 4){
+ RTREE_DECODE_COORD(eInt, pCellData, aCoord[i]);
+ }
+ if( pConstraint->op==RTREE_MATCH ){
+ rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo,
+ nCoord, aCoord, &i);
+ if( i==0 ) *peWithin = NOT_WITHIN;
+ *prScore = RTREE_ZERO;
+ }else{
+ pInfo->aCoord = aCoord;
+ pInfo->iLevel = pSearch->iLevel - 1;
+ pInfo->rScore = pInfo->rParentScore = pSearch->rScore;
+ pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin;
+ rc = pConstraint->u.xQueryFunc(pInfo);
+ if( pInfo->eWithin<*peWithin ) *peWithin = pInfo->eWithin;
+ if( pInfo->rScore<*prScore || *prScore<RTREE_ZERO ){
+ *prScore = pInfo->rScore;
}
}
-
- *pbEof = bRes;
return rc;
}
/*
-** Test if the cell that cursor pCursor currently points to
-** would be filtered (excluded) by the constraints in the
-** pCursor->aConstraint[] array. If so, set *pbEof to true before
-** returning. If the cell is not filtered (excluded) by the constraints,
-** set pbEof to zero.
-**
-** Return SQLITE_OK if successful or an SQLite error code if an error
-** occurs within a geometry callback.
-**
-** This function assumes that the cell is part of a leaf node.
-*/
-static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
- RtreeCell cell;
- int ii;
- *pbEof = 0;
-
- nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
- for(ii=0; ii<pCursor->nConstraint; ii++){
- RtreeConstraint *p = &pCursor->aConstraint[ii];
- RtreeDValue 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 || p->op==RTREE_MATCH
- );
- 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;
- default: {
- int rc;
- assert( p->op==RTREE_MATCH );
- rc = testRtreeGeom(pRtree, p, &cell, &res);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- break;
- }
- }
-
- if( !res ){
- *pbEof = 1;
- return SQLITE_OK;
- }
- }
-
- return SQLITE_OK;
-}
-
-/*
-** 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 */
+** Check the internal RTree node given by pCellData against constraint p.
+** If this constraint cannot be satisfied by any child within the node,
+** set *peWithin to NOT_WITHIN.
+*/
+static void rtreeNonleafConstraint(
+ RtreeConstraint *p, /* The constraint to test */
+ int eInt, /* True if RTree holds integer coordinates */
+ u8 *pCellData, /* Raw cell content as appears on disk */
+ int *peWithin /* Adjust downward, as appropriate */
){
- int isEof;
- int rc;
- int ii;
- RtreeNode *pChild;
- sqlite3_int64 iRowid;
-
- RtreeNode *pSavedNode = pCursor->pNode;
- int iSavedCell = pCursor->iCell;
+ sqlite3_rtree_dbl val; /* Coordinate value convert to a double */
- assert( iHeight>=0 );
+ /* p->iCoord might point to either a lower or upper bound coordinate
+ ** in a coordinate pair. But make pCellData point to the lower bound.
+ */
+ pCellData += 8 + 4*(p->iCoord&0xfe);
- if( iHeight==0 ){
- rc = testRtreeEntry(pRtree, pCursor, &isEof);
- }else{
- rc = testRtreeCell(pRtree, pCursor, &isEof);
- }
- if( rc!=SQLITE_OK || isEof || iHeight==0 ){
- goto descend_to_cell_out;
- }
+ 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:
+ case RTREE_EQ:
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the lower bound of the coordinate pair */
+ if( p->u.rValue>=val ) return;
+ if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */
+ /* Fall through for the RTREE_EQ case */
- iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell);
- rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild);
- if( rc!=SQLITE_OK ){
- goto descend_to_cell_out;
+ default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */
+ pCellData += 4;
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the upper bound of the coordinate pair */
+ if( p->u.rValue<=val ) return;
}
+ *peWithin = NOT_WITHIN;
+}
- 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 ){
- goto descend_to_cell_out;
- }
- }
+/*
+** Check the leaf RTree cell given by pCellData against constraint p.
+** If this constraint is not satisfied, set *peWithin to NOT_WITHIN.
+** If the constraint is satisfied, leave *peWithin unchanged.
+**
+** The constraint is of the form: xN op $val
+**
+** The op is given by p->op. The xN is p->iCoord-th coordinate in
+** pCellData. $val is given by p->u.rValue.
+*/
+static void rtreeLeafConstraint(
+ RtreeConstraint *p, /* The constraint to test */
+ int eInt, /* True if RTree holds integer coordinates */
+ u8 *pCellData, /* Raw cell content as appears on disk */
+ int *peWithin /* Adjust downward, as appropriate */
+){
+ RtreeDValue xN; /* Coordinate value converted to a double */
- if( isEof ){
- assert( pCursor->pNode==pChild );
- nodeReference(pSavedNode);
- nodeRelease(pRtree, pChild);
- pCursor->pNode = pSavedNode;
- pCursor->iCell = iSavedCell;
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ );
+ pCellData += 8 + p->iCoord*4;
+ RTREE_DECODE_COORD(eInt, pCellData, xN);
+ switch( p->op ){
+ case RTREE_LE: if( xN <= p->u.rValue ) return; break;
+ case RTREE_LT: if( xN < p->u.rValue ) return; break;
+ case RTREE_GE: if( xN >= p->u.rValue ) return; break;
+ case RTREE_GT: if( xN > p->u.rValue ) return; break;
+ default: if( xN == p->u.rValue ) return; break;
}
-
-descend_to_cell_out:
- *pEof = isEof;
- return rc;
+ *peWithin = NOT_WITHIN;
}
/*
@@ -1074,6 +1073,7 @@ static int nodeRowidIndex(
){
int ii;
int nCell = NCELL(pNode);
+ assert( nCell<200 );
for(ii=0; ii<nCell; ii++){
if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
*piIndex = ii;
@@ -1096,48 +1096,302 @@ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
return SQLITE_OK;
}
-/*
-** Rtree virtual table module xNext method.
+/*
+** Compare two search points. Return negative, zero, or positive if the first
+** is less than, equal to, or greater than the second.
+**
+** The rScore is the primary key. Smaller rScore values come first.
+** If the rScore is a tie, then use iLevel as the tie breaker with smaller
+** iLevel values coming first. In this way, if rScore is the same for all
+** SearchPoints, then iLevel becomes the deciding factor and the result
+** is a depth-first search, which is the desired default behavior.
+*/
+static int rtreeSearchPointCompare(
+ const RtreeSearchPoint *pA,
+ const RtreeSearchPoint *pB
+){
+ if( pA->rScore<pB->rScore ) return -1;
+ if( pA->rScore>pB->rScore ) return +1;
+ if( pA->iLevel<pB->iLevel ) return -1;
+ if( pA->iLevel>pB->iLevel ) return +1;
+ return 0;
+}
+
+/*
+** Interchange to search points in a cursor.
+*/
+static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){
+ RtreeSearchPoint t = p->aPoint[i];
+ assert( i<j );
+ p->aPoint[i] = p->aPoint[j];
+ p->aPoint[j] = t;
+ i++; j++;
+ if( i<RTREE_CACHE_SZ ){
+ if( j>=RTREE_CACHE_SZ ){
+ nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
+ p->aNode[i] = 0;
+ }else{
+ RtreeNode *pTemp = p->aNode[i];
+ p->aNode[i] = p->aNode[j];
+ p->aNode[j] = pTemp;
+ }
+ }
+}
+
+/*
+** Return the search point with the lowest current score.
*/
-static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
- Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab);
- RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
- int rc = SQLITE_OK;
+static RtreeSearchPoint *rtreeSearchPointFirst(RtreeCursor *pCur){
+ return pCur->bPoint ? &pCur->sPoint : pCur->nPoint ? pCur->aPoint : 0;
+}
- /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is
- ** already at EOF. It is against the rules to call the xNext() method of
- ** a cursor that has already reached EOF.
- */
- assert( pCsr->pNode );
+/*
+** Get the RtreeNode for the search point with the lowest score.
+*/
+static RtreeNode *rtreeNodeOfFirstSearchPoint(RtreeCursor *pCur, int *pRC){
+ sqlite3_int64 id;
+ int ii = 1 - pCur->bPoint;
+ assert( ii==0 || ii==1 );
+ assert( pCur->bPoint || pCur->nPoint );
+ if( pCur->aNode[ii]==0 ){
+ assert( pRC!=0 );
+ id = ii ? pCur->aPoint[0].id : pCur->sPoint.id;
+ *pRC = nodeAcquire(RTREE_OF_CURSOR(pCur), id, 0, &pCur->aNode[ii]);
+ }
+ return pCur->aNode[ii];
+}
- if( pCsr->iStrategy==1 ){
- /* This "scan" is a direct lookup by rowid. There is no next entry. */
- nodeRelease(pRtree, pCsr->pNode);
- pCsr->pNode = 0;
+/*
+** Push a new element onto the priority queue
+*/
+static RtreeSearchPoint *rtreeEnqueue(
+ RtreeCursor *pCur, /* The cursor */
+ RtreeDValue rScore, /* Score for the new search point */
+ u8 iLevel /* Level for the new search point */
+){
+ int i, j;
+ RtreeSearchPoint *pNew;
+ if( pCur->nPoint>=pCur->nPointAlloc ){
+ int nNew = pCur->nPointAlloc*2 + 8;
+ pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0]));
+ if( pNew==0 ) return 0;
+ pCur->aPoint = pNew;
+ pCur->nPointAlloc = nNew;
+ }
+ i = pCur->nPoint++;
+ pNew = pCur->aPoint + i;
+ pNew->rScore = rScore;
+ pNew->iLevel = iLevel;
+ assert( iLevel>=0 && iLevel<=RTREE_MAX_DEPTH );
+ while( i>0 ){
+ RtreeSearchPoint *pParent;
+ j = (i-1)/2;
+ pParent = pCur->aPoint + j;
+ if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break;
+ rtreeSearchPointSwap(pCur, j, i);
+ i = j;
+ pNew = pParent;
+ }
+ return pNew;
+}
+
+/*
+** Allocate a new RtreeSearchPoint and return a pointer to it. Return
+** NULL if malloc fails.
+*/
+static RtreeSearchPoint *rtreeSearchPointNew(
+ RtreeCursor *pCur, /* The cursor */
+ RtreeDValue rScore, /* Score for the new search point */
+ u8 iLevel /* Level for the new search point */
+){
+ RtreeSearchPoint *pNew, *pFirst;
+ pFirst = rtreeSearchPointFirst(pCur);
+ pCur->anQueue[iLevel]++;
+ if( pFirst==0
+ || pFirst->rScore>rScore
+ || (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
+ ){
+ if( pCur->bPoint ){
+ int ii;
+ pNew = rtreeEnqueue(pCur, rScore, iLevel);
+ if( pNew==0 ) return 0;
+ ii = (int)(pNew - pCur->aPoint) + 1;
+ if( ii<RTREE_CACHE_SZ ){
+ assert( pCur->aNode[ii]==0 );
+ pCur->aNode[ii] = pCur->aNode[0];
+ }else{
+ nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
+ }
+ pCur->aNode[0] = 0;
+ *pNew = pCur->sPoint;
+ }
+ pCur->sPoint.rScore = rScore;
+ pCur->sPoint.iLevel = iLevel;
+ pCur->bPoint = 1;
+ return &pCur->sPoint;
}else{
- /* 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;
+ return rtreeEnqueue(pCur, rScore, iLevel);
+ }
+}
+
+#if 0
+/* Tracing routines for the RtreeSearchPoint queue */
+static void tracePoint(RtreeSearchPoint *p, int idx, RtreeCursor *pCur){
+ if( idx<0 ){ printf(" s"); }else{ printf("%2d", idx); }
+ printf(" %d.%05lld.%02d %g %d",
+ p->iLevel, p->id, p->iCell, p->rScore, p->eWithin
+ );
+ idx++;
+ if( idx<RTREE_CACHE_SZ ){
+ printf(" %p\n", pCur->aNode[idx]);
+ }else{
+ printf("\n");
+ }
+}
+static void traceQueue(RtreeCursor *pCur, const char *zPrefix){
+ int ii;
+ printf("=== %9s ", zPrefix);
+ if( pCur->bPoint ){
+ tracePoint(&pCur->sPoint, -1, pCur);
+ }
+ for(ii=0; ii<pCur->nPoint; ii++){
+ if( ii>0 || pCur->bPoint ) printf(" ");
+ tracePoint(&pCur->aPoint[ii], ii, pCur);
+ }
+}
+# define RTREE_QUEUE_TRACE(A,B) traceQueue(A,B)
+#else
+# define RTREE_QUEUE_TRACE(A,B) /* no-op */
+#endif
+
+/* Remove the search point with the lowest current score.
+*/
+static void rtreeSearchPointPop(RtreeCursor *p){
+ int i, j, k, n;
+ i = 1 - p->bPoint;
+ assert( i==0 || i==1 );
+ if( p->aNode[i] ){
+ nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
+ p->aNode[i] = 0;
+ }
+ if( p->bPoint ){
+ p->anQueue[p->sPoint.iLevel]--;
+ p->bPoint = 0;
+ }else if( p->nPoint ){
+ p->anQueue[p->aPoint[0].iLevel]--;
+ n = --p->nPoint;
+ p->aPoint[0] = p->aPoint[n];
+ if( n<RTREE_CACHE_SZ-1 ){
+ p->aNode[1] = p->aNode[n+1];
+ p->aNode[n+1] = 0;
+ }
+ i = 0;
+ while( (j = i*2+1)<n ){
+ k = j+1;
+ if( k<n && rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[j])<0 ){
+ if( rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[i])<0 ){
+ rtreeSearchPointSwap(p, i, k);
+ i = k;
+ }else{
+ break;
+ }
+ }else{
+ if( rtreeSearchPointCompare(&p->aPoint[j], &p->aPoint[i])<0 ){
+ rtreeSearchPointSwap(p, i, j);
+ i = j;
+ }else{
+ break;
}
}
- pCsr->pNode = pNode->pParent;
- rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell);
- if( rc!=SQLITE_OK ){
- return rc;
+ }
+ }
+}
+
+
+/*
+** Continue the search on cursor pCur until the front of the queue
+** contains an entry suitable for returning as a result-set row,
+** or until the RtreeSearchPoint queue is empty, indicating that the
+** query has completed.
+*/
+static int rtreeStepToLeaf(RtreeCursor *pCur){
+ RtreeSearchPoint *p;
+ Rtree *pRtree = RTREE_OF_CURSOR(pCur);
+ RtreeNode *pNode;
+ int eWithin;
+ int rc = SQLITE_OK;
+ int nCell;
+ int nConstraint = pCur->nConstraint;
+ int ii;
+ int eInt;
+ RtreeSearchPoint x;
+
+ eInt = pRtree->eCoordType==RTREE_COORD_INT32;
+ while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+ pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
+ if( rc ) return rc;
+ nCell = NCELL(pNode);
+ assert( nCell<200 );
+ while( p->iCell<nCell ){
+ sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
+ u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
+ eWithin = FULLY_WITHIN;
+ for(ii=0; ii<nConstraint; ii++){
+ RtreeConstraint *pConstraint = pCur->aConstraint + ii;
+ if( pConstraint->op>=RTREE_MATCH ){
+ rc = rtreeCallbackConstraint(pConstraint, eInt, pCellData, p,
+ &rScore, &eWithin);
+ if( rc ) return rc;
+ }else if( p->iLevel==1 ){
+ rtreeLeafConstraint(pConstraint, eInt, pCellData, &eWithin);
+ }else{
+ rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
+ }
+ if( eWithin==NOT_WITHIN ) break;
+ }
+ p->iCell++;
+ if( eWithin==NOT_WITHIN ) continue;
+ x.iLevel = p->iLevel - 1;
+ if( x.iLevel ){
+ x.id = readInt64(pCellData);
+ x.iCell = 0;
+ }else{
+ x.id = p->id;
+ x.iCell = p->iCell - 1;
+ }
+ if( p->iCell>=nCell ){
+ RTREE_QUEUE_TRACE(pCur, "POP-S:");
+ rtreeSearchPointPop(pCur);
}
- nodeReference(pCsr->pNode);
- nodeRelease(pRtree, pNode);
- iHeight++;
+ if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO;
+ p = rtreeSearchPointNew(pCur, rScore, x.iLevel);
+ if( p==0 ) return SQLITE_NOMEM;
+ p->eWithin = eWithin;
+ p->id = x.id;
+ p->iCell = x.iCell;
+ RTREE_QUEUE_TRACE(pCur, "PUSH-S:");
+ break;
+ }
+ if( p->iCell>=nCell ){
+ RTREE_QUEUE_TRACE(pCur, "POP-Se:");
+ rtreeSearchPointPop(pCur);
}
}
+ pCur->atEOF = p==0;
+ return SQLITE_OK;
+}
+/*
+** Rtree virtual table module xNext method.
+*/
+static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+ int rc = SQLITE_OK;
+
+ /* Move to the next entry that matches the configured constraints. */
+ RTREE_QUEUE_TRACE(pCsr, "POP-Nx:");
+ rtreeSearchPointPop(pCsr);
+ rc = rtreeStepToLeaf(pCsr);
return rc;
}
@@ -1145,13 +1399,14 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
** 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;
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+ int rc = SQLITE_OK;
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+ if( rc==SQLITE_OK && p ){
+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ }
+ return rc;
}
/*
@@ -1160,13 +1415,18 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
Rtree *pRtree = (Rtree *)cur->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)cur;
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+ RtreeCoord c;
+ int rc = SQLITE_OK;
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+ if( rc ) return rc;
+ if( p==0 ) return SQLITE_OK;
if( i==0 ){
- i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
- sqlite3_result_int64(ctx, iRowid);
+ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else{
- RtreeCoord c;
- nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
+ if( rc ) return rc;
+ nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
sqlite3_result_double(ctx, c.f);
@@ -1177,7 +1437,6 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
sqlite3_result_int(ctx, c.i);
}
}
-
return SQLITE_OK;
}
@@ -1188,12 +1447,18 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
** *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){
+static int findLeafNode(
+ Rtree *pRtree, /* RTree to search */
+ i64 iRowid, /* The rowid searching for */
+ RtreeNode **ppLeaf, /* Write the node here */
+ sqlite3_int64 *piNode /* Write the node-id here */
+){
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);
+ if( piNode ) *piNode = iNode;
rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
sqlite3_reset(pRtree->pReadRowid);
}else{
@@ -1209,9 +1474,10 @@ static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
** operator.
*/
static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
- RtreeMatchArg *p;
- sqlite3_rtree_geometry *pGeom;
- int nBlob;
+ RtreeMatchArg *pBlob; /* BLOB returned by geometry function */
+ sqlite3_rtree_query_info *pInfo; /* Callback information */
+ int nBlob; /* Size of the geometry function blob */
+ int nExpected; /* Expected size of the BLOB */
/* Check that value is actually a blob. */
if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR;
@@ -1224,27 +1490,29 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
return SQLITE_ERROR;
}
- pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc(
- sizeof(sqlite3_rtree_geometry) + nBlob
- );
- if( !pGeom ) return SQLITE_NOMEM;
- memset(pGeom, 0, sizeof(sqlite3_rtree_geometry));
- p = (RtreeMatchArg *)&pGeom[1];
+ pInfo = (sqlite3_rtree_query_info*)sqlite3_malloc( sizeof(*pInfo)+nBlob );
+ if( !pInfo ) return SQLITE_NOMEM;
+ memset(pInfo, 0, sizeof(*pInfo));
+ pBlob = (RtreeMatchArg*)&pInfo[1];
- memcpy(p, sqlite3_value_blob(pValue), nBlob);
- if( p->magic!=RTREE_GEOMETRY_MAGIC
- || nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(RtreeDValue))
- ){
- sqlite3_free(pGeom);
+ memcpy(pBlob, sqlite3_value_blob(pValue), nBlob);
+ nExpected = (int)(sizeof(RtreeMatchArg) +
+ (pBlob->nParam-1)*sizeof(RtreeDValue));
+ if( pBlob->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=nExpected ){
+ sqlite3_free(pInfo);
return SQLITE_ERROR;
}
+ pInfo->pContext = pBlob->cb.pContext;
+ pInfo->nParam = pBlob->nParam;
+ pInfo->aParam = pBlob->aParam;
- pGeom->pContext = p->pContext;
- pGeom->nParam = p->nParam;
- pGeom->aParam = p->aParam;
-
- pCons->xGeom = p->xGeom;
- pCons->pGeom = pGeom;
+ if( pBlob->cb.xGeom ){
+ pCons->u.xGeom = pBlob->cb.xGeom;
+ }else{
+ pCons->op = RTREE_QUERY;
+ pCons->u.xQueryFunc = pBlob->cb.xQueryFunc;
+ }
+ pCons->pInfo = pInfo;
return SQLITE_OK;
}
@@ -1258,44 +1526,59 @@ static int rtreeFilter(
){
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-
RtreeNode *pRoot = 0;
int ii;
int rc = SQLITE_OK;
+ int iCell = 0;
rtreeReference(pRtree);
+ /* Reset the cursor to the same state as rtreeOpen() leaves it in. */
freeCursorConstraints(pCsr);
- pCsr->iStrategy = idxNum;
+ sqlite3_free(pCsr->aPoint);
+ memset(pCsr, 0, sizeof(RtreeCursor));
+ pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
+ pCsr->iStrategy = idxNum;
if( idxNum==1 ){
/* Special case - lookup by rowid. */
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
+ RtreeSearchPoint *p; /* Search point for the the leaf */
i64 iRowid = sqlite3_value_int64(argv[0]);
- rc = findLeafNode(pRtree, iRowid, &pLeaf);
- pCsr->pNode = pLeaf;
- if( pLeaf ){
- assert( rc==SQLITE_OK );
- rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell);
+ i64 iNode = 0;
+ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
+ if( rc==SQLITE_OK && pLeaf!=0 ){
+ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
+ assert( p!=0 ); /* Always returns pCsr->sPoint */
+ pCsr->aNode[0] = pLeaf;
+ p->id = iNode;
+ p->eWithin = PARTLY_WITHIN;
+ rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
+ p->iCell = iCell;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
+ }else{
+ pCsr->atEOF = 1;
}
}else{
/* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
** with the configured constraints.
*/
- if( argc>0 ){
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+ if( rc==SQLITE_OK && argc>0 ){
pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc);
pCsr->nConstraint = argc;
if( !pCsr->aConstraint ){
rc = SQLITE_NOMEM;
}else{
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
+ memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
assert( (idxStr==0 && argc==0)
|| (idxStr && (int)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';
- if( p->op==RTREE_MATCH ){
+ p->iCoord = idxStr[ii*2+1]-'0';
+ if( p->op>=RTREE_MATCH ){
/* A MATCH operator. The right-hand-side must be a blob that
** can be cast into an RtreeMatchArg object. One created using
** an sqlite3_rtree_geometry_callback() SQL user function.
@@ -1304,46 +1587,53 @@ static int rtreeFilter(
if( rc!=SQLITE_OK ){
break;
}
+ p->pInfo->nCoord = pRtree->nDim*2;
+ p->pInfo->anQueue = pCsr->anQueue;
+ p->pInfo->mxLevel = pRtree->iDepth + 1;
}else{
#ifdef SQLITE_RTREE_INT_ONLY
- p->rValue = sqlite3_value_int64(argv[ii]);
+ p->u.rValue = sqlite3_value_int64(argv[ii]);
#else
- p->rValue = sqlite3_value_double(argv[ii]);
+ p->u.rValue = sqlite3_value_double(argv[ii]);
#endif
}
}
}
}
-
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) );
+ RtreeSearchPoint *pNew;
+ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ pNew->id = 1;
+ pNew->iCell = 0;
+ pNew->eWithin = PARTLY_WITHIN;
+ assert( pCsr->bPoint==1 );
+ pCsr->aNode[0] = pRoot;
+ pRoot = 0;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
+ rc = rtreeStepToLeaf(pCsr);
}
}
+ nodeRelease(pRtree, pRoot);
rtreeRelease(pRtree);
return rc;
}
/*
+** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support estimatedRows. In that case this function is a no-op.
+*/
+static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
+#if SQLITE_VERSION_NUMBER>=3008002
+ if( sqlite3_libversion_number()>=3008002 ){
+ pIdxInfo->estimatedRows = nRow;
+ }
+#endif
+}
+
+/*
** Rtree virtual table module xBestIndex method. There are three
** table scan strategies to choose from (in order from most to
** least desirable):
@@ -1378,13 +1668,14 @@ static int rtreeFilter(
** is 'a', the second from the left 'b' etc.
*/
static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ Rtree *pRtree = (Rtree*)tab;
int rc = SQLITE_OK;
int ii;
+ i64 nRow; /* Estimated rows returned by this scan */
int iIdx = 0;
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
memset(zIdxStr, 0, sizeof(zIdxStr));
- UNUSED_PARAMETER(tab);
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
@@ -1404,9 +1695,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
/* This strategy involves a two rowid lookups on an B-Tree structures
** and then a linear search of an R-Tree node. This should be
** considered almost as quick as a direct rowid lookup (for which
- ** sqlite uses an internal cost of 0.0).
+ ** sqlite uses an internal cost of 0.0). It is expected to return
+ ** a single row.
*/
- pIdxInfo->estimatedCost = 10.0;
+ pIdxInfo->estimatedCost = 30.0;
+ setEstimatedRows(pIdxInfo, 1);
return SQLITE_OK;
}
@@ -1424,7 +1717,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
break;
}
zIdxStr[iIdx++] = op;
- zIdxStr[iIdx++] = p->iColumn - 1 + 'a';
+ zIdxStr[iIdx++] = p->iColumn - 1 + '0';
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
pIdxInfo->aConstraintUsage[ii].omit = 1;
}
@@ -1435,8 +1728,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
return SQLITE_NOMEM;
}
- assert( iIdx>=0 );
- pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1));
+
+ nRow = pRtree->nRowEst / (iIdx + 1);
+ pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
+ setEstimatedRows(pIdxInfo, nRow);
+
return rc;
}
@@ -1514,62 +1810,32 @@ static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
return (cellArea(pRtree, &cell)-area);
}
-#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT
static RtreeDValue cellOverlap(
Rtree *pRtree,
RtreeCell *p,
RtreeCell *aCell,
- int nCell,
- int iExclude
+ int nCell
){
int ii;
- RtreeDValue overlap = 0.0;
+ RtreeDValue overlap = RTREE_ZERO;
for(ii=0; ii<nCell; ii++){
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
- if( ii!=iExclude )
-#else
- assert( iExclude==-1 );
- UNUSED_PARAMETER(iExclude);
-#endif
- {
- int jj;
- RtreeDValue o = (RtreeDValue)1;
- for(jj=0; jj<(pRtree->nDim*2); jj+=2){
- RtreeDValue x1, 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);
- }
+ int jj;
+ RtreeDValue o = (RtreeDValue)1;
+ for(jj=0; jj<(pRtree->nDim*2); jj+=2){
+ RtreeDValue x1, 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 = (RtreeDValue)0;
+ break;
+ }else{
+ o = o * (x2-x1);
}
- overlap += o;
}
+ overlap += o;
}
return overlap;
}
-#endif
-
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
-static RtreeDValue cellOverlapEnlargement(
- Rtree *pRtree,
- RtreeCell *p,
- RtreeCell *pInsert,
- RtreeCell *aCell,
- int nCell,
- int iExclude
-){
- RtreeDValue before, after;
- before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
- cellUnion(pRtree, p, pInsert);
- after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
- return (after-before);
-}
-#endif
/*
@@ -1591,12 +1857,8 @@ static int ChooseLeaf(
int iCell;
sqlite3_int64 iBest = 0;
- RtreeDValue fMinGrowth = 0.0;
- RtreeDValue fMinArea = 0.0;
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
- RtreeDValue fMinOverlap = 0.0;
- RtreeDValue overlap;
-#endif
+ RtreeDValue fMinGrowth = RTREE_ZERO;
+ RtreeDValue fMinArea = RTREE_ZERO;
int nCell = NCELL(pNode);
RtreeCell cell;
@@ -1604,22 +1866,6 @@ static int ChooseLeaf(
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.
@@ -1631,26 +1877,9 @@ static int ChooseLeaf(
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);
- }else{
- overlap = 0.0;
- }
- if( (iCell==0)
- || (overlap<fMinOverlap)
- || (overlap==fMinOverlap && growth<fMinGrowth)
- || (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea)
- ){
- bBest = 1;
- fMinOverlap = overlap;
- }
-#else
if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
bBest = 1;
}
-#endif
if( bBest ){
fMinGrowth = growth;
fMinArea = area;
@@ -1721,155 +1950,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
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;
- RtreeDValue maxNormalInnerWidth = (RtreeDValue)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++){
- RtreeDValue x1 = DCOORD(aCell[0].aCoord[i*2]);
- RtreeDValue x2 = DCOORD(aCell[0].aCoord[i*2+1]);
- RtreeDValue x3 = x1;
- RtreeDValue x4 = x2;
- int jj;
-
- int iCellLeft = 0;
- int iCellRight = 0;
-
- for(jj=1; jj<nCell; jj++){
- RtreeDValue left = DCOORD(aCell[jj].aCoord[i*2]);
- RtreeDValue right = DCOORD(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 ){
- RtreeDValue 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;
- RtreeDValue fDiff;
- int ii;
- for(ii=0; ii<nCell; ii++){
- if( aiUsed[ii]==0 ){
- RtreeDValue left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
- RtreeDValue right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
- RtreeDValue 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;
- RtreeDValue fWaste = 0.0;
-
- for(ii=0; ii<nCell; ii++){
- for(jj=ii+1; jj<nCell; jj++){
- RtreeDValue right = cellArea(pRtree, &aCell[jj]);
- RtreeDValue growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
- RtreeDValue 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
@@ -2010,7 +2090,6 @@ static void SortByDimension(
}
}
-#if VARIANT_RSTARTREE_SPLIT
/*
** Implementation of the R*-tree variant of SplitNode from Beckman[1990].
*/
@@ -2029,7 +2108,7 @@ static int splitNodeStartree(
int iBestDim = 0;
int iBestSplit = 0;
- RtreeDValue fBestMargin = 0.0;
+ RtreeDValue fBestMargin = RTREE_ZERO;
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
@@ -2050,9 +2129,9 @@ static int splitNodeStartree(
}
for(ii=0; ii<pRtree->nDim; ii++){
- RtreeDValue margin = 0.0;
- RtreeDValue fBestOverlap = 0.0;
- RtreeDValue fBestArea = 0.0;
+ RtreeDValue margin = RTREE_ZERO;
+ RtreeDValue fBestOverlap = RTREE_ZERO;
+ RtreeDValue fBestArea = RTREE_ZERO;
int iBestLeft = 0;
int nLeft;
@@ -2078,7 +2157,7 @@ static int splitNodeStartree(
}
margin += cellMargin(pRtree, &left);
margin += cellMargin(pRtree, &right);
- overlap = cellOverlap(pRtree, &left, &right, 1, -1);
+ overlap = cellOverlap(pRtree, &left, &right, 1);
area = cellArea(pRtree, &left) + cellArea(pRtree, &right);
if( (nLeft==RTREE_MINCELLS(pRtree))
|| (overlap<fBestOverlap)
@@ -2110,63 +2189,7 @@ static int splitNodeStartree(
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);
- if( !aiUsed ){
- return SQLITE_NOMEM;
- }
- 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);
- RtreeDValue 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,
@@ -2244,7 +2267,8 @@ static int SplitNode(
memset(pLeft->zData, 0, pRtree->iNodeSize);
memset(pRight->zData, 0, pRtree->iNodeSize);
- rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox);
+ rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight,
+ &leftbbox, &rightbbox);
if( rc!=SQLITE_OK ){
goto splitnode_out;
}
@@ -2527,7 +2551,7 @@ static int Reinsert(
}
for(ii=0; ii<nCell; ii++){
- aDistance[ii] = 0.0;
+ aDistance[ii] = RTREE_ZERO;
for(iDim=0; iDim<pRtree->nDim; iDim++){
RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
DCOORD(aCell[ii].aCoord[iDim*2]));
@@ -2593,16 +2617,12 @@ static int rtreeInsertCell(
}
}
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{
rc = AdjustTree(pRtree, pNode, pCell);
if( rc==SQLITE_OK ){
@@ -2672,7 +2692,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
** about to be deleted.
*/
if( rc==SQLITE_OK ){
- rc = findLeafNode(pRtree, iDelete, &pLeaf);
+ rc = findLeafNode(pRtree, iDelete, &pLeaf, 0);
}
/* Delete the cell in question from the leaf node. */
@@ -2911,6 +2931,43 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
return rc;
}
+/*
+** This function populates the pRtree->nRowEst variable with an estimate
+** of the number of rows in the virtual table. If possible, this is based
+** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
+*/
+static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
+ const char *zFmt = "SELECT stat FROM %Q.sqlite_stat1 WHERE tbl = '%q_rowid'";
+ char *zSql;
+ sqlite3_stmt *p;
+ int rc;
+ i64 nRow = 0;
+
+ zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0);
+ if( rc==SQLITE_OK ){
+ if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
+ rc = sqlite3_finalize(p);
+ }else if( rc!=SQLITE_NOMEM ){
+ rc = SQLITE_OK;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( nRow==0 ){
+ pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
+ }else{
+ pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
+ }
+ }
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
static sqlite3_module rtreeModule = {
0, /* iVersion */
rtreeCreate, /* xCreate - create a table */
@@ -2972,7 +3029,8 @@ static int rtreeSqlInit(
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);"
+"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
);
@@ -2996,6 +3054,7 @@ static int rtreeSqlInit(
appStmt[7] = &pRtree->pWriteParent;
appStmt[8] = &pRtree->pDeleteParent;
+ rc = rtreeQueryStat1(db, pRtree);
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
if( zSql ){
@@ -3173,6 +3232,8 @@ static int rtreeInit(
if( rc==SQLITE_OK ){
*ppVtab = (sqlite3_vtab *)pRtree;
}else{
+ assert( *ppVtab==0 );
+ assert( pRtree->nBusy==1 );
rtreeRelease(pRtree);
}
return rc;
@@ -3183,10 +3244,10 @@ static int rtreeInit(
** 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:
+** The scalar function takes two arguments: (1) the number of dimensions
+** to the rtree (between 1 and 5, inclusive) and (2) a blob of data containing
+** an r-tree node. For a two-dimensional r-tree structure called "rt", to
+** deserialize all nodes, a statement like:
**
** SELECT rtreenode(2, data) FROM rt_node;
**
@@ -3219,7 +3280,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
nCell = (int)strlen(zCell);
for(jj=0; jj<tree.nDim*2; jj++){
#ifndef SQLITE_RTREE_INT_ONLY
- sqlite3_snprintf(512-nCell,&zCell[nCell], " %f",
+ sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
(double)cell.aCoord[jj].f);
#else
sqlite3_snprintf(512-nCell,&zCell[nCell], " %d",
@@ -3240,6 +3301,15 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
sqlite3_result_text(ctx, zText, -1, sqlite3_free);
}
+/* This routine implements an SQL function that returns the "depth" parameter
+** from the front of a blob that is an r-tree node. For example:
+**
+** SELECT rtreedepth(data) FROM rt_node WHERE nodeno=1;
+**
+** The depth value is 0 for all nodes other than the root node, and the root
+** node always has nodeno=1, so the example above is the primary use for this
+** routine. This routine is intended for testing and analysis only.
+*/
static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
UNUSED_PARAMETER(nArg);
if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB
@@ -3282,22 +3352,31 @@ int sqlite3RtreeInit(sqlite3 *db){
}
/*
-** A version of sqlite3_free() that can be used as a callback. This is used
-** in two places - as the destructor for the blob value returned by the
-** invocation of a geometry function, and as the destructor for the geometry
-** functions themselves.
+** This routine deletes the RtreeGeomCallback object that was attached
+** one of the SQL functions create by sqlite3_rtree_geometry_callback()
+** or sqlite3_rtree_query_callback(). In other words, this routine is the
+** destructor for an RtreeGeomCallback objecct. This routine is called when
+** the corresponding SQL function is deleted.
*/
-static void doSqlite3Free(void *p){
+static void rtreeFreeCallback(void *p){
+ RtreeGeomCallback *pInfo = (RtreeGeomCallback*)p;
+ if( pInfo->xDestructor ) pInfo->xDestructor(pInfo->pContext);
sqlite3_free(p);
}
/*
-** Each call to sqlite3_rtree_geometry_callback() creates an ordinary SQLite
-** scalar user function. This C function is the callback used for all such
-** registered SQL functions.
+** Each call to sqlite3_rtree_geometry_callback() or
+** sqlite3_rtree_query_callback() creates an ordinary SQLite
+** scalar function that is implemented by this routine.
+**
+** All this function does is construct an RtreeMatchArg object that
+** contains the geometry-checking callback routines and a list of
+** parameters to this function, then return that RtreeMatchArg object
+** as a BLOB.
**
-** The scalar user functions return a blob that is interpreted by r-tree
-** table MATCH operators.
+** The R-Tree MATCH operator will read the returned BLOB, deserialize
+** the RtreeMatchArg object, and use the RtreeMatchArg object to figure
+** out which elements of the R-Tree should be returned by the query.
*/
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
@@ -3311,8 +3390,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
}else{
int i;
pBlob->magic = RTREE_GEOMETRY_MAGIC;
- pBlob->xGeom = pGeomCtx->xGeom;
- pBlob->pContext = pGeomCtx->pContext;
+ pBlob->cb = pGeomCtx[0];
pBlob->nParam = nArg;
for(i=0; i<nArg; i++){
#ifdef SQLITE_RTREE_INT_ONLY
@@ -3321,7 +3399,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
#endif
}
- sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free);
+ sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free);
}
}
@@ -3329,10 +3407,10 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
** Register a new geometry function for use with the r-tree MATCH operator.
*/
int sqlite3_rtree_geometry_callback(
- sqlite3 *db,
- const char *zGeom,
- int (*xGeom)(sqlite3_rtree_geometry *, int, RtreeDValue *, int *),
- void *pContext
+ sqlite3 *db, /* Register SQL function on this connection */
+ const char *zGeom, /* Name of the new SQL function */
+ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), /* Callback */
+ void *pContext /* Extra data associated with the callback */
){
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
@@ -3340,17 +3418,44 @@ int sqlite3_rtree_geometry_callback(
pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
if( !pGeomCtx ) return SQLITE_NOMEM;
pGeomCtx->xGeom = xGeom;
+ pGeomCtx->xQueryFunc = 0;
+ pGeomCtx->xDestructor = 0;
pGeomCtx->pContext = pContext;
-
- /* Create the new user-function. Register a destructor function to delete
- ** the context object when it is no longer required. */
return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY,
- (void *)pGeomCtx, geomCallback, 0, 0, doSqlite3Free
+ (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
+ );
+}
+
+/*
+** Register a new 2nd-generation geometry function for use with the
+** r-tree MATCH operator.
+*/
+int sqlite3_rtree_query_callback(
+ sqlite3 *db, /* Register SQL function on this connection */
+ const char *zQueryFunc, /* Name of new SQL function */
+ int (*xQueryFunc)(sqlite3_rtree_query_info*), /* Callback */
+ void *pContext, /* Extra data passed into the callback */
+ void (*xDestructor)(void*) /* Destructor for the extra data */
+){
+ RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
+
+ /* Allocate and populate the context object. */
+ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
+ if( !pGeomCtx ) return SQLITE_NOMEM;
+ pGeomCtx->xGeom = 0;
+ pGeomCtx->xQueryFunc = xQueryFunc;
+ pGeomCtx->xDestructor = xDestructor;
+ pGeomCtx->pContext = pContext;
+ return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
+ (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
#if !SQLITE_CORE
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_rtree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test
index 275b132..0beb16c 100644
--- a/ext/rtree/rtree1.test
+++ b/ext/rtree/rtree1.test
@@ -33,6 +33,7 @@ set testprefix rtree1
# rtree-8.*: Test constrained scans of r-tree data.
#
# rtree-12.*: Test that on-conflict clauses are supported.
+# rtree-13.*: Test that bug [d2889096e7bdeac6d] has been fixed.
#
ifcapable !rtree {
@@ -120,12 +121,13 @@ proc execsql_intout {sql} {
# 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_execsql_test rtree-1.4.1a {
+ CREATE VIRTUAL TABLE t1 USING rtree(ii, x1, x2);
+ INSERT INTO t1 VALUES(1, 5.0, 10.0);
+ SELECT substr(hex(data),1,40) FROM t1_node;
+} {00000001000000000000000140A0000041200000}
+do_execsql_test rtree-1.4.1b {
+ INSERT INTO t1 VALUES(2, 15.0, 20.0);
} {}
do_test rtree-1.4.2 {
db close
@@ -435,16 +437,18 @@ do_test rtree-11.2 {
# Test on-conflict clause handling.
#
db_delete_and_reopen
-do_execsql_test 12.0 {
+do_execsql_test 12.0.1 {
CREATE VIRTUAL TABLE t1 USING rtree_i32(idx, x1, x2, y1, y2);
INSERT INTO t1 VALUES(1, 1, 2, 3, 4);
+ SELECT substr(hex(data),1,56) FROM t1_node;
+} {00000001000000000000000100000001000000020000000300000004}
+do_execsql_test 12.0.2 {
INSERT INTO t1 VALUES(2, 2, 3, 4, 5);
INSERT INTO t1 VALUES(3, 3, 4, 5, 6);
CREATE TABLE source(idx, x1, x2, y1, y2);
INSERT INTO source VALUES(5, 8, 8, 8, 8);
INSERT INTO source VALUES(2, 7, 7, 7, 7);
-
}
db_save_and_close
foreach {tn sql_template testdata} {
@@ -510,4 +514,25 @@ foreach {tn sql_template testdata} {
db close
}
}
+
+#-------------------------------------------------------------------------
+# Test that bug [d2889096e7bdeac6d] has been fixed.
+#
+reset_db
+do_execsql_test 13.1 {
+ CREATE VIRTUAL TABLE t9 USING rtree(id, xmin, xmax);
+ INSERT INTO t9 VALUES(1,0,0);
+ INSERT INTO t9 VALUES(2,0,0);
+ SELECT * FROM t9 WHERE id IN (1, 2);
+} {1 0.0 0.0 2 0.0 0.0}
+
+do_execsql_test 13.2 {
+ WITH r(x) AS (
+ SELECT 1 UNION ALL
+ SELECT 2 UNION ALL
+ SELECT 3
+ )
+ SELECT * FROM r CROSS JOIN t9 WHERE id=x;
+} {1 1 0.0 0.0 2 2 0.0 0.0}
+
finish_test
diff --git a/ext/rtree/rtree6.test b/ext/rtree/rtree6.test
index 92edc8d..cec3a8d 100644
--- a/ext/rtree/rtree6.test
+++ b/ext/rtree/rtree6.test
@@ -57,53 +57,59 @@ do_test rtree6-1.1 {
do_test rtree6-1.2 {
rtree_strategy {SELECT * FROM t1 WHERE x1>10}
-} {Ea}
+} {E0}
do_test rtree6-1.3 {
rtree_strategy {SELECT * FROM t1 WHERE x1<10}
-} {Ca}
+} {C0}
do_test rtree6-1.4 {
rtree_strategy {SELECT * FROM t1,t2 WHERE k=ii AND x1<10}
-} {Ca}
+} {C0}
do_test rtree6-1.5 {
rtree_strategy {SELECT * FROM t1,t2 WHERE k=+ii AND x1<10}
-} {Ca}
+} {C0}
do_eqp_test rtree6.2.1 {
SELECT * FROM t1,t2 WHERE k=+ii AND x1<10
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
+ 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_eqp_test rtree6.2.2 {
SELECT * FROM t1,t2 WHERE k=ii AND x1<10
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:Ca (~0 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0}
+ 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_eqp_test rtree6.2.3 {
SELECT * FROM t1,t2 WHERE k=ii
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:}
+ 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
-do_eqp_test rtree6.2.4 {
+do_eqp_test rtree6.2.4.1 {
+ SELECT * FROM t1,t2 WHERE v=+ii and x1<10 and x2>10
+} {
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
+ 0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
+}
+do_eqp_test rtree6.2.4.2 {
SELECT * FROM t1,t2 WHERE v=10 and x1<10 and x2>10
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:CaEb (~0 rows)}
- 0 1 1 {SCAN TABLE t2 (~100000 rows)}
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1}
+ 0 1 1 {SEARCH TABLE t2 USING AUTOMATIC COVERING INDEX (v=?)}
}
do_eqp_test rtree6.2.5 {
SELECT * FROM t1,t2 WHERE k=ii AND x1<v
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2: (~0 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 2:}
+ 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_execsql_test rtree6-3.1 {
@@ -126,7 +132,7 @@ do_test rtree6.3.2 {
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
-} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
+} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
do_test rtree6.3.3 {
rtree_strategy {
SELECT * FROM t3 WHERE
@@ -137,7 +143,7 @@ do_test rtree6.3.3 {
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND
x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5 AND x1>0.5
}
-} {EaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEaEa}
+} {E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0E0}
do_execsql_test rtree6-3.4 {
SELECT * FROM t3 WHERE x1>0.5 AND x1>0.8 AND x1>1.1
diff --git a/ext/rtree/rtree8.test b/ext/rtree/rtree8.test
index bf22cbf..578a146 100644
--- a/ext/rtree/rtree8.test
+++ b/ext/rtree/rtree8.test
@@ -168,4 +168,3 @@ do_test rtree8-5.3 {
finish_test
-
diff --git a/ext/rtree/rtreeB.test b/ext/rtree/rtreeB.test
index 7cb445c..aeb308e 100644
--- a/ext/rtree/rtreeB.test
+++ b/ext/rtree/rtreeB.test
@@ -41,7 +41,7 @@ ifcapable rtree_int_only {
INSERT INTO t1 VALUES(9223372036854775807, 150, 150, 400, 400);
SELECT rtreenode(2, data) FROM t1_node;
}
- } {{{1073741824 0.000000 0.000000 100.000000 100.000000} {2147483646 0.000000 0.000000 200.000000 200.000000} {4294967296 0.000000 0.000000 300.000000 300.000000} {8589934592 20.000000 20.000000 150.000000 150.000000} {9223372036854775807 150.000000 150.000000 400.000000 400.000000}}}
+ } {{{1073741824 0 0 100 100} {2147483646 0 0 200 200} {4294967296 0 0 300 300} {8589934592 20 20 150 150} {9223372036854775807 150 150 400 400}}}
}
finish_test
diff --git a/ext/rtree/rtreeC.test b/ext/rtree/rtreeC.test
new file mode 100644
index 0000000..94db05a
--- /dev/null
+++ b/ext/rtree/rtreeC.test
@@ -0,0 +1,273 @@
+# 2011 March 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.
+#
+#***********************************************************************
+# Make sure the rtreenode() testing function can handle entries with
+# 64-bit rowids.
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+ifcapable !rtree { finish_test ; return }
+set testprefix rtreeC
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE r_tree USING rtree(id, min_x, max_x, min_y, max_y);
+ CREATE TABLE t(x, y);
+}
+
+do_eqp_test 1.1 {
+ SELECT * FROM r_tree, t
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
+} {
+ 0 0 1 {SCAN TABLE t}
+ 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 1.2 {
+ SELECT * FROM t, r_tree
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
+} {
+ 0 0 0 {SCAN TABLE t}
+ 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 1.3 {
+ SELECT * FROM t, r_tree
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
+} {
+ 0 0 0 {SCAN TABLE t}
+ 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 1.5 {
+ SELECT * FROM t, r_tree
+} {
+ 0 0 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:}
+ 0 1 0 {SCAN TABLE t}
+}
+
+do_execsql_test 2.0 {
+ INSERT INTO t VALUES(0, 0);
+ INSERT INTO t VALUES(0, 1);
+ INSERT INTO t VALUES(0, 2);
+ INSERT INTO t VALUES(0, 3);
+ INSERT INTO t VALUES(0, 4);
+ INSERT INTO t VALUES(0, 5);
+ INSERT INTO t VALUES(0, 6);
+ INSERT INTO t VALUES(0, 7);
+ INSERT INTO t VALUES(0, 8);
+ INSERT INTO t VALUES(0, 9);
+
+ INSERT INTO t SELECT x+1, y FROM t;
+ INSERT INTO t SELECT x+2, y FROM t;
+ INSERT INTO t SELECT x+4, y FROM t;
+ INSERT INTO r_tree SELECT NULL, x-1, x+1, y-1, y+1 FROM t;
+ ANALYZE;
+}
+
+db close
+sqlite3 db test.db
+
+do_eqp_test 2.1 {
+ SELECT * FROM r_tree, t
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
+} {
+ 0 0 1 {SCAN TABLE t}
+ 0 1 0 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 2.2 {
+ SELECT * FROM t, r_tree
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND t.x<=max_y
+} {
+ 0 0 0 {SCAN TABLE t}
+ 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 2.3 {
+ SELECT * FROM t, r_tree
+ WHERE t.x>=min_x AND t.x<=max_x AND t.y>=min_y AND ?<=max_y
+} {
+ 0 0 0 {SCAN TABLE t}
+ 0 1 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:D3B2D1B0}
+}
+
+do_eqp_test 2.5 {
+ SELECT * FROM t, r_tree
+} {
+ 0 0 1 {SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:}
+ 0 1 0 {SCAN TABLE t}
+}
+
+#-------------------------------------------------------------------------
+# Test that the special CROSS JOIN handling works with rtree tables.
+#
+do_execsql_test 3.1 {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(y);
+ CREATE VIRTUAL TABLE t3 USING rtree(z, x1,x2, y1,y2);
+}
+
+do_eqp_test 3.2.1 { SELECT * FROM t1 CROSS JOIN t2 } {
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE t2}
+}
+do_eqp_test 3.2.2 { SELECT * FROM t2 CROSS JOIN t1 } {
+ 0 0 0 {SCAN TABLE t2} 0 1 1 {SCAN TABLE t1}
+}
+
+do_eqp_test 3.3.1 { SELECT * FROM t1 CROSS JOIN t3 } {
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE t3 VIRTUAL TABLE INDEX 2:}
+}
+do_eqp_test 3.3.2 { SELECT * FROM t3 CROSS JOIN t1 } {
+ 0 0 0 {SCAN TABLE t3 VIRTUAL TABLE INDEX 2:}
+ 0 1 1 {SCAN TABLE t1}
+}
+
+#--------------------------------------------------------------------
+# Test that LEFT JOINs are not reordered if the right-hand-side is
+# a virtual table.
+#
+reset_db
+do_execsql_test 4.1 {
+ CREATE TABLE t1(a);
+ CREATE VIRTUAL TABLE t2 USING rtree(b, x1,x2);
+
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+
+ INSERT INTO t2 VALUES(1, 0.0, 0.1);
+ INSERT INTO t2 VALUES(3, 0.0, 0.1);
+}
+
+do_execsql_test 4.2 {
+ SELECT a, b FROM t1 LEFT JOIN t2 ON (+a = +b);
+} {1 1 2 {}}
+
+do_execsql_test 4.3 {
+ SELECT b, a FROM t2 LEFT JOIN t1 ON (+a = +b);
+} {1 1 3 {}}
+
+#--------------------------------------------------------------------
+# Test that the sqlite_stat1 data is used correctly.
+#
+reset_db
+do_execsql_test 5.1 {
+ CREATE TABLE t1(x PRIMARY KEY, y);
+ CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
+
+ INSERT INTO t1(x) VALUES(1);
+ INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2
+ INSERT INTO t1(x) SELECT x+2 FROM t1; -- 4
+ INSERT INTO t1(x) SELECT x+4 FROM t1; -- 8
+ INSERT INTO t1(x) SELECT x+8 FROM t1; -- 16
+ INSERT INTO t1(x) SELECT x+16 FROM t1; -- 32
+ INSERT INTO t1(x) SELECT x+32 FROM t1; -- 64
+ INSERT INTO t1(x) SELECT x+64 FROM t1; -- 128
+ INSERT INTO t1(x) SELECT x+128 FROM t1; -- 256
+ INSERT INTO t1(x) SELECT x+256 FROM t1; -- 512
+ INSERT INTO t1(x) SELECT x+512 FROM t1; --1024
+
+ INSERT INTO rt SELECT x, x, x+1 FROM t1 WHERE x<=5;
+}
+
+# First test a query with no ANALYZE data at all. The outer loop is
+# real table "t1".
+#
+do_eqp_test 5.2 {
+ SELECT * FROM t1, rt WHERE x==id;
+} {
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
+}
+
+# Now create enough ANALYZE data to tell SQLite that virtual table "rt"
+# contains very few rows. This causes it to move "rt" to the outer loop.
+#
+do_execsql_test 5.3 {
+ ANALYZE;
+ DELETE FROM sqlite_stat1 WHERE tbl='t1';
+}
+db close
+sqlite3 db test.db
+do_eqp_test 5.4 {
+ SELECT * FROM t1, rt WHERE x==id;
+} {
+ 0 0 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:}
+ 0 1 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (x=?)}
+}
+
+# Delete the ANALYZE data. "t1" should be the outer loop again.
+#
+do_execsql_test 5.5 { DROP TABLE sqlite_stat1; }
+db close
+sqlite3 db test.db
+do_eqp_test 5.6 {
+ SELECT * FROM t1, rt WHERE x==id;
+} {
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
+}
+
+# This time create and attach a database that contains ANALYZE data for
+# tables of the same names as those used internally by virtual table
+# "rt". Check that the rtree module is not fooled into using this data.
+# Table "t1" should remain the outer loop.
+#
+do_test 5.7 {
+ db backup test.db2
+ sqlite3 db2 test.db2
+ db2 eval {
+ ANALYZE;
+ DELETE FROM sqlite_stat1 WHERE tbl='t1';
+ }
+ db2 close
+ db close
+ sqlite3 db test.db
+ execsql { ATTACH 'test.db2' AS aux; }
+} {}
+do_eqp_test 5.8 {
+ SELECT * FROM t1, rt WHERE x==id;
+} {
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 1:}
+}
+
+#--------------------------------------------------------------------
+# Test that having a second connection drop the sqlite_stat1 table
+# before it is required by rtreeConnect() does not cause problems.
+#
+ifcapable rtree {
+ reset_db
+ do_execsql_test 6.1 {
+ CREATE TABLE t1(x);
+ CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO rt VALUES(1,2,3);
+ ANALYZE;
+ }
+ db close
+ sqlite3 db test.db
+ do_execsql_test 6.2 { SELECT * FROM t1 } {1}
+
+ do_test 6.3 {
+ sqlite3 db2 test.db
+ db2 eval { DROP TABLE sqlite_stat1 }
+ db2 close
+ execsql { SELECT * FROM rt }
+ } {1 2.0 3.0}
+ db close
+}
+
+
+finish_test
diff --git a/ext/rtree/rtreeD.test b/ext/rtree/rtreeD.test
new file mode 100644
index 0000000..c4a7d22
--- /dev/null
+++ b/ext/rtree/rtreeD.test
@@ -0,0 +1,57 @@
+# 2014 March 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.
+#
+#***********************************************************************
+#
+# Miscellaneous tests for errors in the rtree constructor.
+#
+
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source [file join [file dirname [info script]] rtree_util.tcl]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+ifcapable !rtree {
+ finish_test
+ return
+}
+set testprefix rtreeD
+
+#-------------------------------------------------------------------------
+# Test that if an SQLITE_BUSY is encountered within the vtable
+# constructor, a relevant error message is returned.
+#
+do_multiclient_test tn {
+ do_test 1.$tn.1 {
+ sql1 {
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1,2);
+ CREATE VIRTUAL TABLE rt USING rtree(id, minx, maxx, miny, maxy);
+ INSERT INTO rt VALUES(1,2,3,4,5);
+ }
+ } {}
+
+ do_test 1.$tn.2 {
+ sql2 { SELECT * FROM t1; }
+ } {1 2}
+
+ do_test 1.$tn.3 {
+ sql1 { BEGIN EXCLUSIVE; INSERT INTO t1 VALUES(3, 4); }
+ } {}
+
+ do_test 1.$tn.4 {
+ list [catch { sql2 { SELECT * FROM rt } } msg] $msg
+ } {1 {database is locked}}
+}
+
+finish_test
+
+
diff --git a/ext/rtree/rtreeE.test b/ext/rtree/rtreeE.test
new file mode 100644
index 0000000..c450623
--- /dev/null
+++ b/ext/rtree/rtreeE.test
@@ -0,0 +1,129 @@
+# 2010 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 tests for the r-tree module. Specifically, it tests
+# that new-style custom r-tree queries (geometry callbacks) work.
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+ifcapable !rtree { finish_test ; return }
+ifcapable rtree_int_only { finish_test; return }
+
+
+#-------------------------------------------------------------------------
+# Test the example 2d "circle" geometry callback.
+#
+register_circle_geom db
+
+do_execsql_test rtreeE-1.1 {
+ PRAGMA page_size=512;
+ CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1);
+
+ /* A tight pattern of small boxes near 0,0 */
+ WITH RECURSIVE
+ x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+ y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+ INSERT INTO rt1 SELECT x+5*y, x, x+2, y, y+2 FROM x, y;
+
+ /* A looser pattern of small boxes near 100, 0 */
+ WITH RECURSIVE
+ x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+ y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+ INSERT INTO rt1 SELECT 100+x+5*y, x*3+100, x*3+102, y*3, y*3+2 FROM x, y;
+
+ /* A looser pattern of larger boxes near 0, 200 */
+ WITH RECURSIVE
+ x(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM x WHERE x<4),
+ y(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM y WHERE y<4)
+ INSERT INTO rt1 SELECT 200+x+5*y, x*7, x*7+15, y*7+200, y*7+215 FROM x, y;
+} {}
+
+# Queries against each of the three clusters */
+do_execsql_test rtreeE-1.1 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 0.0, 50.0, 3) ORDER BY id;
+} {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}
+do_execsql_test rtreeE-1.2 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(100.0, 0.0, 50.0, 3) ORDER BY id;
+} {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}
+do_execsql_test rtreeE-1.3 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(0.0, 200.0, 50.0, 3) ORDER BY id;
+} {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}
+
+# The Qcircle geometry function gives a lower score to larger leaf-nodes.
+# This causes the 200s to sort before the 100s and the 0s to sort before
+# last.
+#
+do_execsql_test rtreeE-1.4 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,3) AND id%100==0
+} {200 100 0}
+
+# Exclude odd rowids on a depth-first search
+do_execsql_test rtreeE-1.5 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,4) ORDER BY +id
+} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
+
+# Exclude odd rowids on a breadth-first search.
+do_execsql_test rtreeE-1.6 {
+ SELECT id FROM rt1 WHERE id MATCH Qcircle(0,0,1000,5) ORDER BY +id
+} {0 2 4 6 8 10 12 14 16 18 20 22 24 100 102 104 106 108 110 112 114 116 118 120 122 124 200 202 204 206 208 210 212 214 216 218 220 222 224}
+
+# Construct a large 2-D RTree with thousands of random entries.
+#
+do_test rtreeE-2.1 {
+ db eval {
+ CREATE TABLE t2(id,x0,x1,y0,y1);
+ CREATE VIRTUAL TABLE rt2 USING rtree(id,x0,x1,y0,y1);
+ BEGIN;
+ }
+ expr srand(0)
+ for {set i 1} {$i<=10000} {incr i} {
+ set dx [expr {int(rand()*40)+1}]
+ set dy [expr {int(rand()*40)+1}]
+ set x0 [expr {int(rand()*(10000 - $dx))}]
+ set x1 [expr {$x0+$dx}]
+ set y0 [expr {int(rand()*(10000 - $dy))}]
+ set y1 [expr {$y0+$dy}]
+ set id [expr {$i+10000}]
+ db eval {INSERT INTO t2 VALUES($id,$x0,$x1,$y0,$y1)}
+ }
+ db eval {
+ INSERT INTO rt2 SELECT * FROM t2;
+ COMMIT;
+ }
+} {}
+
+for {set i 1} {$i<=200} {incr i} {
+ set dx [expr {int(rand()*100)}]
+ set dy [expr {int(rand()*100)}]
+ set x0 [expr {int(rand()*(10000 - $dx))}]
+ set x1 [expr {$x0+$dx}]
+ set y0 [expr {int(rand()*(10000 - $dy))}]
+ set y1 [expr {$y0+$dy}]
+ set ans [db eval {SELECT id FROM t2 WHERE x1>=$x0 AND x0<=$x1 AND y1>=$y0 AND y0<=$y1 ORDER BY id}]
+ do_execsql_test rtreeE-2.2.$i {
+ SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ORDER BY id
+ } $ans
+}
+
+# Run query that have very deep priority queues
+#
+set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=5000 AND y1>=0 AND y0<=5000 ORDER BY id}]
+do_execsql_test rtreeE-2.3 {
+ SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,5000,0,5000) ORDER BY id
+} $ans
+set ans [db eval {SELECT id FROM t2 WHERE x1>=0 AND x0<=10000 AND y1>=0 AND y0<=10000 ORDER BY id}]
+do_execsql_test rtreeE-2.4 {
+ SELECT id FROM rt2 WHERE id MATCH breadthfirstsearch(0,10000,0,10000) ORDER BY id
+} $ans
+
+finish_test
diff --git a/ext/rtree/sqlite3rtree.h b/ext/rtree/sqlite3rtree.h
index c849091..5de0508 100644
--- a/ext/rtree/sqlite3rtree.h
+++ b/ext/rtree/sqlite3rtree.h
@@ -21,6 +21,16 @@ extern "C" {
#endif
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
+typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
+
+/* The double-precision datatype used by RTree depends on the
+** SQLITE_RTREE_INT_ONLY compile-time option.
+*/
+#ifdef SQLITE_RTREE_INT_ONLY
+ typedef sqlite3_int64 sqlite3_rtree_dbl;
+#else
+ typedef double sqlite3_rtree_dbl;
+#endif
/*
** Register a geometry callback named zGeom that can be used as part of an
@@ -31,11 +41,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
int sqlite3_rtree_geometry_callback(
sqlite3 *db,
const char *zGeom,
-#ifdef SQLITE_RTREE_INT_ONLY
- int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes),
-#else
- int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes),
-#endif
+ int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
void *pContext
);
@@ -47,11 +53,60 @@ int sqlite3_rtree_geometry_callback(
struct sqlite3_rtree_geometry {
void *pContext; /* Copy of pContext passed to s_r_g_c() */
int nParam; /* Size of array aParam[] */
- double *aParam; /* Parameters passed to SQL geom function */
+ sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
void *pUser; /* Callback implementation user data */
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
};
+/*
+** Register a 2nd-generation geometry callback named zScore that can be
+** used as part of an R-Tree geometry query as follows:
+**
+** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
+*/
+int sqlite3_rtree_query_callback(
+ sqlite3 *db,
+ const char *zQueryFunc,
+ int (*xQueryFunc)(sqlite3_rtree_query_info*),
+ void *pContext,
+ void (*xDestructor)(void*)
+);
+
+
+/*
+** A pointer to a structure of the following type is passed as the
+** argument to scored geometry callback registered using
+** sqlite3_rtree_query_callback().
+**
+** Note that the first 5 fields of this structure are identical to
+** sqlite3_rtree_geometry. This structure is a subclass of
+** sqlite3_rtree_geometry.
+*/
+struct sqlite3_rtree_query_info {
+ void *pContext; /* pContext from when function registered */
+ int nParam; /* Number of function parameters */
+ sqlite3_rtree_dbl *aParam; /* value of function parameters */
+ void *pUser; /* callback can use this, if desired */
+ void (*xDelUser)(void*); /* function to free pUser */
+ sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
+ unsigned int *anQueue; /* Number of pending entries in the queue */
+ int nCoord; /* Number of coordinates */
+ int iLevel; /* Level of current node or entry */
+ int mxLevel; /* The largest iLevel value in the tree */
+ sqlite3_int64 iRowid; /* Rowid for current entry */
+ sqlite3_rtree_dbl rParentScore; /* Score of parent node */
+ int eParentWithin; /* Visibility of parent node */
+ int eWithin; /* OUT: Visiblity */
+ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
+};
+
+/*
+** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
+*/
+#define NOT_WITHIN 0 /* Object completely outside of query region */
+#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
+#define FULLY_WITHIN 2 /* Object fully contained within query region */
+
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
diff --git a/magic.txt b/magic.txt
index a2ca209..6562268 100644
--- a/magic.txt
+++ b/magic.txt
@@ -19,10 +19,14 @@
# compatibility only.
#
0 string =SQLite\ format\ 3
->68 belong =0x0f055111 Fossil repository -
>68 belong =0x0f055112 Fossil checkout -
>68 belong =0x0f055113 Fossil global configuration -
+>68 belong =0x0f055111 Fossil repository -
>68 belong =0x42654462 Bentley Systems BeSQLite Database -
>68 belong =0x42654c6e Bentley Systems Localization File -
>60 belong =0x5f4d544e Monotone source repository -
+>68 belong =0x47504b47 OGC GeoPackage file -
+>68 belong =0x47503130 OGC GeoPackage version 1.0 file -
+>68 belong =0x45737269 Esri Spatially-Enabled Database -
+>68 belong =0x4d504258 MBTiles tileset -
>0 string =SQLite SQLite3 database
diff --git a/main.mk b/main.mk
index 912ebb4..718cf5c 100644
--- a/main.mk
+++ b/main.mk
@@ -50,7 +50,8 @@ TCCX += -I$(TOP)/ext/async
# Object files for the SQLite library.
#
-LIBOBJ+= alter.o analyze.o attach.o auth.o \
+LIBOBJ+= vdbe.o parse.o \
+ alter.o analyze.o attach.o auth.o \
backup.o bitvec.o btmutex.o btree.o build.o \
callback.o complete.o ctime.o date.o delete.o expr.o fault.o fkey.o \
fts3.o fts3_aux.o fts3_expr.o fts3_hash.o fts3_icu.o fts3_porter.o \
@@ -63,11 +64,11 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
memjournal.o \
mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
notify.o opcodes.o os.o os_unix.o os_win.o \
- pager.o parse.o pcache.o pcache1.o pragma.o prepare.o printf.o \
+ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
random.o resolve.o rowset.o rtree.o select.o status.o \
table.o tokenize.o trigger.o \
update.o util.o vacuum.o \
- vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
+ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
vdbetrace.o wal.o walker.o where.o utf.o vtab.o
@@ -120,8 +121,10 @@ SRC = \
$(TOP)/src/os.c \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
$(TOP)/src/os_unix.c \
$(TOP)/src/os_win.c \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.c \
$(TOP)/src/pager.h \
$(TOP)/src/parse.y \
@@ -162,7 +165,8 @@ SRC = \
$(TOP)/src/wal.c \
$(TOP)/src/wal.h \
$(TOP)/src/walker.c \
- $(TOP)/src/where.c
+ $(TOP)/src/where.c \
+ $(TOP)/src/whereInt.h
# Source code for extensions
#
@@ -206,6 +210,7 @@ SRC += \
$(TOP)/ext/icu/sqliteicu.h \
$(TOP)/ext/icu/icu.c
SRC += \
+ $(TOP)/ext/rtree/sqlite3rtree.h \
$(TOP)/ext/rtree/rtree.h \
$(TOP)/ext/rtree/rtree.c
@@ -272,12 +277,16 @@ TESTSRC = \
TESTSRC += \
$(TOP)/ext/misc/amatch.c \
$(TOP)/ext/misc/closure.c \
+ $(TOP)/ext/misc/fileio.c \
$(TOP)/ext/misc/fuzzer.c \
$(TOP)/ext/misc/ieee754.c \
$(TOP)/ext/misc/nextchar.c \
+ $(TOP)/ext/misc/percentile.c \
$(TOP)/ext/misc/regexp.c \
$(TOP)/ext/misc/spellfix.c \
- $(TOP)/ext/misc/wholenumber.c
+ $(TOP)/ext/misc/totype.c \
+ $(TOP)/ext/misc/wholenumber.c \
+ $(TOP)/ext/misc/vfslog.c
#TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c
@@ -334,6 +343,8 @@ HDR = \
opcodes.h \
$(TOP)/src/os.h \
$(TOP)/src/os_common.h \
+ $(TOP)/src/os_setup.h \
+ $(TOP)/src/os_win.h \
$(TOP)/src/pager.h \
$(TOP)/src/pcache.h \
parse.h \
@@ -342,7 +353,8 @@ HDR = \
$(TOP)/src/sqliteInt.h \
$(TOP)/src/sqliteLimit.h \
$(TOP)/src/vdbe.h \
- $(TOP)/src/vdbeInt.h
+ $(TOP)/src/vdbeInt.h \
+ $(TOP)/src/whereInt.h
# Header files used by extensions
#
@@ -383,7 +395,7 @@ mptester$(EXE): sqlite3.c $(TOP)/mptest/mptest.c
$(TLIBS) $(THREADLIB)
sqlite3.o: sqlite3.c
- $(TCCX) -c sqlite3.c
+ $(TCCX) -I. -c sqlite3.c
# This target creates a directory named "tsrc" and fills it with
# copies of all of the C source code and header files needed to
@@ -396,7 +408,7 @@ target_source: $(SRC) $(TOP)/tool/vdbe-compress.tcl
mkdir tsrc
cp -f $(SRC) tsrc
rm tsrc/sqlite.h.in tsrc/parse.y
- tclsh $(TOP)/tool/vdbe-compress.tcl <tsrc/vdbe.c >vdbe.new
+ tclsh $(TOP)/tool/vdbe-compress.tcl $(OPTS) <tsrc/vdbe.c >vdbe.new
mv vdbe.new tsrc/vdbe.c
touch target_source
@@ -470,7 +482,7 @@ parse.c: $(TOP)/src/parse.y lemon $(TOP)/addopcodes.awk
mv parse.h parse.h.temp
$(NAWK) -f $(TOP)/addopcodes.awk parse.h.temp >parse.h
-sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION
+sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest.uuid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h
tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h
keywordhash.h: $(TOP)/tool/mkkeywordhash.c
@@ -592,6 +604,9 @@ soaktest: testfixture$(EXE) sqlite3$(EXE)
fulltestonly: testfixture$(EXE) sqlite3$(EXE)
./testfixture$(EXE) $(TOP)/test/full.test
+queryplantest: testfixture$(EXE) sqlite3$(EXE)
+ ./testfixture$(EXE) $(TOP)/test/permutations.test queryplanner
+
test: testfixture$(EXE) sqlite3$(EXE)
./testfixture$(EXE) $(TOP)/test/veryquick.test
@@ -613,6 +628,40 @@ $(TEST_EXTENSION): $(TOP)/src/test_loadext.c
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
./testfixture$(EXE) $(TOP)/test/loadext.test
+showdb$(EXE): $(TOP)/tool/showdb.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showdb$(EXE) \
+ $(TOP)/tool/showdb.c sqlite3.o $(THREADLIB)
+
+showstat4$(EXE): $(TOP)/tool/showstat4.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showstat4$(EXE) \
+ $(TOP)/tool/showstat4.c sqlite3.o $(THREADLIB)
+
+showjournal$(EXE): $(TOP)/tool/showjournal.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showjournal$(EXE) \
+ $(TOP)/tool/showjournal.c sqlite3.o $(THREADLIB)
+
+showwal$(EXE): $(TOP)/tool/showwal.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showwal$(EXE) \
+ $(TOP)/tool/showwal.c sqlite3.o $(THREADLIB)
+
+fts3view$(EXE): $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o fts3view$(EXE) \
+ $(TOP)/ext/fts3/tool/fts3view.c sqlite3.o $(THREADLIB)
+
+rollback-test$(EXE): $(TOP)/tool/rollback-test.c sqlite3.o
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o rollback-test$(EXE) \
+ $(TOP)/tool/rollback-test.c sqlite3.o $(THREADLIB)
+
+LogEst$(EXE): $(TOP)/tool/logest.c sqlite3.h
+ $(TCC) -o LogEst$(EXE) $(TOP)/tool/logest.c
+
+wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
+ $(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \
+ $(TOP)/test/wordcount.c sqlite3.c
+
+speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o
+ $(TCC) -I. -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB)
+
# This target will fail if the SQLite amalgamation contains any exported
# symbols that do not begin with "sqlite3_". It is run as part of the
# releasetest.tcl script.
@@ -620,6 +669,11 @@ extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
checksymbols: sqlite3.o
nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0
+# Build the amalgamation-autoconf package.
+#
+dist: sqlite3.c
+ TOP=$(TOP) sh $(TOP)/tool/mkautoconfamal.sh
+
# Standard install and cleanup targets
#
@@ -641,7 +695,16 @@ clean:
rm -f fts3-testfixture fts3-testfixture.exe
rm -f testfixture testfixture.exe
rm -f threadtest3 threadtest3.exe
- rm -f sqlite3.c fts?amal.c tclsqlite3.c
+ rm -f LogEst LogEst.exe
+ rm -f fts3view fts3view.exe
+ rm -f rollback-test rollback-test.exe
+ rm -f showdb showdb.exe
+ rm -f showjournal showjournal.exe
+ rm -f showstat4 showstat4.exe
+ rm -f showwal showwal.exe
+ rm -f speedtest1 speedtest1.exe
+ rm -f wordcount wordcount.exe
+ rm -f sqlite3.c sqlite3-*.c fts?amal.c tclsqlite3.c
rm -f sqlite3rc.h
rm -f shell.c sqlite3ext.h
rm -f sqlite3_analyzer sqlite3_analyzer.exe sqlite3_analyzer.c
diff --git a/manifest b/manifest
index 5b3b906..28b547a 100644
--- a/manifest
+++ b/manifest
@@ -1,22 +1,45 @@
-C Version\s3.7.17
-D 2013-05-20T00:56:22.515
+C Version\s3.8.6
+D 2014-08-15T11:46:33.931
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
-F Makefile.in f6b58b7bdf6535f0f0620c486dd59aa4662c0b4f
+F Makefile.in 5eb79e334a5de69c87740edd56af6527dd219308
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
-F Makefile.msc 5dc042f51187414d5886ac6d8308630d484690c4
-F Makefile.vxworks db21ed42a01d5740e656b16f92cb5d8d5e5dd315
-F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
-F VERSION 05c7bd63b96f31cfdef5c766ed91307ac121f5aa
+F Makefile.msc 5b04e657cf08a9ac7fc47d876c5c8be962c47d6b
+F Makefile.vxworks 034289efa9d591b04b1a73598623119c306cbba0
+F README.md 64f270c43c38c46de749e419c22f0ae2f4499fe8
+F VERSION 1c877615a9db323e3cd301e3d57d853f9d5c4a07
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
-F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
+F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
+F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
+F autoconf/Makefile.am 8fc2972d92769cf20ab8e4a73ea901b84d69bf44
+F autoconf/README 14458f1046c118efa721aadec5f227e876d3cd38
+F autoconf/README.first 47dd53221023b18c836ab00dba6e00bd86132453
+F autoconf/config.guess 94cc57e2a3fdb9c235b362ace86d77e89d188cad x
+F autoconf/config.sub 1efb390a8fb4bfafd74783a15a8fb5311c84300e x
+F autoconf/configure.ac ba3e99ba1a8171d0682b68443517088fc5d6f13a
+F autoconf/depcomp 0b26f101e3bc9fd1ff0be1da9fb4a82371142f92 x
+F autoconf/install-sh 06ee6336e63bb845c8439d777c32eb2eccc4fbf1 x
+F autoconf/ltmain.sh 7a658a24028f02331c1d2446562758083c5eadd1
+F autoconf/missing d7c9981a81af13370d4ed152b24c0a82b7028585 x
+F autoconf/tea/Makefile.in 5c3b0bdfb66c20d55ebff59d1718864461570ca9
+F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
+F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
+F autoconf/tea/configure.in e0466b881b53f31f5a4a69e7a91ad130902fb359
+F autoconf/tea/doc/sqlite3.n e1fe45d4f5286ee3d0ccc877aca2a0def488e9bb
+F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
+F autoconf/tea/pkgIndex.tcl.in 3ef61715cf1c7bdcff56947ffadb26bc991ca39d
+F autoconf/tea/tclconfig/install-sh bdd5e293591621ae60d9824d86a4b1c5f22c3d00
+F autoconf/tea/tclconfig/tcl.m4 f035b86539a5ab30689e997a11ae9e7fd2e65570
+F autoconf/tea/win/makefile.vc f89d0184d0eee5f7e356ea407964dcd139939928
+F autoconf/tea/win/nmakehlp.c 2070e086f39866b353a482d3a14dedaf26196506
+F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 0921066a13130082764ab4ab6456f7b5bebe56de
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
-F configure 8bb8bd13d3c918c4c1c73480930e81f955ac298a x
-F configure.ac 81c43d151d0b0e406be056394cc9ff4cb3fd0444
+F configure 513f1e2464c3673bcdb5471b13d98e7eeb6f5ca2 x
+F configure.ac 4cf9f60785143fa141b10962ccc885d973792e9a
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
@@ -28,98 +51,104 @@ F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
-F ext/fts1/fts1.c 3e7b253e61aab0bb1fea808c7a0ce36c19432acc
+F ext/fts1/fts1.c f7739dc37500a613cc0dab8fc04d1b5577d02998
F ext/fts1/fts1.h 6060b8f62c1d925ea8356cb1a6598073eb9159a6
F ext/fts1/fts1_hash.c 3196cee866edbebb1c0521e21672e6d599965114
F ext/fts1/fts1_hash.h e7f0d761353996a8175eda351104acfde23afcb0
F ext/fts1/fts1_porter.c b1c7304b8988ba3f764a147cdd32043b4913ea7b
F ext/fts1/fts1_tokenizer.h fdea722c38a9f82ed921642981234f666e47919c
F ext/fts1/fts1_tokenizer1.c fd00d1fe4dc30dfc5c64cba695ce34f4af20d2fa
-F ext/fts1/fulltext.c d935e600d87bc86b7d64f55c7520ea41d6034c5c
+F ext/fts1/fulltext.c 37698e1909584f6d8ea67d1485e3ad39dbf42d19
F ext/fts1/fulltext.h 08525a47852d1d62a0be81d3fc3fe2d23b094efd
F ext/fts1/simple_tokenizer.c 1844d72f7194c3fd3d7e4173053911bf0661b70d
F ext/fts1/tokenizer.h 0c53421b832366d20d720d21ea3e1f6e66a36ef9
F ext/fts2/README.tokenizers 21e3684ea5a095b55d70f6878b4ce6af5932dfb7
F ext/fts2/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts2/fts2.c b48cc0bb657c0a421f4067b79aa0354bd16a046d
+F ext/fts2/fts2.c 72c816a9ae448049fbbe8f18a85698765fc7956c
F ext/fts2/fts2.h da5f76c65163301d1068a971fd32f4119e3c95fa
-F ext/fts2/fts2_hash.c 2689e42e1107ea67207f725cf69cf8972d00cf93
+F ext/fts2/fts2_hash.c 011a1d32de45bb1b519a1fd0048e857d6a843558
F ext/fts2/fts2_hash.h 1824b99dfd8d0225facbdb26a2c87289b2e7dcf8
F ext/fts2/fts2_icu.c 51c5cd3c04954badd329fa738c95fcdb717b5188
-F ext/fts2/fts2_porter.c 747056987951f743e955c8479f1df21a565720fe
-F ext/fts2/fts2_tokenizer.c a86d08c9634fabfa237c8f379008de2e11248d36
+F ext/fts2/fts2_porter.c 2cd4a507bf3c3085fe66f59b0f2a325f65aaacf5
+F ext/fts2/fts2_tokenizer.c 3dbe8058e97afb55fff3ea844120ce3208b114cc
F ext/fts2/fts2_tokenizer.h 27a1a99ca2d615cf7e142839b8d79e8751b4529e
-F ext/fts2/fts2_tokenizer1.c 0123d21078e053bd98fd6186c5c6dc6d67969f2e
+F ext/fts2/fts2_tokenizer1.c 07e223eecb483d448313b5f1553a4f299a7fb7a1
F ext/fts2/mkfts2amal.tcl 974d5d438cb3f7c4a652639262f82418c1e4cff0
F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
-F ext/fts3/fts3.c 4bc160e6ff9ab5456b600f389f8941485ea5082f
+F ext/fts3/fts3.c 2f5e925bdb9d6d3e488c5a981af60cad4f9cdfe7
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h 0b167bed9e63151635620a4f639bc62ac6012cba
-F ext/fts3/fts3_aux.c b02632f6dd0e375ce97870206d914ea6d8df5ccd
-F ext/fts3/fts3_expr.c 193d6fc156d744ab548a2ed06c31869e54dac739
-F ext/fts3/fts3_hash.c 8dd2d06b66c72c628c2732555a32bc0943114914
+F ext/fts3/fts3Int.h 53d4eca1fb23eab00681fb028fb82eb5705c1e21
+F ext/fts3/fts3_aux.c 5c211e17a64885faeb16b9ba7772f9d5445c2365
+F ext/fts3/fts3_expr.c 351395fad6fcb16ecfc61db0861008a70101330c
+F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
-F ext/fts3/fts3_porter.c a465b49fcb8249a755792f87516eff182efa42b3
-F ext/fts3/fts3_snippet.c 5fcfcafff46a2a3a63b8e59fcb51987d01c74695
+F ext/fts3/fts3_porter.c 7f8b4bf5af7c0f20f73b8e87e14fa9298f52e290
+F ext/fts3/fts3_snippet.c 51beb5c1498176fd9caccaf1c75b55cb803a985a
F ext/fts3/fts3_term.c a521f75132f9a495bdca1bdd45949b3191c52763
-F ext/fts3/fts3_test.c f9a1a1702db1bfad3e2d0064746eeb808f125489
+F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038
F ext/fts3/fts3_tokenize_vtab.c 011170fe9eba5ff062f1a31d3188e00267716706
F ext/fts3/fts3_tokenizer.c bbdc731bc91338050675c6d1da9ab82147391e16
F ext/fts3/fts3_tokenizer.h 64c6ef6c5272c51ebe60fc607a896e84288fcbc3
F ext/fts3/fts3_tokenizer1.c 5c98225a53705e5ee34824087478cf477bdb7004
-F ext/fts3/fts3_unicode.c 92391b4b4fb043564c6539ea9b8661e3bcba47b9
-F ext/fts3/fts3_unicode2.c a863f05f758af36777dffc2facc898bc73fec896
-F ext/fts3/fts3_write.c d92c6cbe07363791cfe8b62b4dee67e6f8afc9e2
+F ext/fts3/fts3_unicode.c a93f5edc0aff44ef8b06d7cb55b52026541ca145
+F ext/fts3/fts3_unicode2.c c3d01968d497bd7001e7dc774ba75b372738c057
+F ext/fts3/fts3_write.c 8260388626516a7005d06a9dce94f9e55c6c2a41
F ext/fts3/fts3speed.tcl b54caf6a18d38174f1a6e84219950d85e98bb1e9
F ext/fts3/mkfts3amal.tcl 252ecb7fe6467854f2aa237bf2c390b74e71f100
-F ext/fts3/tool/fts3view.c 6cfc5b67a5f0e09c0d698f9fd012c784bfaa9197
+F ext/fts3/tool/fts3view.c 3986531f2fc0ceca0c89c31ec7d0589b6adb19d6
F ext/fts3/unicode/CaseFolding.txt 8c678ca52ecc95e16bc7afc2dbf6fc9ffa05db8c
F ext/fts3/unicode/UnicodeData.txt cd07314edb62d49fde34debdaf92fa2aa69011e7
-F ext/fts3/unicode/mkunicode.tcl 7a9bc018e2962abb79563c5a39fe581fcbf2f675
+F ext/fts3/unicode/mkunicode.tcl a2567f9d6ad6779879a2e394c120ad8718557e65
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
-F ext/icu/icu.c eb9ae1d79046bd7871aa97ee6da51eb770134b5a
+F ext/icu/icu.c d415ccf984defeb9df2c0e1afcfaa2f6dc05eacb
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
-F ext/misc/amatch.c eae8454cd9dcb287b2a3ec2e65a865a4ac5f0d06
-F ext/misc/closure.c 40788c54c59190a1f52f6492a260d8894a246fe9
-F ext/misc/fuzzer.c 51bd96960b6b077d41d6f3cedefbcb57f29efaa2
-F ext/misc/ieee754.c 2565ce373d842977efe0922dc50b8a41b3289556
-F ext/misc/nextchar.c 1131e2b36116ffc6fe6b2e3464bfdace27978b1e
-F ext/misc/regexp.c c25c65fe775f5d9801fb8573e36ebe73f2c0c2e0
+F ext/misc/amatch.c 678056a4bfcd83c4e82dea81d37543cd1d6dbee1
+F ext/misc/closure.c 636024302cde41b2bf0c542f81c40c624cfb7012
+F ext/misc/compress.c 76e45655f4046e756064ab10c62e18f2eb846b9f
+F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
+F ext/misc/fuzzer.c 136533c53cfce0957f0b48fa11dba27e21c5c01d
+F ext/misc/ieee754.c b0362167289170627659e84173f5d2e8fee8566e
+F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
+F ext/misc/percentile.c bcbee3c061b884eccb80e21651daaae8e1e43c63
+F ext/misc/regexp.c af92cdaa5058fcec1451e49becc7ba44dba023dc
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
-F ext/misc/spellfix.c 6d7ce6105a4b7729f6c44ccdf1ab7e80d9707c02
+F ext/misc/spellfix.c cb016c2dab951ffd7b819a7bc8a750ebd6c26c0f
+F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
+F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
+F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c 757abea591d4ff67c0ff4e8f9776aeda86b18c14
+F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
-F ext/rtree/rtree1.test cf679265ecafff494a768ac9c2f43a70915a6290
+F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
F ext/rtree/rtree3.test a494da55c30ee0bc9b01a91c80c81b387b22d2dc
F ext/rtree/rtree4.test c8fe384f60ebd49540a5fecc990041bf452eb6e0
F ext/rtree/rtree5.test 6a510494f12454bf57ef28f45bc7764ea279431e
-F ext/rtree/rtree6.test 3ff9113b4a872fa935309e3511cd9b7cdb4d2472
+F ext/rtree/rtree6.test 0cfbdf27ee086bf16a3da2c6f2d5b3d6473cb27e
F ext/rtree/rtree7.test 1fa710b9e6bf997a0c1a537b81be7bb6fded1971
-F ext/rtree/rtree8.test 9772e16da71e17e02bdebf0a5188590f289ab37d
+F ext/rtree/rtree8.test db79c812f9e4a11f9b1f3f9934007884610a713a
F ext/rtree/rtree9.test d86ebf08ff6328895613ed577dd8a2a37c472c34
F ext/rtree/rtreeA.test ace05e729a36e342d40cf94e9efc7b4723d9dcdf
-F ext/rtree/rtreeB.test 983e567b49b5dca165940f66b87e161aa30e82b2
+F ext/rtree/rtreeB.test c85f9ce78766c4e68b8b89fbf2979ee9cfa82b4e
+F ext/rtree/rtreeC.test df158dcc81f1a43ce7eef361af03c48ec91f1e06
+F ext/rtree/rtreeD.test 636630357638f5983701550b37f0f5867130d2ca
+F ext/rtree/rtreeE.test 388c1c8602c3ce55c15f03b509e9cf545fb7c41f
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
F ext/rtree/rtree_util.tcl 06aab2ed5b826545bf215fff90ecb9255a8647ea
-F ext/rtree/sqlite3rtree.h c34c1e41d1ab80bb8ad09aae402c9c956871a765
+F ext/rtree/sqlite3rtree.h 83349d519fe5f518b3ea025d18dd1fe51b1684bd
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
-F magic.txt 3f820e18c43504b25da40ff4b4cdb66dc4c4907e
-F main.mk a8ebdf910e2cc10db1f9f54ec316f637458e8001
-F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
-F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
-F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
-F mkopcodec.awk f6fccee29e68493bfd90a2e0466ede5fa94dd2fc
-F mkopcodeh.awk 29b84656502eee5f444c3147f331ee686956ab0e
+F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
+F main.mk 036a65c9042b2f5013baf85c82629d700fe031c4
+F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
+F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -129,191 +158,201 @@ F mptest/mptest.c 499a74af4be293b7c1c7c3d40f332b67227dd739
F mptest/multiwrite01.test 499ad0310da8dff8e8f98d2e272fc2a8aa741b2e
F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
-F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
-F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
-F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
-F src/analyze.c d5f895810e8ff9737c9ec7b76abc3dcff5860335
-F src/attach.c 1816f5a9eea8d2010fc2b22b44f0f63eb3a62704
+F sqlite3.1 3d8b83c91651f53472ca17599dae3457b8b89494
+F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
+F src/alter.c b00900877f766f116f9e16116f1ccacdc21d82f1
+F src/analyze.c f98a351908da29f7b44741cfeb9eb20dda648ba0
+F src/attach.c 3801129015ef59d76bf23c95ef9b0069d18a0c52
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
-F src/backup.c b266767351ae2d847716c56fcb2a1fea7c761c03
+F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
-F src/btree.c fcfbe61a311e54224b23527bbf7586ce320e7b40
-F src/btree.h 6fa8a3ff2483d0bb64a9f0105a8cedeac9e00cca
-F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2
-F src/build.c 92ef9483189389828966153c5950f2e5a49c1182
-F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
+F src/btree.c fa057e30794bfd867963b44a3a42710a45c335a1
+F src/btree.h 4245a349bfe09611d7ff887dbc3a80cee8b7955a
+F src/btreeInt.h cf180d86b2e9e418f638d65baa425c4c69c0e0e3
+F src/build.c 5abf794fe8a605f2005b422e98a3cedad9b9ef5b
+F src/callback.c fcff28cf0df2403dd2f313bb8d1b8f31f6f3cd64
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
-F src/ctime.c 4262c227bc91cecc61ae37ed3a40f08069cfa267
-F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
-F src/delete.c aeabdabeeeaa0584127f291baa9617153d334778
-F src/expr.c e40d198a719aba1d2514f6e1541f9154f976ceb6
+F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
+F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
+F src/delete.c bcf8f72126cea80fc3d5bc5494cf19b3f8935aaf
+F src/expr.c f749009cf4a8534efb5e0d5cd7c9fb1fb0f2836c
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
-F src/fkey.c e16942bd5c8a868ac53287886464a5ed0e72b179
-F src/func.c d3fdcff9274bc161152e67ed3f626841c247f4b9
-F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
-F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
+F src/fkey.c 8545f3b36da47473e10800ea4fb0810fd4062514
+F src/func.c bbb724b74ed96ca42675a7274646a71dd52bcda7
+F src/global.c 1e4bd956dc2f608f87d2a929abc4a20db65f30e4
+F src/hash.c d139319967164f139c8d1bb8a11b14db9c4ba3cd
F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c f7cb141e8ce257cb6b15c497f09e4e23d6055599
+F src/insert.c 991e4964e9295da3993e2c0f81c7faf642371848
F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
-F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
+F src/legacy.c febc2a9e7ad6c1a6191c7b5b9170b325d263f343
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
-F src/loadext.c c48f7f3f170e502fe0cc20748e03c6e0b5a016c2
-F src/main.c c6419ef57392b1aa0cf6ed9c80dfc06b153fe299
-F src/malloc.c fe085aa851b666b7c375c1ff957643dc20a04bf6
+F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
+F src/main.c 1cf92c5c6468f2b6ed99b638706781ccc9c60b42
+F src/malloc.c 0203ebce9152c6a0e5de520140b8ba65187350be
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
-F src/mem1.c 437c7c4af964895d4650f29881df63535caaa1fa
-F src/mem2.c e307323e86b5da1853d7111b68fd6b84ad6f09cf
+F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
+F src/mem2.c dce31758da87ec2cfa52ba4c5df1aed6e07d8e8f
F src/mem3.c 61c9d47b792908c532ca3a62b999cf21795c6534
-F src/mem5.c c2c63b7067570b00bf33d751c39af24182316f7f
-F src/memjournal.c 41a598445c8f249bd9b26ecae700c60dcf34cbf3
-F src/mutex.c d3b66a569368015e0fcb1ac15f81c119f504d3bc
+F src/mem5.c 74670012946c4adc8a6ad84d03acc80959c3e529
+F src/memjournal.c 0683aac6cab6ec2b5374c0db37c0deb2436a3785
+F src/mutex.c 84a073c9a23a8d7bdd2ea832522d1730df18812c
F src/mutex.h 5bc526e19dccc412b7ff04642f6fdad3fdfdabea
-F src/mutex_noop.c 7682796b7d8d39bf1c138248858efcd10c9e1553
-F src/mutex_unix.c c3a4e00f96ba068a8dbef34084465979aaf369cc
-F src/mutex_w32.c 32a9b3841e2d757355f0012b860b1bc5e01eafa0
+F src/mutex_noop.c f3f09fd7a2eb4287cfc799753ffc30380e7b71a1
+F src/mutex_unix.c 1b10d5413dfc794364a8adf3eb3a192926b43fa3
+F src/mutex_w32.c c50939b72368f1cfbddb58520372081a50558548
F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
-F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be
-F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f
+F src/os.c 1b147e4cf7cc39e618115c14a086aed44bc91ace
+F src/os.h 60d419395e32a8029fa380a80a3da2e9030f635e
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
-F src/os_unix.c 75ce49309b8352c7173ce1ef6fc9e8d1f6daab10
-F src/os_win.c 32b8adca3e989565713ff74098b3cb2cb25d6e59
-F src/pager.c 49e23f9898113ddfe90942bdf1c1ef57955d0921
-F src/pager.h 5cb78b8e1adfd5451e600be7719f5a99d87ac3b1
-F src/parse.y 9708365594eea519cdc8504dee425c0a41c79502
-F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
+F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
+F src/os_unix.c bd7df3094a60915c148517504c76df4fca24e542
+F src/os_win.c 1c936c7b0659d0eb12b868e2cd710a570e78873e
+F src/os_win.h 057344a6720b4c8405d9bd98f58cb37a6ee46c25
+F src/pager.c f6bb1fa6cdf2062f2d8aec3e64db302bca519ab8
+F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428
+F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0
+F src/pcache.c d8eafac28290d4bb80332005435db44991d07fc2
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
-F src/pcache1.c 9fd22671c270b35131ef480bbc00392b8b5f8ab9
-F src/pragma.c 8779308bc1ea1901c4bc94dfe9a83d436f73f52c
-F src/prepare.c 743e484233c51109666d402f470523553b41797c
-F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f
-F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
-F src/resolve.c 89f9003e8316ee3a172795459efc2a0274e1d5a8
-F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
-F src/select.c a4641882279becc200f2680f55f3e89d4e7c7f78
-F src/shell.c 2109d54f67c815a100abd7dc6a6e25eddb3b97eb
-F src/sqlite.h.in 5a5a22a9b192d81a9e5dee00274e3a0484c4afb1
-F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
-F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5
-F src/sqliteInt.h 4cc782c9a89b3ddd663e7f68af3fa9e5af596f8b
+F src/pcache1.c 102e6f5a2fbc646154463eb856d1fd716867b64c
+F src/pragma.c d10ef67c4de79f78188b965b4b7988aff1d66f2e
+F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337
+F src/printf.c af06f66927919730f03479fed6ae9854f73419f4
+F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
+F src/resolve.c 0ea356d32a5e884add23d1b9b4e8736681dd5697
+F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
+F src/select.c ea48e891406ccdf748f3eb02893e056d134a0fea
+F src/shell.c 75bb7bd2c80bb44861598f322a417c4bafe98fd7
+F src/sqlite.h.in ed9d35990c61f0388ca6405706455c4095310553
+F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
+F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
+F src/sqliteInt.h 641f8fbb65ca2084c8df95b525f6f82c7a1e91ae
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
-F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
+F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
-F src/tclsqlite.c 2ecec9937e69bc17560ad886da35195daa7261b8
-F src/test1.c 045d45a4f7eeb5d963f8fc150339f1bad279011a
-F src/test2.c 7355101c085304b90024f2261e056cdff13c6c35
+F src/tclsqlite.c e87c99e28a145943666b51b212dacae35fcea0bd
+F src/test1.c 14409a611e9c27c6c522c610bbff5561f05c1558
+F src/test2.c 98049e51a17dc62606a99a9eb95ee477f9996712
F src/test3.c 1c0e5d6f080b8e33c1ce8b3078e7013fdbcd560c
F src/test4.c 9b32d22f5f150abe23c1830e2057c4037c45b3df
-F src/test5.c a6d1ac55ac054d0b2b8f37b5e655b6c92645a013
-F src/test6.c a437f76f9874d2563352a7e6cd0d43217663c220
-F src/test7.c 126b886b53f0358b92aba9b81d3fcbfbe9a93cd6
-F src/test8.c 7ee77ea522ae34aa691dfe407139dec80d4fc039
+F src/test5.c 5a34feec76d9b3a86aab30fd4f6cc9c48cbab4c1
+F src/test6.c 41cacf3b0dd180823919bf9e1fbab287c9266723
+F src/test7.c 72b732baa5642f795655ba1126ea032af46ecfd2
+F src/test8.c 54ccd7b1df5062f0ecbf50a8f7b618f8b1f13b20
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
F src/test_async.c 21e11293a2f72080eda70e1124e9102044531cd8
-F src/test_autoext.c 5c95b5d435eaa09d6c0e7d90371c5ca8cd567701
+F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 3875e899222b651e18b662f86e0e50daa946344e
-F src/test_btree.c 5b89601dcb42a33ba8b820a6b763cc9cb48bac16
-F src/test_config.c 95bb33e9dcaa340a296c0bf0e0ba3d1a1c8004c0
-F src/test_demovfs.c 20a4975127993f4959890016ae9ce5535a880094
+F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
+F src/test_config.c 42fb068a038c8684741522f551325228b1389e63
+F src/test_demovfs.c 69b2085076654ebc18014cbc6386f04409c959a9
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
-F src/test_fs.c 8f786bfd0ad48030cf2a06fb1f050e9c60a150d7
-F src/test_func.c 3a8dd37c08ab43b76d38eea2836e34a3897bf170
+F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
+F src/test_func.c d3013ce36f19ac72a99c73864930fd1fa41832f8
F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd
-F src/test_init.c 3cbad7ce525aec925f8fda2192d576d47f0d478a
+F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32
F src/test_intarray.c 87847c71c3c36889c0bcc9c4baf9d31881665d61
-F src/test_intarray.h b999bb18d090b8d9d9c49d36ec37ef8f341fe169
+F src/test_intarray.h 2ece66438cfd177b78d1bfda7a4180cd3a10844d
F src/test_journal.c f5c0a05b7b3d5930db769b5ee6c3766dc2221a64
-F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
-F src/test_malloc.c 2855429b8232107b3296409be2a8eb68345290c2
-F src/test_multiplex.c 5d691eeb6cb6aa7888da28eba5e62a9a857d3c0f
-F src/test_multiplex.h 9b63b95f07acedee425fdfe49a47197c9bf5f9d8
+F src/test_loadext.c a5251f956ab6af21e138dc1f9c0399394a510cb4
+F src/test_malloc.c 1ff5b1243d96124c9a180f3b89424820a1f337f3
+F src/test_multiplex.c ca90057438b63bf0840ebb84d0ef050624519a76
+F src/test_multiplex.h c08e4e8f8651f0c5e0509b138ff4d5b43ed1f5d3
F src/test_mutex.c 293042d623ebba969160f471a82aa1551626454f
F src/test_onefile.c 0396f220561f3b4eedc450cef26d40c593c69a25
-F src/test_osinst.c 90a845c8183013d80eccb1f29e8805608516edba
+F src/test_osinst.c 3d0340bc31a9f3d8a3547e0272373e80f78dde25
F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00
-F src/test_quota.c 30c64f0ef84734f2231a686df41ed882b0c59bc0
-F src/test_quota.h 8761e463b25e75ebc078bd67d70e39b9c817a0cb
-F src/test_rtree.c 1d764c352b5348bb2869ff5f54aff8eadcb96041
-F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
-F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f
+F src/test_quota.c 65f6348fec0f2b3020c907247fb47556b214abb9
+F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d
+F src/test_rtree.c fdd8d29ca5165c7857987a2ba263fac5c69e231f
+F src/test_schema.c cd12a2223c3a394f4d07bb93bdf6d344c5c121b6
+F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe
F src/test_sqllog.c c1c1bbedbcaf82b93d83e4f9dd990e62476a680e
-F src/test_stat.c d1569c7a4839f13e80187e2c26b2ab4da2d03935
+F src/test_stat.c 9898687a6c2beca733b0dd6fe19163d987826d31
F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
-F src/test_syscall.c 16dbe79fb320fadb5acd7a0a59f49e52ab2d2091
+F src/test_syscall.c 2e21ca7f7dc54a028f1967b63f1e76155c356f9b
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
-F src/test_vfs.c 8e6087a8b3dcc260282074b0efba14b76311120c
-F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
+F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c
+F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
-F src/tokenize.c 1e86210d3976717a19238ea7b047fac481fe8c12
-F src/trigger.c cd95ac64efa60e39faf9b5597443192ff27a22fa
-F src/update.c 4c0c6864c4349ba292042e984a56d15985b57f4e
-F src/utf.c 8d819e2e5104a430fc2005f018db14347c95a38f
-F src/util.c f566b5138099a2df8533b190d0dcc74b7dfbe0c9
-F src/vacuum.c ddf21cc9577c4cb459d08bee9863a78ec000c5bb
-F src/vdbe.c 5f0047130f80c7fd0bc41bc51a653b5542c4fbd5
-F src/vdbe.h b52887278cb173e66188da84dfab216bea61119d
-F src/vdbeInt.h c1e830268b75f04a2901dd895b51a637a26c7045
-F src/vdbeapi.c 085cf9bf169b859a6c8fa43791702bac805cb7aa
-F src/vdbeaux.c ecb43014bcd3019e5aa2b5561e5c3a447f007a08
-F src/vdbeblob.c 5dc79627775bd9a9b494dd956e26297946417d69
-F src/vdbemem.c 833005f1cbbf447289f1973dba2a0c2228c7b8ab
-F src/vdbesort.c 4fad64071ae120c25f39dcac572d716b9cadeb7f
-F src/vdbetrace.c 18cc59cb475e6115129bfde224367d13a35a7d13
-F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
-F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d
+F src/tokenize.c ae45399d6252b4d736af43bee1576ce7bff86aec
+F src/trigger.c 66f3470b03b52b395e839155786966e3e037fddb
+F src/update.c ea336ce7b8b3fc5e316ba8f082e6445babf81059
+F src/utf.c a0314e637768a030e6e84a957d0c4f6ba910cc05
+F src/util.c 3076bdd51cdbf60a6e2e57fada745be37133c73e
+F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
+F src/vdbe.c f7f4066e4d6e3858878d76ce9288ea603e12ddf6
+F src/vdbe.h c63fad052c9e7388d551e556e119c0bcf6bebdf8
+F src/vdbeInt.h f5513f2b5ac1e2c5128996c7ea23add256a301df
+F src/vdbeapi.c 24e40422382beb774daab11fe9fe9d37e8a04949
+F src/vdbeaux.c 25d62ef82cf1be2a1255eacac636fa0d943d8b3d
+F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
+F src/vdbemem.c d90a1e8acf8b63dc9d14cbbea12bfec6cec31394
+F src/vdbesort.c f7f5563bf7d4695ca8f3203f3bf9de96d04ed0b3
+F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
+F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
+F src/wal.c 264df50a1b33124130b23180ded2e2c5663c652a
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
-F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
-F src/where.c 5c4cbc1e5205d8d534c483fa4495c81513b45dea
+F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
+F src/where.c ab20f9c24a422ee8900831b343c3d1e5e7aca87b
+F src/whereInt.h 923820bee9726033a501a08d2fc69b9c1ee4feb3
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
-F test/all.test 52fc8dee494092031a556911d404ca30a749a30b
-F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748
+F test/all.test 6ff7b43c2b4b905c74dc4a813d201d0fa64c5783
+F test/alter.test 547dc2d292644301ac9a7dda22b319b74f9c08d2
F test/alter2.test 7ea05c7d92ac99349a802ef7ada17294dd647060
F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
-F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
+F test/alter4.test d6c011fa0d6227abba762498cafbb607c9609e93
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
-F test/analyze.test f8ab7d15858b4093b06caf5e57e2a5ff7104bdae
-F test/analyze3.test c3c7f6c3951900c188cf94b2d5ee3246d6b3ff89
-F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
-F test/analyze5.test 713354664c5ff1853ab2cbcb740f0cf5cb7c802e
-F test/analyze6.test aa8dae5066bbed35c5f45a507fb87f2d342f2c99
-F test/analyze7.test bd09e89264c664d8d8d2450b6866dcdfae080a13
-F test/analyze8.test 4ca170de2ba30ccb1af2c0406803db72262f9691
+F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
+F test/analyze.test 1772936d66471c65221e437b6d1999c3a03166c4
+F test/analyze3.test bf41f0f680dd1e0d44eed5e769531e93a5320275
+F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
+F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
+F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f
+F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
+F test/analyze8.test 093d15c1c888eed5034304a98c992f7360130b88
+F test/analyze9.test 3ef1b471247308e710a794b6e50a6ab536c5604b
+F test/analyzeA.test 1a5c40079894847976d983ca39c707aaa44b6944
+F test/analyzeB.test 8bf35ee0a548aea831bf56762cb8e7fdb1db083d
+F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
F test/async4.test 1787e3952128aa10238bf39945126de7ca23685a
-F test/async5.test 0dd8701bd588bf6e70c2557a22ae3f22b2567b4c
-F test/atof1.test 9bf1d25180a2e05fc12ce3940cc8003033642f68
+F test/async5.test 383ab533fdb9f7ad228cc99ee66e1acb34cc0dc0
+F test/atof1.test 08a61df9365c341f334a65f4348897312d8f3db7
F test/attach.test 0d112b7713611fdf0340260192749737135fda5f
-F test/attach2.test e54436ed956d3d88bdee61221da59bf3935a0966
-F test/attach3.test d89ccfe4fe6e2b5e368d480fcdfe4b496c54cf4e
+F test/attach2.test 0ec5defa340363de6cd50fd595046465e9aaba2d
+F test/attach3.test 359eb65d00102cdfcef6fa4e81dc1648f8f80b27
F test/attach4.test 53bf502f17647c6d6c5add46dda6bac8b6f4665c
F test/attachmalloc.test 3a4bfca9545bfe906a8d2e622de10fbac5b711b0
-F test/auth.test 4a4c3b034fff7750513520defa910f376c96ab49
-F test/auth2.test a2a371aa6df15f8b0c8109b33d3d7f0f73e4c9aa
+F test/auth.test 5bdf154eb28c0e4bbc0473f335858c0d96171768
+F test/auth2.test c3b415b76c033bedb81292118fb7c01f5f10cbcd
F test/auth3.test a4755e6a2a2fea547ffe63c874eb569e60a28eb5
-F test/autoinc.test bd30d372d00045252f6c2e41b5f41455e1975acf
-F test/autoindex1.test f88146c4c889ea0afbb620e49d83b5fbf5ee4d06
-F test/autovacuum.test 9f22a7733f39c56ef6a5665d10145ac25d8cb574
+F test/autoinc.test c58912526998a39e11f66b533e23cfabea7f25b7
+F test/autoindex1.test 762ff3f8e25d852aae55c6462ca166a80c0cde61
+F test/autoindex2.test 60d2fc6f38364308ce73a9beb01b47ded38697de
+F test/autoindex3.test 8254f689c3241081fad52b7bea18ba53e07e14a2
+F test/autovacuum.test 941892505d2c0f410a0cb5970dfa1c7c4e5f6e74
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85
-F test/backcompat.test ecd841f3a3bfb81518721879cc56a760670e3198
+F test/backcompat.test 19a1f337c68419b020a7481dd272a472c4ad8ef4
F test/backup.test c9cdd23a495864b9edf75a9fa66f5cb7e10fcf62
F test/backup2.test 34986ef926ea522911a51dfdb2f8e99b7b75ebcf
-F test/backup4.test 4d90389daeb781fe718816a4fc836ad1b06791d8
+F test/backup4.test 2a2e4a64388090b152de753fd9e123f28f6a3bd4
F test/backup_ioerr.test 4c3c7147cee85b024ecf6e150e090c32fdbb5135
F test/backup_malloc.test 7162d604ec2b4683c4b3799a48657fb8b5e2d450
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/bc_common.tcl 5c8689cc6d2fb44b7c0968ae4f85eb26d50022fa
-F test/between.test 16b1776c6323faadb097a52d673e8e3d8be7d070
+F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
-F test/bigfile2.test 7c79f1ef0c6c2c2bc1e7bd895596fab32bfb4796
+F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bind.test 3c7b320969000c441a70952b0b15938fbb66237c
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
@@ -323,28 +362,28 @@ F test/boundary1.tcl 6421b2d920d8b09539503a8673339d32f7609eb1
F test/boundary1.test 66d7f4706ccdb42d58eafdb081de07b0eb42d77b
F test/boundary2.tcl e34ef4e930cf1083150d4d2c603e146bd3b76bcb
F test/boundary2.test 9ae758d7dab7e882c8b6cc4a6a10278385bff8fa
-F test/boundary3.tcl 8901d6a503d0bf64251dd81cc74e5ad3add4b119
+F test/boundary3.tcl 23361e108a125dca9c4080c2feb884fe54d69243
F test/boundary3.test 56ef82096b4329aca2be74fa1e2b0f762ea0eb45
F test/boundary4.tcl 0bb4b1a94f4fc5ae59b79b9a2b7a140c405e2983
F test/boundary4.test 89e02fa66397b8a325d5eb102b5806f961f8ec4b
-F test/btreefault.test f52c593513bda80a506c848325c73c840590884d
+F test/btreefault.test c2bcb542685eea44621275cfedbd8a13f65201e3
F test/busy.test 76b4887f8b9160ba903c1ac22e8ff406ad6ae2f0
F test/cache.test 13bc046b26210471ca6f2889aceb1ea52dc717de
-F test/capi2.test e8b18cc61090b6e5e388f54d6b125d711d1b265a
-F test/capi3.test 56ab450125ead38846cbae7e5b6a216686c3cffa
+F test/capi2.test 011c16da245fdc0106a2785035de6b242c05e738
+F test/capi3.test 71bcf2fbd36a9732f617766dfd752552c8e491b5
F test/capi3b.test efb2b9cfd127efa84433cd7a2d72ce0454ae0dc4
-F test/capi3c.test 93d24621c9ff84da9da060f30431e0453db1cdb0
-F test/capi3d.test 17b57ca28be3e37e14c2ba8f787d292d84b724a1
-F test/capi3e.test f7408dda65c92b9056199fdc180f893015f83dde
+F test/capi3c.test a21869e4d50d5dbb7e566e328fc0bc7c2efa6a32
+F test/capi3d.test c84af0c49267f9c3fbf4c1c46aa647646023811e
+F test/capi3e.test 3d49c01ef2a1a55f41d73cba2b23b5059ec460fe
F test/cast.test 4c275cbdc8202d6f9c54a3596701719868ac7dc3
-F test/check.test 2eb93611139a7dfaed3be80067c7dc5ceb5fb287
-F test/close.test e37610d60e9c9b9979a981f3f50071d7dff28616
-F test/closure01.test dbb28f1ea9eeaf0a53ec5bc0fed352e479def8c7
+F test/check.test 5831ddb6f2c687782eaf2e1a07b6e17f24c4f763
+F test/close.test 340bd24cc58b16c6bc01967402755027c37eb815
+F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
-F test/collate1.test fd02c4d8afc71879c4bb952586389961a21fb0ce
-F test/collate2.test 04cebe4a033be319d6ddbb3bbc69464e01700b49
+F test/collate1.test 73b91005f264b7c403e2d63a6708d150679ac99a
+F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
F test/collate3.test 79558a286362cb9ed603c6fa543f1cda7f563f0f
-F test/collate4.test 031f7265c13308b724ba3c49f41cc04612bd92b1
+F test/collate4.test f04d5168685f2eef637ecfa2d4ddf8ec0d600177
F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868
@@ -353,23 +392,30 @@ F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/colmeta.test 087c42997754b8c648819832241daf724f813322
F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
-F test/conflict.test 0b3922d2304a14a47e3ccd61bbd6824327af659b
-F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e
-F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0
-F test/corrupt3.test 889d7cdb811800303aa722d7813fe8a4299cf726
-F test/corrupt4.test b963f9e01e0f92d15c76fb0747876fd4b96dc30a
-F test/corrupt5.test c23da7bfb20917cc7fdbb13ee25c7cc4e9fffeff
-F test/corrupt6.test 4e4161aef1f30b9f34582bb4142334b7f47eacae
-F test/corrupt7.test a90caf89c7d7cb7893ea4d92529bd0c129317ee4
-F test/corrupt8.test 48eb37ffb9a03bceada62219e2bd4c92f4b0cb75
-F test/corrupt9.test 959179e68dc0b7b99f424cf3e0381c86dcdd0112
-F test/corruptA.test fafa652aa585753be4f6b62ff0bb250266eaf7ce
-F test/corruptB.test 20d4a20cbed23958888c3e8995b424a47223d647
-F test/corruptC.test 62a767fe64acb1975f58cc6171192839c783edbb
-F test/corruptD.test 3b09903a2e2fe07ecafe775fea94177f8a4bb34f
-F test/corruptE.test d3a3d7e864a95978195741744dda4abfd8286018
-F test/corruptF.test 984b1706c9c0e4248141b056c21124612628d12e
-F test/count.test 454e1ce985c94d13efeac405ce54439f49336163
+F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8
+F test/conflict2.test 0d3af4fb534fa1bd020c79960bb56e4d52655f09
+F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b
+F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
+F test/corrupt.test 141c39ea650c1365e85a49e402fa05cb9617fb97
+F test/corrupt2.test f2064e0bf934124cc38868fd8badb8f0dd67b552
+F test/corrupt3.test 4b548d0bbe2933bc81d3f54099a05fc4d28aff18
+F test/corrupt4.test b99652079d542b21f4965f6248703b983e40fe80
+F test/corrupt5.test 8ead52af76006f3286e9396cb41898018ccea107
+F test/corrupt6.test 269548d19427ac554c830763b1c5ea54a0252f80
+F test/corrupt7.test 22cc644c2e76c9804bc844121267aa6f8f7c0ded
+F test/corrupt8.test 2399dfe40d2c0c63af86706e30f3e6302a8d0516
+F test/corrupt9.test 730a3db08d4ab9aa43392ea30d9c2b4879cbff85
+F test/corruptA.test 53e56dafd180addcdadb402244b8cb9771d2ba26
+F test/corruptB.test 73a8d6c0b9833697ecf16b63e3c5c05c945b5dec
+F test/corruptC.test 02405cf7ed0c1e989060e1aab6d02ffbc3906fbb
+F test/corruptD.test b3c205fac7952b1de645ce44bb02335cd9e3e040
+F test/corruptE.test 193b4ca4e927e77c1d5f4f56203ddc998432a7ee
+F test/corruptF.test be9fde98e4c93648f1ba52b74e5318edc8f59fe4
+F test/corruptG.test 1ab3bf97ee7bdba70e0ff3ba2320657df55d1804
+F test/corruptH.test 88ed71a086e13591c917aac6de32750e7c7281cb
+F test/corruptI.test 0afbba50bfae006094cc548b4605f521c1179502
+F test/cost.test 19d314526616ce4473eb4e4e450fcb94499ce318
+F test/count.test 42a251178e32f617eda33f76236a7f79825a50b5
F test/coveridxscan.test cdb47d01acc4a634a34fd25abe85189e0d0f1e62
F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
@@ -378,60 +424,63 @@ F test/crash4.test fe2821baf37168dc59dd733dcf7dba2a401487bc
F test/crash5.test 05dd3aa9dbb751a22d5cdaf22a9c49b6667aa219
F test/crash6.test 4c56f1e40d0291e1110790a99807aa875b1647ba
F test/crash7.test 1a194c4900a255258cf94b7fcbfd29536db572df
-F test/crash8.test 38767cb504bbe491de6be4a7006b154973a2309f
+F test/crash8.test 61442a9964ab6b124fc5254e4258b45747842e6f
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47
-F test/date.test f3228180c87bbe5d39c9397bf001c0095c3821b9
-F test/dbstatus.test 207e5b63fcb7b9c3bb8e1fdf38ebd4654ad0e54b
+F test/date.test 42973251b9429f2c41b77eb98a7b0b0ba2d3b2c0
+F test/dbstatus.test 8de104bb5606f19537d23cd553b41349b5ab1204
F test/dbstatus2.test 10418e62b3db5dca070f0c3eef3ea13946f339c2
-F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc
+F test/default.test 792c3c70836f1901e2a8cb34fa0880ed71e2c1a9
F test/delete.test a065b05d2ebf60fd16639c579a4adfb7c381c701
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
-F test/descidx1.test 533dcbda614b0463b0ea029527fd27e5a9ab2d66
+F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
-F test/distinct.test 84da1414b2e6887fffd5ed571311b344c5b082ce
+F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
-F test/e_createtable.test d4e17024b1831e7480f5736cf4e02516a5c90927
-F test/e_delete.test 89aa84d3d1bd284a0689ede04bce10226a5aeaa5
-F test/e_droptrigger.test afd5c4d27dec607f5997a66bf7e2498a082cb235
-F test/e_dropview.test 583411e470458c5d76148542cfb5a5fa84c8f93e
-F test/e_expr.test 5489424d3d9a452ac3701cdf4b680ae31a157894
-F test/e_fkey.test 79a985d95340c6072a884359426ce95cf33e656a
+F test/e_createtable.test 181653f6f45e3adde73f8686600ce5ad7515466b
+F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
+F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
+F test/e_dropview.test 0c9f7f60989164a70a67a9d9c26d1083bc808306
+F test/e_expr.test 8f5fdd7261e2d746813b0c6a1c0e34824ad3c5ad
+F test/e_fkey.test a1783fe1f759e1990e6a11adfcf0702dac4d0707
F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
-F test/e_insert.test d5331cc95e101af2508159fc98b6801631659ffe
-F test/e_reindex.test dfedfc32c5a282b0596c6537cbcd4217fbb1a216
+F test/e_insert.test 7b2fa9cd1456f83474d6c5d27db3abaeb8be2023
+F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589
F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
-F test/e_select.test d5af998a402740d8f0488158d22075df2b6f88fa
-F test/e_select2.test 5c3d3da19c7b3e90ae444579db2b70098599ab92
-F test/e_update.test 161d5dc6a3ed9dd08f5264d13e20735d7a89f00c
-F test/e_uri.test 8f2f56b29456a3f846276fa4e0993d4ef8a15b79
-F test/e_vacuum.test 331da289ae186656cf5f2eb27f577a89c0c221af
+F test/e_select.test 52692ff3849541e828ad4661fe3773a9b8711763
+F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
+F test/e_update.test 312cb8f5ccfe41515a6bb092f8ea562a9bd54d52
+F test/e_uri.test a2c92d80093a7efdcfbb11093651cbea87097b6b
+F test/e_vacuum.test 5bfbdc21b65c0abf24398d0ba31dc88d93ca77a9
F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
F test/enc3.test 90683ad0e6ea587b9d5542ca93568af9a9858c40
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
-F test/eqp.test 46aa946dd55c90635327898275d3e533d23a9845
-F test/errmsg.test 050717f1c6a5685de9c79f5f9f6b83d7c592f73a
+F test/eqp.test 85873fa5816c48915c82c4e74cb5c35a5b48160f
+F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
-F test/exclusive.test a1b324cb21834a490cd052d409d34789cfef57cb
-F test/exclusive2.test 881193eccec225cfed9d7f744b65e57f26adee25
+F test/exclusive.test c7ebbc756eacf544c108b15eed64d7d4e5f86b75
+F test/exclusive2.test 32798111aae78a5deec980eee383213f189df308
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
F test/exists.test 8f7b27b61c2fbe5822f0a1f899c715d14e416e30
F test/expr.test 67c9fd6f8f829e239dc8b0f4a08a73c08b09196d
-F test/fallocate.test b5d34437bd7ab01d41b1464b8117aefd4d32160e
+F test/extension01.test 00d13cec817f331a687a243e0e5a2d87b0e358c9
+F test/fallocate.test 3e979af17dfa7e5e9dda5eba1a696c04fa9d47f7
F test/filectrl.test 14fa712e42c4cb791e09dfd58a6a03efb47ef13a
-F test/filefmt.test dbee33e57818249cdffbbb7b13464635217beff1
-F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da
-F test/fkey2.test 06e0b4cc9e1b3271ae2ae6feeb19755468432111
-F test/fkey3.test 5ec899d12b13bcf1e9ef40eff7fb692fdb91392e
+F test/filefmt.test cb34663f126cbc2d358af552dcaf5c72769b0146
+F test/fkey1.test e1d1fa84cde579185ea01358436839703e415a5b
+F test/fkey2.test 32ca728bcb854feed72d1406ea375fe423eebff2
+F test/fkey3.test 76d475c80b84ee7a5d062e56ccb6ea68882e2b49
F test/fkey4.test 86446017011273aad8f9a99c1a65019e7bd9ca9d
-F test/fkey5.test 0bf64f2d19ad80433ca0b24edbf604a18b353d5f
-F test/fkey_malloc.test bb74c9cb8f8fceed03b58f8a7ef2df98520bbd51
+F test/fkey5.test 8a1fde4e7721ae00b05b3178888833726ca2df8d
+F test/fkey6.test abb59f866c1b44926fd02d1fdd217d831fe04f48
+F test/fkey7.test e31d0e71a41c1d29349a16448d6c420e2c53a8fc
+F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749
F test/format4.test 1f0cac8ff3895e9359ed87e41aaabee982a812eb
F test/fts-9fd058691.test 78b887e30ae6816df0e1fed6259de4b5a64ad33c
F test/fts1a.test 46090311f85da51bb33bd5ce84f7948359c6d8d7
@@ -470,13 +519,13 @@ F test/fts2r.test b154c30b63061d8725e320fba1a39e2201cadd5e
F test/fts2token.test d8070b241a15ff13592a9ae4a8b7c171af6f445a
F test/fts3.test 672a040ea57036fb4b6fdc09027c18d7d24ab654
F test/fts3_common.tcl 99cf6659b87c0f74f55963c2aea03b3a7d66ceb0
-F test/fts3aa.test 909d5f530d30a8e36b9328d67285eae6537c79c0
-F test/fts3ab.test 09aeaa162aee6513d9ff336b6932211008b9d1f9
+F test/fts3aa.test edd20ddbbc5b6015ab340abf2ca278ae11ec387d
+F test/fts3ab.test 7f6cf260ae80dda064023df8e8e503e9a412b91f
F test/fts3ac.test 636ed7486043055d4f126a0e385f2d5a82ebbf63
F test/fts3ad.test e40570cb6f74f059129ad48bcef3d7cbc20dda49
F test/fts3ae.test ce32a13b34b0260928e4213b4481acf801533bda
F test/fts3af.test d394978c534eabf22dd0837e718b913fd66b499c
-F test/fts3ag.test 0b7d303f61ae5d620c4efb5e825713ea34ff9441
+F test/fts3ag.test c003672a215124df7fc6000036d896f498b26b53
F test/fts3ah.test dc9f66c32c296f1bc8bcc4535126bddfeca62894
F test/fts3ai.test 24058fdc6e9e5102c1fd8459591b114b6a85d285
F test/fts3aj.test 0ed71e1dd9b03b843a857dc3eb9b15630e0104fc
@@ -484,102 +533,119 @@ F test/fts3ak.test bd14deafe9d1586e8e9bf032411026ac4f8c925d
F test/fts3al.test 07d64326e79bbdbab20ee87fc3328fbf01641c9f
F test/fts3am.test 218aa6ba0dfc50c7c16b2022aac5c6be593d08d8
F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
-F test/fts3ao.test e7b80272efcced57d1d087a9da5c690dd7c21fd9
-F test/fts3atoken.test fb398ab50aa232489e2a17f9b29d7ad3a3885f36
-F test/fts3auto.test 74315a7377403a57ba82a652a33704197fe1e4be
-F test/fts3aux1.test 03cec2dea379931c219dd4406817485caa69d1d8
+F test/fts3ao.test 3e4e3d5e75c076520341d0bdf4eb17c00e8cbde2
+F test/fts3atoken.test fca30fd86db9241d571c637751e9a8a2f50f1451
+F test/fts3auto.test b981fea19b132b4e6878f50d7c1f369b28f68eb9
+F test/fts3aux1.test f8f287a4a73f381f8fa15b6a70f36245f903d221
+F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba
F test/fts3b.test e93bbb653e52afde110ad53bbd793f14fe7a8984
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
F test/fts3comp1.test a0f5b16a2df44dd0b15751787130af2183167c0c
F test/fts3conf.test ee8500c86dd58ec075e8831a1e216a79989436de
-F test/fts3corrupt.test 7b0f91780ca36118d73324ec803187208ad33b32
+F test/fts3corrupt.test 2710b77983cc7789295ddbffea52c1d3b7506dbb
F test/fts3corrupt2.test 6d96efae2f8a6af3eeaf283aba437e6d0e5447ba
F test/fts3cov.test e0fb00d8b715ddae4a94c305992dfc3ef70353d7
-F test/fts3d.test bf640d79722b720fa1c81834c48cdaa45d531b1a
+F test/fts3d.test 95c17d1b67b33a5eac0bf5a0d11116a0c0ac7a3a
F test/fts3defer.test 0be4440b73a2e651fc1e472066686d6ada4b9963
-F test/fts3defer2.test 83f8744407b7663e36716a9066302d53d49ddf8b
+F test/fts3defer2.test e880e3b65bdf999f4746cdaefa65f14a98b9b724
+F test/fts3defer3.test dd53fc13223c6d8264a98244e9b19abd35ed71cd
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
-F test/fts3expr.test 5e745b2b6348499d9ef8d59015de3182072c564c
+F test/fts3expr.test 3401d47b229c4504424caf362cc4ff704cad4162
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
-F test/fts3expr3.test 1bfb762b53a794f990f3dffaae8bbea5736422f7
+F test/fts3expr3.test 9e91b8edbcb197bf2e92161aa7696446d96dce5f
+F test/fts3expr4.test 0713d94ab951ed88a8c3629a4889a48c55c4067c
F test/fts3fault.test cb72dccb0a3b9f730f16c5240f3fcb9303eb1660
F test/fts3fault2.test 3198eef2804deea7cac8403e771d9cbcb752d887
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
-F test/fts3malloc.test b86ea33db9e8c58c0c2f8027a9fcadaf6a1568be
-F test/fts3matchinfo.test ecb08f586d027eb03941bcfcded6cb9d8ccb3a66
-F test/fts3near.test 12895557870b0f9af7cc0be81a0171abb2d12f12
+F test/fts3join.test 53e66a0c21eb568580674a43b21c059acb26f499
+F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
+F test/fts3matchinfo.test ff423e73faab8fc6d7adeefedf74dd8e2b0b14e0
+F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905
F test/fts3prefix.test b36d4f00b128a51e7b386cc013a874246d9d7dc1
-F test/fts3prefix2.test 477ca96e67f60745b7ac931cfa6e9b080c562da5
-F test/fts3query.test ef79d31fdb355d094baec1c1b24b60439a1fb8a2
+F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce
+F test/fts3query.test 4fefd43ff24993bc2c9b2778f2bec0cc7629e7ed
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
-F test/fts3shared.test 8bb266521d7c5495c0ae522bb4d376ad5387d4a2
-F test/fts3snippet.test 8e956051221a34c7daeb504f023cb54d5fa5a8b2
-F test/fts3sort.test 95be0b19d7e41c44b29014f13ea8bddd495fd659
-F test/fts3tok1.test 4d9e7401679dc71f6b2f76416309b923210bfdbe
-F test/fts3tok_err.test 41e5413139c2ef536ffadfcd1584ee50b1665ec0
-F test/fts4aa.test 95f448fb02c4a976968b08d1b4ce134e720946ae
-F test/fts4check.test 66fa274cab2b615f2fb338b257713aba8fad88a8
-F test/fts4content.test 6efc53b4fd03cab167e6998d2b0b7d4b7d419ee6
+F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e
+F test/fts3snippet.test d524af6bcef4714e059ef559113dbdc924cd33d1
+F test/fts3sort.test ed34c716a11cc2009a35210e84ad5f9c102362ca
+F test/fts3tok1.test c551043de056b0b1582a54e878991f57bad074bc
+F test/fts3tok_err.test 52273cd193b9036282f7bacb43da78c6be87418d
+F test/fts3varint.test 752c08ed5d32c5d7dc211b056f4ed68a76b7e36e
+F test/fts4aa.test 0c3152322c7f0b548cc942ad763eaba0da87ccca
+F test/fts4check.test 74d77f6cdb768ac49df5afda575cef14ae3d239a
+F test/fts4content.test 2e7252557d6d24afa101d9ba1de710d6140e6d06
+F test/fts4docid.test e33c383cfbdff0284685604d256f347a18fdbf01
+F test/fts4growth.test df10fde9f47cf5c71861e63fd8efcd573c4f7e53
+F test/fts4growth2.test 2f063be1902a73cd087355837c52fed42ac11a5d
+F test/fts4incr.test 361960ed3550e781f3f313e17e2182ef9cefc0e9
F test/fts4langid.test 24a6e41063b416bbdf371ff6b4476fa41c194aa7
F test/fts4merge.test c424309743fdd203f8e56a1f1cd7872cd66cc0ee
F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7
-F test/fts4unicode.test 25ccad45896f8e50f6a694cff738a35f798cdb40
+F test/fts4merge4.test d895b1057a7798b67e03455d0fa50e9ea836c47b
+F test/fts4noti.test 524807f0c36d49deea7920cdd4cd687408b58849
+F test/fts4unicode.test 01ec3fe2a7c3cfff3b4c0581b83caa11b33efa36
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
-F test/func.test b0fc34fdc36897769651975a2b0a606312753643
+F test/func.test ae97561957aba6ca9e3a7b8a13aac41830d701ef
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
-F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
+F test/func3.test d202a7606d23f90988a664e88e268aed1087c11c
+F test/func4.test 6beacdfcb0e18c358e6c2dcacf1b65d1fa80955f
+F test/func5.test cdd224400bc3e48d891827cc913a57051a426fa4
F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74
-F test/fuzz.test 77fd50afc12847af50fcf1941679d90adebadde6
+F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
F test/fuzz2.test 207d0f9d06db3eaf47a6b7bfc835b8e2fc397167
-F test/fuzz3.test aec64345184d1662bd30e6a17851ff659d596dc5
+F test/fuzz3.test efd384b896c647b61a2c1848ba70d42aad60a7b3
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
-F test/fuzzer1.test 41bd5aa6ae0cf18d06342a4476e3cad98604ae48
+F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
-F test/hook.test 45cb22b940c3cc0af616ba7430f666e245711a48
+F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
+F test/hexlit.test f9ecde8145bfc2341573473256c74ae37a200497
+F test/hook.test 162d7cef7a2d2b04839fe14402934e6a1b79442f
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
-F test/in.test 5941096407d8c133b9eff15bd3e666624b6cbde3
+F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
-F test/in4.test 64f3cc1acde1b9161ccdd8e5bde3daefdb5b2617
+F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
F test/in5.test 99f9a40af01711b06d2d614ecfe96129f334fba3
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
-F test/incrblob2.test edc3a96e557bd61fb39acc8d2edd43371fbbaa19
-F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
-F test/incrblob4.test 09be37d3dd996a31ea6993bba7837ece549414a8
-F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
-F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
+F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
+F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
+F test/incrblob4.test f26502a5697893e5acea268c910f16478c2f0fab
+F test/incrblob_err.test af1f12ba60d220c9752073ff2bda2ad59e88960d
+F test/incrblobfault.test 280474078f6da9e732cd2a215d3d854969014b6e
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
-F test/incrvacuum3.test 2ffa9e4a23f072bd7902b9ae6471f8822a6522a7
+F test/incrvacuum3.test 75256fb1377e7c39ef2de62bfc42bbff67be295a
F test/incrvacuum_ioerr.test 6ae2f783424e47a0033304808fe27789cf93e635
-F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
+F test/index.test 4d990005a67a36984e4f1a5f1bdccea8d08da4ee
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
-F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
-F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
+F test/index3.test 55a90cff99834305e8141df7afaef39674b57062
+F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33
-F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad
+F test/index6.test fb370966ac3cd0989053dd5385757b5c3e24ab6a
+F test/index7.test a3baf9a625bda7fd49471e99aeae04095fbfeecf
+F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
-F test/insert.test 489aa12a027c83d291f5034a83c8c32e6be1dca2
+F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371
F test/insert2.test 4f3a04d168c728ed5ec2c88842e772606c7ce435
F test/insert3.test 1b7db95a03ad9c5013fdf7d6722b6cd66ee55e30
-F test/insert4.test 87f6798f31d60c4e177622fcc3663367e6ecbd90
+F test/insert4.test 4791662c50518bdd37d394cae9a7a8014e845bb3
F test/insert5.test 394f96728d1258f406fe5f5aeb0aaf29487c39a6
-F test/instr.test a34e1d46a9eefb098a7167ef0e730a4a3d82fba0
+F test/instr.test 737bbf80685232033f3abedc6ae92f75860b5dd2
F test/intarray.test 066b7d7ac38d25bf96f87f1b017bfc687551cdd4
F test/interrupt.test dfe9a67a94b0b2d8f70545ba1a6cca10780d71cc
-F test/intpkey.test 7af30f6ae852d8d1c2b70e4bf1551946742e92d8
-F test/io.test ecf44cc81664ad54d8253e2d88fc705b6554abe3
-F test/ioerr.test 40bb2cfcab63fb6aa7424cd97812a84bc16b5fb8
+F test/intpkey.test 7506090fc08e028712a8bf47e5f54111947e3844
+F test/io.test 3a7abcef18727cc0f2399e04b0e8903eccae50f8
+F test/ioerr.test 2a24bd6ed5a8b062e64bfe1f6cf94fb25e92210d
F test/ioerr2.test 9d71166f8466eda510f1af6137bdabaa82b5408d
F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
-F test/ioerr6.test cf25523b921d1a6a0e5b536cd4acb3c3d979ea52
-F test/join.test 8d63cc4d230a7affafa4b6ab0b97c49b8ccb365c
+F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
+F test/join.test 52d4d49f86d0cf46926672878c4eaf0da399104a
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
@@ -591,34 +657,34 @@ F test/journal3.test ff8af941f9e06161d3db1b46bb9f965ff0e7f307
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
-F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
-F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
+F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
+F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
-F test/like.test 0e5412f4dac4a849f613e1ef8b529d56a6e31d08
+F test/like.test e191e536d0fcd722a6b965e7cd1ee0bfd12a5991
F test/like2.test 3b2ee13149ba4a8a60b59756f4e5d345573852da
-F test/limit.test cc0ab63385239b63c72452b0e93700bf5e8f0b99
-F test/loadext.test 92e6dfefd1229c3ef4aaabd87419efd8fa57a7a5
-F test/loadext2.test 0bcaeb4d81cd5b6e883fdfea3c1bdbe1f173cbca
+F test/limit.test 3d7df19c35ac672a11f7de406cd3205d592babbb
+F test/loadext.test 648cb95f324d1775c54a55c12271b2d1156b633b
+F test/loadext2.test 0408380b57adca04004247179837a18e866a74f7
F test/lock.test 87af515b0c4cf928576d0f89946d67d7c265dfb4
F test/lock2.test 5242d8ac4e2d59c403aebff606af449b455aceff
F test/lock3.test f271375930711ae044080f4fe6d6eda930870d00
F test/lock4.test e175ae13865bc87680607563bafba21f31a26f12
F test/lock5.test 5ad6a1f536036ff1be915cfdd41481aeafda3273
F test/lock6.test ad5b387a3a8096afd3c68a55b9535056431b0cf5
-F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
+F test/lock7.test 49f1eaff1cdc491cc5dee3669f3c671d9f172431
F test/lock_common.tcl 0c270b121d40959fa2f3add382200c27045b3d95
F test/lookaside.test 93f07bac140c5bb1d49f3892d2684decafdc7af2
F test/main.test 39c4bb8a157f57298ed1659d6df89d9f35aaf2c8
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
-F test/malloc.test fd368e31fe98d4779ed80442f311ed9f03bcd1f7
+F test/malloc.test 4eb83876dfe4915766c179b687b8640437f14abf
F test/malloc3.test e3b32c724b5a124b57cb0ed177f675249ad0c66a
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
-F test/malloc5.test a577cbbcc1594c7763b9b3515b3633555751c7f0
+F test/malloc5.test fafce0aa9157060445cd1a56ad50fc79d82f28c3
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
F test/malloc9.test 2307c6ee3703b0a21391f3ea92388b4b73f9105e
-F test/mallocA.test 47006c8d70f29b030652e251cb9d35ba60289198
+F test/mallocA.test 1ba0367fb5434e7bc2fa4afcb30b14174d91b160
F test/mallocAll.test 98f1be74bc9f49a858bc4f361fc58e26486798be
F test/mallocB.test bc475ab850cda896142ab935bbfbc74c24e51ed6
F test/mallocC.test 3dffe16532f109293ce1ccecd0c31dca55ef08c4
@@ -629,80 +695,92 @@ F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f
F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test a88c2b9627c8506bf4703d8397420043a786cdb6
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
-F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
-F test/malloc_common.tcl 9a98856549bfb3fab205edbc1317216edc52e70d
+F test/mallocK.test 3cff7c0f64735f6883bacdd294e45a6ed5714817
+F test/mallocL.test 252ddc7eb4fbf75364eab17b938816085ff1fc17
+F test/malloc_common.tcl 3663f9001ce3e29bbaa9677ffe15cd468e3ec7e3
F test/manydb.test 28385ae2087967aa05c38624cec7d96ec74feb3e
F test/mem5.test c6460fba403c5703141348cd90de1c294188c68f
-F test/memdb.test db5260330676de007be8530d6ecc7c9ab2b06ad3
+F test/memdb.test fcb5297b321b562084fc79d64d5a12a1cd2b639b
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
-F test/memsubsys1.test a8f9e37567453a5d1d9d37ec102d4d88ab6be33f
+F test/memsubsys1.test f97cfd0b30e85c2f1ed16d642e7ac58006be84b2
F test/memsubsys2.test 3a1c1a9de48e5726faa85108b02459fae8cb9ee9
F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
F test/minmax4.test 536a3360470633a177e42fbc19660d146b51daef
-F test/misc1.test 889b40722442380a2f6575f30831b32b2372d70e
+F test/misc1.test 1201a037c24f982cc0e956cdaa34fcaf6439c417
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
-F test/misc3.test fe55130a43e444ee75e2156ff75dc96e964b5738
+F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6
F test/misc5.test 528468b26d03303b1f047146e5eefc941b9069f5
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
-F test/misc7.test dd82ec9250b89178b96cd28b2aca70639d21e5b3
-F test/misuse.test ba4fb5d1a6101d1c171ea38b3c613d0661c83054
+F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
+F test/misuse.test 3c34719944ba045cc6c188a4852ba04680728912
F test/mmap1.test 93d167b328255cbe6679fe1e1a23be1b1197d07b
F test/mmap2.test 9d6dd9ddb4ad2379f29cc78f38ce1e63ed418022
-F test/multiplex.test e08cc7177bd6d85990ee1d71100bb6c684c02256
+F test/mmap3.test c92273e16eb8d23c1d55c9815b446bb72ef0512e
+F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3
+F test/multiplex.test efd015ca0b5b4a57dc9535b8feb1273eebeadb60
F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a
F test/multiplex3.test d228f59eac91839a977eac19f21d053f03e4d101
F test/mutex1.test 78b2b9bb320e51d156c4efdb71b99b051e7a4b41
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test e9648b9d007c7045242af35e11a984d4b169443a
+F test/nolock.test 0540dd96f39b8876e3ffdd8814fad0ea425efeee
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test ce23eb522c9e1fff6443f96376fe67872202061c
-F test/notify3.test a86259abbfb923aa27d30f0fc038c88e5251488a
-F test/notnull.test 2afad748d18fd66d01f66463de73b3e2501fb226
+F test/notify3.test 10ff25cde502e72a92053a2f215d64bece4ef934
+F test/notnull.test f8fcf58669ddba79274daa2770d61dfad8274f62
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/numcast.test 5d126f7f581432e86a90d1e35cac625164aec4a1
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
-F test/orderby1.test f33968647da5c546528fe4d2bf86c6a6a2e5a7ae
+F test/orderby1.test 12426f99518cde45f34215ca6a0ebc0e9bc5c77a
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
+F test/orderby5.test 8f08a54836d21fb7c70245360751aedd1c2286fb
+F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
+F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
-F test/pager1.test 30e63afd425fea12285e9ec5fa1fd000808031f1
+F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
-F test/pagerfault.test 8483e65d33d5636e5b7656204bb274d826e728d9
-F test/pagerfault2.test 1f79ea40d1133b2683a2f811b00f2399f7ec2401
-F test/pagerfault3.test f16e2efcb5fc9996d1356f7cbc44c998318ae1d7
+F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e
+F test/pagerfault.test ae9ee0db5a30aecda9db8290ce3dd12e5f7bbaa1
+F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
+F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
F test/pageropt.test 6b8f6a123a5572c195ad4ae40f2987007923bbd6
F test/pagesize.test 1dd51367e752e742f58e861e65ed7390603827a0
-F test/pcache.test 065aa286e722ab24f2e51792c1f093bf60656b16
+F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
-F test/permutations.test d997a947ab8aabb15f763d50a030b3c11e8ef1b6
-F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
-F test/pragma2.test 3a55f82b954242c642f8342b17dffc8b47472947
+F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
+F test/permutations.test bc474bafb022cc5014ef3a9c3d5ab61d6d6f587c
+F test/pragma.test 19d0241a007bcdd77fc2606ec60fc60357e7fc8b
+F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
-F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
+F test/printf2.test bed79b4c3e5da08ba88ad637c0bf62586843cfb1
+F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
+F test/queryonly.test 5f653159e0f552f0552d43259890c1089391dcca
F test/quick.test 1681febc928d686362d50057c642f77a02c62e57
F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26
-F test/quota.test b5b3eec55a059e0fe493c66c6e27bd2c07676cfd
-F test/quota2.test 6d2bd57e8a4da28817f46db9da18551211cd325f
+F test/quota.test 2379902c25e291eac5c12b4cf96946a3447e3744
+F test/quota2.test 7dc12e08b11cbc4c16c9ba2aa2e040ea8d8ab4b8
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
-F test/rdonly.test c267d050a1d9a6a321de502b737daf28821a518d
+F test/rdonly.test dd30a4858d8e0fbad2304c2bd74a33d4df36412a
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
-F test/releasetest.tcl 06d289d8255794073a58d2850742f627924545ce
-F test/resolver01.test d1b487fc567bdb70b5dd09ccaaa877ddc61a233e
-F test/rollback.test a1b4784b864331eae8b2a98c189efa2a8b11ff07
+F test/releasetest.tcl a0df0dfc5e3ee83ade87b9cc96db41b52d590b9e
+F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
+F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
-F test/rowid.test f777404492adb0e00868fd706a3721328fd3af48
+F test/rowid.test b78b30afb9537a73788ca1233a23a32190a3bb1f
F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798
-F test/savepoint.test f5acd87d0c7a5f4ad6c547b47fd18c0e1aeaf048
+F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09
+F test/savepoint.test 6c53f76dffe5df0dd87646efe3e7aa159c36e07b
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
@@ -713,45 +791,50 @@ F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
F test/schema4.test e6a66e20cc69f0e306667c08be7fda3d11707dc5
-F test/schema5.test 0103e4c0313b3725b5ae5600bdca53006ab53db3
-F test/securedel.test 87a2561151af1f1e349071a89fdd77059f50113c
-F test/securedel2.test f13a916155f790a6b9de835049641b14ef312986
-F test/select1.test deba017eed9daa5af33de868676c997e7eebb931
+F test/schema5.test 29699b4421f183c8f0e88bd28ce7d75d13ea653e
+F test/securedel.test 21749c32ccc30f1ea9e4b9f33295a6521ec20fa0
+F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
+F test/select1.test fc2a61f226a649393664ad54bc5376631801517c
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
-F test/select4.test 00179be44e531fe04c1c3f15df216439dff2519d
+F test/select4.test 8c5a60d439e2df824aed56223566877a883c5c84
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test e76bd10a56988f15726c097a5d5a7966fe82d3b2
-F test/select7.test dad6f00f0d49728a879d6eb6451d4752db0b0abe
+F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
-F test/select9.test c0ca3cd87a8ebb04de2cb1402c77df55d911a0ea
-F test/selectA.test 99cf21df033b93033ea4f34aba14a500f48f04fe
+F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
+F test/selectA.test 64b88a80271c1710966e50e633380696b60a12a4
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test 871fb55d884d3de5943c4057ebd22c2459e71977
F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
F test/selectE.test fc02a1eb04c8eb537091482644b7d778ae8759b7
+F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118
F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746
F test/shared2.test 03eb4a8d372e290107d34b6ce1809919a698e879
-F test/shared3.test ebf77f023f4bdaa8f74f65822b559e86ce5c6257
+F test/shared3.test fcd65cb11d189eff5f5c85cc4fad246fb0933108
F test/shared4.test 72d90821e8d2fc918a08f16d32880868d8ee8e9d
F test/shared6.test 866bb4982c45ce216c61ded5e8fde4e7e2f3ffa9
-F test/shared7.test 960760bc8d03e1419e70dea69cf41db62853616e
-F test/shared8.test b27befbefbe7f4517f1d6b7ff8f64a41ec74165d
+F test/shared7.test a81e99f83e6c51b02ac99c96fb3a2a7b5978c956
+F test/shared8.test 00a07bf5e1337ecf72e94542bdefdc330d7a2538
F test/shared9.test 5f2a8f79b4d6c7d107a01ffa1ed05ae7e6333e21
F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/shared_err.test 0079c05c97d88cfa03989b7c20a8b266983087aa
-F test/sharedlock.test ffa0a3c4ac192145b310f1254f8afca4d553eabf
-F test/shell1.test 4a2f57952719972c6f862134463f8712e953c038
-F test/shell2.test 037d6ad16e873354195d30bb2dc4b5321788154a
-F test/shell3.test 9196c42772d575685e722c92b4b39053c6ebba59
-F test/shell4.test aa4eef8118b412d1a01477a53426ece169ea86a9
-F test/shell5.test fa2188bbb13fe2d183fd04a5f7b512650c35ef5d
+F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
+F test/shell1.test d60946b5fde4d85fe06db7331dfe89011f564350
+F test/shell2.test c57da3a381c099b02c813ba156298d5c2f5c93a3
+F test/shell3.test 5e8545ec72c4413a0e8d4c6be56496e3c257ca29
+F test/shell4.test 8a9c08976291e6c6c808b4d718f4a8b299f339f5
+F test/shell5.test 15a419cc1df21c892ed64f5596ae7a501f2816f2
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
+F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 8c70f62b6e8eb4d54533de6d65bd06b1b9a17868
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
+F test/skipscan1.test 28c7faa41a0d7265040ecb0a0abd90c0904270b2
+F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
+F test/skipscan5.test d8b9692b702745a0e41c23f9da6beac81df01196
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
-F test/softheap1.test c16709a16ad79fa43b32929b2e623d1d117ccf53
+F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
@@ -761,25 +844,27 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
-F test/spellfix.test bea537caf587df30d430c2c6a8fe9f64b8712834
+F test/speedtest1.c d29c8048beb7ea9254191f3fde9414709166a920
+F test/spellfix.test 61309f5efbec53603b3f86457d34a504f80abafe
F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298
-F test/stat.test be8d477306006ec696bc86757cfb34bec79447ce
+F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de
F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9
-F test/subquery.test 869562de9e8c5d8147e0451a2ce5b58cf55ce389
+F test/subquery.test 666fdecceac258f5fd84bed09a64e49d9f37edd9
F test/subquery2.test 91e1e364072aeff431d1f9689b15147e421d88c7
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
-F test/syscall.test a653783d985108c4912cc64d341ffbbb55ad2806
+F test/syscall.test d2fdaad713f103ac611fe7ef9b724c7b69f8149c
F test/sysfault.test fa776e60bf46bdd3ae69f0b73e46ee3977a58ae6
-F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
+F test/table.test 2a1d2fa52c531de5915f28023747d9a8c27b6f31
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
+F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
-F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
-F test/tester.tcl 3b08771e6d601612fe62d13787db0e50aac4cf7b
+F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
+F test/tester.tcl b4ff83a8b069633f4aca788236d10a7086112a49
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -793,48 +878,57 @@ F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
F test/threadtest3.c 0ed13e09690f6204d7455fac3b0e8ece490f6eef
F test/tkt-02a8e81d44.test 6c80d9c7514e2a42d4918bf87bf6bc54f379110c
F test/tkt-26ff0c2d1e.test 888324e751512972c6e0d1a09df740d8f5aaf660
-F test/tkt-2a5629202f.test 1ab32e084e9fc3d36be6dee2617530846a0eb0b6
+F test/tkt-2a5629202f.test 0521bd25658428baa26665aa53ffed9367d33af2
F test/tkt-2d1a5c67d.test d371279946622698ab393ff88cad9f5f6d82960b
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
F test/tkt-31338dca7e.test 6fb8807851964da0d24e942f2e19c7c705b9fb58
F test/tkt-313723c356.test c47f8a9330523e6f35698bf4489bcb29609b53ac
-F test/tkt-385a5b56b9.test 7782a382912a51f09f1d1a1442bca1e75f9c549b
+F test/tkt-385a5b56b9.test c0a06ada41d7f06b1686da0e718553f853771d1e
F test/tkt-38cb5df375.test f3cc8671f1eb604d4ae9cf886ed4366bec656678
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
-F test/tkt-3a77c9714e.test 32bb28afa8c63fc76e972e996193139b63551ed9
-F test/tkt-3fe897352e.test 10de1a67bd5c66b238a4c96abe55531b37bb4f00
-F test/tkt-4a03edc4c8.test 2865e4edbc075b954daa82f8da7cc973033ec76e
+F test/tkt-3a77c9714e.test b08bca26de1140bdf004a37716582a43d7bd8be8
+F test/tkt-3fe897352e.test 27e26eb0f1811aeba4d65aba43a4c52e99da5e70
+F test/tkt-4a03edc4c8.test 91c0e135888cdc3d4eea82406a44b05c8c1648d0
+F test/tkt-4c86b126f2.test cbcc611becd0396890169ab23102dd70048bbc9a
F test/tkt-4dd95f6943.test 3d0ce415d2ee15d3d564121960016b9c7be79407
+F test/tkt-4ef7e3cfca.test 3965ae11cc9cf6e334f9d7d3c1e20bf8d56254b1
F test/tkt-54844eea3f.test a12b851128f46a695e4e378cca67409b9b8f5894
F test/tkt-5d863f876e.test c9f36ca503fa154a3655f92a69d2c30da1747bfa
F test/tkt-5e10420e8d.test 904d1687b3c06d43e5b3555bbcf6802e7c0ffd84
F test/tkt-5ee23731f.test 9db6e1d7209dc0794948b260d6f82b2b1de83a9f
F test/tkt-6bfb98dfc0.test 24780633627b5cfc0635a5500c2389ebfb563336
F test/tkt-752e1646fc.test ea78d88d14fe9866bdd991c634483334639e13bf
-F test/tkt-78e04e52ea.test 703e0bfb23d543edf0426a97e3bbd0ca346508ec
-F test/tkt-7a31705a7e6.test 5a7889fdb095ffbe1622413e0145de1637d421bd
-F test/tkt-7bbfb7d442.test dfa5c8097a8c353ae40705d6cddeb1f99c18b81a
+F test/tkt-78e04e52ea.test 813779f8888f3ca226df656c4eef078f9635f3c9
+F test/tkt-7a31705a7e6.test e75a2bba4eec801b92c8040eb22096ac6d35e844
+F test/tkt-7bbfb7d442.test 7b2cd79c7a17ae6750e75ec1a7846712a69c9d18
F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8
-F test/tkt-80e031a00f.test 9a154173461a4dbe2de49cda73963e04842d52f7
+F test/tkt-80e031a00f.test f50046f474ecf67ad5c50cd9200da04ff887d7cd
F test/tkt-8454a207b9.test c583a9f814a82a2b5ba95207f55001c9f0cd816c
+F test/tkt-868145d012.test a5f941107ece6a64410ca4755c6329b7eb57a356
+F test/tkt-8c63ff0ec.test 258b7fc8d7e4e1cb5362c7d65c143528b9c4cbed
F test/tkt-91e2e8ba6f.test 08c4f94ae07696b05c9b822da0b4e5337a2f54c5
-F test/tkt-94c04eaadb.test fa9c71192f7e2ea2d51bf078bc34e8da6088bf71
+F test/tkt-94c04eaadb.test f738c57c7f68ab8be1c054415af7774617cb6223
+F test/tkt-9a8b09f8e6.test b2ef151d0984b2ebf237760dbeaa50724e5a0667
F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
+F test/tkt-9f2eb3abac.test 85bc63e749f050e6a61c8f9207f1eee65c9d3395
F test/tkt-a7b7803e.test 159ef554234fa1f9fb318c751b284bd1cf858da4
-F test/tkt-b1d3a2e531.test 610ef582413171b379652663111b1f996d9f8f78
+F test/tkt-a8a0d2996a.test eb597379dbcefa24765763d7f682c00cb5924fa9
+F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
+F test/tkt-b75a9ca6b0.test 97cc2d5eeaf82799eb42138c0a1ff64370238ce4
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
-F test/tkt-c48d99d690.test bed446e3513ae10eec1b86fdd186ef750226c408
-F test/tkt-cbd054fa6b.test 9c27ed07b333eed458e5d4543f91ecdcf05aeb19
-F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
+F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447
+F test/tkt-cbd054fa6b.test 06ccd57af3c0c7895d0f7dc844f13c51f8258885
+F test/tkt-d11f09d36e.test d999b548fef885d1d1afa49a0e8544ecf436869d
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
F test/tkt-d82e3f3721.test bcc0dfba658d15bab30fd4a9320c9e35d214ce30
-F test/tkt-f3e5abed55.test 669bb076f2ac573c7398ce00f40cd0ca502043a9
+F test/tkt-f3e5abed55.test d5a0126118142d13e27f6ce9f4c47096e9321c00
+F test/tkt-f67b41381a.test a23bc124c981662db712167bacd0ed8ad11abac9
F test/tkt-f777251dc7a.test af6531446c64bfd268416f07b4df7be7f9c749d2
F test/tkt-f7b4edec.test d998a08ff2b18b7f62edce8e3044317c45efe6c7
-F test/tkt-f973c7ac31.test 1da0ed15ec2c7749fb5ce2828cd69d07153ad9f4
+F test/tkt-f973c7ac31.test 28ef85c7f015477916795246d8286aeda39d4ead
F test/tkt-fa7bf5ec.test 9102dfea58aa371d78969da735f9392c57e2e035
F test/tkt-fc62af4523.test 72825d3febdedcd5593a27989fc05accdbfc2bb4
F test/tkt-fc7bd6358f.test 634bb4af7d661e82d6b61b80c86727bad698e08f
@@ -848,7 +942,7 @@ F test/tkt1512.test a1df1f66caf0b9122d6220c15dcee230298c2c2f
F test/tkt1514.test ddef38e34fea72eb1ab935ded9f17a3fb71dd9df
F test/tkt1536.test 83ff7a7b6e248016f8d682d4f7a4ae114070d466
F test/tkt1537.test e3a14332de9770be8ff14bd15c19a49cbec10808
-F test/tkt1567.test 18023cc3626a365f0118e17b66decedec93b1a6f
+F test/tkt1567.test 52f329386ac77e59260d4af1c58490d61377db20
F test/tkt1644.test 80b6a2bb17885f3cf1cb886d97cdad13232bb869
F test/tkt1667.test 4700d931ed19ea3983e8e703becb28079250b460
F test/tkt1873.test 0e1b8c023050a430c2525179ed4022ddc7c31264
@@ -869,7 +963,7 @@ F test/tkt2686.test 6ee01c9b9e9c48f6d3a1fdd553b1cc4258f903d6
F test/tkt2767.test 569000d842678f9cf2db7e0d1b27cbc9011381b0
F test/tkt2817.test f31839e01f4243cff7399ef654d3af3558cb8d8d
F test/tkt2820.test 39940276b3436d125deb7d8ebeee053e4cf13213
-F test/tkt2822.test c3589494fba643e039bcf0bfde7554ff6028406d
+F test/tkt2822.test f391776423a7c0d0949edfce375708bfb0f3141e
F test/tkt2832.test a9b0b74a02dca166a04d9e37739c414b10929caa
F test/tkt2854.test e432965db29e27e16f539b2ba7f502eb2ccc49af
F test/tkt2920.test a8737380e4ae6424e00c0273dc12775704efbebf
@@ -886,7 +980,7 @@ F test/tkt3346.test 6f67c3ed7db94dfc5df4f5f0b63809a1f611e01a
F test/tkt3357.test 77c37c6482b526fe89941ce951c22d011f5922ed
F test/tkt3419.test 1bbf36d7ea03b638c15804251287c2391f5c1f6b
F test/tkt3424.test 61f831bd2b071bd128fa5d00fbda57e656ca5812
-F test/tkt3442.test 0adb70e9fe9cb750a702065a68ad647409dbc158
+F test/tkt3442.test 53840ec5325bb94544792aad4c20476f81dc26b1
F test/tkt3457.test 44e980fe5334753dcc27b94fa4deabc485a92f74
F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19
F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218
@@ -896,7 +990,7 @@ F test/tkt3527.test 1a6a48441b560bdc53aec581a868eb576234874d
F test/tkt3541.test 5dc257bde9bc833ab9cc6844bf170b998dbb950a
F test/tkt3554.test f599967f279077bace39220cbe76085c7b423725
F test/tkt3581.test 1966b7193f1e3f14951cce8c66907ae69454e9a3
-F test/tkt35xx.test 69d038ce5898f1b64f2084b780bbab1cf9be0a25
+F test/tkt35xx.test f38c1b03713179d414969187c941466e44945b35
F test/tkt3630.test 929f64852103054125200bc825c316d5f75d42f7
F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
@@ -914,22 +1008,23 @@ F test/tkt3841.test 4659845bc53f809a5932c61c6ce8c5bb9d6b947f
F test/tkt3871.test 43ecbc8d90dc83908e2a454aef345acc9d160c6f
F test/tkt3879.test 2ad5bef2c87e9991ce941e054c31abe26ef7fb90
F test/tkt3911.test 74cd324f3ba653040cc6d94cc4857b290d12d633
-F test/tkt3918.test e6cdf6bfcfe9ba939d86a4238a9dc55d6eec5d42
+F test/tkt3918.test ea78bf164e4d55cbde0d83c671ef6fbe930a0032
F test/tkt3922.test f26be40ab4fe6c00795629bd2006d96e270d9b1a
-F test/tkt3929.test 75a862e45bcb39e9a7944c89b92afa531304afca
+F test/tkt3929.test cdf67acf5aa936ec4ffead81db87f8a71fe40e59
F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767
F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da
F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
+F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
-F test/trace2.test c1dc104a8d11a347c870cfea6235e3fc6f6cb06d
+F test/trace2.test 93b47ca6996c66b47f57224cfb146f34e07df382
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
-F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
+F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 373ac5183cc56be69f48ae44090e7f672939f732
-F test/transitive1.test d04aa9023e425d6f2d4aa61dd81ee9e102f89062
+F test/transitive1.test 03f532954f46cdf5608f7766bff0b0c52bf2a7cd
F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03
-F test/trigger2.test 834187beafd1db383af0c659cfa49b0576832816
+F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83
@@ -939,31 +1034,33 @@ F test/trigger8.test 30cb0530bd7c4728055420e3f739aa00412eafa4
F test/trigger9.test 5b0789f1c5c4600961f8e68511b825b87be53e31
F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
-F test/triggerC.test a7b4367392c755bc5fd5fff88011753e6b6afe90
+F test/triggerC.test a68980c5955d62ee24be6f97129d824f199f9a4c
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
+F test/triggerE.test 355e9c5cbaed5cd039a60baad1fb2197caeb8e52
F test/tt3_checkpoint.c 415eccce672d681b297485fc20f44cdf0eac93af
F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
-F test/unique.test cadb172bbd5a2e83cd644d186ccd602085e54edc
-F test/unixexcl.test a9870e46cc6f8390a494513d4f2bf55b5a8b3e46
-F test/unordered.test 93dce7b6c97a817a4fe26980c484605a4511f614
-F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
-F test/uri.test 63e03df051620a18f794b4f4adcdefb3c23b6751
+F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
+F test/unique2.test 41e7f83c6827605991160a31380148a9fc5f1339
+F test/unixexcl.test cd6c765f75e50e8e2c2ba763149e5d340ea19825
+F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
+F test/update.test 1b6c488a8f993d090b7ee9ad0e234faa161b3aeb
+F test/uri.test 23662b7b61958b0f0e47082de7d06341ccf85d5b
F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
F test/vacuum2.test af432e6e3bfc0ea20a80cb86a03c7d9876d38324
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
-F test/veryquick.test 7701bb609fe8bf6535514e8b849a309e8f00573b
-F test/view.test 4057630287bfa5955628fe90a13d4c225d1c7352
-F test/vtab1.test 4403f987860ebddef1ce2de6db7216421035339d
+F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
+F test/view.test f311691d696a5cc27e3c1b875cec1b0866b4ccd9
+F test/vtab1.test b631d147b198cfd7903ab5fed028eb2a3d321dc6
F test/vtab2.test 7bcffc050da5c68f4f312e49e443063e2d391c0d
F test/vtab3.test baad99fd27217f5d6db10660522e0b7192446de1
F test/vtab4.test 942f8b8280b3ea8a41dae20e7822d065ca1cb275
F test/vtab5.test 889f444970393c73f1e077e2bdc5d845e157a391
-F test/vtab6.test c7f290d172609d636fbfc58166eadcb55d5c117c
+F test/vtab6.test 5f5380c425e52993560ab4763db4f826d2ba7b09
F test/vtab7.test ae560ebea870ed04e9aa4177cc302f910faaabb5
F test/vtab8.test e19fa4a538fcd1bb66c22825fa8f71618fb13583
F test/vtab9.test ea58d2b95d61955f87226381716b2d0b1d4e4f9b
@@ -975,82 +1072,105 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
-F test/vtab_shared.test 82f463886e18d7f8395a4b6167c91815efe54839
-F test/wal.test 3a6ebdf0287b38b5537c07c517b30dda9aaac317
-F test/wal2.test d4b470f13c87f6d8268b004380afa04c3c67cb90
+F test/vtab_shared.test ea8778d5b0df200adef2ca7c00c3c37d4375f772
+F test/wal.test 885f32b2b390b30b4aa3dbb0e568f8f78d40f5cc
+F test/wal2.test 1f841d2048080d32f552942e333fd99ce541dada
F test/wal3.test b22eb662bcbc148c5f6d956eaf94b047f7afe9c0
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 8f888b50f66b78821e61ed0e233ded5de378224b
-F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
+F test/wal6.test 527581f5527bf9c24394991e2be83000aace5f9e
+F test/wal64k.test 163655ecd2cb8afef4737cac2a40fdd2eeaf20b8
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
-F test/wal8.test b3ee739fe8f7586aaebdc2367f477ebcf3e3b034
+F test/wal8.test 75c42e1bc4545c277fed212f8fc9b7723cd02216
F test/wal9.test 378e76a9ad09cd9bee06c172ad3547b0129a6750
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
F test/walbak.test b9f68e39646375c2b877be906babcc15d38b4877
F test/walbig.test f437473a16cfb314867c6b5d1dbcd519e73e3434
-F test/walcksum.test f5447800a157c9e2234fbb8e80243f0813941bde
-F test/walcrash.test 4457436593be8c136f9148487c7dccd5e9013af2
-F test/walcrash2.test 019d60b89d96c1937adb2b30b850ac7e86e5a142
-F test/walcrash3.test 595e44c6197f0d0aa509fc135be2fd0209d11a2c
-F test/walfault.test 54ad6e849c727f4da463964b9eb8c8e8e155cf82
+F test/walcksum.test 9afeb96240296c08c72fc524d199c912cfe34daa
+F test/walcrash.test 451d79e528add5c42764cea74aa2750754171b25
+F test/walcrash2.test a0edab4e5390f03b99a790de89aad15d6ec70b36
+F test/walcrash3.test e426aa58122d20f2b9fbe9a507f9eb8cab85b8af
+F test/walfault.test 1f8389f7709877e9b4cc679033d71d6fe529056b
F test/walhook.test ed00a40ba7255da22d6b66433ab61fab16a63483
F test/walmode.test 4022fe03ae6e830583672caa101f046438a0473c
F test/walnoshm.test 84ca10c544632a756467336b7c3b864d493ee496
F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6
-F test/walro.test a31deb621033442a76c3a61e44929250d06f81b1
-F test/walshared.test 6dda2293880c300baf5d791c307f653094585761
+F test/walro.test 34422d1d95aaff0388f0791ec20edb34e2a3ed57
+F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
F test/walslow.test e7be6d9888f83aa5d3d3c7c08aa9b5c28b93609a
F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
-F test/where.test 15ac8611c9439a2c5f4a6ac10cfe4c1ec9854c24
-F test/where2.test 399b3178289925a0aa976b3d60ef139740540ecd
-F test/where3.test 667e75642102c97a00bf9b23d3cb267db321d006
-F test/where4.test e9b9e2f2f98f00379e6031db6a6fca29bae782a2
+F test/where.test 28b64e93428961b07b0d486778d63fd672948f6b
+F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e
+F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e
+F test/where4.test d8420ceeb8323a41ceff1f1841fc528e824e1ecf
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
-F test/where7.test 5c566388f0cc318b0032ce860f4ac5548e3c265a
-F test/where8.test d6a283eb7348a8967d44e2a753f117ab0d21d4f3
+F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8
+F test/where8.test 806f1dcec4088be2b826b33f757fe6e17c3236a1
F test/where8m.test da346596e19d54f0aba35ebade032a7c47d79739
-F test/where9.test 1b4387c6eacc9a32b28b4d837c27f857c785d0d8
-F test/whereA.test 24c234263c8fe358f079d5e57d884fb569d2da0a
+F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
+F test/whereA.test 4d253178d135ec46d1671e440cd8f2b916aa6e6b
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
-F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
-F test/whereD.test 3f3ee93825c94804f1fc91eef2de0d365981759a
-F test/whereE.test 7bd34945797efef15819368479bacc34215e4e1d
-F test/whereF.test a0e296643cabe5278379bc1a0aa158cf3c54a1c9
+F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
+F test/whereD.test fd9120e262f9da3c45940f52aefeef4d15b904e5
+F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
+F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
+F test/whereG.test 69f5ec4b15760a8c860f80e2d55525669390aab3
+F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
+F test/whereI.test 1d89199697919d4930be05a71e7fe620f114e622
+F test/whereJ.test 35a40a50d0e13aa6b0de7cc5d4b204e5f9f9669f
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
-F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
+F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
+F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
+F test/win32lock.test 71642fa56e9b06e5cfffe6bad67cb8c1eb2c555a
+F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
+F test/with1.test 268081a6b14817a262ced4d0ee34d4d2a1dd2068
+F test/with2.test ee227a663586aa09771cafd4fa269c5217eaf775
+F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
+F test/without_rowid1.test 7862e605753c8d25329f665fa09072e842183151
+F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
+F test/without_rowid3.test eac3d5c8a1924725b58503a368f2cbd24fd6c8a0
+F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
+F test/without_rowid5.test b4a639a367f04d382d20e8f44fc1be4f2d57d107
+F test/wordcount.c 9915e06cb33d8ca8109b8700791afe80d305afda
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
-F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
-F tool/build-all-msvc.bat 74fb6e5cca66ebdb6c9bbafb2f8b802f08146d38 x
+F test/zerodamage.test cf6748bad89553cc1632be51a6f54e487e4039ac
+F tool/build-all-msvc.bat a0534c971b86fe95f1983f445db5b896d3394818 x
F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
+F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
-F tool/lemon.c 680980c7935bfa1edec20c804c9e5ba4b1dd96f5
+F tool/lemon.c 3ff0fec22f92dfb54e62eeb48772eddffdbeb0d6
F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
-F tool/mkkeywordhash.c bb52064aa614e1426445e4b2b9b00eeecd23cc79
+F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
+F tool/mkautoconfamal.sh f8d8dbf7d62f409ebed5134998bf5b51d7266383
+F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
+F tool/mkpragmatab.tcl 78a77b2c554d534c6f2dc903130186ed15715460
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
-F tool/mksqlite3c-noext.tcl 8bce31074e4cbe631bb7676526a048335f4c9f02
-F tool/mksqlite3c.tcl a61fe62a2895ca6458c463fccf1211ca1c000fcf
+F tool/mksqlite3c-noext.tcl 1712d3d71256ca1f297046619c89e77a4d7c8f6d
+F tool/mksqlite3c.tcl ba274df71f5e6534b0a913c7c48eabfcbd0934b6
F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
-F tool/mksqlite3internalh.tcl 3dca7bb5374cee003379b8cbac73714f610ef795
-F tool/mkvsix.tcl 0be7f7a591f1e83f9199cb82911b66668ca484c9
+F tool/mksqlite3internalh.tcl b6514145a7d5321b47e64e19b8116cc44f973eb1
+F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105
F tool/offsets.c fe4262fdfa378e8f5499a42136d17bf3b98f6091
-F tool/omittest.tcl 4665982e95a6e5c1bd806cf7bc3dea95be422d77
+F tool/omittest.tcl 34d7ac01fe4fd18e3637f64abe12c40eca0f6b97
F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c
+F tool/pagesig.c ff0ca355fd3c2398e933da5e22439bbff89b803b
F tool/restore_jrnl.tcl 6957a34f8f1f0f8285e07536225ec3b292a9024a
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
-F tool/showdb.c 525ecc443578647703051308ad50a93de6ba2c4b
-F tool/showjournal.c b62cecaab86a4053d944c276bb5232e4d17ece02
-F tool/showwal.c 3f7f7da5ec0cba51b1449a75f700493377da57b5
+F tool/showdb.c b9ee6b6c81a094bf33badbf7e9da34cdbc0cce25
+F tool/showjournal.c 053eb1cc774710c6890b7dd6293300cc297b16a5
+F tool/showstat4.c c39279d6bd37cb999b634f0064f6f86ad7af008f
+F tool/showwal.c 3209120269cdf9380f091459e47b776b4f81dfd3
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
-F tool/spaceanal.tcl 76f583a246a0b027f423252339e711f13198932e
+F tool/spaceanal.tcl 8e50b217c56a6a086a1b47eac9d09c5cd65b996f
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@@ -1061,14 +1181,15 @@ F tool/stack_usage.tcl f8e71b92cdb099a147dad572375595eae55eca43
F tool/symbols-mingw.sh 4dbcea7e74768305384c9fd2ed2b41bbf9f0414d
F tool/symbols.sh fec58532668296d7c7dc48be9c87f75ccdb5814f
F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
-F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
+F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
+F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
-F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
-P 7a9aa21c3506a10ab9465540e81071b39bca447d
-R c103e20e6479251bb5b95c9b2e375810
+F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
+F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
+P 3f45b8192dad7fb1f027cbaa694046e3c1b3e278
+R be8bb3bca247e348b90f7388fee24972
T +bgcolor * #d0c0ff
T +sym-release *
-T +sym-version-3.7.17 *
+T +sym-version-3.8.6 *
U drh
-Z 416a23d38a1b2c089b32c7a6efcbc3bc
+Z 74d8dd4d4aac0ae87245b4bcefd8b172
diff --git a/manifest.uuid b/manifest.uuid
index eb32e70..910d8f9 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-118a3b35693b134d56ebd780123b7fd6f1497668
+9491ba7d738528f168657adb43a198238abde19e
diff --git a/mkdll.sh b/mkdll.sh
deleted file mode 100644
index 9e368d1..0000000
--- a/mkdll.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/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_RTREE=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/mkextu.sh b/mkextu.sh
deleted file mode 100644
index 1d96897..0000000
--- a/mkextu.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/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/mkextw.sh b/mkextw.sh
deleted file mode 100644
index 2d1b6d1..0000000
--- a/mkextw.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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/mkopcodec.awk b/mkopcodec.awk
index 2ef77d4..de19068 100644
--- a/mkopcodec.awk
+++ b/mkopcodec.awk
@@ -12,22 +12,36 @@ 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 "#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)"
+ print "# define OpHelp(X) \"\\0\" X"
+ print "#else"
+ print "# define OpHelp(X)"
+ print "#endif"
print "const char *sqlite3OpcodeName(int i){"
print " static const char *const azName[] = { \"?\","
mx = 0
}
-/define OP_/ {
+/^.define OP_/ {
sub("OP_","",$2)
i = $3+0
label[i] = $2
if( mx<i ) mx = i
+ for(j=5; j<NF; j++) if( $j=="synopsis:" ) break
+ if( j<NF ){
+ j++
+ x = $j
+ for(j=j+1; j<NF; j++) x = x " " $j
+ synopsis[i] = x
+ }else{
+ synopsis[i] = ""
+ }
}
END {
for(i=1; i<=mx; i++){
- printf " /* %3d */ \"%s\",\n", i, label[i]
+ printf " /* %3d */ %-18s OpHelp(\"%s\"),\n", i, \
+ "\"" label[i] "\"", synopsis[i]
}
print " };"
print " return azName[i];"
diff --git a/mkopcodeh.awk b/mkopcodeh.awk
index f6b90c1..babfdc6 100644
--- a/mkopcodeh.awk
+++ b/mkopcodeh.awk
@@ -35,7 +35,34 @@
# Remember the TK_ values from the parse.h file
/^#define TK_/ {
- tk[$2] = 0+$3
+ tk[$2] = 0+$3 # tk[x] holds the numeric value for TK symbol X
+}
+
+# Find "/* Opcode: " lines in the vdbe.c file. Each one introduces
+# a new opcode. Remember which parameters are used.
+/^.. Opcode: / {
+ currentOp = "OP_" $3
+ m = 0
+ for(i=4; i<=NF; i++){
+ x = $i
+ if( x=="P1" ) m += 1
+ if( x=="P2" ) m += 2
+ if( x=="P3" ) m += 4
+ if( x=="P4" ) m += 8
+ if( x=="P5" ) m += 16
+ }
+ paramused[currentOp] = m
+}
+
+# Find "** Synopsis: " lines that follow Opcode:
+/^.. Synopsis: / {
+ if( currentOp ){
+ x = $3
+ for(i=4; i<=NF; i++){
+ x = x " " $i
+ }
+ synopsis[currentOp] = x
+ }
}
# Scan for "case OP_aaaa:" lines in the vdbe.c file
@@ -43,7 +70,7 @@
name = $2
sub(/:/,"",name)
sub("\r","",name)
- op[name] = -1
+ op[name] = -1 # op[x] holds the numeric value for OP symbol x
jump[name] = 0
out2_prerelease[name] = 0
in1[name] = 0
@@ -55,9 +82,11 @@
if($i=="same" && $(i+1)=="as"){
sym = $(i+2)
sub(/,/,"",sym)
- op[name] = tk[sym]
- used[op[name]] = 1
- sameas[op[name]] = sym
+ val = tk[sym]
+ op[name] = val
+ used[val] = 1
+ sameas[val] = sym
+ def[val] = name
}
x = $i
sub(",","",x)
@@ -90,31 +119,69 @@ END {
order[n_op++] = "OP_Noop";
op["OP_Explain"] = -1;
order[n_op++] = "OP_Explain";
+
+ # Assign small values to opcodes that are processed by resolveP2Values()
+ # to make code generation for the switch() statement smaller and faster.
for(i=0; i<n_op; i++){
name = order[i];
- if( op[name]<0 ){
+ if( op[name]>=0 ) continue;
+ if( name=="OP_Function" \
+ || name=="OP_AggStep" \
+ || name=="OP_Transaction" \
+ || name=="OP_AutoCommit" \
+ || name=="OP_Savepoint" \
+ || name=="OP_Checkpoint" \
+ || name=="OP_Vacuum" \
+ || name=="OP_JournalMode" \
+ || name=="OP_VUpdate" \
+ || name=="OP_VFilter" \
+ || name=="OP_Next" \
+ || name=="OP_NextIfOpen" \
+ || name=="OP_SorterNext" \
+ || name=="OP_Prev" \
+ || name=="OP_PrevIfOpen" \
+ ){
cnt++
while( used[cnt] ) cnt++
op[name] = cnt
+ used[cnt] = 1
+ def[cnt] = name
}
- 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"
+ }
+ # Generate the numeric values for opcodes
+ for(i=0; i<n_op; i++){
+ name = order[i];
+ if( op[name]<0 ){
+ cnt++
+ while( used[cnt] ) cnt++
+ op[name] = cnt
+ used[cnt] = 1
+ def[cnt] = name
+ }
}
- seenUnused = 0;
- for(i=1; i<max; i++){
+ max = cnt
+ for(i=1; i<=max; i++){
if( !used[i] ){
- if( !seenUnused ){
- printf "\n/* The following opcode values are never used */\n"
- seenUnused = 1
+ def[i] = "OP_NotUsed_" i
+ }
+ printf "#define %-16s %3d", def[i], i
+ com = ""
+ if( sameas[i] ){
+ com = "same as " sameas[i]
+ }
+ x = synopsis[def[i]]
+ if( x ){
+ if( com=="" ){
+ com = "synopsis: " x
+ } else {
+ com = com ", synopsis: " x
}
- printf "#define %-25s %15d\n", sprintf( "OP_NotUsed_%-3d", i ), i
}
+ if( com!="" ){
+ printf " /* %-42s */", com
+ }
+ printf "\n"
}
# Generate the bitvectors:
@@ -123,12 +190,9 @@ END {
# 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(i=0; i<n_op; i++){
- name = order[i];
- x = op[name]
+ for(i=0; i<=max; i++){
+ name = def[i]
a0 = a1 = a2 = a3 = a4 = a5 = a6 = a7 = 0
- # a7 = a9 = a10 = a11 = a12 = a13 = a14 = a15 = 0
if( jump[name] ) a0 = 1;
if( out2_prerelease[name] ) a1 = 2;
if( in1[name] ) a2 = 4;
@@ -136,8 +200,7 @@ END {
if( in3[name] ) a4 = 16;
if( out2[name] ) a5 = 32;
if( out3[name] ) a6 = 64;
- # 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;
+ bv[i] = a0+a1+a2+a3+a4+a5+a6+a7;
}
print "\n"
print "/* Properties such as \"out2\" or \"jump\" that are specified in"
@@ -158,4 +221,15 @@ END {
if( i%8==7 ) printf("\\\n");
}
print "}"
+ if( 0 ){
+ print "\n/* Bitmask to indicate which fields (P1..P5) of each opcode are"
+ print "** actually used.\n*/"
+ print "#define OP_PARAM_USED_INITIALIZER {\\"
+ for(i=0; i<=max; i++){
+ if( i%8==0 ) printf("/* %3d */",i)
+ printf " 0x%02x,", paramused[def[i]]
+ if( i%8==7 ) printf("\\\n");
+ }
+ print "}"
+ }
}
diff --git a/mptest/config01.test b/mptest/config01.test
new file mode 100644
index 0000000..683ee91
--- /dev/null
+++ b/mptest/config01.test
@@ -0,0 +1,46 @@
+/*
+** Configure five tasks in different ways, then run tests.
+*/
+--if vfsname() GLOB 'unix'
+PRAGMA page_size=8192;
+--task 1
+ PRAGMA journal_mode=PERSIST;
+ PRAGMA mmap_size=0;
+--end
+--task 2
+ PRAGMA journal_mode=TRUNCATE;
+ PRAGMA mmap_size=28672;
+--end
+--task 3
+ PRAGMA journal_mode=MEMORY;
+--end
+--task 4
+ PRAGMA journal_mode=OFF;
+--end
+--task 4
+ PRAGMA mmap_size(268435456);
+--end
+--source multiwrite01.test
+--wait all
+PRAGMA page_size=16384;
+VACUUM;
+CREATE TABLE pgsz(taskid, sz INTEGER);
+--task 1
+ INSERT INTO pgsz VALUES(1, eval('PRAGMA page_size'));
+--end
+--task 2
+ INSERT INTO pgsz VALUES(2, eval('PRAGMA page_size'));
+--end
+--task 3
+ INSERT INTO pgsz VALUES(3, eval('PRAGMA page_size'));
+--end
+--task 4
+ INSERT INTO pgsz VALUES(4, eval('PRAGMA page_size'));
+--end
+--task 5
+ INSERT INTO pgsz VALUES(5, eval('PRAGMA page_size'));
+--end
+--source multiwrite01.test
+--wait all
+SELECT sz FROM pgsz;
+--match 16384 16384 16384 16384 16384
diff --git a/mptest/config02.test b/mptest/config02.test
new file mode 100644
index 0000000..7d4b278
--- /dev/null
+++ b/mptest/config02.test
@@ -0,0 +1,123 @@
+/*
+** Configure five tasks in different ways, then run tests.
+*/
+PRAGMA page_size=512;
+--task 1
+ PRAGMA mmap_size=0;
+--end
+--task 2
+ PRAGMA mmap_size=28672;
+--end
+--task 3
+ PRAGMA mmap_size=8192;
+--end
+--task 4
+ PRAGMA mmap_size=65536;
+--end
+--task 5
+ PRAGMA mmap_size=268435456;
+--end
+--source multiwrite01.test
+--source crash02.subtest
+PRAGMA page_size=1024;
+VACUUM;
+CREATE TABLE pgsz(taskid, sz INTEGER);
+--task 1
+ INSERT INTO pgsz VALUES(1, eval('PRAGMA page_size'));
+--end
+--task 2
+ INSERT INTO pgsz VALUES(2, eval('PRAGMA page_size'));
+--end
+--task 3
+ INSERT INTO pgsz VALUES(3, eval('PRAGMA page_size'));
+--end
+--task 4
+ INSERT INTO pgsz VALUES(4, eval('PRAGMA page_size'));
+--end
+--task 5
+ INSERT INTO pgsz VALUES(5, eval('PRAGMA page_size'));
+--end
+--source multiwrite01.test
+--source crash02.subtest
+--wait all
+SELECT sz FROM pgsz;
+--match 1024 1024 1024 1024 1024
+PRAGMA page_size=2048;
+VACUUM;
+DELETE FROM pgsz;
+--task 1
+ INSERT INTO pgsz VALUES(1, eval('PRAGMA page_size'));
+--end
+--task 2
+ INSERT INTO pgsz VALUES(2, eval('PRAGMA page_size'));
+--end
+--task 3
+ INSERT INTO pgsz VALUES(3, eval('PRAGMA page_size'));
+--end
+--task 4
+ INSERT INTO pgsz VALUES(4, eval('PRAGMA page_size'));
+--end
+--task 5
+ INSERT INTO pgsz VALUES(5, eval('PRAGMA page_size'));
+--end
+--source multiwrite01.test
+--source crash02.subtest
+--wait all
+SELECT sz FROM pgsz;
+--match 2048 2048 2048 2048 2048
+PRAGMA page_size=8192;
+VACUUM;
+DELETE FROM pgsz;
+--task 1
+ INSERT INTO pgsz VALUES(1, eval('PRAGMA page_size'));
+--end
+--task 2
+ INSERT INTO pgsz VALUES(2, eval('PRAGMA page_size'));
+--end
+--task 3
+ INSERT INTO pgsz VALUES(3, eval('PRAGMA page_size'));
+--end
+--task 4
+ INSERT INTO pgsz VALUES(4, eval('PRAGMA page_size'));
+--end
+--task 5
+ INSERT INTO pgsz VALUES(5, eval('PRAGMA page_size'));
+--end
+--source multiwrite01.test
+--source crash02.subtest
+--wait all
+SELECT sz FROM pgsz;
+--match 8192 8192 8192 8192 8192
+PRAGMA page_size=16384;
+VACUUM;
+DELETE FROM pgsz;
+--task 1
+ INSERT INTO pgsz VALUES(1, eval('PRAGMA page_size'));
+--end
+--task 2
+ INSERT INTO pgsz VALUES(2, eval('PRAGMA page_size'));
+--end
+--task 3
+ INSERT INTO pgsz VALUES(3, eval('PRAGMA page_size'));
+--end
+--task 4
+ INSERT INTO pgsz VALUES(4, eval('PRAGMA page_size'));
+--end
+--task 5
+ INSERT INTO pgsz VALUES(5, eval('PRAGMA page_size'));
+--end
+--source multiwrite01.test
+--source crash02.subtest
+--wait all
+SELECT sz FROM pgsz;
+--match 16384 16384 16384 16384 16384
+PRAGMA auto_vacuum=FULL;
+VACUUM;
+--source multiwrite01.test
+--source crash02.subtest
+--wait all
+PRAGMA auto_vacuum=FULL;
+PRAGMA page_size=512;
+VACUUM;
+--source multiwrite01.test
+--source crash02.subtest
diff --git a/mptest/crash01.test b/mptest/crash01.test
new file mode 100644
index 0000000..46f170c
--- /dev/null
+++ b/mptest/crash01.test
@@ -0,0 +1,102 @@
+/* Test cases involving incomplete transactions that must be rolled back.
+*/
+--task 1
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t1 VALUES(1, randomblob(2000));
+ INSERT INTO t1 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t1 SELECT a+2, randomblob(1500) FROM t1;
+ INSERT INTO t1 SELECT a+4, randomblob(1500) FROM t1;
+ INSERT INTO t1 SELECT a+8, randomblob(1500) FROM t1;
+ --sleep 1
+ INSERT INTO t1 SELECT a+16, randomblob(1500) FROM t1;
+ --sleep 1
+ INSERT INTO t1 SELECT a+32, randomblob(1500) FROM t1;
+ SELECT count(*) FROM t1;
+ --match 64
+ SELECT avg(length(b)) FROM t1;
+ --match 1500.0
+ --sleep 2
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t1;
+ --match 247
+ SELECT a FROM t1 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t1b ON t1(b);
+ SELECT a FROM t1 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t1 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+--wait 1
+--task 2
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t2 SELECT a, b FROM t1;
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t2;
+ --match 247
+ SELECT a FROM t2 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t2b ON t2(b);
+ SELECT a FROM t2 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t2 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+--task 3
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t3 SELECT a, b FROM t1;
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t3;
+ --match 247
+ SELECT a FROM t3 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t3b ON t3(b);
+ SELECT a FROM t3 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t3 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+--task 4
+ CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t4 SELECT a, b FROM t1;
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t4;
+ --match 247
+ SELECT a FROM t4 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t4b ON t4(b);
+ SELECT a FROM t4 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t4 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+--task 5
+ CREATE TABLE t5(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t5 SELECT a, b FROM t1;
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t5;
+ --match 247
+ SELECT a FROM t5 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t5b ON t5(b);
+ SELECT a FROM t5 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t5 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+--wait all
+/* After the database file has been set up, run the crash2 subscript
+** multiple times. */
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
+--source crash02.subtest
diff --git a/mptest/crash02.subtest b/mptest/crash02.subtest
new file mode 100644
index 0000000..86f64dd
--- /dev/null
+++ b/mptest/crash02.subtest
@@ -0,0 +1,53 @@
+/*
+** This script is called from crash01.test and config02.test and perhaps other
+** script. After the database file has been set up, make a big rollback
+** journal in client 1, then crash client 1.
+** Then in the other clients, do an integrity check.
+*/
+--task 1 leave-hot-journal
+ --sleep 5
+ --finish
+ PRAGMA cache_size=10;
+ BEGIN;
+ UPDATE t1 SET b=randomblob(20000);
+ UPDATE t2 SET b=randomblob(20000);
+ UPDATE t3 SET b=randomblob(20000);
+ UPDATE t4 SET b=randomblob(20000);
+ UPDATE t5 SET b=randomblob(20000);
+ UPDATE t1 SET b=NULL;
+ UPDATE t2 SET b=NULL;
+ UPDATE t3 SET b=NULL;
+ UPDATE t4 SET b=NULL;
+ UPDATE t5 SET b=NULL;
+ --print Task one crashing an incomplete transaction
+ --exit 1
+--end
+--task 2 integrity_check-2
+ SELECT count(*) FROM t1;
+ --match 64
+ --sleep 100
+ PRAGMA integrity_check(10);
+ --match ok
+--end
+--task 3 integrity_check-3
+ SELECT count(*) FROM t1;
+ --match 64
+ --sleep 100
+ PRAGMA integrity_check(10);
+ --match ok
+--end
+--task 4 integrity_check-4
+ SELECT count(*) FROM t1;
+ --match 64
+ --sleep 100
+ PRAGMA integrity_check(10);
+ --match ok
+--end
+--task 5 integrity_check-5
+ SELECT count(*) FROM t1;
+ --match 64
+ --sleep 100
+ PRAGMA integrity_check(10);
+ --match ok
+--end
+--wait all
diff --git a/mptest/mptest.c b/mptest/mptest.c
new file mode 100644
index 0000000..059ae10
--- /dev/null
+++ b/mptest/mptest.c
@@ -0,0 +1,1401 @@
+/*
+** 2013-04-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 is a program used for testing SQLite, and specifically for testing
+** the ability of independent processes to access the same SQLite database
+** concurrently.
+**
+** Compile this program as follows:
+**
+** gcc -g -c -Wall sqlite3.c $(OPTS)
+** gcc -g -o mptest mptest.c sqlite3.o $(LIBS)
+**
+** Recommended options:
+**
+** -DHAVE_USLEEP
+** -DSQLITE_NO_SYNC
+** -DSQLITE_THREADSAFE=0
+** -DSQLITE_OMIT_LOAD_EXTENSION
+**
+** Run like this:
+**
+** ./mptest $database $script
+**
+** where $database is the database to use for testing and $script is a
+** test script.
+*/
+#include "sqlite3.h"
+#include <stdio.h>
+#if defined(_WIN32)
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+/* The suffix to append to the child command lines, if any */
+#if defined(_WIN32)
+# define GETPID (int)GetCurrentProcessId
+#else
+# define GETPID getpid
+#endif
+
+/* Mark a parameter as unused to suppress compiler warnings */
+#define UNUSED_PARAMETER(x) (void)x
+
+/* Global data
+*/
+static struct Global {
+ char *argv0; /* Name of the executable */
+ const char *zVfs; /* Name of VFS to use. Often NULL meaning "default" */
+ char *zDbFile; /* Name of the database */
+ sqlite3 *db; /* Open connection to database */
+ char *zErrLog; /* Filename for error log */
+ FILE *pErrLog; /* Where to write errors */
+ char *zLog; /* Name of output log file */
+ FILE *pLog; /* Where to write log messages */
+ char zName[32]; /* Symbolic name of this process */
+ int taskId; /* Task ID. 0 means supervisor. */
+ int iTrace; /* Tracing level */
+ int bSqlTrace; /* True to trace SQL commands */
+ int bIgnoreSqlErrors; /* Ignore errors in SQL statements */
+ int nError; /* Number of errors */
+ int nTest; /* Number of --match operators */
+ int iTimeout; /* Milliseconds until a busy timeout */
+ int bSync; /* Call fsync() */
+} g;
+
+/* Default timeout */
+#define DEFAULT_TIMEOUT 10000
+
+/*
+** Print a message adding zPrefix[] to the beginning of every line.
+*/
+static void printWithPrefix(FILE *pOut, const char *zPrefix, const char *zMsg){
+ while( zMsg && zMsg[0] ){
+ int i;
+ for(i=0; zMsg[i] && zMsg[i]!='\n' && zMsg[i]!='\r'; i++){}
+ fprintf(pOut, "%s%.*s\n", zPrefix, i, zMsg);
+ zMsg += i;
+ while( zMsg[0]=='\n' || zMsg[0]=='\r' ) zMsg++;
+ }
+}
+
+/*
+** Compare two pointers to strings, where the pointers might be NULL.
+*/
+static int safe_strcmp(const char *a, const char *b){
+ if( a==b ) return 0;
+ if( a==0 ) return -1;
+ if( b==0 ) return 1;
+ return strcmp(a,b);
+}
+
+/*
+** Return TRUE if string z[] matches glob pattern zGlob[].
+** Return FALSE if the pattern does not match.
+**
+** Globbing rules:
+**
+** '*' Matches any sequence of zero or more characters.
+**
+** '?' Matches exactly one character.
+**
+** [...] Matches one character from the enclosed list of
+** characters.
+**
+** [^...] Matches one character not in the enclosed list.
+**
+** '#' Matches any sequence of one or more digits with an
+** optional + or - sign in front
+*/
+int strglob(const char *zGlob, const char *z){
+ int c, c2;
+ int invert;
+ int seen;
+
+ while( (c = (*(zGlob++)))!=0 ){
+ if( c=='*' ){
+ while( (c=(*(zGlob++))) == '*' || c=='?' ){
+ if( c=='?' && (*(z++))==0 ) return 0;
+ }
+ if( c==0 ){
+ return 1;
+ }else if( c=='[' ){
+ while( *z && strglob(zGlob-1,z) ){
+ z++;
+ }
+ return (*z)!=0;
+ }
+ while( (c2 = (*(z++)))!=0 ){
+ while( c2!=c ){
+ c2 = *(z++);
+ if( c2==0 ) return 0;
+ }
+ if( strglob(zGlob,z) ) return 1;
+ }
+ return 0;
+ }else if( c=='?' ){
+ if( (*(z++))==0 ) return 0;
+ }else if( c=='[' ){
+ int prior_c = 0;
+ seen = 0;
+ invert = 0;
+ c = *(z++);
+ if( c==0 ) return 0;
+ c2 = *(zGlob++);
+ if( c2=='^' ){
+ invert = 1;
+ c2 = *(zGlob++);
+ }
+ if( c2==']' ){
+ if( c==']' ) seen = 1;
+ c2 = *(zGlob++);
+ }
+ while( c2 && c2!=']' ){
+ if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
+ c2 = *(zGlob++);
+ if( c>=prior_c && c<=c2 ) seen = 1;
+ prior_c = 0;
+ }else{
+ if( c==c2 ){
+ seen = 1;
+ }
+ prior_c = c2;
+ }
+ c2 = *(zGlob++);
+ }
+ if( c2==0 || (seen ^ invert)==0 ) return 0;
+ }else if( c=='#' ){
+ if( (z[0]=='-' || z[0]=='+') && isdigit(z[1]) ) z++;
+ if( !isdigit(z[0]) ) return 0;
+ z++;
+ while( isdigit(z[0]) ){ z++; }
+ }else{
+ if( c!=(*(z++)) ) return 0;
+ }
+ }
+ return *z==0;
+}
+
+/*
+** Close output stream pOut if it is not stdout or stderr
+*/
+static void maybeClose(FILE *pOut){
+ if( pOut!=stdout && pOut!=stderr ) fclose(pOut);
+}
+
+/*
+** Print an error message
+*/
+static void errorMessage(const char *zFormat, ...){
+ va_list ap;
+ char *zMsg;
+ char zPrefix[30];
+ va_start(ap, zFormat);
+ zMsg = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:ERROR: ", g.zName);
+ if( g.pLog ){
+ printWithPrefix(g.pLog, zPrefix, zMsg);
+ fflush(g.pLog);
+ }
+ if( g.pErrLog && safe_strcmp(g.zErrLog,g.zLog) ){
+ printWithPrefix(g.pErrLog, zPrefix, zMsg);
+ fflush(g.pErrLog);
+ }
+ sqlite3_free(zMsg);
+ g.nError++;
+}
+
+/* Forward declaration */
+static int trySql(const char*, ...);
+
+/*
+** Print an error message and then quit.
+*/
+static void fatalError(const char *zFormat, ...){
+ va_list ap;
+ char *zMsg;
+ char zPrefix[30];
+ va_start(ap, zFormat);
+ zMsg = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:FATAL: ", g.zName);
+ if( g.pLog ){
+ printWithPrefix(g.pLog, zPrefix, zMsg);
+ fflush(g.pLog);
+ maybeClose(g.pLog);
+ }
+ if( g.pErrLog && safe_strcmp(g.zErrLog,g.zLog) ){
+ printWithPrefix(g.pErrLog, zPrefix, zMsg);
+ fflush(g.pErrLog);
+ maybeClose(g.pErrLog);
+ }
+ sqlite3_free(zMsg);
+ if( g.db ){
+ int nTry = 0;
+ g.iTimeout = 0;
+ while( trySql("UPDATE client SET wantHalt=1;")==SQLITE_BUSY
+ && (nTry++)<100 ){
+ sqlite3_sleep(10);
+ }
+ }
+ sqlite3_close(g.db);
+ exit(1);
+}
+
+
+/*
+** Print a log message
+*/
+static void logMessage(const char *zFormat, ...){
+ va_list ap;
+ char *zMsg;
+ char zPrefix[30];
+ va_start(ap, zFormat);
+ zMsg = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s: ", g.zName);
+ if( g.pLog ){
+ printWithPrefix(g.pLog, zPrefix, zMsg);
+ fflush(g.pLog);
+ }
+ sqlite3_free(zMsg);
+}
+
+/*
+** Return the length of a string omitting trailing whitespace
+*/
+static int clipLength(const char *z){
+ int n = (int)strlen(z);
+ while( n>0 && isspace(z[n-1]) ){ n--; }
+ return n;
+}
+
+/*
+** Auxiliary SQL function to return the name of the VFS
+*/
+static void vfsNameFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ char *zVfs = 0;
+ UNUSED_PARAMETER(argc);
+ UNUSED_PARAMETER(argv);
+ sqlite3_file_control(db, "main", SQLITE_FCNTL_VFSNAME, &zVfs);
+ if( zVfs ){
+ sqlite3_result_text(context, zVfs, -1, sqlite3_free);
+ }
+}
+
+/*
+** Busy handler with a g.iTimeout-millisecond timeout
+*/
+static int busyHandler(void *pCD, int count){
+ UNUSED_PARAMETER(pCD);
+ if( count*10>g.iTimeout ){
+ if( g.iTimeout>0 ) errorMessage("timeout after %dms", g.iTimeout);
+ return 0;
+ }
+ sqlite3_sleep(10);
+ return 1;
+}
+
+/*
+** SQL Trace callback
+*/
+static void sqlTraceCallback(void *NotUsed1, const char *zSql){
+ UNUSED_PARAMETER(NotUsed1);
+ logMessage("[%.*s]", clipLength(zSql), zSql);
+}
+
+/*
+** SQL error log callback
+*/
+static void sqlErrorCallback(void *pArg, int iErrCode, const char *zMsg){
+ UNUSED_PARAMETER(pArg);
+ if( iErrCode==SQLITE_ERROR && g.bIgnoreSqlErrors ) return;
+ if( (iErrCode&0xff)==SQLITE_SCHEMA && g.iTrace<3 ) return;
+ if( g.iTimeout==0 && (iErrCode&0xff)==SQLITE_BUSY && g.iTrace<3 ) return;
+ if( (iErrCode&0xff)==SQLITE_NOTICE ){
+ logMessage("(info) %s", zMsg);
+ }else{
+ errorMessage("(errcode=%d) %s", iErrCode, zMsg);
+ }
+}
+
+/*
+** Prepare an SQL statement. Issue a fatal error if unable.
+*/
+static sqlite3_stmt *prepareSql(const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ int rc;
+ sqlite3_stmt *pStmt = 0;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pStmt);
+ fatalError("%s\n%s\n", sqlite3_errmsg(g.db), zSql);
+ }
+ sqlite3_free(zSql);
+ return pStmt;
+}
+
+/*
+** Run arbitrary SQL. Issue a fatal error on failure.
+*/
+static void runSql(const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ int rc;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ rc = sqlite3_exec(g.db, zSql, 0, 0, 0);
+ if( rc!=SQLITE_OK ){
+ fatalError("%s\n%s\n", sqlite3_errmsg(g.db), zSql);
+ }
+ sqlite3_free(zSql);
+}
+
+/*
+** Try to run arbitrary SQL. Return success code.
+*/
+static int trySql(const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ int rc;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ rc = sqlite3_exec(g.db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ return rc;
+}
+
+/* Structure for holding an arbitrary length string
+*/
+typedef struct String String;
+struct String {
+ char *z; /* the string */
+ int n; /* Slots of z[] used */
+ int nAlloc; /* Slots of z[] allocated */
+};
+
+/* Free a string */
+static void stringFree(String *p){
+ if( p->z ) sqlite3_free(p->z);
+ memset(p, 0, sizeof(*p));
+}
+
+/* Append n bytes of text to a string. If n<0 append the entire string. */
+static void stringAppend(String *p, const char *z, int n){
+ if( n<0 ) n = (int)strlen(z);
+ if( p->n+n>=p->nAlloc ){
+ int nAlloc = p->nAlloc*2 + n + 100;
+ char *z = sqlite3_realloc(p->z, nAlloc);
+ if( z==0 ) fatalError("out of memory");
+ p->z = z;
+ p->nAlloc = nAlloc;
+ }
+ memcpy(p->z+p->n, z, n);
+ p->n += n;
+ p->z[p->n] = 0;
+}
+
+/* Reset a string to an empty string */
+static void stringReset(String *p){
+ if( p->z==0 ) stringAppend(p, " ", 1);
+ p->n = 0;
+ p->z[0] = 0;
+}
+
+/* Append a new token onto the end of the string */
+static void stringAppendTerm(String *p, const char *z){
+ int i;
+ if( p->n ) stringAppend(p, " ", 1);
+ if( z==0 ){
+ stringAppend(p, "nil", 3);
+ return;
+ }
+ for(i=0; z[i] && !isspace(z[i]); i++){}
+ if( i>0 && z[i]==0 ){
+ stringAppend(p, z, i);
+ return;
+ }
+ stringAppend(p, "'", 1);
+ while( z[0] ){
+ for(i=0; z[i] && z[i]!='\''; i++){}
+ if( z[i] ){
+ stringAppend(p, z, i+1);
+ stringAppend(p, "'", 1);
+ z += i+1;
+ }else{
+ stringAppend(p, z, i);
+ break;
+ }
+ }
+ stringAppend(p, "'", 1);
+}
+
+/*
+** Callback function for evalSql()
+*/
+static int evalCallback(void *pCData, int argc, char **argv, char **azCol){
+ String *p = (String*)pCData;
+ int i;
+ UNUSED_PARAMETER(azCol);
+ for(i=0; i<argc; i++) stringAppendTerm(p, argv[i]);
+ return 0;
+}
+
+/*
+** Run arbitrary SQL and record the results in an output string
+** given by the first parameter.
+*/
+static int evalSql(String *p, const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ int rc;
+ char *zErrMsg = 0;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ assert( g.iTimeout>0 );
+ rc = sqlite3_exec(g.db, zSql, evalCallback, p, &zErrMsg);
+ sqlite3_free(zSql);
+ if( rc ){
+ char zErr[30];
+ sqlite3_snprintf(sizeof(zErr), zErr, "error(%d)", rc);
+ stringAppendTerm(p, zErr);
+ if( zErrMsg ){
+ stringAppendTerm(p, zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }
+ return rc;
+}
+
+/*
+** Auxiliary SQL function to recursively evaluate SQL.
+*/
+static void evalFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ const char *zSql = (const char*)sqlite3_value_text(argv[0]);
+ String res;
+ char *zErrMsg = 0;
+ int rc;
+ UNUSED_PARAMETER(argc);
+ memset(&res, 0, sizeof(res));
+ rc = sqlite3_exec(db, zSql, evalCallback, &res, &zErrMsg);
+ if( zErrMsg ){
+ sqlite3_result_error(context, zErrMsg, -1);
+ sqlite3_free(zErrMsg);
+ }else if( rc ){
+ sqlite3_result_error_code(context, rc);
+ }else{
+ sqlite3_result_text(context, res.z, -1, SQLITE_TRANSIENT);
+ }
+ stringFree(&res);
+}
+
+/*
+** Look up the next task for client iClient in the database.
+** Return the task script and the task number and mark that
+** task as being under way.
+*/
+static int startScript(
+ int iClient, /* The client number */
+ char **pzScript, /* Write task script here */
+ int *pTaskId, /* Write task number here */
+ char **pzTaskName /* Name of the task */
+){
+ sqlite3_stmt *pStmt = 0;
+ int taskId;
+ int rc;
+ int totalTime = 0;
+
+ *pzScript = 0;
+ g.iTimeout = 0;
+ while(1){
+ rc = trySql("BEGIN IMMEDIATE");
+ if( rc==SQLITE_BUSY ){
+ sqlite3_sleep(10);
+ totalTime += 10;
+ continue;
+ }
+ if( rc!=SQLITE_OK ){
+ fatalError("in startScript: %s", sqlite3_errmsg(g.db));
+ }
+ if( g.nError || g.nTest ){
+ runSql("UPDATE counters SET nError=nError+%d, nTest=nTest+%d",
+ g.nError, g.nTest);
+ g.nError = 0;
+ g.nTest = 0;
+ }
+ pStmt = prepareSql("SELECT 1 FROM client WHERE id=%d AND wantHalt",iClient);
+ rc = sqlite3_step(pStmt);
+ sqlite3_finalize(pStmt);
+ if( rc==SQLITE_ROW ){
+ runSql("DELETE FROM client WHERE id=%d", iClient);
+ g.iTimeout = DEFAULT_TIMEOUT;
+ runSql("COMMIT TRANSACTION;");
+ return SQLITE_DONE;
+ }
+ pStmt = prepareSql(
+ "SELECT script, id, name FROM task"
+ " WHERE client=%d AND starttime IS NULL"
+ " ORDER BY id LIMIT 1", iClient);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ int n = sqlite3_column_bytes(pStmt, 0);
+ *pzScript = sqlite3_malloc(n+1);
+ strcpy(*pzScript, (const char*)sqlite3_column_text(pStmt, 0));
+ *pTaskId = taskId = sqlite3_column_int(pStmt, 1);
+ *pzTaskName = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 2));
+ sqlite3_finalize(pStmt);
+ runSql("UPDATE task"
+ " SET starttime=strftime('%%Y-%%m-%%d %%H:%%M:%%f','now')"
+ " WHERE id=%d;", taskId);
+ g.iTimeout = DEFAULT_TIMEOUT;
+ runSql("COMMIT TRANSACTION;");
+ return SQLITE_OK;
+ }
+ sqlite3_finalize(pStmt);
+ if( rc==SQLITE_DONE ){
+ if( totalTime>30000 ){
+ errorMessage("Waited over 30 seconds with no work. Giving up.");
+ runSql("DELETE FROM client WHERE id=%d; COMMIT;", iClient);
+ sqlite3_close(g.db);
+ exit(1);
+ }
+ while( trySql("COMMIT")==SQLITE_BUSY ){
+ sqlite3_sleep(10);
+ totalTime += 10;
+ }
+ sqlite3_sleep(100);
+ totalTime += 100;
+ continue;
+ }
+ fatalError("%s", sqlite3_errmsg(g.db));
+ }
+ g.iTimeout = DEFAULT_TIMEOUT;
+}
+
+/*
+** Mark a script as having finished. Remove the CLIENT table entry
+** if bShutdown is true.
+*/
+static int finishScript(int iClient, int taskId, int bShutdown){
+ runSql("UPDATE task"
+ " SET endtime=strftime('%%Y-%%m-%%d %%H:%%M:%%f','now')"
+ " WHERE id=%d;", taskId);
+ if( bShutdown ){
+ runSql("DELETE FROM client WHERE id=%d", iClient);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Start up a client process for iClient, if it is not already
+** running. If the client is already running, then this routine
+** is a no-op.
+*/
+static void startClient(int iClient){
+ runSql("INSERT OR IGNORE INTO client VALUES(%d,0)", iClient);
+ if( sqlite3_changes(g.db) ){
+ char *zSys;
+ int rc;
+ zSys = sqlite3_mprintf("%s \"%s\" --client %d --trace %d",
+ g.argv0, g.zDbFile, iClient, g.iTrace);
+ if( g.bSqlTrace ){
+ zSys = sqlite3_mprintf("%z --sqltrace", zSys);
+ }
+ if( g.bSync ){
+ zSys = sqlite3_mprintf("%z --sync", zSys);
+ }
+ if( g.zVfs ){
+ zSys = sqlite3_mprintf("%z --vfs \"%s\"", zSys, g.zVfs);
+ }
+ if( g.iTrace>=2 ) logMessage("system('%q')", zSys);
+#if !defined(_WIN32)
+ zSys = sqlite3_mprintf("%z &", zSys);
+ rc = system(zSys);
+ if( rc ) errorMessage("system() fails with error code %d", rc);
+#else
+ {
+ STARTUPINFOA startupInfo;
+ PROCESS_INFORMATION processInfo;
+ memset(&startupInfo, 0, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+ memset(&processInfo, 0, sizeof(processInfo));
+ rc = CreateProcessA(NULL, zSys, NULL, NULL, FALSE, 0, NULL, NULL,
+ &startupInfo, &processInfo);
+ if( rc ){
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ }else{
+ errorMessage("CreateProcessA() fails with error code %lu",
+ GetLastError());
+ }
+ }
+#endif
+ sqlite3_free(zSys);
+ }
+}
+
+/*
+** Read the entire content of a file into memory
+*/
+static char *readFile(const char *zFilename){
+ FILE *in = fopen(zFilename, "rb");
+ long sz;
+ char *z;
+ if( in==0 ){
+ fatalError("cannot open \"%s\" for reading", zFilename);
+ }
+ fseek(in, 0, SEEK_END);
+ sz = ftell(in);
+ rewind(in);
+ z = sqlite3_malloc( sz+1 );
+ sz = (long)fread(z, 1, sz, in);
+ z[sz] = 0;
+ fclose(in);
+ return z;
+}
+
+/*
+** Return the length of the next token.
+*/
+static int tokenLength(const char *z, int *pnLine){
+ int n = 0;
+ if( isspace(z[0]) || (z[0]=='/' && z[1]=='*') ){
+ int inC = 0;
+ int c;
+ if( z[0]=='/' ){
+ inC = 1;
+ n = 2;
+ }
+ while( (c = z[n++])!=0 ){
+ if( c=='\n' ) (*pnLine)++;
+ if( isspace(c) ) continue;
+ if( inC && c=='*' && z[n]=='/' ){
+ n++;
+ inC = 0;
+ }else if( !inC && c=='/' && z[n]=='*' ){
+ n++;
+ inC = 1;
+ }else if( !inC ){
+ break;
+ }
+ }
+ n--;
+ }else if( z[0]=='-' && z[1]=='-' ){
+ for(n=2; z[n] && z[n]!='\n'; n++){}
+ if( z[n] ){ (*pnLine)++; n++; }
+ }else if( z[0]=='"' || z[0]=='\'' ){
+ int delim = z[0];
+ for(n=1; z[n]; n++){
+ if( z[n]=='\n' ) (*pnLine)++;
+ if( z[n]==delim ){
+ n++;
+ if( z[n+1]!=delim ) break;
+ }
+ }
+ }else{
+ int c;
+ for(n=1; (c = z[n])!=0 && !isspace(c) && c!='"' && c!='\'' && c!=';'; n++){}
+ }
+ return n;
+}
+
+/*
+** Copy a single token into a string buffer.
+*/
+static int extractToken(const char *zIn, int nIn, char *zOut, int nOut){
+ int i;
+ if( nIn<=0 ){
+ zOut[0] = 0;
+ return 0;
+ }
+ for(i=0; i<nIn && i<nOut-1 && !isspace(zIn[i]); i++){ zOut[i] = zIn[i]; }
+ zOut[i] = 0;
+ return i;
+}
+
+/*
+** Find the number of characters up to the start of the next "--end" token.
+*/
+static int findEnd(const char *z, int *pnLine){
+ int n = 0;
+ while( z[n] && (strncmp(z+n,"--end",5) || !isspace(z[n+5])) ){
+ n += tokenLength(z+n, pnLine);
+ }
+ return n;
+}
+
+/*
+** Find the number of characters up to the first character past the
+** of the next "--endif" or "--else" token. Nested --if commands are
+** also skipped.
+*/
+static int findEndif(const char *z, int stopAtElse, int *pnLine){
+ int n = 0;
+ while( z[n] ){
+ int len = tokenLength(z+n, pnLine);
+ if( (strncmp(z+n,"--endif",7)==0 && isspace(z[n+7]))
+ || (stopAtElse && strncmp(z+n,"--else",6)==0 && isspace(z[n+6]))
+ ){
+ return n+len;
+ }
+ if( strncmp(z+n,"--if",4)==0 && isspace(z[n+4]) ){
+ int skip = findEndif(z+n+len, 0, pnLine);
+ n += skip + len;
+ }else{
+ n += len;
+ }
+ }
+ return n;
+}
+
+/*
+** Wait for a client process to complete all its tasks
+*/
+static void waitForClient(int iClient, int iTimeout, char *zErrPrefix){
+ sqlite3_stmt *pStmt;
+ int rc;
+ if( iClient>0 ){
+ pStmt = prepareSql(
+ "SELECT 1 FROM task"
+ " WHERE client=%d"
+ " AND client IN (SELECT id FROM client)"
+ " AND endtime IS NULL",
+ iClient);
+ }else{
+ pStmt = prepareSql(
+ "SELECT 1 FROM task"
+ " WHERE client IN (SELECT id FROM client)"
+ " AND endtime IS NULL");
+ }
+ g.iTimeout = 0;
+ while( ((rc = sqlite3_step(pStmt))==SQLITE_BUSY || rc==SQLITE_ROW)
+ && iTimeout>0
+ ){
+ sqlite3_reset(pStmt);
+ sqlite3_sleep(50);
+ iTimeout -= 50;
+ }
+ sqlite3_finalize(pStmt);
+ g.iTimeout = DEFAULT_TIMEOUT;
+ if( rc!=SQLITE_DONE ){
+ if( zErrPrefix==0 ) zErrPrefix = "";
+ if( iClient>0 ){
+ errorMessage("%stimeout waiting for client %d", zErrPrefix, iClient);
+ }else{
+ errorMessage("%stimeout waiting for all clients", zErrPrefix);
+ }
+ }
+}
+
+/* Return a pointer to the tail of a filename
+*/
+static char *filenameTail(char *z){
+ int i, j;
+ for(i=j=0; z[i]; i++) if( z[i]=='/' ) j = i+1;
+ return z+j;
+}
+
+/*
+** Interpret zArg as a boolean value. Return either 0 or 1.
+*/
+static int booleanValue(char *zArg){
+ int i;
+ if( zArg==0 ) return 0;
+ for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
+ if( i>0 && zArg[i]==0 ) return atoi(zArg);
+ if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
+ return 1;
+ }
+ if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
+ return 0;
+ }
+ errorMessage("unknown boolean: [%s]", zArg);
+ return 0;
+}
+
+
+/* This routine exists as a convenient place to set a debugger
+** breakpoint.
+*/
+static void test_breakpoint(void){ static volatile int cnt = 0; cnt++; }
+
+/* Maximum number of arguments to a --command */
+#define MX_ARG 2
+
+/*
+** Run a script.
+*/
+static void runScript(
+ int iClient, /* The client number, or 0 for the master */
+ int taskId, /* The task ID for clients. 0 for master */
+ char *zScript, /* Text of the script */
+ char *zFilename /* File from which script was read. */
+){
+ int lineno = 1;
+ int prevLine = 1;
+ int ii = 0;
+ int iBegin = 0;
+ int n, c, j;
+ int len;
+ int nArg;
+ String sResult;
+ char zCmd[30];
+ char zError[1000];
+ char azArg[MX_ARG][100];
+
+ memset(&sResult, 0, sizeof(sResult));
+ stringReset(&sResult);
+ while( (c = zScript[ii])!=0 ){
+ prevLine = lineno;
+ len = tokenLength(zScript+ii, &lineno);
+ if( isspace(c) || (c=='/' && zScript[ii+1]=='*') ){
+ ii += len;
+ continue;
+ }
+ if( c!='-' || zScript[ii+1]!='-' || !isalpha(zScript[ii+2]) ){
+ ii += len;
+ continue;
+ }
+
+ /* Run any prior SQL before processing the new --command */
+ if( ii>iBegin ){
+ char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
+ evalSql(&sResult, zSql);
+ sqlite3_free(zSql);
+ iBegin = ii + len;
+ }
+
+ /* Parse the --command */
+ if( g.iTrace>=2 ) logMessage("%.*s", len, zScript+ii);
+ n = extractToken(zScript+ii+2, len-2, zCmd, sizeof(zCmd));
+ for(nArg=0; n<len-2 && nArg<MX_ARG; nArg++){
+ while( n<len-2 && isspace(zScript[ii+2+n]) ){ n++; }
+ if( n>=len-2 ) break;
+ n += extractToken(zScript+ii+2+n, len-2-n,
+ azArg[nArg], sizeof(azArg[nArg]));
+ }
+ for(j=nArg; j<MX_ARG; j++) azArg[j++][0] = 0;
+
+ /*
+ ** --sleep N
+ **
+ ** Pause for N milliseconds
+ */
+ if( strcmp(zCmd, "sleep")==0 ){
+ sqlite3_sleep(atoi(azArg[0]));
+ }else
+
+ /*
+ ** --exit N
+ **
+ ** Exit this process. If N>0 then exit without shutting down
+ ** SQLite. (In other words, simulate a crash.)
+ */
+ if( strcmp(zCmd, "exit")==0 ){
+ int rc = atoi(azArg[0]);
+ finishScript(iClient, taskId, 1);
+ if( rc==0 ) sqlite3_close(g.db);
+ exit(rc);
+ }else
+
+ /*
+ ** --testcase NAME
+ **
+ ** Begin a new test case. Announce in the log that the test case
+ ** has begun.
+ */
+ if( strcmp(zCmd, "testcase")==0 ){
+ if( g.iTrace==1 ) logMessage("%.*s", len - 1, zScript+ii);
+ stringReset(&sResult);
+ }else
+
+ /*
+ ** --finish
+ **
+ ** Mark the current task as having finished, even if it is not.
+ ** This can be used in conjunction with --exit to simulate a crash.
+ */
+ if( strcmp(zCmd, "finish")==0 && iClient>0 ){
+ finishScript(iClient, taskId, 1);
+ }else
+
+ /*
+ ** --reset
+ **
+ ** Reset accumulated results back to an empty string
+ */
+ if( strcmp(zCmd, "reset")==0 ){
+ stringReset(&sResult);
+ }else
+
+ /*
+ ** --match ANSWER...
+ **
+ ** Check to see if output matches ANSWER. Report an error if not.
+ */
+ if( strcmp(zCmd, "match")==0 ){
+ int jj;
+ char *zAns = zScript+ii;
+ for(jj=7; jj<len-1 && isspace(zAns[jj]); jj++){}
+ zAns += jj;
+ if( len-jj-1!=sResult.n || strncmp(sResult.z, zAns, len-jj-1) ){
+ errorMessage("line %d of %s:\nExpected [%.*s]\n Got [%s]",
+ prevLine, zFilename, len-jj-1, zAns, sResult.z);
+ }
+ g.nTest++;
+ stringReset(&sResult);
+ }else
+
+ /*
+ ** --glob ANSWER...
+ ** --notglob ANSWER....
+ **
+ ** Check to see if output does or does not match the glob pattern
+ ** ANSWER.
+ */
+ if( strcmp(zCmd, "glob")==0 || strcmp(zCmd, "notglob")==0 ){
+ int jj;
+ char *zAns = zScript+ii;
+ char *zCopy;
+ int isGlob = (zCmd[0]=='g');
+ for(jj=9-3*isGlob; jj<len-1 && isspace(zAns[jj]); jj++){}
+ zAns += jj;
+ zCopy = sqlite3_mprintf("%.*s", len-jj-1, zAns);
+ if( (sqlite3_strglob(zCopy, sResult.z)==0)^isGlob ){
+ errorMessage("line %d of %s:\nExpected [%s]\n Got [%s]",
+ prevLine, zFilename, zCopy, sResult.z);
+ }
+ sqlite3_free(zCopy);
+ g.nTest++;
+ stringReset(&sResult);
+ }else
+
+ /*
+ ** --output
+ **
+ ** Output the result of the previous SQL.
+ */
+ if( strcmp(zCmd, "output")==0 ){
+ logMessage("%s", sResult.z);
+ }else
+
+ /*
+ ** --source FILENAME
+ **
+ ** Run a subscript from a separate file.
+ */
+ if( strcmp(zCmd, "source")==0 ){
+ char *zNewFile, *zNewScript;
+ char *zToDel = 0;
+ zNewFile = azArg[0];
+ if( zNewFile[0]!='/' ){
+ int k;
+ for(k=(int)strlen(zFilename)-1; k>=0 && zFilename[k]!='/'; k--){}
+ if( k>0 ){
+ zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile);
+ }
+ }
+ zNewScript = readFile(zNewFile);
+ if( g.iTrace ) logMessage("begin script [%s]\n", zNewFile);
+ runScript(0, 0, zNewScript, zNewFile);
+ sqlite3_free(zNewScript);
+ if( g.iTrace ) logMessage("end script [%s]\n", zNewFile);
+ sqlite3_free(zToDel);
+ }else
+
+ /*
+ ** --print MESSAGE....
+ **
+ ** Output the remainder of the line to the log file
+ */
+ if( strcmp(zCmd, "print")==0 ){
+ int jj;
+ for(jj=7; jj<len && isspace(zScript[ii+jj]); jj++){}
+ logMessage("%.*s", len-jj, zScript+ii+jj);
+ }else
+
+ /*
+ ** --if EXPR
+ **
+ ** Skip forward to the next matching --endif or --else if EXPR is false.
+ */
+ if( strcmp(zCmd, "if")==0 ){
+ int jj, rc;
+ sqlite3_stmt *pStmt;
+ for(jj=4; jj<len && isspace(zScript[ii+jj]); jj++){}
+ pStmt = prepareSql("SELECT %.*s", len-jj, zScript+ii+jj);
+ rc = sqlite3_step(pStmt);
+ if( rc!=SQLITE_ROW || sqlite3_column_int(pStmt, 0)==0 ){
+ ii += findEndif(zScript+ii+len, 1, &lineno);
+ }
+ sqlite3_finalize(pStmt);
+ }else
+
+ /*
+ ** --else
+ **
+ ** This command can only be encountered if currently inside an --if that
+ ** is true. Skip forward to the next matching --endif.
+ */
+ if( strcmp(zCmd, "else")==0 ){
+ ii += findEndif(zScript+ii+len, 0, &lineno);
+ }else
+
+ /*
+ ** --endif
+ **
+ ** This command can only be encountered if currently inside an --if that
+ ** is true or an --else of a false if. This is a no-op.
+ */
+ if( strcmp(zCmd, "endif")==0 ){
+ /* no-op */
+ }else
+
+ /*
+ ** --start CLIENT
+ **
+ ** Start up the given client.
+ */
+ if( strcmp(zCmd, "start")==0 && iClient==0 ){
+ int iNewClient = atoi(azArg[0]);
+ if( iNewClient>0 ){
+ startClient(iNewClient);
+ }
+ }else
+
+ /*
+ ** --wait CLIENT TIMEOUT
+ **
+ ** Wait until all tasks complete for the given client. If CLIENT is
+ ** "all" then wait for all clients to complete. Wait no longer than
+ ** TIMEOUT milliseconds (default 10,000)
+ */
+ if( strcmp(zCmd, "wait")==0 && iClient==0 ){
+ int iTimeout = nArg>=2 ? atoi(azArg[1]) : 10000;
+ sqlite3_snprintf(sizeof(zError),zError,"line %d of %s\n",
+ prevLine, zFilename);
+ waitForClient(atoi(azArg[0]), iTimeout, zError);
+ }else
+
+ /*
+ ** --task CLIENT
+ ** <task-content-here>
+ ** --end
+ **
+ ** Assign work to a client. Start the client if it is not running
+ ** already.
+ */
+ if( strcmp(zCmd, "task")==0 && iClient==0 ){
+ int iTarget = atoi(azArg[0]);
+ int iEnd;
+ char *zTask;
+ char *zTName;
+ iEnd = findEnd(zScript+ii+len, &lineno);
+ if( iTarget<0 ){
+ errorMessage("line %d of %s: bad client number: %d",
+ prevLine, zFilename, iTarget);
+ }else{
+ zTask = sqlite3_mprintf("%.*s", iEnd, zScript+ii+len);
+ if( nArg>1 ){
+ zTName = sqlite3_mprintf("%s", azArg[1]);
+ }else{
+ zTName = sqlite3_mprintf("%s:%d", filenameTail(zFilename), prevLine);
+ }
+ startClient(iTarget);
+ runSql("INSERT INTO task(client,script,name)"
+ " VALUES(%d,'%q',%Q)", iTarget, zTask, zTName);
+ sqlite3_free(zTask);
+ sqlite3_free(zTName);
+ }
+ iEnd += tokenLength(zScript+ii+len+iEnd, &lineno);
+ len += iEnd;
+ iBegin = ii+len;
+ }else
+
+ /*
+ ** --breakpoint
+ **
+ ** This command calls "test_breakpoint()" which is a routine provided
+ ** as a convenient place to set a debugger breakpoint.
+ */
+ if( strcmp(zCmd, "breakpoint")==0 ){
+ test_breakpoint();
+ }else
+
+ /*
+ ** --show-sql-errors BOOLEAN
+ **
+ ** Turn display of SQL errors on and off.
+ */
+ if( strcmp(zCmd, "show-sql-errors")==0 ){
+ g.bIgnoreSqlErrors = nArg>=1 ? !booleanValue(azArg[0]) : 1;
+ }else
+
+
+ /* error */{
+ errorMessage("line %d of %s: unknown command --%s",
+ prevLine, zFilename, zCmd);
+ }
+ ii += len;
+ }
+ if( iBegin<ii ){
+ char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
+ runSql(zSql);
+ sqlite3_free(zSql);
+ }
+ stringFree(&sResult);
+}
+
+/*
+** Look for a command-line option. If present, return a pointer.
+** Return NULL if missing.
+**
+** hasArg==0 means the option is a flag. It is either present or not.
+** hasArg==1 means the option has an argument. Return a pointer to the
+** argument.
+*/
+static char *findOption(
+ char **azArg,
+ int *pnArg,
+ const char *zOption,
+ int hasArg
+){
+ int i, j;
+ char *zReturn = 0;
+ int nArg = *pnArg;
+
+ assert( hasArg==0 || hasArg==1 );
+ for(i=0; i<nArg; i++){
+ const char *z;
+ if( i+hasArg >= nArg ) break;
+ z = azArg[i];
+ if( z[0]!='-' ) continue;
+ z++;
+ if( z[0]=='-' ){
+ if( z[1]==0 ) break;
+ z++;
+ }
+ if( strcmp(z,zOption)==0 ){
+ if( hasArg && i==nArg-1 ){
+ fatalError("command-line option \"--%s\" requires an argument", z);
+ }
+ if( hasArg ){
+ zReturn = azArg[i+1];
+ }else{
+ zReturn = azArg[i];
+ }
+ j = i+1+(hasArg!=0);
+ while( j<nArg ) azArg[i++] = azArg[j++];
+ *pnArg = i;
+ return zReturn;
+ }
+ }
+ return zReturn;
+}
+
+/* Print a usage message for the program and exit */
+static void usage(const char *argv0){
+ int i;
+ const char *zTail = argv0;
+ for(i=0; argv0[i]; i++){
+ if( argv0[i]=='/' ) zTail = argv0+i+1;
+ }
+ fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail);
+ exit(1);
+}
+
+/* Report on unrecognized arguments */
+static void unrecognizedArguments(
+ const char *argv0,
+ int nArg,
+ char **azArg
+){
+ int i;
+ fprintf(stderr,"%s: unrecognized arguments:", argv0);
+ for(i=0; i<nArg; i++){
+ fprintf(stderr," %s", azArg[i]);
+ }
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+int main(int argc, char **argv){
+ const char *zClient;
+ int iClient;
+ int n, i;
+ int openFlags = SQLITE_OPEN_READWRITE;
+ int rc;
+ char *zScript;
+ int taskId;
+ const char *zTrace;
+ const char *zCOption;
+
+ g.argv0 = argv[0];
+ g.iTrace = 1;
+ if( argc<2 ) usage(argv[0]);
+ g.zDbFile = argv[1];
+ if( strglob("*.test", g.zDbFile) ) usage(argv[0]);
+ if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){
+ fprintf(stderr, "SQLite library and header mismatch\n"
+ "Library: %s\n"
+ "Header: %s\n",
+ sqlite3_sourceid(), SQLITE_SOURCE_ID);
+ exit(1);
+ }
+ n = argc-2;
+ sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID());
+ g.zVfs = findOption(argv+2, &n, "vfs", 1);
+ zClient = findOption(argv+2, &n, "client", 1);
+ g.zErrLog = findOption(argv+2, &n, "errlog", 1);
+ g.zLog = findOption(argv+2, &n, "log", 1);
+ zTrace = findOption(argv+2, &n, "trace", 1);
+ if( zTrace ) g.iTrace = atoi(zTrace);
+ if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0;
+ g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0;
+ g.bSync = findOption(argv+2, &n, "sync", 0)!=0;
+ if( g.zErrLog ){
+ g.pErrLog = fopen(g.zErrLog, "a");
+ }else{
+ g.pErrLog = stderr;
+ }
+ if( g.zLog ){
+ g.pLog = fopen(g.zLog, "a");
+ }else{
+ g.pLog = stdout;
+ }
+
+ sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0);
+ if( zClient ){
+ iClient = atoi(zClient);
+ if( iClient<1 ) fatalError("illegal client number: %d\n", iClient);
+ sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d",
+ GETPID(), iClient);
+ }else{
+ if( g.iTrace>0 ){
+ printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" );
+ for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){
+ printf("-DSQLITE_%s\n", zCOption);
+ }
+ fflush(stdout);
+ }
+ iClient = 0;
+ unlink(g.zDbFile);
+ openFlags |= SQLITE_OPEN_CREATE;
+ }
+ rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs);
+ if( rc ) fatalError("cannot open [%s]", g.zDbFile);
+ sqlite3_enable_load_extension(g.db, 1);
+ sqlite3_busy_handler(g.db, busyHandler, 0);
+ sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0,
+ vfsNameFunc, 0, 0);
+ sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0,
+ evalFunc, 0, 0);
+ g.iTimeout = DEFAULT_TIMEOUT;
+ if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0);
+ if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
+ if( iClient>0 ){
+ if( n>0 ) unrecognizedArguments(argv[0], n, argv+2);
+ if( g.iTrace ) logMessage("start-client");
+ while(1){
+ char *zTaskName = 0;
+ rc = startScript(iClient, &zScript, &taskId, &zTaskName);
+ if( rc==SQLITE_DONE ) break;
+ if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId);
+ runScript(iClient, taskId, zScript, zTaskName);
+ if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId);
+ finishScript(iClient, taskId, 0);
+ sqlite3_free(zTaskName);
+ sqlite3_sleep(10);
+ }
+ if( g.iTrace ) logMessage("end-client");
+ }else{
+ sqlite3_stmt *pStmt;
+ int iTimeout;
+ if( n==0 ){
+ fatalError("missing script filename");
+ }
+ if( n>1 ) unrecognizedArguments(argv[0], n, argv+2);
+ runSql(
+ "CREATE TABLE task(\n"
+ " id INTEGER PRIMARY KEY,\n"
+ " name TEXT,\n"
+ " client INTEGER,\n"
+ " starttime DATE,\n"
+ " endtime DATE,\n"
+ " script TEXT\n"
+ ");"
+ "CREATE INDEX task_i1 ON task(client, starttime);\n"
+ "CREATE INDEX task_i2 ON task(client, endtime);\n"
+ "CREATE TABLE counters(nError,nTest);\n"
+ "INSERT INTO counters VALUES(0,0);\n"
+ "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n"
+ );
+ zScript = readFile(argv[2]);
+ if( g.iTrace ) logMessage("begin script [%s]\n", argv[2]);
+ runScript(0, 0, zScript, argv[2]);
+ sqlite3_free(zScript);
+ if( g.iTrace ) logMessage("end script [%s]\n", argv[2]);
+ waitForClient(0, 2000, "during shutdown...\n");
+ trySql("UPDATE client SET wantHalt=1");
+ sqlite3_sleep(10);
+ g.iTimeout = 0;
+ iTimeout = 1000;
+ while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY
+ || rc==SQLITE_ROW) && iTimeout>0 ){
+ sqlite3_sleep(10);
+ iTimeout -= 10;
+ }
+ sqlite3_sleep(100);
+ pStmt = prepareSql("SELECT nError, nTest FROM counters");
+ iTimeout = 1000;
+ while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){
+ sqlite3_sleep(10);
+ iTimeout -= 10;
+ }
+ if( rc==SQLITE_ROW ){
+ g.nError += sqlite3_column_int(pStmt, 0);
+ g.nTest += sqlite3_column_int(pStmt, 1);
+ }
+ sqlite3_finalize(pStmt);
+ }
+ sqlite3_close(g.db);
+ maybeClose(g.pLog);
+ maybeClose(g.pErrLog);
+ if( iClient==0 ){
+ printf("Summary: %d errors in %d tests\n", g.nError, g.nTest);
+ }
+ return g.nError>0;
+}
diff --git a/mptest/multiwrite01.test b/mptest/multiwrite01.test
new file mode 100644
index 0000000..4f88a68
--- /dev/null
+++ b/mptest/multiwrite01.test
@@ -0,0 +1,405 @@
+/*
+** This script sets up five different tasks all writing and updating
+** the database at the same time, but each in its own table.
+*/
+--task 1 build-t1
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t1 VALUES(1, randomblob(2000));
+ INSERT INTO t1 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t1 SELECT a+2, randomblob(1500) FROM t1;
+ INSERT INTO t1 SELECT a+4, randomblob(1500) FROM t1;
+ INSERT INTO t1 SELECT a+8, randomblob(1500) FROM t1;
+ --sleep 1
+ INSERT INTO t1 SELECT a+16, randomblob(1500) FROM t1;
+ --sleep 1
+ INSERT INTO t1 SELECT a+32, randomblob(1500) FROM t1;
+ SELECT count(*) FROM t1;
+ --match 64
+ SELECT avg(length(b)) FROM t1;
+ --match 1500.0
+ --sleep 2
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t1;
+ --match 247
+ SELECT a FROM t1 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t1b ON t1(b);
+ SELECT a FROM t1 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t1 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+
+--task 2 build-t2
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t2 VALUES(1, randomblob(2000));
+ INSERT INTO t2 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t2 SELECT a+2, randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT a+4, randomblob(1500) FROM t2;
+ INSERT INTO t2 SELECT a+8, randomblob(1500) FROM t2;
+ --sleep 1
+ INSERT INTO t2 SELECT a+16, randomblob(1500) FROM t2;
+ --sleep 1
+ INSERT INTO t2 SELECT a+32, randomblob(1500) FROM t2;
+ SELECT count(*) FROM t2;
+ --match 64
+ SELECT avg(length(b)) FROM t2;
+ --match 1500.0
+ --sleep 2
+ UPDATE t2 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t2;
+ --match 247
+ SELECT a FROM t2 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t2b ON t2(b);
+ SELECT a FROM t2 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t2 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+--task 3 build-t3
+ DROP TABLE IF EXISTS t3;
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t3 VALUES(1, randomblob(2000));
+ INSERT INTO t3 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t3 SELECT a+2, randomblob(1500) FROM t3;
+ INSERT INTO t3 SELECT a+4, randomblob(1500) FROM t3;
+ INSERT INTO t3 SELECT a+8, randomblob(1500) FROM t3;
+ --sleep 1
+ INSERT INTO t3 SELECT a+16, randomblob(1500) FROM t3;
+ --sleep 1
+ INSERT INTO t3 SELECT a+32, randomblob(1500) FROM t3;
+ SELECT count(*) FROM t3;
+ --match 64
+ SELECT avg(length(b)) FROM t3;
+ --match 1500.0
+ --sleep 2
+ UPDATE t3 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t3;
+ --match 247
+ SELECT a FROM t3 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t3b ON t3(b);
+ SELECT a FROM t3 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t3 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+--task 4 build-t4
+ DROP TABLE IF EXISTS t4;
+ CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t4 VALUES(1, randomblob(2000));
+ INSERT INTO t4 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t4 SELECT a+2, randomblob(1500) FROM t4;
+ INSERT INTO t4 SELECT a+4, randomblob(1500) FROM t4;
+ INSERT INTO t4 SELECT a+8, randomblob(1500) FROM t4;
+ --sleep 1
+ INSERT INTO t4 SELECT a+16, randomblob(1500) FROM t4;
+ --sleep 1
+ INSERT INTO t4 SELECT a+32, randomblob(1500) FROM t4;
+ SELECT count(*) FROM t4;
+ --match 64
+ SELECT avg(length(b)) FROM t4;
+ --match 1500.0
+ --sleep 2
+ UPDATE t4 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t4;
+ --match 247
+ SELECT a FROM t4 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t4b ON t4(b);
+ SELECT a FROM t4 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t4 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+--task 5 build-t5
+ DROP TABLE IF EXISTS t5;
+ CREATE TABLE t5(a INTEGER PRIMARY KEY, b);
+ --sleep 1
+ INSERT INTO t5 VALUES(1, randomblob(2000));
+ INSERT INTO t5 VALUES(2, randomblob(1000));
+ --sleep 1
+ INSERT INTO t5 SELECT a+2, randomblob(1500) FROM t5;
+ INSERT INTO t5 SELECT a+4, randomblob(1500) FROM t5;
+ INSERT INTO t5 SELECT a+8, randomblob(1500) FROM t5;
+ --sleep 1
+ INSERT INTO t5 SELECT a+16, randomblob(1500) FROM t5;
+ --sleep 1
+ INSERT INTO t5 SELECT a+32, randomblob(1500) FROM t5;
+ SELECT count(*) FROM t5;
+ --match 64
+ SELECT avg(length(b)) FROM t5;
+ --match 1500.0
+ --sleep 2
+ UPDATE t5 SET b='x'||a||'y';
+ SELECT sum(length(b)) FROM t5;
+ --match 247
+ SELECT a FROM t5 WHERE b='x17y';
+ --match 17
+ CREATE INDEX t5b ON t5(b);
+ SELECT a FROM t5 WHERE b='x17y';
+ --match 17
+ SELECT a FROM t5 WHERE b GLOB 'x2?y' ORDER BY b DESC LIMIT 5;
+ --match 29 28 27 26 25
+--end
+
+--wait all
+SELECT count(*), sum(length(b)) FROM t1;
+--match 64 247
+SELECT count(*), sum(length(b)) FROM t2;
+--match 64 247
+SELECT count(*), sum(length(b)) FROM t3;
+--match 64 247
+SELECT count(*), sum(length(b)) FROM t4;
+--match 64 247
+SELECT count(*), sum(length(b)) FROM t5;
+--match 64 247
+
+--task 1
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 5
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 3
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 2
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 4
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--wait all
+
+--task 5
+ DROP INDEX t5b;
+ --sleep 5
+ PRAGMA integrity_check(10);
+ --match ok
+ CREATE INDEX t5b ON t5(b DESC);
+--end
+--task 3
+ DROP INDEX t3b;
+ --sleep 5
+ PRAGMA integrity_check(10);
+ --match ok
+ CREATE INDEX t3b ON t3(b DESC);
+--end
+--task 1
+ DROP INDEX t1b;
+ --sleep 5
+ PRAGMA integrity_check(10);
+ --match ok
+ CREATE INDEX t1b ON t1(b DESC);
+--end
+--task 2
+ DROP INDEX t2b;
+ --sleep 5
+ PRAGMA integrity_check(10);
+ --match ok
+ CREATE INDEX t2b ON t2(b DESC);
+--end
+--task 4
+ DROP INDEX t4b;
+ --sleep 5
+ PRAGMA integrity_check(10);
+ --match ok
+ CREATE INDEX t4b ON t4(b DESC);
+--end
+--wait all
+
+--task 1
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 5
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 3
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 2
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 4
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--wait all
+
+VACUUM;
+PRAGMA integrity_check(10);
+--match ok
+
+--task 1
+ UPDATE t1 SET b=randomblob(20000);
+ --sleep 5
+ UPDATE t1 SET b='x'||a||'y';
+ SELECT a FROM t1 WHERE b='x63y';
+ --match 63
+--end
+--task 2
+ UPDATE t2 SET b=randomblob(20000);
+ --sleep 5
+ UPDATE t2 SET b='x'||a||'y';
+ SELECT a FROM t2 WHERE b='x63y';
+ --match 63
+--end
+--task 3
+ UPDATE t3 SET b=randomblob(20000);
+ --sleep 5
+ UPDATE t3 SET b='x'||a||'y';
+ SELECT a FROM t3 WHERE b='x63y';
+ --match 63
+--end
+--task 4
+ UPDATE t4 SET b=randomblob(20000);
+ --sleep 5
+ UPDATE t4 SET b='x'||a||'y';
+ SELECT a FROM t4 WHERE b='x63y';
+ --match 63
+--end
+--task 5
+ UPDATE t5 SET b=randomblob(20000);
+ --sleep 5
+ UPDATE t5 SET b='x'||a||'y';
+ SELECT a FROM t5 WHERE b='x63y';
+ --match 63
+--end
+--wait all
+
+--task 1
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 5
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 3
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 2
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--task 4
+ SELECT t1.a FROM t1, t2
+ WHERE t2.b GLOB 'x3?y' AND t1.b=('x'||(t2.a+3)||'y')
+ ORDER BY t1.a LIMIT 4
+ --match 33 34 35 36
+ SELECT t3.a FROM t3, t4
+ WHERE t4.b GLOB 'x4?y' AND t3.b=('x'||(t4.a+5)||'y')
+ ORDER BY t3.a LIMIT 7
+ --match 45 46 47 48 49 50 51
+--end
+--wait all
diff --git a/sqlcipher-1.1.8-testkey.db b/sqlcipher-1.1.8-testkey.db
index 36ef196..1e069c5 100644
--- a/sqlcipher-1.1.8-testkey.db
+++ b/sqlcipher-1.1.8-testkey.db
Binary files differ
diff --git a/sqlcipher-2.0-be-testkey.db b/sqlcipher-2.0-be-testkey.db
index f6f70f8..fcabab3 100644
--- a/sqlcipher-2.0-be-testkey.db
+++ b/sqlcipher-2.0-be-testkey.db
Binary files differ
diff --git a/sqlcipher-2.0-beta-testkey.db b/sqlcipher-2.0-beta-testkey.db
index 5db36bb..394c915 100755
--- a/sqlcipher-2.0-beta-testkey.db
+++ b/sqlcipher-2.0-beta-testkey.db
Binary files differ
diff --git a/sqlcipher-2.0-le-testkey.db b/sqlcipher-2.0-le-testkey.db
index 00a431b..15a9723 100644
--- a/sqlcipher-2.0-le-testkey.db
+++ b/sqlcipher-2.0-le-testkey.db
Binary files differ
diff --git a/sqlcipher-3.0-testkey.db b/sqlcipher-3.0-testkey.db
new file mode 100644
index 0000000..6af4430
--- /dev/null
+++ b/sqlcipher-3.0-testkey.db
Binary files differ
diff --git a/sqlcipher.1 b/sqlcipher.1
index 3a83046..1aafd45 100644
--- a/sqlcipher.1
+++ b/sqlcipher.1
@@ -2,7 +2,7 @@
.\" 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 SQLCIPHER 1 "Mon Apr 15 23:49:17 2002"
+.TH SQLCIPHER 1 "Mon Jan 31 11:14:00 2014"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
@@ -49,7 +49,7 @@ a table named "memos" and insert a couple of records into that table:
$
.B sqlcipher mydata.db
.br
-SQLite version 2.0.3
+SQLite version 3.8.3
.br
Enter ".help" for instructions
.br
@@ -108,15 +108,24 @@ sqlite>
.B .help
.nf
.cc |
+.backup ?DB? FILE Backup DB (default "main") to FILE
+.bail ON|OFF Stop after hitting an error. Default OFF
.databases List names and files of attached databases
.dump ?TABLE? ... Dump the database in an SQL text format
+ If TABLE specified, only dump tables matching
+ LIKE pattern TABLE.
.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.
+.explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.
+ With no args, it turns EXPLAIN on.
.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
+.indices ?TABLE? Show names of all indices
+ If TABLE specified, only show indices for tables
+ matching LIKE pattern TABLE.
+.load FILE ?ENTRY? Load an extension library
+.log FILE|off Turn logging on or off. FILE can be stderr/stdout
.mode MODE ?TABLE? Set output mode where MODE is one of:
csv Comma-separated values
column Left-aligned columns. (See .width)
@@ -126,46 +135,76 @@ sqlite>
list Values delimited by .separator string
tabs Tab-separated values
tcl TCL list elements
-.nullvalue STRING Print STRING in place of NULL values
+.nullvalue STRING Use STRING in place of NULL values
+.open ?FILENAME? Close existing database and reopen FILENAME
.output FILENAME Send output to FILENAME
.output stdout Send output to the screen
+.print STRING... Print literal STRING
.prompt MAIN CONTINUE Replace the standard prompts
.quit Exit this program
.read FILENAME Execute SQL in FILENAME
+.restore ?DB? FILE Restore content of DB (default "main") from FILE
.schema ?TABLE? Show the CREATE statements
+ If TABLE specified, only show tables matching
+ LIKE pattern TABLE.
.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
+.stats ON|OFF Turn stats on or off
+.tables ?TABLE? List names of tables
+ If TABLE specified, only list tables matching
+ LIKE pattern TABLE.
.timeout MS Try opening locked tables for MS milliseconds
-.width NUM NUM ... Set column widths for "column" mode
+.trace FILE|off Output each SQL statement as it is run
+.vfsname ?AUX? Print the name of the VFS stack
+.width NUM1 NUM2 ... Set column widths for "column" mode
+.timer ON|OFF Turn the CPU timer measurement on or off
sqlite>
|cc .
.sp
.fi
-
.SH OPTIONS
.B sqlcipher
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.
+.B \-bail
+Stop after hitting an error.
+.TP
+.B \-batch
+Force batch I/O.
+.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
+.BI \-cmd\ command
+run
+.I command
+before reading stdin
+.TP
+.B \-csv
+Set output mode to CSV (comma separated values).
.TP
.B \-echo
Print commands before execution.
.TP
+.BI \-init\ file
+Read and execute commands from
+.I file
+, which can contain a mix of SQL statements and meta-commands.
+.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.
+.B \-help
+Show help on options and exit.
.TP
.B \-html
Query results will be output as simple HTML tables.
.TP
+.B \-interactive
+Force interactive I/O.
+.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
@@ -175,18 +214,28 @@ scripts or other programs
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 '|'.
+.BI \-mmap\ N
+Set default mmap size to
+.I N
+\.
.TP
.BI \-nullvalue\ string
Set string used to represent NULL values. Default is ''
(empty string).
.TP
+.BI \-separator\ separator
+Set output field separator. Default is '|'.
+.TP
+.B \-stats
+Print memory stats before each finalize.
+.TP
.B \-version
Show SQLite version.
.TP
-.B \-help
-Show help on options and exit.
+.BI \-vfs\ name
+Use
+.I name
+as the default VFS.
.SH INIT FILE
@@ -222,8 +271,9 @@ o All other command line options are processed.
.SH SEE ALSO
http://www.sqlcipher.net/
.br
-The sqlite-doc package
+The sqlite3-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>.
+by others). It was subsequently revised by Bill Bumgarner <bbum@mac.com> and
+further updated by Laszlo Boszormenyi <gcs@debian.hu> .
diff --git a/sqlcipher.pc.in b/sqlcipher.pc.in
index c8d0aa9..6ef1a0d 100644
--- a/sqlcipher.pc.in
+++ b/sqlcipher.pc.in
@@ -5,9 +5,9 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
-Name: SQLite
+Name: SQLCipher
Description: SQL database engine
-Version: @RELEASE@
-Libs: -L${libdir} -lsqlite3
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lsqlcipher
Libs.private: @LIBS@
Cflags: -I${includedir}
diff --git a/sqlcipher.xcodeproj/project.pbxproj b/sqlcipher.xcodeproj/project.pbxproj
index 064d843..e984986 100644
--- a/sqlcipher.xcodeproj/project.pbxproj
+++ b/sqlcipher.xcodeproj/project.pbxproj
@@ -21,11 +21,12 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 28B46E6317CD07A700672510 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28B46E6217CD07A600672510 /* Security.framework */; };
9069D0A30FCE1A4D0042E34C /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = 9069D0A20FCE1A4D0042E34C /* sqlite3.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
- 9069D08E0FCE18790042E34C /* PBXContainerItemProxy */ = {
+ 289BE0E7180C4930003E52DA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
proxyType = 1;
@@ -35,6 +36,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
+ 28B46E6217CD07A600672510 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
9069D0A20FCE1A4D0042E34C /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = "<group>"; };
D2AAC046055464E500DB518D /* libsqlcipher.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libsqlcipher.a; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
@@ -44,6 +46,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 28B46E6317CD07A700672510 /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -53,6 +56,7 @@
08FB7794FE84155DC02AAC07 /* sqlcipher */ = {
isa = PBXGroup;
children = (
+ 28B46E6217CD07A600672510 /* Security.framework */,
08FB7795FE84155DC02AAC07 /* Source */,
C6A0FF2B0290797F04C91782 /* Documentation */,
1AB674ADFE9D54B511CA2CBB /* Products */,
@@ -107,7 +111,7 @@
buildRules = (
);
dependencies = (
- 9069D08F0FCE18790042E34C /* PBXTargetDependency */,
+ 289BE0E8180C4930003E52DA /* PBXTargetDependency */,
);
name = sqlcipher;
productName = sqlcipher;
@@ -156,7 +160,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "./configure --enable-tempstore=yes CC=/usr/bin/gcc CFLAGS=\"-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2\"\nmake sqlite3.c\nexit 0";
+ shellScript = "./configure --enable-tempstore=yes --with-crypto-lib=commoncrypto CFLAGS=\"-DSQLITE_HAS_CODEC -DSQLITE_TEMP_STORE=2\"\nmake sqlite3.c\nexit 0";
};
/* End PBXShellScriptBuildPhase section */
@@ -172,10 +176,10 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
- 9069D08F0FCE18790042E34C /* PBXTargetDependency */ = {
+ 289BE0E8180C4930003E52DA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9069D08B0FCE185A0042E34C /* amalgamation */;
- targetProxy = 9069D08E0FCE18790042E34C /* PBXContainerItemProxy */;
+ targetProxy = 289BE0E7180C4930003E52DA /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@@ -189,21 +193,20 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
- HEADER_SEARCH_PATHS = (
- "$(PROJECT_DIR)",
- "$(OPENSSL_SRC)/include",
- );
+ HEADER_SEARCH_PATHS = "$(PROJECT_DIR)";
INSTALL_PATH = /usr/local/lib;
OTHER_CFLAGS = (
"-DSQLITE_HAS_CODEC",
"-DSQLITE_TEMP_STORE=2",
"-DSQLITE_THREADSAFE",
+ "-DSQLCIPHER_CRYPTO_CC",
);
"OTHER_CFLAGS[arch=armv6]" = (
"-mno-thumb",
"-DSQLITE_HAS_CODEC",
"-DSQLITE_TEMP_STORE=2",
"-DSQLITE_THREADSAFE",
+ "-DSQLCIPHER_CRYPTO_CC",
);
OTHER_LDFLAGS = "";
PRODUCT_NAME = sqlcipher;
@@ -217,10 +220,7 @@
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_MODEL_TUNING = G5;
- HEADER_SEARCH_PATHS = (
- "$(PROJECT_DIR)",
- "$(OPENSSL_SRC)/include",
- );
+ HEADER_SEARCH_PATHS = "$(PROJECT_DIR)";
INSTALL_PATH = /usr/local/lib;
OTHER_CFLAGS = (
"-DSQLITE_HAS_CODEC",
@@ -228,6 +228,7 @@
"-DSQLITE_OS_UNIX=1",
"-DSQLITE_TEMP_STORE=2",
"-DSQLITE_THREADSAFE",
+ "-DSQLCIPHER_CRYPTO_CC",
);
"OTHER_CFLAGS[arch=armv6]" = (
"-mno-thumb",
@@ -236,6 +237,7 @@
"-DSQLITE_OS_UNIX=1",
"-DSQLITE_TEMP_STORE=2",
"-DSQLITE_THREADSAFE",
+ "-DSQLCIPHER_CRYPTO_CC",
);
OTHER_LDFLAGS = "";
PRODUCT_NAME = sqlcipher;
@@ -245,10 +247,8 @@
1DEB91F008733DB70010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- "ARCHS[sdk=iphoneos*]" = (
- armv7s,
- armv7,
- );
+ "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
+ "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
"ARCHS[sdk=macosx*]" = (
x86_64,
i386,
@@ -257,31 +257,34 @@
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ "IPHONEOS_DEPLOYMENT_TARGET[arch=arm64]" = 7.0;
ONLY_ACTIVE_ARCH = YES;
- SDKROOT = "$(PLATFORM_NAME)";
+ SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
+ VALID_ARCHS = "arm64 armv7 armv7s x86_64 i386";
};
name = Debug;
};
1DEB91F108733DB70010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_BIT)";
- "ARCHS[sdk=iphoneos*]" = (
- armv7s,
- armv7,
- );
+ "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
+ "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
"ARCHS[sdk=macosx*]" = (
- x86_64,
i386,
+ x86_64,
);
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- SDKROOT = "$(PLATFORM_NAME)";
+ IPHONEOS_DEPLOYMENT_TARGET = 4.3;
+ "IPHONEOS_DEPLOYMENT_TARGET[arch=arm64]" = 7.0;
+ SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphonesimulator macosx iphoneos";
+ VALID_ARCHS = "arm64 armv7 armv7s x86_64 i386";
};
name = Release;
};
diff --git a/src/alter.c b/src/alter.c
index a49d334..64204b7 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -77,8 +77,8 @@ static void renameTableFunc(
assert( len>0 );
} while( token!=TK_LP && token!=TK_USING );
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
- zTableName, tname.z+tname.n);
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
+ zSql, zTableName, tname.z+tname.n);
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
@@ -116,6 +116,7 @@ static void renameParentFunc(
int token; /* Type of token */
UNUSED_PARAMETER(NotUsed);
+ if( zInput==0 || zOld==0 ) return;
for(z=zInput; *z; z=z+n){
n = sqlite3GetToken(z, &token);
if( token==TK_REFERENCES ){
@@ -130,7 +131,7 @@ static void renameParentFunc(
sqlite3Dequote(zParent);
if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
- (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew
+ (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
);
sqlite3DbFree(db, zOutput);
zOutput = zOut;
@@ -216,8 +217,8 @@ static void renameTriggerFunc(
/* Variable tname now contains the token that is the old table-name
** in the CREATE TRIGGER statement.
*/
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
- zTableName, tname.z+tname.n);
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
+ zSql, zTableName, tname.z+tname.n);
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
@@ -469,7 +470,7 @@ void sqlite3AlterRenameTable(
}
#endif
- /* Begin a transaction and code the VerifyCookie for database iDb.
+ /* Begin a transaction for database iDb.
** Then modify the schema cookie (since the ALTER TABLE modifies the
** schema). Open a statement transaction if the table is a virtual
** table.
@@ -605,6 +606,7 @@ void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
sqlite3VdbeUsesBtree(v, iDb);
sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2);
sqlite3VdbeJumpHere(v, j1);
sqlite3ReleaseTempReg(pParse, r1);
@@ -687,7 +689,7 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** can handle (i.e. not CURRENT_TIME etc.)
*/
if( pDflt ){
- sqlite3_value *pVal;
+ sqlite3_value *pVal = 0;
if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
db->mallocFailed = 1;
return;
diff --git a/src/analyze.c b/src/analyze.c
index 9a3e959..f9c03dc 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -1,5 +1,5 @@
/*
-** 2005 July 8
+** 2005-07-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -20,15 +20,23 @@
** CREATE TABLE sqlite_stat1(tbl, idx, stat);
** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
+** CREATE TABLE sqlite_stat4(tbl, idx, nEq, nLt, nDLt, sample);
**
** Additional tables might be added in future releases of SQLite.
** The sqlite_stat2 table is not created or used unless the SQLite version
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
-** The sqlite_stat2 table is superceded by sqlite_stat3, which is only
+** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
** created and used by SQLite versions 3.7.9 and later and with
-** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3
-** is a superset of sqlite_stat2.
+** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
+** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
+** version of sqlite_stat3 and is only available when compiled with
+** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
+** not possible to enable both STAT3 and STAT4 at the same time. If they
+** are both enabled, then STAT4 takes precedence.
+**
+** For most applications, sqlite_stat1 provides all the statisics required
+** for the query planner to make good choices.
**
** Format of sqlite_stat1:
**
@@ -36,7 +44,8 @@
** name in the idx column. The tbl column is the name of the table to
** which the index belongs. In each such row, the stat column will be
** a string consisting of a list of integers. The first integer in this
-** list is the number of rows in the index and in the table. The second
+** list is the number of rows in the index. (This is the same as the
+** number of rows in the table, except for partial indices.) The second
** integer is the average number of rows in the index that have the same
** value in the first column of the index. The third integer is the average
** number of rows in the index that have the same value for the first two
@@ -83,54 +92,82 @@
**
** Format for sqlite_stat3:
**
-** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is
-** used to avoid compatibility problems.
+** The sqlite_stat3 format is a subset of sqlite_stat4. Hence, the
+** sqlite_stat4 format will be described first. Further information
+** about sqlite_stat3 follows the sqlite_stat4 description.
+**
+** Format for sqlite_stat4:
+**
+** As with sqlite_stat2, the sqlite_stat4 table contains histogram data
+** to aid the query planner in choosing good indices based on the values
+** that indexed columns are compared against in the WHERE clauses of
+** queries.
**
-** The format of the sqlite_stat3 table is similar to the format of
-** the sqlite_stat2 table. There are multiple entries for each index.
+** The sqlite_stat4 table contains multiple entries for each index.
** The idx column names the index and the tbl column is the table of the
** index. If the idx and tbl columns are the same, then the sample is
-** of the INTEGER PRIMARY KEY. The sample column is a value taken from
-** the left-most column of the index. The nEq column is the approximate
-** number of entires in the index whose left-most column exactly matches
-** the sample. nLt is the approximate number of entires whose left-most
-** column is less than the sample. The nDLt column is the approximate
-** number of distinct left-most entries in the index that are less than
-** the sample.
+** of the INTEGER PRIMARY KEY. The sample column is a blob which is the
+** binary encoding of a key from the index. The nEq column is a
+** list of integers. The first integer is the approximate number
+** of entries in the index whose left-most column exactly matches
+** the left-most column of the sample. The second integer in nEq
+** is the approximate number of entries in the index where the
+** first two columns match the first two columns of the sample.
+** And so forth. nLt is another list of integers that show the approximate
+** number of entries that are strictly less than the sample. The first
+** integer in nLt contains the number of entries in the index where the
+** left-most column is less than the left-most column of the sample.
+** The K-th integer in the nLt entry is the number of index entries
+** where the first K columns are less than the first K columns of the
+** sample. The nDLt column is like nLt except that it contains the
+** number of distinct entries in the index that are less than the
+** sample.
**
-** Future versions of SQLite might change to store a string containing
-** multiple integers values in the nDLt column of sqlite_stat3. The first
-** integer will be the number of prior index entires that are distinct in
-** the left-most column. The second integer will be the number of prior index
-** entries that are distinct in the first two columns. The third integer
-** will be the number of prior index entries that are distinct in the first
-** three columns. And so forth. With that extension, the nDLt field is
-** similar in function to the sqlite_stat1.stat field.
-**
-** There can be an arbitrary number of sqlite_stat3 entries per index.
-** The ANALYZE command will typically generate sqlite_stat3 tables
+** There can be an arbitrary number of sqlite_stat4 entries per index.
+** The ANALYZE command will typically generate sqlite_stat4 tables
** that contain between 10 and 40 samples which are distributed across
** the key space, though not uniformly, and which include samples with
-** largest possible nEq values.
+** large nEq values.
+**
+** Format for sqlite_stat3 redux:
+**
+** The sqlite_stat3 table is like sqlite_stat4 except that it only
+** looks at the left-most column of the index. The sqlite_stat3.sample
+** column contains the actual value of the left-most column instead
+** of a blob encoding of the complete index key as is found in
+** sqlite_stat4.sample. The nEq, nLt, and nDLt entries of sqlite_stat3
+** all contain just a single integer which is the same as the first
+** integer in the equivalent columns in sqlite_stat4.
*/
#ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h"
+#if defined(SQLITE_ENABLE_STAT4)
+# define IsStat4 1
+# define IsStat3 0
+#elif defined(SQLITE_ENABLE_STAT3)
+# define IsStat4 0
+# define IsStat3 1
+#else
+# define IsStat4 0
+# define IsStat3 0
+# undef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 1
+#endif
+#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
+
/*
-** This routine generates code that opens the sqlite_stat1 table for
-** writing with cursor iStatCur. If the library was built with the
-** SQLITE_ENABLE_STAT3 macro defined, then the sqlite_stat3 table is
-** opened for writing using cursor (iStatCur+1)
+** This routine generates code that opens the sqlite_statN tables.
+** The sqlite_stat1 table is always relevant. sqlite_stat2 is now
+** obsolete. sqlite_stat3 and sqlite_stat4 are only opened when
+** appropriate compile-time options are provided.
**
-** If the sqlite_stat1 tables does not previously exist, it is created.
-** Similarly, if the sqlite_stat3 table does not exist and the library
-** is compiled with SQLITE_ENABLE_STAT3 defined, it is created.
+** If the sqlite_statN tables do not previously exist, it is created.
**
** Argument zWhere may be a pointer to a buffer containing a table name,
** or it may be a NULL pointer. If it is not NULL, then all entries in
-** the sqlite_stat1 and (if applicable) sqlite_stat3 tables associated
-** with the named table are deleted. If zWhere==0, then code is generated
-** to delete all stat table entries.
+** the sqlite_statN tables associated with the named table are deleted.
+** If zWhere==0, then code is generated to delete all stat table entries.
*/
static void openStatTable(
Parse *pParse, /* Parsing context */
@@ -144,18 +181,24 @@ static void openStatTable(
const char *zCols;
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
-#ifdef SQLITE_ENABLE_STAT3
+#if defined(SQLITE_ENABLE_STAT4)
+ { "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat3", 0 },
+#elif defined(SQLITE_ENABLE_STAT3)
{ "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat4", 0 },
+#else
+ { "sqlite_stat3", 0 },
+ { "sqlite_stat4", 0 },
#endif
};
-
- int aRoot[] = {0, 0};
- u8 aCreateTbl[] = {0, 0};
-
int i;
sqlite3 *db = pParse->db;
Db *pDb;
Vdbe *v = sqlite3GetVdbe(pParse);
+ int aRoot[ArraySize(aTable)];
+ u8 aCreateTbl[ArraySize(aTable)];
+
if( v==0 ) return;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3VdbeDb(v)==db );
@@ -168,258 +211,738 @@ static void openStatTable(
const char *zTab = aTable[i].zName;
Table *pStat;
if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
- /* The sqlite_stat[12] table does not exist. Create it. Note that a
- ** side-effect of the CREATE TABLE statement is to leave 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.%s(%s)", pDb->zName, zTab, aTable[i].zCols
- );
- aRoot[i] = pParse->regRoot;
- aCreateTbl[i] = OPFLAG_P2ISREG;
+ if( aTable[i].zCols ){
+ /* The sqlite_statN table does not exist. Create it. Note that a
+ ** side-effect of the CREATE TABLE statement is to leave 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.%s(%s)", pDb->zName, zTab, aTable[i].zCols
+ );
+ aRoot[i] = pParse->regRoot;
+ aCreateTbl[i] = OPFLAG_P2ISREG;
+ }
}else{
/* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
** entire contents of the table. */
aRoot[i] = pStat->tnum;
+ aCreateTbl[i] = 0;
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere
+ "DELETE FROM %Q.%s WHERE %s=%Q",
+ pDb->zName, zTab, zWhereType, zWhere
);
}else{
- /* The sqlite_stat[12] table already exists. Delete all rows. */
+ /* The sqlite_stat[134] table already exists. Delete all rows. */
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
}
}
}
- /* Open the sqlite_stat[13] tables for writing. */
- for(i=0; i<ArraySize(aTable); i++){
- sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
- sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
+ /* Open the sqlite_stat[134] tables for writing. */
+ for(i=0; aTable[i].zCols; i++){
+ assert( i<ArraySize(aTable) );
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
+ VdbeComment((v, aTable[i].zName));
}
}
/*
-** Recommended number of samples for sqlite_stat3
+** Recommended number of samples for sqlite_stat4
*/
-#ifndef SQLITE_STAT3_SAMPLES
-# define SQLITE_STAT3_SAMPLES 24
+#ifndef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 24
#endif
/*
-** Three SQL functions - stat3_init(), stat3_push(), and stat3_pop() -
+** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
-typedef struct Stat3Accum Stat3Accum;
-struct Stat3Accum {
+typedef struct Stat4Accum Stat4Accum;
+typedef struct Stat4Sample Stat4Sample;
+struct Stat4Sample {
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
+ tRowcnt *anDLt; /* sqlite_stat4.nDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ tRowcnt *anLt; /* sqlite_stat4.nLt */
+ union {
+ i64 iRowid; /* Rowid in main table of the key */
+ u8 *aRowid; /* Key for WITHOUT ROWID tables */
+ } u;
+ u32 nRowid; /* Sizeof aRowid[] */
+ u8 isPSample; /* True if a periodic sample */
+ int iCol; /* If !isPSample, the reason for inclusion */
+ u32 iHash; /* Tiebreaker hash */
+#endif
+};
+struct Stat4Accum {
tRowcnt nRow; /* Number of rows in the entire table */
tRowcnt nPSample; /* How often to do a periodic sample */
- int iMin; /* Index of entry with minimum nEq and hash */
+ int nCol; /* Number of columns in index + pk/rowid */
+ int nKeyCol; /* Number of index columns w/o the pk/rowid */
int mxSample; /* Maximum number of samples to accumulate */
- int nSample; /* Current number of samples */
+ Stat4Sample current; /* Current row as a Stat4Sample */
u32 iPrn; /* Pseudo-random number used for sampling */
- struct Stat3Sample {
- i64 iRowid; /* Rowid in main table of the key */
- tRowcnt nEq; /* sqlite_stat3.nEq */
- tRowcnt nLt; /* sqlite_stat3.nLt */
- tRowcnt nDLt; /* sqlite_stat3.nDLt */
- u8 isPSample; /* True if a periodic sample */
- u32 iHash; /* Tiebreaker hash */
- } *a; /* An array of samples */
+ Stat4Sample *aBest; /* Array of nCol best samples */
+ int iMin; /* Index in a[] of entry with minimum score */
+ int nSample; /* Current number of samples */
+ int iGet; /* Index of current sample accessed by stat_get() */
+ Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
+ sqlite3 *db; /* Database connection, for malloc() */
};
-#ifdef SQLITE_ENABLE_STAT3
+/* Reclaim memory used by a Stat4Sample
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleClear(sqlite3 *db, Stat4Sample *p){
+ assert( db!=0 );
+ if( p->nRowid ){
+ sqlite3DbFree(db, p->u.aRowid);
+ p->nRowid = 0;
+ }
+}
+#endif
+
+/* Initialize the BLOB value of a ROWID
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
+ assert( db!=0 );
+ if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
+ p->u.aRowid = sqlite3DbMallocRaw(db, n);
+ if( p->u.aRowid ){
+ p->nRowid = n;
+ memcpy(p->u.aRowid, pData, n);
+ }else{
+ p->nRowid = 0;
+ }
+}
+#endif
+
+/* Initialize the INTEGER value of a ROWID.
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
+ assert( db!=0 );
+ if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
+ p->nRowid = 0;
+ p->u.iRowid = iRowid;
+}
+#endif
+
+
/*
-** Implementation of the stat3_init(C,S) SQL function. The two parameters
-** are the number of rows in the table or index (C) and the number of samples
-** to accumulate (S).
+** Copy the contents of object (*pFrom) into (*pTo).
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
+ pTo->isPSample = pFrom->isPSample;
+ pTo->iCol = pFrom->iCol;
+ pTo->iHash = pFrom->iHash;
+ memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol);
+ if( pFrom->nRowid ){
+ sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid);
+ }else{
+ sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid);
+ }
+}
+#endif
+
+/*
+** Reclaim all memory of a Stat4Accum structure.
+*/
+static void stat4Destructor(void *pOld){
+ Stat4Accum *p = (Stat4Accum*)pOld;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int i;
+ for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
+ for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
+ sampleClear(p->db, &p->current);
+#endif
+ sqlite3DbFree(p->db, p);
+}
+
+/*
+** Implementation of the stat_init(N,K,C) SQL function. The three parameters
+** are:
+** N: The number of columns in the index including the rowid/pk (note 1)
+** K: The number of columns in the index excluding the rowid/pk.
+** C: The number of rows in the index (note 2)
+**
+** Note 1: In the special case of the covering index that implements a
+** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
+** total number of columns in the table.
+**
+** Note 2: C is only used for STAT3 and STAT4.
**
-** This routine allocates the Stat3Accum object.
+** For indexes on ordinary rowid tables, N==K+1. But for indexes on
+** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
+** PRIMARY KEY of the table. The covering index that implements the
+** original WITHOUT ROWID table as N==K as a special case.
**
-** The return value is the Stat3Accum object (P).
+** This routine allocates the Stat4Accum object in heap memory. The return
+** value is a pointer to the the Stat4Accum object encoded as a blob (i.e.
+** the size of the blob is sizeof(void*) bytes).
*/
-static void stat3Init(
+static void statInit(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- Stat3Accum *p;
- tRowcnt nRow;
- int mxSample;
- int n;
+ Stat4Accum *p;
+ int nCol; /* Number of columns in index being sampled */
+ int nKeyCol; /* Number of key columns */
+ int nColUp; /* nCol rounded up for alignment */
+ int n; /* Bytes of space to allocate */
+ sqlite3 *db; /* Database connection */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int mxSample = SQLITE_STAT4_SAMPLES;
+#endif
+ /* Decode the three function arguments */
UNUSED_PARAMETER(argc);
- nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
- mxSample = sqlite3_value_int(argv[1]);
- n = sizeof(*p) + sizeof(p->a[0])*mxSample;
- p = sqlite3MallocZero( n );
+ nCol = sqlite3_value_int(argv[0]);
+ assert( nCol>0 );
+ nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
+ nKeyCol = sqlite3_value_int(argv[1]);
+ assert( nKeyCol<=nCol );
+ assert( nKeyCol>0 );
+
+ /* Allocate the space required for the Stat4Accum object */
+ n = sizeof(*p)
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
+ + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
+ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
+#endif
+ ;
+ db = sqlite3_context_db_handle(context);
+ p = sqlite3DbMallocZero(db, n);
if( p==0 ){
sqlite3_result_error_nomem(context);
return;
}
- p->a = (struct Stat3Sample*)&p[1];
- p->nRow = nRow;
- p->mxSample = mxSample;
- p->nPSample = p->nRow/(mxSample/3+1) + 1;
- sqlite3_randomness(sizeof(p->iPrn), &p->iPrn);
- sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
+
+ p->db = db;
+ p->nRow = 0;
+ p->nCol = nCol;
+ p->nKeyCol = nKeyCol;
+ p->current.anDLt = (tRowcnt*)&p[1];
+ p->current.anEq = &p->current.anDLt[nColUp];
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ u8 *pSpace; /* Allocated space not yet assigned */
+ int i; /* Used to iterate through p->aSample[] */
+
+ p->iGet = -1;
+ p->mxSample = mxSample;
+ p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
+ p->current.anLt = &p->current.anEq[nColUp];
+ p->iPrn = nCol*0x689e962d ^ sqlite3_value_int(argv[2])*0xd0944565;
+
+ /* Set up the Stat4Accum.a[] and aBest[] arrays */
+ p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
+ p->aBest = &p->a[mxSample];
+ pSpace = (u8*)(&p->a[mxSample+nCol]);
+ for(i=0; i<(mxSample+nCol); i++){
+ p->a[i].anEq = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ }
+ assert( (pSpace - (u8*)p)==n );
+
+ for(i=0; i<nCol; i++){
+ p->aBest[i].iCol = i;
+ }
+ }
+#endif
+
+ /* Return a pointer to the allocated object to the caller */
+ sqlite3_result_blob(context, p, sizeof(p), stat4Destructor);
}
-static const FuncDef stat3InitFuncdef = {
- 2, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Init, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_init", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+static const FuncDef statInitFuncdef = {
+ 2+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statInit, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_init", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
};
+#ifdef SQLITE_ENABLE_STAT4
+/*
+** pNew and pOld are both candidate non-periodic samples selected for
+** the same column (pNew->iCol==pOld->iCol). Ignoring this column and
+** considering only any trailing columns and the sample hash value, this
+** function returns true if sample pNew is to be preferred over pOld.
+** In other words, if we assume that the cardinalities of the selected
+** column for pNew and pOld are equal, is pNew to be preferred over pOld.
+**
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
+*/
+static int sampleIsBetterPost(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
+){
+ int nCol = pAccum->nCol;
+ int i;
+ assert( pNew->iCol==pOld->iCol );
+ for(i=pNew->iCol+1; i<nCol; i++){
+ if( pNew->anEq[i]>pOld->anEq[i] ) return 1;
+ if( pNew->anEq[i]<pOld->anEq[i] ) return 0;
+ }
+ if( pNew->iHash>pOld->iHash ) return 1;
+ return 0;
+}
+#endif
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** Return true if pNew is to be preferred over pOld.
+**
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
+*/
+static int sampleIsBetter(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
+){
+ tRowcnt nEqNew = pNew->anEq[pNew->iCol];
+ tRowcnt nEqOld = pOld->anEq[pOld->iCol];
+
+ assert( pOld->isPSample==0 && pNew->isPSample==0 );
+ assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
+
+ if( (nEqNew>nEqOld) ) return 1;
+#ifdef SQLITE_ENABLE_STAT4
+ if( nEqNew==nEqOld ){
+ if( pNew->iCol<pOld->iCol ) return 1;
+ return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
+ }
+ return 0;
+#else
+ return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
+#endif
+}
+
+/*
+** Copy the contents of sample *pNew into the p->a[] array. If necessary,
+** remove the least desirable sample from p->a[] to make room.
+*/
+static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
+ Stat4Sample *pSample = 0;
+ int i;
+
+ assert( IsStat4 || nEqZero==0 );
+
+#ifdef SQLITE_ENABLE_STAT4
+ if( pNew->isPSample==0 ){
+ Stat4Sample *pUpgrade = 0;
+ assert( pNew->anEq[pNew->iCol]>0 );
+
+ /* This sample is being added because the prefix that ends in column
+ ** iCol occurs many times in the table. However, if we have already
+ ** added a sample that shares this prefix, there is no need to add
+ ** this one. Instead, upgrade the priority of the highest priority
+ ** existing sample that shares this prefix. */
+ for(i=p->nSample-1; i>=0; i--){
+ Stat4Sample *pOld = &p->a[i];
+ if( pOld->anEq[pNew->iCol]==0 ){
+ if( pOld->isPSample ) return;
+ assert( pOld->iCol>pNew->iCol );
+ assert( sampleIsBetter(p, pNew, pOld) );
+ if( pUpgrade==0 || sampleIsBetter(p, pOld, pUpgrade) ){
+ pUpgrade = pOld;
+ }
+ }
+ }
+ if( pUpgrade ){
+ pUpgrade->iCol = pNew->iCol;
+ pUpgrade->anEq[pUpgrade->iCol] = pNew->anEq[pUpgrade->iCol];
+ goto find_new_min;
+ }
+ }
+#endif
+
+ /* If necessary, remove sample iMin to make room for the new sample. */
+ if( p->nSample>=p->mxSample ){
+ Stat4Sample *pMin = &p->a[p->iMin];
+ tRowcnt *anEq = pMin->anEq;
+ tRowcnt *anLt = pMin->anLt;
+ tRowcnt *anDLt = pMin->anDLt;
+ sampleClear(p->db, pMin);
+ memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1));
+ pSample = &p->a[p->nSample-1];
+ pSample->nRowid = 0;
+ pSample->anEq = anEq;
+ pSample->anDLt = anDLt;
+ pSample->anLt = anLt;
+ p->nSample = p->mxSample-1;
+ }
+
+ /* The "rows less-than" for the rowid column must be greater than that
+ ** for the last sample in the p->a[] array. Otherwise, the samples would
+ ** be out of order. */
+#ifdef SQLITE_ENABLE_STAT4
+ assert( p->nSample==0
+ || pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
+#endif
+
+ /* Insert the new sample */
+ pSample = &p->a[p->nSample];
+ sampleCopy(p, pSample, pNew);
+ p->nSample++;
+
+ /* Zero the first nEqZero entries in the anEq[] array. */
+ memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
+
+#ifdef SQLITE_ENABLE_STAT4
+ find_new_min:
+#endif
+ if( p->nSample>=p->mxSample ){
+ int iMin = -1;
+ for(i=0; i<p->mxSample; i++){
+ if( p->a[i].isPSample ) continue;
+ if( iMin<0 || sampleIsBetter(p, &p->a[iMin], &p->a[i]) ){
+ iMin = i;
+ }
+ }
+ assert( iMin>=0 );
+ p->iMin = iMin;
+ }
+}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+/*
+** Field iChng of the index being scanned has changed. So at this point
+** p->current contains a sample that reflects the previous row of the
+** index. The value of anEq[iChng] and subsequent anEq[] elements are
+** correct at this point.
+*/
+static void samplePushPrevious(Stat4Accum *p, int iChng){
+#ifdef SQLITE_ENABLE_STAT4
+ int i;
+
+ /* Check if any samples from the aBest[] array should be pushed
+ ** into IndexSample.a[] at this point. */
+ for(i=(p->nCol-2); i>=iChng; i--){
+ Stat4Sample *pBest = &p->aBest[i];
+ pBest->anEq[i] = p->current.anEq[i];
+ if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
+ sampleInsert(p, pBest, i);
+ }
+ }
+
+ /* Update the anEq[] fields of any samples already collected. */
+ for(i=p->nSample-1; i>=0; i--){
+ int j;
+ for(j=iChng; j<p->nCol; j++){
+ if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j];
+ }
+ }
+#endif
+
+#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
+ if( iChng==0 ){
+ tRowcnt nLt = p->current.anLt[0];
+ tRowcnt nEq = p->current.anEq[0];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
+ p->current.isPSample = 1;
+ sampleInsert(p, &p->current, 0);
+ p->current.isPSample = 0;
+ }else
+
+ /* Or if it is a non-periodic sample. Add it in this case too. */
+ if( p->nSample<p->mxSample
+ || sampleIsBetter(p, &p->current, &p->a[p->iMin])
+ ){
+ sampleInsert(p, &p->current, 0);
+ }
+ }
+#endif
+
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ UNUSED_PARAMETER( p );
+ UNUSED_PARAMETER( iChng );
+#endif
+}
/*
-** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The
-** arguments describe a single key instance. This routine makes the
-** decision about whether or not to retain this key for the sqlite_stat3
-** table.
+** Implementation of the stat_push SQL function: stat_push(P,C,R)
+** Arguments:
+**
+** P Pointer to the Stat4Accum object created by stat_init()
+** C Index of left-most column to differ from previous row
+** R Rowid for the current row. Might be a key record for
+** WITHOUT ROWID tables.
**
-** The return value is NULL.
+** This SQL function always returns NULL. It's purpose it to accumulate
+** statistical data and/or samples in the Stat4Accum object about the
+** index being analyzed. The stat_get() SQL function will later be used to
+** extract relevant information for constructing the sqlite_statN tables.
+**
+** The R parameter is only used for STAT3 and STAT4
*/
-static void stat3Push(
+static void statPush(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]);
- tRowcnt nEq = sqlite3_value_int64(argv[0]);
- tRowcnt nLt = sqlite3_value_int64(argv[1]);
- tRowcnt nDLt = sqlite3_value_int64(argv[2]);
- i64 rowid = sqlite3_value_int64(argv[3]);
- u8 isPSample = 0;
- u8 doInsert = 0;
- int iMin = p->iMin;
- struct Stat3Sample *pSample;
int i;
- u32 h;
- UNUSED_PARAMETER(context);
- UNUSED_PARAMETER(argc);
- if( nEq==0 ) return;
- h = p->iPrn = p->iPrn*1103515245 + 12345;
- if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){
- doInsert = isPSample = 1;
- }else if( p->nSample<p->mxSample ){
- doInsert = 1;
+ /* The three function arguments */
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+ int iChng = sqlite3_value_int(argv[1]);
+
+ UNUSED_PARAMETER( argc );
+ UNUSED_PARAMETER( context );
+ assert( p->nCol>0 );
+ assert( iChng<p->nCol );
+
+ if( p->nRow==0 ){
+ /* This is the first call to this function. Do initialization. */
+ for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
}else{
- if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){
- doInsert = 1;
+ /* Second and subsequent calls get processed here */
+ samplePushPrevious(p, iChng);
+
+ /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
+ ** to the current row of the index. */
+ for(i=0; i<iChng; i++){
+ p->current.anEq[i]++;
+ }
+ for(i=iChng; i<p->nCol; i++){
+ p->current.anDLt[i]++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ p->current.anLt[i] += p->current.anEq[i];
+#endif
+ p->current.anEq[i] = 1;
}
}
- if( !doInsert ) return;
- if( p->nSample==p->mxSample ){
- assert( p->nSample - iMin - 1 >= 0 );
- memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1));
- pSample = &p->a[p->nSample-1];
+ p->nRow++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
+ sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
}else{
- pSample = &p->a[p->nSample++];
+ sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
+ sqlite3_value_blob(argv[2]));
}
- pSample->iRowid = rowid;
- pSample->nEq = nEq;
- pSample->nLt = nLt;
- pSample->nDLt = nDLt;
- pSample->iHash = h;
- pSample->isPSample = isPSample;
-
- /* Find the new minimum */
- if( p->nSample==p->mxSample ){
- pSample = p->a;
- i = 0;
- while( pSample->isPSample ){
- i++;
- pSample++;
- assert( i<p->nSample );
+ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
+#endif
+
+#ifdef SQLITE_ENABLE_STAT4
+ {
+ tRowcnt nLt = p->current.anLt[p->nCol-1];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
+ p->current.isPSample = 1;
+ p->current.iCol = 0;
+ sampleInsert(p, &p->current, p->nCol-1);
+ p->current.isPSample = 0;
}
- nEq = pSample->nEq;
- h = pSample->iHash;
- iMin = i;
- for(i++, pSample++; i<p->nSample; i++, pSample++){
- if( pSample->isPSample ) continue;
- if( pSample->nEq<nEq
- || (pSample->nEq==nEq && pSample->iHash<h)
- ){
- iMin = i;
- nEq = pSample->nEq;
- h = pSample->iHash;
+
+ /* Update the aBest[] array. */
+ for(i=0; i<(p->nCol-1); i++){
+ p->current.iCol = i;
+ if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
+ sampleCopy(p, &p->aBest[i], &p->current);
}
}
- p->iMin = iMin;
}
+#endif
}
-static const FuncDef stat3PushFuncdef = {
- 5, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Push, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_push", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+static const FuncDef statPushFuncdef = {
+ 2+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statPush, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_push", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
};
+#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
+#define STAT_GET_ROWID 1 /* "rowid" column of stat[34] entry */
+#define STAT_GET_NEQ 2 /* "neq" column of stat[34] entry */
+#define STAT_GET_NLT 3 /* "nlt" column of stat[34] entry */
+#define STAT_GET_NDLT 4 /* "ndlt" column of stat[34] entry */
+
/*
-** Implementation of the stat3_get(P,N,...) SQL function. This routine is
-** used to query the results. Content is returned for the Nth sqlite_stat3
-** row where N is between 0 and S-1 and S is the number of samples. The
-** value returned depends on the number of arguments.
+** Implementation of the stat_get(P,J) SQL function. This routine is
+** used to query statistical information that has been gathered into
+** the Stat4Accum object by prior calls to stat_push(). The P parameter
+** is a BLOB which is decoded into a pointer to the Stat4Accum objects.
+** The content to returned is determined by the parameter J
+** which is one of the STAT_GET_xxxx values defined above.
**
-** argc==2 result: rowid
-** argc==3 result: nEq
-** argc==4 result: nLt
-** argc==5 result: nDLt
+** If neither STAT3 nor STAT4 are enabled, then J is always
+** STAT_GET_STAT1 and is hence omitted and this routine becomes
+** a one-parameter function, stat_get(P), that always returns the
+** stat1 table entry information.
*/
-static void stat3Get(
+static void statGet(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- int n = sqlite3_value_int(argv[1]);
- Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]);
-
- assert( p!=0 );
- if( p->nSample<=n ) return;
- switch( argc ){
- case 2: sqlite3_result_int64(context, p->a[n].iRowid); break;
- case 3: sqlite3_result_int64(context, p->a[n].nEq); break;
- case 4: sqlite3_result_int64(context, p->a[n].nLt); break;
- default: sqlite3_result_int64(context, p->a[n].nDLt); break;
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* STAT3 and STAT4 have a parameter on this routine. */
+ int eCall = sqlite3_value_int(argv[1]);
+ assert( argc==2 );
+ assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
+ || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
+ || eCall==STAT_GET_NDLT
+ );
+ if( eCall==STAT_GET_STAT1 )
+#else
+ assert( argc==1 );
+#endif
+ {
+ /* Return the value to store in the "stat" column of the sqlite_stat1
+ ** table for this index.
+ **
+ ** The value is a string composed of a list of integers describing
+ ** the index. The first integer in the list is the total number of
+ ** entries in the index. There is one additional integer in the list
+ ** for each indexed column. This additional integer is an estimate of
+ ** the number of rows matched by a stabbing query on the index using
+ ** a key with the corresponding number of fields. In other words,
+ ** if the index is on columns (a,b) and the sqlite_stat1 value is
+ ** "100 10 2", then SQLite estimates that:
+ **
+ ** * the index contains 100 rows,
+ ** * "WHERE a=?" matches 10 rows, and
+ ** * "WHERE a=? AND b=?" matches 2 rows.
+ **
+ ** If D is the count of distinct values and K is the total number of
+ ** rows, then each estimate is computed as:
+ **
+ ** I = (K+D-1)/D
+ */
+ char *z;
+ int i;
+
+ char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+
+ sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
+ z = zRet + sqlite3Strlen30(zRet);
+ for(i=0; i<p->nKeyCol; i++){
+ u64 nDistinct = p->current.anDLt[i] + 1;
+ u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
+ sqlite3_snprintf(24, z, " %llu", iVal);
+ z += sqlite3Strlen30(z);
+ assert( p->current.anEq[i] );
+ }
+ assert( z[0]=='\0' && z>zRet );
+
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
}
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ else if( eCall==STAT_GET_ROWID ){
+ if( p->iGet<0 ){
+ samplePushPrevious(p, 0);
+ p->iGet = 0;
+ }
+ if( p->iGet<p->nSample ){
+ Stat4Sample *pS = p->a + p->iGet;
+ if( pS->nRowid==0 ){
+ sqlite3_result_int64(context, pS->u.iRowid);
+ }else{
+ sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid,
+ SQLITE_TRANSIENT);
+ }
+ }
+ }else{
+ tRowcnt *aCnt = 0;
+
+ assert( p->iGet<p->nSample );
+ switch( eCall ){
+ case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break;
+ case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break;
+ default: {
+ aCnt = p->a[p->iGet].anDLt;
+ p->iGet++;
+ break;
+ }
+ }
+
+ if( IsStat3 ){
+ sqlite3_result_int64(context, (i64)aCnt[0]);
+ }else{
+ char *zRet = sqlite3MallocZero(p->nCol * 25);
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ int i;
+ char *z = zRet;
+ for(i=0; i<p->nCol; i++){
+ sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]);
+ z += sqlite3Strlen30(z);
+ }
+ assert( z[0]=='\0' && z>zRet );
+ z[-1] = '\0';
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ }
+ }
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( argc );
+#endif
}
-static const FuncDef stat3GetFuncdef = {
- -1, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
- 0, /* pUserData */
- 0, /* pNext */
- stat3Get, /* xFunc */
- 0, /* xStep */
- 0, /* xFinalize */
- "stat3_get", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+static const FuncDef statGetFuncdef = {
+ 1+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statGet, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat_get", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
};
-#endif /* SQLITE_ENABLE_STAT3 */
-
-
+static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
+ assert( regOut!=regStat4 && regOut!=regStat4+1 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
+#elif SQLITE_DEBUG
+ assert( iParam==STAT_GET_STAT1 );
+#else
+ UNUSED_PARAMETER( iParam );
+#endif
+ sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4, regOut);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statGetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 1 + IsStat34);
+}
/*
** Generate code to do an analysis of all indices associated with
@@ -430,41 +953,31 @@ static void analyzeOneTable(
Table *pTab, /* Table whose indices are to be analyzed */
Index *pOnlyIdx, /* If not NULL, only analyze this one index */
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
- int iMem /* Available memory locations begin here */
+ int iMem, /* Available memory locations begin here */
+ int iTab /* Next available cursor */
){
sqlite3 *db = pParse->db; /* Database handle */
Index *pIdx; /* An index to being analyzed */
int iIdxCur; /* Cursor open on index being analyzed */
+ int iTabCur; /* Table cursor */
Vdbe *v; /* The virtual machine being built up */
int i; /* Loop counter */
- int topOfLoop; /* The top of the loop */
- int endOfLoop; /* The end of the loop */
int jZeroRows = -1; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
- int regTabname = iMem++; /* Register containing table name */
- int regIdxname = iMem++; /* Register containing index name */
- int regStat1 = iMem++; /* The stat column of sqlite_stat1 */
-#ifdef SQLITE_ENABLE_STAT3
- int regNumEq = regStat1; /* Number of instances. Same as regStat1 */
- int regNumLt = iMem++; /* Number of keys less than regSample */
- int regNumDLt = iMem++; /* Number of distinct keys less than regSample */
- int regSample = iMem++; /* The next sample value */
- int regRowid = regSample; /* Rowid of a sample */
- int regAccum = iMem++; /* Register to hold Stat3Accum object */
- int regLoop = iMem++; /* Loop counter */
- int regCount = iMem++; /* Number of rows in the table or index */
- int regTemp1 = iMem++; /* Intermediate register */
- int regTemp2 = iMem++; /* Intermediate register */
- int once = 1; /* One-time initialization */
- int shortJump = 0; /* Instruction address */
- int iTabCur = pParse->nTab++; /* Table cursor */
+ u8 needTableCnt = 1; /* True to count the table */
+ int regNewRowid = iMem++; /* Rowid for the inserted record */
+ int regStat4 = iMem++; /* Register to hold Stat4Accum object */
+ int regChng = iMem++; /* Index of changed index field */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int regRowid = iMem++; /* Rowid argument passed to stat_push() */
#endif
- int regCol = iMem++; /* Content of a column in analyzed table */
- int regRec = iMem++; /* Register holding completed record */
int regTemp = iMem++; /* Temporary use register */
- int regNewRowid = iMem++; /* Rowid for the inserted record */
-
+ int regTabname = iMem++; /* Register containing table name */
+ int regIdxname = iMem++; /* Register containing index name */
+ int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
+ int regPrev = iMem; /* MUST BE LAST (see below) */
+ pParse->nMem = MAX(pParse->nMem, iMem);
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
return;
@@ -488,215 +1001,271 @@ static void analyzeOneTable(
}
#endif
- /* Establish a read-lock on the table at the shared-cache level. */
+ /* Establish a read-lock on the table at the shared-cache level.
+ ** Open a read-only cursor on the table. Also allocate a cursor number
+ ** to use for scanning indexes (iIdxCur). No index cursor is opened at
+ ** this time though. */
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
-
- iIdxCur = pParse->nTab++;
+ iTabCur = iTab++;
+ iIdxCur = iTab++;
+ pParse->nTab = MAX(pParse->nTab, iTab);
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
+
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int nCol;
- KeyInfo *pKey;
- int addrIfNot = 0; /* address of OP_IfNot */
- int *aChngAddr; /* Array of jump instruction addresses */
+ int nCol; /* Number of columns in pIdx. "N" */
+ int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrNextRow; /* Address of "next_row:" */
+ const char *zIdxName; /* Name of the index */
+ int nColTest; /* Number of columns to test for changes */
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
- VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
- nCol = pIdx->nColumn;
- aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol);
- if( aChngAddr==0 ) continue;
- pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- if( iMem+1+(nCol*2)>pParse->nMem ){
- pParse->nMem = iMem+1+(nCol*2);
+ if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIdx) ){
+ nCol = pIdx->nKeyCol;
+ zIdxName = pTab->zName;
+ nColTest = nCol - 1;
+ }else{
+ nCol = pIdx->nColumn;
+ zIdxName = pIdx->zName;
+ nColTest = pIdx->uniqNotNull ? pIdx->nKeyCol-1 : nCol-1;
}
- /* Open a cursor to the index to be analyzed. */
- assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
- (char *)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
-
/* Populate the register containing the index name. */
- sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, zIdxName, 0);
+ VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName));
-#ifdef SQLITE_ENABLE_STAT3
- if( once ){
- once = 0;
- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- }
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
- sqlite3VdbeAddOp3(v, OP_Null, 0, regSample, regAccum);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
- (char*)&stat3InitFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2);
-#endif /* SQLITE_ENABLE_STAT3 */
-
- /* The block of memory cells initialized here is used as follows.
+ /*
+ ** Pseudo-code for loop that calls stat_push():
+ **
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto chng_addr_0;
**
- ** iMem:
- ** The total number of rows in the table.
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto chng_addr_N
**
- ** iMem+1 .. iMem+nCol:
- ** Number of distinct entries in index considering the
- ** left-most N columns only, where N is between 1 and nCol,
- ** inclusive.
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
**
- ** iMem+nCol+1 .. Mem+2*nCol:
- ** Previous value of indexed columns, from left to right.
+ ** endDistinctTest:
+ ** regRowid = idx(rowid)
+ ** stat_push(P, regChng, regRowid)
+ ** Next csr
+ ** if !eof(csr) goto next_row;
**
- ** Cells iMem through iMem+nCol are initialized to 0. The others are
- ** initialized to contain an SQL NULL.
+ ** end_of_scan:
*/
- for(i=0; i<=nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i);
- }
- for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
- }
- /* Start the analysis loop. This loop runs through all the entries in
- ** the index b-tree. */
- endOfLoop = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
- topOfLoop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */
-
- for(i=0; i<nCol; i++){
- CollSeq *pColl;
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
- if( i==0 ){
- /* Always record the very first row */
- addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
- }
- assert( pIdx->azColl!=0 );
- assert( pIdx->azColl[i]!=0 );
- pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
- aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
- (char*)pColl, P4_COLLSEQ);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- VdbeComment((v, "jump if column %d changed", i));
-#ifdef SQLITE_ENABLE_STAT3
- if( i==0 ){
- sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1);
- VdbeComment((v, "incr repeat count"));
- }
-#endif
- }
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
- for(i=0; i<nCol; i++){
- sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
- if( i==0 ){
- sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
- (char*)&stat3PushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, pIdx->nColumn, regRowid);
- sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt);
- sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq);
-#endif
- }
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
- }
- sqlite3DbFree(db, aChngAddr);
+ /* Make sure there are enough memory cells allocated to accommodate
+ ** the regPrev array and a trailing rowid (the rowid slot is required
+ ** when building a record to insert into the sample column of
+ ** the sqlite_stat4 table. */
+ pParse->nMem = MAX(pParse->nMem, regPrev+nColTest);
- /* Always jump here after updating the iMem+1...iMem+1+nCol counters */
- sqlite3VdbeResolveLabel(v, endOfLoop);
+ /* Open a read-only cursor on the index being analyzed. */
+ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ VdbeComment((v, "%s", pIdx->zName));
- sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
- (char*)&stat3PushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop);
- shortJump =
- sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2);
- sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1);
- sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1);
- sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample);
- sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 3);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 4);
- sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt,
- (char*)&stat3GetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 5);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump);
- sqlite3VdbeJumpHere(v, shortJump+2);
-#endif
-
- /* Store the results in sqlite_stat1.
+ /* Invoke the stat_init() function. The arguments are:
+ **
+ ** (1) the number of columns in the index including the rowid
+ ** (or for a WITHOUT ROWID table, the number of PK columns),
+ ** (2) the number of columns in the key without the rowid/pk
+ ** (3) the number of rows in the index,
**
- ** The result is a single row of the sqlite_stat1 table. The first
- ** two columns are the names of the table and index. The third column
- ** is a string composed of a list of integer statistics about the
- ** index. The first integer in the list is the total number of entries
- ** in the index. There is one additional integer in the list for each
- ** column of the table. This additional integer is a guess of how many
- ** rows of the table the index will select. If D is the count of distinct
- ** values and K is the total number of rows, then the integer is computed
- ** as:
**
- ** I = (K+D-1)/D
+ ** The third argument is only used for STAT3 and STAT4
+ */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
+#endif
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
+ sqlite3VdbeAddOp3(v, OP_Function, 0, regStat4+1, regStat4);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statInitFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2+IsStat34);
+
+ /* Implementation of the following:
+ **
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto next_push_0;
**
- ** If K==0 then no entry is made into the sqlite_stat1 table.
- ** If K>0 then it is always the case the D>0 so division by zero
- ** is never possible.
*/
- sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1);
- if( jZeroRows<0 ){
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
+ addrNextRow = sqlite3VdbeCurrentAddr(v);
+
+ if( nColTest>0 ){
+ int endDistinctTest = sqlite3VdbeMakeLabel(v);
+ int *aGotoChng; /* Array of jump instruction addresses */
+ aGotoChng = sqlite3DbMallocRaw(db, sizeof(int)*nColTest);
+ if( aGotoChng==0 ) continue;
+
+ /*
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto endDistinctTest
+ */
+ sqlite3VdbeAddOp0(v, OP_Goto);
+ addrNextRow = sqlite3VdbeCurrentAddr(v);
+ if( nColTest==1 && pIdx->nKeyCol==1 && IsUniqueIndex(pIdx) ){
+ /* For a single-column UNIQUE index, once we have found a non-NULL
+ ** row, we know that all the rest will be distinct, so skip
+ ** subsequent distinctness tests. */
+ sqlite3VdbeAddOp2(v, OP_NotNull, regPrev, endDistinctTest);
+ VdbeCoverage(v);
+ }
+ for(i=0; i<nColTest; i++){
+ char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
+ aGotoChng[i] =
+ sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, endDistinctTest);
+
+
+ /*
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
+ */
+ sqlite3VdbeJumpHere(v, addrNextRow-1);
+ for(i=0; i<nColTest; i++){
+ sqlite3VdbeJumpHere(v, aGotoChng[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
+ }
+ sqlite3VdbeResolveLabel(v, endDistinctTest);
+ sqlite3DbFree(db, aGotoChng);
}
- for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
- 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, regStat1, regStat1);
+
+ /*
+ ** chng_addr_N:
+ ** regRowid = idx(rowid) // STAT34 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** Next csr
+ ** if !eof(csr) goto next_row;
+ */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( regRowid==(regStat4+2) );
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ int j, k, regKey;
+ regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
+ VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
+ sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
}
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
+#endif
+ assert( regChng==(regStat4+1) );
+ sqlite3VdbeAddOp3(v, OP_Function, 1, regStat4, regTemp);
+ sqlite3VdbeChangeP4(v, -1, (char*)&statPushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
+
+ /* Add the entry to the stat1 table. */
+ callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+
+ /* Add the entries to the stat3 or stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ int regEq = regStat1;
+ int regLt = regStat1+1;
+ int regDLt = regStat1+2;
+ int regSample = regStat1+3;
+ int regCol = regStat1+4;
+ int regSampleRowid = regCol + nCol;
+ int addrNext;
+ int addrIsNull;
+ u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+
+ pParse->nMem = MAX(pParse->nMem, regCol+nCol);
+
+ addrNext = sqlite3VdbeCurrentAddr(v);
+ callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
+ addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
+ VdbeCoverage(v);
+ callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
+ callStatGet(v, regStat4, STAT_GET_NLT, regLt);
+ callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
+ sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
+ /* We know that the regSampleRowid row exists because it was read by
+ ** the previous loop. Thus the not-found jump of seekOp will never
+ ** be taken */
+ VdbeCoverageNeverTaken(v);
+#ifdef SQLITE_ENABLE_STAT3
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
+ pIdx->aiColumn[0], regSample);
+#else
+ for(i=0; i<nCol; i++){
+ i16 iCol = pIdx->aiColumn[i];
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur, iCol, regCol+i);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
+#endif
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
+ sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
+ sqlite3VdbeJumpHere(v, addrIsNull);
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+ /* End of analysis */
+ sqlite3VdbeJumpHere(v, addrRewind);
}
- /* If the table has no indices, create a single sqlite_stat1 entry
- ** containing NULL as the index name and the row count as the content.
+
+ /* Create a single sqlite_stat1 entry containing NULL as the index
+ ** name and the row count as the content.
*/
- if( pTab->pIndex==0 ){
- sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
+ if( pOnlyIdx==0 && needTableCnt ){
VdbeComment((v, "%s", pTab->zName));
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
- }else{
+ sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1);
+ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "aaa", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeJumpHere(v, jZeroRows);
- jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
- sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- if( pParse->nMem<regRec ) pParse->nMem = regRec;
- sqlite3VdbeJumpHere(v, jZeroRows);
}
@@ -720,16 +1289,18 @@ static void analyzeDatabase(Parse *pParse, int iDb){
HashElem *k;
int iStatCur;
int iMem;
+ int iTab;
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
pParse->nTab += 3;
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
+ iTab = pParse->nTab;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
- analyzeOneTable(pParse, pTab, 0, iStatCur, iMem);
+ analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
}
loadAnalysis(pParse, iDb);
}
@@ -754,7 +1325,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
}else{
openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl");
}
- analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1);
+ analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur,pParse->nMem+1,pParse->nTab);
loadAnalysis(pParse, iDb);
}
@@ -778,6 +1349,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
Table *pTab;
Index *pIdx;
Token *pTableName;
+ Vdbe *v;
/* Read the database schema. If an error occurs, leave an error message
** and code in pParse and return NULL. */
@@ -825,6 +1397,8 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
}
}
}
+ v = sqlite3GetVdbe(pParse);
+ if( v ) sqlite3VdbeAddOp0(v, OP_Expire);
}
/*
@@ -838,6 +1412,68 @@ struct analysisInfo {
};
/*
+** The first argument points to a nul-terminated string containing a
+** list of space separated integers. Read the first nOut of these into
+** the array aOut[].
+*/
+static void decodeIntArray(
+ char *zIntArray, /* String containing int array to decode */
+ int nOut, /* Number of slots in aOut[] */
+ tRowcnt *aOut, /* Store integers here */
+ LogEst *aLog, /* Or, if aOut==0, here */
+ Index *pIndex /* Handle extra flags for this index, if not NULL */
+){
+ char *z = zIntArray;
+ int c;
+ int i;
+ tRowcnt v;
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( z==0 ) z = "";
+#else
+ if( NEVER(z==0) ) z = "";
+#endif
+ for(i=0; *z && i<nOut; i++){
+ v = 0;
+ while( (c=z[0])>='0' && c<='9' ){
+ v = v*10 + c - '0';
+ z++;
+ }
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( aOut ){
+ aOut[i] = v;
+ }else
+#else
+ assert( aOut==0 );
+ UNUSED_PARAMETER(aOut);
+#endif
+ {
+ aLog[i] = sqlite3LogEst(v);
+ }
+ if( *z==' ' ) z++;
+ }
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( pIndex!=0 );
+#else
+ if( pIndex )
+#endif
+ while( z[0] ){
+ if( sqlite3_strglob("unordered*", z)==0 ){
+ pIndex->bUnordered = 1;
+ }else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
+ pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
+ }
+#ifdef SQLITE_ENABLE_COSTMULT
+ else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
+ pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
+ }
+#endif
+ while( z[0]!=0 && z[0]!=' ' ) z++;
+ while( z[0]==' ' ) z++;
+ }
+}
+
+/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
@@ -852,8 +1488,6 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
analysisInfo *pInfo = (analysisInfo*)pData;
Index *pIndex;
Table *pTable;
- int i, c, n;
- tRowcnt v;
const char *z;
assert( argc==3 );
@@ -866,28 +1500,29 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pTable==0 ){
return 0;
}
- if( argv[1] ){
- pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
- }else{
+ if( argv[1]==0 ){
pIndex = 0;
+ }else if( sqlite3_stricmp(argv[0],argv[1])==0 ){
+ pIndex = sqlite3PrimaryKeyIndex(pTable);
+ }else{
+ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
}
- n = pIndex ? pIndex->nColumn : 0;
z = argv[2];
- for(i=0; *z && i<=n; i++){
- v = 0;
- while( (c=z[0])>='0' && c<='9' ){
- v = v*10 + c - '0';
- z++;
- }
- if( i==0 ) pTable->nRowEst = v;
- if( pIndex==0 ) break;
- pIndex->aiRowEst[i] = v;
- if( *z==' ' ) z++;
- if( strcmp(z, "unordered")==0 ){
- pIndex->bUnordered = 1;
- break;
- }
+
+ if( pIndex ){
+ pIndex->bUnordered = 0;
+ decodeIntArray((char*)z, pIndex->nKeyCol+1, 0, pIndex->aiRowLogEst, pIndex);
+ if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0];
+ }else{
+ Index fakeIdx;
+ fakeIdx.szIdxRow = pTable->szTabRow;
+#ifdef SQLITE_ENABLE_COSTMULT
+ fakeIdx.pTable = pTable;
+#endif
+ decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx);
+ pTable->szTabRow = fakeIdx.szIdxRow;
}
+
return 0;
}
@@ -896,14 +1531,12 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
- if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
- sqlite3DbFree(db, p->u.z);
- }
+ sqlite3DbFree(db, p->p);
}
sqlite3DbFree(db, pIdx->aSample);
}
@@ -914,31 +1547,100 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
}
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
-** Load content from the sqlite_stat3 table into the Index.aSample[]
-** arrays of all indices.
+** Populate the pIdx->aAvgEq[] array based on the samples currently
+** stored in pIdx->aSample[].
+*/
+static void initAvgEq(Index *pIdx){
+ if( pIdx ){
+ IndexSample *aSample = pIdx->aSample;
+ IndexSample *pFinal = &aSample[pIdx->nSample-1];
+ int iCol;
+ int nCol = 1;
+ if( pIdx->nSampleCol>1 ){
+ /* If this is stat4 data, then calculate aAvgEq[] values for all
+ ** sample columns except the last. The last is always set to 1, as
+ ** once the trailing PK fields are considered all index keys are
+ ** unique. */
+ nCol = pIdx->nSampleCol-1;
+ pIdx->aAvgEq[nCol] = 1;
+ }
+ for(iCol=0; iCol<nCol; iCol++){
+ int i; /* Used to iterate through samples */
+ tRowcnt sumEq = 0; /* Sum of the nEq values */
+ tRowcnt nSum = 0; /* Number of terms contributing to sumEq */
+ tRowcnt avgEq = 0;
+ tRowcnt nDLt = pFinal->anDLt[iCol];
+
+ /* Set nSum to the number of distinct (iCol+1) field prefixes that
+ ** occur in the stat4 table for this index before pFinal. Set
+ ** sumEq to the sum of the nEq values for column iCol for the same
+ ** set (adding the value only once where there exist dupicate
+ ** prefixes). */
+ for(i=0; i<(pIdx->nSample-1); i++){
+ if( aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol] ){
+ sumEq += aSample[i].anEq[iCol];
+ nSum++;
+ }
+ }
+ if( nDLt>nSum ){
+ avgEq = (pFinal->anLt[iCol] - sumEq)/(nDLt - nSum);
+ }
+ if( avgEq==0 ) avgEq = 1;
+ pIdx->aAvgEq[iCol] = avgEq;
+ }
+ }
+}
+
+/*
+** Look up an index by name. Or, if the name of a WITHOUT ROWID table
+** is supplied instead, find the PRIMARY KEY index for that table.
+*/
+static Index *findIndexOrPrimaryKey(
+ sqlite3 *db,
+ const char *zName,
+ const char *zDb
+){
+ Index *pIdx = sqlite3FindIndex(db, zName, zDb);
+ if( pIdx==0 ){
+ Table *pTab = sqlite3FindTable(db, zName, zDb);
+ if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab);
+ }
+ return pIdx;
+}
+
+/*
+** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** into the relevant Index.aSample[] arrays.
+**
+** Arguments zSql1 and zSql2 must point to SQL statements that return
+** data equivalent to the following (statements are different for stat3,
+** see the caller of this function for details):
+**
+** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
+** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
+**
+** where %Q is replaced with the database name before the SQL is executed.
*/
-static int loadStat3(sqlite3 *db, const char *zDb){
+static int loadStatTbl(
+ sqlite3 *db, /* Database handle */
+ int bStat3, /* Assume single column records only */
+ const char *zSql1, /* SQL statement 1 (see above) */
+ const char *zSql2, /* SQL statement 2 (see above) */
+ const char *zDb /* Database name (e.g. "main") */
+){
int rc; /* Result codes from subroutines */
sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
char *zSql; /* Text of the SQL statement */
Index *pPrevIdx = 0; /* Previous index in the loop */
- int idx = 0; /* slot in pIdx->aSample[] for next sample */
- int eType; /* Datatype of a sample */
IndexSample *pSample; /* A slot in pIdx->aSample[] */
assert( db->lookaside.bEnabled==0 );
- if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
- return SQLITE_OK;
- }
-
- zSql = sqlite3MPrintf(db,
- "SELECT idx,count(*) FROM %Q.sqlite_stat3"
- " GROUP BY idx", zDb);
+ zSql = sqlite3MPrintf(db, zSql1, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
@@ -947,30 +1649,54 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int nIdxCol = 1; /* Number of columns in stat4 records */
+
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int nSample; /* Number of samples */
+ int nByte; /* Bytes of space required */
+ int i; /* Bytes of space required */
+ tRowcnt *pSpace;
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
- pIdx = sqlite3FindIndex(db, zIndex, zDb);
- if( pIdx==0 ) continue;
- assert( pIdx->nSample==0 );
- pIdx->nSample = nSample;
- pIdx->aSample = sqlite3DbMallocZero(db, nSample*sizeof(IndexSample));
- pIdx->avgEq = pIdx->aiRowEst[1];
+ pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
+ assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
+ /* Index.nSample is non-zero at this point if data has already been
+ ** loaded from the stat4 table. In this case ignore stat3 data. */
+ if( pIdx==0 || pIdx->nSample ) continue;
+ if( bStat3==0 ){
+ assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nIdxCol = pIdx->nKeyCol;
+ }else{
+ nIdxCol = pIdx->nColumn;
+ }
+ }
+ pIdx->nSampleCol = nIdxCol;
+ nByte = sizeof(IndexSample) * nSample;
+ nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
+ nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
+
+ pIdx->aSample = sqlite3DbMallocZero(db, nByte);
if( pIdx->aSample==0 ){
- db->mallocFailed = 1;
sqlite3_finalize(pStmt);
return SQLITE_NOMEM;
}
+ pSpace = (tRowcnt*)&pIdx->aSample[nSample];
+ pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
+ for(i=0; i<nSample; i++){
+ pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol;
+ }
+ assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) );
}
rc = sqlite3_finalize(pStmt);
if( rc ) return rc;
- zSql = sqlite3MPrintf(db,
- "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb);
+ zSql = sqlite3MPrintf(db, zSql2, zDb);
if( !zSql ){
return SQLITE_NOMEM;
}
@@ -979,86 +1705,88 @@ static int loadStat3(sqlite3 *db, const char *zDb){
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
- char *zIndex; /* Index name */
- Index *pIdx; /* Pointer to the index object */
- int i; /* Loop counter */
- tRowcnt sumEq; /* Sum of the nEq values */
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int nCol = 1; /* Number of columns in index */
zIndex = (char *)sqlite3_column_text(pStmt, 0);
if( zIndex==0 ) continue;
- pIdx = sqlite3FindIndex(db, zIndex, zDb);
+ pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
- if( pIdx==pPrevIdx ){
- idx++;
- }else{
+ /* This next condition is true if data has already been loaded from
+ ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ nCol = pIdx->nSampleCol;
+ if( bStat3 && nCol>1 ) continue;
+ if( pIdx!=pPrevIdx ){
+ initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
- idx = 0;
}
- assert( idx<pIdx->nSample );
- pSample = &pIdx->aSample[idx];
- pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1);
- pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2);
- pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3);
- if( idx==pIdx->nSample-1 ){
- if( pSample->nDLt>0 ){
- for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq;
- pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt;
- }
- if( pIdx->avgEq<=0 ) pIdx->avgEq = 1;
- }
- eType = sqlite3_column_type(pStmt, 4);
- pSample->eType = (u8)eType;
- switch( eType ){
- case SQLITE_INTEGER: {
- pSample->u.i = sqlite3_column_int64(pStmt, 4);
- break;
- }
- case SQLITE_FLOAT: {
- pSample->u.r = sqlite3_column_double(pStmt, 4);
- break;
- }
- case SQLITE_NULL: {
- break;
- }
- default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); {
- const char *z = (const char *)(
- (eType==SQLITE_BLOB) ?
- sqlite3_column_blob(pStmt, 4):
- sqlite3_column_text(pStmt, 4)
- );
- int n = z ? sqlite3_column_bytes(pStmt, 4) : 0;
- pSample->nByte = n;
- if( n < 1){
- pSample->u.z = 0;
- }else{
- pSample->u.z = sqlite3DbMallocRaw(db, n);
- if( pSample->u.z==0 ){
- db->mallocFailed = 1;
- sqlite3_finalize(pStmt);
- return SQLITE_NOMEM;
- }
- memcpy(pSample->u.z, z, n);
- }
- }
+ pSample = &pIdx->aSample[pIdx->nSample];
+ decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0);
+
+ /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
+ ** This is in case the sample record is corrupted. In that case, the
+ ** sqlite3VdbeRecordCompare() may read up to two varints past the
+ ** end of the allocated buffer before it realizes it is dealing with
+ ** a corrupt record. Adding the two 0x00 bytes prevents this from causing
+ ** a buffer overread. */
+ pSample->n = sqlite3_column_bytes(pStmt, 4);
+ pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
+ if( pSample->p==0 ){
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM;
}
+ memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n);
+ pIdx->nSample++;
}
- return sqlite3_finalize(pStmt);
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) initAvgEq(pPrevIdx);
+ return rc;
}
-#endif /* SQLITE_ENABLE_STAT3 */
/*
-** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The
+** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** the Index.aSample[] arrays of all indices.
+*/
+static int loadStat4(sqlite3 *db, const char *zDb){
+ int rc = SQLITE_OK; /* Result codes from subroutines */
+
+ assert( db->lookaside.bEnabled==0 );
+ if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
+ rc = loadStatTbl(db, 0,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
+ zDb
+ );
+ }
+
+ if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
+ rc = loadStatTbl(db, 1,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
+ zDb
+ );
+ }
+
+ return rc;
+}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+/*
+** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat3 are used to populate the
+** arrays. The contents of sqlite_stat3/4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined
-** during compilation and the sqlite_stat3 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
+** during compilation and the sqlite_stat3/4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT3 was defined during compilation and the
-** sqlite_stat3 table is not present in the database, SQLITE_ERROR is
+** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
**
@@ -1080,7 +1808,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
@@ -1104,12 +1832,12 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
- /* Load the statistics from the sqlite_stat3 table. */
-#ifdef SQLITE_ENABLE_STAT3
+ /* Load the statistics from the sqlite_stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( rc==SQLITE_OK ){
int lookasideEnabled = db->lookaside.bEnabled;
db->lookaside.bEnabled = 0;
- rc = loadStat3(db, sInfo.zDatabase);
+ rc = loadStat4(db, sInfo.zDatabase);
db->lookaside.bEnabled = lookasideEnabled;
}
#endif
diff --git a/src/attach.c b/src/attach.c
index b8e1219..89050fd 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -38,10 +38,6 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
if( pExpr ){
if( pExpr->op!=TK_ID ){
rc = sqlite3ResolveExprNames(pName, pExpr);
- if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){
- sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken);
- return SQLITE_ERROR;
- }
}else{
pExpr->op = TK_STRING;
}
@@ -158,6 +154,9 @@ static void attachFunc(
sqlite3PagerLockingMode(pPager, db->dfltLockMode);
sqlite3BtreeSecureDelete(aNew->pBt,
sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+ sqlite3BtreeSetPagerFlags(aNew->pBt, 3 | (db->flags & PAGER_FLAGS_MASK));
+#endif
}
aNew->safety_level = 3;
aNew->zName = sqlite3DbStrDup(db, zName);
@@ -376,8 +375,7 @@ attach_end:
void sqlite3Detach(Parse *pParse, Expr *pDbname){
static const FuncDef detach_func = {
1, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
detachFunc, /* xFunc */
@@ -398,8 +396,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
static const FuncDef attach_func = {
3, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
attachFunc, /* xFunc */
@@ -416,11 +413,8 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
-**
-** The return value indicates whether or not fixation is required. TRUE
-** means we do need to fix the database references, FALSE means we do not.
*/
-int sqlite3FixInit(
+void sqlite3FixInit(
DbFixer *pFix, /* The fixer to be initialized */
Parse *pParse, /* Error messages will be written here */
int iDb, /* This is the database that must be used */
@@ -429,7 +423,6 @@ int sqlite3FixInit(
){
sqlite3 *db;
- if( NEVER(iDb<0) || iDb==1 ) return 0;
db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
@@ -437,7 +430,7 @@ int sqlite3FixInit(
pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
- return 1;
+ pFix->bVarOnly = (iDb==1);
}
/*
@@ -465,15 +458,17 @@ int sqlite3FixSrcList(
if( NEVER(pList==0) ) return 0;
zDb = pFix->zDb;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
+ if( pFix->bVarOnly==0 ){
+ if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return 1;
+ }
+ sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
}
- sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
- pItem->zDatabase = 0;
- pItem->pSchema = pFix->pSchema;
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
@@ -496,9 +491,21 @@ int sqlite3FixSelect(
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
+ return 1;
+ }
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
+ return 1;
+ }
pSelect = pSelect->pPrior;
}
return 0;
@@ -508,7 +515,15 @@ int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */
){
while( pExpr ){
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ) break;
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return 1;
+ }
+ }
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ) break;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
}else{
diff --git a/src/backup.c b/src/backup.c
index 252f61c..4a6bc74 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -15,12 +15,6 @@
#include "sqliteInt.h"
#include "btreeInt.h"
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
/*
** Structure allocated for each backup operation.
*/
@@ -102,6 +96,7 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
rc = SQLITE_ERROR;
}
sqlite3DbFree(pErrorDb, pParse->zErrMsg);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(pErrorDb, pParse);
}
if( rc ){
@@ -398,7 +393,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
rc = sqlite3PagerAcquire(pSrcPager, iSrcPg, &pSrcPg,
- PAGER_ACQUIRE_READONLY);
+ PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
sqlite3PagerUnref(pSrcPg);
@@ -531,7 +526,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Sync the database file to disk. */
if( rc==SQLITE_OK ){
- rc = sqlite3PagerSync(pDestPager);
+ rc = sqlite3PagerSync(pDestPager, 0);
}
}else{
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
@@ -606,10 +601,10 @@ int sqlite3_backup_finish(sqlite3_backup *p){
/* Set the error code of the destination database handle. */
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
- sqlite3Error(p->pDestDb, rc, 0);
-
- /* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
+ sqlite3Error(p->pDestDb, rc, 0);
+
+ /* Exit the mutexes and free the backup context structure. */
sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
diff --git a/src/btree.c b/src/btree.c
index 3ca6058..60bc7de 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -162,7 +162,7 @@ static int hasSharedCacheTableLock(
** the correct locks are held. So do not bother - just return true.
** This case does not come up very often anyhow.
*/
- if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){
+ if( isIndex && (!pSchema || (pSchema->schemaFlags&DB_SchemaLoaded)==0) ){
return 1;
}
@@ -446,16 +446,11 @@ static int cursorHoldsMutex(BtCursor *p){
}
#endif
-
-#ifndef SQLITE_OMIT_INCRBLOB
/*
-** Invalidate the overflow page-list cache for cursor pCur, if any.
+** Invalidate the overflow cache of the cursor passed as the first argument.
+** on the shared btree structure pBt.
*/
-static void invalidateOverflowCache(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- sqlite3_free(pCur->aOverflow);
- pCur->aOverflow = 0;
-}
+#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl)
/*
** Invalidate the overflow page-list cache for all cursors opened
@@ -469,6 +464,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
}
}
+#ifndef SQLITE_OMIT_INCRBLOB
/*
** This function is called before modifying the contents of a table
** to invalidate any incrblob cursors that are open on the
@@ -491,16 +487,14 @@ static void invalidateIncrblobCursors(
BtShared *pBt = pBtree->pBt;
assert( sqlite3BtreeHoldsMutex(pBtree) );
for(p=pBt->pCursor; p; p=p->pNext){
- if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){
+ if( (p->curFlags & BTCF_Incrblob)!=0 && (isClearTable || p->info.nKey==iRow) ){
p->eState = CURSOR_INVALID;
}
}
}
#else
- /* Stub functions when INCRBLOB is omitted */
- #define invalidateOverflowCache(x)
- #define invalidateAllOverflowCache(x)
+ /* Stub function when INCRBLOB is omitted */
#define invalidateIncrblobCursors(x,y,z)
#endif /* SQLITE_OMIT_INCRBLOB */
@@ -684,7 +678,7 @@ static int btreeMoveto(
){
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
- char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
+ char aSpace[200]; /* Temp space for pIdxKey - to avoid a malloc */
char *pFree = 0;
if( pKey ){
@@ -694,6 +688,10 @@ static int btreeMoveto(
);
if( pIdxKey==0 ) return SQLITE_NOMEM;
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
+ if( pIdxKey->nField==0 ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
+ return SQLITE_CORRUPT_BKPT;
+ }
}else{
pIdxKey = 0;
}
@@ -724,6 +722,9 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
+ if( pCur->skipNext && pCur->eState==CURSOR_VALID ){
+ pCur->eState = CURSOR_SKIPNEXT;
+ }
}
return rc;
}
@@ -739,20 +740,32 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
** 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.
+** integer *pHasMoved is set as follows:
+**
+** 0: The cursor is unchanged
+** 1: The cursor is still pointing at the same row, but the pointers
+** returned by sqlite3BtreeKeyFetch() or sqlite3BtreeDataFetch()
+** might now be invalid because of a balance() or other change to the
+** b-tree.
+** 2: The cursor is no longer pointing to the row. The row might have
+** been deleted out from under the cursor.
*/
int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
int rc;
+ if( pCur->eState==CURSOR_VALID ){
+ *pHasMoved = 0;
+ return SQLITE_OK;
+ }
rc = restoreCursorPosition(pCur);
if( rc ){
- *pHasMoved = 1;
+ *pHasMoved = 2;
return rc;
}
- if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){
- *pHasMoved = 1;
+ if( pCur->eState!=CURSOR_VALID || NEVER(pCur->skipNext!=0) ){
+ *pHasMoved = 2;
}else{
- *pHasMoved = 0;
+ *pHasMoved = 1;
}
return SQLITE_OK;
}
@@ -937,7 +950,8 @@ static void btreeParseCellPtr(
assert( n==4-4*pPage->leaf );
if( pPage->intKey ){
if( pPage->hasData ){
- n += getVarint32(&pCell[n], nPayload);
+ assert( n==0 );
+ n = getVarint32(pCell, nPayload);
}else{
nPayload = 0;
}
@@ -1215,7 +1229,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
}else if( gap+2<=top ){
/* Search the freelist looking for a free slot big enough to satisfy
** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accomadate it.
+ ** the list that is large enough to accommodate it.
*/
int pc, addr;
for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
@@ -1534,13 +1548,12 @@ static void zeroPage(MemPage *pPage, int flags){
memset(&data[hdr], 0, pBt->usableSize - hdr);
}
data[hdr] = (char)flags;
- first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
+ first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
- pPage->hdrOffset = hdr;
pPage->cellOffset = first;
pPage->aDataEnd = &data[pBt->usableSize];
pPage->aCellIdx = &data[first];
@@ -1581,15 +1594,12 @@ static int btreeGetPage(
BtShared *pBt, /* The btree */
Pgno pgno, /* Number of the page to fetch */
MemPage **ppPage, /* Return the page in this parameter */
- int noContent, /* Do not load page content if true */
- int bReadonly /* True if a read-only (mmap) page is ok */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
){
int rc;
DbPage *pDbPage;
- int flags = (noContent ? PAGER_ACQUIRE_NOCONTENT : 0)
- | (bReadonly ? PAGER_ACQUIRE_READONLY : 0);
- assert( noContent==0 || bReadonly==0 );
+ assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY );
assert( sqlite3_mutex_held(pBt->mutex) );
rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
if( rc ) return rc;
@@ -1622,7 +1632,7 @@ static Pgno btreePagecount(BtShared *pBt){
u32 sqlite3BtreeLastPage(Btree *p){
assert( sqlite3BtreeHoldsMutex(p) );
assert( ((p->pBt->nPage)&0x8000000)==0 );
- return (int)btreePagecount(p->pBt);
+ return btreePagecount(p->pBt);
}
/*
@@ -1637,16 +1647,17 @@ static int getAndInitPage(
BtShared *pBt, /* The database file */
Pgno pgno, /* Number of the page to get */
MemPage **ppPage, /* Write the page pointer here */
- int bReadonly /* True if a read-only (mmap) page is ok */
+ int bReadonly /* PAGER_GET_READONLY or 0 */
){
int rc;
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( bReadonly==PAGER_GET_READONLY || bReadonly==0 );
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, pgno, ppPage, 0, bReadonly);
- if( rc==SQLITE_OK ){
+ rc = btreeGetPage(pBt, pgno, ppPage, bReadonly);
+ if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
rc = btreeInitPage(*ppPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
@@ -1667,10 +1678,11 @@ static void releasePage(MemPage *pPage){
if( pPage ){
assert( pPage->aData );
assert( pPage->pBt );
+ assert( pPage->pDbPage!=0 );
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- sqlite3PagerUnref(pPage->pDbPage);
+ sqlite3PagerUnrefNotNull(pPage->pDbPage);
}
}
@@ -2050,6 +2062,18 @@ static int removeFromSharingList(BtShared *pBt){
static void allocateTempSpace(BtShared *pBt){
if( !pBt->pTmpSpace ){
pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+
+ /* One of the uses of pBt->pTmpSpace is to format cells before
+ ** inserting them into a leaf page (function fillInCell()). If
+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
+ ** by the various routines that manipulate binary cells. Which
+ ** can mean that fillInCell() only initializes the first 2 or 3
+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from
+ ** it into a database page. This is not actually a problem, but it
+ ** does cause a valgrind error when the 1 or 2 bytes of unitialized
+ ** data is passed to system call write(). So to avoid this error,
+ ** zero the first 4 bytes of temp space here. */
+ if( pBt->pTmpSpace ) memset(pBt->pTmpSpace, 0, 4);
}
}
@@ -2143,6 +2167,7 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
return SQLITE_OK;
}
+#if SQLITE_MAX_MMAP_SIZE>0
/*
** Change the limit on the amount of the database file that may be
** memory mapped.
@@ -2155,6 +2180,7 @@ int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
/*
** Change the way data is synced to disk in order to increase or decrease
@@ -2165,17 +2191,14 @@ int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-int sqlite3BtreeSetSafetyLevel(
+int sqlite3BtreeSetPagerFlags(
Btree *p, /* The btree to set the safety level on */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int fullSync, /* PRAGMA fullfsync. */
- int ckptFullSync /* PRAGMA checkpoint_fullfync */
+ unsigned pgFlags /* Various PAGER_* flags */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
- assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
- sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
+ sqlite3PagerSetFlags(pBt->pPager, pgFlags);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -2381,7 +2404,7 @@ static int lockBtree(BtShared *pBt){
assert( pBt->pPage1==0 );
rc = sqlite3PagerSharedLock(pBt->pPager);
if( rc!=SQLITE_OK ) return rc;
- rc = btreeGetPage(pBt, 1, &pPage1, 0, 0);
+ rc = btreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
/* Do some checking to help insure the file we opened really is
@@ -2534,7 +2557,8 @@ static int countValidCursors(BtShared *pBt, int wrOnly){
BtCursor *pCur;
int r = 0;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( (wrOnly==0 || pCur->wrFlag) && pCur->eState!=CURSOR_FAULT ) r++;
+ if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0)
+ && pCur->eState!=CURSOR_FAULT ) r++;
}
return r;
}
@@ -2668,7 +2692,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun;
}
- assert( IfNotOmitAV(pBt->bDoTruncate)==0 );
+ assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
/* Write transactions are not possible on a read-only database */
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
@@ -2963,7 +2987,7 @@ static int relocatePage(
** iPtrPage.
*/
if( eType!=PTRMAP_ROOTPAGE ){
- rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0, 0);
+ rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -3047,7 +3071,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
- rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0, 0);
+ rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -3158,7 +3182,7 @@ int sqlite3BtreeIncrVacuum(Btree *p){
/*
** This routine is called prior to sqlite3PagerCommit when a transaction
-** is commited for an auto-vacuum database.
+** is committed for an auto-vacuum database.
**
** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
** the database file should be truncated to during the commit process.
@@ -3273,12 +3297,13 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
*/
static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3 *db = p->db;
assert( sqlite3BtreeHoldsMutex(p) );
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->bDoTruncate = 0;
#endif
- if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
+ if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){
/* If there are other active statements that belong to this database
** handle, downgrade to a read-only transaction. The other statements
** may still be reading from the database. */
@@ -3445,7 +3470,7 @@ int sqlite3BtreeRollback(Btree *p, int tripCode){
/* The rollback may have destroyed the pPage1->aData value. So
** call btreeGetPage() on page 1 again to make
** sure pPage1->aData is set correctly. */
- if( btreeGetPage(pBt, 1, &pPage1, 0, 0)==SQLITE_OK ){
+ if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){
int nPage = get4byte(28+(u8*)pPage1->aData);
testcase( nPage==0 );
if( nPage==0 ) sqlite3PagerPagecount(pBt->pPager, &nPage);
@@ -3608,14 +3633,14 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- pCur->wrFlag = (u8)wrFlag;
+ assert( wrFlag==0 || wrFlag==BTCF_WriteFlag );
+ pCur->curFlags = wrFlag;
pCur->pNext = pBt->pCursor;
if( pCur->pNext ){
pCur->pNext->pPrev = pCur;
}
pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID;
- pCur->cachedRowid = 0;
return SQLITE_OK;
}
int sqlite3BtreeCursor(
@@ -3657,36 +3682,6 @@ void sqlite3BtreeCursorZero(BtCursor *p){
}
/*
-** Set the cached rowid value of every cursor in the same database file
-** as pCur and having the same root page number as pCur. The value is
-** set to iRowid.
-**
-** Only positive rowid values are considered valid for this cache.
-** The cache is initialized to zero, indicating an invalid cache.
-** A btree will work fine with zero or negative rowids. We just cannot
-** cache zero or negative rowids, which means tables that use zero or
-** negative rowids might run a little slower. But in practice, zero
-** or negative rowids are very uncommon so this should not be a problem.
-*/
-void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){
- BtCursor *p;
- for(p=pCur->pBt->pCursor; p; p=p->pNext){
- if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid;
- }
- assert( pCur->cachedRowid==iRowid );
-}
-
-/*
-** Return the cached rowid for the given cursor. A negative or zero
-** return value indicates that the rowid cache is invalid and should be
-** ignored. If the rowid cache has never before been set, then a
-** zero is returned.
-*/
-sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){
- return pCur->cachedRowid;
-}
-
-/*
** Close a cursor. The read lock on the database file is released
** when the last cursor is closed.
*/
@@ -3709,7 +3704,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
releasePage(pCur->apPage[i]);
}
unlockBtreeIfUnused(pBt);
- invalidateOverflowCache(pCur);
+ sqlite3DbFree(pBtree->db, pCur->aOverflow);
/* sqlite3_free(pCur); */
sqlite3BtreeLeave(pBtree);
}
@@ -3737,7 +3732,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
int iPage = pCur->iPage;
memset(&info, 0, sizeof(info));
btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
- assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
+ assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
}
#else
#define assertCellInfo(x)
@@ -3748,7 +3743,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
if( pCur->info.nSize==0 ){
int iPage = pCur->iPage;
btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
- pCur->validNKey = 1;
+ pCur->curFlags |= BTCF_ValidNKey;
}else{
assertCellInfo(pCur);
}
@@ -3758,8 +3753,8 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
#define getCellInfo(pCur) \
if( pCur->info.nSize==0 ){ \
int iPage = pCur->iPage; \
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
- pCur->validNKey = 1; \
+ btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
+ pCur->curFlags |= BTCF_ValidNKey; \
}else{ \
assertCellInfo(pCur); \
}
@@ -3880,7 +3875,7 @@ static int getOverflowPage(
assert( next==0 || rc==SQLITE_DONE );
if( rc==SQLITE_OK ){
- rc = btreeGetPage(pBt, ovfl, &pPage, 0, (ppPage==0));
+ rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0);
assert( rc==SQLITE_OK || pPage==0 );
if( rc==SQLITE_OK ){
next = get4byte(pPage->aData);
@@ -3930,10 +3925,12 @@ static int copyPayload(
/*
** This function is used to read or overwrite payload information
-** for the entry that the pCur cursor is pointing to. If the eOp
-** parameter is 0, this is a read operation (data copied into
-** buffer pBuf). If it is non-zero, a write (data copied from
-** buffer pBuf).
+** for the entry that the pCur cursor is pointing to. The eOp
+** argument is interpreted as follows:
+**
+** 0: The operation is a read. Populate the overflow cache.
+** 1: The operation is a write. Populate the overflow cache.
+** 2: The operation is a read. Do not populate the overflow cache.
**
** A total of "amt" bytes are read or written beginning at "offset".
** Data is read to or from the buffer pBuf.
@@ -3941,11 +3938,11 @@ static int copyPayload(
** The content being read or written might appear on the main page
** or be scattered out on multiple overflow pages.
**
-** If the BtCursor.isIncrblobHandle flag is set, and the current
-** cursor entry uses one or more overflow pages, this function
-** allocates space for and lazily popluates the overflow page-list
-** cache array (BtCursor.aOverflow). Subsequent calls use this
-** cache to make seeking to the supplied offset more efficient.
+** If the current cursor entry uses one or more overflow pages and the
+** eOp argument is not 2, this function may allocate space for and lazily
+** popluates the overflow page-list cache array (BtCursor.aOverflow).
+** Subsequent calls use this cache to make seeking to the supplied offset
+** more efficient.
**
** Once an overflow page-list cache has been allocated, it may be
** invalidated if some other cursor writes to the same table, or if
@@ -3969,15 +3966,22 @@ static int accessPayload(
int iIdx = 0;
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ int bEnd; /* True if reading to end of data */
+#endif
assert( pPage );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
assert( cursorHoldsMutex(pCur) );
+ assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey);
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ bEnd = (offset+amt==nKey+pCur->info.nData);
+#endif
if( NEVER(offset+amt > nKey+pCur->info.nData)
|| &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
@@ -3992,7 +3996,7 @@ static int accessPayload(
if( a+offset>pCur->info.nLocal ){
a = pCur->info.nLocal - offset;
}
- rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage);
+ rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
offset = 0;
pBuf += a;
amt -= a;
@@ -4006,21 +4010,30 @@ static int accessPayload(
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
-#ifndef SQLITE_OMIT_INCRBLOB
- /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
- ** has not been allocated, allocate it now. The array is sized at
- ** one entry for each overflow page in the overflow chain. The
- ** page number of the first overflow page is stored in aOverflow[0],
- ** etc. A value of 0 in the aOverflow[] array means "not yet known"
- ** (the cache is lazily populated).
+ /* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
+ ** Except, do not allocate aOverflow[] for eOp==2.
+ **
+ ** The aOverflow[] array is sized at one entry for each overflow page
+ ** in the overflow chain. The page number of the first overflow page is
+ ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array
+ ** means "not yet known" (the cache is lazily populated).
*/
- if( pCur->isIncrblobHandle && !pCur->aOverflow ){
+ if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
- pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl);
- /* nOvfl is always positive. If it were zero, fetchPayload would have
- ** been used instead of this routine. */
- if( ALWAYS(nOvfl) && !pCur->aOverflow ){
- rc = SQLITE_NOMEM;
+ if( nOvfl>pCur->nOvflAlloc ){
+ Pgno *aNew = (Pgno*)sqlite3DbRealloc(
+ pCur->pBtree->db, pCur->aOverflow, nOvfl*2*sizeof(Pgno)
+ );
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pCur->nOvflAlloc = nOvfl*2;
+ pCur->aOverflow = aNew;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
+ pCur->curFlags |= BTCF_ValidOvfl;
}
}
@@ -4028,22 +4041,19 @@ static int accessPayload(
** entry for the first required overflow page is valid, skip
** directly to it.
*/
- if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 && pCur->aOverflow[offset/ovflSize] ){
iIdx = (offset/ovflSize);
nextPage = pCur->aOverflow[iIdx];
offset = (offset%ovflSize);
}
-#endif
for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){
-#ifndef SQLITE_OMIT_INCRBLOB
/* If required, populate the overflow page-list cache. */
- if( pCur->aOverflow ){
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
pCur->aOverflow[iIdx] = nextPage;
}
-#endif
if( offset>=ovflSize ){
/* The only reason to read this page is to obtain the page
@@ -4051,13 +4061,17 @@ static int accessPayload(
** data is not required. So first try to lookup the overflow
** page-list cache, if any, then fall back to the getOverflowPage()
** function.
+ **
+ ** Note that the aOverflow[] array must be allocated because eOp!=2
+ ** here. If eOp==2, then offset==0 and this branch is never taken.
*/
-#ifndef SQLITE_OMIT_INCRBLOB
- if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){
+ assert( eOp!=2 );
+ assert( pCur->curFlags & BTCF_ValidOvfl );
+ if( pCur->aOverflow[iIdx+1] ){
nextPage = pCur->aOverflow[iIdx+1];
- } else
-#endif
+ }else{
rc = getOverflowPage(pBt, nextPage, 0, &nextPage);
+ }
offset -= ovflSize;
}else{
/* Need to read this page properly. It contains some of the
@@ -4079,13 +4093,15 @@ static int accessPayload(
** 3) the database is file-backed, and
** 4) there is no open write-transaction, and
** 5) the database is not a WAL database,
+ ** 6) all data from the page is being read.
**
** then data can be read directly from the database file into the
** output buffer, bypassing the page-cache altogether. This speeds
** up loading large records that span many overflow pages.
*/
- if( eOp==0 /* (1) */
+ if( (eOp&0x01)==0 /* (1) */
&& offset==0 /* (2) */
+ && (bEnd || a==ovflSize) /* (6) */
&& pBt->inTransaction==TRANS_READ /* (4) */
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
&& pBt->pPage1->aData[19]==0x01 /* (5) */
@@ -4102,12 +4118,12 @@ static int accessPayload(
{
DbPage *pDbPage;
rc = sqlite3PagerAcquire(pBt->pPager, nextPage, &pDbPage,
- (eOp==0 ? PAGER_ACQUIRE_READONLY : 0)
+ ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
);
if( rc==SQLITE_OK ){
aPayload = sqlite3PagerGetData(pDbPage);
nextPage = get4byte(aPayload);
- rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
+ rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage);
sqlite3PagerUnref(pDbPage);
offset = 0;
}
@@ -4176,10 +4192,10 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
/*
** Return a pointer to payload information from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
-** the key if skipKey==0 and it points to the beginning of data if
-** skipKey==1. The number of bytes of available key/data is written
-** into *pAmt. If *pAmt==0, then the value returned will not be
-** a valid pointer.
+** the key if index btrees (pPage->intKey==0) and is the data for
+** table btrees (pPage->intKey==1). The number of bytes of available
+** key/data is written into *pAmt. If *pAmt==0, then the value
+** returned will not be a valid pointer.
**
** This routine is an optimization. It is common for the entire key
** and data to fit on the local page and for there to be no overflow
@@ -4192,41 +4208,18 @@ int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** page of the database. The data might change or move the next time
** any btree routine is called.
*/
-static const unsigned char *fetchPayload(
+static const void *fetchPayload(
BtCursor *pCur, /* Cursor pointing to entry to read from */
- int *pAmt, /* Write the number of available bytes here */
- int skipKey /* read beginning at data if this is true */
+ u32 *pAmt /* Write the number of available bytes here */
){
- unsigned char *aPayload;
- MemPage *pPage;
- u32 nKey;
- u32 nLocal;
-
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( cursorHoldsMutex(pCur) );
- pPage = pCur->apPage[pCur->iPage];
- assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
- if( NEVER(pCur->info.nSize==0) ){
- btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage],
- &pCur->info);
- }
- aPayload = pCur->info.pCell;
- aPayload += pCur->info.nHeader;
- if( pPage->intKey ){
- nKey = 0;
- }else{
- nKey = (int)pCur->info.nKey;
- }
- if( skipKey ){
- aPayload += nKey;
- nLocal = pCur->info.nLocal - nKey;
- }else{
- nLocal = pCur->info.nLocal;
- assert( nLocal<=nKey );
- }
- *pAmt = nLocal;
- return aPayload;
+ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ assert( pCur->info.nSize>0 );
+ *pAmt = pCur->info.nLocal;
+ return (void*)(pCur->info.pCell + pCur->info.nHeader);
}
@@ -4244,23 +4237,11 @@ static const unsigned char *fetchPayload(
** 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){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 0);
- }
- return p;
+const void *sqlite3BtreeKeyFetch(BtCursor *pCur, u32 *pAmt){
+ return fetchPayload(pCur, pAmt);
}
-const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 1);
- }
- return p;
+const void *sqlite3BtreeDataFetch(BtCursor *pCur, u32 *pAmt){
+ return fetchPayload(pCur, pAmt);
}
@@ -4286,14 +4267,15 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, newPgno, &pNewPage, (pCur->wrFlag==0));
+ rc = getAndInitPage(pBt, newPgno, &pNewPage,
+ (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc ) return rc;
pCur->apPage[i+1] = pNewPage;
pCur->aiIdx[i+1] = 0;
pCur->iPage++;
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
return SQLITE_CORRUPT_BKPT;
}
@@ -4351,7 +4333,7 @@ static void moveToParent(BtCursor *pCur){
releasePage(pCur->apPage[pCur->iPage]);
pCur->iPage--;
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
}
/*
@@ -4378,8 +4360,6 @@ static void moveToParent(BtCursor *pCur){
static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
assert( cursorHoldsMutex(pCur) );
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
@@ -4394,55 +4374,51 @@ static int moveToRoot(BtCursor *pCur){
}
if( pCur->iPage>=0 ){
- int i;
- for(i=1; i<=pCur->iPage; i++){
- releasePage(pCur->apPage[i]);
- }
- pCur->iPage = 0;
+ while( pCur->iPage ) releasePage(pCur->apPage[pCur->iPage--]);
}else if( pCur->pgnoRoot==0 ){
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}else{
- rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0], pCur->wrFlag==0);
+ rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
+ (pCur->curFlags & BTCF_WriteFlag)==0 ? PAGER_GET_READONLY : 0);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
}
pCur->iPage = 0;
-
- /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
- ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
- ** NULL, the caller expects a table b-tree. If this is not the case,
- ** return an SQLITE_CORRUPT error. */
- assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
- if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
- return SQLITE_CORRUPT_BKPT;
- }
}
-
- /* Assert that the root page is of the correct type. This must be the
- ** case as the call to this function that loaded the root-page (either
- ** this call or a previous invocation) would have detected corruption
- ** if the assumption were not true, and it is not possible for the flags
- ** byte to have been modified while this cursor is holding a reference
- ** to the page. */
pRoot = pCur->apPage[0];
assert( pRoot->pgno==pCur->pgnoRoot );
- assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );
+
+ /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
+ ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
+ ** NULL, the caller expects a table b-tree. If this is not the case,
+ ** return an SQLITE_CORRUPT error.
+ **
+ ** Earlier versions of SQLite assumed that this test could not fail
+ ** if the root page was already loaded when this function was called (i.e.
+ ** if pCur->iPage>=0). But this is not so if the database is corrupted
+ ** in such a way that page pRoot is linked into a second b-tree table
+ ** (or the freelist). */
+ assert( pRoot->intKey==1 || pRoot->intKey==0 );
+ if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
+ return SQLITE_CORRUPT_BKPT;
+ }
pCur->aiIdx[0] = 0;
pCur->info.nSize = 0;
- pCur->atLast = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
- if( pRoot->nCell==0 && !pRoot->leaf ){
+ if( pRoot->nCell>0 ){
+ pCur->eState = CURSOR_VALID;
+ }else if( !pRoot->leaf ){
Pgno subpage;
if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT;
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage);
}else{
- pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
+ pCur->eState = CURSOR_INVALID;
}
return rc;
}
@@ -4494,7 +4470,7 @@ static int moveToRightmost(BtCursor *pCur){
if( rc==SQLITE_OK ){
pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~BTCF_ValidNKey;
}
return rc;
}
@@ -4533,7 +4509,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */
- if( CURSOR_VALID==pCur->eState && pCur->atLast ){
+ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
#ifdef SQLITE_DEBUG
/* This block serves to assert() that the cursor really does point
** to the last entry in the b-tree. */
@@ -4556,7 +4532,12 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
- pCur->atLast = rc==SQLITE_OK ?1:0;
+ if( rc==SQLITE_OK ){
+ pCur->curFlags |= BTCF_AtLast;
+ }else{
+ pCur->curFlags &= ~BTCF_AtLast;
+ }
+
}
}
return rc;
@@ -4598,6 +4579,7 @@ int sqlite3BtreeMovetoUnpacked(
int *pRes /* Write search results here */
){
int rc;
+ RecordCompare xRecordCompare;
assert( cursorHoldsMutex(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
@@ -4606,19 +4588,30 @@ int sqlite3BtreeMovetoUnpacked(
/* 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
+ if( pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
&& pCur->apPage[0]->intKey
){
if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
}
- if( pCur->atLast && pCur->info.nKey<intKey ){
+ if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){
*pRes = -1;
return SQLITE_OK;
}
}
+ if( pIdxKey ){
+ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
+ pIdxKey->isCorrupt = 0;
+ assert( pIdxKey->default_rc==1
+ || pIdxKey->default_rc==0
+ || pIdxKey->default_rc==-1
+ );
+ }else{
+ xRecordCompare = 0; /* All keys are integers */
+ }
+
rc = moveToRoot(pCur);
if( rc ){
return rc;
@@ -4633,10 +4626,10 @@ int sqlite3BtreeMovetoUnpacked(
}
assert( pCur->apPage[0]->intKey || pIdxKey );
for(;;){
- int lwr, upr, idx;
+ int lwr, upr, idx, c;
Pgno chldPg;
MemPage *pPage = pCur->apPage[pCur->iPage];
- int c;
+ u8 *pCell; /* Pointer to current cell in pPage */
/* pPage->nCell must be greater than zero. If this is the root-page
** the cursor would have been INVALID above and this for(;;) loop
@@ -4648,35 +4641,47 @@ int sqlite3BtreeMovetoUnpacked(
assert( pPage->intKey==(pIdxKey==0) );
lwr = 0;
upr = pPage->nCell-1;
- if( biasRight ){
- pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
- }else{
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
- }
- for(;;){
- u8 *pCell; /* Pointer to current cell in pPage */
-
- assert( idx==pCur->aiIdx[pCur->iPage] );
- pCur->info.nSize = 0;
- pCell = findCell(pPage, idx) + pPage->childPtrSize;
- if( pPage->intKey ){
+ assert( biasRight==0 || biasRight==1 );
+ idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( xRecordCompare==0 ){
+ for(;;){
i64 nCellKey;
+ pCell = findCell(pPage, idx) + pPage->childPtrSize;
if( pPage->hasData ){
- u32 dummy;
- pCell += getVarint32(pCell, dummy);
+ while( 0x80 <= *(pCell++) ){
+ if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ }
}
getVarint(pCell, (u64*)&nCellKey);
- if( nCellKey==intKey ){
- c = 0;
- }else if( nCellKey<intKey ){
- c = -1;
+ if( nCellKey<intKey ){
+ lwr = idx+1;
+ if( lwr>upr ){ c = -1; break; }
+ }else if( nCellKey>intKey ){
+ upr = idx-1;
+ if( lwr>upr ){ c = +1; break; }
}else{
- assert( nCellKey>intKey );
- c = +1;
+ assert( nCellKey==intKey );
+ pCur->curFlags |= BTCF_ValidNKey;
+ pCur->info.nKey = nCellKey;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( !pPage->leaf ){
+ lwr = idx;
+ goto moveto_next_layer;
+ }else{
+ *pRes = 0;
+ rc = SQLITE_OK;
+ goto moveto_finish;
+ }
}
- pCur->validNKey = 1;
- pCur->info.nKey = nCellKey;
- }else{
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
+ }
+ }else{
+ for(;;){
+ int nCell;
+ pCell = findCell(pPage, idx) + pPage->childPtrSize;
+
/* The maximum supported page-size is 65536 bytes. This means that
** the maximum number of record bytes stored on an index B-Tree
** page is less than 16384 bytes and may be stored as a 2-byte
@@ -4685,23 +4690,20 @@ int sqlite3BtreeMovetoUnpacked(
** stored entirely within the b-tree page by inspecting the first
** 2 bytes of the cell.
*/
- int nCell = pCell[0];
- if( nCell<=pPage->max1bytePayload
- /* && (pCell+nCell)<pPage->aDataEnd */
- ){
+ nCell = pCell[0];
+ if( nCell<=pPage->max1bytePayload ){
/* This branch runs if the record-size field of the cell is a
** single byte varint and the record fits entirely on the main
** b-tree page. */
testcase( pCell+nCell+1==pPage->aDataEnd );
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey, 0);
}else if( !(pCell[1] & 0x80)
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
- /* && (pCell+nCell+2)<=pPage->aDataEnd */
){
/* The record-size field is a 2 byte varint and the record
** fits entirely on the main b-tree page. */
testcase( pCell+nCell+2==pPage->aDataEnd );
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey, 0);
}else{
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
@@ -4716,57 +4718,55 @@ int sqlite3BtreeMovetoUnpacked(
rc = SQLITE_NOMEM;
goto moveto_finish;
}
- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2);
if( rc ){
sqlite3_free(pCellKey);
goto moveto_finish;
}
- c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
+ c = xRecordCompare(nCell, pCellKey, pIdxKey, 0);
sqlite3_free(pCellKey);
}
- }
- if( c==0 ){
- if( pPage->intKey && !pPage->leaf ){
- lwr = idx;
- break;
+ assert( pIdxKey->isCorrupt==0 || c==0 );
+ if( c<0 ){
+ lwr = idx+1;
+ }else if( c>0 ){
+ upr = idx-1;
}else{
+ assert( c==0 );
*pRes = 0;
rc = SQLITE_OK;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( pIdxKey->isCorrupt ) rc = SQLITE_CORRUPT;
goto moveto_finish;
}
+ if( lwr>upr ) break;
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
}
- if( c<0 ){
- lwr = idx+1;
- }else{
- upr = idx-1;
- }
- if( lwr>upr ){
- break;
- }
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
}
assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
assert( pPage->isInit );
if( pPage->leaf ){
- chldPg = 0;
- }else if( lwr>=pPage->nCell ){
- chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- }else{
- chldPg = get4byte(findCell(pPage, lwr));
- }
- if( chldPg==0 ){
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
goto moveto_finish;
}
+moveto_next_layer:
+ if( lwr>=pPage->nCell ){
+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ }else{
+ chldPg = get4byte(findCell(pPage, lwr));
+ }
pCur->aiIdx[pCur->iPage] = (u16)lwr;
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
rc = moveToChild(pCur, chldPg);
- if( rc ) goto moveto_finish;
+ if( rc ) break;
}
moveto_finish:
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
return rc;
}
@@ -4791,6 +4791,15 @@ int sqlite3BtreeEof(BtCursor *pCur){
** successful then set *pRes=0. If the cursor
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
*/
int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
int rc;
@@ -4798,21 +4807,31 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
MemPage *pPage;
assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
assert( pRes!=0 );
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext>0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ if( pCur->eState!=CURSOR_VALID ){
+ invalidateOverflowCache(pCur);
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ *pRes = 0;
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext>0 ){
+ pCur->skipNext = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
idx = ++pCur->aiIdx[pCur->iPage];
@@ -4826,11 +4845,14 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
testcase( idx>pPage->nCell );
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
- if( rc ) return rc;
+ if( rc ){
+ *pRes = 0;
+ return rc;
+ }
rc = moveToLeftmost(pCur);
*pRes = 0;
return rc;
@@ -4866,27 +4888,48 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
** successful then set *pRes=0. If the cursor
** was already pointing to the first entry in the database before
** this routine was called, then set *pRes=1.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
*/
int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
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;
- }
- if( pCur->skipNext<0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( pRes!=0 );
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl);
+ if( pCur->eState!=CURSOR_VALID ){
+ if( ALWAYS(pCur->eState>=CURSOR_REQUIRESEEK) ){
+ rc = btreeRestoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ *pRes = 0;
+ return rc;
+ }
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext<0 ){
+ pCur->skipNext = 0;
+ *pRes = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
assert( pPage->isInit );
@@ -4894,6 +4937,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
int idx = pCur->aiIdx[pCur->iPage];
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
if( rc ){
+ *pRes = 0;
return rc;
}
rc = moveToRightmost(pCur);
@@ -4907,7 +4951,7 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
moveToParent(pCur);
}
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
pCur->aiIdx[pCur->iPage]--;
pPage = pCur->apPage[pCur->iPage];
@@ -5017,7 +5061,7 @@ static int allocateBtreePage(
if( iTrunk>mxPage ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
}
if( rc ){
pTrunk = 0;
@@ -5081,7 +5125,7 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iNewTrunk==mxPage );
- rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0);
if( rc!=SQLITE_OK ){
goto end_allocate_page;
}
@@ -5160,8 +5204,8 @@ static int allocateBtreePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
- noContent = !btreeGetHasContent(pBt, *pPgno);
- rc = btreeGetPage(pBt, *pPgno, ppPage, noContent, 0);
+ noContent = !btreeGetHasContent(pBt, *pPgno) ? PAGER_GET_NOCONTENT : 0;
+ rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
@@ -5193,7 +5237,7 @@ static int allocateBtreePage(
** here are confined to those pages that lie between the end of the
** database image and the end of the database file.
*/
- int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate));
+ int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate)) ? PAGER_GET_NOCONTENT : 0;
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
@@ -5209,7 +5253,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent, 0);
+ rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -5223,7 +5267,7 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent, 0);
+ rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
@@ -5240,6 +5284,7 @@ end_allocate_page:
if( rc==SQLITE_OK ){
if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
releasePage(*ppPage);
+ *ppPage = 0;
return SQLITE_CORRUPT_BKPT;
}
(*ppPage)->isInit = 0;
@@ -5291,7 +5336,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
*/
- if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0, 0))!=0) )
+ if( (!pPage && ((rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0) )
|| ((rc = sqlite3PagerWrite(pPage->pDbPage))!=0)
){
goto freepage_out;
@@ -5318,7 +5363,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
u32 nLeaf; /* Initial number of leaf cells on trunk page */
iTrunk = get4byte(&pPage1->aData[32]);
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0, 0);
+ rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
if( rc!=SQLITE_OK ){
goto freepage_out;
}
@@ -5364,7 +5409,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
** first trunk in the free-list is full. Either way, the page being freed
** will become the new first trunk page in the free-list.
*/
- if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0, 0)) ){
+ if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){
goto freepage_out;
}
rc = sqlite3PagerWrite(pPage->pDbPage);
@@ -5501,7 +5546,7 @@ static int fillInCell(
nHeader += 4;
}
if( pPage->hasData ){
- nHeader += putVarint(&pCell[nHeader], nData+nZero);
+ nHeader += putVarint32(&pCell[nHeader], nData+nZero);
}else{
nData = nZero = 0;
}
@@ -5629,7 +5674,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
u32 pc; /* Offset to cell content of cell being deleted */
u8 *data; /* pPage->aData */
u8 *ptr; /* Used to move bytes around within data[] */
- u8 *endPtr; /* End of loop */
int rc; /* The return code */
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
@@ -5654,13 +5698,8 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
*pRC = rc;
return;
}
- endPtr = &pPage->aCellIdx[2*pPage->nCell - 2];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr<endPtr ){
- *(u16*)ptr = *(u16*)&ptr[2];
- ptr += 2;
- }
pPage->nCell--;
+ memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
put2byte(&data[hdr+3], pPage->nCell);
pPage->nFree += 2;
}
@@ -5697,15 +5736,13 @@ static void insertCell(
int ins; /* Index in data[] where new cell pointer is inserted */
int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
- u8 *ptr; /* Used for moving information around in data[] */
- u8 *endPtr; /* End of the loop */
-
int nSkip = (iChild ? 4 : 0);
if( *pRC ) return;
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
- assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
+ assert( MX_CELL(pPage->pBt)<=10921 );
+ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -5750,13 +5787,7 @@ static void insertCell(
if( iChild ){
put4byte(&data[idx], iChild);
}
- ptr = &data[end];
- endPtr = &data[ins];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr>endPtr ){
- *(u16*)ptr = *(u16*)&ptr[-2];
- ptr -= 2;
- }
+ memmove(&data[ins+2], &data[ins], end-ins);
put2byte(&data[ins], idx);
put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -6946,7 +6977,7 @@ int sqlite3BtreeInsert(
}
assert( cursorHoldsMutex(pCur) );
- assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE
+ assert( (pCur->curFlags & BTCF_WriteFlag)!=0 && pBt->inTransaction==TRANS_WRITE
&& (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
@@ -6971,11 +7002,17 @@ int sqlite3BtreeInsert(
rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
if( rc ) return rc;
- /* If this is an insert into a table b-tree, invalidate any incrblob
- ** cursors open on the row being replaced (assuming this is a replace
- ** operation - if it is not, the following is a no-op). */
if( pCur->pKeyInfo==0 ){
+ /* If this is an insert into a table b-tree, invalidate any incrblob
+ ** cursors open on the row being replaced */
invalidateIncrblobCursors(p, nKey, 0);
+
+ /* If the cursor is currently on the last row and we are appending a
+ ** new row onto the end, set the "loc" to avoid an unnecessary btreeMoveto()
+ ** call */
+ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && nKey>0 && pCur->info.nKey==nKey-1 ){
+ loc = -1;
+ }
}
if( !loc ){
@@ -7026,7 +7063,7 @@ int sqlite3BtreeInsert(
/* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move
- ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey
+ ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables.
**
** Previous versions of SQLite called moveToRoot() to move the cursor
@@ -7045,8 +7082,8 @@ int sqlite3BtreeInsert(
** row without seeking the cursor. This can be a big performance boost.
*/
pCur->info.nSize = 0;
- pCur->validNKey = 0;
if( rc==SQLITE_OK && pPage->nOverflow ){
+ pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -7078,7 +7115,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
- assert( pCur->wrFlag );
+ assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
@@ -7101,7 +7138,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
** sub-tree headed by the child page of the cell being deleted. This makes
** balancing the tree following the delete operation easier. */
if( !pPage->leaf ){
- int notUsed;
+ int notUsed = 0;
rc = sqlite3BtreePrevious(pCur, &notUsed);
if( rc ) return rc;
}
@@ -7263,7 +7300,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
}
/* Move the page currently at pgnoRoot to pgnoMove. */
- rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0);
+ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -7284,7 +7321,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
if( rc!=SQLITE_OK ){
return rc;
}
- rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0, 0);
+ rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -7354,6 +7391,7 @@ static int clearDatabasePage(
int rc;
unsigned char *pCell;
int i;
+ int hdr;
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
@@ -7362,6 +7400,7 @@ static int clearDatabasePage(
rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
+ hdr = pPage->hdrOffset;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
if( !pPage->leaf ){
@@ -7372,7 +7411,7 @@ static int clearDatabasePage(
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
- rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange);
+ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}else if( pnChange ){
assert( pPage->intKey );
@@ -7381,7 +7420,7 @@ static int clearDatabasePage(
if( freePageFlag ){
freePage(pPage, &rc);
}else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
- zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
+ zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
}
cleardatabasepage_out:
@@ -7422,6 +7461,15 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
}
/*
+** Delete all information from the single table that pCur is open on.
+**
+** This routine only work for pCur on an ephemeral table.
+*/
+int sqlite3BtreeClearTableOfCursor(BtCursor *pCur){
+ return sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0);
+}
+
+/*
** Erase all information in a table and add the root of the table to
** the freelist. Except, the root of the principle table (the one on
** page 1) is never added to the freelist.
@@ -7462,7 +7510,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return SQLITE_LOCKED_SHAREDCACHE;
}
- rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0, 0);
+ rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable, 0);
if( rc ){
@@ -7497,7 +7545,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
*/
MemPage *pMove;
releasePage(pPage);
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -7507,7 +7555,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return rc;
}
pMove = 0;
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0, 0);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
freePage(pMove, &rc);
releasePage(pMove);
if( rc!=SQLITE_OK ){
@@ -7718,11 +7766,11 @@ static void checkAppendMsg(
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
if( zMsg1 ){
- sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1);
+ sqlite3StrAccumAppendAll(&pCheck->errMsg, zMsg1);
}
sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
va_end(ap);
- if( pCheck->errMsg.mallocFailed ){
+ if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
}
}
@@ -7919,7 +7967,7 @@ static int checkTreePage(
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
if( checkRef(pCheck, iPage, zParentContext) ) return 0;
- if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0, 0))!=0 ){
+ if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck, zContext,
"unable to get the page. error code=%d", rc);
return 0;
@@ -8380,7 +8428,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
int rc;
assert( cursorHoldsMutex(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
- assert( pCsr->isIncrblobHandle );
+ assert( pCsr->curFlags & BTCF_Incrblob );
rc = restoreCursorPosition(pCsr);
if( rc!=SQLITE_OK ){
@@ -8409,7 +8457,7 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
** (d) there are no conflicting read-locks, and
** (e) the cursor points at a valid row of an intKey table.
*/
- if( !pCsr->wrFlag ){
+ if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){
return SQLITE_READONLY;
}
assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0
@@ -8422,20 +8470,10 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
}
/*
-** Set a flag on this cursor to cache the locations of pages from the
-** overflow list for the current row. This is used by cursors opened
-** for incremental blob IO only.
-**
-** This function sets a flag only. The actual page location cache
-** (stored in BtCursor.aOverflow[]) is allocated and used by function
-** accessPayload() (the worker function for sqlite3BtreeData() and
-** sqlite3BtreePutData()).
+** Mark this cursor as an incremental blob cursor.
*/
-void sqlite3BtreeCacheOverflow(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- invalidateOverflowCache(pCur);
- pCur->isIncrblobHandle = 1;
+void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
+ pCur->curFlags |= BTCF_Incrblob;
}
#endif
@@ -8483,3 +8521,10 @@ void sqlite3BtreeCursorHints(BtCursor *pCsr, unsigned int mask){
assert( mask==BTREE_BULKLOAD || mask==0 );
pCsr->hints = mask;
}
+
+/*
+** Return true if the given Btree is read-only.
+*/
+int sqlite3BtreeIsReadonly(Btree *p){
+ return (p->pBt->btsFlags & BTS_READ_ONLY)!=0;
+}
diff --git a/src/btree.h b/src/btree.h
index ace0f8c..7118534 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -63,8 +63,10 @@ int sqlite3BtreeOpen(
int sqlite3BtreeClose(Btree*);
int sqlite3BtreeSetCacheSize(Btree*,int);
-int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
-int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
+#if SQLITE_MAX_MMAP_SIZE>0
+ int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
+#endif
+int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
int sqlite3BtreeSyncDisabled(Btree*);
int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
int sqlite3BtreeGetPageSize(Btree*);
@@ -113,6 +115,7 @@ int sqlite3BtreeIncrVacuum(Btree *);
int sqlite3BtreeDropTable(Btree*, int, int*);
int sqlite3BtreeClearTable(Btree*, int, int*);
+int sqlite3BtreeClearTableOfCursor(BtCursor*);
void sqlite3BtreeTripAllCursors(Btree*, int);
void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
@@ -178,21 +181,20 @@ int sqlite3BtreeEof(BtCursor*);
int sqlite3BtreePrevious(BtCursor*, int *pRes);
int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
-const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
-const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
+const void *sqlite3BtreeKeyFetch(BtCursor*, u32 *pAmt);
+const void *sqlite3BtreeDataFetch(BtCursor*, u32 *pAmt);
int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
-void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
-sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*);
char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
struct Pager *sqlite3BtreePager(Btree*);
int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
-void sqlite3BtreeCacheOverflow(BtCursor *);
+void sqlite3BtreeIncrblobCursor(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *);
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask);
+int sqlite3BtreeIsReadonly(Btree *pBt);
#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
diff --git a/src/btreeInt.h b/src/btreeInt.h
index ce3c549..d1cdd46 100644
--- a/src/btreeInt.h
+++ b/src/btreeInt.h
@@ -56,13 +56,13 @@
**
** OFFSET SIZE DESCRIPTION
** 0 16 Header string: "SQLite format 3\000"
-** 16 2 Page size in bytes.
+** 16 2 Page size in bytes. (1 means 65536)
** 18 1 File format write version
** 19 1 File format read version
** 20 1 Bytes of unused space at the end of each page
-** 21 1 Max embedded payload fraction
-** 22 1 Min embedded payload fraction
-** 23 1 Min leaf payload fraction
+** 21 1 Max embedded payload fraction (must be 64)
+** 22 1 Min embedded payload fraction (must be 32)
+** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
** 28 4 Reserved for future use
** 32 4 First freelist page
@@ -76,9 +76,10 @@
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
** 60 4 User version
** 64 4 Incremental vacuum mode
-** 68 4 unused
-** 72 4 unused
-** 76 4 unused
+** 68 4 Application-ID
+** 72 20 unused
+** 92 4 The version-valid-for number
+** 96 4 SQLITE_VERSION_NUMBER
**
** All of the integer values are big-endian (most significant byte first).
**
@@ -495,22 +496,15 @@ struct BtCursor {
BtShared *pBt; /* The BtShared this cursor points to */
BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
-#ifndef SQLITE_OMIT_INCRBLOB
Pgno *aOverflow; /* Cache of overflow page locations */
-#endif
- Pgno pgnoRoot; /* The root page of this tree */
- sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
CellInfo info; /* A parse of the cell we are pointing at */
- i64 nKey; /* Size of pKey, or last integer key */
- void *pKey; /* Saved key that was cursor's last known position */
+ i64 nKey; /* Size of pKey, or last integer key */
+ void *pKey; /* Saved key that was cursor last known position */
+ Pgno pgnoRoot; /* The root page of this tree */
+ int nOvflAlloc; /* Allocated size of aOverflow[] array */
int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
- u8 wrFlag; /* True if writable */
- u8 atLast; /* Cursor pointing to the last entry */
- u8 validNKey; /* True if info.nKey is valid */
+ u8 curFlags; /* zero or more BTCF_* flags defined below */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_INCRBLOB
- u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
-#endif
u8 hints; /* As configured by CursorSetHints() */
i16 iPage; /* Index of current page in apPage */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
@@ -518,16 +512,30 @@ struct BtCursor {
};
/*
+** Legal values for BtCursor.curFlags
+*/
+#define BTCF_WriteFlag 0x01 /* True if a write cursor */
+#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
+#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
+#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
+#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
+
+/*
** Potential values for BtCursor.eState.
**
-** CURSOR_VALID:
-** Cursor points to a valid entry. getPayload() etc. may be called.
-**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
+** CURSOR_VALID:
+** Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_SKIPNEXT:
+** Cursor is valid except that the Cursor.skipNext field is non-zero
+** indicating that the next sqlite3BtreeNext() or sqlite3BtreePrevious()
+** operation should be a no-op.
+**
** CURSOR_REQUIRESEEK:
** 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
@@ -544,8 +552,9 @@ struct BtCursor {
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
-#define CURSOR_REQUIRESEEK 2
-#define CURSOR_FAULT 3
+#define CURSOR_SKIPNEXT 2
+#define CURSOR_REQUIRESEEK 3
+#define CURSOR_FAULT 4
/*
** The database page the PENDING_BYTE occupies. This page is never used.
diff --git a/src/build.c b/src/build.c
index 3c91cdc..a9a8f21 100644
--- a/src/build.c
+++ b/src/build.c
@@ -114,6 +114,19 @@ static void codeTableLocks(Parse *pParse){
#endif
/*
+** Return TRUE if the given yDbMask object is empty - if it contains no
+** 1 bits. This routine is used by the DbMaskAllZero() and DbMaskNotZero()
+** macros when SQLITE_MAX_ATTACHED is greater than 30.
+*/
+#if SQLITE_MAX_ATTACHED>30
+int sqlite3DbMaskAllZero(yDbMask m){
+ int i;
+ for(i=0; i<sizeof(yDbMask); i++) if( m[i] ) return 0;
+ return 1;
+}
+#endif
+
+/*
** This routine is called after a single SQL statement has been
** parsed and a VDBE program to execute that statement has been
** prepared. This routine puts the finishing touches on the
@@ -140,6 +153,7 @@ void sqlite3FinishCoding(Parse *pParse){
assert( !pParse->isMultiWrite
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
+ while( sqlite3VdbeDeletePriorOpcode(v, OP_Close) ){}
sqlite3VdbeAddOp0(v, OP_Halt);
/* The cookie mask contains one bit for each database file open.
@@ -148,30 +162,30 @@ void sqlite3FinishCoding(Parse *pParse){
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
- if( pParse->cookieGoto>0 ){
- yDbMask mask;
- int iDb;
- sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
- for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
- if( (mask & pParse->cookieMask)==0 ) continue;
+ if( db->mallocFailed==0
+ && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr)
+ ){
+ int iDb, i;
+ assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
+ sqlite3VdbeJumpHere(v, 0);
+ for(iDb=0; iDb<db->nDb; iDb++){
+ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
sqlite3VdbeUsesBtree(v, iDb);
- sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
- if( db->init.busy==0 ){
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp3(v, OP_VerifyCookie,
- iDb, pParse->cookieValue[iDb],
- db->aDb[iDb].pSchema->iGeneration);
- }
+ sqlite3VdbeAddOp4Int(v,
+ OP_Transaction, /* Opcode */
+ iDb, /* P1 */
+ DbMaskTest(pParse->writeMask,iDb), /* P2 */
+ pParse->cookieValue[iDb], /* P3 */
+ db->aDb[iDb].pSchema->iGeneration /* P4 */
+ );
+ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- {
- int i;
- for(i=0; i<pParse->nVtabLock; i++){
- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
- }
- pParse->nVtabLock = 0;
+ for(i=0; i<pParse->nVtabLock; i++){
+ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
}
+ pParse->nVtabLock = 0;
#endif
/* Once all the cookies have been verified and transactions opened,
@@ -184,8 +198,17 @@ void sqlite3FinishCoding(Parse *pParse){
*/
sqlite3AutoincrementBegin(pParse);
+ /* Code constant expressions that where factored out of inner loops */
+ if( pParse->pConstExpr ){
+ ExprList *pEL = pParse->pConstExpr;
+ pParse->okConstFactor = 0;
+ for(i=0; i<pEL->nExpr; i++){
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
+ }
+ }
+
/* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, 1);
}
}
@@ -193,10 +216,6 @@ void sqlite3FinishCoding(Parse *pParse){
/* Get the VDBE program ready for execution
*/
if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){
-#ifdef SQLITE_DEBUG
- FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
- sqlite3VdbeTrace(v, trace);
-#endif
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
/* A minimum of one cursor is required if autoincrement is used
* See ticket [a696379c1f08866] */
@@ -211,8 +230,7 @@ void sqlite3FinishCoding(Parse *pParse){
pParse->nMem = 0;
pParse->nSet = 0;
pParse->nVar = 0;
- pParse->cookieMask = 0;
- pParse->cookieGoto = 0;
+ DbMaskZero(pParse->cookieMask);
}
/*
@@ -382,7 +400,10 @@ static void freeIndex(sqlite3 *db, Index *p){
#ifndef SQLITE_OMIT_ANALYZE
sqlite3DeleteIndexSamples(db, p);
#endif
+ if( db==0 || db->pnBytesFreed==0 ) sqlite3KeyInfoUnref(p->pKeyInfo);
+ sqlite3ExprDelete(db, p->pPartIdxWhere);
sqlite3DbFree(db, p->zColAff);
+ if( p->isResized ) sqlite3DbFree(db, p->azColl);
sqlite3DbFree(db, p);
}
@@ -640,8 +661,7 @@ char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
void sqlite3OpenMasterTable(Parse *p, int iDb){
Vdbe *v = sqlite3GetVdbe(p);
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
- sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
- sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
if( p->nTab==0 ){
p->nTab = 1;
}
@@ -747,6 +767,27 @@ int sqlite3CheckObjectName(Parse *pParse, const char *zName){
}
/*
+** Return the PRIMARY KEY index of a table
+*/
+Index *sqlite3PrimaryKeyIndex(Table *pTab){
+ Index *p;
+ for(p=pTab->pIndex; p && !IsPrimaryKeyIndex(p); p=p->pNext){}
+ return p;
+}
+
+/*
+** Return the column of index pIdx that corresponds to table
+** column iCol. Return -1 if not found.
+*/
+i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
+ int i;
+ for(i=0; i<pIdx->nColumn; i++){
+ if( iCol==pIdx->aiColumn[i] ) return i;
+ }
+ return -1;
+}
+
+/*
** Begin constructing a new table representation in memory. This is
** the first of several action routines that get called in response
** to a CREATE TABLE statement. In particular, this routine is called
@@ -878,7 +919,7 @@ void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
- pTable->nRowEst = 1000000;
+ pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
@@ -921,7 +962,7 @@ void sqlite3StartTable(
reg3 = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
- j1 = sqlite3VdbeAddOp1(v, OP_If, reg3);
+ j1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v);
fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ?
1 : SQLITE_MAX_FILE_FORMAT;
sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3);
@@ -945,7 +986,7 @@ void sqlite3StartTable(
}else
#endif
{
- sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
+ pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
}
sqlite3OpenMasterTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
@@ -1025,6 +1066,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
** be called next to set pCol->affinity correctly.
*/
pCol->affinity = SQLITE_AFF_NONE;
+ pCol->szEst = 1;
p->nCol++;
}
@@ -1066,15 +1108,18 @@ void sqlite3AddNotNull(Parse *pParse, int onError){
** If none of the substrings in the above table are found,
** SQLITE_AFF_NUMERIC is returned.
*/
-char sqlite3AffinityType(const char *zIn){
+char sqlite3AffinityType(const char *zIn, u8 *pszEst){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
+ const char *zChar = 0;
- if( zIn ) while( zIn[0] ){
+ if( zIn==0 ) return aff;
+ while( zIn[0] ){
h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
- aff = SQLITE_AFF_TEXT;
+ aff = SQLITE_AFF_TEXT;
+ zChar = zIn;
}else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */
aff = SQLITE_AFF_TEXT;
}else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */
@@ -1082,6 +1127,7 @@ char sqlite3AffinityType(const char *zIn){
}else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */
&& (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
aff = SQLITE_AFF_NONE;
+ if( zIn[0]=='(' ) zChar = zIn;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */
&& aff==SQLITE_AFF_NUMERIC ){
@@ -1099,6 +1145,28 @@ char sqlite3AffinityType(const char *zIn){
}
}
+ /* If pszEst is not NULL, store an estimate of the field size. The
+ ** estimate is scaled so that the size of an integer is 1. */
+ if( pszEst ){
+ *pszEst = 1; /* default size is approx 4 bytes */
+ if( aff<=SQLITE_AFF_NONE ){
+ if( zChar ){
+ while( zChar[0] ){
+ if( sqlite3Isdigit(zChar[0]) ){
+ int v = 0;
+ sqlite3GetInt32(zChar, &v);
+ v = v/4 + 1;
+ if( v>255 ) v = 255;
+ *pszEst = v; /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
+ break;
+ }
+ zChar++;
+ }
+ }else{
+ *pszEst = 5; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
+ }
+ }
+ }
return aff;
}
@@ -1120,7 +1188,7 @@ void sqlite3AddColumnType(Parse *pParse, Token *pType){
pCol = &p->aCol[p->nCol-1];
assert( pCol->zType==0 );
pCol->zType = sqlite3NameFromToken(pParse->db, pType);
- pCol->affinity = sqlite3AffinityType(pCol->zType);
+ pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst);
}
/*
@@ -1186,6 +1254,7 @@ void sqlite3AddPrimaryKey(
Table *pTab = pParse->pNewTable;
char *zType = 0;
int iCol = -1, i;
+ int nTerm;
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
if( pTab->tabFlags & TF_HasPrimaryKey ){
sqlite3ErrorMsg(pParse,
@@ -1196,38 +1265,43 @@ void sqlite3AddPrimaryKey(
if( pList==0 ){
iCol = pTab->nCol - 1;
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
+ zType = pTab->aCol[iCol].zType;
+ nTerm = 1;
}else{
- for(i=0; i<pList->nExpr; i++){
+ nTerm = pList->nExpr;
+ for(i=0; i<nTerm; i++){
for(iCol=0; iCol<pTab->nCol; iCol++){
if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
+ pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
+ zType = pTab->aCol[iCol].zType;
break;
}
}
- if( iCol<pTab->nCol ){
- pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
- }
}
- if( pList->nExpr>1 ) iCol = -1;
- }
- if( iCol>=0 && iCol<pTab->nCol ){
- zType = pTab->aCol[iCol].zType;
}
- if( zType && sqlite3StrICmp(zType, "INTEGER")==0
- && sortOrder==SQLITE_SO_ASC ){
+ if( nTerm==1
+ && zType && sqlite3StrICmp(zType, "INTEGER")==0
+ && sortOrder==SQLITE_SO_ASC
+ ){
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
+ if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
"INTEGER PRIMARY KEY");
#endif
}else{
+ Vdbe *v = pParse->pVdbe;
Index *p;
- p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0);
+ if( v ) pParse->addrSkipPK = sqlite3VdbeAddOp0(v, OP_Noop);
+ p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
+ 0, sortOrder, 0);
if( p ){
- p->autoIndex = 2;
+ p->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
+ if( v ) sqlite3VdbeJumpHere(v, pParse->addrSkipPK);
}
pList = 0;
}
@@ -1246,7 +1320,10 @@ void sqlite3AddCheckConstraint(
){
#ifndef SQLITE_OMIT_CHECK
Table *pTab = pParse->pNewTable;
- if( pTab && !IN_DECLARE_VTAB ){
+ sqlite3 *db = pParse->db;
+ if( pTab && !IN_DECLARE_VTAB
+ && !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt)
+ ){
pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
if( pParse->constraintName.n ){
sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
@@ -1276,6 +1353,7 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
if( sqlite3LocateCollSeq(pParse, zColl) ){
Index *pIdx;
+ sqlite3DbFree(db, p->aCol[i].zColl);
p->aCol[i].zColl = zColl;
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
@@ -1283,7 +1361,7 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
** collation type was added. Correct this if it is the case.
*/
for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
- assert( pIdx->nColumn==1 );
+ assert( pIdx->nKeyCol==1 );
if( pIdx->aiColumn[0]==i ){
pIdx->azColl[0] = p->aCol[i].zColl;
}
@@ -1391,10 +1469,10 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
for(j=0; zIdent[j]; j++){
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
}
- needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID;
- if( !needQuote ){
- needQuote = zIdent[j];
- }
+ needQuote = sqlite3Isdigit(zIdent[0])
+ || sqlite3KeywordCode(zIdent, j)!=TK_ID
+ || zIdent[j]!=0
+ || j==0;
if( needQuote ) z[i++] = '"';
for(j=0; zIdent[j]; j++){
@@ -1466,7 +1544,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
zType = azType[pCol->affinity - SQLITE_AFF_TEXT];
len = sqlite3Strlen30(zType);
assert( pCol->affinity==SQLITE_AFF_NONE
- || pCol->affinity==sqlite3AffinityType(zType) );
+ || pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
assert( k<=n );
@@ -1476,6 +1554,191 @@ static char *createTableStmt(sqlite3 *db, Table *p){
}
/*
+** Resize an Index object to hold N columns total. Return SQLITE_OK
+** on success and SQLITE_NOMEM on an OOM error.
+*/
+static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
+ char *zExtra;
+ int nByte;
+ if( pIdx->nColumn>=N ) return SQLITE_OK;
+ assert( pIdx->isResized==0 );
+ nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
+ zExtra = sqlite3DbMallocZero(db, nByte);
+ if( zExtra==0 ) return SQLITE_NOMEM;
+ memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
+ pIdx->azColl = (char**)zExtra;
+ zExtra += sizeof(char*)*N;
+ memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
+ pIdx->aiColumn = (i16*)zExtra;
+ zExtra += sizeof(i16)*N;
+ memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
+ pIdx->aSortOrder = (u8*)zExtra;
+ pIdx->nColumn = N;
+ pIdx->isResized = 1;
+ return SQLITE_OK;
+}
+
+/*
+** Estimate the total row width for a table.
+*/
+static void estimateTableWidth(Table *pTab){
+ unsigned wTable = 0;
+ const Column *pTabCol;
+ int i;
+ for(i=pTab->nCol, pTabCol=pTab->aCol; i>0; i--, pTabCol++){
+ wTable += pTabCol->szEst;
+ }
+ if( pTab->iPKey<0 ) wTable++;
+ pTab->szTabRow = sqlite3LogEst(wTable*4);
+}
+
+/*
+** Estimate the average size of a row for an index.
+*/
+static void estimateIndexWidth(Index *pIdx){
+ unsigned wIndex = 0;
+ int i;
+ const Column *aCol = pIdx->pTable->aCol;
+ for(i=0; i<pIdx->nColumn; i++){
+ i16 x = pIdx->aiColumn[i];
+ assert( x<pIdx->pTable->nCol );
+ wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst;
+ }
+ pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
+}
+
+/* Return true if value x is found any of the first nCol entries of aiCol[]
+*/
+static int hasColumn(const i16 *aiCol, int nCol, int x){
+ while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1;
+ return 0;
+}
+
+/*
+** This routine runs at the end of parsing a CREATE TABLE statement that
+** has a WITHOUT ROWID clause. The job of this routine is to convert both
+** internal schema data structures and the generated VDBE code so that they
+** are appropriate for a WITHOUT ROWID table instead of a rowid table.
+** Changes include:
+**
+** (1) Convert the OP_CreateTable into an OP_CreateIndex. There is
+** no rowid btree for a WITHOUT ROWID. Instead, the canonical
+** data storage is a covering index btree.
+** (2) Bypass the creation of the sqlite_master table entry
+** for the PRIMARY KEY as the the primary key index is now
+** identified by the sqlite_master table entry of the table itself.
+** (3) Set the Index.tnum of the PRIMARY KEY Index object in the
+** schema to the rootpage from the main table.
+** (4) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
+** (5) Add all table columns to the PRIMARY KEY Index object
+** so that the PRIMARY KEY is a covering index. The surplus
+** columns are part of KeyInfo.nXField and are not used for
+** sorting or lookup or uniqueness checks.
+** (6) Replace the rowid tail on all automatically generated UNIQUE
+** indices with the PRIMARY KEY columns.
+*/
+static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
+ Index *pIdx;
+ Index *pPk;
+ int nPk;
+ int i, j;
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+
+ /* Convert the OP_CreateTable opcode that would normally create the
+ ** root-page for the table into a OP_CreateIndex opcode. The index
+ ** created will become the PRIMARY KEY index.
+ */
+ if( pParse->addrCrTab ){
+ assert( v );
+ sqlite3VdbeGetOp(v, pParse->addrCrTab)->opcode = OP_CreateIndex;
+ }
+
+ /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
+ ** table entry.
+ */
+ if( pParse->addrSkipPK ){
+ assert( v );
+ sqlite3VdbeGetOp(v, pParse->addrSkipPK)->opcode = OP_Goto;
+ }
+
+ /* Locate the PRIMARY KEY index. Or, if this table was originally
+ ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
+ */
+ if( pTab->iPKey>=0 ){
+ ExprList *pList;
+ pList = sqlite3ExprListAppend(pParse, 0, 0);
+ if( pList==0 ) return;
+ pList->a[0].zName = sqlite3DbStrDup(pParse->db,
+ pTab->aCol[pTab->iPKey].zName);
+ pList->a[0].sortOrder = pParse->iPkSortOrder;
+ assert( pParse->pNewTable==pTab );
+ pPk = sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0);
+ if( pPk==0 ) return;
+ pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY;
+ pTab->iPKey = -1;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ }
+ pPk->isCovering = 1;
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+
+ /* Make sure every column of the PRIMARY KEY is NOT NULL */
+ for(i=0; i<nPk; i++){
+ pTab->aCol[pPk->aiColumn[i]].notNull = 1;
+ }
+ pPk->uniqNotNull = 1;
+
+ /* The root page of the PRIMARY KEY is the table root page */
+ pPk->tnum = pTab->tnum;
+
+ /* Update the in-memory representation of all UNIQUE indices by converting
+ ** the final rowid column into one or more columns of the PRIMARY KEY.
+ */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int n;
+ if( IsPrimaryKeyIndex(pIdx) ) continue;
+ for(i=n=0; i<nPk; i++){
+ if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
+ }
+ if( n==0 ){
+ /* This index is a superset of the primary key */
+ pIdx->nColumn = pIdx->nKeyCol;
+ continue;
+ }
+ if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
+ for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
+ if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
+ pIdx->aiColumn[j] = pPk->aiColumn[i];
+ pIdx->azColl[j] = pPk->azColl[i];
+ j++;
+ }
+ }
+ assert( pIdx->nColumn>=pIdx->nKeyCol+n );
+ assert( pIdx->nColumn>=j );
+ }
+
+ /* Add all table columns to the PRIMARY KEY index
+ */
+ if( nPk<pTab->nCol ){
+ if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
+ for(i=0, j=nPk; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, j, i) ){
+ assert( j<pPk->nColumn );
+ pPk->aiColumn[j] = i;
+ pPk->azColl[j] = "BINARY";
+ j++;
+ }
+ }
+ assert( pPk->nColumn==j );
+ assert( pTab->nCol==j );
+ }else{
+ pPk->nColumn = pTab->nCol;
+ }
+}
+
+/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
**
@@ -1498,12 +1761,14 @@ static char *createTableStmt(sqlite3 *db, Table *p){
void sqlite3EndTable(
Parse *pParse, /* Parse context */
Token *pCons, /* The ',' token after the last column defn. */
- Token *pEnd, /* The final ')' token in the CREATE TABLE */
+ Token *pEnd, /* The ')' before options in the CREATE TABLE */
+ u8 tabOpts, /* Extra table options. Usually 0. */
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
){
- Table *p;
- sqlite3 *db = pParse->db;
- int iDb;
+ Table *p; /* The new table */
+ sqlite3 *db = pParse->db; /* The database connection */
+ int iDb; /* Database in which the table lives */
+ Index *pIdx; /* An implied index of the table */
if( (pEnd==0 && pSelect==0) || db->mallocFailed ){
return;
@@ -1513,43 +1778,45 @@ void sqlite3EndTable(
assert( !db->init.busy || !pSelect );
+ /* If the db->init.busy is 1 it means we are reading the SQL off the
+ ** "sqlite_master" or "sqlite_temp_master" table on the disk.
+ ** So do not write to the disk again. Extract the root page number
+ ** for the table from the db->init.newTnum field. (The page number
+ ** should have been put there by the sqliteOpenCb routine.)
+ */
+ if( db->init.busy ){
+ p->tnum = db->init.newTnum;
+ }
+
+ /* Special processing for WITHOUT ROWID Tables */
+ if( tabOpts & TF_WithoutRowid ){
+ if( (p->tabFlags & TF_Autoincrement) ){
+ sqlite3ErrorMsg(pParse,
+ "AUTOINCREMENT not allowed on WITHOUT ROWID tables");
+ return;
+ }
+ if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
+ sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
+ }else{
+ p->tabFlags |= TF_WithoutRowid;
+ convertToWithoutRowidTable(pParse, p);
+ }
+ }
+
iDb = sqlite3SchemaToIndex(db, p->pSchema);
#ifndef SQLITE_OMIT_CHECK
/* Resolve names in all CHECK constraint expressions.
*/
if( p->pCheck ){
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
- NameContext sNC; /* Name context for pParse->pNewTable */
- ExprList *pList; /* List of all CHECK constraints */
- int i; /* Loop counter */
-
- memset(&sNC, 0, sizeof(sNC));
- memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = p->zName;
- sSrc.a[0].pTab = p;
- sSrc.a[0].iCursor = -1;
- sNC.pParse = pParse;
- sNC.pSrcList = &sSrc;
- sNC.ncFlags = NC_IsCheck;
- pList = p->pCheck;
- for(i=0; i<pList->nExpr; i++){
- if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
- return;
- }
- }
+ sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0, p->pCheck);
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
- /* If the db->init.busy is 1 it means we are reading the SQL off the
- ** "sqlite_master" or "sqlite_temp_master" table on the disk.
- ** So do not write to the disk again. Extract the root page number
- ** for the table from the db->init.newTnum field. (The page number
- ** should have been put there by the sqliteOpenCb routine.)
- */
- if( db->init.busy ){
- p->tnum = db->init.newTnum;
+ /* Estimate the average row size for the table and for all implied indices */
+ estimateTableWidth(p);
+ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
+ estimateIndexWidth(pIdx);
}
/* If not initializing, then create a record for the new table
@@ -1625,7 +1892,9 @@ void sqlite3EndTable(
if( pSelect ){
zStmt = createTableStmt(db, p);
}else{
- n = (int)(pEnd->z - pParse->sNameToken.z) + 1;
+ Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd;
+ n = (int)(pEnd2->z - pParse->sNameToken.z);
+ if( pEnd2->z[0]!=';' ) n += pEnd2->n;
zStmt = sqlite3MPrintf(db,
"CREATE %s %.*s", zType2, n, pParse->sNameToken.z
);
@@ -1668,7 +1937,7 @@ void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "tbl_name='%q'", p->zName));
+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
}
@@ -1738,9 +2007,8 @@ void sqlite3CreateView(
}
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
- if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
- && sqlite3FixSelect(&sFix, pSelect)
- ){
+ sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
+ if( sqlite3FixSelect(&sFix, pSelect) ){
sqlite3SelectDelete(db, pSelect);
return;
}
@@ -1774,7 +2042,7 @@ void sqlite3CreateView(
sEnd.n = 1;
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
- sqlite3EndTable(pParse, 0, &sEnd, 0);
+ sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
return;
}
#endif /* SQLITE_OMIT_VIEW */
@@ -1862,7 +2130,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
pSelTab->aCol = 0;
sqlite3DeleteTable(db, pSelTab);
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
- pTable->pSchema->flags |= DB_UnresetViews;
+ pTable->pSchema->schemaFlags |= DB_UnresetViews;
}else{
pTable->nCol = 0;
nErr++;
@@ -2040,7 +2308,7 @@ static void sqlite3ClearStatTables(
){
int i;
const char *zDbName = pParse->db->aDb[iDb].zName;
- for(i=1; i<=3; i++){
+ for(i=1; i<=4; i++){
char zTab[24];
sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
@@ -2229,8 +2497,8 @@ exit_drop_table:
** currently under construction. pFromCol determines which columns
** in the current table point to the foreign key. If pFromCol==0 then
** connect the key to the last column inserted. pTo is the name of
-** the table referred to. pToCol is a list of tables in the other
-** pTo table that the foreign key points to. flags contains all
+** the table referred to (a.k.a the "parent" table). pToCol is a list
+** of tables in the parent pTo table. flags contains all
** information about the conflict resolution algorithms specified
** in the ON DELETE, ON UPDATE and ON INSERT clauses.
**
@@ -2390,6 +2658,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int addr1; /* Address of top of loop */
int addr2; /* Address to jump to for next iteration */
int tnum; /* Root page of index */
+ int iPartIdxLabel; /* Jump to this label to skip a row */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
int regRecord; /* Register holding assemblied index record */
@@ -2412,36 +2681,39 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
tnum = memRootPage;
}else{
tnum = pIndex->tnum;
- sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
}
- pKey = sqlite3IndexKeyinfo(pParse, pIndex);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
- (char *)pKey, P4_KEYINFO_HANDOFF);
- sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
+ pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
/* Open the sorter cursor if we are to use one. */
iSorter = pParse->nTab++;
- sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
+ sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)
+ sqlite3KeyInfoRef(pKey), P4_KEYINFO);
/* Open the table. Loop through all rows of the table, inserting index
** records into the sorter. */
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); VdbeCoverage(v);
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
+ sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0);
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
+ sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
- addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
- if( pIndex->onError!=OE_None ){
+ if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
+ (char *)pKey, P4_KEYINFO);
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
+
+ addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
+ assert( pKey!=0 || db->mallocFailed || pParse->nErr );
+ if( IsUniqueIndex(pIndex) && pKey!=0 ){
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
addr2 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE,
- OE_Abort, "indexed columns are not unique", P4_STATIC
- );
+ sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
+ pIndex->nKeyCol); VdbeCoverage(v);
+ sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
@@ -2449,7 +2721,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_Close, iTab);
@@ -2458,6 +2730,41 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
}
/*
+** Allocate heap space to hold an Index object with nCol columns.
+**
+** Increase the allocation size to provide an extra nExtra bytes
+** of 8-byte aligned space after the Index object and return a
+** pointer to this extra space in *ppExtra.
+*/
+Index *sqlite3AllocateIndexObject(
+ sqlite3 *db, /* Database connection */
+ i16 nCol, /* Total number of columns in the index */
+ int nExtra, /* Number of bytes of extra space to alloc */
+ char **ppExtra /* Pointer to the "extra" space */
+){
+ Index *p; /* Allocated index object */
+ int nByte; /* Bytes of space for Index object + arrays */
+
+ nByte = ROUND8(sizeof(Index)) + /* Index structure */
+ ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
+ ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
+ sizeof(i16)*nCol + /* Index.aiColumn */
+ sizeof(u8)*nCol); /* Index.aSortOrder */
+ p = sqlite3DbMallocZero(db, nByte + nExtra);
+ if( p ){
+ char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
+ p->azColl = (char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
+ p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
+ p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
+ p->aSortOrder = (u8*)pExtra;
+ p->nColumn = nCol;
+ p->nKeyCol = nCol - 1;
+ *ppExtra = ((char*)p) + nByte;
+ }
+ return p;
+}
+
+/*
** Create a new index for an SQL table. pName1.pName2 is the name of the index
** and pTblList is the name of the table that is to be indexed. Both will
** be NULL for a primary key or an index that is created to satisfy a
@@ -2471,7 +2778,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
**
** If the index is created successfully, return a pointer to the new Index
** structure. This is used by sqlite3AddPrimaryKey() to mark the index
-** as the tables primary key (Index.autoIndex==2).
+** as the tables primary key (Index.idxType==SQLITE_IDXTYPE_PRIMARYKEY)
*/
Index *sqlite3CreateIndex(
Parse *pParse, /* All information about this parse */
@@ -2481,7 +2788,7 @@ Index *sqlite3CreateIndex(
ExprList *pList, /* A list of columns to be indexed */
int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
Token *pStart, /* The CREATE token that begins this statement */
- Token *pEnd, /* The ")" that closes the CREATE INDEX statement */
+ Expr *pPIWhere, /* WHERE clause for partial indices */
int sortOrder, /* Sort order of primary key when pList==NULL */
int ifNotExist /* Omit error if index already exists */
){
@@ -2491,7 +2798,6 @@ Index *sqlite3CreateIndex(
char *zName = 0; /* Name of the index */
int nName; /* Number of characters in zName */
int i, j;
- Token nullId; /* Fake token for an empty ID list */
DbFixer sFix; /* For assigning database names to pTable */
int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */
sqlite3 *db = pParse->db;
@@ -2499,11 +2805,12 @@ Index *sqlite3CreateIndex(
int iDb; /* Index of the database that is being written */
Token *pName = 0; /* Unqualified name of the index to create */
struct ExprList_item *pListItem; /* For looping over pList */
- int nCol;
- int nExtra = 0;
- char *zExtra;
+ const Column *pTabCol; /* A column in the table */
+ int nExtra = 0; /* Space allocated for zExtra[] */
+ int nExtraCol; /* Number of extra columns needed */
+ char *zExtra = 0; /* Extra space after the Index object */
+ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */
- assert( pStart==0 || pEnd!=0 ); /* pEnd must be non-NULL if pStart is */
assert( pParse->nErr==0 ); /* Never called with prior errors */
if( db->mallocFailed || IN_DECLARE_VTAB ){
goto exit_create_index;
@@ -2539,9 +2846,8 @@ Index *sqlite3CreateIndex(
}
#endif
- if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) &&
- sqlite3FixSrcList(&sFix, pTblName)
- ){
+ sqlite3FixInit(&sFix, pParse, iDb, "index", pName);
+ if( sqlite3FixSrcList(&sFix, pTblName) ){
/* Because the parser constructs pTblName from a single identifier,
** sqlite3FixSrcList can never fail. */
assert(0);
@@ -2549,7 +2855,13 @@ Index *sqlite3CreateIndex(
pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]);
assert( db->mallocFailed==0 || pTab==0 );
if( pTab==0 ) goto exit_create_index;
- assert( db->aDb[iDb].pSchema==pTab->pSchema );
+ if( iDb==1 && db->aDb[iDb].pSchema!=pTab->pSchema ){
+ sqlite3ErrorMsg(pParse,
+ "cannot create a TEMP index on non-TEMP table \"%s\"",
+ pTab->zName);
+ goto exit_create_index;
+ }
+ if( !HasRowid(pTab) ) pPk = sqlite3PrimaryKeyIndex(pTab);
}else{
assert( pName==0 );
assert( pStart==0 );
@@ -2645,11 +2957,10 @@ Index *sqlite3CreateIndex(
** So create a fake list to simulate this.
*/
if( pList==0 ){
- nullId.z = pTab->aCol[pTab->nCol-1].zName;
- nullId.n = sqlite3Strlen30((char*)nullId.z);
pList = sqlite3ExprListAppend(pParse, 0, 0);
if( pList==0 ) goto exit_create_index;
- sqlite3ExprListSetName(pParse, pList, &nullId, 0);
+ pList->a[0].zName = sqlite3DbStrDup(pParse->db,
+ pTab->aCol[pTab->nCol-1].zName);
pList->a[0].sortOrder = (u8)sortOrder;
}
@@ -2668,35 +2979,28 @@ Index *sqlite3CreateIndex(
** Allocate the index structure.
*/
nName = sqlite3Strlen30(zName);
- nCol = pList->nExpr;
- pIndex = sqlite3DbMallocZero(db,
- ROUND8(sizeof(Index)) + /* Index structure */
- ROUND8(sizeof(tRowcnt)*(nCol+1)) + /* Index.aiRowEst */
- sizeof(char *)*nCol + /* Index.azColl */
- sizeof(int)*nCol + /* Index.aiColumn */
- sizeof(u8)*nCol + /* Index.aSortOrder */
- nName + 1 + /* Index.zName */
- nExtra /* Collation sequence names */
- );
+ nExtraCol = pPk ? pPk->nKeyCol : 1;
+ pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol,
+ nName + nExtra + 1, &zExtra);
if( db->mallocFailed ){
goto exit_create_index;
}
- zExtra = (char*)pIndex;
- pIndex->aiRowEst = (tRowcnt*)&zExtra[ROUND8(sizeof(Index))];
- pIndex->azColl = (char**)
- ((char*)pIndex->aiRowEst + ROUND8(sizeof(tRowcnt)*nCol+1));
- assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowEst) );
+ assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst) );
assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
- pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
- pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
- pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
- zExtra = (char *)(&pIndex->zName[nName+1]);
+ pIndex->zName = zExtra;
+ zExtra += nName + 1;
memcpy(pIndex->zName, zName, nName+1);
pIndex->pTable = pTab;
- pIndex->nColumn = pList->nExpr;
pIndex->onError = (u8)onError;
- pIndex->autoIndex = (u8)(pName==0);
+ pIndex->uniqNotNull = onError!=OE_None;
+ pIndex->idxType = pName ? SQLITE_IDXTYPE_APPDEF : SQLITE_IDXTYPE_UNIQUE;
pIndex->pSchema = db->aDb[iDb].pSchema;
+ pIndex->nKeyCol = pList->nExpr;
+ if( pPIWhere ){
+ sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere, 0);
+ pIndex->pPartIdxWhere = pPIWhere;
+ pPIWhere = 0;
+ }
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
/* Check to see if we should honor DESC requests on index columns
@@ -2719,7 +3023,6 @@ Index *sqlite3CreateIndex(
*/
for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
const char *zColName = pListItem->zName;
- Column *pTabCol;
int requestedSortOrder;
char *zColl; /* Collation sequence name */
@@ -2732,7 +3035,8 @@ Index *sqlite3CreateIndex(
pParse->checkSchema = 1;
goto exit_create_index;
}
- pIndex->aiColumn[i] = j;
+ assert( pTab->nCol<=0x7fff && j<=0x7fff );
+ pIndex->aiColumn[i] = (i16)j;
if( pListItem->pExpr ){
int nColl;
assert( pListItem->pExpr->op==TK_COLLATE );
@@ -2753,8 +3057,27 @@ Index *sqlite3CreateIndex(
pIndex->azColl[i] = zColl;
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
+ if( pTab->aCol[j].notNull==0 ) pIndex->uniqNotNull = 0;
+ }
+ if( pPk ){
+ for(j=0; j<pPk->nKeyCol; j++){
+ int x = pPk->aiColumn[j];
+ if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
+ pIndex->nColumn--;
+ }else{
+ pIndex->aiColumn[i] = x;
+ pIndex->azColl[i] = pPk->azColl[j];
+ pIndex->aSortOrder[i] = pPk->aSortOrder[j];
+ i++;
+ }
+ }
+ assert( i==pIndex->nColumn );
+ }else{
+ pIndex->aiColumn[i] = -1;
+ pIndex->azColl[i] = "BINARY";
}
sqlite3DefaultRowEst(pIndex);
+ if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
if( pTab==pParse->pNewTable ){
/* This routine has been called to create an automatic index as a
@@ -2781,12 +3104,12 @@ Index *sqlite3CreateIndex(
Index *pIdx;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int k;
- assert( pIdx->onError!=OE_None );
- assert( pIdx->autoIndex );
- assert( pIndex->onError!=OE_None );
+ assert( IsUniqueIndex(pIdx) );
+ assert( pIdx->idxType!=SQLITE_IDXTYPE_APPDEF );
+ assert( IsUniqueIndex(pIndex) );
- if( pIdx->nColumn!=pIndex->nColumn ) continue;
- for(k=0; k<pIdx->nColumn; k++){
+ if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue;
+ for(k=0; k<pIdx->nKeyCol; k++){
const char *z1;
const char *z2;
if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
@@ -2794,7 +3117,7 @@ Index *sqlite3CreateIndex(
z2 = pIndex->azColl[k];
if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
}
- if( k==pIdx->nColumn ){
+ if( k==pIdx->nKeyCol ){
if( pIdx->onError!=pIndex->onError ){
/* This constraint creates the same index as a previous
** constraint specified somewhere in the CREATE TABLE statement.
@@ -2836,22 +3159,20 @@ Index *sqlite3CreateIndex(
}
}
- /* If the db->init.busy is 0 then create the index on disk. This
- ** involves writing the index into the master table and filling in the
- ** index with the current table contents.
- **
- ** The db->init.busy is 0 when the user first enters a CREATE INDEX
- ** command. db->init.busy is 1 when a database is opened and
- ** CREATE INDEX statements are read out of the master table. In
- ** the latter case the index already exists on disk, which is why
- ** we don't want to recreate it.
+ /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
+ ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
+ ** emit code to allocate the index rootpage on disk and make an entry for
+ ** the index in the sqlite_master table and populate the index with
+ ** content. But, do not do this if we are simply reading the sqlite_master
+ ** table to parse the schema, or if this index is the PRIMARY KEY index
+ ** of a WITHOUT ROWID table.
**
- ** If pTblName==0 it means this index is generated as a primary key
- ** or UNIQUE constraint of a CREATE TABLE statement. Since the table
+ ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
+ ** or UNIQUE index in a CREATE TABLE statement. Since the table
** has just been created, it contains no data and the index initialization
** step can be skipped.
*/
- else{ /* if( db->init.busy==0 ) */
+ else if( pParse->nErr==0 && (HasRowid(pTab) || pTblName!=0) ){
Vdbe *v;
char *zStmt;
int iMem = ++pParse->nMem;
@@ -2869,12 +3190,11 @@ Index *sqlite3CreateIndex(
** the zStmt variable
*/
if( pStart ){
- assert( pEnd!=0 );
+ int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
+ if( pName->z[n-1]==';' ) n--;
/* A named index with an explicit CREATE INDEX statement */
zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
- onError==OE_None ? "" : " UNIQUE",
- (int)(pEnd->z - pName->z) + 1,
- pName->z);
+ onError==OE_None ? "" : " UNIQUE", n, pName->z);
}else{
/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
/* zStmt = sqlite3MPrintf(""); */
@@ -2930,10 +3250,8 @@ Index *sqlite3CreateIndex(
/* Clean up before exiting */
exit_create_index:
- if( pIndex ){
- sqlite3DbFree(db, pIndex->zColAff);
- sqlite3DbFree(db, pIndex);
- }
+ if( pIndex ) freeIndex(db, pIndex);
+ sqlite3ExprDelete(db, pPIWhere);
sqlite3ExprListDelete(db, pList);
sqlite3SrcListDelete(db, pTblName);
sqlite3DbFree(db, zName);
@@ -2948,7 +3266,7 @@ exit_create_index:
** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the
** number of rows in the table that match any particular value of the
** first column of the index. aiRowEst[2] is an estimate of the number
-** of rows that match any particular combiniation of the first 2 columns
+** of rows that match any particular combination of the first 2 columns
** of the index. And so forth. It must always be the case that
*
** aiRowEst[N]<=aiRowEst[N-1]
@@ -2959,20 +3277,27 @@ exit_create_index:
** are based on typical values found in actual indices.
*/
void sqlite3DefaultRowEst(Index *pIdx){
- tRowcnt *a = pIdx->aiRowEst;
+ /* 10, 9, 8, 7, 6 */
+ LogEst aVal[] = { 33, 32, 30, 28, 26 };
+ LogEst *a = pIdx->aiRowLogEst;
+ int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
int i;
- tRowcnt n;
- assert( a!=0 );
- a[0] = pIdx->pTable->nRowEst;
- if( a[0]<10 ) a[0] = 10;
- n = 10;
- for(i=1; i<=pIdx->nColumn; i++){
- a[i] = n;
- if( n>5 ) n--;
- }
- if( pIdx->onError!=OE_None ){
- a[pIdx->nColumn] = 1;
+
+ /* Set the first entry (number of rows in the index) to the estimated
+ ** number of rows in the table. Or 10, if the estimated number of rows
+ ** in the table is less than that. */
+ a[0] = pIdx->pTable->nRowLogEst;
+ if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
+
+ /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
+ ** 6 and each subsequent value (if any) is 5. */
+ memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
+ for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
+ a[i] = 23; assert( 23==sqlite3LogEst(5) );
}
+
+ assert( 0==sqlite3LogEst(1) );
+ if( IsUniqueIndex(pIdx) ) a[pIdx->nKeyCol] = 0;
}
/*
@@ -3003,7 +3328,7 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){
pParse->checkSchema = 1;
goto exit_drop_index;
}
- if( pIndex->autoIndex ){
+ if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
"or PRIMARY KEY constraint cannot be dropped", 0);
goto exit_drop_index;
@@ -3172,7 +3497,7 @@ SrcList *sqlite3SrcListEnlarge(
assert( iStart<=pSrc->nSrc );
/* Allocate additional space if needed */
- if( pSrc->nSrc+nExtra>pSrc->nAlloc ){
+ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){
SrcList *pNew;
int nAlloc = pSrc->nSrc+nExtra;
int nGot;
@@ -3184,7 +3509,7 @@ SrcList *sqlite3SrcListEnlarge(
}
pSrc = pNew;
nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
- pSrc->nAlloc = (u16)nGot;
+ pSrc->nAlloc = nGot;
}
/* Move existing slots that come after the newly inserted slots
@@ -3192,7 +3517,7 @@ SrcList *sqlite3SrcListEnlarge(
for(i=pSrc->nSrc-1; i>=iStart; i--){
pSrc->a[i+nExtra] = pSrc->a[i];
}
- pSrc->nSrc += (i16)nExtra;
+ pSrc->nSrc += nExtra;
/* Zero the newly allocated slots */
memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra);
@@ -3524,59 +3849,24 @@ int sqlite3OpenTempDatabase(Parse *pParse){
}
/*
-** Generate VDBE code that will verify the schema cookie and start
-** a read-transaction for all named database files.
-**
-** It is important that all schema cookies be verified and all
-** read transactions be started before anything else happens in
-** the VDBE program. But this routine can be called after much other
-** code has been generated. So here is what we do:
-**
-** The first time this routine is called, we code an OP_Goto that
-** will jump to a subroutine at the end of the program. Then we
-** record every database that needs its schema verified in the
-** pParse->cookieMask field. Later, after all other code has been
-** generated, the subroutine that does the cookie verifications and
-** starts the transactions will be coded and the OP_Goto P2 value
-** will be made to point to that subroutine. The generation of the
-** cookie verification subroutine code happens in sqlite3FinishCoding().
-**
-** If iDb<0 then code the OP_Goto only - don't set flag to verify the
-** schema on any databases. This can be used to position the OP_Goto
-** early in the code, before we know if any database tables will be used.
+** Record the fact that the schema cookie will need to be verified
+** for database iDb. The code to actually verify the schema cookie
+** will occur at the end of the top-level VDBE and will be generated
+** later, by sqlite3FinishCoding().
*/
void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
+ sqlite3 *db = pToplevel->db;
-#ifndef SQLITE_OMIT_TRIGGER
- if( pToplevel!=pParse ){
- /* This branch is taken if a trigger is currently being coded. In this
- ** case, set cookieGoto to a non-zero value to show that this function
- ** has been called. This is used by the sqlite3ExprCodeConstants()
- ** function. */
- pParse->cookieGoto = -1;
- }
-#endif
- if( pToplevel->cookieGoto==0 ){
- Vdbe *v = sqlite3GetVdbe(pToplevel);
- if( v==0 ) return; /* This only happens if there was a prior error */
- pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
- }
- if( iDb>=0 ){
- sqlite3 *db = pToplevel->db;
- yDbMask mask;
-
- assert( iDb<db->nDb );
- assert( db->aDb[iDb].pBt!=0 || iDb==1 );
- assert( iDb<SQLITE_MAX_ATTACHED+2 );
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- mask = ((yDbMask)1)<<iDb;
- if( (pToplevel->cookieMask & mask)==0 ){
- pToplevel->cookieMask |= mask;
- pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
- if( !OMIT_TEMPDB && iDb==1 ){
- sqlite3OpenTempDatabase(pToplevel);
- }
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 || iDb==1 );
+ assert( iDb<SQLITE_MAX_ATTACHED+2 );
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
+ DbMaskSet(pToplevel->cookieMask, iDb);
+ pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
+ if( !OMIT_TEMPDB && iDb==1 ){
+ sqlite3OpenTempDatabase(pToplevel);
}
}
}
@@ -3612,7 +3902,7 @@ void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){
void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3CodeVerifySchema(pParse, iDb);
- pToplevel->writeMask |= ((yDbMask)1)<<iDb;
+ DbMaskSet(pToplevel->writeMask, iDb);
pToplevel->isMultiWrite |= setStatement;
}
@@ -3659,7 +3949,8 @@ void sqlite3HaltConstraint(
int errCode, /* extended error code */
int onError, /* Constraint type */
char *p4, /* Error message */
- int p4type /* P4_STATIC or P4_TRANSIENT */
+ i8 p4type, /* P4_STATIC or P4_TRANSIENT */
+ u8 p5Errmsg /* P5_ErrMsg type */
){
Vdbe *v = sqlite3GetVdbe(pParse);
assert( (errCode&0xff)==SQLITE_CONSTRAINT );
@@ -3667,6 +3958,59 @@ void sqlite3HaltConstraint(
sqlite3MayAbort(pParse);
}
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
+ if( p5Errmsg ) sqlite3VdbeChangeP5(v, p5Errmsg);
+}
+
+/*
+** Code an OP_Halt due to UNIQUE or PRIMARY KEY constraint violation.
+*/
+void sqlite3UniqueConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Constraint type */
+ Index *pIdx /* The index that triggers the constraint */
+){
+ char *zErr;
+ int j;
+ StrAccum errMsg;
+ Table *pTab = pIdx->pTable;
+
+ sqlite3StrAccumInit(&errMsg, 0, 0, 200);
+ errMsg.db = pParse->db;
+ for(j=0; j<pIdx->nKeyCol; j++){
+ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
+ sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
+ sqlite3StrAccumAppend(&errMsg, ".", 1);
+ sqlite3StrAccumAppendAll(&errMsg, zCol);
+ }
+ zErr = sqlite3StrAccumFinish(&errMsg);
+ sqlite3HaltConstraint(pParse,
+ IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY
+ : SQLITE_CONSTRAINT_UNIQUE,
+ onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
+}
+
+
+/*
+** Code an OP_Halt due to non-unique rowid.
+*/
+void sqlite3RowidConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Conflict resolution algorithm */
+ Table *pTab /* The table with the non-unique rowid */
+){
+ char *zMsg;
+ int rc;
+ if( pTab->iPKey>=0 ){
+ zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName,
+ pTab->aCol[pTab->iPKey].zName);
+ rc = SQLITE_CONSTRAINT_PRIMARYKEY;
+ }else{
+ zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName);
+ rc = SQLITE_CONSTRAINT_ROWID;
+ }
+ sqlite3HaltConstraint(pParse, rc, onError, zMsg, P4_DYNAMIC,
+ P5_ConstraintUnique);
}
/*
@@ -3679,8 +4023,8 @@ static int collationMatch(const char *zColl, Index *pIndex){
assert( zColl!=0 );
for(i=0; i<pIndex->nColumn; i++){
const char *z = pIndex->azColl[i];
- assert( z!=0 );
- if( 0==sqlite3StrICmp(z, zColl) ){
+ assert( z!=0 || pIndex->aiColumn[i]<0 );
+ if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){
return 1;
}
}
@@ -3799,38 +4143,118 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
#endif
/*
-** Return a dynamicly allocated KeyInfo structure that can be used
-** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
+** Return a KeyInfo structure that is appropriate for the given Index.
**
-** If successful, a pointer to the new structure is returned. In this case
-** 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.
+** The KeyInfo structure for an index is cached in the Index object.
+** So there might be multiple references to the returned pointer. The
+** caller should not try to modify the KeyInfo object.
+**
+** The caller should invoke sqlite3KeyInfoUnref() on the returned object
+** when it has finished using it.
*/
-KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
- int i;
- int nCol = pIdx->nColumn;
- int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol;
+KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
+ if( pParse->nErr ) return 0;
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ if( pIdx->pKeyInfo && pIdx->pKeyInfo->db!=pParse->db ){
+ sqlite3KeyInfoUnref(pIdx->pKeyInfo);
+ pIdx->pKeyInfo = 0;
+ }
+#endif
+ if( pIdx->pKeyInfo==0 ){
+ int i;
+ int nCol = pIdx->nColumn;
+ int nKey = pIdx->nKeyCol;
+ KeyInfo *pKey;
+ if( pIdx->uniqNotNull ){
+ pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey);
+ }else{
+ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0);
+ }
+ if( pKey ){
+ assert( sqlite3KeyInfoIsWriteable(pKey) );
+ for(i=0; i<nCol; i++){
+ char *zColl = pIdx->azColl[i];
+ assert( zColl!=0 );
+ pKey->aColl[i] = strcmp(zColl,"BINARY")==0 ? 0 :
+ sqlite3LocateCollSeq(pParse, zColl);
+ pKey->aSortOrder[i] = pIdx->aSortOrder[i];
+ }
+ if( pParse->nErr ){
+ sqlite3KeyInfoUnref(pKey);
+ }else{
+ pIdx->pKeyInfo = pKey;
+ }
+ }
+ }
+ return sqlite3KeyInfoRef(pIdx->pKeyInfo);
+}
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** This routine is invoked once per CTE by the parser while parsing a
+** WITH clause.
+*/
+With *sqlite3WithAdd(
+ Parse *pParse, /* Parsing context */
+ With *pWith, /* Existing WITH clause, or NULL */
+ Token *pName, /* Name of the common-table */
+ ExprList *pArglist, /* Optional column name list for the table */
+ Select *pQuery /* Query used to initialize the table */
+){
sqlite3 *db = pParse->db;
- KeyInfo *pKey = (KeyInfo *)sqlite3DbMallocZero(db, nBytes);
+ With *pNew;
+ char *zName;
- 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++){
- char *zColl = pIdx->azColl[i];
- assert( zColl );
- pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
- pKey->aSortOrder[i] = pIdx->aSortOrder[i];
+ /* Check that the CTE name is unique within this WITH clause. If
+ ** not, store an error in the Parse structure. */
+ zName = sqlite3NameFromToken(pParse->db, pName);
+ if( zName && pWith ){
+ int i;
+ for(i=0; i<pWith->nCte; i++){
+ if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){
+ sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName);
+ }
}
- pKey->nField = (u16)nCol;
}
- if( pParse->nErr ){
- sqlite3DbFree(db, pKey);
- pKey = 0;
+ if( pWith ){
+ int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
+ pNew = sqlite3DbRealloc(db, pWith, nByte);
+ }else{
+ pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
+ }
+ assert( zName!=0 || pNew==0 );
+ assert( db->mallocFailed==0 || pNew==0 );
+
+ if( pNew==0 ){
+ sqlite3ExprListDelete(db, pArglist);
+ sqlite3SelectDelete(db, pQuery);
+ sqlite3DbFree(db, zName);
+ pNew = pWith;
+ }else{
+ pNew->a[pNew->nCte].pSelect = pQuery;
+ pNew->a[pNew->nCte].pCols = pArglist;
+ pNew->a[pNew->nCte].zName = zName;
+ pNew->a[pNew->nCte].zErr = 0;
+ pNew->nCte++;
+ }
+
+ return pNew;
+}
+
+/*
+** Free the contents of the With object passed as the second argument.
+*/
+void sqlite3WithDelete(sqlite3 *db, With *pWith){
+ if( pWith ){
+ int i;
+ for(i=0; i<pWith->nCte; i++){
+ struct Cte *pCte = &pWith->a[i];
+ sqlite3ExprListDelete(db, pCte->pCols);
+ sqlite3SelectDelete(db, pCte->pSelect);
+ sqlite3DbFree(db, pCte->zName);
+ }
+ sqlite3DbFree(db, pWith);
}
- return pKey;
}
+#endif /* !defined(SQLITE_OMIT_CTE) */
diff --git a/src/callback.c b/src/callback.c
index d40c65c..46fbe2c 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -270,9 +270,9 @@ static int matchQuality(
}
/* Bonus points if the text encoding matches */
- if( enc==p->iPrefEnc ){
+ if( enc==(p->funcFlags & SQLITE_FUNC_ENCMASK) ){
match += 2; /* Exact encoding match */
- }else if( (enc & p->iPrefEnc & 2)!=0 ){
+ }else if( (enc & p->funcFlags & 2)!=0 ){
match += 1; /* Both are UTF16, but with different byte orders */
}
@@ -357,7 +357,6 @@ FuncDef *sqlite3FindFunction(
assert( nArg>=(-2) );
assert( nArg>=(-1) || createFlag==0 );
- assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
/* First search for a match amongst the application-defined functions.
@@ -406,7 +405,7 @@ FuncDef *sqlite3FindFunction(
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
pBest->zName = (char *)&pBest[1];
pBest->nArg = (u16)nArg;
- pBest->iPrefEnc = enc;
+ pBest->funcFlags = enc;
memcpy(pBest->zName, zName, nName);
pBest->zName[nName] = 0;
sqlite3FuncDefInsert(&db->aFunc, pBest);
@@ -448,9 +447,9 @@ void sqlite3SchemaClear(void *p){
sqlite3HashClear(&temp1);
sqlite3HashClear(&pSchema->fkeyHash);
pSchema->pSeqTab = 0;
- if( pSchema->flags & DB_SchemaLoaded ){
+ if( pSchema->schemaFlags & DB_SchemaLoaded ){
pSchema->iGeneration++;
- pSchema->flags &= ~DB_SchemaLoaded;
+ pSchema->schemaFlags &= ~DB_SchemaLoaded;
}
}
diff --git a/src/crypto.c b/src/crypto.c
index 2551e6b..c9814e3 100644
--- a/src/crypto.c
+++ b/src/crypto.c
@@ -78,7 +78,7 @@ static int codec_set_pass_key(sqlite3* db, int nDb, const void *zKey, int nKey,
return SQLITE_ERROR;
}
-int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) {
+int sqlcipher_codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const char *zRight) {
struct Db *pDb = &db->aDb[iDb];
codec_ctx *ctx = NULL;
int rc;
@@ -87,8 +87,35 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
}
- CODEC_TRACE(("codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
-
+ CODEC_TRACE(("sqlcipher_codec_pragma: entered db=%p iDb=%d pParse=%p zLeft=%s zRight=%s ctx=%p\n", db, iDb, pParse, zLeft, zRight, ctx));
+
+ if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && zRight ) {
+ sqlcipher_codec_set_store_pass(ctx, sqlite3GetBoolean(zRight, 1));
+ } else
+ if( sqlite3StrICmp(zLeft, "cipher_store_pass")==0 && !zRight ) {
+ char *store_pass_value = sqlite3_mprintf("%d", sqlcipher_codec_get_store_pass(ctx));
+ codec_vdbe_return_static_string(pParse, "cipher_store_pass", store_pass_value);
+ sqlite3_free(store_pass_value);
+ }
+ if( sqlite3StrICmp(zLeft, "cipher_profile")== 0 && zRight ){
+ char *profile_status = sqlite3_mprintf("%d", sqlcipher_cipher_profile(db, zRight));
+ codec_vdbe_return_static_string(pParse, "cipher_profile", profile_status);
+ sqlite3_free(profile_status);
+ } else
+ if( sqlite3StrICmp(zLeft, "cipher_add_random")==0 && zRight ){
+ if(ctx) {
+ char *add_random_status = sqlite3_mprintf("%d", sqlcipher_codec_add_random(ctx, zRight, sqlite3Strlen30(zRight)));
+ codec_vdbe_return_static_string(pParse, "cipher_add_random", add_random_status);
+ sqlite3_free(add_random_status);
+ }
+ } else
+ if( sqlite3StrICmp(zLeft, "cipher_migrate")==0 && !zRight ){
+ if(ctx){
+ char *migrate_status = sqlite3_mprintf("%d", sqlcipher_codec_ctx_migrate(ctx));
+ codec_vdbe_return_static_string(pParse, "cipher_migrate", migrate_status);
+ sqlite3_free(migrate_status);
+ }
+ } else
if( sqlite3StrICmp(zLeft, "cipher_provider")==0 && !zRight ){
if(ctx) { codec_vdbe_return_static_string(pParse, "cipher_provider",
sqlcipher_codec_get_cipher_provider(ctx));
@@ -110,6 +137,15 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
if( sqlite3StrICmp(zLeft, "rekey_cipher")==0 && zRight ){
if(ctx) sqlcipher_codec_ctx_set_cipher(ctx, zRight, 1); // change write cipher only
}else
+ if( sqlite3StrICmp(zLeft,"cipher_default_kdf_iter")==0 ){
+ if( zRight ) {
+ sqlcipher_set_default_kdf_iter(atoi(zRight)); // change default KDF iterations
+ } else {
+ char *kdf_iter = sqlite3_mprintf("%d", sqlcipher_get_default_kdf_iter());
+ codec_vdbe_return_static_string(pParse, "cipher_default_kdf_iter", kdf_iter);
+ sqlite3_free(kdf_iter);
+ }
+ }else
if( sqlite3StrICmp(zLeft, "kdf_iter")==0 ){
if(ctx) {
if( zRight ) {
@@ -204,7 +240,7 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
if(zRight) {
if (sqlite3StrNICmp(zRight ,"x'", 2) == 0 && sqlite3Strlen30(zRight) == 5) {
unsigned char mask = 0;
- const char *hex = zRight+2;
+ const unsigned char *hex = (const unsigned char *)zRight+2;
cipher_hex2bin(hex,2,&mask);
sqlcipher_set_hmac_salt_mask(mask);
}
@@ -220,6 +256,7 @@ int codec_pragma(sqlite3* db, int iDb, Parse *pParse, const char *zLeft, const c
return 1;
}
+
/*
* sqlite3Codec can be called in multiple modes.
* encrypt mode - expected to return a pointer to the
@@ -325,16 +362,41 @@ void sqlite3_activate_see(const char* in) {
/* do nothing, security enhancements are always active */
}
+static int sqlcipher_find_db_index(sqlite3 *db, const char *zDb) {
+ int db_index;
+ if(zDb == NULL){
+ return 0;
+ }
+ for(db_index = 0; db_index < db->nDb; db_index++) {
+ struct Db *pDb = &db->aDb[db_index];
+ if(strcmp(pDb->zName, zDb) == 0) {
+ return db_index;
+ }
+ }
+ return 0;
+}
+
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) {
- CODEC_TRACE(("sqlite3_key: entered db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey));
+ CODEC_TRACE(("sqlite3_key entered: db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey));
+ return sqlite3_key_v2(db, "main", pKey, nKey);
+}
+
+int sqlite3_key_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) {
+ CODEC_TRACE(("sqlite3_key_v2: entered db=%p zDb=%s pKey=%s nKey=%d\n", db, zDb, (char *)pKey, nKey));
/* attach key if db and pKey are not null and nKey is > 0 */
if(db && pKey && nKey) {
- return sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db
+ int db_index = sqlcipher_find_db_index(db, zDb);
+ return sqlite3CodecAttach(db, db_index, pKey, nKey);
}
return SQLITE_ERROR;
}
-/* sqlite3_rekey
+int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
+ CODEC_TRACE(("sqlite3_rekey entered: db=%p pKey=%s nKey=%d\n", db, (char *)pKey, nKey));
+ return sqlite3_rekey_v2(db, "main", pKey, nKey);
+}
+
+/* sqlite3_rekey_v2
** Given a database, this will reencrypt the database using a new key.
** There is only one possible modes of operation - to encrypt a database
** that is already encrpyted. If the database is not already encrypted
@@ -344,11 +406,12 @@ int sqlite3_key(sqlite3 *db, const void *pKey, int nKey) {
** 2. If there is NOT already a key present do nothing
** 3. If there is a key present, re-encrypt the database with the new key
*/
-int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
- CODEC_TRACE(("sqlite3_rekey: entered db=%p pKey=%s, nKey=%d\n", db, (char *)pKey, nKey));
+int sqlite3_rekey_v2(sqlite3 *db, const char *zDb, const void *pKey, int nKey) {
+ CODEC_TRACE(("sqlite3_rekey_v2: entered db=%p zDb=%s pKey=%s, nKey=%d\n", db, zDb, (char *)pKey, nKey));
if(db && pKey && nKey) {
- struct Db *pDb = &db->aDb[0];
- CODEC_TRACE(("sqlite3_rekey: database pDb=%p\n", pDb));
+ int db_index = sqlcipher_find_db_index(db, zDb);
+ struct Db *pDb = &db->aDb[db_index];
+ CODEC_TRACE(("sqlite3_rekey_v2: database pDb=%p db_index:%d\n", pDb, db_index));
if(pDb->pBt) {
codec_ctx *ctx;
int rc, page_count;
@@ -360,13 +423,13 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
if(ctx == NULL) {
/* there was no codec attached to this database, so this should do nothing! */
- CODEC_TRACE(("sqlite3_rekey: no codec attached to db, exiting\n"));
+ CODEC_TRACE(("sqlite3_rekey_v2: no codec attached to db, exiting\n"));
return SQLITE_OK;
}
sqlite3_mutex_enter(db->mutex);
- codec_set_pass_key(db, 0, pKey, nKey, CIPHER_WRITE_CTX);
+ codec_set_pass_key(db, db_index, pKey, nKey, CIPHER_WRITE_CTX);
/* do stuff here to rewrite the database
** 1. Create a transaction on the database
@@ -376,7 +439,7 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
*/
rc = sqlite3BtreeBeginTrans(pDb->pBt, 1); /* begin write transaction */
sqlite3PagerPagecount(pPager, &page_count);
- for(pgno = 1; rc == SQLITE_OK && pgno <= page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */
+ for(pgno = 1; rc == SQLITE_OK && pgno <= (unsigned int)page_count; pgno++) { /* pgno's start at 1 see pager.c:pagerAcquire */
if(!sqlite3pager_is_mj_pgno(pPager, pgno)) { /* skip this page (see pager.c:pagerAcquire for reasoning) */
rc = sqlite3PagerGet(pPager, pgno, &page);
if(rc == SQLITE_OK) { /* write page see pager_incr_changecounter for example */
@@ -384,21 +447,21 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
if(rc == SQLITE_OK) {
sqlite3PagerUnref(page);
} else {
- CODEC_TRACE(("sqlite3_rekey: error %d occurred writing page %d\n", rc, pgno));
+ CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred writing page %d\n", rc, pgno));
}
} else {
- CODEC_TRACE(("sqlite3_rekey: error %d occurred getting page %d\n", rc, pgno));
+ CODEC_TRACE(("sqlite3_rekey_v2: error %d occurred getting page %d\n", rc, pgno));
}
}
}
/* if commit was successful commit and copy the rekey data to current key, else rollback to release locks */
if(rc == SQLITE_OK) {
- CODEC_TRACE(("sqlite3_rekey: committing\n"));
+ CODEC_TRACE(("sqlite3_rekey_v2: committing\n"));
rc = sqlite3BtreeCommit(pDb->pBt);
sqlcipher_codec_key_copy(ctx, CIPHER_WRITE_CTX);
} else {
- CODEC_TRACE(("sqlite3_rekey: rollback\n"));
+ CODEC_TRACE(("sqlite3_rekey_v2: rollback\n"));
sqlite3BtreeRollback(pDb->pBt, SQLITE_ABORT_ROLLBACK);
}
@@ -412,13 +475,15 @@ int sqlite3_rekey(sqlite3 *db, const void *pKey, int nKey) {
void sqlite3CodecGetKey(sqlite3* db, int nDb, void **zKey, int *nKey) {
struct Db *pDb = &db->aDb[nDb];
CODEC_TRACE(("sqlite3CodecGetKey: entered db=%p, nDb=%d\n", db, nDb));
-
if( pDb->pBt ) {
codec_ctx *ctx;
sqlite3pager_get_codec(pDb->pBt->pBt->pPager, (void **) &ctx);
-
- if(ctx) { /* if the codec has an attached codec_context user the raw key data */
- sqlcipher_codec_get_pass(ctx, zKey, nKey);
+ if(ctx) {
+ if(sqlcipher_codec_get_store_pass(ctx) == 1) {
+ sqlcipher_codec_get_pass(ctx, zKey, nKey);
+ } else {
+ sqlcipher_codec_get_keyspec(ctx, zKey, nKey);
+ }
} else {
*zKey = NULL;
*nKey = 0;
diff --git a/src/crypto.h b/src/crypto.h
index a45b57c..b24b85a 100644
--- a/src/crypto.h
+++ b/src/crypto.h
@@ -44,7 +44,7 @@
#define FILE_HEADER_SZ 16
#ifndef CIPHER_VERSION
-#define CIPHER_VERSION "2.2.1"
+#define CIPHER_VERSION "3.2.0"
#endif
#ifndef CIPHER
@@ -59,7 +59,7 @@
#define CIPHER_READWRITE_CTX 2
#ifndef PBKDF2_ITER
-#define PBKDF2_ITER 4000
+#define PBKDF2_ITER 64000
#endif
/* possible flags for cipher_ctx->flags */
@@ -142,13 +142,20 @@ static int cipher_hex2int(char c) {
(c>='a' && c<='f') ? (c)-'a'+10 : 0;
}
-static void cipher_hex2bin(const char *hex, int sz, unsigned char *out){
+static void cipher_hex2bin(const unsigned char *hex, int sz, unsigned char *out){
int i;
for(i = 0; i < sz; i += 2){
out[i/2] = (cipher_hex2int(hex[i])<<4) | cipher_hex2int(hex[i+1]);
}
}
+static void cipher_bin2hex(const unsigned char* in, int sz, char *out) {
+ int i;
+ for(i=0; i < sz; i++) {
+ sqlite3_snprintf(3, out + (i*2), "%02x ", in[i]);
+ }
+}
+
/* extensions defined in crypto_impl.c */
typedef struct codec_ctx codec_ctx;
@@ -167,12 +174,15 @@ int sqlcipher_page_cipher(codec_ctx *, int, Pgno, int, int, unsigned char *, uns
void sqlcipher_codec_ctx_set_error(codec_ctx *, int);
int sqlcipher_codec_ctx_set_pass(codec_ctx *, const void *, int, int);
-void sqlcipher_codec_get_pass(codec_ctx *, void **zKey, int *nKey);
+void sqlcipher_codec_get_keyspec(codec_ctx *, void **zKey, int *nKey);
int sqlcipher_codec_ctx_set_pagesize(codec_ctx *, int);
int sqlcipher_codec_ctx_get_pagesize(codec_ctx *);
int sqlcipher_codec_ctx_get_reservesize(codec_ctx *);
+void sqlcipher_set_default_kdf_iter(int iter);
+int sqlcipher_get_default_kdf_iter();
+
int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *, int, int);
int sqlcipher_codec_ctx_get_kdf_iter(codec_ctx *ctx, int);
@@ -202,6 +212,14 @@ int sqlcipher_codec_ctx_unset_flag(codec_ctx *ctx, unsigned int flag);
int sqlcipher_codec_ctx_get_flag(codec_ctx *ctx, unsigned int flag, int for_ctx);
const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx);
+int sqlcipher_codec_ctx_migrate(codec_ctx *ctx);
+int sqlcipher_codec_add_random(codec_ctx *ctx, const char *data, int random_sz);
+int sqlcipher_cipher_profile(sqlite3 *db, const char *destination);
+static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time);
+static int sqlcipher_codec_get_store_pass(codec_ctx *ctx);
+static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey);
+static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value);
+
#endif
#endif
/* END SQLCIPHER */
diff --git a/src/crypto_cc.c b/src/crypto_cc.c
index cf932f6..1e18dc7 100644
--- a/src/crypto_cc.c
+++ b/src/crypto_cc.c
@@ -36,6 +36,10 @@
#include <CommonCrypto/CommonCrypto.h>
#include <Security/SecRandom.h>
+static int sqlcipher_cc_add_random(void *ctx, void *buffer, int length) {
+ return SQLITE_OK;
+}
+
/* generate a defined number of random bytes */
static int sqlcipher_cc_random (void *ctx, void *buffer, int length) {
return (SecRandomCopyBytes(kSecRandomDefault, length, (uint8_t *)buffer) == 0) ? SQLITE_OK : SQLITE_ERROR;
@@ -54,8 +58,8 @@ static int sqlcipher_cc_hmac(void *ctx, unsigned char *hmac_key, int key_sz, uns
return SQLITE_OK;
}
-static int sqlcipher_cc_kdf(void *ctx, const char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
- CCKeyDerivationPBKDF(kCCPBKDF2, pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA1, workfactor, key, key_sz);
+static int sqlcipher_cc_kdf(void *ctx, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
+ CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pass, pass_sz, salt, salt_sz, kCCPRFHmacAlgSHA1, workfactor, key, key_sz);
return SQLITE_OK;
}
@@ -105,7 +109,7 @@ static int sqlcipher_cc_ctx_copy(void *target_ctx, void *source_ctx) {
}
static int sqlcipher_cc_ctx_cmp(void *c1, void *c2) {
- return SQLITE_OK;
+ return 1; /* always indicate contexts are the same */
}
static int sqlcipher_cc_ctx_init(void **ctx) {
@@ -132,6 +136,7 @@ int sqlcipher_cc_setup(sqlcipher_provider *p) {
p->ctx_cmp = sqlcipher_cc_ctx_cmp;
p->ctx_init = sqlcipher_cc_ctx_init;
p->ctx_free = sqlcipher_cc_ctx_free;
+ p->add_random = sqlcipher_cc_add_random;
return SQLITE_OK;
}
diff --git a/src/crypto_impl.c b/src/crypto_impl.c
index 1e7fa99..9a77e0f 100644
--- a/src/crypto_impl.c
+++ b/src/crypto_impl.c
@@ -47,6 +47,7 @@
to keep track of read / write state separately. The following
struct and associated functions are defined here */
typedef struct {
+ int store_pass;
int derive_key;
int kdf_iter;
int fast_kdf_iter;
@@ -56,16 +57,19 @@ typedef struct {
int pass_sz;
int reserve_sz;
int hmac_sz;
+ int keyspec_sz;
unsigned int flags;
unsigned char *key;
unsigned char *hmac_key;
- char *pass;
+ unsigned char *pass;
+ char *keyspec;
sqlcipher_provider *provider;
void *provider_ctx;
} cipher_ctx;
static unsigned int default_flags = DEFAULT_CIPHER_FLAGS;
static unsigned char hmac_salt_mask = HMAC_SALT_MASK;
+static int default_kdf_iter = PBKDF2_ITER;
static unsigned int sqlcipher_activate_count = 0;
static sqlite3_mutex* sqlcipher_provider_mutex = NULL;
static sqlcipher_provider *default_provider = NULL;
@@ -79,6 +83,8 @@ struct codec_ctx {
Btree *pBt;
cipher_ctx *read_ctx;
cipher_ctx *write_ctx;
+ unsigned int skip_read_hmac;
+ unsigned int need_kdf_salt;
};
int sqlcipher_register_provider(sqlcipher_provider *p) {
@@ -215,7 +221,9 @@ void sqlcipher_free(void *ptr, int sz) {
#if defined(__unix__) || defined(__APPLE__)
munlock(ptr, sz);
#elif defined(_WIN32)
- VirtualUnlock(ptr, sz);
+#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
+VirtualUnlock(ptr, sz);
+#endif
#endif
#endif
}
@@ -236,8 +244,10 @@ void* sqlcipher_malloc(int sz) {
#if defined(__unix__) || defined(__APPLE__)
mlock(ptr, sz);
#elif defined(_WIN32)
+#if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP))
VirtualLock(ptr, sz);
#endif
+#endif
}
#endif
return ptr;
@@ -289,6 +299,7 @@ static void sqlcipher_cipher_ctx_free(cipher_ctx **iCtx) {
sqlcipher_free(ctx->key, ctx->key_sz);
sqlcipher_free(ctx->hmac_key, ctx->key_sz);
sqlcipher_free(ctx->pass, ctx->pass_sz);
+ sqlcipher_free(ctx->keyspec, ctx->keyspec_sz);
sqlcipher_free(ctx, sizeof(cipher_ctx));
}
@@ -299,9 +310,7 @@ static void sqlcipher_cipher_ctx_free(cipher_ctx **iCtx) {
* returns 1 otherwise
*/
static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
- CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered c1=%p c2=%p\n", c1, c2));
-
- if(
+ int are_equal = (
c1->iv_sz == c2->iv_sz
&& c1->kdf_iter == c2->kdf_iter
&& c1->fast_kdf_iter == c2->fast_kdf_iter
@@ -315,9 +324,43 @@ static int sqlcipher_cipher_ctx_cmp(cipher_ctx *c1, cipher_ctx *c2) {
|| !sqlcipher_memcmp((const unsigned char*)c1->pass,
(const unsigned char*)c2->pass,
c1->pass_sz)
- )
- ) return 0;
- return 1;
+ ));
+
+ CODEC_TRACE(("sqlcipher_cipher_ctx_cmp: entered \
+ c1=%p c2=%p \
+ c1->iv_sz=%d c2->iv_sz=%d \
+ c1->kdf_iter=%d c2->kdf_iter=%d \
+ c1->fast_kdf_iter=%d c2->fast_kdf_iter=%d \
+ c1->key_sz=%d c2->key_sz=%d \
+ c1->pass_sz=%d c2->pass_sz=%d \
+ c1->flags=%d c2->flags=%d \
+ c1->hmac_sz=%d c2->hmac_sz=%d \
+ c1->provider_ctx=%p c2->provider_ctx=%p \
+ c1->pass=%p c2->pass=%p \
+ c1->pass=%s c2->pass=%s \
+ provider->ctx_cmp=%d \
+ sqlcipher_memcmp=%d \
+ are_equal=%d \
+ \n",
+ c1, c2,
+ c1->iv_sz, c2->iv_sz,
+ c1->kdf_iter, c2->kdf_iter,
+ c1->fast_kdf_iter, c2->fast_kdf_iter,
+ c1->key_sz, c2->key_sz,
+ c1->pass_sz, c2->pass_sz,
+ c1->flags, c2->flags,
+ c1->hmac_sz, c2->hmac_sz,
+ c1->provider_ctx, c2->provider_ctx,
+ c1->pass, c2->pass,
+ c1->pass, c2->pass,
+ c1->provider->ctx_cmp(c1->provider_ctx, c2->provider_ctx),
+ sqlcipher_memcmp((const unsigned char*)c1->pass,
+ (const unsigned char*)c2->pass,
+ c1->pass_sz),
+ are_equal
+ ));
+
+ return !are_equal; /* return 0 if they are the same, 1 otherwise */
}
/**
@@ -336,8 +379,9 @@ static int sqlcipher_cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
CODEC_TRACE(("sqlcipher_cipher_ctx_copy: entered target=%p, source=%p\n", target, source));
sqlcipher_free(target->pass, target->pass_sz);
+ sqlcipher_free(target->keyspec, target->keyspec_sz);
memcpy(target, source, sizeof(cipher_ctx));
-
+
target->key = key; //restore pointer to previously allocated key data
memcpy(target->key, source->key, CIPHER_MAX_KEY_SZ);
@@ -350,31 +394,79 @@ static int sqlcipher_cipher_ctx_copy(cipher_ctx *target, cipher_ctx *source) {
target->provider_ctx = provider_ctx; // restore pointer to previouly allocated provider context;
target->provider->ctx_copy(target->provider_ctx, source->provider_ctx);
- target->pass = sqlcipher_malloc(source->pass_sz);
- if(target->pass == NULL) return SQLITE_NOMEM;
- memcpy(target->pass, source->pass, source->pass_sz);
+ if(source->pass && source->pass_sz) {
+ target->pass = sqlcipher_malloc(source->pass_sz);
+ if(target->pass == NULL) return SQLITE_NOMEM;
+ memcpy(target->pass, source->pass, source->pass_sz);
+ }
+ if(source->keyspec && source->keyspec_sz) {
+ target->keyspec = sqlcipher_malloc(source->keyspec_sz);
+ if(target->keyspec == NULL) return SQLITE_NOMEM;
+ memcpy(target->keyspec, source->keyspec, source->keyspec_sz);
+ }
+ return SQLITE_OK;
+}
+/**
+ * Set the keyspec for the cipher_ctx
+ *
+ * returns SQLITE_OK if assignment was successfull
+ * returns SQLITE_NOMEM if an error occured allocating memory
+ */
+static int sqlcipher_cipher_ctx_set_keyspec(cipher_ctx *ctx, const unsigned char *key, int key_sz, const unsigned char *salt, int salt_sz) {
+
+ /* free, zero existing pointers and size */
+ sqlcipher_free(ctx->keyspec, ctx->keyspec_sz);
+ ctx->keyspec = NULL;
+ ctx->keyspec_sz = 0;
+
+ /* establic a hex-formated key specification, containing the raw encryption key and
+ the salt used to generate it */
+ ctx->keyspec_sz = ((key_sz + salt_sz) * 2) + 3;
+ ctx->keyspec = sqlcipher_malloc(ctx->keyspec_sz);
+ if(ctx->keyspec == NULL) return SQLITE_NOMEM;
+
+ ctx->keyspec[0] = 'x';
+ ctx->keyspec[1] = '\'';
+ cipher_bin2hex(key, key_sz, ctx->keyspec + 2);
+ cipher_bin2hex(salt, salt_sz, ctx->keyspec + (key_sz * 2) + 2);
+ ctx->keyspec[ctx->keyspec_sz - 1] = '\'';
return SQLITE_OK;
}
+static int sqlcipher_codec_get_store_pass(codec_ctx *ctx) {
+ return ctx->read_ctx->store_pass;
+}
+
+static void sqlcipher_codec_set_store_pass(codec_ctx *ctx, int value) {
+ ctx->read_ctx->store_pass = value;
+}
+
+static void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
+ *zKey = ctx->read_ctx->pass;
+ *nKey = ctx->read_ctx->pass_sz;
+}
/**
- * Set the raw password / key data for a cipher context
+ * Set the passphrase for the cipher_ctx
*
* returns SQLITE_OK if assignment was successfull
* returns SQLITE_NOMEM if an error occured allocating memory
- * returns SQLITE_ERROR if the key couldn't be set because the pass was null or size was zero
*/
static int sqlcipher_cipher_ctx_set_pass(cipher_ctx *ctx, const void *zKey, int nKey) {
+
+ /* free, zero existing pointers and size */
sqlcipher_free(ctx->pass, ctx->pass_sz);
- ctx->pass_sz = nKey;
- if(zKey && nKey) {
+ ctx->pass = NULL;
+ ctx->pass_sz = 0;
+
+ if(zKey && nKey) { /* if new password is provided, copy it */
+ ctx->pass_sz = nKey;
ctx->pass = sqlcipher_malloc(nKey);
if(ctx->pass == NULL) return SQLITE_NOMEM;
memcpy(ctx->pass, zKey, nKey);
- return SQLITE_OK;
- }
- return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
}
int sqlcipher_codec_ctx_set_pass(codec_ctx *ctx, const void *zKey, int nKey, int for_ctx) {
@@ -415,6 +507,15 @@ const char* sqlcipher_codec_ctx_get_cipher(codec_ctx *ctx, int for_ctx) {
return c_ctx->provider->get_cipher(c_ctx->provider_ctx);
}
+/* set the global default KDF iteration */
+void sqlcipher_set_default_kdf_iter(int iter) {
+ default_kdf_iter = iter;
+}
+
+int sqlcipher_get_default_kdf_iter() {
+ return default_kdf_iter;
+}
+
int sqlcipher_codec_ctx_set_kdf_iter(codec_ctx *ctx, int kdf_iter, int for_ctx) {
cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx;
int rc;
@@ -537,9 +638,9 @@ void* sqlcipher_codec_ctx_get_kdf_salt(codec_ctx *ctx) {
return ctx->kdf_salt;
}
-void sqlcipher_codec_get_pass(codec_ctx *ctx, void **zKey, int *nKey) {
- *zKey = ctx->read_ctx->pass;
- *nKey = ctx->read_ctx->pass_sz;
+void sqlcipher_codec_get_keyspec(codec_ctx *ctx, void **zKey, int *nKey) {
+ *zKey = ctx->read_ctx->keyspec;
+ *nKey = ctx->read_ctx->keyspec_sz;
}
int sqlcipher_codec_ctx_set_pagesize(codec_ctx *ctx, int size) {
@@ -596,12 +697,11 @@ int sqlcipher_codec_ctx_init(codec_ctx **iCtx, Db *pDb, Pager *pPager, sqlite3_f
if((rc = sqlcipher_cipher_ctx_init(&ctx->write_ctx)) != SQLITE_OK) return rc;
if(fd == NULL || sqlite3OsRead(fd, ctx->kdf_salt, FILE_HEADER_SZ, 0) != SQLITE_OK) {
- /* if unable to read the bytes, generate random salt */
- if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
+ ctx->need_kdf_salt = 1;
}
if((rc = sqlcipher_codec_ctx_set_cipher(ctx, CIPHER, 0)) != SQLITE_OK) return rc;
- if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, PBKDF2_ITER, 0)) != SQLITE_OK) return rc;
+ if((rc = sqlcipher_codec_ctx_set_kdf_iter(ctx, default_kdf_iter, 0)) != SQLITE_OK) return rc;
if((rc = sqlcipher_codec_ctx_set_fast_kdf_iter(ctx, FAST_PBKDF2_ITER, 0)) != SQLITE_OK) return rc;
if((rc = sqlcipher_codec_ctx_set_pass(ctx, zKey, nKey, 0)) != SQLITE_OK) return rc;
@@ -707,7 +807,7 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
memcpy(iv_out, iv_in, c_ctx->iv_sz); /* copy the iv from the input to output buffer */
}
- if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT)) {
+ if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) {
if(sqlcipher_page_hmac(c_ctx, pgno, in, size + c_ctx->iv_sz, hmac_out) != SQLITE_OK) {
sqlcipher_memset(out, 0, page_sz);
CODEC_TRACE(("codec_cipher: hmac operations failed for pgno=%d\n", pgno));
@@ -750,7 +850,11 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
* Derive an encryption key for a cipher contex key based on the raw password.
*
* If the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
- * the key space (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
+ * the key (i.e 64 hex chars for a 256 bit key) then the key data will be used directly.
+
+ * Else, if the raw key data is formated as x'hex' and there are exactly enough hex chars to fill
+ * the key and the salt (i.e 92 hex chars for a 256 bit key and 16 byte salt) then it will be unpacked
+ * as the key followed by the salt.
*
* Otherwise, a key data will be derived using PBKDF2
*
@@ -758,27 +862,40 @@ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int
* returns SQLITE_ERROR if the key could't be derived (for instance if pass is NULL or pass_sz is 0)
*/
static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
- CODEC_TRACE(("codec_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \
+ int rc;
+ CODEC_TRACE(("cipher_ctx_key_derive: entered c_ctx->pass=%s, c_ctx->pass_sz=%d \
ctx->kdf_salt=%p ctx->kdf_salt_sz=%d c_ctx->kdf_iter=%d \
ctx->hmac_kdf_salt=%p, c_ctx->fast_kdf_iter=%d c_ctx->key_sz=%d\n",
c_ctx->pass, c_ctx->pass_sz, ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter,
ctx->hmac_kdf_salt, c_ctx->fast_kdf_iter, c_ctx->key_sz));
-
+
if(c_ctx->pass && c_ctx->pass_sz) { // if pass is not null
- if (c_ctx->pass_sz == ((c_ctx->key_sz*2)+3) && sqlite3StrNICmp(c_ctx->pass ,"x'", 2) == 0) {
+
+ if(ctx->need_kdf_salt) {
+ if(ctx->read_ctx->provider->random(ctx->read_ctx->provider_ctx, ctx->kdf_salt, FILE_HEADER_SZ) != SQLITE_OK) return SQLITE_ERROR;
+ ctx->need_kdf_salt = 0;
+ }
+ if (c_ctx->pass_sz == ((c_ctx->key_sz * 2) + 3) && sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0) {
int n = c_ctx->pass_sz - 3; /* adjust for leading x' and tailing ' */
- const char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
- CODEC_TRACE(("codec_key_derive: using raw key from hex\n"));
+ const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
+ CODEC_TRACE(("cipher_ctx_key_derive: using raw key from hex\n"));
cipher_hex2bin(z, n, c_ctx->key);
+ } else if (c_ctx->pass_sz == (((c_ctx->key_sz + ctx->kdf_salt_sz) * 2) + 3) && sqlite3StrNICmp((const char *)c_ctx->pass ,"x'", 2) == 0) {
+ const unsigned char *z = c_ctx->pass + 2; /* adjust lead offset of x' */
+ CODEC_TRACE(("cipher_ctx_key_derive: using raw key from hex\n"));
+ cipher_hex2bin(z, (c_ctx->key_sz * 2), c_ctx->key);
+ cipher_hex2bin(z + (c_ctx->key_sz * 2), (ctx->kdf_salt_sz * 2), ctx->kdf_salt);
} else {
- CODEC_TRACE(("codec_key_derive: deriving key using full PBKDF2 with %d iterations\n", c_ctx->kdf_iter));
- c_ctx->provider->kdf(c_ctx->provider_ctx, (const char*) c_ctx->pass, c_ctx->pass_sz,
+ CODEC_TRACE(("cipher_ctx_key_derive: deriving key using full PBKDF2 with %d iterations\n", c_ctx->kdf_iter));
+ c_ctx->provider->kdf(c_ctx->provider_ctx, c_ctx->pass, c_ctx->pass_sz,
ctx->kdf_salt, ctx->kdf_salt_sz, c_ctx->kdf_iter,
c_ctx->key_sz, c_ctx->key);
-
}
+ /* set the context "keyspec" containing the hex-formatted key and salt to be used when attaching databases */
+ if((rc = sqlcipher_cipher_ctx_set_keyspec(c_ctx, c_ctx->key, c_ctx->key_sz, ctx->kdf_salt, ctx->kdf_salt_sz)) != SQLITE_OK) return rc;
+
/* if this context is setup to use hmac checks, generate a seperate and different
key for HMAC. In this case, we use the output of the previous KDF as the input to
this KDF run. This ensures a distinct but predictable HMAC key. */
@@ -795,11 +912,11 @@ static int sqlcipher_cipher_ctx_key_derive(codec_ctx *ctx, cipher_ctx *c_ctx) {
ctx->hmac_kdf_salt[i] ^= hmac_salt_mask;
}
- CODEC_TRACE(("codec_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n",
+ CODEC_TRACE(("cipher_ctx_key_derive: deriving hmac key from encryption key using PBKDF2 with %d iterations\n",
c_ctx->fast_kdf_iter));
- c_ctx->provider->kdf(c_ctx->provider_ctx, (const char*)c_ctx->key, c_ctx->key_sz,
+ c_ctx->provider->kdf(c_ctx->provider_ctx, c_ctx->key, c_ctx->key_sz,
ctx->hmac_kdf_salt, ctx->kdf_salt_sz, c_ctx->fast_kdf_iter,
c_ctx->key_sz, c_ctx->hmac_key);
}
@@ -818,12 +935,19 @@ int sqlcipher_codec_key_derive(codec_ctx *ctx) {
if(ctx->write_ctx->derive_key) {
if(sqlcipher_cipher_ctx_cmp(ctx->write_ctx, ctx->read_ctx) == 0) {
- // the relevant parameters are the same, just copy read key
+ /* the relevant parameters are the same, just copy read key */
if(sqlcipher_cipher_ctx_copy(ctx->write_ctx, ctx->read_ctx) != SQLITE_OK) return SQLITE_ERROR;
} else {
if(sqlcipher_cipher_ctx_key_derive(ctx, ctx->write_ctx) != SQLITE_OK) return SQLITE_ERROR;
}
}
+
+ /* TODO: wipe and free passphrase after key derivation */
+ if(ctx->read_ctx->store_pass != 1) {
+ sqlcipher_cipher_ctx_set_pass(ctx->read_ctx, NULL, 0);
+ sqlcipher_cipher_ctx_set_pass(ctx->write_ctx, NULL, 0);
+ }
+
return SQLITE_OK;
}
@@ -839,5 +963,261 @@ const char* sqlcipher_codec_get_cipher_provider(codec_ctx *ctx) {
return ctx->read_ctx->provider->get_provider_name(ctx->read_ctx);
}
+
+static int sqlcipher_check_connection(const char *filename, char *key, int key_sz, char *sql, int *user_version) {
+ int rc;
+ sqlite3 *db = NULL;
+ sqlite3_stmt *statement = NULL;
+ char *query_user_version = "PRAGMA user_version;";
+
+ rc = sqlite3_open(filename, &db);
+ if(rc != SQLITE_OK){
+ goto cleanup;
+ }
+ rc = sqlite3_key(db, key, key_sz);
+ if(rc != SQLITE_OK){
+ goto cleanup;
+ }
+ rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
+ if(rc != SQLITE_OK){
+ goto cleanup;
+ }
+ rc = sqlite3_prepare(db, query_user_version, -1, &statement, NULL);
+ if(rc != SQLITE_OK){
+ goto cleanup;
+ }
+ rc = sqlite3_step(statement);
+ if(rc == SQLITE_ROW){
+ *user_version = sqlite3_column_int(statement, 0);
+ rc = SQLITE_OK;
+ }
+
+cleanup:
+ if(statement){
+ sqlite3_finalize(statement);
+ }
+ if(db){
+ sqlite3_close(db);
+ }
+ return rc;
+}
+
+int sqlcipher_codec_ctx_migrate(codec_ctx *ctx) {
+ u32 meta;
+ int rc = 0;
+ int command_idx = 0;
+ int password_sz;
+ int saved_flags;
+ int saved_nChange;
+ int saved_nTotalChange;
+ void (*saved_xTrace)(void*,const char*);
+ Db *pDb = 0;
+ sqlite3 *db = ctx->pBt->db;
+ const char *db_filename = sqlite3_db_filename(db, "main");
+ char *migrated_db_filename = sqlite3_mprintf("%s-migrated", db_filename);
+ char *pragma_hmac_off = "PRAGMA cipher_use_hmac = OFF;";
+ char *pragma_4k_kdf_iter = "PRAGMA kdf_iter = 4000;";
+ char *pragma_1x_and_4k;
+ char *set_user_version;
+ char *key;
+ int key_sz;
+ int user_version = 0;
+ int upgrade_1x_format = 0;
+ int upgrade_4k_format = 0;
+ static const unsigned char aCopy[] = {
+ BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */
+ BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
+ BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
+ BTREE_USER_VERSION, 0, /* Preserve the user version */
+ BTREE_APPLICATION_ID, 0, /* Preserve the application id */
+ };
+
+
+ key_sz = ctx->read_ctx->pass_sz + 1;
+ key = sqlcipher_malloc(key_sz);
+ memset(key, 0, key_sz);
+ memcpy(key, ctx->read_ctx->pass, ctx->read_ctx->pass_sz);
+
+ if(db_filename){
+ const char* commands[5];
+ char *attach_command = sqlite3_mprintf("ATTACH DATABASE '%s-migrated' as migrate KEY '%q';",
+ db_filename, key);
+
+ int rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, "", &user_version);
+ if(rc == SQLITE_OK){
+ CODEC_TRACE(("No upgrade required - exiting\n"));
+ goto exit;
+ }
+
+ // Version 2 - check for 4k with hmac format
+ rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_4k_kdf_iter, &user_version);
+ if(rc == SQLITE_OK) {
+ CODEC_TRACE(("Version 2 format found\n"));
+ upgrade_4k_format = 1;
+ }
+
+ // Version 1 - check both no hmac and 4k together
+ pragma_1x_and_4k = sqlite3_mprintf("%s%s", pragma_hmac_off,
+ pragma_4k_kdf_iter);
+ rc = sqlcipher_check_connection(db_filename, key, ctx->read_ctx->pass_sz, pragma_1x_and_4k, &user_version);
+ sqlite3_free(pragma_1x_and_4k);
+ if(rc == SQLITE_OK) {
+ CODEC_TRACE(("Version 1 format found\n"));
+ upgrade_1x_format = 1;
+ upgrade_4k_format = 1;
+ }
+
+ if(upgrade_1x_format == 0 && upgrade_4k_format == 0) {
+ CODEC_TRACE(("Upgrade format not determined\n"));
+ goto handle_error;
+ }
+
+ set_user_version = sqlite3_mprintf("PRAGMA migrate.user_version = %d;", user_version);
+ commands[0] = upgrade_4k_format == 1 ? pragma_4k_kdf_iter : "";
+ commands[1] = upgrade_1x_format == 1 ? pragma_hmac_off : "";
+ commands[2] = attach_command;
+ commands[3] = "SELECT sqlcipher_export('migrate');";
+ commands[4] = set_user_version;
+
+ for(command_idx = 0; command_idx < ArraySize(commands); command_idx++){
+ const char *command = commands[command_idx];
+ if(strcmp(command, "") == 0){
+ continue;
+ }
+ rc = sqlite3_exec(db, command, NULL, NULL, NULL);
+ if(rc != SQLITE_OK){
+ break;
+ }
+ }
+ sqlite3_free(attach_command);
+ sqlite3_free(set_user_version);
+ sqlcipher_free(key, key_sz);
+
+ if(rc == SQLITE_OK){
+ Btree *pDest;
+ Btree *pSrc;
+ int i = 0;
+
+ if( !db->autoCommit ){
+ CODEC_TRACE(("cannot migrate from within a transaction"));
+ goto handle_error;
+ }
+ if( db->nVdbeActive>1 ){
+ CODEC_TRACE(("cannot migrate - SQL statements in progress"));
+ goto handle_error;
+ }
+
+ /* Save the current value of the database flags so that it can be
+ ** restored before returning. Then set the writable-schema flag, and
+ ** disable CHECK and foreign key constraints. */
+ saved_flags = db->flags;
+ saved_nChange = db->nChange;
+ saved_nTotalChange = db->nTotalChange;
+ saved_xTrace = db->xTrace;
+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
+ db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
+ db->xTrace = 0;
+
+ pDest = db->aDb[0].pBt;
+ pDb = &(db->aDb[db->nDb-1]);
+ pSrc = pDb->pBt;
+
+ rc = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL);
+ rc = sqlite3BtreeBeginTrans(pSrc, 2);
+ rc = sqlite3BtreeBeginTrans(pDest, 2);
+
+ assert( 1==sqlite3BtreeIsInTrans(pDest) );
+ assert( 1==sqlite3BtreeIsInTrans(pSrc) );
+
+ sqlite3CodecGetKey(db, db->nDb - 1, (void**)&key, &password_sz);
+ sqlite3CodecAttach(db, 0, key, password_sz);
+ sqlite3pager_get_codec(pDest->pBt->pPager, (void**)&ctx);
+
+ ctx->skip_read_hmac = 1;
+ for(i=0; i<ArraySize(aCopy); i+=2){
+ sqlite3BtreeGetMeta(pSrc, aCopy[i], &meta);
+ rc = sqlite3BtreeUpdateMeta(pDest, aCopy[i], meta+aCopy[i+1]);
+ if( NEVER(rc!=SQLITE_OK) ) goto handle_error;
+ }
+ rc = sqlite3BtreeCopyFile(pDest, pSrc);
+ ctx->skip_read_hmac = 0;
+ if( rc!=SQLITE_OK ) goto handle_error;
+ rc = sqlite3BtreeCommit(pDest);
+
+ db->flags = saved_flags;
+ db->nChange = saved_nChange;
+ db->nTotalChange = saved_nTotalChange;
+ db->xTrace = saved_xTrace;
+ db->autoCommit = 1;
+ if( pDb ){
+ sqlite3BtreeClose(pDb->pBt);
+ pDb->pBt = 0;
+ pDb->pSchema = 0;
+ }
+ sqlite3ResetAllSchemasOfConnection(db);
+ remove(migrated_db_filename);
+ sqlite3_free(migrated_db_filename);
+ } else {
+ CODEC_TRACE(("*** migration failure** \n\n"));
+ }
+
+ }
+ goto exit;
+
+ handle_error:
+ CODEC_TRACE(("An error occurred attempting to migrate the database\n"));
+ rc = SQLITE_ERROR;
+
+ exit:
+ return rc;
+}
+
+int sqlcipher_codec_add_random(codec_ctx *ctx, const char *zRight, int random_sz){
+ const char *suffix = &zRight[random_sz-1];
+ int n = random_sz - 3; /* adjust for leading x' and tailing ' */
+ if (n > 0 &&
+ sqlite3StrNICmp((const char *)zRight ,"x'", 2) == 0 &&
+ sqlite3StrNICmp(suffix, "'", 1) == 0 &&
+ n % 2 == 0) {
+ int rc = 0;
+ int buffer_sz = n / 2;
+ unsigned char *random;
+ const unsigned char *z = (const unsigned char *)zRight + 2; /* adjust lead offset of x' */
+ CODEC_TRACE(("sqlcipher_codec_add_random: using raw random blob from hex\n"));
+ random = sqlcipher_malloc(buffer_sz);
+ memset(random, 0, buffer_sz);
+ cipher_hex2bin(z, n, random);
+ rc = ctx->read_ctx->provider->add_random(ctx->read_ctx->provider_ctx, random, buffer_sz);
+ sqlcipher_free(random, buffer_sz);
+ return rc;
+ }
+ return SQLITE_ERROR;
+}
+
+int sqlcipher_cipher_profile(sqlite3 *db, const char *destination){
+ FILE *f;
+ if( strcmp(destination,"stdout")==0 ){
+ f = stdout;
+ }else if( strcmp(destination, "stderr")==0 ){
+ f = stderr;
+ }else if( strcmp(destination, "off")==0 ){
+ f = 0;
+ }else{
+ f = fopen(destination, "wb");
+ if( f==0 ){
+ return SQLITE_ERROR;
+ }
+ }
+ sqlite3_profile(db, sqlcipher_profile_callback, f);
+ return SQLITE_OK;
+}
+
+static void sqlcipher_profile_callback(void *file, const char *sql, sqlite3_uint64 run_time){
+ FILE *f = (FILE*)file;
+ double elapsed = run_time/1000000.0;
+ if( f ) fprintf(f, "Elapsed time:%.3f ms - %s\n", elapsed, sql);
+}
+
+
#endif
/* END SQLCIPHER */
diff --git a/src/crypto_libtomcrypt.c b/src/crypto_libtomcrypt.c
index 0299771..22f4efc 100644
--- a/src/crypto_libtomcrypt.c
+++ b/src/crypto_libtomcrypt.c
@@ -35,48 +35,89 @@
#include "sqlcipher.h"
#include <tomcrypt.h>
-typedef struct {
- prng_state prng;
-} ltc_ctx;
-
+#define FORTUNA_MAX_SZ 32
+static prng_state prng;
static unsigned int ltc_init = 0;
+static unsigned int ltc_ref_count = 0;
+static sqlite3_mutex* ltc_rand_mutex = NULL;
static int sqlcipher_ltc_add_random(void *ctx, void *buffer, int length) {
- ltc_ctx *ltc = (ltc_ctx*)ctx;
- int rc = fortuna_add_entropy(buffer, length, &(ltc->prng));
- return rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
+ int rc = 0;
+ int data_to_read = length;
+ int block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
+ const unsigned char * data = (const unsigned char *)buffer;
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_enter(ltc_rand_mutex);
+#endif
+ while(data_to_read > 0){
+ rc = fortuna_add_entropy(data, block_sz, &prng);
+ rc = rc != CRYPT_OK ? SQLITE_ERROR : SQLITE_OK;
+ if(rc != SQLITE_OK){
+ break;
+ }
+ data_to_read -= block_sz;
+ data += block_sz;
+ block_sz = data_to_read < FORTUNA_MAX_SZ ? data_to_read : FORTUNA_MAX_SZ;
+ }
+ fortuna_ready(&prng);
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_leave(ltc_rand_mutex);
+#endif
+ return rc;
}
static int sqlcipher_ltc_activate(void *ctx) {
- ltc_ctx *ltc = (ltc_ctx*)ctx;
- int random_buffer_sz = 32;
- unsigned char random_buffer[random_buffer_sz];
-
+ unsigned char random_buffer[FORTUNA_MAX_SZ];
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ if(ltc_rand_mutex == NULL){
+ ltc_rand_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ }
+ sqlite3_mutex_enter(ltc_rand_mutex);
+#endif
+ sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
if(ltc_init == 0) {
if(register_prng(&fortuna_desc) != CRYPT_OK) return SQLITE_ERROR;
if(register_cipher(&rijndael_desc) != CRYPT_OK) return SQLITE_ERROR;
if(register_hash(&sha1_desc) != CRYPT_OK) return SQLITE_ERROR;
+ if(fortuna_start(&prng) != CRYPT_OK) {
+ return SQLITE_ERROR;
+ }
ltc_init = 1;
}
- if(fortuna_start(&(ltc->prng)) != CRYPT_OK) {
- return SQLITE_ERROR;
- }
- sqlite3_randomness(random_buffer_sz, &random_buffer);
- if(sqlcipher_ltc_add_random(ctx, random_buffer, random_buffer_sz) != SQLITE_OK) {
- return SQLITE_ERROR;
- }
- if(sqlcipher_ltc_add_random(ctx, &ltc, sizeof(ltc_ctx*)) != SQLITE_OK) {
- return SQLITE_ERROR;
- }
- if(fortuna_ready(&(ltc->prng)) != CRYPT_OK) {
+ ltc_ref_count++;
+#ifndef SQLCIPHER_TEST
+ sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer);
+#endif
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_leave(ltc_rand_mutex);
+#endif
+ if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) {
return SQLITE_ERROR;
}
+ sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ);
return SQLITE_OK;
}
static int sqlcipher_ltc_deactivate(void *ctx) {
- ltc_ctx *ltc = (ltc_ctx*)ctx;
- fortuna_done(&(ltc->prng));
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_enter(ltc_rand_mutex);
+#endif
+ ltc_ref_count--;
+ if(ltc_ref_count == 0){
+ fortuna_done(&prng);
+ sqlcipher_memset((void *)&prng, 0, sizeof(prng));
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_leave(ltc_rand_mutex);
+ sqlite3_mutex_free(ltc_rand_mutex);
+ ltc_rand_mutex = NULL;
+#endif
+ }
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ else {
+ sqlite3_mutex_leave(ltc_rand_mutex);
+ }
+#endif
+ return SQLITE_OK;
}
static const char* sqlcipher_ltc_get_provider_name(void *ctx) {
@@ -84,13 +125,13 @@ static const char* sqlcipher_ltc_get_provider_name(void *ctx) {
}
static int sqlcipher_ltc_random(void *ctx, void *buffer, int length) {
- ltc_ctx *ltc = (ltc_ctx*)ctx;
- int rc;
-
- if((rc = fortuna_ready(&(ltc->prng))) != CRYPT_OK) {
- return SQLITE_ERROR;
- }
- fortuna_read(buffer, length, &(ltc->prng));
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_enter(ltc_rand_mutex);
+#endif
+ fortuna_read(buffer, length, &prng);
+#ifndef SQLCIPHER_LTC_NO_MUTEX_RAND
+ sqlite3_mutex_leave(ltc_rand_mutex);
+#endif
return SQLITE_OK;
}
@@ -107,12 +148,12 @@ static int sqlcipher_ltc_hmac(void *ctx, unsigned char *hmac_key, int key_sz, un
return SQLITE_OK;
}
-static int sqlcipher_ltc_kdf(void *ctx, const char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
+static int sqlcipher_ltc_kdf(void *ctx, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
int rc, hash_idx;
unsigned long outlen = key_sz;
- unsigned long random_buffer_sz = 256;
- char random_buffer[random_buffer_sz];
- ltc_ctx *ltc = (ltc_ctx*)ctx;
+ unsigned long random_buffer_sz = sizeof(char) * 256;
+ unsigned char *random_buffer = sqlcipher_malloc(random_buffer_sz);
+ sqlcipher_memset(random_buffer, 0, random_buffer_sz);
hash_idx = find_hash("sha1");
if((rc = pkcs_5_alg2(pass, pass_sz, salt, salt_sz,
@@ -124,6 +165,7 @@ static int sqlcipher_ltc_kdf(void *ctx, const char *pass, int pass_sz, unsigned
return SQLITE_ERROR;
}
sqlcipher_ltc_add_random(ctx, random_buffer, random_buffer_sz);
+ sqlcipher_free(random_buffer, random_buffer_sz);
return SQLITE_OK;
}
@@ -132,7 +174,7 @@ static const char* sqlcipher_ltc_get_cipher(void *ctx) {
}
static int sqlcipher_ltc_cipher(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out) {
- int rc, cipher_idx, hash_idx;
+ int rc, cipher_idx;
symmetric_CBC cbc;
if((cipher_idx = find_cipher(sqlcipher_ltc_get_cipher(ctx))) == -1) return SQLITE_ERROR;
@@ -168,7 +210,6 @@ static int sqlcipher_ltc_get_hmac_sz(void *ctx) {
}
static int sqlcipher_ltc_ctx_copy(void *target_ctx, void *source_ctx) {
- memcpy(target_ctx, source_ctx, sizeof(ltc_ctx));
return SQLITE_OK;
}
@@ -177,15 +218,12 @@ static int sqlcipher_ltc_ctx_cmp(void *c1, void *c2) {
}
static int sqlcipher_ltc_ctx_init(void **ctx) {
- *ctx = sqlcipher_malloc(sizeof(ltc_ctx));
- if(*ctx == NULL) return SQLITE_NOMEM;
- sqlcipher_ltc_activate(*ctx);
+ sqlcipher_ltc_activate(NULL);
return SQLITE_OK;
}
static int sqlcipher_ltc_ctx_free(void **ctx) {
sqlcipher_ltc_deactivate(&ctx);
- sqlcipher_free(*ctx, sizeof(ltc_ctx));
return SQLITE_OK;
}
@@ -208,6 +246,7 @@ int sqlcipher_ltc_setup(sqlcipher_provider *p) {
p->ctx_init = sqlcipher_ltc_ctx_init;
p->ctx_free = sqlcipher_ltc_ctx_free;
p->add_random = sqlcipher_ltc_add_random;
+ return SQLITE_OK;
}
#endif
diff --git a/src/crypto_openssl.c b/src/crypto_openssl.c
index 6035b7c..150ab92 100644
--- a/src/crypto_openssl.c
+++ b/src/crypto_openssl.c
@@ -154,8 +154,8 @@ static int sqlcipher_openssl_hmac(void *ctx, unsigned char *hmac_key, int key_sz
return SQLITE_OK;
}
-static int sqlcipher_openssl_kdf(void *ctx, const char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
- PKCS5_PBKDF2_HMAC_SHA1(pass, pass_sz, salt, salt_sz, workfactor, key_sz, key);
+static int sqlcipher_openssl_kdf(void *ctx, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key) {
+ PKCS5_PBKDF2_HMAC_SHA1((const char *)pass, pass_sz, salt, salt_sz, workfactor, key_sz, key);
return SQLITE_OK;
}
diff --git a/src/ctime.c b/src/ctime.c
index 60595ff..286f66e 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -117,7 +117,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_ENABLE_RTREE
"ENABLE_RTREE",
#endif
-#ifdef SQLITE_ENABLE_STAT3
+#if defined(SQLITE_ENABLE_STAT4)
+ "ENABLE_STAT4",
+#elif defined(SQLITE_ENABLE_STAT3)
"ENABLE_STAT3",
#endif
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -213,6 +215,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_OMIT_COMPOUND_SELECT
"OMIT_COMPOUND_SELECT",
#endif
+#ifdef SQLITE_OMIT_CTE
+ "OMIT_CTE",
+#endif
#ifdef SQLITE_OMIT_DATETIME_FUNCS
"OMIT_DATETIME_FUNCS",
#endif
@@ -345,6 +350,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_SOUNDEX
"SOUNDEX",
#endif
+#ifdef SQLITE_SYSTEM_MALLOC
+ "SYSTEM_MALLOC",
+#endif
#ifdef SQLITE_TCL
"TCL",
#endif
@@ -360,6 +368,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_USE_ALLOCA
"USE_ALLOCA",
#endif
+#ifdef SQLITE_WIN32_MALLOC
+ "WIN32_MALLOC",
+#endif
#ifdef SQLITE_ZERO_MALLOC
"ZERO_MALLOC"
#endif
diff --git a/src/date.c b/src/date.c
index 758dd7c..f8f4ee0 100644
--- a/src/date.c
+++ b/src/date.c
@@ -294,8 +294,8 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
** Return the number of errors.
*/
static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
- sqlite3 *db = sqlite3_context_db_handle(context);
- if( sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD)==SQLITE_OK ){
+ p->iJD = sqlite3StmtCurrentTime(context);
+ if( p->iJD>0 ){
p->validJD = 1;
return 0;
}else{
@@ -426,6 +426,10 @@ static void clearYMD_HMS_TZ(DateTime *p){
**
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
** routine will always fail.
+**
+** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
+** library function localtime_r() is used to assist in the calculation of
+** local time.
*/
static int osLocaltime(time_t *t, struct tm *pTm){
int rc;
@@ -482,6 +486,11 @@ static sqlite3_int64 localtimeOffset(
x = *p;
computeYMD_HMS(&x);
if( x.Y<1971 || x.Y>=2038 ){
+ /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
+ ** works for years between 1970 and 2037. For dates outside this range,
+ ** SQLite attempts to map the year into an equivalent year within this
+ ** range, do the calculation, then map the year back.
+ */
x.Y = 2000;
x.M = 1;
x.D = 1;
@@ -1078,8 +1087,8 @@ static void currentTimeFunc(
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
- db = sqlite3_context_db_handle(context);
- if( sqlite3OsCurrentTimeInt64(db->pVfs, &iT) ) return;
+ iT = sqlite3StmtCurrentTime(context);
+ if( iT<=0 ) return;
t = iT/1000 - 10000*(sqlite3_int64)21086676;
#ifdef HAVE_GMTIME_R
pTm = gmtime_r(&t, &sNow);
diff --git a/src/delete.c b/src/delete.c
index 634e115..c74d8ea 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -97,10 +97,8 @@ void sqlite3MaterializeView(
SrcList *pFrom;
sqlite3 *db = pParse->db;
int iDb = sqlite3SchemaToIndex(db, pView->pSchema);
-
pWhere = sqlite3ExprDup(db, pWhere, 0);
pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
-
if( pFrom ){
assert( pFrom->nSrc==1 );
pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
@@ -108,10 +106,7 @@ void sqlite3MaterializeView(
assert( pFrom->a[0].pOn==0 );
assert( pFrom->a[0].pUsing==0 );
}
-
pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
- if( pSel ) pSel->selFlags |= SF_Materialize;
-
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
sqlite3Select(pParse, pSel, &dest);
sqlite3SelectDelete(db, pSel);
@@ -134,7 +129,7 @@ Expr *sqlite3LimitWhere(
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
Expr *pLimit, /* The LIMIT clause. May be null */
Expr *pOffset, /* The OFFSET clause. May be null */
- char *zStmtType /* Either DELETE or UPDATE. For error messages. */
+ char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
){
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
@@ -209,7 +204,8 @@ limit_where_cleanup_2:
sqlite3ExprDelete(pParse->db, pOffset);
return 0;
}
-#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
+#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
+ /* && !defined(SQLITE_OMIT_SUBQUERY) */
/*
** Generate code for a DELETE FROM statement.
@@ -226,18 +222,34 @@ void sqlite3DeleteFrom(
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
const char *zDb; /* Name of database holding pTab */
- int end, addr = 0; /* A couple addresses of generated code */
int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Index *pIdx; /* For looping over indices of the table */
- int iCur; /* VDBE Cursor number for pTab */
+ int iTabCur; /* Cursor number for the table */
+ int iDataCur; /* VDBE cursor for the canonical data source */
+ int iIdxCur; /* Cursor number of the first index */
+ int nIdx; /* Number of indices */
sqlite3 *db; /* Main database structure */
AuthContext sContext; /* Authorization context */
NameContext sNC; /* Name context to resolve expressions in */
int iDb; /* Database number */
int memCnt = -1; /* Memory cell used for change counting */
int rcauth; /* Value returned by authorization callback */
-
+ int okOnePass; /* True for one-pass algorithm without the FIFO */
+ int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
+ u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
+ Index *pPk; /* The PRIMARY KEY index on the table */
+ int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */
+ i16 nPk = 1; /* Number of columns in the PRIMARY KEY */
+ int iKey; /* Memory cell holding key of row to be deleted */
+ i16 nKey; /* Number of memory cells in the row key */
+ int iEphCur = 0; /* Ephemeral table holding all primary key values */
+ int iRowSet = 0; /* Register for rowset of rows to delete */
+ int addrBypass = 0; /* Address of jump over the delete logic */
+ int addrLoop = 0; /* Top of the delete loop */
+ int addrDelete = 0; /* Jump directly to the delete logic */
+ int addrEphOpen = 0; /* Instruction to open the Ephermeral table */
+
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */
Trigger *pTrigger; /* List of table triggers, if required */
@@ -292,11 +304,11 @@ void sqlite3DeleteFrom(
}
assert(!isView || pTrigger);
- /* Assign cursor number to the table and all its indices.
+ /* Assign cursor numbers to the table and all its indices.
*/
assert( pTabList->nSrc==1 );
- iCur = pTabList->a[0].iCursor = pParse->nTab++;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ iTabCur = pTabList->a[0].iCursor = pParse->nTab++;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
pParse->nTab++;
}
@@ -320,7 +332,8 @@ void sqlite3DeleteFrom(
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
- sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
+ iDataCur = iIdxCur = iTabCur;
}
#endif
@@ -350,78 +363,171 @@ void sqlite3DeleteFrom(
&& 0==sqlite3FkRequired(pParse, pTab, 0, 0)
){
assert( !isView );
- sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
- pTab->zName, P4_STATIC);
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
+ pTab->zName, P4_STATIC);
+ }
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->pSchema==pTab->pSchema );
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
- /* The usual case: There is a WHERE clause so we have to scan through
- ** the table and pick which records to delete.
- */
{
- int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
- int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
- int regRowid; /* Actual register containing rowids */
-
- /* Collect rowids of every row to be deleted.
+ if( HasRowid(pTab) ){
+ /* For a rowid table, initialize the RowSet to an empty set */
+ pPk = 0;
+ nPk = 1;
+ iRowSet = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
+ }else{
+ /* For a WITHOUT ROWID table, create an ephermeral table used to
+ ** hold all primary keys for rows to be deleted. */
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+ iPk = pParse->nMem+1;
+ pParse->nMem += nPk;
+ iEphCur = pParse->nTab++;
+ addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
+
+ /* Construct a query to find the rowid or primary key for every row
+ ** to be deleted, based on the WHERE clause.
*/
- sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
- pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK, 0
- );
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
+ WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK,
+ iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
- regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0);
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
+ okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+
+ /* Keep track of the number of rows to be deleted */
if( db->flags & SQLITE_CountRows ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
+
+ /* Extract the rowid or primary key for the current row */
+ if( pPk ){
+ for(i=0; i<nPk; i++){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
+ pPk->aiColumn[i], iPk+i);
+ }
+ iKey = iPk;
+ }else{
+ iKey = pParse->nMem + 1;
+ iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
+ if( iKey>pParse->nMem ) pParse->nMem = iKey;
+ }
+
+ if( okOnePass ){
+ /* For ONEPASS, no need to store the rowid/primary-key. There is only
+ ** one, so just keep it in its register(s) and fall through to the
+ ** delete code.
+ */
+ nKey = nPk; /* OP_Found will use an unpacked key */
+ aToOpen = sqlite3DbMallocRaw(db, nIdx+2);
+ if( aToOpen==0 ){
+ sqlite3WhereEnd(pWInfo);
+ goto delete_from_cleanup;
+ }
+ memset(aToOpen, 1, nIdx+1);
+ aToOpen[nIdx+1] = 0;
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
+ if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
+ addrDelete = sqlite3VdbeAddOp0(v, OP_Goto); /* Jump to DELETE logic */
+ }else if( pPk ){
+ /* Construct a composite key for the row to be deleted and remember it */
+ iKey = ++pParse->nMem;
+ nKey = 0; /* Zero tells OP_Found to use a composite key */
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
+ sqlite3IndexAffinityStr(v, pPk), nPk);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
+ }else{
+ /* Get the rowid of the row to be deleted and remember it in the RowSet */
+ nKey = 1; /* OP_Seek always uses a single rowid */
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
+ }
+
+ /* End of the WHERE loop */
sqlite3WhereEnd(pWInfo);
-
- /* Delete every item whose key was written to the list during the
- ** database scan. We have to delete items after the scan is complete
- ** because deleting an item can change the scan order. */
- end = sqlite3VdbeMakeLabel(v);
-
+ if( okOnePass ){
+ /* Bypass the delete logic below if the WHERE loop found zero rows */
+ addrBypass = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBypass);
+ sqlite3VdbeJumpHere(v, addrDelete);
+ }
+
/* Unless this is a view, open cursors for the table we are
** deleting from and all its indices. If this is a view, then the
** only effect this statement has is to fire the INSTEAD OF
- ** triggers. */
+ ** triggers.
+ */
if( !isView ){
- sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iTabCur, aToOpen,
+ &iDataCur, &iIdxCur);
+ assert( pPk || iDataCur==iTabCur );
+ assert( pPk || iIdxCur==iDataCur+1 );
}
-
- addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
-
+
+ /* Set up a loop over the rowids/primary-keys that were found in the
+ ** where-clause loop above.
+ */
+ if( okOnePass ){
+ /* Just one row. Hence the top-of-loop is a no-op */
+ assert( nKey==nPk ); /* OP_Found will use an unpacked key */
+ if( aToOpen[iDataCur-iTabCur] ){
+ assert( pPk!=0 );
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
+ VdbeCoverage(v);
+ }
+ }else if( pPk ){
+ addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey);
+ assert( nKey==0 ); /* OP_Found will use a composite key */
+ }else{
+ addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
+ VdbeCoverage(v);
+ assert( nKey==1 );
+ }
+
/* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, OE_Abort);
sqlite3MayAbort(pParse);
}else
#endif
{
int count = (pParse->nested==0); /* True to count changes */
- sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ iKey, nKey, count, OE_Default, okOnePass);
}
-
- /* End of the delete loop */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
- sqlite3VdbeResolveLabel(v, end);
-
+
+ /* End of the loop over all rowids/primary-keys. */
+ if( okOnePass ){
+ sqlite3VdbeResolveLabel(v, addrBypass);
+ }else if( pPk ){
+ sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrLoop);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrLoop);
+ sqlite3VdbeJumpHere(v, addrLoop);
+ }
+
/* Close the cursors open on the table and its indexes. */
if( !isView && !IsVirtual(pTab) ){
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
+ if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
+ for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i);
}
- sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
- }
+ } /* End non-truncate path */
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
@@ -445,6 +551,7 @@ delete_from_cleanup:
sqlite3AuthContextPop(&sContext);
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprDelete(db, pWhere);
+ sqlite3DbFree(db, aToOpen);
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
@@ -459,50 +566,63 @@ delete_from_cleanup:
/*
** This routine generates VDBE code that causes a single row of a
-** single table to be deleted.
+** single table to be deleted. Both the original table entry and
+** all indices are removed.
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. A read/write cursor pointing to pTab, the table containing the row
-** to be deleted, must be opened as cursor number $iCur.
+** 1. iDataCur is an open cursor on the btree that is the canonical data
+** store for the table. (This will be either the table itself,
+** in the case of a rowid table, or the PRIMARY KEY index in the case
+** of a WITHOUT ROWID table.)
**
** 2. Read/write cursors for all indices of pTab must be open as
-** cursor number base+i for the i-th index.
+** cursor number iIdxCur+i for the i-th index.
**
-** 3. The record number of the row to be deleted must be stored in
-** memory cell iRowid.
-**
-** This routine generates code to remove both the table record and all
-** index entries that point to that record.
+** 3. The primary key for the row to be deleted must be stored in a
+** sequence of nPk memory cells starting at iPk. If nPk==0 that means
+** that a search record formed from OP_MakeRecord is contained in the
+** single memory location iPk.
*/
void sqlite3GenerateRowDelete(
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, /* If non-zero, increment the row change counter */
Trigger *pTrigger, /* List of triggers to (potentially) fire */
- int onconf /* Default ON CONFLICT policy for triggers */
+ int iDataCur, /* Cursor from which column data is extracted */
+ int iIdxCur, /* First index cursor */
+ int iPk, /* First memory cell containing the PRIMARY KEY */
+ i16 nPk, /* Number of PRIMARY KEY memory cells */
+ u8 count, /* If non-zero, increment the row change counter */
+ u8 onconf, /* Default ON CONFLICT policy for triggers */
+ u8 bNoSeek /* iDataCur is already pointing to the row to delete */
){
Vdbe *v = pParse->pVdbe; /* Vdbe */
int iOld = 0; /* First register in OLD.* array */
int iLabel; /* Label resolved to end of generated code */
+ u8 opSeek; /* Seek opcode */
/* Vdbe is guaranteed to have been allocated by this stage. */
assert( v );
+ VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
+ iDataCur, iIdxCur, iPk, (int)nPk));
/* Seek cursor iCur to the row to delete. If this row no longer exists
** (this can happen if a trigger program has already deleted it), do
** not attempt to delete it or fire any DELETE triggers. */
iLabel = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
+ opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+ if( !bNoSeek ){
+ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
+ VdbeCoverageIf(v, opSeek==OP_NotExists);
+ VdbeCoverageIf(v, opSeek==OP_NotFound);
+ }
/* If there are any triggers to fire, allocate a range of registers to
** use for the old.* references in the triggers. */
if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){
u32 mask; /* Mask of OLD.* columns in use */
int iCol; /* Iterator used while populating OLD.* */
+ int addrStart; /* Start of BEFORE trigger programs */
/* TODO: Could use temporary registers here. Also could attempt to
** avoid copying the contents of the rowid register. */
@@ -515,36 +635,44 @@ void sqlite3GenerateRowDelete(
/* Populate the OLD.* pseudo-table register array. These values will be
** used by any BEFORE and AFTER triggers that exist. */
- sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld);
+ sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
for(iCol=0; iCol<pTab->nCol; iCol++){
- if( mask==0xffffffff || mask&(1<<iCol) ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld+iCol+1);
+ testcase( mask!=0xffffffff && iCol==31 );
+ testcase( mask!=0xffffffff && iCol==32 );
+ if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
}
}
/* Invoke BEFORE DELETE trigger programs. */
+ addrStart = sqlite3VdbeCurrentAddr(v);
sqlite3CodeRowTrigger(pParse, pTrigger,
TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel
);
- /* Seek the cursor to the row to be deleted again. It may be that
- ** the BEFORE triggers coded above have already removed the row
- ** being deleted. Do not attempt to delete the row a second time, and
- ** do not fire AFTER triggers. */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
+ /* If any BEFORE triggers were coded, then seek the cursor to the
+ ** row to be deleted again. It may be that the BEFORE triggers moved
+ ** the cursor or of already deleted the row that the cursor was
+ ** pointing to.
+ */
+ if( addrStart<sqlite3VdbeCurrentAddr(v) ){
+ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
+ VdbeCoverageIf(v, opSeek==OP_NotExists);
+ VdbeCoverageIf(v, opSeek==OP_NotFound);
+ }
/* Do FK processing. This call checks that any FK constraints that
** refer to this table (i.e. constraints attached to other tables)
** are not violated by deleting this row. */
- sqlite3FkCheck(pParse, pTab, iOld, 0);
+ sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
}
/* Delete the index and table entries. Skip this step if pTab is really
** a view (in which case the only effect of the DELETE statement is to
** fire the INSTEAD OF triggers). */
if( pTab->pSelect==0 ){
- sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
- sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
if( count ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
@@ -553,7 +681,7 @@ void sqlite3GenerateRowDelete(
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just deleted. */
- sqlite3FkActions(pParse, pTab, 0, iOld);
+ sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
/* Invoke AFTER DELETE trigger programs. */
sqlite3CodeRowTrigger(pParse, pTrigger,
@@ -564,58 +692,98 @@ void sqlite3GenerateRowDelete(
** trigger programs were invoked. Or if a trigger program throws a
** RAISE(IGNORE) exception. */
sqlite3VdbeResolveLabel(v, iLabel);
+ VdbeModuleComment((v, "END: GenRowDel()"));
}
/*
** This routine generates VDBE code that causes the deletion of all
-** index entries associated with a single row of a single table.
+** index entries associated with a single row of a single table, pTab
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. A read/write cursor pointing to pTab, the table containing the row
-** to be deleted, must be opened as cursor number "iCur".
+** 1. A read/write cursor "iDataCur" must be open on the canonical storage
+** btree for the table pTab. (This will be either the table itself
+** for rowid tables or to the primary key index for WITHOUT ROWID
+** tables.)
**
** 2. Read/write cursors for all indices of pTab must be open as
-** cursor number iCur+i for the i-th index.
+** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex
+** index is the 0-th index.)
**
-** 3. The "iCur" cursor must be pointing to the row that is to be
-** deleted.
+** 3. The "iDataCur" cursor must be already be positioned on the row
+** that is to be deleted.
*/
void sqlite3GenerateRowIndexDelete(
Parse *pParse, /* Parsing and code generating context */
Table *pTab, /* Table containing the row to be deleted */
- int iCur, /* Cursor number for the table */
+ int iDataCur, /* Cursor of table holding data. */
+ int iIdxCur, /* First index cursor */
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( 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);
+ int i; /* Index loop counter */
+ int r1 = -1; /* Register holding an index key */
+ int iPartIdxLabel; /* Jump destination for skipping partial index entries */
+ Index *pIdx; /* Current index */
+ Index *pPrior = 0; /* Prior index */
+ Vdbe *v; /* The prepared statement under construction */
+ Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
+
+ v = pParse->pVdbe;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ assert( iIdxCur+i!=iDataCur || pPk==pIdx );
+ if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
+ if( pIdx==pPk ) continue;
+ VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
+ &iPartIdxLabel, pPrior, r1);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
+ pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
+ sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
+ pPrior = pIdx;
}
}
/*
-** Generate code that will assemble an index key and put it in register
+** Generate code that will assemble an index key and stores 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.
+** the entry that needs indexing. If pTab is a WITHOUT ROWID table, then
+** iCur must be the cursor of the PRIMARY KEY index.
**
** 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.
+**
+** If *piPartIdxLabel is not NULL, fill it in with a label and jump
+** to that label if pIdx is a partial index that should be skipped.
+** The label should be resolved using sqlite3ResolvePartIdxLabel().
+** A partial index should be skipped if its WHERE clause evaluates
+** to false or null. If pIdx is not a partial index, *piPartIdxLabel
+** will be set to zero which is an empty label that is ignored by
+** sqlite3ResolvePartIdxLabel().
+**
+** The pPrior and regPrior parameters are used to implement a cache to
+** avoid unnecessary register loads. If pPrior is not NULL, then it is
+** a pointer to a different index for which an index key has just been
+** computed into register regPrior. If the current pIdx index is generating
+** its key into the same sequence of registers and if pPrior and pIdx share
+** a column in common, then the register corresponding to that column already
+** holds the correct value and the loading of that register is skipped.
+** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK
+** on a table with multiple indices, and especially with the ROWID or
+** PRIMARY KEY columns of the index.
*/
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 regOut, /* Write the new index key to this register */
- int doMakeRec /* Run the OP_MakeRecord instruction if true */
+ Parse *pParse, /* Parsing context */
+ Index *pIdx, /* The index for which to generate a key */
+ int iDataCur, /* Cursor number from which to take column data */
+ int regOut, /* Put the new key into this register if not 0 */
+ int prefixOnly, /* Compute only a unique prefix of the key */
+ int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */
+ Index *pPrior, /* Previously generated index key */
+ int regPrior /* Register holding previous generated key */
){
Vdbe *v = pParse->pVdbe;
int j;
@@ -623,30 +791,47 @@ int sqlite3GenerateIndexKey(
int regBase;
int nCol;
- 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 ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j);
+ if( piPartIdxLabel ){
+ if( pIdx->pPartIdxWhere ){
+ *piPartIdxLabel = sqlite3VdbeMakeLabel(v);
+ pParse->iPartIdxTab = iDataCur;
+ sqlite3ExprCachePush(pParse);
+ sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
+ SQLITE_JUMPIFNULL);
}else{
- sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j);
- sqlite3ColumnDefault(v, pTab, idx, -1);
+ *piPartIdxLabel = 0;
}
}
- if( doMakeRec ){
- const char *zAff;
- if( pTab->pSelect
- || OptimizationDisabled(pParse->db, SQLITE_IdxRealAsInt)
- ){
- zAff = 0;
- }else{
- zAff = sqlite3IndexAffinityStr(v, pIdx);
- }
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
+ nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
+ regBase = sqlite3GetTempRange(pParse, nCol);
+ if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;
+ for(j=0; j<nCol; j++){
+ if( pPrior && pPrior->aiColumn[j]==pIdx->aiColumn[j] ) continue;
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pIdx->aiColumn[j],
+ regBase+j);
+ /* If the column affinity is REAL but the number is an integer, then it
+ ** might be stored in the table as an integer (using a compact
+ ** representation) then converted to REAL by an OP_RealAffinity opcode.
+ ** But we are getting ready to store this value back into an index, where
+ ** it should be converted by to INTEGER again. So omit the OP_RealAffinity
+ ** opcode if it is present */
+ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);
}
- sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
+ if( regOut ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
+ }
+ sqlite3ReleaseTempRange(pParse, regBase, nCol);
return regBase;
}
+
+/*
+** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label
+** because it was a partial index, then this routine should be called to
+** resolve that label.
+*/
+void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
+ if( iLabel ){
+ sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);
+ sqlite3ExprCachePop(pParse);
+ }
+}
diff --git a/src/expr.c b/src/expr.c
index 660397e..0d2292e 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -33,6 +33,7 @@
char sqlite3ExprAffinity(Expr *pExpr){
int op;
pExpr = sqlite3ExprSkipCollate(pExpr);
+ if( pExpr->flags & EP_Generic ) return 0;
op = pExpr->op;
if( op==TK_SELECT ){
assert( pExpr->flags&EP_xIsSelect );
@@ -41,7 +42,7 @@ char sqlite3ExprAffinity(Expr *pExpr){
#ifndef SQLITE_OMIT_CAST
if( op==TK_CAST ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- return sqlite3AffinityType(pExpr->u.zToken);
+ return sqlite3AffinityType(pExpr->u.zToken, 0);
}
#endif
if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER)
@@ -65,12 +66,16 @@ char sqlite3ExprAffinity(Expr *pExpr){
** If a memory allocation error occurs, that fact is recorded in pParse->db
** and the pExpr parameter is returned unchanged.
*/
-Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr *pExpr, Token *pCollName){
+Expr *sqlite3ExprAddCollateToken(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* Add the "COLLATE" clause to this expression */
+ const Token *pCollName /* Name of collating sequence */
+){
if( pCollName->n>0 ){
Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, 1);
if( pNew ){
pNew->pLeft = pExpr;
- pNew->flags |= EP_Collate;
+ pNew->flags |= EP_Collate|EP_Skip;
pExpr = pNew;
}
}
@@ -85,13 +90,21 @@ Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
}
/*
-** Skip over any TK_COLLATE and/or TK_AS operators at the root of
-** an expression.
+** Skip over any TK_COLLATE or TK_AS operators and any unlikely()
+** or likelihood() function at the root of an expression.
*/
Expr *sqlite3ExprSkipCollate(Expr *pExpr){
- while( pExpr && (pExpr->op==TK_COLLATE || pExpr->op==TK_AS) ){
- pExpr = pExpr->pLeft;
- }
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ if( ExprHasProperty(pExpr, EP_Unlikely) ){
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ assert( pExpr->x.pList->nExpr>0 );
+ assert( pExpr->op==TK_FUNCTION );
+ pExpr = pExpr->x.pList->a[0].pExpr;
+ }else{
+ assert( pExpr->op==TK_COLLATE || pExpr->op==TK_AS );
+ pExpr = pExpr->pLeft;
+ }
+ }
return pExpr;
}
@@ -110,12 +123,12 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
Expr *p = pExpr;
while( p ){
int op = p->op;
+ if( p->flags & EP_Generic ) break;
if( op==TK_CAST || op==TK_UPLUS ){
p = p->pLeft;
continue;
}
- assert( op!=TK_REGISTER || p->op2!=TK_COLLATE );
- if( op==TK_COLLATE ){
+ if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
break;
}
@@ -516,16 +529,25 @@ Expr *sqlite3PExpr(
}
/*
-** Return 1 if an expression must be FALSE in all cases and 0 if the
-** expression might be true. This is an optimization. If is OK to
-** return 0 here even if the expression really is always false (a
-** false negative). But it is a bug to return 1 if the expression
-** might be true in some rare circumstances (a false positive.)
+** If the expression is always either TRUE or FALSE (respectively),
+** then return 1. If one cannot determine the truth value of the
+** expression at compile-time return 0.
+**
+** This is an optimization. If is OK to return 0 here even if
+** the expression really is always false or false (a false negative).
+** But it is a bug to return 1 if the expression might have different
+** boolean values in different circumstances (a false positive.)
**
** Note that if the expression is part of conditional for a
** LEFT JOIN, then we cannot determine at compile-time whether or not
** is it true or false, so always return 0.
*/
+static int exprAlwaysTrue(Expr *p){
+ int v = 0;
+ if( ExprHasProperty(p, EP_FromJoin) ) return 0;
+ if( !sqlite3ExprIsInteger(p, &v) ) return 0;
+ return v!=0;
+}
static int exprAlwaysFalse(Expr *p){
int v = 0;
if( ExprHasProperty(p, EP_FromJoin) ) return 0;
@@ -597,7 +619,7 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
const char *z;
if( pExpr==0 ) return;
- assert( !ExprHasAnyProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
z = pExpr->u.zToken;
assert( z!=0 );
assert( z[0]!=0 );
@@ -667,12 +689,12 @@ void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p==0 ) return;
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
+ if( !ExprHasProperty(p, EP_TokenOnly) ){
+ /* The Expr.x union is never used at the same time as Expr.pRight */
+ assert( p->x.pList==0 || p->pRight==0 );
sqlite3ExprDelete(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
- if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){
- sqlite3DbFree(db, p->u.zToken);
- }
+ if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( ExprHasProperty(p, EP_xIsSelect) ){
sqlite3SelectDelete(db, p->x.pSelect);
}else{
@@ -732,16 +754,19 @@ static int exprStructSize(Expr *p){
static int dupedExprStructSize(Expr *p, int flags){
int nSize;
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
+ assert( EXPR_FULLSIZE<=0xfff );
+ assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
if( 0==(flags&EXPRDUP_REDUCE) ){
nSize = EXPR_FULLSIZE;
}else{
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
assert( !ExprHasProperty(p, EP_FromJoin) );
- assert( (p->flags2 & EP2_MallocedToken)==0 );
- assert( (p->flags2 & EP2_Irreducible)==0 );
- if( p->pLeft || p->pRight || p->x.pList ){
+ assert( !ExprHasProperty(p, EP_MemToken) );
+ assert( !ExprHasProperty(p, EP_NoReduce) );
+ if( p->pLeft || p->x.pList ){
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
}else{
+ assert( p->pRight==0 );
nSize = EXPR_TOKENONLYSIZE | EP_TokenOnly;
}
}
@@ -835,7 +860,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static);
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
pNew->flags |= staticFlag;
@@ -855,7 +880,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
}
/* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly) ){
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
zAlloc += dupedExprNodeSize(p, flags);
if( ExprHasProperty(pNew, EP_Reduced) ){
pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
@@ -865,8 +890,7 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
*pzBuffer = zAlloc;
}
}else{
- pNew->flags2 = 0;
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
+ if( !ExprHasProperty(p, EP_TokenOnly) ){
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
@@ -878,6 +902,33 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
}
/*
+** Create and return a deep copy of the object passed as the second
+** argument. If an OOM condition is encountered, NULL is returned
+** and the db->mallocFailed flag set.
+*/
+#ifndef SQLITE_OMIT_CTE
+static With *withDup(sqlite3 *db, With *p){
+ With *pRet = 0;
+ if( p ){
+ int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
+ pRet = sqlite3DbMallocZero(db, nByte);
+ if( pRet ){
+ int i;
+ pRet->nCte = p->nCte;
+ for(i=0; i<p->nCte; i++){
+ pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
+ pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
+ pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName);
+ }
+ }
+ }
+ return pRet;
+}
+#else
+# define withDup(x,y) 0
+#endif
+
+/*
** The following group of routines make deep copies of expressions,
** expression lists, ID lists, and select statements. The copies can
** be deleted (by being passed to their respective ...Delete() routines)
@@ -904,7 +955,6 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
if( p==0 ) return 0;
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
- pNew->iECursor = 0;
pNew->nExpr = i = p->nExpr;
if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; i<p->nExpr; i+=i){}
pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) );
@@ -920,8 +970,8 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
- pItem->iOrderByCol = pOldItem->iOrderByCol;
- pItem->iAlias = pOldItem->iAlias;
+ pItem->bSpanIsTab = pOldItem->bSpanIsTab;
+ pItem->u = pOldItem->u;
}
return pNew;
}
@@ -957,6 +1007,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
pNewItem->regReturn = pOldItem->regReturn;
pNewItem->isCorrelated = pOldItem->isCorrelated;
pNewItem->viaCoroutine = pOldItem->viaCoroutine;
+ pNewItem->isRecursive = pOldItem->isRecursive;
pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex);
pNewItem->notIndexed = pOldItem->notIndexed;
pNewItem->pIndex = pOldItem->pIndex;
@@ -1014,10 +1065,10 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
pNew->iLimit = 0;
pNew->iOffset = 0;
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
- pNew->pRightmost = 0;
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
- pNew->addrOpenEphm[2] = -1;
+ pNew->nSelectRow = p->nSelectRow;
+ pNew->pWith = withDup(db, p->pWith);
return pNew;
}
#else
@@ -1175,16 +1226,19 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
/* If pWalker->u.i is 3 then any term of the expression that comes from
** the ON or USING clauses of a join disqualifies the expression
** from being considered constant. */
- if( pWalker->u.i==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){
+ if( pWalker->u.i==3 && ExprHasProperty(pExpr, EP_FromJoin) ){
pWalker->u.i = 0;
return WRC_Abort;
}
switch( pExpr->op ){
/* Consider functions to be constant if all their arguments are constant
- ** and pWalker->u.i==2 */
+ ** and either pWalker->u.i==2 or the function as the SQLITE_FUNC_CONST
+ ** flag. */
case TK_FUNCTION:
- if( pWalker->u.i==2 ) return 0;
+ if( pWalker->u.i==2 || ExprHasProperty(pExpr,EP_Constant) ){
+ return WRC_Continue;
+ }
/* Fall through */
case TK_ID:
case TK_COLUMN:
@@ -1278,6 +1332,7 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){
case TK_UMINUS: {
int v;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
+ assert( v!=(-2147483647-1) );
*pValue = -v;
rc = 1;
}
@@ -1313,30 +1368,15 @@ int sqlite3ExprCanBeNull(const Expr *p){
case TK_FLOAT:
case TK_BLOB:
return 0;
+ case TK_COLUMN:
+ assert( p->pTab!=0 );
+ return p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0;
default:
return 1;
}
}
/*
-** Generate an OP_IsNull instruction that tests register iReg and jumps
-** to location iDest if the value in iReg is NULL. The value in iReg
-** was computed by pExpr. If we can look at pExpr at compile-time and
-** determine that it can never generate a NULL, then the OP_IsNull operation
-** can be omitted.
-*/
-void sqlite3ExprCodeIsNullJump(
- Vdbe *v, /* The VDBE under construction */
- const Expr *pExpr, /* Only generate OP_IsNull if this expr can be NULL */
- int iReg, /* Test the value in this register for NULL */
- int iDest /* Jump here if the value is null */
-){
- if( sqlite3ExprCanBeNull(pExpr) ){
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iDest);
- }
-}
-
-/*
** Return TRUE if the given expression is a constant which would be
** unchanged by OP_Affinity with the affinity given in the second
** argument.
@@ -1439,6 +1479,40 @@ int sqlite3CodeOnce(Parse *pParse){
}
/*
+** Generate code that checks the left-most column of index table iCur to see if
+** it contains any NULL entries. Cause the register at regHasNull to be set
+** to a non-NULL value if iCur contains no NULLs. Cause register regHasNull
+** to be set to NULL if iCur contains one or more NULL values.
+*/
+static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
+ int j1;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regHasNull);
+ j1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, regHasNull);
+ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ VdbeComment((v, "first_entry_in(%d)", iCur));
+ sqlite3VdbeJumpHere(v, j1);
+}
+
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** The argument is an IN operator with a list (not a subquery) on the
+** right-hand side. Return TRUE if that list is constant.
+*/
+static int sqlite3InRhsIsConstant(Expr *pIn){
+ Expr *pLHS;
+ int res;
+ assert( !ExprHasProperty(pIn, EP_xIsSelect) );
+ pLHS = pIn->pLeft;
+ pIn->pLeft = 0;
+ res = sqlite3ExprIsConstant(pIn);
+ pIn->pLeft = pLHS;
+ return res;
+}
+#endif
+
+/*
** This function is used by the implementation of the IN (...) operator.
** The pX parameter is the expression on the RHS of the IN operator, which
** might be either a list of expressions or a subquery.
@@ -1447,7 +1521,7 @@ int sqlite3CodeOnce(Parse *pParse){
** be used either to test for membership in the RHS set or to iterate through
** all members of the RHS set, skipping duplicates.
**
-** A cursor is opened on the b-tree object that the RHS of the IN operator
+** A cursor is opened on the b-tree object that is the RHS of the IN operator
** and pX->iTable is set to the index of that cursor.
**
** The returned value of this function indicates the b-tree type, as follows:
@@ -1457,6 +1531,8 @@ int sqlite3CodeOnce(Parse *pParse){
** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index.
** IN_INDEX_EPH - The cursor was opened on a specially created and
** populated epheremal table.
+** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be
+** implemented as a sequence of comparisons.
**
** An existing b-tree might be used if the RHS expression pX is a simple
** subquery such as:
@@ -1466,51 +1542,56 @@ int sqlite3CodeOnce(Parse *pParse){
** If the RHS of the IN operator is a list or a more complex subquery, then
** an ephemeral table might need to be generated from the RHS and then
** pX->iTable made to point to the ephermeral table instead of an
-** existing table.
+** existing table.
**
-** If the prNotFound parameter is 0, then the b-tree 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
+** The inFlags parameter must contain exactly one of the bits
+** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP. If inFlags contains
+** IN_INDEX_MEMBERSHIP, then the generated table will be used for a
+** fast membership test. When the IN_INDEX_LOOP bit is set, the
+** IN index will be used to loop over all values of the RHS of the
+** IN operator.
+**
+** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
+** through the set members) then the b-tree must not contain duplicates.
+** 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
** has a UNIQUE constraint or UNIQUE index.
**
-** If the prNotFound parameter is not 0, then the b-tree will be used
-** for fast set membership tests. In this case an epheremal table must
+** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
+** for fast set membership tests) then 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.
**
+** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and
+** if the RHS of the IN operator is a list (not a subquery) then this
+** routine might decide that creating an ephemeral b-tree for membership
+** testing is too expensive and return IN_INDEX_NOOP. In that case, the
+** calling routine should implement the IN operator using a sequence
+** of Eq or Ne comparison operations.
+**
** When the b-tree is being used for membership tests, the calling function
-** 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 any chance that the (...) might contain a NULL value at
+** might need to know whether or not the RHS side of the IN operator
+** contains a NULL. If prRhsHasNull is not a NULL pointer and
+** if there is any chance that the (...) might 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 (...) contains a
-** NULL value, then *prNotFound is left unchanged.
+** to *prRhsHasNull. If there is no chance that the (...) contains a
+** NULL value, then *prRhsHasNull is left unchanged.
**
-** If a register is allocated and its location stored in *prNotFound, then
-** its initial value is NULL. If the (...) does not remain constant
-** for the duration of the query (i.e. the SELECT within the (...)
-** is a correlated subquery) then the value of the allocated register is
-** reset to NULL each time the subquery is rerun. 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.
+** If a register is allocated and its location stored in *prRhsHasNull, then
+** the value in that register will be NULL if the b-tree contains one or more
+** NULL values, and it will be some non-NULL value if the b-tree contains no
+** NULL values.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
+int sqlite3FindInIndex(Parse *pParse, Expr *pX, u32 inFlags, int *prRhsHasNull){
Select *p; /* SELECT to the right of IN operator */
int eType = 0; /* Type of RHS table. IN_INDEX_* */
int iTab = pParse->nTab++; /* Cursor of the RHS table */
- int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
+ int mustBeUnique; /* True if RHS must be unique */
Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
assert( pX->op==TK_IN );
+ mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0;
/* Check to see if an existing table or index can be used to
** satisfy the query. This is preferable to generating a new
@@ -1521,8 +1602,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
sqlite3 *db = pParse->db; /* Database connection */
Table *pTab; /* Table <table>. */
Expr *pExpr; /* Expression <column> */
- int iCol; /* Index of column <column> */
- int iDb; /* Database idx for pTab */
+ i16 iCol; /* Index of column <column> */
+ i16 iDb; /* Database idx for pTab */
assert( p ); /* Because of isCandidateForInOpt(p) */
assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
@@ -1530,9 +1611,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
pTab = p->pSrc->a[0].pTab;
pExpr = p->pEList->a[0].pExpr;
- iCol = pExpr->iColumn;
+ iCol = (i16)pExpr->iColumn;
- /* Code an OP_VerifyCookie and OP_TableLock for <table>. */
+ /* Code an OP_Transaction and OP_TableLock for <table>. */
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
@@ -1543,9 +1624,8 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
*/
assert(v);
if( iCol<0 ){
- int iAddr;
-
- iAddr = sqlite3CodeOnce(pParse);
+ int iAddr = sqlite3CodeOnce(pParse);
+ VdbeCoverage(v);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
@@ -1568,46 +1648,55 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
if( (pIdx->aiColumn[0]==iCol)
&& sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
- && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
+ && (!mustBeUnique || (pIdx->nKeyCol==1 && IsUniqueIndex(pIdx)))
){
- int iAddr;
- char *pKey;
-
- pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
- iAddr = sqlite3CodeOnce(pParse);
-
- sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
- pKey,P4_KEYINFO_HANDOFF);
+ int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
- sqlite3VdbeJumpHere(v, iAddr);
- if( prNotFound && !pTab->aCol[iCol].notNull ){
- *prNotFound = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
+ if( prRhsHasNull && !pTab->aCol[iCol].notNull ){
+ *prRhsHasNull = ++pParse->nMem;
+ sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
}
+ sqlite3VdbeJumpHere(v, iAddr);
}
}
}
}
+ /* If no preexisting index is available for the IN clause
+ ** and IN_INDEX_NOOP is an allowed reply
+ ** and the RHS of the IN operator is a list, not a subquery
+ ** and the RHS is not contant or has two or fewer terms,
+ ** then it is not worth creating an ephermeral table to evaluate
+ ** the IN operator so return IN_INDEX_NOOP.
+ */
+ if( eType==0
+ && (inFlags & IN_INDEX_NOOP_OK)
+ && !ExprHasProperty(pX, EP_xIsSelect)
+ && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
+ ){
+ eType = IN_INDEX_NOOP;
+ }
+
+
if( eType==0 ){
- /* Could not found an existing table or index to use as the RHS b-tree.
+ /* Could not find an existing table or index to use as the RHS b-tree.
** We will have to generate an ephemeral table to do the job.
*/
- double savedNQueryLoop = pParse->nQueryLoop;
+ u32 savedNQueryLoop = pParse->nQueryLoop;
int rMayHaveNull = 0;
eType = IN_INDEX_EPH;
- if( prNotFound ){
- *prNotFound = rMayHaveNull = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, *prNotFound);
- }else{
- testcase( pParse->nQueryLoop>(double)1 );
- pParse->nQueryLoop = (double)1;
- if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){
+ if( inFlags & IN_INDEX_LOOP ){
+ pParse->nQueryLoop = 0;
+ if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
eType = IN_INDEX_ROWID;
}
+ }else if( prRhsHasNull ){
+ *prRhsHasNull = rMayHaveNull = ++pParse->nMem;
}
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
pParse->nQueryLoop = savedNQueryLoop;
@@ -1638,15 +1727,9 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
**
** If rMayHaveNull is non-zero, that means that the operation is an IN
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
-** Furthermore, the IN is in a WHERE clause and that we really want
-** to iterate over the RHS of the IN operator in order to quickly locate
-** all corresponding LHS elements. All this routine does is initialize
-** the register given by rMayHaveNull to NULL. Calling routines will take
-** care of changing this register value to non-NULL if the RHS is NULL-free.
-**
-** If rMayHaveNull is zero, that means that the subquery is being used
-** for membership testing only. There is no need to initialize any
-** registers to indicate the presense or absence of NULLs on the RHS.
+** All this routine does is initialize the register given by rMayHaveNull
+** to NULL. Calling routines will take care of changing this register
+** value to non-NULL if the RHS is NULL-free.
**
** For a SELECT or EXISTS operator, return the register that holds the
** result. For IN operators or if an error occurs, the return value is 0.
@@ -1655,10 +1738,10 @@ int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
int sqlite3CodeSubselect(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
- int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
+ int rHasNullFlag, /* Register that records whether NULLs exist in RHS */
int isRowid /* If true, LHS of IN operator is a rowid */
){
- int testAddr = -1; /* One-time test address */
+ int jmpIfDynamic = -1; /* One-time test address */
int rReg = 0; /* Register storing resulting */
Vdbe *v = sqlite3GetVdbe(pParse);
if( NEVER(v==0) ) return 0;
@@ -1674,14 +1757,14 @@ int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) ){
- testAddr = sqlite3CodeOnce(pParse);
+ if( !ExprHasProperty(pExpr, EP_VarSelect) ){
+ jmpIfDynamic = sqlite3CodeOnce(pParse); VdbeCoverage(v);
}
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
char *zMsg = sqlite3MPrintf(
- pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
+ pParse->db, "EXECUTE %s%s SUBQUERY %d", jmpIfDynamic>=0?"":"CORRELATED ",
pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
@@ -1691,14 +1774,9 @@ int sqlite3CodeSubselect(
switch( pExpr->op ){
case TK_IN: {
char affinity; /* Affinity of the LHS of the IN */
- KeyInfo keyInfo; /* Keyinfo for the generated table */
- static u8 sortOrder = 0; /* Fake aSortOrder for keyInfo */
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
-
- if( rMayHaveNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
- }
+ KeyInfo *pKeyInfo = 0; /* Key information */
affinity = sqlite3ExprAffinity(pLeft);
@@ -1717,10 +1795,7 @@ int sqlite3CodeSubselect(
*/
pExpr->iTable = pParse->nTab++;
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
- if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
- memset(&keyInfo, 0, sizeof(keyInfo));
- keyInfo.nField = 1;
- keyInfo.aSortOrder = &sortOrder;
+ pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, 1, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...)
@@ -1728,6 +1803,7 @@ int sqlite3CodeSubselect(
** Generate code to write the results of the select into the temporary
** table allocated and opened above.
*/
+ Select *pSelect = pExpr->x.pSelect;
SelectDest dest;
ExprList *pEList;
@@ -1735,15 +1811,21 @@ int sqlite3CodeSubselect(
sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
dest.affSdst = (u8)affinity;
assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
- pExpr->x.pSelect->iLimit = 0;
- if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
+ pSelect->iLimit = 0;
+ testcase( pSelect->selFlags & SF_Distinct );
+ pSelect->selFlags &= ~SF_Distinct;
+ testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
+ if( sqlite3Select(pParse, pSelect, &dest) ){
+ sqlite3KeyInfoUnref(pKeyInfo);
return 0;
}
- pEList = pExpr->x.pSelect->pEList;
- if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){
- keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
- pEList->a[0].pExpr);
- }
+ pEList = pSelect->pEList;
+ assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
+ assert( pEList!=0 );
+ assert( pEList->nExpr>0 );
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ pKeyInfo->aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
+ pEList->a[0].pExpr);
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
**
@@ -1760,13 +1842,15 @@ int sqlite3CodeSubselect(
if( !affinity ){
affinity = SQLITE_AFF_NONE;
}
- keyInfo.aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
- keyInfo.aSortOrder = &sortOrder;
+ if( pKeyInfo ){
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ }
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
r2 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
+ if( isRowid ) sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
Expr *pE2 = pItem->pExpr;
int iValToIns;
@@ -1776,9 +1860,9 @@ int sqlite3CodeSubselect(
** 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);
- testAddr = -1;
+ if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
+ sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
+ jmpIfDynamic = -1;
}
/* Evaluate the expression and insert it into the temp table */
@@ -1789,6 +1873,7 @@ int sqlite3CodeSubselect(
if( isRowid ){
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
@@ -1800,8 +1885,8 @@ int sqlite3CodeSubselect(
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
}
- if( !isRowid ){
- sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO);
+ if( pKeyInfo ){
+ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
}
break;
}
@@ -1842,15 +1927,19 @@ int sqlite3CodeSubselect(
return 0;
}
rReg = dest.iSDParm;
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
break;
}
}
- if( testAddr>=0 ){
- sqlite3VdbeJumpHere(v, testAddr);
+ if( rHasNullFlag ){
+ sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag);
}
- sqlite3ExprCachePop(pParse, 1);
+
+ if( jmpIfDynamic>=0 ){
+ sqlite3VdbeJumpHere(v, jmpIfDynamic);
+ }
+ sqlite3ExprCachePop(pParse);
return rReg;
}
@@ -1869,7 +1958,7 @@ int sqlite3CodeSubselect(
** if the LHS is NULL or if the LHS is not contained within the RHS and the
** RHS contains one or more NULL values.
**
-** This routine generates code will jump to destIfFalse if the LHS is not
+** This routine generates code that jumps to destIfFalse if the LHS is not
** contained within the RHS. If due to NULLs we cannot determine if the LHS
** is contained in the RHS then jump to destIfNull. If the LHS is contained
** within the RHS then fall through.
@@ -1892,7 +1981,9 @@ static void sqlite3ExprCodeIN(
v = pParse->pVdbe;
assert( v!=0 ); /* OOM detected prior to this routine */
VdbeNoopComment((v, "begin IN expr"));
- eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull);
+ eType = sqlite3FindInIndex(pParse, pExpr,
+ IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK,
+ destIfFalse==destIfNull ? 0 : &rRhsHasNull);
/* Figure out the affinity to use to create a key from the results
** of the expression. affinityStr stores a static string suitable for
@@ -1906,86 +1997,118 @@ static void sqlite3ExprCodeIN(
r1 = sqlite3GetTempReg(pParse);
sqlite3ExprCode(pParse, pExpr->pLeft, r1);
- /* If the LHS is NULL, then the result is either false or NULL depending
- ** on whether the RHS is empty or not, respectively.
+ /* If sqlite3FindInIndex() did not find or create an index that is
+ ** suitable for evaluating the IN operator, then evaluate using a
+ ** sequence of comparisons.
*/
- if( destIfNull==destIfFalse ){
- /* Shortcut for the common case where the false and NULL outcomes are
- ** the same. */
- sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull);
- }else{
- int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1);
- sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
- sqlite3VdbeJumpHere(v, addr1);
- }
-
- if( eType==IN_INDEX_ROWID ){
- /* In this case, the RHS is the ROWID of table b-tree
- */
- sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse);
- sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1);
+ if( eType==IN_INDEX_NOOP ){
+ ExprList *pList = pExpr->x.pList;
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ int labelOk = sqlite3VdbeMakeLabel(v);
+ int r2, regToFree;
+ int regCkNull = 0;
+ int ii;
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ if( destIfNull!=destIfFalse ){
+ regCkNull = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, regCkNull);
+ }
+ for(ii=0; ii<pList->nExpr; ii++){
+ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
+ sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
+ }
+ if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
+ sqlite3VdbeAddOp4(v, OP_Eq, r1, labelOk, r2,
+ (void*)pColl, P4_COLLSEQ);
+ VdbeCoverageIf(v, ii<pList->nExpr-1);
+ VdbeCoverageIf(v, ii==pList->nExpr-1);
+ sqlite3VdbeChangeP5(v, affinity);
+ }else{
+ assert( destIfNull==destIfFalse );
+ sqlite3VdbeAddOp4(v, OP_Ne, r1, destIfFalse, r2,
+ (void*)pColl, P4_COLLSEQ); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, affinity | SQLITE_JUMPIFNULL);
+ }
+ sqlite3ReleaseTempReg(pParse, regToFree);
+ }
+ if( regCkNull ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
+ }
+ sqlite3VdbeResolveLabel(v, labelOk);
+ sqlite3ReleaseTempReg(pParse, regCkNull);
}else{
- /* In this case, the RHS is an index b-tree.
- */
- sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1);
-
- /* 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 the LHS is NULL, then the result is either false or NULL depending
+ ** on whether the RHS is empty or not, respectively.
*/
- if( rRhsHasNull==0 || destIfFalse==destIfNull ){
- /* This branch runs if it is known at compile time that the RHS
- ** cannot contain NULL values. This happens as the result
- ** of a "NOT NULL" constraint in the database schema.
- **
- ** Also run this branch if NULL is equivalent to FALSE
- ** for this particular IN operator.
+ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
+ if( destIfNull==destIfFalse ){
+ /* Shortcut for the common case where the false and NULL outcomes are
+ ** the same. */
+ sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); VdbeCoverage(v);
+ }else{
+ int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
+ sqlite3VdbeJumpHere(v, addr1);
+ }
+ }
+
+ if( eType==IN_INDEX_ROWID ){
+ /* In this case, the RHS is the ROWID of table b-tree
*/
- sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1);
-
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1);
+ VdbeCoverage(v);
}else{
- /* In this branch, the RHS of the IN might contain a NULL and
- ** the presence of a NULL on the RHS makes a difference in the
- ** outcome.
- */
- int j1, j2, j3;
-
- /* First check to see if the LHS is contained in the RHS. If so,
- ** then the presence of NULLs in the RHS does not matter, so jump
- ** over all of the code that follows.
- */
- j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
-
- /* Here we begin generating code that runs if the LHS is not
- ** contained within the RHS. Generate additional code that
- ** tests the RHS for NULLs. If the RHS contains a NULL then
- ** jump to destIfNull. If there are no NULLs in the RHS then
- ** jump to destIfFalse.
+ /* In this case, the RHS is an index b-tree.
*/
- j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull);
- j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull);
- sqlite3VdbeJumpHere(v, j3);
- sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1);
- sqlite3VdbeJumpHere(v, j2);
-
- /* Jump to the appropriate target depending on whether or not
- ** the RHS contains a NULL
- */
- sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
-
- /* The OP_Found at the top of this branch jumps here when true,
- ** causing the overall IN expression evaluation to fall through.
+ sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1);
+
+ /* 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.
*/
- sqlite3VdbeJumpHere(v, j1);
+ assert( destIfFalse!=destIfNull || rRhsHasNull==0 );
+ if( rRhsHasNull==0 ){
+ /* This branch runs if it is known at compile time that the RHS
+ ** cannot contain NULL values. This happens as the result
+ ** of a "NOT NULL" constraint in the database schema.
+ **
+ ** Also run this branch if NULL is equivalent to FALSE
+ ** for this particular IN operator.
+ */
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1);
+ VdbeCoverage(v);
+ }else{
+ /* In this branch, the RHS of the IN might contain a NULL and
+ ** the presence of a NULL on the RHS makes a difference in the
+ ** outcome.
+ */
+ int j1;
+
+ /* First check to see if the LHS is contained in the RHS. If so,
+ ** then the answer is TRUE the presence of NULLs in the RHS does
+ ** not matter. If the LHS is not contained in the RHS, then the
+ ** answer is NULL if the RHS contains NULLs and the answer is
+ ** FALSE if the RHS is NULL-free.
+ */
+ j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_IsNull, rRhsHasNull, destIfNull);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
+ sqlite3VdbeJumpHere(v, j1);
+ }
}
}
sqlite3ReleaseTempReg(pParse, r1);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
VdbeComment((v, "end IN expr"));
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -2042,7 +2165,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
i64 value;
const char *z = pExpr->u.zToken;
assert( z!=0 );
- c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
+ c = sqlite3DecOrHexToI64(z, &value);
if( c==0 || (c==2 && negFlag) ){
char *zV;
if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
@@ -2052,7 +2175,14 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
#ifdef SQLITE_OMIT_FLOATING_POINT
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
#else
- codeReal(v, z, negFlag, iMem);
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( sqlite3_strnicmp(z,"0x",2)==0 ){
+ sqlite3ErrorMsg(pParse, "hex literal too big: %s", z);
+ }else
+#endif
+ {
+ codeReal(v, z, negFlag, iMem);
+ }
#endif
}
}
@@ -2159,19 +2289,28 @@ void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
*/
void sqlite3ExprCachePush(Parse *pParse){
pParse->iCacheLevel++;
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("PUSH to %d\n", pParse->iCacheLevel);
+ }
+#endif
}
/*
** Remove from the column cache any entries that were added since the
-** the previous N Push operations. In other words, restore the cache
-** to the state it was in N Pushes ago.
+** the previous sqlite3ExprCachePush operation. In other words, restore
+** the cache to the state it was in prior the most recent Push.
*/
-void sqlite3ExprCachePop(Parse *pParse, int N){
+void sqlite3ExprCachePop(Parse *pParse){
int i;
struct yColCache *p;
- assert( N>0 );
- assert( pParse->iCacheLevel>=N );
- pParse->iCacheLevel -= N;
+ assert( pParse->iCacheLevel>=1 );
+ pParse->iCacheLevel--;
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("POP to %d\n", pParse->iCacheLevel);
+ }
+#endif
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
if( p->iReg && p->iLevel>pParse->iCacheLevel ){
cacheEntryClear(pParse, p);
@@ -2202,15 +2341,19 @@ static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
void sqlite3ExprCodeGetColumnOfTable(
Vdbe *v, /* The VDBE under construction */
Table *pTab, /* The table containing the value */
- int iTabCur, /* The cursor for this table */
+ int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
int iCol, /* Index of the column to extract */
- int regOut /* Extract the valud into this register */
+ int regOut /* Extract the value into this register */
){
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
}else{
int op = IsVirtual(pTab) ? OP_VColumn : OP_Column;
- sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut);
+ int x = iCol;
+ if( !HasRowid(pTab) ){
+ x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol);
+ }
+ sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut);
}
if( iCol>=0 ){
sqlite3ColumnDefault(v, pTab, iCol, regOut);
@@ -2262,6 +2405,11 @@ void sqlite3ExprCacheClear(Parse *pParse){
int i;
struct yColCache *p;
+#if SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("CLEAR\n");
+ }
+#endif
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
if( p->iReg ){
cacheEntryClear(pParse, p);
@@ -2286,7 +2434,7 @@ void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
int i;
struct yColCache *p;
assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo );
- sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg-1);
+ sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
int x = p->iReg;
if( x>=iFrom && x<iFrom+nReg ){
@@ -2315,6 +2463,16 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
/*
+** Convert an expression node to a TK_REGISTER
+*/
+static void exprToRegister(Expr *p, int iReg){
+ p->op2 = p->op;
+ p->op = TK_REGISTER;
+ p->iTable = iReg;
+ ExprClearProperty(p, EP_Skip);
+}
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -2333,6 +2491,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
int regFree2 = 0; /* If non-zero free this temporary register */
int r1, r2, r3, r4; /* Various register numbers */
sqlite3 *db = pParse->db; /* The database connection */
+ Expr tempX; /* Temporary expression node */
assert( target>0 && target<=pParse->nMem );
if( v==0 ){
@@ -2361,15 +2520,20 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
/* Otherwise, fall thru into the TK_COLUMN case */
}
case TK_COLUMN: {
- if( pExpr->iTable<0 ){
- /* This only happens when coding check constraints */
- assert( pParse->ckBase>0 );
- inReg = pExpr->iColumn + pParse->ckBase;
- }else{
- inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
- pExpr->iColumn, pExpr->iTable, target,
- pExpr->op2);
+ int iTab = pExpr->iTable;
+ if( iTab<0 ){
+ if( pParse->ckBase>0 ){
+ /* Generating CHECK constraints or inserting into partial index */
+ inReg = pExpr->iColumn + pParse->ckBase;
+ break;
+ }else{
+ /* Deleting from a partial index */
+ iTab = pParse->iPartIdxTab;
+ }
}
+ inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ pExpr->iColumn, iTab, target,
+ pExpr->op2);
break;
}
case TK_INTEGER: {
@@ -2434,7 +2598,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
int aff, to_op;
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- aff = sqlite3AffinityType(pExpr->u.zToken);
+ aff = sqlite3AffinityType(pExpr->u.zToken, 0);
to_op = aff - SQLITE_AFF_TEXT + OP_ToText;
assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT );
assert( to_op==OP_ToBlob || aff!=SQLITE_AFF_NONE );
@@ -2462,22 +2626,16 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
case TK_GE:
case TK_NE:
case TK_EQ: {
- assert( TK_LT==OP_Lt );
- assert( TK_LE==OP_Le );
- assert( TK_GT==OP_Gt );
- assert( TK_GE==OP_Ge );
- assert( TK_EQ==OP_Eq );
- assert( TK_NE==OP_Ne );
- testcase( op==TK_LT );
- testcase( op==TK_LE );
- testcase( op==TK_GT );
- testcase( op==TK_GE );
- testcase( op==TK_EQ );
- testcase( op==TK_NE );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, inReg, SQLITE_STOREP2);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -2491,6 +2649,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
op = (op==TK_IS) ? TK_EQ : TK_NE;
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==TK_EQ);
+ VdbeCoverageIf(v, op==TK_NE);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -2507,28 +2667,17 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
case TK_LSHIFT:
case TK_RSHIFT:
case TK_CONCAT: {
- assert( TK_AND==OP_And );
- assert( TK_OR==OP_Or );
- assert( TK_PLUS==OP_Add );
- assert( TK_MINUS==OP_Subtract );
- assert( TK_REM==OP_Remainder );
- assert( TK_BITAND==OP_BitAnd );
- assert( TK_BITOR==OP_BitOr );
- assert( TK_SLASH==OP_Divide );
- assert( TK_LSHIFT==OP_ShiftLeft );
- assert( TK_RSHIFT==OP_ShiftRight );
- assert( TK_CONCAT==OP_Concat );
- 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 );
+ assert( TK_AND==OP_And ); testcase( op==TK_AND );
+ assert( TK_OR==OP_Or ); testcase( op==TK_OR );
+ assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
+ assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
+ assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
+ assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
+ assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
+ assert( TK_SLASH==OP_Divide ); testcase( op==TK_SLASH );
+ assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT );
+ assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT );
+ assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
sqlite3VdbeAddOp3(v, op, r2, r1, target);
@@ -2547,8 +2696,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
codeReal(v, pLeft->u.zToken, 1, target);
#endif
}else{
- regFree1 = r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, r1);
+ tempX.op = TK_INTEGER;
+ tempX.flags = EP_IntValue|EP_TokenOnly;
+ tempX.u.iValue = 0;
+ r1 = sqlite3ExprCodeTemp(pParse, &tempX, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree2);
sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
testcase( regFree2==0 );
@@ -2558,10 +2709,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
case TK_BITNOT:
case TK_NOT: {
- assert( TK_BITNOT==OP_BitNot );
- assert( TK_NOT==OP_Not );
- testcase( op==TK_BITNOT );
- testcase( op==TK_NOT );
+ assert( TK_BITNOT==OP_BitNot ); testcase( op==TK_BITNOT );
+ assert( TK_NOT==OP_Not ); testcase( op==TK_NOT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
testcase( regFree1==0 );
inReg = target;
@@ -2571,15 +2720,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
case TK_ISNULL:
case TK_NOTNULL: {
int addr;
- assert( TK_ISNULL==OP_IsNull );
- assert( TK_NOTNULL==OP_NotNull );
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
+ assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
+ assert( TK_NOTNULL==OP_NotNull ); 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);
+ VdbeCoverageIf(v, op==TK_ISNULL);
+ VdbeCoverageIf(v, op==TK_NOTNULL);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
sqlite3VdbeJumpHere(v, addr);
break;
}
@@ -2593,22 +2742,19 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
break;
}
- case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pFarg; /* List of function arguments */
int nFarg; /* Number of function arguments */
FuncDef *pDef; /* The function definition object */
int nId; /* Length of the function name in bytes */
const char *zId; /* The function name */
- int constMask = 0; /* Mask of function arguments that are constant */
+ u32 constMask = 0; /* Mask of function arguments that are constant */
int i; /* Loop counter */
u8 enc = ENC(db); /* The text encoding used by this database */
CollSeq *pColl = 0; /* A collating sequence */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- testcase( op==TK_CONST_FUNC );
- testcase( op==TK_FUNCTION );
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
pFarg = 0;
}else{
pFarg = pExpr->x.pList;
@@ -2618,7 +2764,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0);
- if( pDef==0 ){
+ if( pDef==0 || pDef->xFunc==0 ){
sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId);
break;
}
@@ -2627,31 +2773,54 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
** IFNULL() functions. This avoids unnecessary evalation of
** arguments past the first non-NULL argument.
*/
- if( pDef->flags & SQLITE_FUNC_COALESCE ){
+ if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){
int endCoalesce = sqlite3VdbeMakeLabel(v);
assert( nFarg>=2 );
sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
for(i=1; i<nFarg; i++){
sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce);
+ VdbeCoverage(v);
sqlite3ExprCacheRemove(pParse, target, 1);
sqlite3ExprCachePush(pParse);
sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
}
sqlite3VdbeResolveLabel(v, endCoalesce);
break;
}
+ /* The UNLIKELY() function is a no-op. The result is the value
+ ** of the first argument.
+ */
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ assert( nFarg>=1 );
+ sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
+ break;
+ }
+ for(i=0; i<nFarg; i++){
+ if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
+ testcase( i==31 );
+ constMask |= MASKBIT32(i);
+ }
+ if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
+ pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
+ }
+ }
if( pFarg ){
- r1 = sqlite3GetTempRange(pParse, nFarg);
+ if( constMask ){
+ r1 = pParse->nMem+1;
+ pParse->nMem += nFarg;
+ }else{
+ r1 = sqlite3GetTempRange(pParse, nFarg);
+ }
/* For length() and typeof() functions with a column argument,
** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
** loading.
*/
- if( (pDef->flags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
+ if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
u8 exprOp;
assert( nFarg==1 );
assert( pFarg->a[0].pExpr!=0 );
@@ -2659,14 +2828,16 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
- testcase( pDef->flags==SQLITE_FUNC_LENGTH );
- pFarg->a[0].pExpr->op2 = pDef->flags;
+ testcase( pDef->funcFlags & OPFLAG_LENGTHARG );
+ pFarg->a[0].pExpr->op2 =
+ pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG);
}
}
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
- sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
- sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
+ sqlite3ExprCodeExprList(pParse, pFarg, r1,
+ SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
+ sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */
}else{
r1 = 0;
}
@@ -2689,22 +2860,14 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr);
}
#endif
- for(i=0; i<nFarg; i++){
- if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
- constMask |= (1<<i);
- }
- if( (pDef->flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
- pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
- }
- }
- if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
(char*)pDef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nFarg);
- if( nFarg ){
+ if( nFarg && constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
}
break;
@@ -2754,13 +2917,14 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
r3 = sqlite3GetTempReg(pParse);
r4 = sqlite3GetTempReg(pParse);
codeCompare(pParse, pLeft, pRight, OP_Ge,
- r1, r2, r3, SQLITE_STOREP2);
+ r1, r2, r3, SQLITE_STOREP2); VdbeCoverage(v);
pLItem++;
pRight = pLItem->pExpr;
sqlite3ReleaseTempReg(pParse, regFree2);
r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
testcase( regFree2==0 );
codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2);
+ VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
sqlite3ReleaseTempReg(pParse, r3);
sqlite3ReleaseTempReg(pParse, r4);
@@ -2838,9 +3002,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
** 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.
+ ** Y is in the last element of pExpr->x.pList if pExpr->x.pList->nExpr is
+ ** odd. The Y is also optional. If the number of elements in x.pList
+ ** is even, then Y is omitted and the "otherwise" result 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,
@@ -2855,27 +3019,23 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
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 = 0; /* X==Ei (form A) or just Ei (form B) */
VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; )
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
- assert((pExpr->x.pList->nExpr % 2) == 0);
assert(pExpr->x.pList->nExpr > 0);
pEList = pExpr->x.pList;
aListelem = pEList->a;
nExpr = pEList->nExpr;
endLabel = sqlite3VdbeMakeLabel(v);
if( (pX = pExpr->pLeft)!=0 ){
- cacheX = *pX;
+ tempX = *pX;
testcase( pX->op==TK_COLUMN );
- testcase( pX->op==TK_REGISTER );
- cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, &regFree1);
+ exprToRegister(&tempX, sqlite3ExprCodeTemp(pParse, pX, &regFree1));
testcase( regFree1==0 );
- cacheX.op = TK_REGISTER;
opCompare.op = TK_EQ;
- opCompare.pLeft = &cacheX;
+ opCompare.pLeft = &tempX;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
@@ -2883,7 +3043,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
** purposes and possibly overwritten. */
regFree1 = 0;
}
- for(i=0; i<nExpr; i=i+2){
+ for(i=0; i<nExpr-1; i=i+2){
sqlite3ExprCachePush(pParse);
if( pX ){
assert( pTest!=0 );
@@ -2895,16 +3055,15 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
testcase( pTest->op==TK_COLUMN );
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);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
sqlite3VdbeResolveLabel(v, nextCase);
}
- if( pExpr->pRight ){
+ if( (nExpr&1)!=0 ){
sqlite3ExprCachePush(pParse);
- sqlite3ExprCode(pParse, pExpr->pRight, target);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCode(pParse, pEList->a[nExpr-1].pExpr, target);
+ sqlite3ExprCachePop(pParse);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
@@ -2932,9 +3091,10 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
if( pExpr->affinity==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
+ VdbeCoverage(v);
}else{
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
- pExpr->affinity, pExpr->u.zToken, 0);
+ pExpr->affinity, pExpr->u.zToken, 0, 0);
}
break;
@@ -2947,6 +3107,28 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
}
/*
+** Factor out the code of the given expression to initialization time.
+*/
+void sqlite3ExprCodeAtInit(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The expression to code when the VDBE initializes */
+ int regDest, /* Store the value in this register */
+ u8 reusable /* True if this expression is reusable */
+){
+ ExprList *p;
+ assert( ConstFactorOk(pParse) );
+ p = pParse->pConstExpr;
+ pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
+ p = sqlite3ExprListAppend(pParse, p, pExpr);
+ if( p ){
+ struct ExprList_item *pItem = &p->a[p->nExpr-1];
+ pItem->u.iConstExprReg = regDest;
+ pItem->reusable = reusable;
+ }
+ pParse->pConstExpr = p;
+}
+
+/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
@@ -2954,15 +3136,40 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
** 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.
+**
+** If pExpr is a constant, then this routine might generate this
+** code to fill the register in the initialization section of the
+** VDBE program, in order to factor it out of the evaluation loop.
*/
int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
- int r1 = sqlite3GetTempReg(pParse);
- int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
- if( r2==r1 ){
- *pReg = r1;
+ int r2;
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+ if( ConstFactorOk(pParse)
+ && pExpr->op!=TK_REGISTER
+ && sqlite3ExprIsConstantNotJoin(pExpr)
+ ){
+ ExprList *p = pParse->pConstExpr;
+ int i;
+ *pReg = 0;
+ if( p ){
+ struct ExprList_item *pItem;
+ for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){
+ if( pItem->reusable && sqlite3ExprCompare(pItem->pExpr,pExpr,-1)==0 ){
+ return pItem->u.iConstExprReg;
+ }
+ }
+ }
+ r2 = ++pParse->nMem;
+ sqlite3ExprCodeAtInit(pParse, pExpr, r2, 1);
}else{
- sqlite3ReleaseTempReg(pParse, r1);
- *pReg = 0;
+ int r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
+ if( r2==r1 ){
+ *pReg = r1;
+ }else{
+ sqlite3ReleaseTempReg(pParse, r1);
+ *pReg = 0;
+ }
}
return r2;
}
@@ -2972,7 +3179,7 @@ int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
** results in register target. The results are guaranteed to appear
** in register target.
*/
-int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
+void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
int inReg;
assert( target>0 && target<=pParse->nMem );
@@ -2985,7 +3192,20 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
}
}
- return target;
+}
+
+/*
+** Generate code that will evaluate expression pExpr and store the
+** results in register target. The results are guaranteed to appear
+** in register target. If the expression is constant, then this routine
+** might choose to code the expression at initialization time.
+*/
+void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
+ if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){
+ sqlite3ExprCodeAtInit(pParse, pExpr, target, 0);
+ }else{
+ sqlite3ExprCode(pParse, pExpr, target);
+ }
}
/*
@@ -3000,26 +3220,16 @@ int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
** times. They are evaluated once and the results of the expression
** are reused.
*/
-int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
+void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
Vdbe *v = pParse->pVdbe;
- int inReg;
- inReg = sqlite3ExprCode(pParse, pExpr, target);
+ int iMem;
+
assert( target>0 );
- /* This routine is called for terms to INSERT or UPDATE. And the only
- ** other place where expressions can be converted into TK_REGISTER is
- ** in WHERE clause processing. So as currently implemented, there is
- ** no way for a TK_REGISTER to exist here. But it seems prudent to
- ** keep the ALWAYS() in case the conditions above change with future
- ** modifications or enhancements. */
- if( ALWAYS(pExpr->op!=TK_REGISTER) ){
- int iMem;
- iMem = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
- pExpr->iTable = iMem;
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
- }
- return inReg;
+ assert( pExpr->op!=TK_REGISTER );
+ sqlite3ExprCode(pParse, pExpr, target);
+ iMem = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Copy, target, iMem);
+ exprToRegister(pExpr, iMem);
}
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
@@ -3096,7 +3306,7 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
const char *zAff = "unk";
- switch( sqlite3AffinityType(pExpr->u.zToken) ){
+ switch( sqlite3AffinityType(pExpr->u.zToken, 0) ){
case SQLITE_AFF_TEXT: zAff = "TEXT"; break;
case SQLITE_AFF_NONE: zAff = "NONE"; break;
case SQLITE_AFF_NUMERIC: zAff = "NUMERIC"; break;
@@ -3144,10 +3354,9 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
}
case TK_AGG_FUNCTION:
- case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pFarg; /* List of function arguments */
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
pFarg = 0;
}else{
pFarg = pExpr->x.pList;
@@ -3296,165 +3505,50 @@ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
#endif /* SQLITE_DEBUG */
/*
-** 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(Walker *pWalker, Expr *pExpr){
- Parse *pParse = pWalker->pParse;
- switch( pExpr->op ){
- case TK_IN:
- case TK_REGISTER: {
- return WRC_Prune;
- }
- case TK_COLLATE: {
- return WRC_Continue;
- }
- 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->x.pList;
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- if( pList ){
- int i = pList->nExpr;
- struct ExprList_item *pItem = pList->a;
- for(; i>0; i--, pItem++){
- if( ALWAYS(pItem->pExpr) ) pItem->pExpr->flags |= EP_FixedDest;
- }
- }
- break;
- }
- }
- if( isAppropriateForFactoring(pExpr) ){
- int r1 = ++pParse->nMem;
- int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
- /* If r2!=r1, it means that register r1 is never used. That is harmless
- ** but suboptimal, so we want to know about the situation to fix it.
- ** Hence the following assert: */
- assert( r2==r1 );
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
- pExpr->iTable = r2;
- return WRC_Prune;
- }
- return WRC_Continue;
-}
-
-/*
-** 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.
-**
-** This routine is a no-op if the jump to the cookie-check code has
-** already occur. Since the cookie-check jump is generated prior to
-** any other serious processing, this check ensures that there is no
-** way to accidently bypass the constant initializations.
-**
-** This routine is also a no-op if the SQLITE_FactorOutConst optimization
-** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)
-** interface. This allows test logic to verify that the same answer is
-** obtained for queries regardless of whether or not constants are
-** precomputed into registers or if they are inserted in-line.
-*/
-void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
- Walker w;
- if( pParse->cookieGoto ) return;
- if( OptimizationDisabled(pParse->db, SQLITE_FactorOutConst) ) return;
- memset(&w, 0, sizeof(w));
- w.xExprCallback = evalConstExpr;
- w.pParse = pParse;
- sqlite3WalkExpr(&w, pExpr);
-}
-
-
-/*
** Generate code that pushes the value of every element of the given
** expression list into a sequence of registers beginning at target.
**
** Return the number of elements evaluated.
+**
+** The SQLITE_ECEL_DUP flag prevents the arguments from being
+** filled using OP_SCopy. OP_Copy must be used instead.
+**
+** The SQLITE_ECEL_FACTOR argument allows constant arguments to be
+** factored out into initialization code.
*/
int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */
ExprList *pList, /* The expression list to be coded */
int target, /* Where to write results */
- int doHardCopy /* Make a hard copy of every element */
+ u8 flags /* SQLITE_ECEL_* flags */
){
struct ExprList_item *pItem;
int i, n;
+ u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy;
assert( pList!=0 );
assert( target>0 );
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
n = pList->nExpr;
+ if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
- int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
- if( inReg!=target+i ){
- sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy,
- inReg, target+i);
+ if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
+ sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0);
+ }else{
+ int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
+ if( inReg!=target+i ){
+ VdbeOp *pOp;
+ Vdbe *v = pParse->pVdbe;
+ if( copyOp==OP_Copy
+ && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
+ && pOp->p1+pOp->p3+1==inReg
+ && pOp->p2+pOp->p3+1==target+i
+ ){
+ pOp->p3++;
+ }else{
+ sqlite3VdbeAddOp2(v, copyOp, inReg, target+i);
+ }
+ }
}
}
return n;
@@ -3496,8 +3590,7 @@ static void exprCodeBetween(
compRight.op = TK_LE;
compRight.pLeft = &exprX;
compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
- exprX.op = TK_REGISTER;
+ exprToRegister(&exprX, sqlite3ExprCodeTemp(pParse, &exprX, &regFree1));
if( jumpIfTrue ){
sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
}else{
@@ -3545,17 +3638,19 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_AND: {
int d2 = sqlite3VdbeMakeLabel(v);
testcase( jumpIfNull==0 );
- sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_OR: {
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_NOT: {
@@ -3569,23 +3664,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_GE:
case TK_NE:
case TK_EQ: {
- assert( TK_LT==OP_Lt );
- assert( TK_LE==OP_Le );
- assert( TK_GT==OP_Gt );
- assert( TK_GE==OP_Ge );
- assert( TK_EQ==OP_Eq );
- assert( TK_NE==OP_Ne );
- 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 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -3599,18 +3688,20 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
op = (op==TK_IS) ? TK_EQ : TK_NE;
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==TK_EQ);
+ VdbeCoverageIf(v, op==TK_NE);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- assert( TK_ISNULL==OP_IsNull );
- assert( TK_NOTNULL==OP_NotNull );
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
+ assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
+ assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
sqlite3VdbeAddOp2(v, op, r1, dest);
+ VdbeCoverageIf(v, op==TK_ISNULL);
+ VdbeCoverageIf(v, op==TK_NOTNULL);
testcase( regFree1==0 );
break;
}
@@ -3630,10 +3721,17 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
}
#endif
default: {
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
- sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
- testcase( regFree1==0 );
- testcase( jumpIfNull==0 );
+ if( exprAlwaysTrue(pExpr) ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ }else if( exprAlwaysFalse(pExpr) ){
+ /* No-op */
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
+ VdbeCoverage(v);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
+ }
break;
}
}
@@ -3696,17 +3794,19 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_AND: {
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_OR: {
int d2 = sqlite3VdbeMakeLabel(v);
testcase( jumpIfNull==0 );
- sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_NOT: {
@@ -3720,17 +3820,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_GE:
case TK_NE:
case TK_EQ: {
- 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 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
@@ -3744,16 +3844,18 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==TK_EQ);
+ VdbeCoverageIf(v, op==TK_NE);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
sqlite3VdbeAddOp2(v, op, r1, dest);
+ testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
+ testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
testcase( regFree1==0 );
break;
}
@@ -3775,10 +3877,17 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
}
#endif
default: {
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
- sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
- testcase( regFree1==0 );
- testcase( jumpIfNull==0 );
+ if( exprAlwaysFalse(pExpr) ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ }else if( exprAlwaysTrue(pExpr) ){
+ /* no-op */
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
+ VdbeCoverage(v);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
+ }
break;
}
}
@@ -3792,6 +3901,12 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
** by a COLLATE operator at the top level. Return 2 if there are differences
** other than the top-level COLLATE operator.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
+** The pA side might be using TK_REGISTER. If that is the case and pB is
+** not using TK_REGISTER but is otherwise equivalent, then still return 0.
+**
** Sometimes this routine will return 2 even if the two expressions
** really are equivalent. If we cannot prove that the expressions are
** identical, we return 2 just to be safe. So if this routine
@@ -3802,39 +3917,44 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
*/
-int sqlite3ExprCompare(Expr *pA, Expr *pB){
- if( pA==0||pB==0 ){
+int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){
+ u32 combinedFlags;
+ if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
- assert( !ExprHasAnyProperty(pA, EP_TokenOnly|EP_Reduced) );
- assert( !ExprHasAnyProperty(pB, EP_TokenOnly|EP_Reduced) );
- if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
+ combinedFlags = pA->flags | pB->flags;
+ if( combinedFlags & EP_IntValue ){
+ if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
+ return 0;
+ }
return 2;
}
- if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
if( pA->op!=pB->op ){
- if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB)<2 ){
+ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB, iTab)<2 ){
return 1;
}
- if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft)<2 ){
+ if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft, iTab)<2 ){
return 1;
}
return 2;
}
- if( sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 2;
- if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2;
- if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2;
- if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2;
- if( ExprHasProperty(pA, EP_IntValue) ){
- if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){
- return 2;
- }
- }else if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken){
- if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2;
+ if( pA->op!=TK_COLUMN && ALWAYS(pA->op!=TK_AGG_COLUMN) && pA->u.zToken ){
if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
return pA->op==TK_COLLATE ? 1 : 2;
}
}
+ if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
+ if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
+ if( combinedFlags & EP_xIsSelect ) return 2;
+ if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2;
+ if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2;
+ if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
+ if( ALWAYS((combinedFlags & EP_Reduced)==0) ){
+ if( pA->iColumn!=pB->iColumn ) return 2;
+ if( pA->iTable!=pB->iTable
+ && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
+ }
+ }
return 0;
}
@@ -3842,6 +3962,9 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Compare two ExprList objects. Return 0 if they are identical and
** non-zero if they differ in any way.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
** This routine might return non-zero for equivalent ExprLists. The
** only consequence will be disabled optimizations. But this routine
** must never return 0 if the two ExprList objects are different, or
@@ -3850,7 +3973,7 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Two NULL pointers are considered to be the same. But a NULL pointer
** always differs from a non-NULL pointer.
*/
-int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
+int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
int i;
if( pA==0 && pB==0 ) return 0;
if( pA==0 || pB==0 ) return 1;
@@ -3859,7 +3982,46 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
- if( sqlite3ExprCompare(pExprA, pExprB) ) return 1;
+ if( sqlite3ExprCompare(pExprA, pExprB, iTab) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Return true if we can prove the pE2 will always be true if pE1 is
+** true. Return false if we cannot complete the proof or if pE2 might
+** be false. Examples:
+**
+** pE1: x==5 pE2: x==5 Result: true
+** pE1: x>0 pE2: x==5 Result: false
+** pE1: x=21 pE2: x=21 OR y=43 Result: true
+** pE1: x!=123 pE2: x IS NOT NULL Result: true
+** pE1: x!=?1 pE2: x IS NOT NULL Result: true
+** pE1: x IS NULL pE2: x IS NOT NULL Result: false
+** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false
+**
+** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
+** Expr.iTable<0 then assume a table number given by iTab.
+**
+** When in doubt, return false. Returning true might give a performance
+** improvement. Returning false might cause a performance reduction, but
+** it will always give the correct answer and is hence always safe.
+*/
+int sqlite3ExprImpliesExpr(Expr *pE1, Expr *pE2, int iTab){
+ if( sqlite3ExprCompare(pE1, pE2, iTab)==0 ){
+ return 1;
+ }
+ if( pE2->op==TK_OR
+ && (sqlite3ExprImpliesExpr(pE1, pE2->pLeft, iTab)
+ || sqlite3ExprImpliesExpr(pE1, pE2->pRight, iTab) )
+ ){
+ return 1;
+ }
+ if( pE2->op==TK_NOTNULL
+ && sqlite3ExprCompare(pE1->pLeft, pE2->pLeft, iTab)==0
+ && (pE1->op!=TK_ISNULL && pE1->op!=TK_IS)
+ ){
+ return 1;
}
return 0;
}
@@ -3976,7 +4138,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
struct SrcList_item *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
struct AggInfo_col *pCol;
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
/* If we reach this point, it means that pExpr refers to a table
** that is in the FROM clause of the aggregate query.
@@ -4025,7 +4187,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
** Convert the pExpr to be a TK_AGG_COLUMN referring to that
** pAggInfo->aCol[] entry.
*/
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->pAggInfo = pAggInfo;
pExpr->op = TK_AGG_COLUMN;
pExpr->iAgg = (i16)k;
@@ -4044,7 +4206,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( sqlite3ExprCompare(pItem->pExpr, pExpr)==0 ){
+ if( sqlite3ExprCompare(pItem->pExpr, pExpr, -1)==0 ){
break;
}
}
@@ -4071,8 +4233,8 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
}
/* Make pExpr point to the appropriate pAggInfo->aFunc[] entry
*/
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pExpr);
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->iAgg = (i16)i;
pExpr->pAggInfo = pAggInfo;
return WRC_Prune;
diff --git a/src/fkey.c b/src/fkey.c
index ac35bc1..50c10da 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -225,7 +225,7 @@ int sqlite3FkLocateIndex(
}
for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){
+ if( pIdx->nKeyCol==nCol && IsUniqueIndex(pIdx) ){
/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
** of columns. If each indexed column corresponds to a foreign key
** column of pFKey, then this index is a winner. */
@@ -233,8 +233,8 @@ int sqlite3FkLocateIndex(
if( zKey==0 ){
/* If zKey is NULL, then this foreign key is implicitly mapped to
** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
- ** identified by the test (Index.autoIndex==2). */
- if( pIdx->autoIndex==2 ){
+ ** identified by the test. */
+ if( IsPrimaryKeyIndex(pIdx) ){
if( aiCol ){
int i;
for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
@@ -248,7 +248,7 @@ int sqlite3FkLocateIndex(
** the default collation sequences for each column. */
int i, j;
for(i=0; i<nCol; i++){
- int iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
+ i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
char *zDfltColl; /* Def. collation for column */
char *zIdxCol; /* Name of indexed column */
@@ -340,10 +340,11 @@ static void fkLookupParent(
** search for a matching row in the parent table. */
if( nIncr<0 ){
sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
+ VdbeCoverage(v);
}
for(i=0; i<pFKey->nCol; i++){
int iReg = aiCol[i] + regData + 1;
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk);
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v);
}
if( isIgnore==0 ){
@@ -360,17 +361,19 @@ static void fkLookupParent(
** will have INTEGER affinity applied to it, which may not be correct. */
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
+ VdbeCoverage(v);
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
** increment the constraint-counter. */
if( pTab==pFKey->pFrom && nIncr==1 ){
- sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
+ sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
}
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
sqlite3VdbeJumpHere(v, iMustBeInt);
@@ -379,10 +382,9 @@ static void fkLookupParent(
int nCol = pFKey->nCol;
int regTemp = sqlite3GetTempRange(pParse, nCol);
int regRec = sqlite3GetTempReg(pParse);
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
- sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
}
@@ -407,30 +409,32 @@ static void fkLookupParent(
/* The parent key is a composite key that includes the IPK column */
iParent = regData;
}
- sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
+ sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
- sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
- sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
+ sqlite3IndexAffinityStr(v,pIdx), nCol);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempRange(pParse, regTemp, nCol);
}
}
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel
+ && !pParse->isMultiWrite
+ ){
/* Special case: If this is an INSERT statement that will insert exactly
** one row into the table, raise a constraint immediately instead of
** incrementing a counter. This is necessary as the VM code is being
** generated for will not open a statement transaction. */
assert( nIncr==1 );
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
- OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
}else{
if( nIncr>0 && pFKey->isDeferred==0 ){
sqlite3ParseToplevel(pParse)->mayAbort = 1;
@@ -442,6 +446,62 @@ static void fkLookupParent(
sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
+
+/*
+** Return an Expr object that refers to a memory register corresponding
+** to column iCol of table pTab.
+**
+** regBase is the first of an array of register that contains the data
+** for pTab. regBase itself holds the rowid. regBase+1 holds the first
+** column. regBase+2 holds the second column, and so forth.
+*/
+static Expr *exprTableRegister(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* The table whose content is at r[regBase]... */
+ int regBase, /* Contents of table pTab */
+ i16 iCol /* Which column of pTab is desired */
+){
+ Expr *pExpr;
+ Column *pCol;
+ const char *zColl;
+ sqlite3 *db = pParse->db;
+
+ pExpr = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pExpr ){
+ if( iCol>=0 && iCol!=pTab->iPKey ){
+ pCol = &pTab->aCol[iCol];
+ pExpr->iTable = regBase + iCol + 1;
+ pExpr->affinity = pCol->affinity;
+ zColl = pCol->zColl;
+ if( zColl==0 ) zColl = db->pDfltColl->zName;
+ pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
+ }else{
+ pExpr->iTable = regBase;
+ pExpr->affinity = SQLITE_AFF_INTEGER;
+ }
+ }
+ return pExpr;
+}
+
+/*
+** Return an Expr object that refers to column iCol of table pTab which
+** has cursor iCur.
+*/
+static Expr *exprTableColumn(
+ sqlite3 *db, /* The database connection */
+ Table *pTab, /* The table whose column is desired */
+ int iCursor, /* The open cursor on the table */
+ i16 iCol /* The column that is wanted */
+){
+ Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
+ if( pExpr ){
+ pExpr->pTab = pTab;
+ pExpr->iTable = iCursor;
+ pExpr->iColumn = iCol;
+ }
+ return pExpr;
+}
+
/*
** This function is called to generate code executed when a row is deleted
** from the parent table of foreign key constraint pFKey and, if pFKey is
@@ -457,13 +517,13 @@ static void fkLookupParent(
** --------------------------------------------------------------------------
** DELETE immediate Increment the "immediate constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT immediate Decrement the "immediate constraint counter".
**
** DELETE deferred Increment the "deferred constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT deferred Decrement the "deferred constraint counter".
**
@@ -472,12 +532,12 @@ static void fkLookupParent(
*/
static void fkScanChildren(
Parse *pParse, /* Parse context */
- SrcList *pSrc, /* SrcList containing the table to scan */
- Table *pTab,
- Index *pIdx, /* Foreign key index */
- FKey *pFKey, /* Foreign key relationship */
+ SrcList *pSrc, /* The child table to be scanned */
+ Table *pTab, /* The parent table */
+ Index *pIdx, /* Index on parent covering the foreign key */
+ FKey *pFKey, /* The foreign key linking pSrc to pTab */
int *aiCol, /* Map from pIdx cols to child table cols */
- int regData, /* Referenced table data starts here */
+ int regData, /* Parent row data starts here */
int nIncr /* Amount to increment deferred counter by */
){
sqlite3 *db = pParse->db; /* Database handle */
@@ -488,10 +548,14 @@ static void fkScanChildren(
int iFkIfZero = 0; /* Address of OP_FkIfZero */
Vdbe *v = sqlite3GetVdbe(pParse);
- assert( !pIdx || pIdx->pTable==pTab );
+ assert( pIdx==0 || pIdx->pTable==pTab );
+ assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol );
+ assert( pIdx!=0 || pFKey->nCol==1 );
+ assert( pIdx!=0 || HasRowid(pTab) );
if( nIncr<0 ){
iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0);
+ VdbeCoverage(v);
}
/* Create an Expr object representing an SQL expression like:
@@ -506,29 +570,11 @@ static void fkScanChildren(
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
Expr *pEq; /* Expression (pLeft = pRight) */
- int iCol; /* Index of column in child table */
+ i16 iCol; /* Index of column in child table */
const char *zCol; /* Name of column in child table */
- pLeft = sqlite3Expr(db, TK_REGISTER, 0);
- if( pLeft ){
- /* Set the collation sequence and affinity of the LHS of each TK_EQ
- ** expression to the parent key column defaults. */
- if( pIdx ){
- Column *pCol;
- const char *zColl;
- iCol = pIdx->aiColumn[i];
- pCol = &pTab->aCol[iCol];
- if( pTab->iPKey==iCol ) iCol = -1;
- pLeft->iTable = regData+iCol+1;
- pLeft->affinity = pCol->affinity;
- zColl = pCol->zColl;
- if( zColl==0 ) zColl = db->pDfltColl->zName;
- pLeft = sqlite3ExprAddCollateString(pParse, pLeft, zColl);
- }else{
- pLeft->iTable = regData;
- pLeft->affinity = SQLITE_AFF_INTEGER;
- }
- }
+ iCol = pIdx ? pIdx->aiColumn[i] : -1;
+ pLeft = exprTableRegister(pParse, pTab, regData, iCol);
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iCol>=0 );
zCol = pFKey->pFrom->aCol[iCol].zName;
@@ -537,24 +583,39 @@ static void fkScanChildren(
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
}
- /* If the child table is the same as the parent table, and this scan
- ** is taking place as part of a DELETE operation (operation D.2), omit the
- ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE
- ** clause, where $rowid is the rowid of the row being deleted. */
+ /* If the child table is the same as the parent table, then add terms
+ ** to the WHERE clause that prevent this entry from being scanned.
+ ** The added WHERE clause terms are like this:
+ **
+ ** $current_rowid!=rowid
+ ** NOT( $current_a==a AND $current_b==b AND ... )
+ **
+ ** The first form is used for rowid tables. The second form is used
+ ** for WITHOUT ROWID tables. In the second form, the primary key is
+ ** (a,b,...)
+ */
if( pTab==pFKey->pFrom && nIncr>0 ){
- Expr *pEq; /* Expression (pLeft = pRight) */
+ Expr *pNe; /* Expression (pLeft != pRight) */
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
- pLeft = sqlite3Expr(db, TK_REGISTER, 0);
- pRight = sqlite3Expr(db, TK_COLUMN, 0);
- if( pLeft && pRight ){
- pLeft->iTable = regData;
- pLeft->affinity = SQLITE_AFF_INTEGER;
- pRight->iTable = pSrc->a[0].iCursor;
- pRight->iColumn = -1;
+ if( HasRowid(pTab) ){
+ pLeft = exprTableRegister(pParse, pTab, regData, -1);
+ pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
+ pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
+ }else{
+ Expr *pEq, *pAll = 0;
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pIdx!=0 );
+ for(i=0; i<pPk->nKeyCol; i++){
+ i16 iCol = pIdx->aiColumn[i];
+ pLeft = exprTableRegister(pParse, pTab, regData, iCol);
+ pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
+ pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
+ pAll = sqlite3ExprAnd(db, pAll, pEq);
+ }
+ pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
}
- pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
- pWhere = sqlite3ExprAnd(db, pWhere, pEq);
+ pWhere = sqlite3ExprAnd(db, pWhere, pNe);
}
/* Resolve the references in the WHERE clause. */
@@ -584,8 +645,8 @@ static void fkScanChildren(
}
/*
-** This function returns a pointer to the head of a linked list of FK
-** constraints for which table pTab is the parent table. For example,
+** This function returns a linked list of FKey objects (connected by
+** FKey.pNextTo) holding all children of table pTab. For example,
** given the following schema:
**
** CREATE TABLE t1(a PRIMARY KEY);
@@ -653,11 +714,11 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
** when this statement is run. */
FKey *p;
for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( p->isDeferred ) break;
+ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
}
if( !p ) return;
iSkip = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip);
+ sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v);
}
pParse->disableTriggers = 1;
@@ -667,11 +728,18 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
/* If the DELETE has generated immediate foreign key constraint
** violations, halt the VDBE and return an error at this point, before
** any modifications to the schema are made. This is because statement
- ** transactions are not able to rollback schema changes. */
- sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
- OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ ** transactions are not able to rollback schema changes.
+ **
+ ** If the SQLITE_DeferFKs flag is set, then this is not required, as
+ ** the statement transaction will not be rolled back even if FK
+ ** constraints are violated.
+ */
+ if( (db->flags & SQLITE_DeferFKs)==0 ){
+ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
+ }
if( iSkip ){
sqlite3VdbeResolveLabel(v, iSkip);
@@ -679,6 +747,70 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
}
}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the child table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** child key for FK constraint *p are modified.
+*/
+static int fkChildIsModified(
+ Table *pTab, /* Table being updated */
+ FKey *p, /* Foreign key for which pTab is the child */
+ int *aChange, /* Array indicating modified columns */
+ int bChngRowid /* True if rowid is modified by this update */
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ int iChildKey = p->aCol[i].iFrom;
+ if( aChange[iChildKey]>=0 ) return 1;
+ if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
+ }
+ return 0;
+}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the parent table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** parent key for FK constraint *p are modified.
+*/
+static int fkParentIsModified(
+ Table *pTab,
+ FKey *p,
+ int *aChange,
+ int bChngRowid
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ char *zKey = p->aCol[i].zCol;
+ int iKey;
+ for(iKey=0; iKey<pTab->nCol; iKey++){
+ if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
+ Column *pCol = &pTab->aCol[iKey];
+ if( zKey ){
+ if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
+ }else if( pCol->colFlags & COLFLAG_PRIMKEY ){
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
/*
** This function is called when inserting, deleting or updating a row of
** table pTab to generate VDBE code to perform foreign key constraint
@@ -703,7 +835,9 @@ void sqlite3FkCheck(
Parse *pParse, /* Parse context */
Table *pTab, /* Row is being deleted from this table */
int regOld, /* Previous row data is stored here */
- int regNew /* New row data is stored here */
+ int regNew, /* New row data is stored here */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
sqlite3 *db = pParse->db; /* Database handle */
FKey *pFKey; /* Used to iterate through FKs */
@@ -731,6 +865,13 @@ void sqlite3FkCheck(
int i;
int isIgnore = 0;
+ if( aChange
+ && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
+ && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
+ ){
+ continue;
+ }
+
/* Find the parent table of this foreign key. Also find a unique index
** on the parent key columns in the parent table. If either of these
** schema items cannot be located, set an error in pParse and return
@@ -755,7 +896,7 @@ void sqlite3FkCheck(
int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
for(i=0; i<pFKey->nCol; i++){
int iReg = pFKey->aCol[i].iFrom + regOld + 1;
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump);
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v);
}
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
}
@@ -807,13 +948,20 @@ void sqlite3FkCheck(
sqlite3DbFree(db, aiFree);
}
- /* Loop through all the foreign key constraints that refer to this table */
+ /* Loop through all the foreign key constraints that refer to this table.
+ ** (the "child" constraints) */
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
Index *pIdx = 0; /* Foreign key index for pFKey */
SrcList *pSrc;
int *aiCol = 0;
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
+ continue;
+ }
+
+ if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel && !pParse->isMultiWrite
+ ){
assert( regOld==0 && regNew!=0 );
/* Inserting a single row into a parent table cannot cause an immediate
** foreign key violation. So do nothing in this case. */
@@ -826,9 +974,8 @@ void sqlite3FkCheck(
}
assert( aiCol || pFKey->nCol==1 );
- /* Create a SrcList structure containing a single table (the table
- ** the foreign key that refers to this table is attached to). This
- ** is required for the sqlite3WhereXXX() interface. */
+ /* Create a SrcList structure containing the child table. We need the
+ ** child table as a SrcList for sqlite3WhereBegin() */
pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
if( pSrc ){
struct SrcList_item *pItem = pSrc->a;
@@ -877,13 +1024,14 @@ u32 sqlite3FkOldmask(
Index *pIdx = 0;
sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
if( pIdx ){
- for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
}
}
}
return mask;
}
+
/*
** This function is called before generating code to update or delete a
** row contained in table pTab. If the operation is a DELETE, then
@@ -913,32 +1061,16 @@ int sqlite3FkRequired(
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
- int i;
FKey *p;
/* Check if any child key columns are being modified. */
for(p=pTab->pFKey; p; p=p->pNextFrom){
- for(i=0; i<p->nCol; i++){
- int iChildKey = p->aCol[i].iFrom;
- if( aChange[iChildKey]>=0 ) return 1;
- if( iChildKey==pTab->iPKey && chngRowid ) return 1;
- }
+ if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
- for(i=0; i<p->nCol; i++){
- char *zKey = p->aCol[i].zCol;
- int iKey;
- for(iKey=0; iKey<pTab->nCol; iKey++){
- Column *pCol = &pTab->aCol[iKey];
- if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey)
- : (pCol->colFlags & COLFLAG_PRIMKEY)!=0) ){
- if( aChange[iKey]>=0 ) return 1;
- if( iKey==pTab->iPKey && chngRowid ) return 1;
- }
- }
- }
+ if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
}
}
@@ -1084,7 +1216,7 @@ static Trigger *fkActionTrigger(
tFrom.z = zFrom;
tFrom.n = nFrom;
- pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed");
+ pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
pRaise->affinity = OE_Abort;
}
@@ -1164,7 +1296,9 @@ void sqlite3FkActions(
Parse *pParse, /* Parse context */
Table *pTab, /* Table being updated or deleted from */
ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */
- int regOld /* Address of array containing old row */
+ int regOld, /* Address of array containing old row */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
/* If foreign-key support is enabled, iterate through all FKs that
** refer to table pTab. If there is an action associated with the FK
@@ -1173,9 +1307,11 @@ void sqlite3FkActions(
if( pParse->db->flags&SQLITE_ForeignKeys ){
FKey *pFKey; /* Iterator variable */
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
- Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
- if( pAction ){
- sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
+ if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
+ Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
+ if( pAct ){
+ sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
+ }
}
}
}
diff --git a/src/func.c b/src/func.c
index c02f096..84fac28 100644
--- a/src/func.c
+++ b/src/func.c
@@ -9,12 +9,9 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement various SQL
-** functions of SQLite.
-**
-** There is only one exported symbol in this file - the function
-** sqliteRegisterBuildinFunctions() found at the bottom of the file.
-** All other code has file scope.
+** This file contains the C-language implementions for many of the SQL
+** functions of SQLite. (Some function, and in particular the date and
+** time functions, are implemented separately.)
*/
#include "sqliteInt.h"
#include <stdlib.h>
@@ -137,9 +134,9 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
case SQLITE_INTEGER: {
i64 iVal = sqlite3_value_int64(argv[0]);
if( iVal<0 ){
- if( (iVal<<1)==0 ){
- /* IMP: R-35460-15084 If X is the integer -9223372036854775807 then
- ** abs(X) throws an integer overflow error since there is no
+ if( iVal==SMALLEST_INT64 ){
+ /* IMP: R-31676-45509 If X is the integer -9223372036854775808
+ ** then abs(X) throws an integer overflow error since there is no
** equivalent positive 64-bit two complement value. */
sqlite3_result_error(context, "integer overflow", -1);
return;
@@ -219,6 +216,32 @@ static void instrFunc(
}
/*
+** Implementation of the printf() function.
+*/
+static void printfFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ PrintfArguments x;
+ StrAccum str;
+ const char *zFormat;
+ int n;
+
+ if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){
+ x.nArg = argc-1;
+ x.nUsed = 0;
+ x.apArg = argv+1;
+ sqlite3StrAccumInit(&str, 0, 0, SQLITE_MAX_LENGTH);
+ str.db = sqlite3_context_db_handle(context);
+ sqlite3XPrintf(&str, SQLITE_PRINTF_SQLFUNC, zFormat, &x);
+ n = str.nChar;
+ sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
+ SQLITE_DYNAMIC);
+ }
+}
+
+/*
** Implementation of the substr() function.
**
** substr(x,p1,p2) returns p2 characters of x[] beginning with p1.
@@ -228,7 +251,7 @@ static void instrFunc(
**
** If p1 is negative, then we begin abs(p1) from the end of x[].
**
-** If p2 is negative, return the p2 characters preceeding p1.
+** If p2 is negative, return the p2 characters preceding p1.
*/
static void substrFunc(
sqlite3_context *context,
@@ -418,14 +441,14 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
/*
-** The COALESCE() and IFNULL() functions are implemented as VDBE code so
-** that unused argument values do not have to be computed. However, we
-** still need some kind of function implementation for this routines in
-** the function table. That function implementation will never be called
-** so it doesn't matter what the implementation is. We might as well use
-** the "version()" function as a substitute.
+** Some functions like COALESCE() and IFNULL() and UNLIKELY() are implemented
+** as VDBE code so that unused argument values do not have to be computed.
+** However, we still need some kind of function implementation for this
+** routines in the function table. The noopFunc macro provides this.
+** noopFunc will never be called so it doesn't matter what the implementation
+** is. We might as well use the "version()" function as a substitute.
*/
-#define ifnullFunc versionFunc /* Substitute function - never called */
+#define noopFunc versionFunc /* Substitute function - never called */
/*
** Implementation of random(). Return a random integer.
@@ -544,9 +567,9 @@ struct compareInfo {
*/
#if defined(SQLITE_EBCDIC)
# define sqlite3Utf8Read(A) (*((*A)++))
-# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
+# define GlobUpperToLower(A) A = sqlite3UpperToLower[A]
#else
-# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
+# define GlobUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@@ -625,11 +648,11 @@ static int patternCompare(
}
while( (c2 = sqlite3Utf8Read(&zString))!=0 ){
if( noCase ){
- GlogUpperToLower(c2);
- GlogUpperToLower(c);
+ GlobUpperToLower(c2);
+ GlobUpperToLower(c);
while( c2 != 0 && c2 != c ){
c2 = sqlite3Utf8Read(&zString);
- GlogUpperToLower(c2);
+ GlobUpperToLower(c2);
}
}else{
while( c2 != 0 && c2 != c ){
@@ -681,8 +704,8 @@ static int patternCompare(
}else{
c2 = sqlite3Utf8Read(&zString);
if( noCase ){
- GlogUpperToLower(c);
- GlogUpperToLower(c2);
+ GlobUpperToLower(c);
+ GlobUpperToLower(c2);
}
if( c!=c2 ){
return 0;
@@ -887,10 +910,6 @@ static const char hexdigits[] = {
};
/*
-** EXPERIMENTAL - This is not an official function. The interface may
-** change. This function may disappear. Do not write code that depends
-** on this function.
-**
** Implementation of the QUOTE() function. This function takes a single
** argument. If the argument is numeric, the return value is the same as
** the argument. If the argument is NULL, the return value is the string
@@ -995,7 +1014,7 @@ static void charFunc(
){
unsigned char *z, *zOut;
int i;
- zOut = z = sqlite3_malloc( argc*4 );
+ zOut = z = sqlite3_malloc( argc*4+1 );
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -1079,7 +1098,7 @@ static void zeroblobFunc(
/*
** The replace() function. Three arguments are all strings: call
** them A, B, and C. The result is also a string which is derived
-** from A by replacing every occurance of B with C. The match
+** from A by replacing every occurrence of B with C. The match
** must be exact. Collating sequences are not used.
*/
static void replaceFunc(
@@ -1515,20 +1534,20 @@ static void groupConcatStep(
zSep = ",";
nSep = 1;
}
- sqlite3StrAccumAppend(pAccum, zSep, nSep);
+ if( nSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
}
zVal = (char*)sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
- sqlite3StrAccumAppend(pAccum, zVal, nVal);
+ if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
}
}
static void groupConcatFinalize(sqlite3_context *context){
StrAccum *pAccum;
pAccum = sqlite3_aggregate_context(context, 0);
if( pAccum ){
- if( pAccum->tooBig ){
+ if( pAccum->accError==STRACCUM_TOOBIG ){
sqlite3_result_error_toobig(context);
- }else if( pAccum->mallocFailed ){
+ }else if( pAccum->accError==STRACCUM_NOMEM ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
@@ -1572,7 +1591,7 @@ static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
2, SQLITE_UTF8, 0);
if( ALWAYS(pDef) ){
- pDef->flags = flagVal;
+ pDef->funcFlags |= flagVal;
}
}
@@ -1616,7 +1635,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
pDef = sqlite3FindFunction(db, pExpr->u.zToken,
sqlite3Strlen30(pExpr->u.zToken),
2, SQLITE_UTF8, 0);
- if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
+ if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
@@ -1628,7 +1647,7 @@ int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
- *pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0;
+ *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
return 1;
}
@@ -1666,6 +1685,7 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(instr, 2, 0, 0, instrFunc ),
FUNCTION(substr, 2, 0, 0, substrFunc ),
FUNCTION(substr, 3, 0, 0, substrFunc ),
+ FUNCTION(printf, -1, 0, 0, printfFunc ),
FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
FUNCTION(char, -1, 0, 0, charFunc ),
FUNCTION(abs, 1, 0, 0, absFunc ),
@@ -1677,11 +1697,14 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(lower, 1, 0, 0, lowerFunc ),
FUNCTION(coalesce, 1, 0, 0, 0 ),
FUNCTION(coalesce, 0, 0, 0, 0 ),
- FUNCTION2(coalesce, -1, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
FUNCTION(hex, 1, 0, 0, hexFunc ),
- FUNCTION2(ifnull, 2, 0, 0, ifnullFunc, SQLITE_FUNC_COALESCE),
- FUNCTION(random, 0, 0, 0, randomFunc ),
- FUNCTION(randomblob, 1, 0, 0, randomBlob ),
+ FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
+ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ VFUNCTION(random, 0, 0, 0, randomFunc ),
+ VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
@@ -1691,9 +1714,9 @@ void sqlite3RegisterGlobalFunctions(void){
FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
FUNCTION(quote, 1, 0, 0, quoteFunc ),
- FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
- FUNCTION(changes, 0, 0, 0, changes ),
- FUNCTION(total_changes, 0, 0, 0, total_changes ),
+ VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
+ VFUNCTION(changes, 0, 0, 0, changes ),
+ VFUNCTION(total_changes, 0, 0, 0, total_changes ),
FUNCTION(replace, 3, 0, 0, replaceFunc ),
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
#ifdef SQLITE_SOUNDEX
@@ -1707,7 +1730,7 @@ void sqlite3RegisterGlobalFunctions(void){
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
/* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
- {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
+ {0,SQLITE_UTF8|SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
@@ -1733,4 +1756,7 @@ void sqlite3RegisterGlobalFunctions(void){
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
+#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
+ sqlite3AnalyzeFunctions();
+#endif
}
diff --git a/src/global.c b/src/global.c
index 7b02cf2..2c14b58 100644
--- a/src/global.c
+++ b/src/global.c
@@ -148,6 +148,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0x7ffffffe, /* mxStrlen */
+ 0, /* neverCorrupt */
128, /* szLookaside */
500, /* nLookaside */
{0,0,0,0,0,0,0,0}, /* m */
@@ -172,18 +173,24 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* isMutexInit */
0, /* isMallocInit */
0, /* isPCacheInit */
- 0, /* pInitMutex */
0, /* nRefInitMutex */
+ 0, /* pInitMutex */
0, /* xLog */
0, /* pLogArg */
- 0, /* bLocaltimeFault */
#ifdef SQLITE_ENABLE_SQLLOG
0, /* xSqllog */
- 0 /* pSqllogArg */
+ 0, /* pSqllogArg */
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ 0, /* xVdbeBranch */
+ 0, /* pVbeBranchArg */
#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ 0, /* xTestCallback */
+#endif
+ 0 /* bLocaltimeFault */
};
-
/*
** Hash table for global functions - functions common to all
** database connections. After initialization, this table is
diff --git a/src/hash.c b/src/hash.c
index e81dcf9..f9901fe 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -53,7 +53,7 @@ void sqlite3HashClear(Hash *pH){
** The hashing function.
*/
static unsigned int strHash(const char *z, int nKey){
- int h = 0;
+ unsigned int h = 0;
assert( nKey>=0 );
while( nKey > 0 ){
h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
diff --git a/src/insert.c b/src/insert.c
index 9a5661f..5964b91 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -15,10 +15,16 @@
#include "sqliteInt.h"
/*
-** Generate code that will open a table for reading.
+** Generate code that will
+**
+** (1) acquire a lock for table pTab then
+** (2) open pTab as cursor iCur.
+**
+** If pTab is a WITHOUT ROWID table, then it is the PRIMARY KEY index
+** for that table that is actually opened.
*/
void sqlite3OpenTable(
- Parse *p, /* Generate code into this VDBE */
+ Parse *pParse, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */
int iDb, /* The database index in sqlite3.aDb[] */
Table *pTab, /* The table to be opened */
@@ -26,12 +32,21 @@ void sqlite3OpenTable(
){
Vdbe *v;
assert( !IsVirtual(pTab) );
- v = sqlite3GetVdbe(p);
+ v = sqlite3GetVdbe(pParse);
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
- sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
- sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
- sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
- VdbeComment((v, "%s", pTab->zName));
+ sqlite3TableLock(pParse, iDb, pTab->tnum,
+ (opcode==OP_OpenWrite)?1:0, pTab->zName);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol);
+ VdbeComment((v, "%s", pTab->zName));
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ assert( pPk->tnum=pTab->tnum );
+ sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ VdbeComment((v, "%s", pTab->zName));
+ }
}
/*
@@ -67,15 +82,15 @@ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
int n;
Table *pTab = pIdx->pTable;
sqlite3 *db = sqlite3VdbeDb(v);
- pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+2);
+ pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
db->mallocFailed = 1;
return 0;
}
for(n=0; n<pIdx->nColumn; n++){
- pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
+ i16 x = pIdx->aiColumn[n];
+ pIdx->zColAff[n] = x<0 ? SQLITE_AFF_INTEGER : pTab->aCol[x].affinity;
}
- pIdx->zColAff[n++] = SQLITE_AFF_INTEGER;
pIdx->zColAff[n] = 0;
}
@@ -83,10 +98,16 @@ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
}
/*
-** 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:
+** Compute the affinity string for table pTab, if it has not already been
+** computed. As an optimization, omit trailing SQLITE_AFF_NONE affinities.
+**
+** If the affinity exists (if it is no entirely SQLITE_AFF_NONE values) and
+** if iReg>0 then code an OP_Affinity opcode that will set the affinities
+** for register iReg and following. Or if affinities exists and iReg==0,
+** then just set the P4 operand of the previous opcode (which should be
+** an OP_MakeRecord) to the affinity string.
+**
+** A column affinity string has one character per column:
**
** Character Column affinity
** ------------------------------
@@ -96,19 +117,11 @@ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
** 'd' INTEGER
** 'e' REAL
*/
-void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
- /* The first time a column affinity string for a particular table
- ** is required, it is allocated and populated here. It is then
- ** stored as a member of the Table structure for subsequent use.
- **
- ** The column affinity string will eventually be deleted by
- ** sqlite3DeleteTable() when the Table structure itself is cleaned up.
- */
- if( !pTab->zColAff ){
- char *zColAff;
- int i;
+void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
+ int i;
+ char *zColAff = pTab->zColAff;
+ if( zColAff==0 ){
sqlite3 *db = sqlite3VdbeDb(v);
-
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
if( !zColAff ){
db->mallocFailed = 1;
@@ -118,22 +131,28 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
for(i=0; i<pTab->nCol; i++){
zColAff[i] = pTab->aCol[i].affinity;
}
- zColAff[pTab->nCol] = '\0';
-
+ do{
+ zColAff[i--] = 0;
+ }while( i>=0 && zColAff[i]==SQLITE_AFF_NONE );
pTab->zColAff = zColAff;
}
-
- sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT);
+ i = sqlite3Strlen30(zColAff);
+ if( i ){
+ if( iReg ){
+ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
+ }else{
+ sqlite3VdbeChangeP4(v, -1, zColAff, i);
+ }
+ }
}
/*
** 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
+** have been opened at any point in the VDBE 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.
+** run without using a temporary table for the results of the SELECT.
*/
-static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
+static int readsTable(Parse *p, int iDb, Table *pTab){
Vdbe *v = sqlite3GetVdbe(p);
int i;
int iEnd = sqlite3VdbeCurrentAddr(v);
@@ -141,7 +160,7 @@ static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0;
#endif
- for(i=iStartAddr; i<iEnd; i++){
+ for(i=1; i<iEnd; i++){
VdbeOp *pOp = sqlite3VdbeGetOp(v, i);
assert( pOp!=0 );
if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
@@ -242,14 +261,14 @@ void sqlite3AutoincrementBegin(Parse *pParse){
sqlite3VdbeAddOp3(v, OP_Null, 0, memId, memId+1);
addr = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
+ sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId);
- sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId);
+ sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9);
- sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2);
+ sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, 0, memId);
sqlite3VdbeAddOp0(v, OP_Close);
}
@@ -284,25 +303,16 @@ void sqlite3AutoincrementEnd(Parse *pParse){
assert( v );
for(p = pParse->pAinc; p; p = p->pNext){
Db *pDb = &db->aDb[p->iDb];
- int j1, j2, j3, j4, j5;
+ int j1;
int iRec;
int memId = p->regCtr;
iRec = sqlite3GetTempReg(pParse);
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1);
- j2 = sqlite3VdbeAddOp0(v, OP_Rewind);
- j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec);
- j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec);
- sqlite3VdbeAddOp2(v, OP_Next, 0, j3);
- sqlite3VdbeJumpHere(v, j2);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1);
- j5 = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, j4);
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeJumpHere(v, j5);
sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec);
sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
@@ -320,97 +330,6 @@ void sqlite3AutoincrementEnd(Parse *pParse){
#endif /* SQLITE_OMIT_AUTOINCREMENT */
-/*
-** Generate code for a co-routine that will evaluate a subquery one
-** row at a time.
-**
-** The pSelect parameter is the subquery that the co-routine will evaluation.
-** Information about the location of co-routine and the registers it will use
-** is returned by filling in the pDest object.
-**
-** Registers are allocated as follows:
-**
-** pDest->iSDParm The register holding the next entry-point of the
-** co-routine. Run the co-routine to its next breakpoint
-** by calling "OP_Yield $X" where $X is pDest->iSDParm.
-**
-** pDest->iSDParm+1 The register holding the "completed" flag for the
-** co-routine. This register is 0 if the previous Yield
-** generated a new result row, or 1 if the subquery
-** has completed. If the Yield is called again
-** after this register becomes 1, then the VDBE will
-** halt with an SQLITE_INTERNAL error.
-**
-** pDest->iSdst First result register.
-**
-** pDest->nSdst Number of result registers.
-**
-** This routine handles all of the register allocation and fills in the
-** pDest structure appropriately.
-**
-** Here is a schematic of the generated code assuming that X is the
-** co-routine entry-point register reg[pDest->iSDParm], that EOF is the
-** completed flag reg[pDest->iSDParm+1], and R and S are the range of
-** registers that hold the result set, reg[pDest->iSdst] through
-** reg[pDest->iSdst+pDest->nSdst-1]:
-**
-** X <- A
-** EOF <- 0
-** goto B
-** A: setup for the SELECT
-** loop rows in the SELECT
-** load results into registers R..S
-** yield X
-** end loop
-** cleanup after the SELECT
-** EOF <- 1
-** yield X
-** halt-error
-** B:
-**
-** To use this subroutine, the caller generates code as follows:
-**
-** [ Co-routine generated by this subroutine, shown above ]
-** S: yield X
-** if EOF goto E
-** if skip this row, goto C
-** if terminate loop, goto E
-** deal with this row
-** C: goto S
-** E:
-*/
-int sqlite3CodeCoroutine(Parse *pParse, Select *pSelect, SelectDest *pDest){
- int regYield; /* Register holding co-routine entry-point */
- int regEof; /* Register holding co-routine completion flag */
- int addrTop; /* Top of the co-routine */
- int j1; /* Jump instruction */
- int rc; /* Result code */
- Vdbe *v; /* VDBE under construction */
-
- regYield = ++pParse->nMem;
- regEof = ++pParse->nMem;
- v = sqlite3GetVdbe(pParse);
- addrTop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_Integer, addrTop+2, regYield); /* X <- A */
- VdbeComment((v, "Co-routine entry point"));
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */
- VdbeComment((v, "Co-routine completion flag"));
- sqlite3SelectDestInit(pDest, SRT_Coroutine, regYield);
- j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- rc = sqlite3Select(pParse, pSelect, pDest);
- assert( pParse->nErr==0 || rc );
- if( pParse->db->mallocFailed && rc==SQLITE_OK ) rc = SQLITE_NOMEM;
- if( rc ) return rc;
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
- sqlite3VdbeAddOp1(v, OP_Yield, regYield); /* yield X */
- sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort);
- VdbeComment((v, "End of coroutine"));
- sqlite3VdbeJumpHere(v, j1); /* label B: */
- return rc;
-}
-
-
-
/* Forward declaration */
static int xferOptimization(
Parse *pParse, /* Parser context */
@@ -421,7 +340,7 @@ static int xferOptimization(
);
/*
-** This routine is call to handle SQL of the following forms:
+** This routine is called to handle SQL of the following forms:
**
** insert into TABLE (IDLIST) values(EXPRLIST)
** insert into TABLE (IDLIST) select
@@ -436,12 +355,12 @@ static int xferOptimization(
** data for the insert.
**
** The code generated follows one of four templates. For a simple
-** select with data coming from a VALUES clause, the code executes
+** insert with data coming from a VALUES clause, the code executes
** 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
+** put VALUES clause expressions into registers
** write the resulting record into <table>
** cleanup
**
@@ -473,7 +392,6 @@ static int xferOptimization(
** 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
@@ -482,12 +400,9 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
-** EOF <- 1
-** yield X
-** goto A
+** end-coroutine X
** B: open write cursor to <table> and its indices
-** C: yield X
-** if EOF goto D
+** C: yield X, at EOF goto D
** insert the select result into <table> from R..R+n
** goto C
** D: cleanup
@@ -498,7 +413,6 @@ static int xferOptimization(
** 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
@@ -507,12 +421,9 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
-** EOF <- 1
-** yield X
-** halt-error
+** end co-routine R
** B: open temp table
-** L: yield X
-** if EOF goto M
+** L: yield X, at EOF goto M
** insert row from R..R+n into temp table
** goto L
** M: open write cursor to <table> and its indices
@@ -525,7 +436,6 @@ static int xferOptimization(
void sqlite3Insert(
Parse *pParse, /* Parser context */
SrcList *pTabList, /* Name of table into which we are inserting */
- ExprList *pList, /* List of values to be inserted */
Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */
@@ -539,18 +449,21 @@ void sqlite3Insert(
Index *pIdx; /* For looping over indices of the table */
int nColumn; /* Number of columns in the data */
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 iDataCur = 0; /* VDBE cursor that is the main data repository */
+ int iIdxCur = 0; /* First index cursor */
+ int ipkColumn = -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 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 iDb; /* Index of database holding TABLE */
Db *pDb; /* The database containing table being inserted into */
- int appendFlag = 0; /* True if the insert is likely to be an append */
+ u8 useTempTable = 0; /* Store SELECT results in intermediate table */
+ u8 appendFlag = 0; /* True if the insert is likely to be an append */
+ u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
+ u8 bIdListInOrder = 1; /* True if IDLIST is in table order */
+ ExprList *pList = 0; /* List of VALUES() to be inserted */
/* Register allocations */
int regFromSelect = 0;/* Base register for data coming from SELECT */
@@ -559,7 +472,6 @@ void sqlite3Insert(
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 regEof = 0; /* Register recording end of SELECT data */
int *aRegIdx = 0; /* One register allocated to each index */
#ifndef SQLITE_OMIT_TRIGGER
@@ -574,6 +486,17 @@ void sqlite3Insert(
goto insert_cleanup;
}
+ /* If the Select object is really just a simple VALUES() list with a
+ ** single row values (the common case) then keep that one row of values
+ ** and go ahead and discard the Select object
+ */
+ if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){
+ pList = pSelect->pEList;
+ pSelect->pEList = 0;
+ sqlite3SelectDelete(db, pSelect);
+ pSelect = 0;
+ }
+
/* Locate the table into which we will be inserting new information.
*/
assert( pTabList->nSrc==1 );
@@ -590,6 +513,7 @@ void sqlite3Insert(
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
goto insert_cleanup;
}
+ withoutRowid = !HasRowid(pTab);
/* Figure out if we have any triggers and if the table being
** inserted into is a view
@@ -609,16 +533,13 @@ void sqlite3Insert(
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
/* If pTab is really a view, make sure it has been initialized.
- ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual
- ** module table).
+ ** ViewGetColumnNames() is a no-op if pTab is not a view.
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto insert_cleanup;
}
- /* Ensure that:
- * (a) the table is not read-only,
- * (b) that if it is a view then ON INSERT triggers exist
+ /* Cannot insert into a read-only table.
*/
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
goto insert_cleanup;
@@ -653,33 +574,93 @@ void sqlite3Insert(
*/
regAutoinc = autoIncBegin(pParse, iDb, pTab);
+ /* Allocate registers for holding the rowid of the new row,
+ ** the content of the new row, and the assemblied row record.
+ */
+ regRowid = regIns = pParse->nMem+1;
+ pParse->nMem += pTab->nCol + 1;
+ if( IsVirtual(pTab) ){
+ regRowid++;
+ pParse->nMem++;
+ }
+ regData = regRowid+1;
+
+ /* If the INSERT statement included an IDLIST term, then make sure
+ ** all elements of the IDLIST really are columns of the table and
+ ** remember the column indices.
+ **
+ ** If the table has an INTEGER PRIMARY KEY column and that column
+ ** is named in the IDLIST, then record in the ipkColumn variable
+ ** the index into IDLIST of the primary key column. ipkColumn is
+ ** the index of the primary key as it appears in IDLIST, not as
+ ** is appears in the original table. (The index of the INTEGER
+ ** PRIMARY KEY in the original table is pTab->iPKey.)
+ */
+ if( pColumn ){
+ for(i=0; i<pColumn->nId; i++){
+ pColumn->a[i].idx = -1;
+ }
+ for(i=0; i<pColumn->nId; i++){
+ for(j=0; j<pTab->nCol; j++){
+ if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
+ pColumn->a[i].idx = j;
+ if( i!=j ) bIdListInOrder = 0;
+ if( j==pTab->iPKey ){
+ ipkColumn = i; assert( !withoutRowid );
+ }
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){
+ ipkColumn = i;
+ bIdListInOrder = 0;
+ }else{
+ sqlite3ErrorMsg(pParse, "table %S has no column named %s",
+ pTabList, 0, pColumn->a[i].zName);
+ pParse->checkSchema = 1;
+ goto insert_cleanup;
+ }
+ }
+ }
+ }
+
/* Figure out how many columns of data are supplied. If the data
** 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 a co-routine to run that
- ** SELECT. */
- int rc = sqlite3CodeCoroutine(pParse, pSelect, &dest);
- if( rc ) goto insert_cleanup;
-
- regEof = dest.iSDParm + 1;
+ /* Data is coming from a SELECT. Generate a co-routine to run the SELECT */
+ int regYield; /* Register holding co-routine entry-point */
+ int addrTop; /* Top of the co-routine */
+ int rc; /* Result code */
+
+ regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
+ rc = sqlite3Select(pParse, pSelect, &dest);
regFromSelect = dest.iSdst;
+ assert( pParse->nErr==0 || rc );
+ if( rc || db->mallocFailed ) goto insert_cleanup;
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
- assert( dest.nSdst==nColumn );
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
- ** FALSE if each* row of the SELECT can be written directly into
+ ** FALSE if each output 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( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){
+ if( pTrigger || readsTable(pParse, iDb, pTab) ){
useTempTable = 1;
}
@@ -689,28 +670,25 @@ void sqlite3Insert(
** here is from the 4th template:
**
** B: open temp table
- ** L: yield X
- ** if EOF goto M
+ ** L: yield X, goto M at EOF
** insert row from R..R+n into temp table
** goto L
** M: ...
*/
int regRec; /* Register to hold packed record */
int regTempRowid; /* Register to hold temp table ROWID */
- int addrTop; /* Label "L" */
- int addrIf; /* Address of jump to M */
+ int addrL; /* Label "L" */
srcTab = pParse->nTab++;
regRec = sqlite3GetTempReg(pParse);
regTempRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
- addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
- addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
+ addrL = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
- sqlite3VdbeJumpHere(v, addrIf);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrL);
+ sqlite3VdbeJumpHere(v, addrL);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempReg(pParse, regTempRowid);
}
@@ -731,6 +709,14 @@ void sqlite3Insert(
}
}
+ /* If there is no IDLIST term but the table has an integer primary
+ ** key, the set the ipkColumn variable to the integer primary key
+ ** column index in the original table definition.
+ */
+ if( pColumn==0 && nColumn>0 ){
+ ipkColumn = pTab->iPKey;
+ }
+
/* Make sure the number of columns in the source data matches the number
** of columns to be inserted into the table.
*/
@@ -749,52 +735,6 @@ void sqlite3Insert(
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
goto insert_cleanup;
}
-
- /* If the INSERT statement included an IDLIST term, then make sure
- ** all elements of the IDLIST really are columns of the table and
- ** remember the column indices.
- **
- ** If the table has an INTEGER PRIMARY KEY column and that column
- ** is named in the IDLIST, then record in the keyColumn variable
- ** the index into IDLIST of the primary key column. keyColumn is
- ** the index of the primary key as it appears in IDLIST, not as
- ** is appears in the original table. (The index of the primary
- ** key in the original table is pTab->iPKey.)
- */
- if( pColumn ){
- for(i=0; i<pColumn->nId; i++){
- pColumn->a[i].idx = -1;
- }
- for(i=0; i<pColumn->nId; i++){
- for(j=0; j<pTab->nCol; j++){
- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
- pColumn->a[i].idx = j;
- if( j==pTab->iPKey ){
- keyColumn = i;
- }
- break;
- }
- }
- if( j>=pTab->nCol ){
- if( sqlite3IsRowid(pColumn->a[i].zName) ){
- keyColumn = i;
- }else{
- sqlite3ErrorMsg(pParse, "table %S has no column named %s",
- pTabList, 0, pColumn->a[i].zName);
- pParse->checkSchema = 1;
- goto insert_cleanup;
- }
- }
- }
- }
-
- /* If there is no IDLIST term but the table has an integer primary
- ** key, the set the keyColumn variable to the primary key column index
- ** in the original table definition.
- */
- if( pColumn==0 && nColumn>0 ){
- keyColumn = pTab->iPKey;
- }
/* Initialize the count of rows to be inserted
*/
@@ -806,9 +746,8 @@ void sqlite3Insert(
/* If this is not a view, open the table and and all indices */
if( !isView ){
int nIdx;
-
- baseCur = pParse->nTab;
- nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite);
+ nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, -1, 0,
+ &iDataCur, &iIdxCur);
aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
if( aRegIdx==0 ){
goto insert_cleanup;
@@ -823,38 +762,26 @@ void sqlite3Insert(
/* This block codes the top of loop only. The complete loop is the
** following pseudocode (template 4):
**
- ** rewind temp table
+ ** rewind temp table, if empty goto D
** C: loop over rows of intermediate table
** transfer values form intermediate table into <table>
** end loop
** D: ...
*/
- addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
+ addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab); VdbeCoverage(v);
addrCont = sqlite3VdbeCurrentAddr(v);
}else if( pSelect ){
/* This block codes the top of loop only. The complete loop is the
** following pseudocode (template 3):
**
- ** C: yield X
- ** if EOF goto D
+ ** C: yield X, at EOF goto D
** insert the select result into <table> from R..R+n
** goto C
** D: ...
*/
- addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
- 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.
- */
- regRowid = regIns = pParse->nMem+1;
- pParse->nMem += pTab->nCol + 1;
- if( IsVirtual(pTab) ){
- regRowid++;
- pParse->nMem++;
+ addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
+ VdbeCoverage(v);
}
- regData = regRowid+1;
/* Run the BEFORE and INSTEAD OF triggers, if there are any
*/
@@ -868,20 +795,21 @@ 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
*/
- if( keyColumn<0 ){
+ if( ipkColumn<0 ){
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
}else{
int j1;
+ assert( !withoutRowid );
if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regCols);
}else{
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols);
+ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols);
}
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
/* Cannot have triggers on a virtual table. If it were possible,
@@ -915,8 +843,7 @@ void sqlite3Insert(
** table column affinities.
*/
if( !isView ){
- sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol);
- sqlite3TableAffinityStr(v, pTab);
+ sqlite3TableAffinity(v, pTab, regCols+1);
}
/* Fire BEFORE or INSTEAD OF triggers */
@@ -926,29 +853,27 @@ void sqlite3Insert(
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
}
- /* 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
- ** case the record number is the same as that column.
+ /* Compute the content of the next row to insert into a range of
+ ** registers beginning at regIns.
*/
if( !isView ){
if( IsVirtual(pTab) ){
/* The row that the VUpdate opcode will delete: none */
sqlite3VdbeAddOp2(v, OP_Null, 0, regIns);
}
- if( keyColumn>=0 ){
+ if( ipkColumn>=0 ){
if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid);
+ sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
}else{
VdbeOp *pOp;
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
+ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
pOp = sqlite3VdbeGetOp(v, -1);
if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
appendFlag = 1;
pOp->opcode = OP_NewRowid;
- pOp->p1 = baseCur;
+ pOp->p1 = iDataCur;
pOp->p2 = regRowid;
pOp->p3 = regAutoinc;
}
@@ -959,24 +884,24 @@ void sqlite3Insert(
if( !appendFlag ){
int j1;
if( !IsVirtual(pTab) ){
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid);
- sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
sqlite3VdbeJumpHere(v, j1);
}else{
j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2); VdbeCoverage(v);
}
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); VdbeCoverage(v);
}
- }else if( IsVirtual(pTab) ){
+ }else if( IsVirtual(pTab) || withoutRowid ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid);
}else{
- sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
appendFlag = 1;
}
autoIncStep(pParse, regAutoinc, regRowid);
- /* Push onto the stack, data for all columns of the new entry, beginning
+ /* Compute data for all columns of the new entry, beginning
** with the first column.
*/
nHidden = 0;
@@ -984,10 +909,11 @@ void sqlite3Insert(
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. */
- sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore);
+ ** Whenever this column is read, the rowid will be substituted
+ ** in its place. Hence, fill this column with a NULL to avoid
+ ** taking up data space with information that will never be used.
+ ** As there may be shallow copies of this value, make it a soft-NULL */
+ sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
continue;
}
if( pColumn==0 ){
@@ -1004,11 +930,13 @@ void sqlite3Insert(
}
}
if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore);
+ sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
}else if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
+ if( regFromSelect!=regData ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
+ }
}else{
sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
}
@@ -1028,13 +956,12 @@ void sqlite3Insert(
#endif
{
int isReplace; /* Set to true if constraints may cause a replace */
- sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
- keyColumn>=0, 0, onError, endOfLoop, &isReplace
- );
- sqlite3FkCheck(pParse, pTab, 0, regIns);
- sqlite3CompleteInsertion(
- pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+ regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace
);
+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
+ regIns, aRegIdx, 0, appendFlag, isReplace==0);
}
}
@@ -1055,7 +982,7 @@ void sqlite3Insert(
*/
sqlite3VdbeResolveLabel(v, endOfLoop);
if( useTempTable ){
- sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont);
+ sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrInsTop);
sqlite3VdbeAddOp1(v, OP_Close, srcTab);
}else if( pSelect ){
@@ -1065,9 +992,9 @@ void sqlite3Insert(
if( !IsVirtual(pTab) && !isView ){
/* Close all tables opened */
- sqlite3VdbeAddOp1(v, OP_Close, baseCur);
- for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur);
+ if( iDataCur<iIdxCur ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
+ for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+ sqlite3VdbeAddOp1(v, OP_Close, idx+iIdxCur);
}
}
@@ -1112,36 +1039,48 @@ insert_cleanup:
#undef tmask
#endif
-
/*
-** Generate code to do constraint checks prior to an INSERT or an UPDATE.
-**
-** The input is a range of consecutive registers as follows:
-**
-** 1. The rowid of the row after the update.
-**
-** 2. The data in the first column of the entry after the update.
+** Generate code to do constraint checks prior to an INSERT or an UPDATE
+** on table pTab.
**
-** i. Data from middle columns...
+** The regNewData parameter is the first register in a range that contains
+** the data to be inserted or the data after the update. There will be
+** pTab->nCol+1 registers in this range. The first register (the one
+** that regNewData points to) will contain the new rowid, or NULL in the
+** case of a WITHOUT ROWID table. The second register in the range will
+** contain the content of the first table column. The third register will
+** contain the content of the second table column. And so forth.
**
-** N. The data in the last column of the entry after the update.
+** The regOldData parameter is similar to regNewData except that it contains
+** the data prior to an UPDATE rather than afterwards. regOldData is zero
+** for an INSERT. This routine can distinguish between UPDATE and INSERT by
+** checking regOldData for zero.
**
-** The regRowid parameter is the index of the register containing (1).
+** For an UPDATE, the pkChng boolean is true if the true primary key (the
+** rowid for a normal table or the PRIMARY KEY for a WITHOUT ROWID table)
+** might be modified by the UPDATE. If pkChng is false, then the key of
+** the iDataCur content table is guaranteed to be unchanged by the UPDATE.
**
-** If isUpdate is true and rowidChng is non-zero, then rowidChng contains
-** the address of a register containing the rowid before the update takes
-** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate
-** is false, indicating an INSERT statement, then a non-zero rowidChng
-** indicates that the rowid was explicitly specified as part of the
-** 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 an update.
+** For an INSERT, the pkChng boolean indicates whether or not the rowid
+** was explicitly specified as part of the INSERT statement. If pkChng
+** is zero, it means that the either rowid is computed automatically or
+** that the table is a WITHOUT ROWID table and has no rowid. On an INSERT,
+** pkChng will only be true if the INSERT statement provides an integer
+** value for either the rowid column or its INTEGER PRIMARY KEY alias.
**
-** The code generated by this routine store new index entries into
+** The code generated by this routine will 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.
+** at pTab->pIndex.
+**
+** The caller must have already opened writeable cursors on the main
+** table and all applicable indices (that is to say, all indices for which
+** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
+** inserting or updating a rowid table, or the cursor for the PRIMARY KEY
+** index when operating on a WITHOUT ROWID table. iIdxCur is the cursor
+** for the first index in the pTab->pIndex list. Cursors for other indices
+** are at iIdxCur+N for the N-th element of the pTab->pIndex list.
**
** This routine also generates code to check constraints. NOT NULL,
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
@@ -1151,22 +1090,23 @@ insert_cleanup:
** Constraint type Action What Happens
** --------------- ---------- ----------------------------------------
** any ROLLBACK The current transaction is rolled back and
-** sqlite3_exec() returns immediately with a
+** sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT.
**
** any ABORT Back out changes from the current command
** only (do not do a complete rollback) then
-** cause sqlite3_exec() to return immediately
+** cause sqlite3_step() to return immediately
** with SQLITE_CONSTRAINT.
**
-** any FAIL Sqlite3_exec() returns immediately with a
+** any FAIL Sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT. The
** transaction is not rolled back and any
-** prior changes are retained.
+** changes to prior rows are retained.
**
-** any IGNORE The record number and data is popped from
-** the stack and there is an immediate jump
-** to label ignoreDest.
+** any IGNORE The attempt in insert or update the current
+** row is skipped, without throwing an error.
+** Processing continues with the next row.
+** (There is an immediate jump to ignoreDest.)
**
** NOT NULL REPLACE The NULL value is replace by the default
** value for that column. If the default value
@@ -1181,44 +1121,59 @@ insert_cleanup:
** Or if overrideError==OE_Default, then the pParse->onError parameter
** is used. Or if pParse->onError==OE_Default then the onError value
** for the constraint is used.
-**
-** The calling routine must open a read/write cursor for pTab with
-** 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 aRegIdx[i]==0.
*/
void sqlite3GenerateConstraintChecks(
- Parse *pParse, /* The parser context */
- Table *pTab, /* the table into which we are inserting */
- 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 */
- int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
+ Parse *pParse, /* The parser context */
+ Table *pTab, /* The table being inserted or updated */
+ int *aRegIdx, /* Use register aRegIdx[i] for index i. 0 for unused */
+ int iDataCur, /* Canonical data cursor (main table or PK index) */
+ int iIdxCur, /* First index cursor */
+ int regNewData, /* First register in a range holding values to insert */
+ int regOldData, /* Previous content. 0 for INSERTs */
+ u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
+ u8 overrideError, /* Override onError to this if not OE_Default */
+ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
+ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
){
- int i; /* loop counter */
- Vdbe *v; /* VDBE under constrution */
- int nCol; /* Number of columns */
- int onError; /* Conflict resolution strategy */
- int j1; /* Addresss of jump instruction */
- int j2 = 0, j3; /* Addresses of jump instructions */
- int regData; /* Register containing first data column */
- int iCur; /* Table cursor number */
+ Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
+ Index *pPk = 0; /* The PRIMARY KEY index */
sqlite3 *db; /* Database connection */
+ int i; /* loop counter */
+ int ix; /* Index loop counter */
+ int nCol; /* Number of columns */
+ int onError; /* Conflict resolution strategy */
+ int j1; /* Addresss of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
- int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
-
+ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
+ int ipkTop = 0; /* Top of the rowid change constraint check */
+ int ipkBottom = 0; /* Bottom of the rowid change constraint check */
+ u8 isUpdate; /* True if this is an UPDATE operation */
+ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
+ int regRowid = -1; /* Register holding ROWID value */
+
+ isUpdate = regOldData!=0;
db = pParse->db;
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
- regData = regRowid + 1;
+
+ /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
+ ** normal rowid tables. nPkField is the number of key fields in the
+ ** pPk index or 1 for a rowid table. In other words, nPkField is the
+ ** number of fields in the true primary key of the table. */
+ if( HasRowid(pTab) ){
+ pPk = 0;
+ nPkField = 1;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ nPkField = pPk->nKeyCol;
+ }
+
+ /* Record that this module has started */
+ VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
+ iDataCur, iIdxCur, regNewData, regOldData, pkChng));
/* Test all NOT NULL constraints.
*/
@@ -1241,24 +1196,26 @@ void sqlite3GenerateConstraintChecks(
switch( onError ){
case OE_Abort:
sqlite3MayAbort(pParse);
+ /* Fall through */
case OE_Rollback:
case OE_Fail: {
- char *zMsg;
- sqlite3VdbeAddOp3(v, OP_HaltIfNull,
- SQLITE_CONSTRAINT_NOTNULL, onError, regData+i);
- zMsg = sqlite3MPrintf(db, "%s.%s may not be NULL",
- pTab->zName, pTab->aCol[i].zName);
- sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
+ char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
+ pTab->aCol[i].zName);
+ sqlite3VdbeAddOp4(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError,
+ regNewData+1+i, zMsg, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
+ VdbeCoverage(v);
break;
}
case OE_Ignore: {
- sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest);
+ VdbeCoverage(v);
break;
}
default: {
assert( onError==OE_Replace );
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
+ j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); VdbeCoverage(v);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
sqlite3VdbeJumpHere(v, j1);
break;
}
@@ -1270,7 +1227,7 @@ void sqlite3GenerateConstraintChecks(
#ifndef SQLITE_OMIT_CHECK
if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
ExprList *pCheck = pTab->pCheck;
- pParse->ckBase = regData;
+ pParse->ckBase = regNewData+1;
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
for(i=0; i<pCheck->nExpr; i++){
int allOk = sqlite3VdbeMakeLabel(v);
@@ -1278,37 +1235,61 @@ void sqlite3GenerateConstraintChecks(
if( onError==OE_Ignore ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
}else{
- char *zConsName = pCheck->a[i].zName;
+ char *zName = pCheck->a[i].zName;
+ if( zName==0 ) zName = pTab->zName;
if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
- if( zConsName ){
- zConsName = sqlite3MPrintf(db, "constraint %s failed", zConsName);
- }else{
- zConsName = 0;
- }
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
- onError, zConsName, P4_DYNAMIC);
+ onError, zName, P4_TRANSIENT,
+ P5_ConstraintCheck);
}
sqlite3VdbeResolveLabel(v, allOk);
}
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
- /* If we have an INTEGER PRIMARY KEY, make sure the primary key
- ** of the new record does not previously exist. Except, if this
- ** is an UPDATE and the primary key is not changing, that is OK.
+ /* If rowid is changing, make sure the new rowid does not previously
+ ** exist in the table.
*/
- if( rowidChng ){
+ if( pkChng && pPk==0 ){
+ int addrRowidOk = sqlite3VdbeMakeLabel(v);
+
+ /* Figure out what action to take in case of a rowid collision */
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
onError = OE_Abort;
}
-
+
if( isUpdate ){
- j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng);
+ /* pkChng!=0 does not mean that the rowid has change, only that
+ ** it might have changed. Skip the conflict logic below if the rowid
+ ** is unchanged. */
+ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverage(v);
+ }
+
+ /* If the response to a rowid conflict is REPLACE but the response
+ ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
+ ** to defer the running of the rowid conflict checking until after
+ ** the UNIQUE constraints have run.
+ */
+ if( onError==OE_Replace && overrideError!=OE_Replace ){
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){
+ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto);
+ break;
+ }
+ }
}
- j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
+
+ /* Check to see if the new rowid already exists in the table. Skip
+ ** the following conflict logic if it does not. */
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
+ VdbeCoverage(v);
+
+ /* Generate code that deals with a rowid collision */
switch( onError ){
default: {
onError = OE_Abort;
@@ -1317,8 +1298,7 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY,
- onError, "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3RowidConstraint(pParse, onError, pTab);
break;
}
case OE_Replace: {
@@ -1350,57 +1330,93 @@ void sqlite3GenerateConstraintChecks(
}
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
sqlite3MultiWrite(pParse);
- sqlite3GenerateRowDelete(
- pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace
- );
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ regNewData, 1, 0, OE_Replace, 1);
}else if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
- sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0);
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, 0);
}
seenReplace = 1;
break;
}
case OE_Ignore: {
- assert( seenReplace==0 );
+ /*assert( seenReplace==0 );*/
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
break;
}
}
- sqlite3VdbeJumpHere(v, j3);
- if( isUpdate ){
- sqlite3VdbeJumpHere(v, j2);
+ sqlite3VdbeResolveLabel(v, addrRowidOk);
+ if( ipkTop ){
+ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, ipkTop);
}
}
/* 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.
+ ** Compute the revised record entries for indices as we go.
+ **
+ ** This loop also handles the case of the PRIMARY KEY index for a
+ ** WITHOUT ROWID table.
*/
- for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
- int regIdx;
- int regR;
-
- if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */
+ for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
+ int regIdx; /* Range of registers hold conent for pIdx */
+ int regR; /* Range of registers holding conflicting PK */
+ int iThisCur; /* Cursor for this UNIQUE index */
+ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
+
+ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
+ if( bAffinityDone==0 ){
+ sqlite3TableAffinity(v, pTab, regNewData+1);
+ bAffinityDone = 1;
+ }
+ iThisCur = iIdxCur+ix;
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
+
+ /* Skip partial indices for which the WHERE clause is not true */
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
+ pParse->ckBase = regNewData+1;
+ sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
+ SQLITE_JUMPIFNULL);
+ pParse->ckBase = 0;
+ }
- /* Create a key for accessing the index entry */
- regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1);
+ /* Create a record for this index entry as it should appear after
+ ** the insert or update. Store that record in the aRegIdx[ix] register
+ */
+ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
for(i=0; i<pIdx->nColumn; i++){
- int idx = pIdx->aiColumn[i];
- if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
+ int iField = pIdx->aiColumn[i];
+ int x;
+ if( iField<0 || iField==pTab->iPKey ){
+ if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
+ x = regNewData;
+ regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
}else{
- sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i);
+ x = iField + regNewData + 1;
}
+ sqlite3VdbeAddOp2(v, OP_SCopy, x, regIdx+i);
+ VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
+ VdbeComment((v, "for %s", pIdx->zName));
+ sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
+
+ /* In an UPDATE operation, if this index is the PRIMARY KEY index
+ ** of a WITHOUT ROWID table and there has been no change the
+ ** primary key, then no collision is possible. The collision detection
+ ** logic below can all be skipped. */
+ if( isUpdate && pPk==pIdx && pkChng==0 ){
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ continue;
}
- sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
- sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
- sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
- /* Find out what action to take in case there is an indexing conflict */
+ /* Find out what action to take in case there is a uniqueness conflict */
onError = pIdx->onError;
if( onError==OE_None ){
- sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
continue; /* pIdx is not a UNIQUE index */
}
if( overrideError!=OE_Default ){
@@ -1408,18 +1424,64 @@ void sqlite3GenerateConstraintChecks(
}else if( onError==OE_Default ){
onError = OE_Abort;
}
- if( seenReplace ){
- if( onError==OE_Ignore ) onError = OE_Replace;
- else if( onError==OE_Fail ) onError = OE_Abort;
- }
/* Check to see if the new index entry will be unique */
- regR = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR);
- j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
- regR, SQLITE_INT_TO_PTR(regIdx),
- P4_INT32);
- sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
+ regIdx, pIdx->nKeyCol); VdbeCoverage(v);
+
+ /* Generate code to handle collisions */
+ regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
+ if( isUpdate || onError==OE_Replace ){
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
+ /* Conflict only if the rowid of the existing index entry
+ ** is different from old-rowid */
+ if( isUpdate ){
+ sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverage(v);
+ }
+ }else{
+ int x;
+ /* Extract the PRIMARY KEY from the end of the index entry and
+ ** store it in registers regR..regR+nPk-1 */
+ if( pIdx!=pPk ){
+ for(i=0; i<pPk->nKeyCol; i++){
+ x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
+ VdbeComment((v, "%s.%s", pTab->zName,
+ pTab->aCol[pPk->aiColumn[i]].zName));
+ }
+ }
+ if( isUpdate ){
+ /* If currently processing the PRIMARY KEY of a WITHOUT ROWID
+ ** table, only conflict if the new PRIMARY KEY values are actually
+ ** different from the old.
+ **
+ ** For a UNIQUE index, only conflict if the PRIMARY KEY values
+ ** of the matched index row are different from the original PRIMARY
+ ** KEY values of this row before the update. */
+ int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
+ int op = OP_Ne;
+ int regCmp = (IsPrimaryKeyIndex(pIdx) ? regIdx : regR);
+
+ for(i=0; i<pPk->nKeyCol; i++){
+ char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
+ x = pPk->aiColumn[i];
+ if( i==(pPk->nKeyCol-1) ){
+ addrJump = addrUniqueOk;
+ op = OP_Eq;
+ }
+ sqlite3VdbeAddOp4(v, op,
+ regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ
+ );
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverageIf(v, op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Ne);
+ }
+ }
+ }
+ }
/* Generate code that executes if the new index entry is not unique */
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
@@ -1428,30 +1490,10 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- int j;
- StrAccum errMsg;
- const char *zSep;
- char *zErr;
-
- sqlite3StrAccumInit(&errMsg, 0, 0, 200);
- errMsg.db = db;
- zSep = pIdx->nColumn>1 ? "columns " : "column ";
- for(j=0; j<pIdx->nColumn; j++){
- char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
- sqlite3StrAccumAppend(&errMsg, zSep, -1);
- zSep = ", ";
- sqlite3StrAccumAppend(&errMsg, zCol, -1);
- }
- sqlite3StrAccumAppend(&errMsg,
- pIdx->nColumn>1 ? " are not unique" : " is not unique", -1);
- zErr = sqlite3StrAccumFinish(&errMsg);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_UNIQUE,
- onError, zErr, 0);
- sqlite3DbFree(errMsg.db, zErr);
+ sqlite3UniqueConstraint(pParse, onError, pIdx);
break;
}
case OE_Ignore: {
- assert( seenReplace==0 );
sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
break;
}
@@ -1462,26 +1504,29 @@ void sqlite3GenerateConstraintChecks(
if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
}
- sqlite3GenerateRowDelete(
- pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace
- );
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ regR, nPkField, 0, OE_Replace, pIdx==pPk);
seenReplace = 1;
break;
}
}
- sqlite3VdbeJumpHere(v, j3);
- sqlite3ReleaseTempReg(pParse, regR);
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
+ if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
}
-
- if( pbMayReplace ){
- *pbMayReplace = seenReplace;
+ if( ipkTop ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, ipkTop+1);
+ sqlite3VdbeJumpHere(v, ipkBottom);
}
+
+ *pbMayReplace = seenReplace;
+ VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
-** A consecutive range of registers starting at regRowid contains the
+** A consecutive range of registers starting at regNewData contains the
** rowid and the content to be inserted.
**
** The arguments to this routine should be the same as the first six
@@ -1490,36 +1535,46 @@ void sqlite3GenerateConstraintChecks(
void sqlite3CompleteInsertion(
Parse *pParse, /* The parser context */
Table *pTab, /* the table into which we are inserting */
- int baseCur, /* Index of a read/write cursor pointing at pTab */
- int regRowid, /* Range of content */
+ int iDataCur, /* Cursor of the canonical data source */
+ int iIdxCur, /* First index cursor */
+ int regNewData, /* Range of content */
int *aRegIdx, /* Register used by each index. 0 for unused indices */
int isUpdate, /* True for UPDATE, False for INSERT */
int appendBias, /* True if this is likely to be an append */
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
){
- int i;
- Vdbe *v;
- int nIdx;
- Index *pIdx;
- u8 pik_flags;
- int regData;
- int regRec;
+ Vdbe *v; /* Prepared statements under construction */
+ Index *pIdx; /* An index being inserted or updated */
+ u8 pik_flags; /* flag values passed to the btree insert */
+ int regData; /* Content registers (after the rowid) */
+ int regRec; /* Register holding assemblied record for the table */
+ int i; /* Loop counter */
+ u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
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--){
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
if( aRegIdx[i]==0 ) continue;
- sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
- if( useSeekResult ){
- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ bAffinityDone = 1;
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
}
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
+ pik_flags = 0;
+ if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ assert( pParse->nested==0 );
+ pik_flags |= OPFLAG_NCHANGE;
+ }
+ if( pik_flags ) sqlite3VdbeChangeP5(v, pik_flags);
}
- regData = regRowid + 1;
+ if( !HasRowid(pTab) ) return;
+ regData = regNewData + 1;
regRec = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
- sqlite3TableAffinityStr(v, pTab);
+ if( !bAffinityDone ) sqlite3TableAffinity(v, pTab, 0);
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
if( pParse->nested ){
pik_flags = 0;
@@ -1533,7 +1588,7 @@ void sqlite3CompleteInsertion(
if( useSeekResult ){
pik_flags |= OPFLAG_USESEEKRESULT;
}
- sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
if( !pParse->nested ){
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
}
@@ -1541,39 +1596,71 @@ void sqlite3CompleteInsertion(
}
/*
-** Generate code that will open cursors for a table and for all
-** indices of that table. The "baseCur" parameter is the cursor number used
-** for the table. Indices are opened on subsequent cursors.
+** Allocate cursors for the pTab table and all its indices and generate
+** code to open and initialized those cursors.
+**
+** The cursor for the object that contains the complete data (normally
+** the table itself, but the PRIMARY KEY index in the case of a WITHOUT
+** ROWID table) is returned in *piDataCur. The first index cursor is
+** returned in *piIdxCur. The number of indices is returned.
+**
+** Use iBase as the first cursor (either the *piDataCur for rowid tables
+** or the first index for WITHOUT ROWID tables) if it is non-negative.
+** If iBase is negative, then allocate the next available cursor.
**
-** Return the number of indices on the table.
+** For a rowid table, *piDataCur will be exactly one less than *piIdxCur.
+** For a WITHOUT ROWID table, *piDataCur will be somewhere in the range
+** of *piIdxCurs, depending on where the PRIMARY KEY index appears on the
+** pTab->pIndex list.
*/
int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
- int baseCur, /* Cursor number assigned to the table */
- int op /* OP_OpenRead or OP_OpenWrite */
+ int op, /* OP_OpenRead or OP_OpenWrite */
+ int iBase, /* Use this for the table cursor, if there is one */
+ u8 *aToOpen, /* If not NULL: boolean for each table and index */
+ int *piDataCur, /* Write the database source cursor number here */
+ int *piIdxCur /* Write the first index cursor number here */
){
int i;
int iDb;
+ int iDataCur;
Index *pIdx;
Vdbe *v;
- if( IsVirtual(pTab) ) return 0;
+ assert( op==OP_OpenRead || op==OP_OpenWrite );
+ if( IsVirtual(pTab) ){
+ assert( aToOpen==0 );
+ *piDataCur = 0;
+ *piIdxCur = 1;
+ return 0;
+ }
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
- 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 );
- sqlite3VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb,
- (char*)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
+ if( iBase<0 ) iBase = pParse->nTab;
+ iDataCur = iBase++;
+ if( piDataCur ) *piDataCur = iDataCur;
+ if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){
+ sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op);
+ }else{
+ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName);
}
- if( pParse->nTab<baseCur+i ){
- pParse->nTab = baseCur+i;
+ if( piIdxCur ) *piIdxCur = iBase;
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ int iIdxCur = iBase++;
+ assert( pIdx->pSchema==pTab->pSchema );
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) && piDataCur ){
+ *piDataCur = iIdxCur;
+ }
+ if( aToOpen==0 || aToOpen[i+1] ){
+ sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ VdbeComment((v, "%s", pIdx->zName));
+ }
}
- return i-1;
+ if( iBase>pParse->nTab ) pParse->nTab = iBase;
+ return i;
}
@@ -1612,18 +1699,19 @@ static int xferCompatibleCollation(const char *z1, const char *z2){
** * The same DESC and ASC markings occurs on all columns
** * The same onError processing (OE_Abort, OE_Ignore, etc)
** * The same collating sequence on each column
+** * The index has the exact same WHERE clause
*/
static int xferCompatibleIndex(Index *pDest, Index *pSrc){
int i;
assert( pDest && pSrc );
assert( pDest->pTable!=pSrc->pTable );
- if( pDest->nColumn!=pSrc->nColumn ){
+ if( pDest->nKeyCol!=pSrc->nKeyCol ){
return 0; /* Different number of columns */
}
if( pDest->onError!=pSrc->onError ){
return 0; /* Different conflict resolution strategies */
}
- for(i=0; i<pSrc->nColumn; i++){
+ for(i=0; i<pSrc->nKeyCol; i++){
if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){
return 0; /* Different columns indexed */
}
@@ -1634,6 +1722,9 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
return 0; /* Different collating sequences */
}
}
+ if( sqlite3ExprCompare(pSrc->pPartIdxWhere, pDest->pPartIdxWhere, -1) ){
+ return 0; /* Different WHERE clauses */
+ }
/* If no test above fails then the indices must be compatible */
return 1;
@@ -1679,10 +1770,9 @@ static int xferOptimization(
int iDbSrc; /* The database of pSrc */
int iSrc, iDest; /* Cursors from source and destination */
int addr1, addr2; /* Loop addresses */
- int emptyDestTest; /* Address of test for empty pDest */
- int emptySrcTest; /* Address of test for empty pSrc */
+ int emptyDestTest = 0; /* Address of test for empty pDest */
+ int emptySrcTest = 0; /* Address of test for empty pSrc */
Vdbe *v; /* The VDBE we are building */
- KeyInfo *pKey; /* Key information for an index */
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 */
@@ -1690,6 +1780,12 @@ static int xferOptimization(
if( pSelect==0 ){
return 0; /* Must be of the form INSERT INTO ... SELECT ... */
}
+ if( pParse->pWith || pSelect->pWith ){
+ /* Do not attempt to process this query if there are an WITH clauses
+ ** attached to it. Proceeding may generate a false "no such table: xxx"
+ ** error if pSelect reads from a CTE named "xxx". */
+ return 0;
+ }
if( sqlite3TriggerList(pParse, pDest) ){
return 0; /* tab1 must not have triggers */
}
@@ -1752,6 +1848,9 @@ static int xferOptimization(
if( pSrc==pDest ){
return 0; /* tab1 and tab2 may not be the same table */
}
+ if( HasRowid(pDest)!=HasRowid(pSrc) ){
+ return 0; /* source and destination must both be WITHOUT ROWID or not */
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pSrc->tabFlags & TF_Virtual ){
return 0; /* tab2 must not be a virtual table */
@@ -1767,18 +1866,27 @@ static int xferOptimization(
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
}
for(i=0; i<pDest->nCol; i++){
- if( pDest->aCol[i].affinity!=pSrc->aCol[i].affinity ){
+ Column *pDestCol = &pDest->aCol[i];
+ Column *pSrcCol = &pSrc->aCol[i];
+ if( pDestCol->affinity!=pSrcCol->affinity ){
return 0; /* Affinity must be the same on all columns */
}
- if( !xferCompatibleCollation(pDest->aCol[i].zColl, pSrc->aCol[i].zColl) ){
+ if( !xferCompatibleCollation(pDestCol->zColl, pSrcCol->zColl) ){
return 0; /* Collating sequence must be the same on all columns */
}
- if( pDest->aCol[i].notNull && !pSrc->aCol[i].notNull ){
+ if( pDestCol->notNull && !pSrcCol->notNull ){
return 0; /* tab2 must be NOT NULL if tab1 is */
}
+ /* Default values for second and subsequent columns need to match. */
+ if( i>0
+ && ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0)
+ || (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0))
+ ){
+ return 0; /* Default values must be the same for all columns */
+ }
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
- if( pDestIdx->onError!=OE_None ){
+ if( IsUniqueIndex(pDestIdx) ){
destHasUniqueIdx = 1;
}
for(pSrcIdx=pSrc->pIndex; pSrcIdx; pSrcIdx=pSrcIdx->pNext){
@@ -1789,7 +1897,7 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck, pDest->pCheck) ){
+ if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -1822,7 +1930,10 @@ static int xferOptimization(
iSrc = pParse->nTab++;
iDest = pParse->nTab++;
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
+ regData = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
+ assert( HasRowid(pDest) || destHasUniqueIdx );
if( (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
|| destHasUniqueIdx /* (2) */
|| (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
@@ -1841,60 +1952,60 @@ static int xferOptimization(
**
** (3) onError is something other than OE_Abort and OE_Rollback.
*/
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); VdbeCoverage(v);
emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
sqlite3VdbeJumpHere(v, addr1);
- }else{
- emptyDestTest = 0;
}
- sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
- emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
- regData = sqlite3GetTempReg(pParse);
- regRowid = sqlite3GetTempReg(pParse);
- if( pDest->iPKey>=0 ){
- addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_PRIMARYKEY,
- onError, "PRIMARY KEY must be unique", P4_STATIC);
- sqlite3VdbeJumpHere(v, addr2);
- autoIncStep(pParse, regAutoinc, regRowid);
- }else if( pDest->pIndex==0 ){
- addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
+ if( HasRowid(pSrc) ){
+ sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
+ emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
+ if( pDest->iPKey>=0 ){
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
+ VdbeCoverage(v);
+ sqlite3RowidConstraint(pParse, onError, pDest);
+ sqlite3VdbeJumpHere(v, addr2);
+ autoIncStep(pParse, regAutoinc, regRowid);
+ }else if( pDest->pIndex==0 ){
+ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
+ }else{
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ assert( (pDest->tabFlags & TF_Autoincrement)==0 );
+ }
+ 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); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
}else{
- addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- assert( (pDest->tabFlags & TF_Autoincrement)==0 );
- }
- 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);
+ sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName);
+ sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
+ }
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
}
assert( pSrcIdx );
- sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
- pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx);
- sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc,
- (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc);
+ sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx);
VdbeComment((v, "%s", pSrcIdx->zName));
- pKey = sqlite3IndexKeyinfo(pParse, pDestIdx);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest,
- (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest);
+ sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx);
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
VdbeComment((v, "%s", pDestIdx->zName));
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
- sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1);
+ sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
}
- sqlite3VdbeJumpHere(v, emptySrcTest);
+ if( emptySrcTest ) sqlite3VdbeJumpHere(v, emptySrcTest);
sqlite3ReleaseTempReg(pParse, regRowid);
sqlite3ReleaseTempReg(pParse, regData);
- sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
if( emptyDestTest ){
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
sqlite3VdbeJumpHere(v, emptyDestTest);
diff --git a/src/legacy.c b/src/legacy.c
index 94649ae..1913f0b 100644
--- a/src/legacy.c
+++ b/src/legacy.c
@@ -96,6 +96,9 @@ int sqlite3_exec(
}
}
if( xCallback(pArg, nCol, azVals, azCols) ){
+ /* EVIDENCE-OF: R-38229-40159 If the callback function to
+ ** sqlite3_exec() returns non-zero, then sqlite3_exec() will
+ ** return SQLITE_ABORT. */
rc = SQLITE_ABORT;
sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0;
diff --git a/src/loadext.c b/src/loadext.c
index cdcf6a9..828e865 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -669,6 +669,35 @@ int sqlite3_auto_extension(void (*xInit)(void)){
}
/*
+** Cancel a prior call to sqlite3_auto_extension. Remove xInit from the
+** set of routines that is invoked for each new database connection, if it
+** is currently on the list. If xInit is not on the list, then this
+** routine is a no-op.
+**
+** Return 1 if xInit was found on the list and removed. Return 0 if xInit
+** was not on the list.
+*/
+int sqlite3_cancel_auto_extension(void (*xInit)(void)){
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ int i;
+ int n = 0;
+ wsdAutoextInit;
+ sqlite3_mutex_enter(mutex);
+ for(i=wsdAutoext.nExt-1; i>=0; i--){
+ if( wsdAutoext.aExt[i]==xInit ){
+ wsdAutoext.nExt--;
+ wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt];
+ n++;
+ break;
+ }
+ }
+ sqlite3_mutex_leave(mutex);
+ return n;
+}
+
+/*
** Reset the automatic extension loading mechanism.
*/
void sqlite3_reset_auto_extension(void){
diff --git a/src/main.c b/src/main.c
index 39f6042..cea7282 100644
--- a/src/main.c
+++ b/src/main.c
@@ -117,6 +117,9 @@ char *sqlite3_data_directory = 0;
int sqlite3_initialize(void){
MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
int rc; /* Result code */
+#ifdef SQLITE_EXTRA_INIT
+ int bRunExtraInit = 0; /* Extra initialization needed */
+#endif
#ifdef SQLITE_OMIT_WSD
rc = sqlite3_wsd_init(4096, 24);
@@ -132,13 +135,6 @@ int sqlite3_initialize(void){
*/
if( sqlite3GlobalConfig.isInit ) return SQLITE_OK;
-#ifdef SQLITE_ENABLE_SQLLOG
- {
- extern void sqlite3_init_sqllog(void);
- sqlite3_init_sqllog();
- }
-#endif
-
/* Make sure the mutex subsystem is initialized. If unable to
** initialize the mutex subsystem, return early with the error.
** If the system is so sick that we are unable to allocate a mutex,
@@ -214,6 +210,9 @@ int sqlite3_initialize(void){
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
sqlite3GlobalConfig.isInit = 1;
+#ifdef SQLITE_EXTRA_INIT
+ bRunExtraInit = 1;
+#endif
}
sqlite3GlobalConfig.inProgress = 0;
}
@@ -254,7 +253,7 @@ int sqlite3_initialize(void){
** compile-time option.
*/
#ifdef SQLITE_EXTRA_INIT
- if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
+ if( bRunExtraInit ){
int SQLITE_EXTRA_INIT(const char*);
rc = SQLITE_EXTRA_INIT(0);
}
@@ -442,8 +441,8 @@ int sqlite3_config(int op, ...){
memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.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.
+ ** mem5.c/mem3.c methods. The enclosing #if guarantees at
+ ** least one of these methods is currently enabled.
*/
#ifdef SQLITE_ENABLE_MEMSYS3
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
@@ -462,7 +461,7 @@ int sqlite3_config(int op, ...){
break;
}
- /* Record a pointer to the logger funcction and its first argument.
+ /* Record a pointer to the logger function and its first argument.
** The default is NULL. Logging is disabled if the function pointer is
** NULL.
*/
@@ -509,6 +508,13 @@ int sqlite3_config(int op, ...){
break;
}
+#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC)
+ case SQLITE_CONFIG_WIN32_HEAPSIZE: {
+ sqlite3GlobalConfig.nHeap = va_arg(ap, int);
+ break;
+ }
+#endif
+
default: {
rc = SQLITE_ERROR;
break;
@@ -575,7 +581,8 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
db->lookaside.bEnabled = 1;
db->lookaside.bMalloced = pBuf==0 ?1:0;
}else{
- db->lookaside.pEnd = 0;
+ db->lookaside.pStart = db;
+ db->lookaside.pEnd = db;
db->lookaside.bEnabled = 0;
db->lookaside.bMalloced = 0;
}
@@ -701,7 +708,7 @@ static int binCollFunc(
/*
** Another built-in collating sequence: NOCASE.
**
-** This collating sequence is intended to be used for "case independant
+** This collating sequence is intended to be used for "case independent
** comparison". SQLite's knowledge of upper and lower case equivalents
** extends only to the 26 characters used in the English language.
**
@@ -793,6 +800,7 @@ static void disconnectAllVtab(sqlite3 *db){
}
}
}
+ sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
#else
UNUSED_PARAMETER(db);
@@ -819,6 +827,8 @@ static int connectionIsBusy(sqlite3 *db){
*/
static int sqlite3Close(sqlite3 *db, int forceZombie){
if( !db ){
+ /* EVIDENCE-OF: R-63257-11740 Calling sqlite3_close() or
+ ** sqlite3_close_v2() with a NULL pointer argument is a harmless no-op. */
return SQLITE_OK;
}
if( !sqlite3SafetyCheckSickOrOk(db) ){
@@ -973,9 +983,7 @@ void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
#endif
sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
- if( db->pErr ){
- sqlite3ValueFree(db->pErr);
- }
+ sqlite3ValueFree(db->pErr);
sqlite3CloseExtensions(db);
db->magic = SQLITE_MAGIC_ERROR;
@@ -1024,7 +1032,6 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
inTrans = 1;
}
sqlite3BtreeRollback(p, tripCode);
- db->aDb[i].inTrans = 0;
}
}
sqlite3VtabRollback(db);
@@ -1038,6 +1045,8 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
/* Any deferred constraint violations have now been resolved. */
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
/* If one has been configured, invoke the rollback-hook callback */
if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
@@ -1049,8 +1058,7 @@ void sqlite3RollbackAll(sqlite3 *db, int tripCode){
** Return a static string containing the name corresponding to the error code
** specified in the argument.
*/
-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \
- defined(SQLITE_DEBUG_OS_TRACE)
+#if (defined(SQLITE_DEBUG) && SQLITE_OS_WIN) || defined(SQLITE_TEST)
const char *sqlite3ErrName(int rc){
const char *zName = 0;
int i, origRc = rc;
@@ -1064,6 +1072,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
+ case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
@@ -1071,6 +1080,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
+ case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
@@ -1083,7 +1093,6 @@ const char *sqlite3ErrName(int rc){
case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break;
case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break;
case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break;
- case SQLITE_IOERR_BLOCKED: zName = "SQLITE_IOERR_BLOCKED"; break;
case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break;
case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break;
case SQLITE_IOERR_CHECKRESERVEDLOCK:
@@ -1098,6 +1107,8 @@ const char *sqlite3ErrName(int rc){
case SQLITE_IOERR_SEEK: zName = "SQLITE_IOERR_SEEK"; break;
case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break;
case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break;
+ case SQLITE_IOERR_GETTEMPPATH: zName = "SQLITE_IOERR_GETTEMPPATH"; break;
+ case SQLITE_IOERR_CONVPATH: zName = "SQLITE_IOERR_CONVPATH"; break;
case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break;
case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
@@ -1106,6 +1117,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break;
case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
+ case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
@@ -1124,6 +1136,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break;
case SQLITE_CONSTRAINT_FUNCTION:
zName = "SQLITE_CONSTRAINT_FUNCTION"; break;
+ case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break;
case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
@@ -1137,6 +1150,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_NOTICE_RECOVER_ROLLBACK:
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
+ case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
}
}
@@ -1297,7 +1311,7 @@ void sqlite3_progress_handler(
sqlite3_mutex_enter(db->mutex);
if( nOps>0 ){
db->xProgress = xProgress;
- db->nProgressOps = nOps;
+ db->nProgressOps = (unsigned)nOps;
db->pProgressArg = pArg;
}else{
db->xProgress = 0;
@@ -1350,6 +1364,7 @@ int sqlite3CreateFunc(
){
FuncDef *p;
int nName;
+ int extraFlags;
assert( sqlite3_mutex_held(db->mutex) );
if( zFunctionName==0 ||
@@ -1360,6 +1375,10 @@ int sqlite3CreateFunc(
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
return SQLITE_MISUSE_BKPT;
}
+
+ assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
+ extraFlags = enc & SQLITE_DETERMINISTIC;
+ enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
#ifndef SQLITE_OMIT_UTF16
/* If SQLITE_UTF16 is specified as the encoding type, transform this
@@ -1373,10 +1392,10 @@ int sqlite3CreateFunc(
enc = SQLITE_UTF16NATIVE;
}else if( enc==SQLITE_ANY ){
int rc;
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
pUserData, xFunc, xStep, xFinal, pDestructor);
if( rc==SQLITE_OK ){
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
pUserData, xFunc, xStep, xFinal, pDestructor);
}
if( rc!=SQLITE_OK ){
@@ -1394,8 +1413,8 @@ int sqlite3CreateFunc(
** operation to continue but invalidate all precompiled statements.
*/
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
- if( p && p->iPrefEnc==enc && p->nArg==nArg ){
- if( db->activeVdbeCnt ){
+ if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
@@ -1419,7 +1438,8 @@ int sqlite3CreateFunc(
pDestructor->nRef++;
}
p->pDestructor = pDestructor;
- p->flags = 0;
+ p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
+ testcase( p->funcFlags & SQLITE_DETERMINISTIC );
p->xFunc = xFunc;
p->xStep = xStep;
p->xFinalize = xFinal;
@@ -1849,6 +1869,7 @@ const char *sqlite3_errmsg(sqlite3 *db){
if( db->mallocFailed ){
z = sqlite3ErrStr(SQLITE_NOMEM);
}else{
+ testcase( db->pErr==0 );
z = (char*)sqlite3_value_text(db->pErr);
assert( !db->mallocFailed );
if( z==0 ){
@@ -1890,8 +1911,7 @@ const void *sqlite3_errmsg16(sqlite3 *db){
}else{
z = sqlite3_value_text16(db->pErr);
if( z==0 ){
- sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
- SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3Error(db, db->errCode, sqlite3ErrStr(db->errCode));
z = sqlite3_value_text16(db->pErr);
}
/* A malloc() may have failed within the call to sqlite3_value_text16()
@@ -1939,6 +1959,32 @@ const char *sqlite3_errstr(int rc){
}
/*
+** Invalidate all cached KeyInfo objects for database connection "db"
+*/
+static void invalidateCachedKeyInfo(sqlite3 *db){
+ Db *pDb; /* A single database */
+ int iDb; /* The database index number */
+ HashElem *k; /* For looping over tables in pDb */
+ Table *pTab; /* A table in the database */
+ Index *pIdx; /* Each index */
+
+ for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){
+ if( pDb->pBt==0 ) continue;
+ sqlite3BtreeEnter(pDb->pBt);
+ for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){
+ pTab = (Table*)sqliteHashData(k);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->pKeyInfo && pIdx->pKeyInfo->db==db ){
+ sqlite3KeyInfoUnref(pIdx->pKeyInfo);
+ pIdx->pKeyInfo = 0;
+ }
+ }
+ }
+ sqlite3BtreeLeave(pDb->pBt);
+ }
+}
+
+/*
** Create a new collating function for database "db". The name is zName
** and the encoding is enc.
*/
@@ -1976,12 +2022,13 @@ static int createCollation(
*/
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0);
if( pColl && pColl->xCmp ){
- if( db->activeVdbeCnt ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
}
sqlite3ExpirePreparedStatements(db);
+ invalidateCachedKeyInfo(db);
/* If collation sequence pColl was created directly by a call to
** sqlite3_create_collation, and not generated by synthCollSeq(),
@@ -2030,7 +2077,7 @@ static const int aHardLimit[] = {
SQLITE_MAX_FUNCTION_ARG,
SQLITE_MAX_ATTACHED,
SQLITE_MAX_LIKE_PATTERN_LENGTH,
- SQLITE_MAX_VARIABLE_NUMBER,
+ SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
SQLITE_MAX_TRIGGER_DEPTH,
};
@@ -2055,8 +2102,8 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
#endif
-#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62
-# error SQLITE_MAX_ATTACHED must be between 0 and 62
+#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
+# error SQLITE_MAX_ATTACHED must be between 0 and 125
#endif
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
@@ -2174,20 +2221,20 @@ int sqlite3ParseUri(
zFile = sqlite3_malloc(nByte);
if( !zFile ) return SQLITE_NOMEM;
+ iIn = 5;
+#ifndef SQLITE_ALLOW_URI_AUTHORITY
/* Discard the scheme and authority segments of the URI. */
if( zUri[5]=='/' && zUri[6]=='/' ){
iIn = 7;
while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
-
if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
*pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
iIn-7, &zUri[7]);
rc = SQLITE_ERROR;
goto parse_uri_out;
}
- }else{
- iIn = 5;
}
+#endif
/* Copy the filename and any query parameters into the zFile buffer.
** Decode %HH escape codes along the way.
@@ -2451,7 +2498,10 @@ static int openDatabase(
db->nextAutovac = -1;
db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
- db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger
+ db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
+#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
+ | SQLITE_AutoIndex
+#endif
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
#endif
@@ -2575,8 +2625,6 @@ static int openDatabase(
}
#endif
- sqlite3Error(db, rc, 0);
-
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
** mode. Doing nothing at all also makes NORMAL the default.
@@ -2587,6 +2635,8 @@ static int openDatabase(
SQLITE_DEFAULT_LOCKING_MODE);
#endif
+ if( rc ) sqlite3Error(db, rc, 0);
+
/* Enable the lookaside-malloc subsystem */
setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
sqlite3GlobalConfig.nLookaside);
@@ -2791,8 +2841,6 @@ int sqlite3_global_recover(void){
** 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.
-**
-******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
*/
int sqlite3_get_autocommit(sqlite3 *db){
return db->autoCommit;
@@ -3048,7 +3096,7 @@ int sqlite3_test_control(int op, ...){
** to the xRandomness method of the default VFS.
*/
case SQLITE_TESTCTRL_PRNG_RESET: {
- sqlite3PrngResetState();
+ sqlite3_randomness(0,0);
break;
}
@@ -3068,6 +3116,28 @@ int sqlite3_test_control(int op, ...){
}
/*
+ ** sqlite3_test_control(FAULT_INSTALL, xCallback)
+ **
+ ** Arrange to invoke xCallback() whenever sqlite3FaultSim() is called,
+ ** if xCallback is not NULL.
+ **
+ ** As a test of the fault simulator mechanism itself, sqlite3FaultSim(0)
+ ** is called immediately after installing the new callback and the return
+ ** value from sqlite3FaultSim(0) becomes the return from
+ ** sqlite3_test_control().
+ */
+ case SQLITE_TESTCTRL_FAULT_INSTALL: {
+ /* MSVC is picky about pulling func ptrs from va lists.
+ ** http://support.microsoft.com/kb/47961
+ ** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int));
+ */
+ typedef int(*TESTCALLBACKFUNC_t)(int);
+ sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
+ rc = sqlite3FaultSim(0);
+ break;
+ }
+
+ /*
** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
**
** Register hooks to call to indicate which malloc() failures
@@ -3158,6 +3228,22 @@ int sqlite3_test_control(int op, ...){
break;
}
+ /*
+ ** sqlite3_test_control(SQLITE_TESTCTRL_BYTEORDER);
+ **
+ ** The integer returned reveals the byte-order of the computer on which
+ ** SQLite is running:
+ **
+ ** 1 big-endian, determined at run-time
+ ** 10 little-endian, determined at run-time
+ ** 432101 big-endian, determined at compile-time
+ ** 123410 little-endian, determined at compile-time
+ */
+ case SQLITE_TESTCTRL_BYTEORDER: {
+ rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
+ break;
+ }
+
/* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
**
** Set the nReserve size to N for the main database on the database
@@ -3248,6 +3334,44 @@ int sqlite3_test_control(int op, ...){
}
#endif
+ /* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int);
+ **
+ ** Set or clear a flag that indicates that the database file is always well-
+ ** formed and never corrupt. This flag is clear by default, indicating that
+ ** database files might have arbitrary corruption. Setting the flag during
+ ** testing causes certain assert() statements in the code to be activated
+ ** that demonstrat invariants on well-formed database files.
+ */
+ case SQLITE_TESTCTRL_NEVER_CORRUPT: {
+ sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int);
+ break;
+ }
+
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
+ **
+ ** Set the VDBE coverage callback function to xCallback with context
+ ** pointer ptr.
+ */
+ case SQLITE_TESTCTRL_VDBE_COVERAGE: {
+#ifdef SQLITE_VDBE_COVERAGE
+ typedef void (*branch_callback)(void*,int,u8,u8);
+ sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback);
+ sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*);
+#endif
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_ISINIT);
+ **
+ ** Return SQLITE_OK if SQLite has been initialized and SQLITE_ERROR if
+ ** not.
+ */
+ case SQLITE_TESTCTRL_ISINIT: {
+ if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
+ break;
+ }
+
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */
@@ -3296,7 +3420,7 @@ sqlite3_int64 sqlite3_uri_int64(
){
const char *z = sqlite3_uri_parameter(zFilename, zParam);
sqlite3_int64 v;
- if( z && sqlite3Atoi64(z, &v, sqlite3Strlen30(z), SQLITE_UTF8)==SQLITE_OK ){
+ if( z && sqlite3DecOrHexToI64(z, &v)==SQLITE_OK ){
bDflt = v;
}
return bDflt;
@@ -3332,5 +3456,5 @@ const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
*/
int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
Btree *pBt = sqlite3DbNameToBtree(db, zDbName);
- return pBt ? sqlite3PagerIsreadonly(sqlite3BtreePager(pBt)) : -1;
+ return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
}
diff --git a/src/malloc.c b/src/malloc.c
index 35a44e5..9c11d07 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -433,7 +433,7 @@ void sqlite3ScratchFree(void *p){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, void *p){
- return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+ return p>=db->lookaside.pStart && p<db->lookaside.pEnd;
}
#else
#define isLookaside(A,B) 0
@@ -449,8 +449,9 @@ int sqlite3MallocSize(void *p){
return sqlite3GlobalConfig.m.xSize(p);
}
int sqlite3DbMallocSize(sqlite3 *db, void *p){
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- if( db && isLookaside(db, p) ){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( isLookaside(db, p) ){
return db->lookaside.sz;
}else{
assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
@@ -484,6 +485,7 @@ void sqlite3_free(void *p){
*/
void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
+ if( p==0 ) return;
if( db ){
if( db->pnBytesFreed ){
*db->pnBytesFreed += sqlite3DbMallocSize(db, p);
diff --git a/src/mem1.c b/src/mem1.c
index 3578496..6dbf105 100644
--- a/src/mem1.c
+++ b/src/mem1.c
@@ -49,16 +49,6 @@
** macros.
*/
#ifdef SQLITE_SYSTEM_MALLOC
-
-/*
-** The MSVCRT has malloc_usable_size() but it is called _msize().
-** The use of _msize() is automatic, but can be disabled by compiling
-** with -DSQLITE_WITHOUT_MSIZE
-*/
-#if defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
-# define SQLITE_MALLOCSIZE _msize
-#endif
-
#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
/*
@@ -81,22 +71,48 @@ static malloc_zone_t* _sqliteZone_;
** Use standard C library malloc and free on non-Apple systems.
** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
*/
-#define SQLITE_MALLOC(x) malloc(x)
-#define SQLITE_FREE(x) free(x)
-#define SQLITE_REALLOC(x,y) realloc((x),(y))
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
-#if (defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)) \
- || (defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE))
-# include <malloc.h> /* Needed for malloc_usable_size on linux */
-#endif
-#ifdef HAVE_MALLOC_USABLE_SIZE
-# ifndef SQLITE_MALLOCSIZE
-# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
-# endif
-#else
-# undef SQLITE_MALLOCSIZE
+/*
+** The malloc.h header file is needed for malloc_usable_size() function
+** on some systems (e.g. Linux).
+*/
+#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLOC_USABLE_SIZE)
+# define SQLITE_USE_MALLOC_H
+# define SQLITE_USE_MALLOC_USABLE_SIZE
+/*
+** The MSVCRT has malloc_usable_size(), but it is called _msize(). The
+** use of _msize() is automatic, but can be disabled by compiling with
+** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
+** the malloc.h header file.
+*/
+#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
+# define SQLITE_USE_MALLOC_H
+# define SQLITE_USE_MSIZE
#endif
+/*
+** Include the malloc.h header file, if necessary. Also set define macro
+** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize()
+** for MSVC and malloc_usable_size() for most other systems (e.g. Linux).
+** The memory size function can always be overridden manually by defining
+** the macro SQLITE_MALLOCSIZE to the desired function name.
+*/
+#if defined(SQLITE_USE_MALLOC_H)
+# include <malloc.h>
+# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
+# endif
+# elif defined(SQLITE_USE_MSIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE _msize
+# endif
+# endif
+#endif /* defined(SQLITE_USE_MALLOC_H) */
+
#endif /* __APPLE__ or not __APPLE__ */
/*
diff --git a/src/mem2.c b/src/mem2.c
index 26448ea..99ea425 100644
--- a/src/mem2.c
+++ b/src/mem2.c
@@ -179,7 +179,7 @@ static int sqlite3MemSize(void *p){
return 0;
}
pHdr = sqlite3MemsysGetHeader(p);
- return pHdr->iSize;
+ return (int)pHdr->iSize;
}
/*
@@ -221,7 +221,7 @@ static void randomFill(char *pBuf, int nByte){
x = SQLITE_PTR_TO_INT(pBuf);
y = nByte | 1;
while( nByte >= 4 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(int*)pBuf = r;
@@ -229,7 +229,7 @@ static void randomFill(char *pBuf, int nByte){
nByte -= 4;
}
while( nByte-- > 0 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(pBuf++) = r & 0xff;
@@ -324,9 +324,9 @@ static void sqlite3MemFree(void *pPrior){
}
z = (char*)pBt;
z -= pHdr->nTitle;
- adjustStats(pHdr->iSize, -1);
+ adjustStats((int)pHdr->iSize, -1);
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
- pHdr->iSize + sizeof(int) + pHdr->nTitle);
+ (int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
free(z);
sqlite3_mutex_leave(mem.mutex);
}
@@ -348,9 +348,9 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3MemMalloc(nByte);
if( pNew ){
- memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
+ memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
if( nByte>pOldHdr->iSize ){
- randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize);
+ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
}
sqlite3MemFree(pPrior);
}
@@ -465,7 +465,7 @@ void sqlite3MemdebugSync(){
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
void **pBt = (void**)pHdr;
pBt -= pHdr->nBacktraceSlots;
- mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
+ mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
}
}
diff --git a/src/mem5.c b/src/mem5.c
index 783cef6..67615bb 100644
--- a/src/mem5.c
+++ b/src/mem5.c
@@ -130,13 +130,13 @@ static SQLITE_WSD struct Mem5Global {
} mem5;
/*
-** Access the static variable through a macro for SQLITE_OMIT_WSD
+** Access the static variable through a macro for SQLITE_OMIT_WSD.
*/
#define mem5 GLOBAL(struct Mem5Global, mem5)
/*
** Assuming mem5.zPool is divided up into an array of Mem5Link
-** structures, return a pointer to the idx-th such lik.
+** structures, return a pointer to the idx-th such link.
*/
#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
@@ -202,7 +202,7 @@ static void memsys5Leave(void){
static int memsys5Size(void *p){
int iSize = 0;
if( p ){
- int i = ((u8 *)p-mem5.zPool)/mem5.szAtom;
+ int i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
assert( i>=0 && i<mem5.nBlock );
iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
}
@@ -210,29 +210,10 @@ static int memsys5Size(void *p){
}
/*
-** 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. Return NULL if nBytes==0.
**
-** The caller guarantees that nByte positive.
+** The caller guarantees that nByte is positive.
**
** The caller has obtained a mutex prior to invoking this
** routine so there is never any chance that two or more
@@ -267,13 +248,14 @@ static void *memsys5MallocUnsafe(int nByte){
** 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++){}
+ for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){}
if( iBin>LOGMAX ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
return 0;
}
- i = memsys5UnlinkFirst(iBin);
+ i = mem5.aiFreelist[iBin];
+ memsys5Unlink(i, iBin);
while( iBin>iLogsize ){
int newSize;
@@ -293,6 +275,12 @@ static void *memsys5MallocUnsafe(int nByte){
if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
+#ifdef SQLITE_DEBUG
+ /* Make sure the allocated memory does not assume that it is set to zero
+ ** or retains a value from a previous allocation */
+ memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz);
+#endif
+
/* Return a pointer to the allocated memory. */
return (void*)&mem5.zPool[i*mem5.szAtom];
}
@@ -307,7 +295,7 @@ static void memsys5FreeUnsafe(void *pOld){
/* Set iBlock to the index of the block pointed to by pOld in
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
*/
- iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom;
+ iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
/* Check that the pointer pOld points to a valid, non-free block. */
assert( iBlock>=0 && iBlock<mem5.nBlock );
@@ -350,11 +338,18 @@ static void memsys5FreeUnsafe(void *pOld){
}
size *= 2;
}
+
+#ifdef SQLITE_DEBUG
+ /* Overwrite freed memory with the 0x55 bit pattern to verify that it is
+ ** not used after being freed */
+ memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size);
+#endif
+
memsys5Link(iBlock, iLogsize);
}
/*
-** Allocate nBytes of memory
+** Allocate nBytes of memory.
*/
static void *memsys5Malloc(int nBytes){
sqlite3_int64 *p = 0;
diff --git a/src/memjournal.c b/src/memjournal.c
index 0572594..65ed378 100644
--- a/src/memjournal.c
+++ b/src/memjournal.c
@@ -31,12 +31,6 @@ typedef struct FileChunk FileChunk;
*/
#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
/*
** The rollback journal is composed of a linked list of these structures.
*/
diff --git a/src/mutex.c b/src/mutex.c
index b567e7c..bad5a7c 100644
--- a/src/mutex.c
+++ b/src/mutex.c
@@ -81,7 +81,7 @@ int sqlite3MutexEnd(void){
*/
sqlite3_mutex *sqlite3_mutex_alloc(int id){
#ifndef SQLITE_OMIT_AUTOINIT
- if( sqlite3_initialize() ) return 0;
+ if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
#endif
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
diff --git a/src/mutex_noop.c b/src/mutex_noop.c
index 456e82a..1a900c2 100644
--- a/src/mutex_noop.c
+++ b/src/mutex_noop.c
@@ -107,7 +107,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
- static sqlite3_debug_mutex aStatic[6];
+ static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1];
sqlite3_debug_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index eca7295..c866314 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -96,10 +96,13 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -133,6 +136,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
sqlite3_mutex *p;
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index 27d10af..218342d 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -9,13 +9,25 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement mutexes for win32
+** This file contains the C functions that implement mutexes for Win32.
*/
#include "sqliteInt.h"
+#if SQLITE_OS_WIN
+/*
+** Include code that is common to all os_*.c files
+*/
+#include "os_common.h"
+
+/*
+** Include the header file for the Windows VFS.
+*/
+#include "os_win.h"
+#endif
+
/*
** The code in this file is only used if we are compiling multithreaded
-** on a win32 system.
+** on a Win32 system.
*/
#ifdef SQLITE_MUTEX_W32
@@ -28,48 +40,22 @@ struct sqlite3_mutex {
#ifdef SQLITE_DEBUG
volatile int nRef; /* Number of enterances */
volatile DWORD owner; /* Thread holding this mutex */
- int trace; /* True to trace changes */
+ volatile int trace; /* True to trace changes */
#endif
};
-#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
-#endif
/*
-** 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.
-**
-** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
-** which is only available if your application was compiled with
-** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
-** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
-** this out as well.
+** These are the initializer values used when declaring a "static" mutex
+** on Win32. It should be noted that all mutexes require initialization
+** on the Win32 platform.
*/
-#if 0
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
-# define mutexIsNT() (1)
+#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
+
+#ifdef SQLITE_DEBUG
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
+ 0L, (DWORD)0, 0 }
#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 */
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
#endif
#ifdef SQLITE_DEBUG
@@ -80,20 +66,24 @@ struct sqlite3_mutex {
static int winMutexHeld(sqlite3_mutex *p){
return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
+
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
return p->nRef==0 || p->owner!=tid;
}
+
static int winMutexNotheld(sqlite3_mutex *p){
- DWORD tid = GetCurrentThreadId();
+ DWORD tid = GetCurrentThreadId();
return winMutexNotheld2(p, tid);
}
#endif
-
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static sqlite3_mutex winMutex_staticMutexes[6] = {
+static sqlite3_mutex winMutex_staticMutexes[] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
@@ -101,17 +91,20 @@ static sqlite3_mutex winMutex_staticMutexes[6] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
+
static int winMutex_isInit = 0;
-/* As winMutexInit() and winMutexEnd() are called as part
-** of the sqlite3_initialize and sqlite3_shutdown()
-** processing, the "interlocked" magic is probably not
-** strictly necessary.
+static int winMutex_isNt = -1; /* <0 means "need to query" */
+
+/* As the winMutexInit() and winMutexEnd() functions are called as part
+** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
+** "interlocked" magic used here is probably not strictly necessary.
*/
-static long winMutex_lock = 0;
+static LONG volatile winMutex_lock = 0;
+int sqlite3_win32_is_nt(void); /* os_win.c */
void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
-static int winMutexInit(void){
+static int winMutexInit(void){
/* The first to increment to 1 does actual initialization */
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
int i;
@@ -124,16 +117,17 @@ static int winMutexInit(void){
}
winMutex_isInit = 1;
}else{
- /* Someone else is in the process of initing the static mutexes */
+ /* Another thread is (in the process of) initializing the static
+ ** mutexes */
while( !winMutex_isInit ){
sqlite3_win32_sleep(1);
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
-static int winMutexEnd(void){
- /* The first to decrement to 0 does actual shutdown
+static int winMutexEnd(void){
+ /* The first to decrement to 0 does actual shutdown
** (which should be the last to shutdown.) */
if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
if( winMutex_isInit==1 ){
@@ -144,7 +138,7 @@ static int winMutexEnd(void){
winMutex_isInit = 0;
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
/*
@@ -159,10 +153,13 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -185,7 +182,7 @@ static int winMutexEnd(void){
**
** 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
+** 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.
*/
@@ -196,9 +193,12 @@ static sqlite3_mutex *winMutexAlloc(int iType){
case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE: {
p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
+ if( p ){
#ifdef SQLITE_DEBUG
p->id = iType;
+#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
+ p->trace = 1;
+#endif
#endif
#if SQLITE_OS_WINRT
InitializeCriticalSectionEx(&p->mutex, 0, 0);
@@ -209,12 +209,15 @@ static sqlite3_mutex *winMutexAlloc(int iType){
break;
}
default: {
- assert( winMutex_isInit==1 );
assert( iType-2 >= 0 );
assert( iType-2 < ArraySize(winMutex_staticMutexes) );
+ assert( winMutex_isInit==1 );
p = &winMutex_staticMutexes[iType-2];
#ifdef SQLITE_DEBUG
p->id = iType;
+#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
+ p->trace = 1;
+#endif
#endif
break;
}
@@ -230,8 +233,11 @@ static sqlite3_mutex *winMutexAlloc(int iType){
*/
static void winMutexFree(sqlite3_mutex *p){
assert( p );
+#ifdef SQLITE_DEBUG
assert( p->nRef==0 && p->owner==0 );
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
+#endif
+ assert( winMutex_isInit==1 );
DeleteCriticalSection(&p->mutex);
sqlite3_free(p);
}
@@ -248,30 +254,39 @@ static void winMutexFree(sqlite3_mutex *p){
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
+#endif
#ifdef SQLITE_DEBUG
- DWORD tid = GetCurrentThreadId();
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
+#else
+ assert( p );
#endif
+ assert( winMutex_isInit==1 );
EnterCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
assert( p->nRef>0 || p->owner==0 );
- p->owner = tid;
+ p->owner = tid;
p->nRef++;
if( p->trace ){
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
+
static int winMutexTry(sqlite3_mutex *p){
-#ifndef NDEBUG
- DWORD tid = GetCurrentThreadId();
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
#endif
int rc = SQLITE_BUSY;
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
/*
** 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.
+ ** fail.
**
** The TryEnterCriticalSection() interface is only available on WinNT.
** And some windows compilers complain if you try to use it without
@@ -279,18 +294,27 @@ static int winMutexTry(sqlite3_mutex *p){
** For that reason, we will omit this optimization for now. See
** ticket #2685.
*/
-#if 0
- if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
+ assert( winMutex_isInit==1 );
+ assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
+ if( winMutex_isNt<0 ){
+ winMutex_isNt = sqlite3_win32_is_nt();
+ }
+ assert( winMutex_isNt==0 || winMutex_isNt==1 );
+ if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
+#ifdef SQLITE_DEBUG
p->owner = tid;
p->nRef++;
+#endif
rc = SQLITE_OK;
}
#else
UNUSED_PARAMETER(p);
#endif
#ifdef SQLITE_DEBUG
- if( rc==SQLITE_OK && p->trace ){
- printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ if( p->trace ){
+ OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
+ tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
}
#endif
return rc;
@@ -303,18 +327,23 @@ static int winMutexTry(sqlite3_mutex *p){
** is not currently allocated. SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){
-#ifndef NDEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
+#endif
+ assert( p );
+#ifdef SQLITE_DEBUG
assert( p->nRef>0 );
assert( p->owner==tid );
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
+ assert( winMutex_isInit==1 );
LeaveCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ){
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
@@ -336,7 +365,7 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
0
#endif
};
-
return &sMutex;
}
+
#endif /* SQLITE_MUTEX_W32 */
diff --git a/src/os.c b/src/os.c
index be2ea4c..b6c28a1 100644
--- a/src/os.c
+++ b/src/os.c
@@ -107,7 +107,21 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
** routine has no return value since the return value would be meaningless.
*/
int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
- DO_OS_MALLOC_TEST(id);
+#ifdef SQLITE_TEST
+ if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
+ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
+ ** is using a regular VFS, it is called after the corresponding
+ ** transaction has been committed. Injecting a fault at this point
+ ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
+ ** but the transaction is committed anyway.
+ **
+ ** The core must call OsFileControl() though, not OsFileControlHint(),
+ ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
+ ** means the commit really has failed and an error should be returned
+ ** to the user. */
+ DO_OS_MALLOC_TEST(id);
+ }
+#endif
return id->pMethods->xFileControl(id, op, pArg);
}
void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
diff --git a/src/os.h b/src/os.h
index 070a2dd..3920a62 100644
--- a/src/os.h
+++ b/src/os.h
@@ -21,83 +21,10 @@
#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, and SQLITE_OS_OTHER
-** will defined to either 1 or 0. One of the four will be 1. The other
-** three will be 0.
+** Attempt to automatically detect the operating system and setup the
+** necessary pre-processor macros for it.
*/
-#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
-# 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
-# else
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 1
-# endif
-# else
-# define SQLITE_OS_UNIX 0
-# endif
-#else
-# ifndef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# endif
-#endif
-
-#if SQLITE_OS_WIN
-# include <windows.h>
-#endif
-
-/*
-** Determine if we are dealing with Windows NT.
-**
-** We ought to be able to determine if we are compiling for win98 or winNT
-** using the _WIN32_WINNT macro as follows:
-**
-** #if defined(_WIN32_WINNT)
-** # define SQLITE_OS_WINNT 1
-** #else
-** # define SQLITE_OS_WINNT 0
-** #endif
-**
-** However, vs2005 does not set _WIN32_WINNT by default, as it ought to,
-** so the above test does not work. We'll just assume that everything is
-** winNT unless the programmer explicitly says otherwise by setting
-** SQLITE_OS_WINNT to 0.
-*/
-#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
-# define SQLITE_OS_WINNT 1
-#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
-
-/*
-** Determine if we are dealing with WinRT, which provides only a subset of
-** the full Win32 API.
-*/
-#if !defined(SQLITE_OS_WINRT)
-# define SQLITE_OS_WINRT 0
-#endif
+#include "os_setup.h"
/* If the SET_FULLSYNC macro is not defined above, then make it
** a no-op
diff --git a/src/os_setup.h b/src/os_setup.h
new file mode 100644
index 0000000..68de144
--- /dev/null
+++ b/src/os_setup.h
@@ -0,0 +1,57 @@
+/*
+** 2013 November 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 pre-processor directives related to operating system
+** detection and/or setup.
+*/
+#ifndef _OS_SETUP_H_
+#define _OS_SETUP_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, and SQLITE_OS_OTHER will defined to either 1 or 0. One of
+** the three will be 1. The other two will be 0.
+*/
+#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
+# 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
+# else
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 1
+# endif
+# else
+# define SQLITE_OS_UNIX 0
+# endif
+#else
+# ifndef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# endif
+#endif
+
+#endif /* _OS_SETUP_H_ */
diff --git a/src/os_unix.c b/src/os_unix.c
index abc23a4..b1a0bed 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -46,13 +46,6 @@
#include "sqliteInt.h"
#if SQLITE_OS_UNIX /* This file is used on unix only */
-/* Use posix_fallocate() if it is available
-*/
-#if !defined(HAVE_POSIX_FALLOCATE) \
- && (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
-# define HAVE_POSIX_FALLOCATE 1
-#endif
-
/*
** There are various methods for file locking used for concurrency
** control:
@@ -91,32 +84,6 @@
#endif
/*
-** These #defines should enable >2GB file support on Posix if the
-** underlying operating system supports it. If the OS lacks
-** large file support, these should be no-ops.
-**
-** 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
-** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
-** without this option, LFS is enable. But LFS does not exist in the kernel
-** in RedHat 6.0, so the code won't work. Hence, for maximum binary
-** portability you should omit LFS.
-**
-** The previous paragraph was written in 2005. (This paragraph is written
-** on 2008-11-28.) These days, all Linux kernels support large files, so
-** you should probably leave LFS enabled. But some embedded platforms might
-** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
-*/
-#ifndef SQLITE_DISABLE_LFS
-# define _LARGE_FILE 1
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-# endif
-# define _LARGEFILE_SOURCE 1
-#endif
-
-/*
** standard include files.
*/
#include <sys/types.h>
@@ -127,11 +94,10 @@
#include <sys/time.h>
#include <errno.h>
#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
-#include <sys/mman.h>
+# include <sys/mman.h>
#endif
-
-#if SQLITE_ENABLE_LOCKING_STYLE
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
# include <sys/ioctl.h>
# if OS_VXWORKS
# include <semaphore.h>
@@ -225,11 +191,13 @@ struct unixFile {
const char *zPath; /* Name of the file */
unixShm *pShm; /* Shared memory segment information */
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
+#if SQLITE_MAX_MMAP_SIZE>0
int nFetchOut; /* Number of outstanding xFetch refs */
sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
void *pMapRegion; /* Memory mapped region */
+#endif
#ifdef __QNXNTO__
int sectorSize; /* Device sector size */
int deviceCharacteristics; /* Precomputed device characteristics */
@@ -265,6 +233,12 @@ struct unixFile {
#endif
};
+/* This variable holds the process id (pid) from when the xRandomness()
+** method was called. If xOpen() is called from a different process id,
+** indicating that a fork() has occurred, the PRNG will be reset.
+*/
+static int randomnessPid = 0;
+
/*
** Allowed values for the unixFile.ctrlFlags bitmask:
*/
@@ -343,11 +317,16 @@ static int posixOpen(const char *zFile, int flags, int mode){
** we are not running as root.
*/
static int posixFchown(int fd, uid_t uid, gid_t gid){
+#if OS_VXWORKS
+ return 0;
+#else
return geteuid() ? 0 : fchown(fd,uid,gid);
+#endif
}
/* Forward reference */
static int openDirectory(const char*, int*);
+static int unixGetpagesize(void);
/*
** Many system calls are accessed through pointer-to-functions so that
@@ -398,7 +377,7 @@ static struct unix_syscall {
{ "read", (sqlite3_syscall_ptr)read, 0 },
#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent)
-#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
+#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
{ "pread", (sqlite3_syscall_ptr)pread, 0 },
#else
{ "pread", (sqlite3_syscall_ptr)0, 0 },
@@ -415,7 +394,7 @@ static struct unix_syscall {
{ "write", (sqlite3_syscall_ptr)write, 0 },
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
-#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE
+#if defined(USE_PREAD) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
{ "pwrite", (sqlite3_syscall_ptr)pwrite, 0 },
#else
{ "pwrite", (sqlite3_syscall_ptr)0, 0 },
@@ -456,6 +435,7 @@ static struct unix_syscall {
{ "fchown", (sqlite3_syscall_ptr)posixFchown, 0 },
#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
{ "mmap", (sqlite3_syscall_ptr)mmap, 0 },
#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent)
@@ -468,6 +448,10 @@ static struct unix_syscall {
{ "mremap", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent)
+ { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
+#define osGetpagesize ((int(*)(void))aSyscall[24].pCurrent)
+
+#endif
}; /* End of the overrideable system calls */
@@ -555,6 +539,15 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
}
/*
+** Do not accept any file descriptor less than this value, in order to avoid
+** opening database file using file descriptors that are commonly used for
+** standard input, output, and error.
+*/
+#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
+# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
+#endif
+
+/*
** Invoke open(). Do so multiple times, until it either succeeds or
** fails for some reason other than EINTR.
**
@@ -574,13 +567,23 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
static int robust_open(const char *z, int f, mode_t m){
int fd;
mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
- do{
+ while(1){
#if defined(O_CLOEXEC)
fd = osOpen(z,f|O_CLOEXEC,m2);
#else
fd = osOpen(z,f,m2);
#endif
- }while( fd<0 && errno==EINTR );
+ if( fd<0 ){
+ if( errno==EINTR ) continue;
+ break;
+ }
+ if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ osClose(fd);
+ sqlite3_log(SQLITE_WARNING,
+ "attempt to open \"%s\" as file descriptor %d", z, fd);
+ fd = -1;
+ if( osOpen("/dev/null", f, m)<0 ) break;
+ }
if( fd>=0 ){
if( m!=0 ){
struct stat statbuf;
@@ -761,16 +764,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
case EPERM:
return SQLITE_PERM;
- /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
- ** this module never makes such a call. And the code in SQLite itself
- ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
- ** this case is also commented out. If the system does set errno to EDEADLK,
- ** the default SQLITE_IOERR_XXX code will be returned. */
-#if 0
- case EDEADLK:
- return SQLITE_IOERR_BLOCKED;
-#endif
-
#if EOPNOTSUPP!=ENOTSUP
case EOPNOTSUPP:
/* something went terribly awry, unless during file system support
@@ -1299,6 +1292,19 @@ static int findInodeInfo(
return SQLITE_OK;
}
+/*
+** Return TRUE if pFile has been renamed or unlinked since it was first opened.
+*/
+static int fileHasMoved(unixFile *pFile){
+#if OS_VXWORKS
+ return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId;
+#else
+ struct stat buf;
+ return pFile->pInode!=0 &&
+ (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
+#endif
+}
+
/*
** Check a unixFile that is a database. Verify the following:
@@ -1333,10 +1339,7 @@ static void verifyDbFile(unixFile *pFile){
pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
}
- if( pFile->pInode!=0
- && ((rc = osStat(pFile->zPath, &buf))!=0
- || buf.st_ino!=pFile->pInode->fileId.ino)
- ){
+ if( fileHasMoved(pFile) ){
sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
pFile->ctrlFlags |= UNIXFILE_WARNED;
return;
@@ -1874,12 +1877,16 @@ end_unlock:
** the requested locking level, this routine is a no-op.
*/
static int unixUnlock(sqlite3_file *id, int eFileLock){
+#if SQLITE_MAX_MMAP_SIZE>0
assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
+#endif
return posixUnlock(id, eFileLock, 0);
}
+#if SQLITE_MAX_MMAP_SIZE>0
static int unixMapfile(unixFile *pFd, i64 nByte);
static void unixUnmapfile(unixFile *pFd);
+#endif
/*
** This function performs the parts of the "close file" operation
@@ -1893,7 +1900,9 @@ static void unixUnmapfile(unixFile *pFd);
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
+#if SQLITE_MAX_MMAP_SIZE>0
unixUnmapfile(pFile);
+#endif
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
pFile->h = -1;
@@ -1907,6 +1916,13 @@ static int closeUnixFile(sqlite3_file *id){
pFile->pId = 0;
}
#endif
+#ifdef SQLITE_UNLINK_AFTER_CLOSE
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){
+ osUnlink(pFile->zPath);
+ sqlite3_free(*(char**)&pFile->zPath);
+ pFile->zPath = 0;
+ }
+#endif
OSTRACE(("CLOSE %-3d\n", pFile->h));
OpenCounter(-1);
sqlite3_free(pFile->pUnused);
@@ -2429,7 +2445,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
/* Otherwise see if some other process holds it. */
if( !reserved ){
sem_t *pSem = pFile->pInode->pSem;
- struct stat statBuf;
if( sem_trywait(pSem)==-1 ){
int tErrno = errno;
@@ -2482,7 +2497,6 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
*/
static int semLock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
- int fd;
sem_t *pSem = pFile->pInode->pSem;
int rc = SQLITE_OK;
@@ -3098,6 +3112,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
#endif
TIMER_START;
assert( cnt==(cnt&0x1ffff) );
+ assert( id->h>2 );
cnt &= 0x1ffff;
do{
#if defined(USE_PREAD)
@@ -3212,6 +3227,7 @@ static int seekAndWriteFd(
int rc = 0; /* Value returned by system call */
assert( nBuf==(nBuf&0x1ffff) );
+ assert( fd>2 );
nBuf &= 0x1ffff;
TIMER_START;
@@ -3597,6 +3613,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
}
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
/* If the file was just truncated to a size smaller than the currently
** mapped region, reduce the effective mapping size as well. SQLite will
** use read() and write() to access data beyond this point from now on.
@@ -3604,6 +3621,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
if( nByte<pFile->mmapSize ){
pFile->mmapSize = nByte;
}
+#endif
return SQLITE_OK;
}
@@ -3693,6 +3711,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
}
}
+#if SQLITE_MAX_MMAP_SIZE>0
if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
int rc;
if( pFile->szChunk<=0 ){
@@ -3705,6 +3724,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
rc = unixMapfile(pFile, nByte);
return rc;
}
+#endif
return SQLITE_OK;
}
@@ -3773,18 +3793,28 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
return SQLITE_OK;
}
+ case SQLITE_FCNTL_HAS_MOVED: {
+ *(int*)pArg = fileHasMoved(pFile);
+ return SQLITE_OK;
+ }
+#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
*(i64*)pArg = pFile->mmapSizeMax;
- if( newLimit>=0 ){
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
pFile->mmapSizeMax = newLimit;
- if( newLimit<pFile->mmapSize ) pFile->mmapSize = newLimit;
+ if( pFile->mmapSize>0 ){
+ unixUnmapfile(pFile);
+ rc = unixMapfile(pFile, -1);
+ }
}
- return SQLITE_OK;
+ return rc;
}
+#endif
#ifdef SQLITE_DEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
@@ -3929,8 +3959,25 @@ static int unixDeviceCharacteristics(sqlite3_file *id){
return rc;
}
-#ifndef SQLITE_OMIT_WAL
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+
+/*
+** Return the system page size.
+**
+** This function should not be called directly by other code in this file.
+** Instead, it should be called via macro osGetpagesize().
+*/
+static int unixGetpagesize(void){
+#if defined(_BSD_SOURCE)
+ return getpagesize();
+#else
+ return (int)sysconf(_SC_PAGESIZE);
+#endif
+}
+
+#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
+#ifndef SQLITE_OMIT_WAL
/*
** Object used to represent an shared memory buffer.
@@ -4047,7 +4094,7 @@ static int unixShmSystemLock(
#ifdef SQLITE_DEBUG
{ u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = (1<<(ofst+n)) - (1<<ofst);
+ mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock %d ok", ofst));
@@ -4081,6 +4128,22 @@ static int unixShmSystemLock(
return rc;
}
+/*
+** Return the minimum number of 32KB shm regions that should be mapped at
+** a time, assuming that each mapping must be an integer multiple of the
+** current system page-size.
+**
+** Usually, this is 1. The exception seems to be systems that are configured
+** to use 64KB pages - in this case each mapping must cover at least two
+** shm regions.
+*/
+static int unixShmRegionPerMap(void){
+ int shmsz = 32*1024; /* SHM region size */
+ int pgsz = osGetpagesize(); /* System page size */
+ assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */
+ if( pgsz<shmsz ) return 1;
+ return pgsz/shmsz;
+}
/*
** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
@@ -4092,10 +4155,11 @@ static void unixShmPurge(unixFile *pFd){
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
if( p && p->nRef==0 ){
+ int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->mutex);
- for(i=0; i<p->nRegion; i++){
+ for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->h>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
}else{
@@ -4302,6 +4366,8 @@ static int unixShmMap(
unixShm *p;
unixShmNode *pShmNode;
int rc = SQLITE_OK;
+ int nShmPerMap = unixShmRegionPerMap();
+ int nReqRegion;
/* If the shared-memory file has not yet been opened, open it now. */
if( pDbFd->pShm==0 ){
@@ -4317,9 +4383,12 @@ static int unixShmMap(
assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
- if( pShmNode->nRegion<=iRegion ){
+ /* Minimum number of regions required to be mapped. */
+ nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
+
+ if( pShmNode->nRegion<nReqRegion ){
char **apNew; /* New apRegion[] array */
- int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
+ int nByte = nReqRegion*szRegion; /* Minimum required file size */
struct stat sStat; /* Used by fstat() */
pShmNode->szRegion = szRegion;
@@ -4368,17 +4437,19 @@ static int unixShmMap(
/* Map the requested memory region into this processes address space. */
apNew = (char **)sqlite3_realloc(
- pShmNode->apRegion, (iRegion+1)*sizeof(char *)
+ pShmNode->apRegion, nReqRegion*sizeof(char *)
);
if( !apNew ){
rc = SQLITE_IOERR_NOMEM;
goto shmpage_out;
}
pShmNode->apRegion = apNew;
- while(pShmNode->nRegion<=iRegion){
+ while( pShmNode->nRegion<nReqRegion ){
+ int nMap = szRegion*nShmPerMap;
+ int i;
void *pMem;
if( pShmNode->h>=0 ){
- pMem = osMmap(0, szRegion,
+ pMem = osMmap(0, nMap,
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
);
@@ -4394,8 +4465,11 @@ static int unixShmMap(
}
memset(pMem, 0, szRegion);
}
- pShmNode->apRegion[pShmNode->nRegion] = pMem;
- pShmNode->nRegion++;
+
+ for(i=0; i<nShmPerMap; i++){
+ pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i];
+ }
+ pShmNode->nRegion += nShmPerMap;
}
}
@@ -4595,37 +4669,20 @@ static int unixShmUnmap(
# define unixShmUnmap 0
#endif /* #ifndef SQLITE_OMIT_WAL */
+#if SQLITE_MAX_MMAP_SIZE>0
/*
** If it is currently memory mapped, unmap file pFd.
*/
static void unixUnmapfile(unixFile *pFd){
assert( pFd->nFetchOut==0 );
-#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->pMapRegion ){
osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
pFd->pMapRegion = 0;
pFd->mmapSize = 0;
pFd->mmapSizeActual = 0;
}
-#endif
}
-#if SQLITE_MAX_MMAP_SIZE>0
-/*
-** Return the system page size.
-*/
-static int unixGetPagesize(void){
-#if HAVE_MREMAP
- return 512;
-#elif defined(_BSD_SOURCE)
- return getpagesize();
-#else
- return (int)sysconf(_SC_PAGESIZE);
-#endif
-}
-#endif /* SQLITE_MAX_MMAP_SIZE>0 */
-
-#if SQLITE_MAX_MMAP_SIZE>0
/*
** Attempt to set the size of the memory mapping maintained by file
** descriptor pFd to nNew bytes. Any existing mapping is discarded.
@@ -4662,8 +4719,12 @@ static void unixRemapfile(
if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
if( pOrig ){
- const int szSyspage = unixGetPagesize();
+#if HAVE_MREMAP
+ i64 nReuse = pFd->mmapSize;
+#else
+ const int szSyspage = osGetpagesize();
i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
+#endif
u8 *pReq = &pOrig[nReuse];
/* Unmap any pages of the existing mapping that cannot be reused. */
@@ -4710,7 +4771,6 @@ static void unixRemapfile(
pFd->pMapRegion = (void *)pNew;
pFd->mmapSize = pFd->mmapSizeActual = nNew;
}
-#endif
/*
** Memory map or remap the file opened by file-descriptor pFd (if the file
@@ -4729,7 +4789,6 @@ static void unixRemapfile(
** code otherwise.
*/
static int unixMapfile(unixFile *pFd, i64 nByte){
-#if SQLITE_MAX_MMAP_SIZE>0
i64 nMap = nByte;
int rc;
@@ -4755,10 +4814,10 @@ static int unixMapfile(unixFile *pFd, i64 nByte){
unixUnmapfile(pFd);
}
}
-#endif
return SQLITE_OK;
}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
/*
** If possible, return a pointer to a mapping of file fd starting at offset
@@ -4804,6 +4863,7 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
** may now be invalid and should be unmapped.
*/
static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
+#if SQLITE_MAX_MMAP_SIZE>0
unixFile *pFd = (unixFile *)fd; /* The underlying database file */
UNUSED_PARAMETER(iOff);
@@ -4822,6 +4882,11 @@ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
}
assert( pFd->nFetchOut>=0 );
+#else
+ UNUSED_PARAMETER(fd);
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(iOff);
+#endif
return SQLITE_OK;
}
@@ -5153,7 +5218,9 @@ static int fillInUnixFile(
pNew->pVfs = pVfs;
pNew->zPath = zFilename;
pNew->ctrlFlags = (u8)ctrlFlags;
+#if SQLITE_MAX_MMAP_SIZE>0
pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
"psow", SQLITE_POWERSAFE_OVERWRITE) ){
pNew->ctrlFlags |= UNIXFILE_PSOW;
@@ -5310,6 +5377,7 @@ static const char *unixTempFileDir(void){
static const char *azDirs[] = {
0,
0,
+ 0,
"/var/tmp",
"/usr/tmp",
"/tmp",
@@ -5320,7 +5388,8 @@ static const char *unixTempFileDir(void){
const char *zDir = 0;
azDirs[0] = sqlite3_temp_directory;
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMPDIR");
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
if( zDir==0 ) continue;
if( osStat(zDir, &buf) ) continue;
@@ -5607,6 +5676,16 @@ static int unixOpen(
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
+ /* Detect a pid change and reset the PRNG. There is a race condition
+ ** here such that two or more threads all trying to open databases at
+ ** the same instant might all reset the PRNG. But multiple resets
+ ** are harmless.
+ */
+ if( randomnessPid!=getpid() ){
+ randomnessPid = getpid();
+ sqlite3_randomness(0,0);
+ }
+
memset(p, 0, sizeof(unixFile));
if( eType==SQLITE_OPEN_MAIN_DB ){
@@ -5698,6 +5777,12 @@ static int unixOpen(
if( isDelete ){
#if OS_VXWORKS
zPath = zName;
+#elif defined(SQLITE_UNLINK_AFTER_CLOSE)
+ zPath = sqlite3_mprintf("%s", zName);
+ if( zPath==0 ){
+ robust_close(p, fd, __LINE__);
+ return SQLITE_NOMEM;
+ }
#else
osUnlink(zName);
#endif
@@ -5798,7 +5883,11 @@ static int unixDelete(
UNUSED_PARAMETER(NotUsed);
SimulateIOError(return SQLITE_IOERR_DELETE);
if( osUnlink(zPath)==(-1) ){
- if( errno==ENOENT ){
+ if( errno==ENOENT
+#if OS_VXWORKS
+ || errno==0x380003
+#endif
+ ){
rc = SQLITE_IOERR_DELETE_NOENT;
}else{
rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
@@ -5994,18 +6083,18 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** tests repeatable.
*/
memset(zBuf, 0, nBuf);
+ randomnessPid = getpid();
#if !defined(SQLITE_TEST)
{
- int pid, fd, got;
+ int fd, got;
fd = robust_open("/dev/urandom", O_RDONLY, 0);
if( fd<0 ){
time_t t;
time(&t);
memcpy(zBuf, &t, sizeof(t));
- pid = getpid();
- memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
- assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
- nBuf = sizeof(t) + sizeof(pid);
+ memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
+ assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
+ nBuf = sizeof(t) + sizeof(randomnessPid);
}else{
do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
robust_close(0, fd, __LINE__);
@@ -7391,7 +7480,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==24 );
+ assert( ArraySize(aSyscall)==25 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
diff --git a/src/os_win.c b/src/os_win.c
index aeb0881..f479de3 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -15,21 +15,22 @@
#include "sqliteInt.h"
#if SQLITE_OS_WIN /* This file is used for Windows only */
-#ifdef __CYGWIN__
-# include <sys/cygwin.h>
-#endif
-
/*
** Include code that is common to all os_*.c files
*/
#include "os_common.h"
/*
+** Include the header file for the Windows VFS.
+*/
+#include "os_win.h"
+
+/*
** Compiling and using WAL mode requires several APIs that are only
** available in Windows platforms based on the NT kernel.
*/
#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL)
-# error "WAL mode requires support from the Windows NT kernel, compile\
+# error "WAL mode requires support from the Windows NT kernel, compile\
with SQLITE_OMIT_WAL."
#endif
@@ -37,7 +38,7 @@
** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
** based on the sub-platform)?
*/
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
# define SQLITE_WIN32_HAS_ANSI
#endif
@@ -45,11 +46,122 @@
** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
** based on the sub-platform)?
*/
-#if SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
+ !defined(SQLITE_WIN32_NO_WIDE)
# define SQLITE_WIN32_HAS_WIDE
#endif
/*
+** Make sure at least one set of Win32 APIs is available.
+*/
+#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE)
+# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\
+ must be defined."
+#endif
+
+/*
+** Define the required Windows SDK version constants if they are not
+** already available.
+*/
+#ifndef NTDDI_WIN8
+# define NTDDI_WIN8 0x06020000
+#endif
+
+#ifndef NTDDI_WINBLUE
+# define NTDDI_WINBLUE 0x06030000
+#endif
+
+/*
+** Check to see if the GetVersionEx[AW] functions are deprecated on the
+** target system. GetVersionEx was first deprecated in Win8.1.
+*/
+#ifndef SQLITE_WIN32_GETVERSIONEX
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE
+# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */
+# else
+# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */
+# endif
+#endif
+
+/*
+** This constant should already be defined (in the "WinDef.h" SDK file).
+*/
+#ifndef MAX_PATH
+# define MAX_PATH (260)
+#endif
+
+/*
+** Maximum pathname length (in chars) for Win32. This should normally be
+** MAX_PATH.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_CHARS
+# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH)
+#endif
+
+/*
+** This constant should already be defined (in the "WinNT.h" SDK file).
+*/
+#ifndef UNICODE_STRING_MAX_CHARS
+# define UNICODE_STRING_MAX_CHARS (32767)
+#endif
+
+/*
+** Maximum pathname length (in chars) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS.
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_CHARS
+# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
+** characters, so we allocate 4 bytes per character assuming worst-case of
+** 4-bytes-per-character for UTF8.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_BYTES
+# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR).
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_BYTES
+# define SQLITE_WINNT_MAX_PATH_BYTES \
+ (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS)
+#endif
+
+/*
+** Maximum error message length (in chars) for WinRT.
+*/
+#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS
+# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024)
+#endif
+
+/*
+** Returns non-zero if the character should be treated as a directory
+** separator.
+*/
+#ifndef winIsDirSep
+# define winIsDirSep(a) (((a) == '/') || ((a) == '\\'))
+#endif
+
+/*
+** This macro is used when a local variable is set to a value that is
+** [sometimes] not used by the code (e.g. via conditional compilation).
+*/
+#ifndef UNUSED_VARIABLE_VALUE
+# define UNUSED_VARIABLE_VALUE(x) (void)(x)
+#endif
+
+/*
+** Returns the character that should be used as the directory separator.
+*/
+#ifndef winGetDirSep
+# define winGetDirSep() '\\'
+#endif
+
+/*
** Do we need to manually define the Win32 file mapping APIs for use with WAL
** mode (e.g. these APIs are available in the Windows CE SDK; however, they
** are not present in the header file)?
@@ -85,17 +197,10 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
/*
-** Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
-
-/*
** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_FILE_ATTRIBUTES
-# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif
#ifndef FILE_FLAG_MASK
@@ -107,7 +212,7 @@ WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
#endif
#ifndef SQLITE_OMIT_WAL
-/* Forward references */
+/* Forward references to structures used for WAL */
typedef struct winShm winShm; /* A connection to shared-memory */
typedef struct winShmNode winShmNode; /* A region of shared-memory */
#endif
@@ -145,7 +250,7 @@ struct winFile {
int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
#if SQLITE_OS_WINCE
LPWSTR zDeleteOnClose; /* Name of file to delete when closing */
- HANDLE hMutex; /* Mutex used to control access to shared lock */
+ HANDLE hMutex; /* Mutex used to control access to shared lock */
HANDLE hShared; /* Shared memory segment used for locking */
winceLock local; /* Locks obtained by this instance of winFile */
winceLock *shared; /* Global shared lock memory for the file */
@@ -237,6 +342,7 @@ struct winFile {
# define SQLITE_WIN32_HEAP_FLAGS (0)
#endif
+
/*
** The winMemData structure stores information required by the Win32-specific
** sqlite3_mem_methods implementation.
@@ -244,30 +350,41 @@ struct winFile {
typedef struct winMemData winMemData;
struct winMemData {
#ifndef NDEBUG
- u32 magic; /* Magic number to detect structure corruption. */
+ u32 magic1; /* Magic number to detect structure corruption. */
#endif
HANDLE hHeap; /* The handle to our heap. */
BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
+#ifndef NDEBUG
+ u32 magic2; /* Magic number to detect structure corruption. */
+#endif
};
#ifndef NDEBUG
-#define WINMEM_MAGIC 0x42b2830b
+#define WINMEM_MAGIC1 0x42b2830b
+#define WINMEM_MAGIC2 0xbd4d7cf4
#endif
static struct winMemData win_mem_data = {
#ifndef NDEBUG
- WINMEM_MAGIC,
+ WINMEM_MAGIC1,
#endif
NULL, FALSE
+#ifndef NDEBUG
+ ,WINMEM_MAGIC2
+#endif
};
#ifndef NDEBUG
-#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC )
+#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 )
+#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 )
+#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2();
#else
#define winMemAssertMagic()
#endif
-#define winMemGetHeap() win_mem_data.hHeap
+#define winMemGetDataPtr() &win_mem_data
+#define winMemGetHeap() win_mem_data.hHeap
+#define winMemGetOwned() win_mem_data.bOwned
static void *winMemMalloc(int nBytes);
static void winMemFree(void *pPrior);
@@ -293,9 +410,9 @@ const sqlite3_mem_methods *sqlite3MemGetWin32(void);
** can manually set this value to 1 to emulate Win98 behavior.
*/
#ifdef SQLITE_TEST
-int sqlite3_os_type = 0;
+LONG volatile sqlite3_os_type = 0;
#else
-static int sqlite3_os_type = 0;
+static LONG volatile sqlite3_os_type = 0;
#endif
#ifndef SYSCALL
@@ -600,7 +717,8 @@ static struct win_syscall {
#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
-#if defined(SQLITE_WIN32_HAS_ANSI)
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \
+ SQLITE_WIN32_GETVERSIONEX
{ "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
#else
{ "GetVersionExA", (SYSCALL)0, 0 },
@@ -609,10 +727,20 @@ static struct win_syscall {
#define osGetVersionExA ((BOOL(WINAPI*)( \
LPOSVERSIONINFOA))aSyscall[34].pCurrent)
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ { "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
+#else
+ { "GetVersionExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExW ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOW))aSyscall[35].pCurrent)
+
{ "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
- SIZE_T))aSyscall[35].pCurrent)
+ SIZE_T))aSyscall[36].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapCreate", (SYSCALL)HeapCreate, 0 },
@@ -621,7 +749,7 @@ static struct win_syscall {
#endif
#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
- SIZE_T))aSyscall[36].pCurrent)
+ SIZE_T))aSyscall[37].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
@@ -629,21 +757,21 @@ static struct win_syscall {
{ "HeapDestroy", (SYSCALL)0, 0 },
#endif
-#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[37].pCurrent)
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
{ "HeapFree", (SYSCALL)HeapFree, 0 },
-#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[38].pCurrent)
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent)
{ "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
- SIZE_T))aSyscall[39].pCurrent)
+ SIZE_T))aSyscall[40].pCurrent)
{ "HeapSize", (SYSCALL)HeapSize, 0 },
#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[40].pCurrent)
+ LPCVOID))aSyscall[41].pCurrent)
#if !SQLITE_OS_WINRT
{ "HeapValidate", (SYSCALL)HeapValidate, 0 },
@@ -652,7 +780,15 @@ static struct win_syscall {
#endif
#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
- LPCVOID))aSyscall[41].pCurrent)
+ LPCVOID))aSyscall[42].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "HeapCompact", (SYSCALL)HeapCompact, 0 },
+#else
+ { "HeapCompact", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
@@ -660,7 +796,7 @@ static struct win_syscall {
{ "LoadLibraryA", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[42].pCurrent)
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent)
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
!defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -669,7 +805,7 @@ static struct win_syscall {
{ "LoadLibraryW", (SYSCALL)0, 0 },
#endif
-#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[43].pCurrent)
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent)
#if !SQLITE_OS_WINRT
{ "LocalFree", (SYSCALL)LocalFree, 0 },
@@ -677,7 +813,7 @@ static struct win_syscall {
{ "LocalFree", (SYSCALL)0, 0 },
#endif
-#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[44].pCurrent)
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "LockFile", (SYSCALL)LockFile, 0 },
@@ -687,7 +823,7 @@ static struct win_syscall {
#ifndef osLockFile
#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[45].pCurrent)
+ DWORD))aSyscall[47].pCurrent)
#endif
#if !SQLITE_OS_WINCE
@@ -698,7 +834,7 @@ static struct win_syscall {
#ifndef osLockFileEx
#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[46].pCurrent)
+ LPOVERLAPPED))aSyscall[48].pCurrent)
#endif
#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
@@ -708,26 +844,26 @@ static struct win_syscall {
#endif
#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- SIZE_T))aSyscall[47].pCurrent)
+ SIZE_T))aSyscall[49].pCurrent)
{ "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
- int))aSyscall[48].pCurrent)
+ int))aSyscall[50].pCurrent)
{ "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
- LARGE_INTEGER*))aSyscall[49].pCurrent)
+ LARGE_INTEGER*))aSyscall[51].pCurrent)
{ "ReadFile", (SYSCALL)ReadFile, 0 },
#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[50].pCurrent)
+ LPOVERLAPPED))aSyscall[52].pCurrent)
{ "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
-#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[51].pCurrent)
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent)
#if !SQLITE_OS_WINRT
{ "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
@@ -736,7 +872,7 @@ static struct win_syscall {
#endif
#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
- DWORD))aSyscall[52].pCurrent)
+ DWORD))aSyscall[54].pCurrent)
#if !SQLITE_OS_WINRT
{ "Sleep", (SYSCALL)Sleep, 0 },
@@ -744,12 +880,12 @@ static struct win_syscall {
{ "Sleep", (SYSCALL)0, 0 },
#endif
-#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[53].pCurrent)
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
- LPFILETIME))aSyscall[54].pCurrent)
+ LPFILETIME))aSyscall[56].pCurrent)
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
@@ -759,7 +895,7 @@ static struct win_syscall {
#ifndef osUnlockFile
#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- DWORD))aSyscall[55].pCurrent)
+ DWORD))aSyscall[57].pCurrent)
#endif
#if !SQLITE_OS_WINCE
@@ -769,7 +905,7 @@ static struct win_syscall {
#endif
#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
- LPOVERLAPPED))aSyscall[56].pCurrent)
+ LPOVERLAPPED))aSyscall[58].pCurrent)
#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
{ "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
@@ -777,17 +913,17 @@ static struct win_syscall {
{ "UnmapViewOfFile", (SYSCALL)0, 0 },
#endif
-#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[57].pCurrent)
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent)
{ "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
- LPCSTR,LPBOOL))aSyscall[58].pCurrent)
+ LPCSTR,LPBOOL))aSyscall[60].pCurrent)
{ "WriteFile", (SYSCALL)WriteFile, 0 },
#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
- LPOVERLAPPED))aSyscall[59].pCurrent)
+ LPOVERLAPPED))aSyscall[61].pCurrent)
#if SQLITE_OS_WINRT
{ "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
@@ -796,7 +932,7 @@ static struct win_syscall {
#endif
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
- DWORD,DWORD))aSyscall[60].pCurrent)
+ DWORD,DWORD))aSyscall[62].pCurrent)
#if !SQLITE_OS_WINRT
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
@@ -805,7 +941,7 @@ static struct win_syscall {
#endif
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
- DWORD))aSyscall[61].pCurrent)
+ DWORD))aSyscall[63].pCurrent)
#if SQLITE_OS_WINRT
{ "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
@@ -814,7 +950,7 @@ static struct win_syscall {
#endif
#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
- BOOL))aSyscall[62].pCurrent)
+ BOOL))aSyscall[64].pCurrent)
#if SQLITE_OS_WINRT
{ "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
@@ -823,7 +959,7 @@ static struct win_syscall {
#endif
#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
- PLARGE_INTEGER,DWORD))aSyscall[63].pCurrent)
+ PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent)
#if SQLITE_OS_WINRT
{ "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
@@ -832,7 +968,7 @@ static struct win_syscall {
#endif
#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
- FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[64].pCurrent)
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
{ "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
@@ -841,7 +977,7 @@ static struct win_syscall {
#endif
#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
- SIZE_T))aSyscall[65].pCurrent)
+ SIZE_T))aSyscall[67].pCurrent)
#if SQLITE_OS_WINRT
{ "CreateFile2", (SYSCALL)CreateFile2, 0 },
@@ -850,7 +986,7 @@ static struct win_syscall {
#endif
#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
- LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[66].pCurrent)
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
{ "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
@@ -859,7 +995,7 @@ static struct win_syscall {
#endif
#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
- DWORD))aSyscall[67].pCurrent)
+ DWORD))aSyscall[69].pCurrent)
#if SQLITE_OS_WINRT
{ "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
@@ -867,7 +1003,7 @@ static struct win_syscall {
{ "GetTickCount64", (SYSCALL)0, 0 },
#endif
-#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[68].pCurrent)
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent)
#if SQLITE_OS_WINRT
{ "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
@@ -876,7 +1012,7 @@ static struct win_syscall {
#endif
#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
- LPSYSTEM_INFO))aSyscall[69].pCurrent)
+ LPSYSTEM_INFO))aSyscall[71].pCurrent)
#if defined(SQLITE_WIN32_HAS_ANSI)
{ "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
@@ -884,7 +1020,7 @@ static struct win_syscall {
{ "OutputDebugStringA", (SYSCALL)0, 0 },
#endif
-#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[70].pCurrent)
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent)
#if defined(SQLITE_WIN32_HAS_WIDE)
{ "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
@@ -892,11 +1028,11 @@ static struct win_syscall {
{ "OutputDebugStringW", (SYSCALL)0, 0 },
#endif
-#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[71].pCurrent)
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent)
{ "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
-#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[72].pCurrent)
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
{ "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
@@ -905,7 +1041,23 @@ static struct win_syscall {
#endif
#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
- LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[73].pCurrent)
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
+
+/*
+** NOTE: On some sub-platforms, the InterlockedCompareExchange "function"
+** is really just a macro that uses a compiler intrinsic (e.g. x64).
+** So do not try to make this is into a redefinable interface.
+*/
+#if defined(InterlockedCompareExchange)
+ { "InterlockedCompareExchange", (SYSCALL)0, 0 },
+
+#define osInterlockedCompareExchange InterlockedCompareExchange
+#else
+ { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 },
+
+#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG volatile*, \
+ LONG,LONG))aSyscall[76].pCurrent)
+#endif /* defined(InterlockedCompareExchange) */
}; /* End of the overrideable system calls */
@@ -992,6 +1144,94 @@ static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
return 0;
}
+#ifdef SQLITE_WIN32_MALLOC
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one
+** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The
+** "pnLargest" argument, if non-zero, will be used to return the size of the
+** largest committed free block in the heap, in bytes.
+*/
+int sqlite3_win32_compact_heap(LPUINT pnLargest){
+ int rc = SQLITE_OK;
+ UINT nLargest = 0;
+ HANDLE hHeap;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){
+ DWORD lastErrno = osGetLastError();
+ if( lastErrno==NO_ERROR ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
+ osGetLastError(), (void*)hHeap);
+ rc = SQLITE_ERROR;
+ }
+ }
+#else
+ sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOTFOUND;
+#endif
+ if( pnLargest ) *pnLargest = nLargest;
+ return rc;
+}
+
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** destroy and recreate it. If the Win32 native heap is not isolated and/or
+** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will
+** be returned and no changes will be made to the Win32 native heap.
+*/
+int sqlite3_win32_reset_heap(){
+ int rc;
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
+ sqlite3_mutex_enter(pMaster);
+ sqlite3_mutex_enter(pMem);
+ winMemAssertMagic();
+ if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){
+ /*
+ ** At this point, there should be no outstanding memory allocations on
+ ** the heap. Also, since both the master and memsys locks are currently
+ ** being held by us, no other function (i.e. from another thread) should
+ ** be able to even access the heap. Attempt to destroy and recreate our
+ ** isolated Win32 native heap now.
+ */
+ assert( winMemGetHeap()!=NULL );
+ assert( winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ winMemShutdown(winMemGetDataPtr());
+ assert( winMemGetHeap()==NULL );
+ assert( !winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ rc = winMemInit(winMemGetDataPtr());
+ assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL );
+ assert( rc!=SQLITE_OK || winMemGetOwned() );
+ assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 );
+ }else{
+ /*
+ ** The Win32 native heap cannot be modified because it may be in use.
+ */
+ rc = SQLITE_BUSY;
+ }
+ sqlite3_mutex_leave(pMem);
+ sqlite3_mutex_leave(pMaster);
+ return rc;
+}
+#endif /* SQLITE_WIN32_MALLOC */
+
/*
** This function outputs the specified (ANSI) string to the Win32 debugger
** (if available).
@@ -1061,21 +1301,46 @@ void sqlite3_win32_sleep(DWORD milliseconds){
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
-# define isNT() (1)
+
+#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX
+# define osIsNT() (1)
+#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
+# define osIsNT() (1)
#elif !defined(SQLITE_WIN32_HAS_WIDE)
-# define isNT() (0)
+# define osIsNT() (0)
#else
- static int isNT(void){
- if( sqlite3_os_type==0 ){
- OSVERSIONINFOA sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- osGetVersionExA(&sInfo);
- sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
- }
- return sqlite3_os_type==2;
+# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
+#endif
+
+/*
+** This function determines if the machine is running a version of Windows
+** based on the NT kernel.
+*/
+int sqlite3_win32_is_nt(void){
+#if defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX
+ if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WIN8
+ OSVERSIONINFOW sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExW(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#elif defined(SQLITE_WIN32_HAS_ANSI)
+ OSVERSIONINFOA sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExA(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#endif
}
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#elif SQLITE_TEST
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#else
+ return 1;
#endif
+}
#ifdef SQLITE_WIN32_MALLOC
/*
@@ -1090,12 +1355,12 @@ static void *winMemMalloc(int nBytes){
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
- assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
assert( nBytes>=0 );
p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
nBytes, osGetLastError(), (void*)hHeap);
}
return p;
@@ -1112,11 +1377,11 @@ static void winMemFree(void *pPrior){
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
- assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
pPrior, osGetLastError(), (void*)hHeap);
}
}
@@ -1133,7 +1398,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
- assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
assert( nBytes>=0 );
if( !pPrior ){
@@ -1142,7 +1407,7 @@ static void *winMemRealloc(void *pPrior, int nBytes){
p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
}
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
(void*)hHeap);
}
@@ -1161,12 +1426,12 @@ static int winMemSize(void *p){
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
- assert ( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) );
#endif
if( !p ) return 0;
n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
if( n==(SIZE_T)-1 ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
p, osGetLastError(), (void*)hHeap);
return 0;
}
@@ -1187,18 +1452,25 @@ static int winMemInit(void *pAppData){
winMemData *pWinMemData = (winMemData *)pAppData;
if( !pWinMemData ) return SQLITE_ERROR;
- assert( pWinMemData->magic==WINMEM_MAGIC );
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
if( !pWinMemData->hHeap ){
+ DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE;
+ DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap;
+ if( dwMaximumSize==0 ){
+ dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE;
+ }else if( dwInitialSize>dwMaximumSize ){
+ dwInitialSize = dwMaximumSize;
+ }
pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
- SQLITE_WIN32_HEAP_INIT_SIZE,
- SQLITE_WIN32_HEAP_MAX_SIZE);
+ dwInitialSize, dwMaximumSize);
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
- "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u",
- osGetLastError(), SQLITE_WIN32_HEAP_FLAGS,
- SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE);
+ "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
+ osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
+ dwMaximumSize);
return SQLITE_NOMEM;
}
pWinMemData->bOwned = TRUE;
@@ -1208,7 +1480,7 @@ static int winMemInit(void *pAppData){
pWinMemData->hHeap = osGetProcessHeap();
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
- "failed to GetProcessHeap (%d)", osGetLastError());
+ "failed to GetProcessHeap (%lu)", osGetLastError());
return SQLITE_NOMEM;
}
pWinMemData->bOwned = FALSE;
@@ -1229,6 +1501,9 @@ static void winMemShutdown(void *pAppData){
winMemData *pWinMemData = (winMemData *)pAppData;
if( !pWinMemData ) return;
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
+
if( pWinMemData->hHeap ){
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
@@ -1236,7 +1511,7 @@ static void winMemShutdown(void *pAppData){
#endif
if( pWinMemData->bOwned ){
if( !osHeapDestroy(pWinMemData->hHeap) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p",
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
osGetLastError(), (void*)pWinMemData->hHeap);
}
pWinMemData->bOwned = FALSE;
@@ -1273,11 +1548,11 @@ void sqlite3MemSetDefault(void){
#endif /* SQLITE_WIN32_MALLOC */
/*
-** Convert a UTF-8 string to Microsoft Unicode (UTF-16?).
+** Convert a UTF-8 string to Microsoft Unicode (UTF-16?).
**
** Space to hold the returned string is obtained from malloc.
*/
-static LPWSTR utf8ToUnicode(const char *zFilename){
+static LPWSTR winUtf8ToUnicode(const char *zFilename){
int nChar;
LPWSTR zWideFilename;
@@ -1302,7 +1577,7 @@ static LPWSTR utf8ToUnicode(const char *zFilename){
** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
** obtained from sqlite3_malloc().
*/
-static char *unicodeToUtf8(LPCWSTR zWideFilename){
+static char *winUnicodeToUtf8(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
@@ -1326,11 +1601,11 @@ static char *unicodeToUtf8(LPCWSTR zWideFilename){
/*
** Convert an ANSI string to Microsoft Unicode, based on the
** current codepage settings for file apis.
-**
+**
** Space to hold the returned string is obtained
** from sqlite3_malloc.
*/
-static LPWSTR mbcsToUnicode(const char *zFilename){
+static LPWSTR winMbcsToUnicode(const char *zFilename){
int nByte;
LPWSTR zMbcsFilename;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
@@ -1360,7 +1635,7 @@ static LPWSTR mbcsToUnicode(const char *zFilename){
** Space to hold the returned string is obtained from
** sqlite3_malloc().
*/
-static char *unicodeToMbcs(LPCWSTR zWideFilename){
+static char *winUnicodeToMbcs(LPCWSTR zWideFilename){
int nByte;
char *zFilename;
int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP;
@@ -1390,28 +1665,28 @@ char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
char *zFilenameUtf8;
LPWSTR zTmpWide;
- zTmpWide = mbcsToUnicode(zFilename);
+ zTmpWide = winMbcsToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameUtf8 = unicodeToUtf8(zTmpWide);
+ zFilenameUtf8 = winUnicodeToUtf8(zTmpWide);
sqlite3_free(zTmpWide);
return zFilenameUtf8;
}
/*
-** Convert UTF-8 to multibyte character string. Space to hold the
+** Convert UTF-8 to multibyte character string. Space to hold the
** returned string is obtained from sqlite3_malloc().
*/
char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
char *zFilenameMbcs;
LPWSTR zTmpWide;
- zTmpWide = utf8ToUnicode(zFilename);
+ zTmpWide = winUtf8ToUnicode(zFilename);
if( zTmpWide==0 ){
return 0;
}
- zFilenameMbcs = unicodeToMbcs(zTmpWide);
+ zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
sqlite3_free(zTmpWide);
return zFilenameMbcs;
}
@@ -1441,7 +1716,7 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
if( ppDirectory ){
char *zValueUtf8 = 0;
if( zValue && zValue[0] ){
- zValueUtf8 = unicodeToUtf8(zValue);
+ zValueUtf8 = winUnicodeToUtf8(zValue);
if ( zValueUtf8==0 ){
return SQLITE_NOMEM;
}
@@ -1454,11 +1729,11 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
}
/*
-** The return value of getLastErrorMsg
+** The return value of winGetLastErrorMsg
** is zero if the error message fits in the buffer, or non-zero
** otherwise (if the message was truncated).
*/
-static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
+static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
/* FormatMessage returns 0 on failure. Otherwise it
** returns the number of TCHARs written to the output
** buffer, excluding the terminating null char.
@@ -1466,16 +1741,16 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
DWORD dwLen = 0;
char *zOut = 0;
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
- WCHAR zTempWide[MAX_PATH+1]; /* NOTE: Somewhat arbitrary. */
+ WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
lastErrno,
0,
zTempWide,
- MAX_PATH,
+ SQLITE_WIN32_MAX_ERRMSG_CHARS,
0);
#else
LPWSTR zTempWide = NULL;
@@ -1492,7 +1767,7 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
sqlite3BeginBenignMalloc();
- zOut = unicodeToUtf8(zTempWide);
+ zOut = winUnicodeToUtf8(zTempWide);
sqlite3EndBenignMalloc();
#if !SQLITE_OS_WINRT
/* free the system buffer allocated by FormatMessage */
@@ -1540,11 +1815,11 @@ static int getLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
**
** This routine is invoked after an error occurs in an OS function.
** It logs a message using sqlite3_log() containing the current value of
-** error code and, if possible, the human-readable equivalent from
+** error code and, if possible, the human-readable equivalent from
** FormatMessage.
**
** The first argument passed to the macro should be the error code that
-** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
** failed and the associated file-system path, if any.
*/
@@ -1560,7 +1835,7 @@ static int winLogErrorAtLine(
int i; /* Loop counter */
zMsg[0] = 0;
- getLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
+ winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
@@ -1575,7 +1850,7 @@ static int winLogErrorAtLine(
/*
** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
-** will be retried following a locking error - probably caused by
+** will be retried following a locking error - probably caused by
** antivirus software. Also the initial delay before the first retry.
** The delay increases linearly with each retry.
*/
@@ -1585,29 +1860,60 @@ static int winLogErrorAtLine(
#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
#endif
-static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY;
-static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
+static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+
+/*
+** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
+** error code obtained via GetLastError() is eligible to be retried. It
+** must accept the error code DWORD as its only argument and should return
+** non-zero if the error code is transient in nature and the operation
+** responsible for generating the original error might succeed upon being
+** retried. The argument to this macro should be a variable.
+**
+** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it
+** is defined, it will be consulted only when the macro "winIoerrCanRetry1"
+** returns zero. The "winIoerrCanRetry2" macro is completely optional and
+** may be used to include additional error codes in the set that should
+** result in the failing I/O operation being retried by the caller. If
+** defined, the "winIoerrCanRetry2" macro must exhibit external semantics
+** identical to those of the "winIoerrCanRetry1" macro.
+*/
+#if !defined(winIoerrCanRetry1)
+#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
+ ((a)==ERROR_SHARING_VIOLATION) || \
+ ((a)==ERROR_LOCK_VIOLATION) || \
+ ((a)==ERROR_DEV_NOT_EXIST) || \
+ ((a)==ERROR_NETNAME_DELETED) || \
+ ((a)==ERROR_SEM_TIMEOUT) || \
+ ((a)==ERROR_NETWORK_UNREACHABLE))
+#endif
/*
** If a ReadFile() or WriteFile() error occurs, invoke this routine
** to see if it should be retried. Return TRUE to retry. Return FALSE
** to give up with an error.
*/
-static int retryIoerr(int *pnRetry, DWORD *pError){
+static int winRetryIoerr(int *pnRetry, DWORD *pError){
DWORD e = osGetLastError();
- if( *pnRetry>=win32IoerrRetry ){
+ if( *pnRetry>=winIoerrRetry ){
if( pError ){
*pError = e;
}
return 0;
}
- if( e==ERROR_ACCESS_DENIED ||
- e==ERROR_LOCK_VIOLATION ||
- e==ERROR_SHARING_VIOLATION ){
- sqlite3_win32_sleep(win32IoerrRetryDelay*(1+*pnRetry));
+ if( winIoerrCanRetry1(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
++*pnRetry;
return 1;
}
+#if defined(winIoerrCanRetry2)
+ else if( winIoerrCanRetry2(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
+ ++*pnRetry;
+ return 1;
+ }
+#endif
if( pError ){
*pError = e;
}
@@ -1617,11 +1923,11 @@ static int retryIoerr(int *pnRetry, DWORD *pError){
/*
** Log a I/O error retry episode.
*/
-static void logIoerr(int nRetry){
+static void winLogIoerr(int nRetry){
if( nRetry ){
- sqlite3_log(SQLITE_IOERR,
+ sqlite3_log(SQLITE_IOERR,
"delayed %dms for lock/sharing conflict",
- win32IoerrRetryDelay*nRetry*(nRetry+1)/2
+ winIoerrRetryDelay*nRetry*(nRetry+1)/2
);
}
}
@@ -1686,7 +1992,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
BOOL bLogged = FALSE;
BOOL bInit = TRUE;
- zName = utf8ToUnicode(zFilename);
+ zName = winUtf8ToUnicode(zFilename);
if( zName==0 ){
/* out of memory */
return SQLITE_IOERR_NOMEM;
@@ -1706,25 +2012,24 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
pFile->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR, pFile->lastErrno,
- "winceCreateLock1", zFilename);
sqlite3_free(zName);
- return SQLITE_IOERR;
+ return winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock1", zFilename);
}
/* Acquire the mutex before continuing */
winceMutexAcquire(pFile->hMutex);
-
- /* Since the names of named mutexes, semaphores, file mappings etc are
+
+ /* Since the names of named mutexes, semaphores, file mappings etc are
** case-sensitive, take advantage of that by uppercasing the mutex name
** and using that as the shared filemapping name.
*/
osCharUpperW(zName);
pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, sizeof(winceLock),
- zName);
+ zName);
- /* Set a flag that indicates we're the first to create the memory so it
+ /* Set a flag that indicates we're the first to create the memory so it
** must be zero-initialized */
lastErrno = osGetLastError();
if (lastErrno == ERROR_ALREADY_EXISTS){
@@ -1735,7 +2040,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
/* If we succeeded in making the shared memory handle, map it. */
if( pFile->hShared ){
- pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared,
+ pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared,
FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
/* If mapping failed, close the shared memory handle and erase it */
if( !pFile->shared ){
@@ -1761,7 +2066,7 @@ static int winceCreateLock(const char *zFilename, winFile *pFile){
pFile->hMutex = NULL;
return SQLITE_IOERR;
}
-
+
/* Initialize the shared memory if we're supposed to */
if( bInit ){
memset(pFile->shared, 0, sizeof(winceLock));
@@ -1799,13 +2104,13 @@ static void winceDestroyLock(winFile *pFile){
osCloseHandle(pFile->hShared);
/* Done with the mutex */
- winceMutexRelease(pFile->hMutex);
+ winceMutexRelease(pFile->hMutex);
osCloseHandle(pFile->hMutex);
pFile->hMutex = NULL;
}
}
-/*
+/*
** An implementation of the LockFile() API of Windows for CE
*/
static BOOL winceLockFile(
@@ -1959,7 +2264,7 @@ static BOOL winLockFile(
return winceLockFile(phFile, offsetLow, offsetHigh,
numBytesLow, numBytesHigh);
#else
- if( isNT() ){
+ if( osIsNT() ){
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = offsetLow;
@@ -1990,7 +2295,7 @@ static BOOL winUnlockFile(
return winceUnlockFile(phFile, offsetLow, offsetHigh,
numBytesLow, numBytesHigh);
#else
- if( isNT() ){
+ if( osIsNT() ){
OVERLAPPED ovlp;
memset(&ovlp, 0, sizeof(OVERLAPPED));
ovlp.Offset = offsetLow;
@@ -2016,11 +2321,11 @@ static BOOL winUnlockFile(
#endif
/*
-** Move the current position of the file handle passed as the first
-** argument to offset iOffset within the file. If successful, return 0.
+** Move the current position of the file handle passed as the first
+** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
-static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
@@ -2032,11 +2337,11 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
- /* API oddity: If successful, SetFilePointer() returns a dword
+ /* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
- ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
- ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
- ** whether an error has actually occurred, it is also necessary to call
+ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
+ ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
+ ** whether an error has actually occurred, it is also necessary to call
** GetLastError().
*/
dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
@@ -2045,7 +2350,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
&& ((lastErrno = osGetLastError())!=NO_ERROR)) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "seekWinFile", pFile->zPath);
+ "winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
@@ -2066,7 +2371,7 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
if(!bRet){
pFile->lastErrno = osGetLastError();
winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "seekWinFile", pFile->zPath);
+ "winSeekFile", pFile->zPath);
OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
@@ -2077,7 +2382,8 @@ static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
}
#if SQLITE_MAX_MMAP_SIZE>0
-/* Forward references to VFS methods */
+/* Forward references to VFS helper methods used for memory mapped files */
+static int winMapfile(winFile*, sqlite3_int64);
static int winUnmapfile(winFile*);
#endif
@@ -2104,8 +2410,7 @@ static int winClose(sqlite3_file *id){
OSTRACE(("CLOSE file=%p\n", pFile->h));
#if SQLITE_MAX_MMAP_SIZE>0
- rc = winUnmapfile(pFile);
- if( rc!=SQLITE_OK ) return rc;
+ winUnmapfile(pFile);
#endif
do{
@@ -2119,7 +2424,7 @@ static int winClose(sqlite3_file *id){
int cnt = 0;
while(
osDeleteFileW(pFile->zDeleteOnClose)==0
- && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
+ && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
&& cnt++ < WINCE_DELETION_ATTEMPTS
){
sqlite3_win32_sleep(100); /* Wait a little before trying again */
@@ -2181,7 +2486,7 @@ static int winRead(
#endif
#if SQLITE_OS_WINCE
- if( seekWinFile(pFile, offset) ){
+ if( winSeekFile(pFile, offset) ){
OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h));
return SQLITE_FULL;
}
@@ -2194,13 +2499,13 @@ static int winRead(
osGetLastError()!=ERROR_HANDLE_EOF ){
#endif
DWORD lastErrno;
- if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
pFile->lastErrno = lastErrno;
OSTRACE(("READ file=%p, rc=SQLITE_IOERR_READ\n", pFile->h));
return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
- "winRead", pFile->zPath);
+ "winRead", pFile->zPath);
}
- logIoerr(nRetry);
+ winLogIoerr(nRetry);
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
@@ -2253,7 +2558,7 @@ static int winWrite(
#endif
#if SQLITE_OS_WINCE
- rc = seekWinFile(pFile, offset);
+ rc = winSeekFile(pFile, offset);
if( rc==0 ){
#else
{
@@ -2278,7 +2583,7 @@ static int winWrite(
#else
if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
#endif
- if( retryIoerr(&nRetry, &lastErrno) ) continue;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
break;
}
assert( nWrite==0 || nWrite<=(DWORD)nRem );
@@ -2304,13 +2609,14 @@ static int winWrite(
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h));
- return SQLITE_FULL;
+ return winLogError(SQLITE_FULL, pFile->lastErrno,
+ "winWrite1", pFile->zPath);
}
OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h));
return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
- "winWrite", pFile->zPath);
+ "winWrite2", pFile->zPath);
}else{
- logIoerr(nRetry);
+ winLogIoerr(nRetry);
}
OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
@@ -2339,7 +2645,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
- if( seekWinFile(pFile, nByte) ){
+ if( winSeekFile(pFile, nByte) ){
rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
"winTruncate1", pFile->zPath);
}else if( 0==osSetEndOfFile(pFile->h) &&
@@ -2420,6 +2726,7 @@ static int winSync(sqlite3_file *id, int flags){
** no-op
*/
#ifdef SQLITE_NO_SYNC
+ OSTRACE(("SYNC-NOP file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
#else
rc = osFlushFileBuffers(pFile->h);
@@ -2431,7 +2738,7 @@ static int winSync(sqlite3_file *id, int flags){
pFile->lastErrno = osGetLastError();
OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h));
return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
- "winSync", pFile->zPath);
+ "winSync", pFile->zPath);
}
#endif
}
@@ -2472,7 +2779,7 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
&& ((lastErrno = osGetLastError())!=NO_ERROR) ){
pFile->lastErrno = lastErrno;
rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
- "winFileSize", pFile->zPath);
+ "winFileSize", pFile->zPath);
}
}
#endif
@@ -2517,10 +2824,10 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
** Different API routines are called depending on whether or not this
** is Win9x or WinNT.
*/
-static int getReadLock(winFile *pFile){
+static int winGetReadLock(winFile *pFile){
int res;
OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINCE
/*
** NOTE: Windows CE is handled differently here due its lack of the Win32
@@ -2545,18 +2852,18 @@ static int getReadLock(winFile *pFile){
pFile->lastErrno = osGetLastError();
/* No need to log a failure to lock */
}
- OSTRACE(("READ-LOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
+ OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
/*
** Undo a readlock
*/
-static int unlockReadLock(winFile *pFile){
+static int winUnlockReadLock(winFile *pFile){
int res;
DWORD lastErrno;
OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
- if( isNT() ){
+ if( osIsNT() ){
res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
}
#ifdef SQLITE_WIN32_HAS_ANSI
@@ -2567,9 +2874,9 @@ static int unlockReadLock(winFile *pFile){
if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
pFile->lastErrno = lastErrno;
winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
- "unlockReadLock", pFile->zPath);
+ "winUnlockReadLock", pFile->zPath);
}
- OSTRACE(("READ-UNLOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
+ OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
@@ -2644,8 +2951,16 @@ static int winLock(sqlite3_file *id, int locktype){
** If you are using this code as a model for alternative VFSes, do not
** copy this retry logic. It is a hack intended for Windows only.
*/
- OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, rc=%s\n",
- pFile->h, cnt, sqlite3ErrName(res)));
+ lastErrno = osGetLastError();
+ OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
+ pFile->h, cnt, res));
+ if( lastErrno==ERROR_INVALID_HANDLE ){
+ pFile->lastErrno = lastErrno;
+ rc = SQLITE_IOERR_LOCK;
+ OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
+ pFile->h, cnt, sqlite3ErrName(rc)));
+ return rc;
+ }
if( cnt ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
@@ -2658,7 +2973,7 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
+ res = winGetReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
@@ -2689,14 +3004,14 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
+ res = winUnlockReadLock(pFile);
res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
lastErrno = osGetLastError();
- getReadLock(pFile);
+ winGetReadLock(pFile);
}
}
@@ -2713,10 +3028,10 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
rc = SQLITE_OK;
}else{
- OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
- pFile->h, locktype, newLocktype));
pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
+ OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
+ pFile->h, locktype, newLocktype));
}
pFile->locktype = (u8)newLocktype;
OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
@@ -2730,7 +3045,7 @@ static int winLock(sqlite3_file *id, int locktype){
** non-zero, otherwise zero.
*/
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
- int rc;
+ int res;
winFile *pFile = (winFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
@@ -2738,17 +3053,17 @@ static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
assert( id!=0 );
if( pFile->locktype>=RESERVED_LOCK ){
- rc = 1;
- OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (local)\n", pFile->h, rc));
+ res = 1;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
}else{
- rc = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
- if( rc ){
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
+ if( res ){
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
- rc = !rc;
- OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (remote)\n", pFile->h, rc));
+ res = !res;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
}
- *pResOut = rc;
+ *pResOut = res;
OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
pFile->h, pResOut, *pResOut));
return SQLITE_OK;
@@ -2776,18 +3091,18 @@ static int winUnlock(sqlite3_file *id, int locktype){
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
- if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
- "winUnlock", pFile->zPath);
+ "winUnlock", pFile->zPath);
}
}
if( type>=RESERVED_LOCK ){
winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- unlockReadLock(pFile);
+ winUnlockReadLock(pFile);
}
if( type>=PENDING_LOCK ){
winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
@@ -2814,8 +3129,10 @@ static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
}
}
-/* Forward declaration */
-static int getTempname(int nBuf, char *zBuf);
+/* Forward references to VFS helper methods used for temporary files */
+static int winGetTempname(sqlite3_vfs *, char **);
+static int winIsDir(const void *);
+static BOOL winIsDriveLetterAndColon(const char *);
/*
** Control and query of the open file handle.
@@ -2868,44 +3185,62 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
- *(char**)pArg = sqlite3_mprintf("win32");
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_WIN32_AV_RETRY: {
int *a = (int*)pArg;
if( a[0]>0 ){
- win32IoerrRetry = a[0];
+ winIoerrRetry = a[0];
}else{
- a[0] = win32IoerrRetry;
+ a[0] = winIoerrRetry;
}
if( a[1]>0 ){
- win32IoerrRetryDelay = a[1];
+ winIoerrRetryDelay = a[1];
}else{
- a[1] = win32IoerrRetryDelay;
+ a[1] = winIoerrRetryDelay;
}
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
+#ifdef SQLITE_TEST
+ case SQLITE_FCNTL_WIN32_SET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ HANDLE hOldFile = pFile->h;
+ pFile->h = *phFile;
+ *phFile = hOldFile;
+ OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
+ hOldFile, pFile->h));
+ return SQLITE_OK;
+ }
+#endif
case SQLITE_FCNTL_TEMPFILENAME: {
- char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname );
- if( zTFile ){
- getTempname(pFile->pVfs->mxPathname, zTFile);
+ char *zTFile = 0;
+ int rc = winGetTempname(pFile->pVfs, &zTFile);
+ if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
- OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
- return SQLITE_OK;
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
}
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
if( newLimit>sqlite3GlobalConfig.mxMmap ){
newLimit = sqlite3GlobalConfig.mxMmap;
}
*(i64*)pArg = pFile->mmapSizeMax;
- if( newLimit>=0 ) pFile->mmapSizeMax = newLimit;
- OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
- return SQLITE_OK;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ winUnmapfile(pFile);
+ rc = winMapfile(pFile, -1);
+ }
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
}
#endif
}
@@ -2937,23 +3272,23 @@ static int winDeviceCharacteristics(sqlite3_file *id){
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
-/*
+/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
** to get the granularity size.
*/
-SYSTEM_INFO winSysInfo;
+static SYSTEM_INFO winSysInfo;
#ifndef SQLITE_OMIT_WAL
/*
** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect the winLockInfo objects used by
+** global mutex is used to protect the winLockInfo objects used by
** this file, all of which may be shared by multiple threads.
**
-** Function winShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
+** Function winShmMutexHeld() is used to assert() that the global mutex
+** is held when required. This function is only used as part of assert()
** statements. e.g.
**
** winShmEnterMutex()
@@ -2966,7 +3301,7 @@ static void winShmEnterMutex(void){
static void winShmLeaveMutex(void){
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
-#ifdef SQLITE_DEBUG
+#ifndef NDEBUG
static int winShmMutexHeld(void) {
return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
}
@@ -2983,10 +3318,10 @@ static int winShmMutexHeld(void) {
** this object or while reading or writing the following fields:
**
** nRef
-** pNext
+** pNext
**
** The following fields are read-only after the object is created:
-**
+**
** fid
** zFilename
**
@@ -3082,7 +3417,7 @@ static int winShmSystemLock(
if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
}
-
+
if( rc!= 0 ){
rc = SQLITE_OK;
}else{
@@ -3110,7 +3445,6 @@ static int winDelete(sqlite3_vfs *,const char*,int);
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
- BOOL bRc;
assert( winShmMutexHeld() );
OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
osGetCurrentProcessId(), deleteFlag));
@@ -3118,14 +3452,16 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
int i;
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
- bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
bRc = osCloseHandle(p->aRegion[i].hMap);
OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
}
if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
SimulateIOErrorBenign(1);
@@ -3177,7 +3513,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
}
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
- sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
+ sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, create a new one.
@@ -3214,13 +3550,13 @@ static int winOpenSharedMemory(winFile *pDbFd){
}
/* Check to see if another process is holding the dead-man switch.
- ** If not, truncate the file to zero length.
+ ** If not, truncate the file to zero length.
*/
if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
- "winOpenShm", pDbFd->zPath);
+ "winOpenShm", pDbFd->zPath);
}
}
if( rc==SQLITE_OK ){
@@ -3243,7 +3579,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
** the cover of the winShmEnterMutex() mutex and the pointer from the
** new (struct winShm) object to the pShmNode has been set. All that is
** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
+ ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
** mutex.
*/
sqlite3_mutex_enter(pShmNode->mutex);
@@ -3263,7 +3599,7 @@ shm_open_err:
}
/*
-** Close a connection to shared-memory. Delete the underlying
+** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
@@ -3352,7 +3688,7 @@ static int winShmLock(
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
- }
+ }
}else if( flags & SQLITE_SHM_SHARED ){
u16 allShared = 0; /* Union of locks held by connections other than "p" */
@@ -3391,7 +3727,7 @@ static int winShmLock(
break;
}
}
-
+
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
@@ -3411,7 +3747,7 @@ static int winShmLock(
}
/*
-** Implement a memory barrier or memory fence on shared memory.
+** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
@@ -3426,22 +3762,22 @@ static void winShmBarrier(
}
/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file fd. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
+** This function is called to obtain a pointer to region iRegion of the
+** shared-memory associated with the database file fd. Shared-memory regions
+** are numbered starting from zero. Each shared-memory region is szRegion
** bytes in size.
**
** If an error occurs, an error code is returned and *pp is set to NULL.
**
** Otherwise, if the isWrite parameter is 0 and the requested shared-memory
** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** isWrite is non-zero and the requested shared-memory region has not yet
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If
+** isWrite is non-zero and the requested shared-memory region has not yet
** been allocated, it is allocated by this function.
**
** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
+** this call as described above, then it is mapped into this processes
+** address space (if it is not already), *pp is set to point to the mapped
** memory and SQLITE_OK returned.
*/
static int winShmMap(
@@ -3480,7 +3816,7 @@ static int winShmMap(
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap1", pDbFd->zPath);
+ "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -3495,7 +3831,7 @@ static int winShmMap(
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
if( rc!=SQLITE_OK ){
rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap2", pDbFd->zPath);
+ "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
@@ -3513,17 +3849,17 @@ static int winShmMap(
while( pShmNode->nRegion<=iRegion ){
HANDLE hMap = NULL; /* file-mapping handle */
void *pMap = 0; /* Mapped memory region */
-
+
#if SQLITE_OS_WINRT
hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
NULL, PAGE_READWRITE, nByte, NULL
);
#elif defined(SQLITE_WIN32_HAS_WIDE)
- hMap = osCreateFileMappingW(pShmNode->hFile.h,
+ hMap = osCreateFileMappingW(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
#elif defined(SQLITE_WIN32_HAS_ANSI)
- hMap = osCreateFileMappingA(pShmNode->hFile.h,
+ hMap = osCreateFileMappingA(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
#endif
@@ -3549,7 +3885,7 @@ static int winShmMap(
if( !pMap ){
pShmNode->lastErrno = osGetLastError();
rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno,
- "winShmMap3", pDbFd->zPath);
+ "winShmMap3", pDbFd->zPath);
if( hMap ) osCloseHandle(hMap);
goto shmpage_out;
}
@@ -3597,7 +3933,7 @@ static int winUnmapfile(winFile *pFile){
"rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
pFile->pMapRegion));
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
- "winUnmap1", pFile->zPath);
+ "winUnmapfile1", pFile->zPath);
}
pFile->pMapRegion = 0;
pFile->mmapSize = 0;
@@ -3609,7 +3945,7 @@ static int winUnmapfile(winFile *pFile){
OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
osGetCurrentProcessId(), pFile, pFile->hMap));
return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
- "winUnmap2", pFile->zPath);
+ "winUnmapfile2", pFile->zPath);
}
pFile->hMap = NULL;
}
@@ -3620,14 +3956,14 @@ static int winUnmapfile(winFile *pFile){
/*
** Memory map or remap the file opened by file-descriptor pFd (if the file
-** is already mapped, the existing mapping is replaced by the new). Or, if
-** there already exists a mapping for this file, and there are still
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
** outstanding xFetch() references to it, this function is a no-op.
**
-** If parameter nByte is non-negative, then it is the requested size of
-** the mapping to create. Otherwise, if nByte is less than zero, then the
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
** requested size is the size of the file on disk. The actual size of the
-** created mapping is either the requested size or the value configured
+** created mapping is either the requested size or the value configured
** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller.
**
** SQLITE_OK is returned if no error occurs (even if the mapping is not
@@ -3656,7 +3992,7 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
nMap = pFd->mmapSizeMax;
}
nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1);
-
+
if( nMap==0 && pFd->mmapSize>0 ){
winUnmapfile(pFd);
}
@@ -3684,27 +4020,28 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
if( pFd->hMap==NULL ){
pFd->lastErrno = osGetLastError();
rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
- "winMapfile", pFd->zPath);
+ "winMapfile1", pFd->zPath);
/* Log the error, but continue normal operation using xRead/xWrite */
- OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n",
- osGetCurrentProcessId(), pFd));
+ OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return SQLITE_OK;
}
assert( (nMap % winSysInfo.dwPageSize)==0 );
+ assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
#if SQLITE_OS_WINRT
- pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, nMap);
+ pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
#else
- assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
#endif
if( pNew==NULL ){
osCloseHandle(pFd->hMap);
pFd->hMap = NULL;
pFd->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
- "winMapfile", pFd->zPath);
- OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=SQLITE_IOERR_MMAP\n",
- osGetCurrentProcessId(), pFd));
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile2", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
return SQLITE_OK;
}
pFd->pMapRegion = pNew;
@@ -3727,7 +4064,7 @@ static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
** Finally, if an error does occur, return an SQLite error code. The final
** value of *pp is undefined in this case.
**
-** If this function does return a pointer, the caller must eventually
+** If this function does return a pointer, the caller must eventually
** release the reference by calling winUnfetch().
*/
static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
@@ -3762,20 +4099,20 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
}
/*
-** If the third argument is non-NULL, then this function releases a
+** If the third argument is non-NULL, then this function releases a
** reference obtained by an earlier call to winFetch(). The second
** argument passed to this function must be the same as the corresponding
-** argument that was passed to the winFetch() invocation.
+** argument that was passed to the winFetch() invocation.
**
-** Or, if the third argument is NULL, then this function is being called
-** to inform the VFS layer that, according to POSIX, any existing mapping
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
** may now be invalid and should be unmapped.
*/
static int winUnfetch(sqlite3_file *fd, i64 iOff, void *p){
#if SQLITE_MAX_MMAP_SIZE>0
winFile *pFd = (winFile*)fd; /* The underlying database file */
- /* If p==0 (unmap the entire file) then there must be no outstanding
+ /* If p==0 (unmap the entire file) then there must be no outstanding
** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
** then there must be at least one outstanding. */
assert( (p==0)==(pFd->nFetchOut==0) );
@@ -3843,16 +4180,37 @@ static const sqlite3_io_methods winIoMethod = {
** sqlite3_vfs object.
*/
+#if defined(__CYGWIN__)
+/*
+** Convert a filename from whatever the underlying operating system
+** supports for filenames into UTF-8. Space to hold the result is
+** obtained from malloc and must be freed by the calling function.
+*/
+static char *winConvertToUtf8Filename(const void *zFilename){
+ char *zConverted = 0;
+ if( osIsNT() ){
+ zConverted = winUnicodeToUtf8(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = sqlite3_win32_mbcs_to_utf8(zFilename);
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+#endif
+
/*
** 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 void *convertUtf8Filename(const char *zFilename){
+static void *winConvertFromUtf8Filename(const char *zFilename){
void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
+ if( osIsNT() ){
+ zConverted = winUtf8ToUnicode(zFilename);
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
@@ -3864,39 +4222,180 @@ static void *convertUtf8Filename(const char *zFilename){
}
/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
+** This function returns non-zero if the specified UTF-8 string buffer
+** ends with a directory separator character or one was successfully
+** added to it.
+*/
+static int winMakeEndInDirSep(int nBuf, char *zBuf){
+ if( zBuf ){
+ int nLen = sqlite3Strlen30(zBuf);
+ if( nLen>0 ){
+ if( winIsDirSep(zBuf[nLen-1]) ){
+ return 1;
+ }else if( nLen+1<nBuf ){
+ zBuf[nLen] = winGetDirSep();
+ zBuf[nLen+1] = '\0';
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Create a temporary file name and store the resulting pointer into pzBuf.
+** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
-static int getTempname(int nBuf, char *zBuf){
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
- int nTempPath;
- char zTempPath[MAX_PATH+2];
+ int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
+ int nMax, nBuf, nDir, nLen;
+ char *zBuf;
/* 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.
+ ** function failing.
*/
SimulateIOError( return SQLITE_IOERR );
- memset(zTempPath, 0, MAX_PATH+2);
+ /* Allocate a temporary buffer to store the fully qualified file
+ ** name for the temporary file. If this fails, we cannot continue.
+ */
+ nMax = pVfs->mxPathname; nBuf = nMax + 2;
+ zBuf = sqlite3MallocZero( nBuf );
+ if( !zBuf ){
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ /* Figure out the effective temporary directory. First, check if one
+ ** has been explicitly set by the application; otherwise, use the one
+ ** configured by the operating system.
+ */
+ nDir = nMax - (nPre + 15);
+ assert( nDir>0 );
if( sqlite3_temp_directory ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
+ int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
+ if( nDirLen>0 ){
+ if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
+ nDirLen++;
+ }
+ if( nDirLen>nDir ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
+ }
}
-#if !SQLITE_OS_WINRT
- else if( isNT() ){
+#if defined(__CYGWIN__)
+ else{
+ static const char *azDirs[] = {
+ 0, /* getenv("SQLITE_TMPDIR") */
+ 0, /* getenv("TMPDIR") */
+ 0, /* getenv("TMP") */
+ 0, /* getenv("TEMP") */
+ 0, /* getenv("USERPROFILE") */
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ 0 /* List terminator */
+ };
+ unsigned int i;
+ const char *zDir = 0;
+
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMP");
+ if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
+ if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
+ void *zConverted;
+ if( zDir==0 ) continue;
+ /* If the path starts with a drive letter followed by the colon
+ ** character, assume it is already a native Win32 path; otherwise,
+ ** it must be converted to a native Win32 path via the Cygwin API
+ ** prior to using it.
+ */
+ if( winIsDriveLetterAndColon(zDir) ){
+ zConverted = winConvertFromUtf8Filename(zDir);
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( winIsDir(zConverted) ){
+ sqlite3_snprintf(nMax, zBuf, "%s", zDir);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }else{
+ zConverted = sqlite3MallocZero( nMax+1 );
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
+ zConverted, nMax+1)<0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
+ return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
+ "winGetTempname2", zDir);
+ }
+ if( winIsDir(zConverted) ){
+ /* At this point, we know the candidate directory exists and should
+ ** be used. However, we may need to convert the string containing
+ ** its name into UTF-8 (i.e. if it is UTF-16 right now).
+ */
+ char *zUtf8 = winConvertToUtf8Filename(zConverted);
+ if( !zUtf8 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ }
+#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ else if( osIsNT() ){
char *zMulti;
- WCHAR zWidePath[MAX_PATH];
- osGetTempPathW(MAX_PATH-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
+ LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
+ if( !zWidePath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathW(nMax, zWidePath)==0 ){
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname2", 0);
+ }
+ zMulti = winUnicodeToUtf8(zWidePath);
if( zMulti ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
+ sqlite3_snprintf(nMax, zBuf, "%s", zMulti);
sqlite3_free(zMulti);
+ sqlite3_free(zWidePath);
}else{
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
@@ -3904,36 +4403,62 @@ static int getTempname(int nBuf, char *zBuf){
#ifdef SQLITE_WIN32_HAS_ANSI
else{
char *zUtf8;
- char zMbcsPath[MAX_PATH];
- osGetTempPathA(MAX_PATH-30, zMbcsPath);
+ char *zMbcsPath = sqlite3MallocZero( nMax );
+ if( !zMbcsPath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathA(nMax, zMbcsPath)==0 ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname3", 0);
+ }
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
if( zUtf8 ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8);
}else{
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
}
-#endif
-#endif
+#endif /* SQLITE_WIN32_HAS_ANSI */
+#endif /* !SQLITE_OS_WINRT */
- /* Check that the output buffer is large enough for the temporary file
- ** name. If it is not, return SQLITE_ERROR.
+ /*
+ ** Check to make sure the temporary directory ends with an appropriate
+ ** separator. If it does not and there is not enough space left to add
+ ** one, fail.
*/
- nTempPath = sqlite3Strlen30(zTempPath);
+ if( !winMakeEndInDirSep(nDir+1, zBuf) ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0);
+ }
- if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ /*
+ ** Check that the output buffer is large enough for the temporary file
+ ** name in the following format:
+ **
+ ** "<temporary_directory>/etilqs_XXXXXXXXXXXXXXX\0\0"
+ **
+ ** If not, return SQLITE_ERROR. The number 17 is used here in order to
+ ** account for the space used by the 15 character random suffix and the
+ ** two trailing NUL characters. The final directory separator character
+ ** has already added if it was not already present.
+ */
+ nLen = sqlite3Strlen30(zBuf);
+ if( (nLen + nPre + 17) > nBuf ){
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
- return SQLITE_ERROR;
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0);
}
- for(i=nTempPath; i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
- sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
- "%s\\"SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
- zTempPath);
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
@@ -3941,6 +4466,7 @@ static int getTempname(int nBuf, char *zBuf){
}
zBuf[j] = 0;
zBuf[j+1] = 0;
+ *pzBuf = zBuf;
OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
return SQLITE_OK;
@@ -3956,13 +4482,13 @@ static int winIsDir(const void *zConverted){
int rc = 0;
DWORD lastErrno;
- if( isNT() ){
+ if( osIsNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
if( !rc ){
return 0; /* Invalid name? */
}
@@ -3979,14 +4505,14 @@ static int winIsDir(const void *zConverted){
** Open a file.
*/
static int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
+ sqlite3_vfs *pVfs, /* Used to get maximum path name length */
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;
- DWORD lastErrno;
+ DWORD lastErrno = 0;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwCreationDisposition;
@@ -4002,7 +4528,7 @@ static int winOpen(
/* 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_PATH+2]; /* Buffer used to create temp filename */
+ char *zTmpname = 0; /* For temporary filename, if necessary. */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
@@ -4017,8 +4543,8 @@ static int winOpen(
#ifndef NDEBUG
int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
+ eType==SQLITE_OPEN_MASTER_JOURNAL
+ || eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
#endif
@@ -4026,9 +4552,9 @@ static int winOpen(
OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n",
zUtf8Name, id, flags, pOutFlags));
- /* Check the following statements are true:
+ /* Check the following statements are true:
**
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
+ ** (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.
@@ -4038,7 +4564,7 @@ static int winOpen(
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
- /* The main DB, main journal, WAL file and master journal are never
+ /* The main DB, main journal, WAL file and master journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
@@ -4046,9 +4572,9 @@ static int winOpen(
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* 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
+ 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 || eType==SQLITE_OPEN_WAL
);
@@ -4057,19 +4583,18 @@ static int winOpen(
pFile->h = INVALID_HANDLE_VALUE;
#if SQLITE_OS_WINRT
- if( !sqlite3_temp_directory ){
+ if( !zUtf8Name && !sqlite3_temp_directory ){
sqlite3_log(SQLITE_ERROR,
"sqlite3_temp_directory variable should be set for WinRT");
}
#endif
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
*/
if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- memset(zTmpname, 0, MAX_PATH+2);
- rc = getTempname(MAX_PATH+2, zTmpname);
+ assert( isDelete && !isOpenJournal );
+ rc = winGetTempname(pVfs, &zTmpname);
if( rc!=SQLITE_OK ){
OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
return rc;
@@ -4082,17 +4607,19 @@ static int winOpen(
** sqlite3_uri_parameter().
*/
assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) ||
- zUtf8Name[strlen(zUtf8Name)+1]==0 );
+ zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
/* Convert the filename to the system encoding. */
- zConverted = convertUtf8Filename(zUtf8Name);
+ zConverted = winConvertFromUtf8Filename(zUtf8Name);
if( zConverted==0 ){
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
return SQLITE_IOERR_NOMEM;
}
if( winIsDir(zConverted) ){
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
return SQLITE_CANTOPEN_ISDIR;
}
@@ -4103,8 +4630,8 @@ static int winOpen(
dwDesiredAccess = GENERIC_READ;
}
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
+ /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
+ ** created. SQLite doesn't use it to indicate "exclusive access"
** as it is usually understood.
*/
if( isExclusive ){
@@ -4139,7 +4666,7 @@ static int winOpen(
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
@@ -4154,7 +4681,7 @@ static int winOpen(
dwShareMode,
dwCreationDisposition,
&extendedParameters))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
#else
@@ -4164,7 +4691,7 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
#endif
@@ -4177,12 +4704,12 @@ static int winOpen(
dwCreationDisposition,
dwFlagsAndAttributes,
NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt, &lastErrno) ){
+ winRetryIoerr(&cnt, &lastErrno) ){
/* Noop */
}
}
#endif
- logIoerr(cnt);
+ winLogIoerr(cnt);
OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
@@ -4191,8 +4718,9 @@ static int winOpen(
pFile->lastErrno = lastErrno;
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
if( isReadWrite && !isExclusive ){
- return winOpen(pVfs, zName, id,
+ return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY) &
~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
pOutFlags);
@@ -4219,6 +4747,7 @@ static int winOpen(
){
osCloseHandle(h);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
return rc;
}
@@ -4230,6 +4759,7 @@ static int winOpen(
sqlite3_free(zConverted);
}
+ sqlite3_free(zTmpname);
pFile->pMethod = &winIoMethod;
pFile->pVfs = pVfs;
pFile->h = h;
@@ -4273,7 +4803,7 @@ static int winDelete(
int cnt = 0;
int rc;
DWORD attr;
- DWORD lastErrno;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
@@ -4281,11 +4811,12 @@ static int winDelete(
SimulateIOError(return SQLITE_IOERR_DELETE);
OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
- zConverted = convertUtf8Filename(zFilename);
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
+ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
do {
#if SQLITE_OS_WINRT
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
@@ -4324,7 +4855,7 @@ static int winDelete(
rc = SQLITE_OK; /* Deleted OK. */
break;
}
- if ( !retryIoerr(&cnt, &lastErrno) ){
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
@@ -4352,7 +4883,7 @@ static int winDelete(
rc = SQLITE_OK; /* Deleted OK. */
break;
}
- if ( !retryIoerr(&cnt, &lastErrno) ){
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
rc = SQLITE_ERROR; /* No more retries. */
break;
}
@@ -4360,10 +4891,9 @@ static int winDelete(
}
#endif
if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){
- rc = winLogError(SQLITE_IOERR_DELETE, lastErrno,
- "winDelete", zFilename);
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename);
}else{
- logIoerr(cnt);
+ winLogIoerr(cnt);
}
sqlite3_free(zConverted);
OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
@@ -4381,7 +4911,7 @@ static int winAccess(
){
DWORD attr;
int rc = 0;
- DWORD lastErrno;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
@@ -4389,35 +4919,35 @@ static int winAccess(
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
- zConverted = convertUtf8Filename(zFilename);
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
- GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt, &lastErrno) ){}
+ GetFileExInfoStandard,
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
*/
if( flags==SQLITE_ACCESS_EXISTS
- && sAttrData.nFileSizeHigh==0
+ && sAttrData.nFileSizeHigh==0
&& sAttrData.nFileSizeLow==0 ){
attr = INVALID_FILE_ATTRIBUTES;
}else{
attr = sAttrData.dwFileAttributes;
}
}else{
- logIoerr(cnt);
+ winLogIoerr(cnt);
if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
- winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", zFilename);
sqlite3_free(zConverted);
- return SQLITE_IOERR_ACCESS;
+ return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
+ zFilename);
}else{
attr = INVALID_FILE_ATTRIBUTES;
}
@@ -4447,6 +4977,15 @@ static int winAccess(
return SQLITE_OK;
}
+/*
+** Returns non-zero if the specified path name starts with a drive letter
+** followed by a colon character.
+*/
+static BOOL winIsDriveLetterAndColon(
+ const char *zPathname
+){
+ return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
+}
/*
** Returns non-zero if the specified path name should be used verbatim. If
@@ -4464,7 +5003,7 @@ static BOOL winIsVerbatimPathname(
** the final two cases; therefore, we return the safer return value of TRUE
** so that callers of this function will simply use it verbatim.
*/
- if ( zPathname[0]=='/' || zPathname[0]=='\\' ){
+ if ( winIsDirSep(zPathname[0]) ){
return TRUE;
}
@@ -4474,7 +5013,7 @@ static BOOL winIsVerbatimPathname(
** attempt to treat it as a relative path name (i.e. they should simply use
** it verbatim).
*/
- if ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ){
+ if ( winIsDriveLetterAndColon(zPathname) ){
return TRUE;
}
@@ -4496,11 +5035,10 @@ static int winFullPathname(
int nFull, /* Size of output buffer in bytes */
char *zFull /* Output buffer */
){
-
+
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- assert( pVfs->mxPathname>=MAX_PATH );
assert( nFull>=pVfs->mxPathname );
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
/*
@@ -4509,14 +5047,48 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a slash.
*/
- char zOut[MAX_PATH+1];
- memset(zOut, 0, MAX_PATH+1);
- cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
- MAX_PATH+1);
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zOut);
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
+ CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname1", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
}else{
- cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull);
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
+ zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname2", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
}
return SQLITE_OK;
#endif
@@ -4532,8 +5104,8 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a backslash.
*/
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zRelative);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
}else{
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
}
@@ -4548,7 +5120,7 @@ static int winFullPathname(
/* If this path name begins with "/X:", where "X" is any alphabetic
** character, discard the initial "/" from the pathname.
*/
- if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
+ if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
zRelative++;
}
@@ -4565,22 +5137,21 @@ static int winFullPathname(
** for converting the relative path name to an absolute
** one by prepending the data directory and a backslash.
*/
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s\\%s",
- sqlite3_data_directory, zRelative);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
return SQLITE_OK;
}
- zConverted = convertUtf8Filename(zRelative);
+ zConverted = winConvertFromUtf8Filename(zRelative);
if( zConverted==0 ){
return SQLITE_IOERR_NOMEM;
}
- if( isNT() ){
+ if( osIsNT() ){
LPWSTR zTemp;
nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameW1", zConverted);
sqlite3_free(zConverted);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname1", zRelative);
}
nByte += 3;
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
@@ -4590,14 +5161,13 @@ static int winFullPathname(
}
nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameW2", zConverted);
sqlite3_free(zConverted);
sqlite3_free(zTemp);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname2", zRelative);
}
sqlite3_free(zConverted);
- zOut = unicodeToUtf8(zTemp);
+ zOut = winUnicodeToUtf8(zTemp);
sqlite3_free(zTemp);
}
#ifdef SQLITE_WIN32_HAS_ANSI
@@ -4605,10 +5175,9 @@ static int winFullPathname(
char *zTemp;
nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameA1", zConverted);
sqlite3_free(zConverted);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname3", zRelative);
}
nByte += 3;
zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
@@ -4618,11 +5187,10 @@ static int winFullPathname(
}
nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
if( nByte==0 ){
- winLogError(SQLITE_ERROR, osGetLastError(),
- "GetFullPathNameA2", zConverted);
sqlite3_free(zConverted);
sqlite3_free(zTemp);
- return SQLITE_CANTOPEN_FULLPATH;
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname4", zRelative);
}
sqlite3_free(zConverted);
zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
@@ -4644,18 +5212,32 @@ static int winFullPathname(
** 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 *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
+#if defined(__CYGWIN__)
+ int nFull = pVfs->mxPathname+1;
+ char *zFull = sqlite3MallocZero( nFull );
+ void *zConverted = 0;
+ if( zFull==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){
+ sqlite3_free(zFull);
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ zConverted = winConvertFromUtf8Filename(zFull);
+ sqlite3_free(zFull);
+#else
+ void *zConverted = winConvertFromUtf8Filename(zFilename);
UNUSED_PARAMETER(pVfs);
+#endif
if( zConverted==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
return 0;
}
- if( isNT() ){
+ if( osIsNT() ){
#if SQLITE_OS_WINRT
h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
#else
@@ -4667,20 +5249,26 @@ static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
h = osLoadLibraryA((char*)zConverted);
}
#endif
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
sqlite3_free(zConverted);
return (void*)h;
}
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
UNUSED_PARAMETER(pVfs);
- getLastErrorMsg(osGetLastError(), nBuf, zBufOut);
+ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut);
}
static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){
+ FARPROC proc;
UNUSED_PARAMETER(pVfs);
- return (void(*)(void))osGetProcAddressA((HANDLE)pH, zSym);
+ proc = osGetProcAddressA((HANDLE)pH, zSym);
+ OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n",
+ (void*)pH, zSym, (void*)proc));
+ return (void(*)(void))proc;
}
static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
UNUSED_PARAMETER(pVfs);
osFreeLibrary((HANDLE)pHandle);
+ OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle));
}
#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
#define winDlOpen 0
@@ -4760,12 +5348,12 @@ int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
** cannot be found.
*/
static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
- /* FILETIME structure is a 64-bit value representing the number of
- 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
+ /* FILETIME structure is a 64-bit value representing the number of
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
FILETIME ft;
static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
@@ -4773,7 +5361,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
#endif
/* 2^32 - to avoid use of LL and warnings in gcc */
- static const sqlite3_int64 max32BitValue =
+ static const sqlite3_int64 max32BitValue =
(sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 +
(sqlite3_int64)294967296;
@@ -4789,7 +5377,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
#endif
*piNow = winFiletimeEpoch +
- ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
+ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
(sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000;
#ifdef SQLITE_TEST
@@ -4848,7 +5436,7 @@ static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
*/
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
UNUSED_PARAMETER(pVfs);
- return getLastErrorMsg(osGetLastError(), nBuf, zBuf);
+ return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
}
/*
@@ -4858,7 +5446,7 @@ int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
3, /* iVersion */
sizeof(winFile), /* szOsFile */
- MAX_PATH, /* mxPathname */
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
0, /* pNext */
"win32", /* zName */
0, /* pAppData */
@@ -4879,10 +5467,36 @@ int sqlite3_os_init(void){
winGetSystemCall, /* xGetSystemCall */
winNextSystemCall, /* xNextSystemCall */
};
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath", /* 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 */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==74 );
+ assert( ArraySize(aSyscall)==77 );
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
@@ -4895,10 +5509,15 @@ int sqlite3_os_init(void){
assert( winSysInfo.dwPageSize>0 );
sqlite3_vfs_register(&winVfs, 1);
- return SQLITE_OK;
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathVfs, 0);
+#endif
+
+ return SQLITE_OK;
}
-int sqlite3_os_end(void){
+int sqlite3_os_end(void){
#if SQLITE_OS_WINRT
if( sleepObj!=NULL ){
osCloseHandle(sleepObj);
diff --git a/src/os_win.h b/src/os_win.h
new file mode 100644
index 0000000..d662cd4
--- /dev/null
+++ b/src/os_win.h
@@ -0,0 +1,67 @@
+/*
+** 2013 November 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 code that is specific to Windows.
+*/
+#ifndef _OS_WIN_H_
+#define _OS_WIN_H_
+
+/*
+** Include the primary Windows SDK header file.
+*/
+#include "windows.h"
+
+#ifdef __CYGWIN__
+# include <sys/cygwin.h>
+# include <errno.h> /* amalgamator: dontcache */
+#endif
+
+/*
+** Determine if we are dealing with Windows NT.
+**
+** We ought to be able to determine if we are compiling for Windows 9x or
+** Windows NT using the _WIN32_WINNT macro as follows:
+**
+** #if defined(_WIN32_WINNT)
+** # define SQLITE_OS_WINNT 1
+** #else
+** # define SQLITE_OS_WINNT 0
+** #endif
+**
+** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
+** it ought to, so the above test does not work. We'll just assume that
+** everything is Windows NT unless the programmer explicitly says otherwise
+** by setting SQLITE_OS_WINNT to 0.
+*/
+#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
+# define SQLITE_OS_WINNT 1
+#endif
+
+/*
+** Determine if we are dealing with Windows CE - which has a much reduced
+** API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
+/*
+** Determine if we are dealing with WinRT, which provides only a subset of
+** the full Win32 API.
+*/
+#if !defined(SQLITE_OS_WINRT)
+# define SQLITE_OS_WINRT 0
+#endif
+
+#endif /* _OS_WIN_H_ */
diff --git a/src/pager.c b/src/pager.c
index 61727fa..e18eb4b 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -454,6 +454,13 @@ struct PagerSavepoint {
};
/*
+** Bits of the Pager.doNotSpill flag. See further description below.
+*/
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
+
+/*
** A open page cache is an instance of struct Pager. A description of
** some of the more important member variables follows:
**
@@ -519,19 +526,21 @@ struct PagerSavepoint {
** journal file from being successfully finalized, the setMaster flag
** is cleared anyway (and the pager will move to ERROR state).
**
-** doNotSpill, doNotSyncSpill
+** doNotSpill
**
-** These two boolean variables control the behavior of cache-spills
-** (calls made by the pcache module to the pagerStress() routine to
-** write cached data to the file-system in order to free up memory).
+** This variables control the behavior of cache-spills (calls made by
+** the pcache module to the pagerStress() routine to write cached data
+** to the file-system in order to free up memory).
**
-** When doNotSpill is non-zero, writing to the database from pagerStress()
-** is disabled altogether. This is done in a very obscure case that
+** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set,
+** writing to the database from pagerStress() is disabled altogether.
+** The SPILLFLAG_ROLLBACK case is done in a very obscure case that
** comes up during savepoint rollback that requires the pcache module
** to allocate a new page to prevent the journal file from being written
-** while it is being traversed by code in pager_playback().
+** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
+** case is a user preference.
**
-** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from pagerStress()
** is permitted, but syncing the journal file is not. This flag is set
** by sqlite3PagerWrite() when the file-system sector-size is larger than
** the database page-size in order to prevent a journal sync from happening
@@ -617,7 +626,8 @@ struct Pager {
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
- u8 tempFile; /* zFilename is a temporary file */
+ u8 tempFile; /* zFilename is a temporary or immutable file */
+ u8 noLock; /* Do not lock (except in WAL mode) */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
@@ -635,7 +645,6 @@ struct Pager {
u8 changeCountDone; /* Set after incrementing the change-counter */
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
- u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
u8 subjInMemory; /* True to use in-memory sub-journals */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
@@ -1014,11 +1023,12 @@ static char *print_pager_state(Pager *p){
** PagerSavepoint.pInSavepoint.
*/
static int subjRequiresPage(PgHdr *pPg){
- Pgno pgno = pPg->pgno;
Pager *pPager = pPg->pPager;
+ PagerSavepoint *p;
+ Pgno pgno = pPg->pgno;
int i;
for(i=0; i<pPager->nSavepoint; i++){
- PagerSavepoint *p = &pPager->aSavepoint[i];
+ p = &pPager->aSavepoint[i];
if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
return 1;
}
@@ -1029,8 +1039,8 @@ static int subjRequiresPage(PgHdr *pPg){
/*
** Return true if the page is already in the journal file.
*/
-static int pageInJournal(PgHdr *pPg){
- return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
+static int pageInJournal(Pager *pPager, PgHdr *pPg){
+ return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
}
/*
@@ -1082,7 +1092,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
- rc = sqlite3OsUnlock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
if( pPager->eLock!=UNKNOWN_LOCK ){
pPager->eLock = (u8)eLock;
}
@@ -1106,7 +1116,7 @@ static int pagerLockDb(Pager *pPager, int eLock){
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
- rc = sqlite3OsLock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsLock(pPager->fd, eLock);
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
pPager->eLock = (u8)eLock;
IOTRACE(("LOCK %p %d\n", pPager, eLock))
@@ -1237,6 +1247,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|| szJ<16
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|| len>=nMaster
+ || len==0
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|| memcmp(aMagic, aJournalMagic, 8)
@@ -1614,12 +1625,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
if( !zMaster
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
- || pPager->journalMode==PAGER_JOURNALMODE_OFF
+ || !isOpen(pPager->jfd)
){
return SQLITE_OK;
}
pPager->setMaster = 1;
- assert( isOpen(pPager->jfd) );
assert( pPager->journalHdr <= pPager->journalOff );
/* Calculate the length in bytes and the checksum of zMaster */
@@ -1673,7 +1683,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
** already in memory.
*/
static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
- PgHdr *p; /* Return value */
+ PgHdr *p = 0; /* Return value */
/* It is not possible for a call to PcacheFetch() with createFlag==0 to
** fail, since no attempt to allocate dynamic memory will be made.
@@ -1812,6 +1822,7 @@ static void pager_unlock(Pager *pPager){
pPager->changeCountDone = pPager->tempFile;
pPager->eState = PAGER_OPEN;
pPager->errCode = SQLITE_OK;
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
pPager->journalOff = 0;
@@ -1976,7 +1987,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
PgHdr *p = pager_lookup(pPager, 1);
if( p ){
p->pageHash = 0;
- sqlite3PagerUnref(p);
+ sqlite3PagerUnrefNotNull(p);
}
}
#endif
@@ -2005,6 +2016,11 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
rc = pager_truncate(pPager, pPager->dbSize);
}
+ if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ }
+
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -2294,11 +2310,11 @@ static int pager_playback_one_page(
** requiring a journal-sync before it is written.
*/
assert( isSavepnt );
- assert( pPager->doNotSpill==0 );
- pPager->doNotSpill++;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
+ pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
- assert( pPager->doNotSpill==1 );
- pPager->doNotSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
if( rc!=SQLITE_OK ) return rc;
pPg->flags &= ~PGHDR_NEED_READ;
sqlite3PcacheMakeDirty(pPg);
@@ -2818,7 +2834,7 @@ end_playback:
if( rc==SQLITE_OK
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, 0);
}
if( rc==SQLITE_OK ){
rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
@@ -2865,12 +2881,6 @@ static int readDbPage(PgHdr *pPg, u32 iFrame){
assert( pPager->eState>=PAGER_READER && !MEMDB );
assert( isOpen(pPager->fd) );
- if( NEVER(!isOpen(pPager->fd)) ){
- assert( pPager->tempFile );
- memset(pPg->pData, 0, pPager->pageSize);
- return SQLITE_OK;
- }
-
#ifndef SQLITE_OMIT_WAL
if( iFrame ){
/* Try to pull the page from the write-ahead log. */
@@ -2970,7 +2980,7 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
if( rc==SQLITE_OK ){
pPager->xReiniter(pPg);
}
- sqlite3PagerUnref(pPg);
+ sqlite3PagerUnrefNotNull(pPg);
}
}
@@ -3378,10 +3388,10 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
static void pagerFixMaplimit(Pager *pPager){
#if SQLITE_MAX_MMAP_SIZE>0
sqlite3_file *fd = pPager->fd;
- if( isOpen(fd) ){
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){
sqlite3_int64 sz;
- pPager->bUseFetch = (fd->pMethods->iVersion>=3) && pPager->szMmap>0;
sz = pPager->szMmap;
+ pPager->bUseFetch = (sz>0);
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
}
#endif
@@ -3403,9 +3413,12 @@ void sqlite3PagerShrink(Pager *pPager){
}
/*
-** Adjust the robustness of the database to damage due to OS crashes
-** or power failures by changing the number of syncs()s when writing
-** the rollback journal. There are three levels:
+** Adjust settings of the pager to those specified in the pgFlags parameter.
+**
+** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
+** of the database to damage due to OS crashes or power failures by
+** changing the number of syncs()s when writing the journals.
+** There are three levels:
**
** OFF sqlite3OsSync() is never called. This is the default
** for temporary and transient files.
@@ -3446,22 +3459,21 @@ void sqlite3PagerShrink(Pager *pPager){
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-void sqlite3PagerSetSafetyLevel(
+void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int bFullFsync, /* PRAGMA fullfsync */
- int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
+ unsigned pgFlags /* Various flags */
){
+ unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
assert( level>=1 && level<=3 );
pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
- }else if( bFullFsync ){
+ }else if( pgFlags & PAGER_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
- }else if( bCkptFullFsync ){
+ }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
@@ -3472,6 +3484,11 @@ void sqlite3PagerSetSafetyLevel(
if( pPager->fullSync ){
pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
}
+ if( pgFlags & PAGER_CACHESPILL ){
+ pPager->doNotSpill &= ~SPILLFLAG_OFF;
+ }else{
+ pPager->doNotSpill |= SPILLFLAG_OFF;
+ }
}
#endif
@@ -4214,7 +4231,8 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
if( rc==SQLITE_OK
- && (pList->pDirty ? pPager->dbSize : pList->pgno+1)>pPager->dbHintSize
+ && pPager->dbHintSize<pPager->dbSize
+ && (pList->pDirty || pList->pgno>pPager->dbHintSize)
){
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
@@ -4317,7 +4335,7 @@ static int subjournalPage(PgHdr *pPg){
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) );
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 );
assert( pagerUseWal(pPager)
- || pageInJournal(pPg)
+ || pageInJournal(pPager, pPg)
|| pPg->pgno>pPager->dbOrigSize
);
rc = openSubJournal(pPager);
@@ -4371,13 +4389,14 @@ static int pagerStress(void *p, PgHdr *pPg){
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
- /* The doNotSyncSpill flag is set during times when doing a sync of
+ /* The doNotSpill NOSYNC bit is set during times when doing a sync of
** journal (and adding a new header) is not allowed. This occurs
** during calls to sqlite3PagerWrite() while trying to journal multiple
** pages belonging to the same sector.
**
- ** The doNotSpill flag inhibits all cache spilling regardless of whether
- ** or not a sync is required. This is set during a rollback.
+ ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling
+ ** regardless of whether or not a sync is required. This is set during
+ ** a rollback or by user request, respectively.
**
** Spilling is also prohibited when in an error state since that could
** lead to database corruption. In the current implementaton it
@@ -4387,8 +4406,13 @@ static int pagerStress(void *p, PgHdr *pPg){
** test for the error state as a safeguard against future changes.
*/
if( NEVER(pPager->errCode) ) return SQLITE_OK;
- if( pPager->doNotSpill ) return SQLITE_OK;
- if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
+ testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK );
+ testcase( pPager->doNotSpill & SPILLFLAG_OFF );
+ testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC );
+ if( pPager->doNotSpill
+ && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0
+ || (pPg->flags & PGHDR_NEED_SYNC)!=0)
+ ){
return SQLITE_OK;
}
@@ -4651,30 +4675,38 @@ int sqlite3PagerOpen(
** + The value returned by sqlite3OsSectorSize()
** + The largest page size that can be written atomically.
*/
- if( rc==SQLITE_OK && !readOnly ){
- setSectorSize(pPager);
- assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
- if( szPageDflt<pPager->sectorSize ){
- if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
- szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
- }else{
- szPageDflt = (u32)pPager->sectorSize;
+ if( rc==SQLITE_OK ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ if( !readOnly ){
+ setSectorSize(pPager);
+ assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
+ if( szPageDflt<pPager->sectorSize ){
+ if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+ }else{
+ szPageDflt = (u32)pPager->sectorSize;
+ }
}
- }
#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;
+ {
+ 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
+ }
+ pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
+ if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
+ || sqlite3_uri_boolean(zFilename, "immutable", 0) ){
+ vfsFlags |= SQLITE_OPEN_READONLY;
+ goto act_like_temp_file;
+ }
}
}else{
/* If a temporary file is requested, it is not opened immediately.
@@ -4684,10 +4716,14 @@ int sqlite3PagerOpen(
** This branch is also run for an in-memory database. An in-memory
** database is the same as a temp-file that is never written out to
** disk and uses an in-memory rollback journal.
+ **
+ ** This branch also runs for files marked as immutable.
*/
+act_like_temp_file:
tempFile = 1;
- pPager->eState = PAGER_READER;
- pPager->eLock = EXCLUSIVE_LOCK;
+ pPager->eState = PAGER_READER; /* Pretend we already have a lock */
+ pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE locking mode */
+ pPager->noLock = 1; /* Do no locking */
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
@@ -4728,9 +4764,6 @@ int sqlite3PagerOpen(
/* pPager->nPage = 0; */
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
/* pPager->state = PAGER_UNLOCK; */
-#if 0
- assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
-#endif
/* pPager->errMask = 0; */
pPager->tempFile = (u8)tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
@@ -4776,6 +4809,30 @@ int sqlite3PagerOpen(
}
+/* Verify that the database file has not be deleted or renamed out from
+** under the pager. Return SQLITE_OK if the database is still were it ought
+** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
+** code from sqlite3OsAccess()) if the database has gone missing.
+*/
+static int databaseIsUnmoved(Pager *pPager){
+ int bHasMoved = 0;
+ int rc;
+
+ if( pPager->tempFile ) return SQLITE_OK;
+ if( pPager->dbSize==0 ) return SQLITE_OK;
+ assert( pPager->zFilename && pPager->zFilename[0] );
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
+ if( rc==SQLITE_NOTFOUND ){
+ /* If the HAS_MOVED file-control is unimplemented, assume that the file
+ ** has not been moved. That is the historical behavior of SQLite: prior to
+ ** version 3.8.3, it never checked */
+ rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK && bHasMoved ){
+ rc = SQLITE_READONLY_DBMOVED;
+ }
+ return rc;
+}
+
/*
** This function is called after transitioning from PAGER_UNLOCK to
@@ -4841,15 +4898,17 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */
- /* Check the size of the database file. If it consists of 0 pages,
- ** then delete the journal file. See the header comment above for
- ** the reasoning here. Delete the obsolete journal file under
- ** a RESERVED lock to avoid race conditions and to avoid violating
- ** [H33020].
- */
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
- if( nPage==0 ){
+ /* If the database is zero pages in size, that means that either (1) the
+ ** journal is a remnant from a prior database with the same name where
+ ** the database file but not the journal was deleted, or (2) the initial
+ ** transaction that populates a new database is being rolled back.
+ ** In either case, the journal file can be deleted. However, take care
+ ** not to delete the journal file if it is already open due to
+ ** journal_mode=PERSIST.
+ */
+ if( nPage==0 && !jrnlOpen ){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
@@ -5179,7 +5238,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
** page is initialized to all zeros.
**
** If noContent is true, it means that we do not care about the contents
-** of the page. This occurs in two seperate scenarios:
+** of the page. This occurs in two scenarios:
**
** a) When reading a free-list leaf page from the database, and
**
@@ -5210,19 +5269,19 @@ 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 flags /* PAGER_ACQUIRE_XXX flags */
+ int flags /* PAGER_GET_XXX flags */
){
int rc = SQLITE_OK;
PgHdr *pPg = 0;
u32 iFrame = 0; /* Frame to read from WAL file */
- const int noContent = (flags & PAGER_ACQUIRE_NOCONTENT);
+ const int noContent = (flags & PAGER_GET_NOCONTENT);
/* It is acceptable to use a read-only (mmap) page for any page except
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
** flag was specified by the caller. And so long as the db is not a
** temporary or in-memory database. */
const int bMmapOk = (pgno!=1 && USEFETCH(pPager)
- && (pPager->eState==PAGER_READER || (flags & PAGER_ACQUIRE_READONLY))
+ && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
#ifdef SQLITE_HAS_CODEC
&& pPager->xCodec==0
#endif
@@ -5247,7 +5306,7 @@ int sqlite3PagerAcquire(
if( rc!=SQLITE_OK ) goto pager_acquire_err;
}
- if( iFrame==0 && bMmapOk ){
+ if( bMmapOk && iFrame==0 ){
void *pData = 0;
rc = sqlite3OsFetch(pPager->fd,
@@ -5388,16 +5447,19 @@ DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
** are released, a rollback occurs and the lock on the database is
** removed.
*/
-void sqlite3PagerUnref(DbPage *pPg){
- if( pPg ){
- Pager *pPager = pPg->pPager;
- if( pPg->flags & PGHDR_MMAP ){
- pagerReleaseMapPage(pPg);
- }else{
- sqlite3PcacheRelease(pPg);
- }
- pagerUnlockIfUnused(pPager);
+void sqlite3PagerUnrefNotNull(DbPage *pPg){
+ Pager *pPager;
+ assert( pPg!=0 );
+ pPager = pPg->pPager;
+ if( pPg->flags & PGHDR_MMAP ){
+ pagerReleaseMapPage(pPg);
+ }else{
+ sqlite3PcacheRelease(pPg);
}
+ pagerUnlockIfUnused(pPager);
+}
+void sqlite3PagerUnref(DbPage *pPg){
+ if( pPg ) sqlite3PagerUnrefNotNull(pPg);
}
/*
@@ -5452,13 +5514,19 @@ static int pager_open_journal(Pager *pPager){
(SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
(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
+
+ /* Verify that the database still has the same name as it did when
+ ** it was originally opened. */
+ rc = databaseIsUnmoved(pPager);
+ if( rc==SQLITE_OK ){
+#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 || isOpen(pPager->jfd) );
}
@@ -5579,9 +5647,9 @@ int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory){
** of any open savepoints as appropriate.
*/
static int pager_write(PgHdr *pPg){
- void *pData = pPg->pData;
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
+ int inJournal;
/* This routine is not called unless a write-transaction has already
** been started. The journal file may or may not be open at this point.
@@ -5592,14 +5660,8 @@ static int pager_write(PgHdr *pPg){
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
-
- /* If an error has been previously detected, report the same error
- ** again. This should not happen, but the check provides robustness. */
- if( NEVER(pPager->errCode) ) return pPager->errCode;
-
- /* Higher-level routines never call this function if database is not
- ** writable. But check anyway, just for robustness. */
- if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
+ assert( pPager->errCode==0 );
+ assert( pPager->readOnly==0 );
CHECK_PAGE(pPg);
@@ -5623,7 +5685,8 @@ static int pager_write(PgHdr *pPg){
** to the journal then we can return right away.
*/
sqlite3PcacheMakeDirty(pPg);
- if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
+ inJournal = pageInJournal(pPager, pPg);
+ if( inJournal && (pPager->nSavepoint==0 || !subjRequiresPage(pPg)) ){
assert( !pagerUseWal(pPager) );
}else{
@@ -5631,7 +5694,7 @@ static int pager_write(PgHdr *pPg){
** EXCLUSIVE lock on the main database file. Write the current page to
** the transaction journal if it is not there already.
*/
- if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
+ if( !inJournal && !pagerUseWal(pPager) ){
assert( pagerUseWal(pPager)==0 );
if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
u32 cksum;
@@ -5644,7 +5707,7 @@ static int pager_write(PgHdr *pPg){
assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
assert( pPager->journalHdr<=pPager->journalOff );
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
cksum = pager_cksum(pPager, (u8*)pData2);
/* Even if an IO or diskfull error occurs while journalling the
@@ -5696,7 +5759,7 @@ static int pager_write(PgHdr *pPg){
** the statement journal format differs from the standard journal format
** in that it omits the checksums and the header.
*/
- if( subjRequiresPage(pPg) ){
+ if( pPager->nSavepoint>0 && subjRequiresPage(pPg) ){
rc = subjournalPage(pPg);
}
}
@@ -5728,27 +5791,27 @@ int sqlite3PagerWrite(DbPage *pDbPage){
PgHdr *pPg = pDbPage;
Pager *pPager = pPg->pPager;
- Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
assert( (pPg->flags & PGHDR_MMAP)==0 );
assert( pPager->eState>=PAGER_WRITER_LOCKED );
assert( pPager->eState!=PAGER_ERROR );
assert( assert_pager_state(pPager) );
- if( nPagePerSector>1 ){
+ if( pPager->sectorSize > (u32)pPager->pageSize ){
Pgno nPageCount; /* Total number of pages in database file */
Pgno pg1; /* First page of the sector pPg is located on. */
int nPage = 0; /* Number of pages starting at pg1 to journal */
int ii; /* Loop counter */
int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
+ Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
- /* Set the doNotSyncSpill flag to 1. This is because we cannot allow
+ /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
** a journal header to be written between the pages journaled by
** this function.
*/
assert( !MEMDB );
- assert( pPager->doNotSyncSpill==0 );
- pPager->doNotSyncSpill++;
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 );
+ pPager->doNotSpill |= SPILLFLAG_NOSYNC;
/* This trick assumes that both the page-size and sector-size are
** an integer power of 2. It sets variable pg1 to the identifier
@@ -5779,14 +5842,14 @@ int sqlite3PagerWrite(DbPage *pDbPage){
if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
- sqlite3PagerUnref(pPage);
+ sqlite3PagerUnrefNotNull(pPage);
}
}
}else if( (pPage = pager_lookup(pPager, pg))!=0 ){
if( pPage->flags&PGHDR_NEED_SYNC ){
needSync = 1;
}
- sqlite3PagerUnref(pPage);
+ sqlite3PagerUnrefNotNull(pPage);
}
}
@@ -5802,13 +5865,13 @@ int sqlite3PagerWrite(DbPage *pDbPage){
PgHdr *pPage = pager_lookup(pPager, pg1+ii);
if( pPage ){
pPage->flags |= PGHDR_NEED_SYNC;
- sqlite3PagerUnref(pPage);
+ sqlite3PagerUnrefNotNull(pPage);
}
}
}
- assert( pPager->doNotSyncSpill==1 );
- pPager->doNotSyncSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_NOSYNC;
}else{
rc = pager_write(pDbPage);
}
@@ -5955,17 +6018,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
-int sqlite3PagerSync(Pager *pPager){
+int sqlite3PagerSync(Pager *pPager, const char *zMaster){
int rc = SQLITE_OK;
- if( !pPager->noSync ){
+
+ if( isOpen(pPager->fd) ){
+ void *pArg = (void*)zMaster;
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK && !pPager->noSync ){
assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
- }else if( isOpen(pPager->fd) ){
- assert( !MEMDB );
- rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, 0);
- if( rc==SQLITE_NOTFOUND ){
- rc = SQLITE_OK;
- }
}
return rc;
}
@@ -6164,7 +6227,7 @@ int sqlite3PagerCommitPhaseOne(
/* Finally, sync the database file. */
if( !noSync ){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, zMaster);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@@ -6293,7 +6356,9 @@ int sqlite3PagerRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT
- || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR );
+ || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
+ || rc==SQLITE_CANTOPEN
+ );
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
** cache. So call pager_error() on the way out to make any error persistent.
@@ -6589,7 +6654,27 @@ void sqlite3PagerSetCodec(
void *sqlite3PagerGetCodec(Pager *pPager){
return pPager->pCodec;
}
-#endif
+
+/*
+** This function is called by the wal module when writing page content
+** into the log file.
+**
+** This function returns a pointer to a buffer containing the encrypted
+** page content. If a malloc fails, this function may return NULL.
+*/
+void *sqlite3PagerCodec(PgHdr *pPg){
+ void *aData = 0;
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
+ return aData;
+}
+
+/*
+** Return the current pager state
+*/
+int sqlite3PagerState(Pager *pPager){
+ return pPager->eState;
+}
+#endif /* SQLITE_HAS_CODEC */
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
@@ -6676,7 +6761,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
assert( pPager->journalMode==PAGER_JOURNALMODE_OFF ||
- pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
+ pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize );
assert( pPg->flags&PGHDR_DIRTY );
}
@@ -6710,7 +6795,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
if( MEMDB ){
assert( pPgOld );
sqlite3PcacheMove(pPgOld, origPgno);
- sqlite3PagerUnref(pPgOld);
+ sqlite3PagerUnrefNotNull(pPgOld);
}
if( needSyncPgno ){
@@ -6739,7 +6824,7 @@ int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, int isCommit){
}
pPgHdr->flags |= PGHDR_NEED_SYNC;
sqlite3PcacheMakeDirty(pPgHdr);
- sqlite3PagerUnref(pPgHdr);
+ sqlite3PagerUnrefNotNull(pPgHdr);
}
return SQLITE_OK;
@@ -7144,21 +7229,6 @@ int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
-#ifdef SQLITE_HAS_CODEC
-/*
-** This function is called by the wal module when writing page content
-** into the log file.
-**
-** This function returns a pointer to a buffer containing the encrypted
-** page content. If a malloc fails, this function may return NULL.
-*/
-void *sqlite3PagerCodec(PgHdr *pPg){
- void *aData = 0;
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
- return aData;
-}
-#endif /* SQLITE_HAS_CODEC */
-
#endif /* SQLITE_OMIT_DISKIO */
/* BEGIN SQLCIPHER */
diff --git a/src/pager.h b/src/pager.h
index 6f65913..c9ca855 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -81,8 +81,20 @@ typedef struct PgHdr DbPage;
/*
** Flags that make up the mask passed to sqlite3PagerAcquire().
*/
-#define PAGER_ACQUIRE_NOCONTENT 0x01 /* Do not load data from disk */
-#define PAGER_ACQUIRE_READONLY 0x02 /* Read-only page is acceptable */
+#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
+#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
+
+/*
+** Flags for sqlite3PagerSetFlags()
+*/
+#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
+#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
+#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
+#define PAGER_SYNCHRONOUS_MASK 0x03 /* Mask for three values above */
+#define PAGER_FULLFSYNC 0x04 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x08 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x10 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x1c /* All above except SYNCHRONOUS */
/*
** The remainder of this file contains the declarations of the functions
@@ -110,7 +122,7 @@ int sqlite3PagerMaxPageCount(Pager*, int);
void sqlite3PagerSetCachesize(Pager*, int);
void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
void sqlite3PagerShrink(Pager*);
-void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
+void sqlite3PagerSetFlags(Pager*,unsigned);
int sqlite3PagerLockingMode(Pager *, int);
int sqlite3PagerSetJournalMode(Pager *, int);
int sqlite3PagerGetJournalMode(Pager*);
@@ -124,6 +136,7 @@ int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
void sqlite3PagerRef(DbPage*);
void sqlite3PagerUnref(DbPage*);
+void sqlite3PagerUnrefNotNull(DbPage*);
/* Operations on page references. */
int sqlite3PagerWrite(DbPage*);
@@ -138,7 +151,7 @@ void sqlite3PagerPagecount(Pager*, int*);
int sqlite3PagerBegin(Pager*, int exFlag, int);
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerExclusiveLock(Pager*);
-int sqlite3PagerSync(Pager *pPager);
+int sqlite3PagerSync(Pager *pPager, const char *zMaster);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
diff --git a/src/parse.y b/src/parse.y
index 8310b26..dbc129c 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -94,14 +94,6 @@ struct TrigEvent { int a; IdList * b; };
*/
struct AttachKey { int type; Token key; };
-/*
-** One or more VALUES claues
-*/
-struct ValueList {
- ExprList *pList;
- Select *pSelect;
-};
-
} // end %include
// Input is a single SQL command
@@ -163,13 +155,23 @@ ifnotexists(A) ::= IF NOT EXISTS. {A = 1;}
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 ::= LP columnlist conslist_opt(X) RP(E) table_options(F). {
+ sqlite3EndTable(pParse,&X,&E,F,0);
}
create_table_args ::= AS select(S). {
- sqlite3EndTable(pParse,0,0,S);
+ sqlite3EndTable(pParse,0,0,0,S);
sqlite3SelectDelete(pParse->db, S);
}
+%type table_options {u8}
+table_options(A) ::= . {A = 0;}
+table_options(A) ::= WITHOUT nm(X). {
+ if( X.n==5 && sqlite3_strnicmp(X.z,"rowid",5)==0 ){
+ A = TF_WithoutRowid;
+ }else{
+ A = 0;
+ sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z);
+ }
+}
columnlist ::= columnlist COMMA column.
columnlist ::= column.
@@ -192,9 +194,7 @@ columnid(A) ::= nm(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;}
-id(A) ::= INDEXED(X). {A = X;}
+%token_class id ID|INDEXED.
// The following directive causes tokens ABORT, AFTER, ASC, etc. to
// fallback to ID if they will not parse as their original value.
@@ -204,8 +204,8 @@ id(A) ::= INDEXED(X). {A = X;}
ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
- QUERY KEY OF OFFSET PRAGMA RAISE RELEASE REPLACE RESTRICT ROW ROLLBACK
- SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL
+ QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
+ ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
%ifdef SQLITE_OMIT_COMPOUND_SELECT
EXCEPT INTERSECT UNION
%endif SQLITE_OMIT_COMPOUND_SELECT
@@ -213,7 +213,7 @@ id(A) ::= INDEXED(X). {A = X;}
.
%wildcard ANY.
-// Define operator precedence early so that this is the first occurance
+// Define operator precedence early so that this is the first occurrence
// 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.
@@ -239,8 +239,7 @@ id(A) ::= INDEXED(X). {A = X;}
// And "ids" is an identifer-or-string.
//
-%type ids {Token}
-ids(A) ::= ID|STRING(X). {A = X;}
+%token_class ids ID|STRING.
// The name of a column or table can be any of the following:
//
@@ -398,7 +397,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X). {
- SelectDest dest = {SRT_Output, 0, 0, 0, 0};
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
sqlite3Select(pParse, X, &dest);
sqlite3ExplainBegin(pParse->pVdbe);
sqlite3ExplainSelect(pParse->pVdbe, X);
@@ -408,19 +407,52 @@ cmd ::= select(X). {
%type select {Select*}
%destructor select {sqlite3SelectDelete(pParse->db, $$);}
+%type selectnowith {Select*}
+%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
%type oneselect {Select*}
%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
-select(A) ::= oneselect(X). {A = X;}
+select(A) ::= with(W) selectnowith(X). {
+ Select *p = X, *pNext, *pLoop;
+ if( p ){
+ int cnt = 0, mxSelect;
+ p->pWith = W;
+ if( p->pPrior ){
+ pNext = 0;
+ for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
+ pLoop->pNext = pNext;
+ pLoop->selFlags |= SF_Compound;
+ }
+ mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
+ if( mxSelect && cnt>mxSelect ){
+ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
+ }
+ }
+ }else{
+ sqlite3WithDelete(pParse->db, W);
+ }
+ A = p;
+}
+
+selectnowith(A) ::= oneselect(X). {A = X;}
%ifndef SQLITE_OMIT_COMPOUND_SELECT
-select(A) ::= select(X) multiselect_op(Y) oneselect(Z). {
- if( Z ){
- Z->op = (u8)Y;
- Z->pPrior = X;
+selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
+ Select *pRhs = Z;
+ if( pRhs && pRhs->pPrior ){
+ SrcList *pFrom;
+ Token x;
+ x.n = 0;
+ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
+ pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0);
+ }
+ if( pRhs ){
+ pRhs->op = (u8)Y;
+ pRhs->pPrior = X;
+ if( Y!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, X);
}
- A = Z;
+ A = pRhs;
}
%type multiselect_op {int}
multiselect_op(A) ::= UNION(OP). {A = @OP;}
@@ -431,6 +463,23 @@ 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);
}
+oneselect(A) ::= values(X). {A = X;}
+
+%type values {Select*}
+%destructor values {sqlite3SelectDelete(pParse->db, $$);}
+values(A) ::= VALUES LP nexprlist(X) RP. {
+ A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0,0);
+}
+values(A) ::= values(X) COMMA LP exprlist(Y) RP. {
+ Select *pRight = sqlite3SelectNew(pParse,Y,0,0,0,0,0,SF_Values,0,0);
+ if( pRight ){
+ pRight->op = TK_ALL;
+ pRight->pPrior = X;
+ A = pRight;
+ }else{
+ A = X;
+ }
+}
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
// present and false (0) if it is not.
@@ -572,7 +621,7 @@ indexed_opt(A) ::= NOT INDEXED. {A.z=0; A.n=1;}
%type using_opt {IdList*}
%destructor using_opt {sqlite3IdListDelete(pParse->db, $$);}
-using_opt(U) ::= USING LP inscollist(L) RP. {U = L;}
+using_opt(U) ::= USING LP idlist(L) RP. {U = L;}
using_opt(U) ::= . {U = 0;}
@@ -631,15 +680,17 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
/////////////////////////// The DELETE statement /////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
+cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
orderby_opt(O) limit_opt(L). {
+ sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
sqlite3DeleteFrom(pParse,X,W);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
+cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
+ sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W);
}
@@ -654,8 +705,9 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;}
////////////////////////// The UPDATE command ////////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
- orderby_opt(O) limit_opt(L). {
+cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
+ where_opt(W) orderby_opt(O) limit_opt(L). {
+ sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
@@ -663,8 +715,9 @@ cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W)
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
+cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
where_opt(W). {
+ sqlite3WithPush(pParse, C, 1);
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
sqlite3Update(pParse,X,Y,W,R);
@@ -685,68 +738,30 @@ setlist(A) ::= nm(X) EQ expr(Y). {
////////////////////////// The INSERT command /////////////////////////////////
//
-cmd ::= insert_cmd(R) INTO fullname(X) inscollist_opt(F) valuelist(Y).
- {sqlite3Insert(pParse, X, Y.pList, Y.pSelect, 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);}
+cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). {
+ sqlite3WithPush(pParse, W, 1);
+ sqlite3Insert(pParse, X, S, F, R);
+}
+cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES.
+{
+ sqlite3WithPush(pParse, W, 1);
+ sqlite3Insert(pParse, X, 0, F, R);
+}
%type insert_cmd {u8}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
-// A ValueList is either a single VALUES clause or a comma-separated list
-// of VALUES clauses. If it is a single VALUES clause then the
-// ValueList.pList field points to the expression list of that clause.
-// If it is a list of VALUES clauses, then those clauses are transformed
-// into a set of SELECT statements without FROM clauses and connected by
-// UNION ALL and the ValueList.pSelect points to the right-most SELECT in
-// that compound.
-%type valuelist {struct ValueList}
-%destructor valuelist {
- sqlite3ExprListDelete(pParse->db, $$.pList);
- sqlite3SelectDelete(pParse->db, $$.pSelect);
-}
-valuelist(A) ::= VALUES LP nexprlist(X) RP. {
- A.pList = X;
- A.pSelect = 0;
-}
-
-// Since a list of VALUEs is inplemented as a compound SELECT, we have
-// to disable the value list option if compound SELECTs are disabled.
-%ifndef SQLITE_OMIT_COMPOUND_SELECT
-valuelist(A) ::= valuelist(X) COMMA LP exprlist(Y) RP. {
- Select *pRight = sqlite3SelectNew(pParse, Y, 0, 0, 0, 0, 0, 0, 0, 0);
- if( X.pList ){
- X.pSelect = sqlite3SelectNew(pParse, X.pList, 0, 0, 0, 0, 0, 0, 0, 0);
- X.pList = 0;
- }
- A.pList = 0;
- if( X.pSelect==0 || pRight==0 ){
- sqlite3SelectDelete(pParse->db, pRight);
- sqlite3SelectDelete(pParse->db, X.pSelect);
- A.pSelect = 0;
- }else{
- pRight->op = TK_ALL;
- pRight->pPrior = X.pSelect;
- pRight->selFlags |= SF_Values;
- pRight->pPrior->selFlags |= SF_Values;
- A.pSelect = pRight;
- }
-}
-%endif SQLITE_OMIT_COMPOUND_SELECT
-
%type inscollist_opt {IdList*}
%destructor inscollist_opt {sqlite3IdListDelete(pParse->db, $$);}
-%type inscollist {IdList*}
-%destructor inscollist {sqlite3IdListDelete(pParse->db, $$);}
+%type idlist {IdList*}
+%destructor idlist {sqlite3IdListDelete(pParse->db, $$);}
inscollist_opt(A) ::= . {A = 0;}
-inscollist_opt(A) ::= LP inscollist(X) RP. {A = X;}
-inscollist(A) ::= inscollist(X) COMMA nm(Y).
+inscollist_opt(A) ::= LP idlist(X) RP. {A = X;}
+idlist(A) ::= idlist(X) COMMA nm(Y).
{A = sqlite3IdListAppend(pParse->db,X,&Y);}
-inscollist(A) ::= nm(Y).
+idlist(A) ::= nm(Y).
{A = sqlite3IdListAppend(pParse->db,0,&Y);}
/////////////////////////// Expression Processing /////////////////////////////
@@ -799,24 +814,24 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
}
term(A) ::= INTEGER|FLOAT|BLOB(X). {spanExpr(&A, pParse, @X, &X);}
term(A) ::= STRING(X). {spanExpr(&A, pParse, @X, &X);}
-expr(A) ::= REGISTER(X). {
- /* When doing a nested parse, one can include terms in an expression
- ** that look like this: #1 #2 ... These terms refer to registers
- ** in the virtual machine. #N is the N-th register. */
- if( pParse->nested==0 ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X);
- A.pExpr = 0;
+expr(A) ::= VARIABLE(X). {
+ if( X.n>=2 && X.z[0]=='#' && sqlite3Isdigit(X.z[1]) ){
+ /* When doing a nested parse, one can include terms in an expression
+ ** that look like this: #1 #2 ... These terms refer to registers
+ ** in the virtual machine. #N is the N-th register. */
+ if( pParse->nested==0 ){
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &X);
+ A.pExpr = 0;
+ }else{
+ A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X);
+ if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable);
+ }
}else{
- A.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &X);
- if( A.pExpr ) sqlite3GetInt32(&X.z[1], &A.pExpr->iTable);
+ spanExpr(&A, pParse, TK_VARIABLE, &X);
+ sqlite3ExprAssignVarNumber(pParse, A.pExpr);
}
spanSet(&A, &X, &X);
}
-expr(A) ::= VARIABLE(X). {
- spanExpr(&A, pParse, TK_VARIABLE, &X);
- sqlite3ExprAssignVarNumber(pParse, A.pExpr);
- spanSet(&A, &X, &X);
-}
expr(A) ::= expr(E) COLLATE ids(C). {
A.pExpr = sqlite3ExprAddCollateToken(pParse, E.pExpr, &C);
A.zStart = E.zStart;
@@ -828,7 +843,7 @@ expr(A) ::= CAST(X) LP expr(E) AS typetoken(T) RP(Y). {
spanSet(&A,&X,&Y);
}
%endif SQLITE_OMIT_CAST
-expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
+expr(A) ::= id(X) LP distinct(D) exprlist(Y) RP(E). {
if( Y && Y->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &X);
}
@@ -838,17 +853,12 @@ expr(A) ::= ID(X) LP distinct(D) exprlist(Y) RP(E). {
A.pExpr->flags |= EP_Distinct;
}
}
-expr(A) ::= ID(X) LP STAR RP(E). {
+expr(A) ::= id(X) LP STAR RP(E). {
A.pExpr = sqlite3ExprFunction(pParse, 0, &X);
spanSet(&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.pExpr = sqlite3ExprFunction(pParse, 0,&OP);
- if( A.pExpr ){
- A.pExpr->op = TK_CONST_FUNC;
- }
+ A.pExpr = sqlite3ExprFunction(pParse, 0, &OP);
spanSet(&A, &OP, &OP);
}
@@ -882,10 +892,8 @@ expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y).
{spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);}
%type likeop {struct LikeOp}
-likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.bNot = 0;}
-likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.bNot = 1;}
-likeop(A) ::= MATCH(X). {A.eOperator = X; A.bNot = 0;}
-likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.bNot = 1;}
+likeop(A) ::= LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 0;}
+likeop(A) ::= NOT LIKE_KW|MATCH(X). {A.eOperator = X; A.bNot = 1;}
expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] {
ExprList *pList;
pList = sqlite3ExprListAppend(pParse,0, Y.pExpr);
@@ -1012,6 +1020,33 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
*/
A.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[N]);
sqlite3ExprDelete(pParse->db, X.pExpr);
+ }else if( Y->nExpr==1 ){
+ /* Expressions of the form:
+ **
+ ** expr1 IN (?1)
+ ** expr1 NOT IN (?2)
+ **
+ ** with exactly one value on the RHS can be simplified to something
+ ** like this:
+ **
+ ** expr1 == ?1
+ ** expr1 <> ?2
+ **
+ ** But, the RHS of the == or <> is marked with the EP_Generic flag
+ ** so that it may not contribute to the computation of comparison
+ ** affinity or the collating sequence to use for comparison. Otherwise,
+ ** the semantics would be subtly different from IN or NOT IN.
+ */
+ Expr *pRHS = Y->a[0].pExpr;
+ Y->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, Y);
+ /* pRHS cannot be NULL because a malloc error would have been detected
+ ** before now and control would have never reached this point */
+ if( ALWAYS(pRHS) ){
+ pRHS->flags &= ~EP_Collate;
+ pRHS->flags |= EP_Generic;
+ }
+ A.pExpr = sqlite3PExpr(pParse, N ? TK_NE : TK_EQ, X.pExpr, pRHS, 0);
}else{
A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0);
if( A.pExpr ){
@@ -1080,12 +1115,13 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
/* CASE expressions */
expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
- A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, Z, 0);
+ A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0, 0);
if( A.pExpr ){
- A.pExpr->x.pList = Y;
+ A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
sqlite3ExprSetHeight(pParse, A.pExpr);
}else{
sqlite3ExprListDelete(pParse->db, Y);
+ sqlite3ExprDelete(pParse->db, Z);
}
A.zStart = C.z;
A.zEnd = &E.z[E.n];
@@ -1125,10 +1161,10 @@ nexprlist(A) ::= expr(Y).
///////////////////////////// The CREATE INDEX command ///////////////////////
//
cmd ::= createkw(S) uniqueflag(U) INDEX ifnotexists(NE) nm(X) dbnm(D)
- ON nm(Y) LP idxlist(Z) RP(E). {
+ ON nm(Y) LP idxlist(Z) RP where_opt(W). {
sqlite3CreateIndex(pParse, &X, &D,
sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
- &S, &E, SQLITE_SO_ASC, NE);
+ &S, W, SQLITE_SO_ASC, NE);
}
%type uniqueflag {int}
@@ -1192,11 +1228,10 @@ nmnum(A) ::= ON(X). {A = X;}
nmnum(A) ::= DELETE(X). {A = X;}
nmnum(A) ::= DEFAULT(X). {A = X;}
%endif SQLITE_OMIT_PRAGMA
+%token_class number INTEGER|FLOAT.
plus_num(A) ::= PLUS number(X). {A = X;}
plus_num(A) ::= number(X). {A = X;}
minus_num(A) ::= MINUS number(X). {A = X;}
-number(A) ::= INTEGER|FLOAT(X). {A = X;}
-
//////////////////////////// The CREATE TRIGGER command /////////////////////
%ifndef SQLITE_OMIT_TRIGGER
@@ -1225,7 +1260,7 @@ trigger_time(A) ::= . { A = TK_BEFORE; }
%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;}
+trigger_event(A) ::= UPDATE OF idlist(X). {A.a = TK_UPDATE; A.b = X;}
foreach_clause ::= .
foreach_clause ::= FOR EACH ROW.
@@ -1288,12 +1323,8 @@ trigger_cmd(A) ::=
{ A = sqlite3TriggerUpdateStep(pParse->db, &X, Y, Z, R); }
// INSERT
-trigger_cmd(A) ::=
- insert_cmd(R) INTO trnm(X) inscollist_opt(F) valuelist(Y).
- {A = sqlite3TriggerInsertStep(pParse->db, &X, F, Y.pList, Y.pSelect, R);}
-
trigger_cmd(A) ::= insert_cmd(R) INTO trnm(X) inscollist_opt(F) select(S).
- {A = sqlite3TriggerInsertStep(pParse->db, &X, F, 0, S, R);}
+ {A = sqlite3TriggerInsertStep(pParse->db, &X, F, S, R);}
// DELETE
trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y).
@@ -1399,3 +1430,23 @@ anylist ::= .
anylist ::= anylist LP anylist RP.
anylist ::= anylist ANY.
%endif SQLITE_OMIT_VIRTUALTABLE
+
+
+//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
+%type with {With*}
+%type wqlist {With*}
+%destructor with {sqlite3WithDelete(pParse->db, $$);}
+%destructor wqlist {sqlite3WithDelete(pParse->db, $$);}
+
+with(A) ::= . {A = 0;}
+%ifndef SQLITE_OMIT_CTE
+with(A) ::= WITH wqlist(W). { A = W; }
+with(A) ::= WITH RECURSIVE wqlist(W). { A = W; }
+
+wqlist(A) ::= nm(X) idxlist_opt(Y) AS LP select(Z) RP. {
+ A = sqlite3WithAdd(pParse, 0, &X, Y, Z);
+}
+wqlist(A) ::= wqlist(W) COMMA nm(X) idxlist_opt(Y) AS LP select(Z) RP. {
+ A = sqlite3WithAdd(pParse, W, &X, Y, Z);
+}
+%endif SQLITE_OMIT_CTE
diff --git a/src/pcache.c b/src/pcache.c
index 482a188..e18bf93 100644
--- a/src/pcache.c
+++ b/src/pcache.c
@@ -23,7 +23,8 @@ struct PCache {
int szCache; /* Configured cache size */
int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */
- int bPurgeable; /* True if pages are on backing store */
+ u8 bPurgeable; /* True if pages are on backing store */
+ u8 eCreate; /* eCreate value for for xFetch() */
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
void *pStress; /* Argument to xStress */
sqlite3_pcache *pCache; /* Pluggable cache module */
@@ -90,6 +91,10 @@ static void pcacheRemoveFromDirtyList(PgHdr *pPage){
}else{
assert( pPage==p->pDirty );
p->pDirty = pPage->pDirtyNext;
+ if( p->pDirty==0 && p->bPurgeable ){
+ assert( p->eCreate==1 );
+ p->eCreate = 2;
+ }
}
pPage->pDirtyNext = 0;
pPage->pDirtyPrev = 0;
@@ -110,6 +115,9 @@ static void pcacheAddToDirtyList(PgHdr *pPage){
if( pPage->pDirtyNext ){
assert( pPage->pDirtyNext->pDirtyPrev==0 );
pPage->pDirtyNext->pDirtyPrev = pPage;
+ }else if( p->bPurgeable ){
+ assert( p->eCreate==2 );
+ p->eCreate = 1;
}
p->pDirty = pPage;
if( !p->pDirtyTail ){
@@ -179,6 +187,7 @@ void sqlite3PcacheOpen(
p->szPage = szPage;
p->szExtra = szExtra;
p->bPurgeable = bPurgeable;
+ p->eCreate = 2;
p->xStress = xStress;
p->pStress = pStress;
p->szCache = 100;
@@ -218,7 +227,7 @@ int sqlite3PcacheFetch(
int createFlag, /* If true, create page if it does not exist already */
PgHdr **ppPage /* Write the page here */
){
- sqlite3_pcache_page *pPage = 0;
+ sqlite3_pcache_page *pPage;
PgHdr *pPgHdr = 0;
int eCreate;
@@ -229,8 +238,12 @@ int sqlite3PcacheFetch(
/* If the pluggable cache (sqlite3_pcache*) has not been allocated,
** allocate it now.
*/
- if( !pCache->pCache && createFlag ){
+ if( !pCache->pCache ){
sqlite3_pcache *p;
+ if( !createFlag ){
+ *ppPage = 0;
+ return SQLITE_OK;
+ }
p = sqlite3GlobalConfig.pcache2.xCreate(
pCache->szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable
);
@@ -241,11 +254,16 @@ int sqlite3PcacheFetch(
pCache->pCache = p;
}
- eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
- if( pCache->pCache ){
- pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
- }
-
+ /* eCreate defines what to do if the page does not exist.
+ ** 0 Do not allocate a new page. (createFlag==0)
+ ** 1 Allocate a new page if doing so is inexpensive.
+ ** (createFlag==1 AND bPurgeable AND pDirty)
+ ** 2 Allocate a new page even it doing so is difficult.
+ ** (createFlag==1 AND !(bPurgeable AND pDirty)
+ */
+ eCreate = createFlag==0 ? 0 : pCache->eCreate;
+ assert( (createFlag*(1+(!pCache->bPurgeable||!pCache->pDirty)))==eCreate );
+ pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
if( !pPage && eCreate==1 ){
PgHdr *pPg;
diff --git a/src/pcache1.c b/src/pcache1.c
index 4147d2e..1644b06 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -96,6 +96,7 @@ struct PCache1 {
struct PgHdr1 {
sqlite3_pcache_page page;
unsigned int iKey; /* Key value (page number) */
+ u8 isPinned; /* Page in use, not on the LRU list */
PgHdr1 *pNext; /* Next in hash table chain */
PCache1 *pCache; /* Cache that currently owns this page */
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
@@ -424,34 +425,32 @@ static int pcache1ResizeHash(PCache1 *p){
** LRU list, then this function is a no-op.
**
** The PGroup mutex must be held when this function is called.
-**
-** If pPage is NULL then this routine is a no-op.
*/
static void pcache1PinPage(PgHdr1 *pPage){
PCache1 *pCache;
PGroup *pGroup;
- if( pPage==0 ) return;
+ assert( pPage!=0 );
+ assert( pPage->isPinned==0 );
pCache = pPage->pCache;
pGroup = pCache->pGroup;
+ assert( pPage->pLruNext || pPage==pGroup->pLruTail );
+ assert( pPage->pLruPrev || pPage==pGroup->pLruHead );
assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pPage->pLruNext || pPage==pGroup->pLruTail ){
- if( pPage->pLruPrev ){
- pPage->pLruPrev->pLruNext = pPage->pLruNext;
- }
- if( pPage->pLruNext ){
- pPage->pLruNext->pLruPrev = pPage->pLruPrev;
- }
- if( pGroup->pLruHead==pPage ){
- pGroup->pLruHead = pPage->pLruNext;
- }
- if( pGroup->pLruTail==pPage ){
- pGroup->pLruTail = pPage->pLruPrev;
- }
- pPage->pLruNext = 0;
- pPage->pLruPrev = 0;
- pPage->pCache->nRecyclable--;
+ if( pPage->pLruPrev ){
+ pPage->pLruPrev->pLruNext = pPage->pLruNext;
+ }else{
+ pGroup->pLruHead = pPage->pLruNext;
+ }
+ if( pPage->pLruNext ){
+ pPage->pLruNext->pLruPrev = pPage->pLruPrev;
+ }else{
+ pGroup->pLruTail = pPage->pLruPrev;
}
+ pPage->pLruNext = 0;
+ pPage->pLruPrev = 0;
+ pPage->isPinned = 1;
+ pCache->nRecyclable--;
}
@@ -483,6 +482,7 @@ static void pcache1EnforceMaxPage(PGroup *pGroup){
while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
PgHdr1 *p = pGroup->pLruTail;
assert( p->pCache->pGroup==pGroup );
+ assert( p->isPinned==0 );
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
@@ -510,7 +510,7 @@ static void pcache1TruncateUnsafe(
if( pPage->iKey>=iLimit ){
pCache->nPage--;
*pp = pPage->pNext;
- pcache1PinPage(pPage);
+ if( !pPage->isPinned ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
}else{
pp = &pPage->pNext;
@@ -562,7 +562,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
int sz; /* Bytes of memory required to allocate the new cache */
/*
- ** The seperateCache variable is true if each PCache has its own private
+ ** The separateCache variable is true if each PCache has its own private
** PGroup. In other words, separateCache is true for mode (1) where no
** mutexing is required.
**
@@ -720,6 +720,7 @@ static sqlite3_pcache_page *pcache1Fetch(
PGroup *pGroup;
PgHdr1 *pPage = 0;
+ assert( offsetof(PgHdr1,page)==0 );
assert( pCache->bPurgeable || createFlag!=1 );
assert( pCache->bPurgeable || pCache->nMin==0 );
assert( pCache->bPurgeable==0 || pCache->nMin==10 );
@@ -733,8 +734,11 @@ static sqlite3_pcache_page *pcache1Fetch(
}
/* Step 2: Abort if no existing page is found and createFlag is 0 */
- if( pPage || createFlag==0 ){
- pcache1PinPage(pPage);
+ if( pPage ){
+ if( !pPage->isPinned ) pcache1PinPage(pPage);
+ goto fetch_out;
+ }
+ if( createFlag==0 ){
goto fetch_out;
}
@@ -765,6 +769,7 @@ static sqlite3_pcache_page *pcache1Fetch(
if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
goto fetch_out;
}
+ assert( pCache->nHash>0 && pCache->apHash );
/* Step 4. Try to recycle a page. */
if( pCache->bPurgeable && pGroup->pLruTail && (
@@ -774,6 +779,7 @@ static sqlite3_pcache_page *pcache1Fetch(
)){
PCache1 *pOther;
pPage = pGroup->pLruTail;
+ assert( pPage->isPinned==0 );
pcache1RemoveFromHash(pPage);
pcache1PinPage(pPage);
pOther = pPage->pCache;
@@ -810,6 +816,7 @@ static sqlite3_pcache_page *pcache1Fetch(
pPage->pCache = pCache;
pPage->pLruPrev = 0;
pPage->pLruNext = 0;
+ pPage->isPinned = 1;
*(void **)pPage->page.pExtra = 0;
pCache->apHash[h] = pPage;
}
@@ -819,7 +826,7 @@ fetch_out:
pCache->iMaxKey = iKey;
}
pcache1LeaveMutex(pGroup);
- return &pPage->page;
+ return (sqlite3_pcache_page*)pPage;
}
@@ -845,6 +852,7 @@ static void pcache1Unpin(
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
+ assert( pPage->isPinned==1 );
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
pcache1RemoveFromHash(pPage);
@@ -860,6 +868,7 @@ static void pcache1Unpin(
pGroup->pLruHead = pPage;
}
pCache->nRecyclable++;
+ pPage->isPinned = 0;
}
pcache1LeaveMutex(pCache->pGroup);
@@ -986,6 +995,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
nFree += sqlite3MemSize(p);
#endif
+ assert( p->isPinned==0 );
pcache1PinPage(p);
pcache1RemoveFromHash(p);
pcache1FreePage(p);
@@ -1010,6 +1020,7 @@ void sqlite3PcacheStats(
PgHdr1 *p;
int nRecyclable = 0;
for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
+ assert( p->isPinned==0 );
nRecyclable++;
}
*pnCurrent = pcache1.grp.nCurrentPage;
diff --git a/src/pragma.c b/src/pragma.c
index 2297716..92ea5ee 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -13,6 +13,462 @@
*/
#include "sqliteInt.h"
+#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
+# if defined(__APPLE__)
+# define SQLITE_ENABLE_LOCKING_STYLE 1
+# else
+# define SQLITE_ENABLE_LOCKING_STYLE 0
+# endif
+#endif
+
+/***************************************************************************
+** The next block of code, including the PragTyp_XXXX macro definitions and
+** the aPragmaName[] object is composed of generated code. DO NOT EDIT.
+**
+** To add new pragmas, edit the code in ../tool/mkpragmatab.tcl and rerun
+** that script. Then copy/paste the output in place of the following:
+*/
+#define PragTyp_HEADER_VALUE 0
+#define PragTyp_AUTO_VACUUM 1
+#define PragTyp_FLAG 2
+#define PragTyp_BUSY_TIMEOUT 3
+#define PragTyp_CACHE_SIZE 4
+#define PragTyp_CASE_SENSITIVE_LIKE 5
+#define PragTyp_COLLATION_LIST 6
+#define PragTyp_COMPILE_OPTIONS 7
+#define PragTyp_DATA_STORE_DIRECTORY 8
+#define PragTyp_DATABASE_LIST 9
+#define PragTyp_DEFAULT_CACHE_SIZE 10
+#define PragTyp_ENCODING 11
+#define PragTyp_FOREIGN_KEY_CHECK 12
+#define PragTyp_FOREIGN_KEY_LIST 13
+#define PragTyp_INCREMENTAL_VACUUM 14
+#define PragTyp_INDEX_INFO 15
+#define PragTyp_INDEX_LIST 16
+#define PragTyp_INTEGRITY_CHECK 17
+#define PragTyp_JOURNAL_MODE 18
+#define PragTyp_JOURNAL_SIZE_LIMIT 19
+#define PragTyp_LOCK_PROXY_FILE 20
+#define PragTyp_LOCKING_MODE 21
+#define PragTyp_PAGE_COUNT 22
+#define PragTyp_MMAP_SIZE 23
+#define PragTyp_PAGE_SIZE 24
+#define PragTyp_SECURE_DELETE 25
+#define PragTyp_SHRINK_MEMORY 26
+#define PragTyp_SOFT_HEAP_LIMIT 27
+#define PragTyp_STATS 28
+#define PragTyp_SYNCHRONOUS 29
+#define PragTyp_TABLE_INFO 30
+#define PragTyp_TEMP_STORE 31
+#define PragTyp_TEMP_STORE_DIRECTORY 32
+#define PragTyp_WAL_AUTOCHECKPOINT 33
+#define PragTyp_WAL_CHECKPOINT 34
+#define PragTyp_ACTIVATE_EXTENSIONS 35
+#define PragTyp_HEXKEY 36
+#define PragTyp_KEY 37
+#define PragTyp_REKEY 38
+#define PragTyp_LOCK_STATUS 39
+#define PragTyp_PARSER_TRACE 40
+#define PragFlag_NeedSchema 0x01
+static const struct sPragmaNames {
+ const char *const zName; /* Name of pragma */
+ u8 ePragTyp; /* PragTyp_XXX value */
+ u8 mPragFlag; /* Zero or more PragFlag_XXX values */
+ u32 iArg; /* Extra argument */
+} aPragmaNames[] = {
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+ { /* zName: */ "activate_extensions",
+ /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "application_id",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "auto_vacuum",
+ /* ePragTyp: */ PragTyp_AUTO_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_AUTOMATIC_INDEX)
+ { /* zName: */ "automatic_index",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_AutoIndex },
+#endif
+#endif
+ { /* zName: */ "busy_timeout",
+ /* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "cache_size",
+ /* ePragTyp: */ PragTyp_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "cache_spill",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CacheSpill },
+#endif
+ { /* zName: */ "case_sensitive_like",
+ /* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "checkpoint_fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CkptFullFSync },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "collation_list",
+ /* ePragTyp: */ PragTyp_COLLATION_LIST,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
+ { /* zName: */ "compile_options",
+ /* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "count_changes",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CountRows },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
+ { /* zName: */ "data_store_directory",
+ /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "database_list",
+ /* ePragTyp: */ PragTyp_DATABASE_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+ { /* zName: */ "default_cache_size",
+ /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "defer_foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_DeferFKs },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "empty_result_callbacks",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_NullCallback },
+#endif
+#if !defined(SQLITE_OMIT_UTF16)
+ { /* zName: */ "encoding",
+ /* ePragTyp: */ PragTyp_ENCODING,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_key_check",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
+ { /* zName: */ "foreign_key_list",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ForeignKeys },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "freelist_count",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "full_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullColNames },
+ { /* zName: */ "fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullFSync },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "hexkey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "hexrekey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_CHECK)
+ { /* zName: */ "ignore_check_constraints",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_IgnoreChecks },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "incremental_vacuum",
+ /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "index_info",
+ /* ePragTyp: */ PragTyp_INDEX_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "index_list",
+ /* ePragTyp: */ PragTyp_INDEX_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "integrity_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "journal_mode",
+ /* ePragTyp: */ PragTyp_JOURNAL_MODE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "journal_size_limit",
+ /* ePragTyp: */ PragTyp_JOURNAL_SIZE_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "key",
+ /* ePragTyp: */ PragTyp_KEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "legacy_file_format",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_LegacyFileFmt },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
+ { /* zName: */ "lock_proxy_file",
+ /* ePragTyp: */ PragTyp_LOCK_PROXY_FILE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ { /* zName: */ "lock_status",
+ /* ePragTyp: */ PragTyp_LOCK_STATUS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "locking_mode",
+ /* ePragTyp: */ PragTyp_LOCKING_MODE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "max_page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "mmap_size",
+ /* ePragTyp: */ PragTyp_MMAP_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_size",
+ /* ePragTyp: */ PragTyp_PAGE_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "parser_trace",
+ /* ePragTyp: */ PragTyp_PARSER_TRACE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "query_only",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_QueryOnly },
+#endif
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "quick_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "read_uncommitted",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReadUncommitted },
+ { /* zName: */ "recursive_triggers",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_RecTriggers },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "rekey",
+ /* ePragTyp: */ PragTyp_REKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "reverse_unordered_selects",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReverseOrder },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "schema_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "secure_delete",
+ /* ePragTyp: */ PragTyp_SECURE_DELETE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "short_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ShortColNames },
+#endif
+ { /* zName: */ "shrink_memory",
+ /* ePragTyp: */ PragTyp_SHRINK_MEMORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "soft_heap_limit",
+ /* ePragTyp: */ PragTyp_SOFT_HEAP_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "sql_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "stats",
+ /* ePragTyp: */ PragTyp_STATS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "synchronous",
+ /* ePragTyp: */ PragTyp_SYNCHRONOUS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "table_info",
+ /* ePragTyp: */ PragTyp_TABLE_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "temp_store",
+ /* ePragTyp: */ PragTyp_TEMP_STORE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "temp_store_directory",
+ /* ePragTyp: */ PragTyp_TEMP_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "user_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "vdbe_addoptrace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeAddopTrace },
+ { /* zName: */ "vdbe_debug",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace },
+ { /* zName: */ "vdbe_eqp",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeEQP },
+ { /* zName: */ "vdbe_listing",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeListing },
+ { /* zName: */ "vdbe_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeTrace },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_WAL)
+ { /* zName: */ "wal_autocheckpoint",
+ /* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "wal_checkpoint",
+ /* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "writable_schema",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
+#endif
+};
+/* Number of pragmas: 56 on by default, 69 total. */
+/* End of the automatically generated pragma table.
+***************************************************************************/
+
/*
** 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
@@ -24,7 +480,7 @@
** to support legacy SQL code. The safety level used to be boolean
** and older scripts may have used numbers 0 for OFF and 1 for ON.
*/
-static u8 getSafetyLevel(const char *z, int omitFull, int dflt){
+static u8 getSafetyLevel(const char *z, int omitFull, u8 dflt){
/* 123456789 123456789 */
static const char zText[] = "onoffalseyestruefull";
static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
@@ -46,7 +502,7 @@ static u8 getSafetyLevel(const char *z, int omitFull, int dflt){
/*
** Interpret the given string as a boolean value.
*/
-u8 sqlite3GetBoolean(const char *z, int dflt){
+u8 sqlite3GetBoolean(const char *z, u8 dflt){
return getSafetyLevel(z,1,dflt)!=0;
}
@@ -158,92 +614,35 @@ static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
}
-#ifndef SQLITE_OMIT_FLAG_PRAGMAS
+
/*
-** Check to see if zRight and zLeft refer to a pragma that queries
-** or changes one of the flags in db->flags. Return 1 if so and 0 if not.
-** Also, implement the pragma.
+** Set the safety_level and pager flags for pager iDb. Or if iDb<0
+** set these values for all pagers.
*/
-static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
- static const struct sPragmaType {
- const char *zName; /* Name of the pragma */
- int mask; /* Mask for the db->flags value */
- } aPragma[] = {
- { "full_column_names", SQLITE_FullColNames },
- { "short_column_names", SQLITE_ShortColNames },
- { "count_changes", SQLITE_CountRows },
- { "empty_result_callbacks", SQLITE_NullCallback },
- { "legacy_file_format", SQLITE_LegacyFileFmt },
- { "fullfsync", SQLITE_FullFSync },
- { "checkpoint_fullfsync", SQLITE_CkptFullFSync },
- { "reverse_unordered_selects", SQLITE_ReverseOrder },
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- { "automatic_index", SQLITE_AutoIndex },
-#endif
-#ifdef SQLITE_DEBUG
- { "sql_trace", SQLITE_SqlTrace },
- { "vdbe_listing", SQLITE_VdbeListing },
- { "vdbe_trace", SQLITE_VdbeTrace },
- { "vdbe_addoptrace", SQLITE_VdbeAddopTrace},
- { "vdbe_debug", SQLITE_SqlTrace | SQLITE_VdbeListing
- | SQLITE_VdbeTrace },
-#endif
-#ifndef SQLITE_OMIT_CHECK
- { "ignore_check_constraints", SQLITE_IgnoreChecks },
-#endif
- /* The following is VERY experimental */
- { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode },
-
- /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
- ** flag if there are any active statements. */
- { "read_uncommitted", SQLITE_ReadUncommitted },
- { "recursive_triggers", SQLITE_RecTriggers },
-
- /* This flag may only be set if both foreign-key and trigger support
- ** are present in the build. */
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- { "foreign_keys", SQLITE_ForeignKeys },
-#endif
- };
- int i;
- const struct sPragmaType *p;
- for(i=0, p=aPragma; i<ArraySize(aPragma); i++, p++){
- if( sqlite3StrICmp(zLeft, p->zName)==0 ){
- sqlite3 *db = pParse->db;
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- assert( v!=0 ); /* Already allocated by sqlite3Pragma() */
- if( ALWAYS(v) ){
- if( zRight==0 ){
- returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 );
- }else{
- int mask = p->mask; /* Mask of bits to set or clear. */
- if( db->autoCommit==0 ){
- /* Foreign key support may not be enabled or disabled while not
- ** in auto-commit mode. */
- mask &= ~(SQLITE_ForeignKeys);
- }
-
- if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
- }else{
- db->flags &= ~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);
- }
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+static void setAllPagerFlags(sqlite3 *db){
+ if( db->autoCommit ){
+ Db *pDb = db->aDb;
+ int n = db->nDb;
+ assert( SQLITE_FullFSync==PAGER_FULLFSYNC );
+ assert( SQLITE_CkptFullFSync==PAGER_CKPT_FULLFSYNC );
+ assert( SQLITE_CacheSpill==PAGER_CACHESPILL );
+ assert( (PAGER_FULLFSYNC | PAGER_CKPT_FULLFSYNC | PAGER_CACHESPILL)
+ == PAGER_FLAGS_MASK );
+ assert( (pDb->safety_level & PAGER_SYNCHRONOUS_MASK)==pDb->safety_level );
+ while( (n--) > 0 ){
+ if( pDb->pBt ){
+ sqlite3BtreeSetPagerFlags(pDb->pBt,
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK) );
}
-
- return 1;
+ pDb++;
}
}
- return 0;
}
-#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
+#else
+# define setAllPagerFlags(X) /* no-op */
+#endif
+
/*
** Return a human-readable name for a constraint resolution action.
@@ -314,15 +713,16 @@ void sqlite3Pragma(
char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */
const char *zDb = 0; /* The database name */
Token *pId; /* Pointer to <id> token */
- int iDb; /* Database index for <database> */
char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */
+ int iDb; /* Database index for <database> */
+ int lwr, upr, mid; /* Binary search bounds */
int rc; /* return value form SQLITE_FCNTL_PRAGMA */
sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* The specific database being pragmaed */
Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */
/* BEGIN SQLCIPHER */
#ifdef SQLITE_HAS_CODEC
- extern int codec_pragma(sqlite3*, int, Parse *, const char *, const char *);
+ extern int sqlcipher_codec_pragma(sqlite3*, int, Parse *, const char *, const char *);
#endif
/* END SQLCIPHER */
@@ -377,23 +777,51 @@ void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
sqlite3_free(aFcntl[0]);
}
- }else if( rc!=SQLITE_NOTFOUND ){
+ goto pragma_out;
+ }
+ if( rc!=SQLITE_NOTFOUND ){
if( aFcntl[0] ){
sqlite3ErrorMsg(pParse, "%s", aFcntl[0]);
sqlite3_free(aFcntl[0]);
}
pParse->nErr++;
pParse->rc = rc;
- }else
-
+
+ goto pragma_out;
+ }
+
/* BEGIN SQLCIPHER */
#ifdef SQLITE_HAS_CODEC
- if(codec_pragma(db, iDb, pParse, zLeft, zRight)) {
- /* codec_pragma executes internal */
- }else
- #endif
-/* END SQLCIPHER */
-
+ if(sqlcipher_codec_pragma(db, iDb, pParse, zLeft, zRight)) {
+ /* sqlcipher_codec_pragma executes internal */
+ goto pragma_out;
+ }
+#endif
+/* END SQLCIPHER */
+
+ /* Locate the pragma in the lookup table */
+ lwr = 0;
+ upr = ArraySize(aPragmaNames)-1;
+ while( lwr<=upr ){
+ mid = (lwr+upr)/2;
+ rc = sqlite3_stricmp(zLeft, aPragmaNames[mid].zName);
+ if( rc==0 ) break;
+ if( rc<0 ){
+ upr = mid - 1;
+ }else{
+ lwr = mid + 1;
+ }
+ }
+ if( lwr>upr ) goto pragma_out;
+
+ /* Make sure the database schema is loaded if the pragma requires that */
+ if( (aPragmaNames[mid].mPragFlag & PragFlag_NeedSchema)!=0 ){
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ }
+
+ /* Jump to the appropriate pragma handler */
+ switch( aPragmaNames[mid].ePragTyp ){
+
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/*
** PRAGMA [database.]default_cache_size
@@ -411,7 +839,8 @@ void sqlite3Pragma(
** size. But continue to take the absolute value of the default cache
** size of historical compatibility.
*/
- if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){
+ case PragTyp_DEFAULT_CACHE_SIZE: {
+ static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList getCacheSize[] = {
{ OP_Transaction, 0, 0, 0}, /* 0 */
{ OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */
@@ -424,13 +853,12 @@ void sqlite3Pragma(
{ 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", SQLITE_STATIC);
pParse->nMem += 2;
- addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
+ addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize,iLn);
sqlite3VdbeChangeP1(v, addr, iDb);
sqlite3VdbeChangeP1(v, addr+1, iDb);
sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
@@ -443,7 +871,8 @@ void sqlite3Pragma(
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -456,7 +885,7 @@ void sqlite3Pragma(
** database page size value. The value can only be set if
** the database has not yet been created.
*/
- if( sqlite3StrICmp(zLeft,"page_size")==0 ){
+ case PragTyp_PAGE_SIZE: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
if( !zRight ){
@@ -471,7 +900,8 @@ void sqlite3Pragma(
db->mallocFailed = 1;
}
}
- }else
+ break;
+ }
/*
** PRAGMA [database.]secure_delete
@@ -481,7 +911,7 @@ void sqlite3Pragma(
** secure_delete flag. The second form changes the secure_delete
** flag setting and reports thenew value.
*/
- if( sqlite3StrICmp(zLeft,"secure_delete")==0 ){
+ case PragTyp_SECURE_DELETE: {
Btree *pBt = pDb->pBt;
int b = -1;
assert( pBt!=0 );
@@ -496,7 +926,8 @@ void sqlite3Pragma(
}
b = sqlite3BtreeSecureDelete(pBt, b);
returnSingleInt(pParse, "secure_delete", b);
- }else
+ break;
+ }
/*
** PRAGMA [database.]max_page_count
@@ -515,11 +946,8 @@ void sqlite3Pragma(
**
** Return the number of pages in the specified database.
*/
- if( sqlite3StrICmp(zLeft,"page_count")==0
- || sqlite3StrICmp(zLeft,"max_page_count")==0
- ){
+ case PragTyp_PAGE_COUNT: {
int iReg;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3CodeVerifySchema(pParse, iDb);
iReg = ++pParse->nMem;
if( sqlite3Tolower(zLeft[0])=='p' ){
@@ -531,13 +959,14 @@ void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
- }else
+ break;
+ }
/*
** PRAGMA [database.]locking_mode
** PRAGMA [database.]locking_mode = (normal|exclusive)
*/
- if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
+ case PragTyp_LOCKING_MODE: {
const char *zRet = "normal";
int eMode = getLockingMode(zRight);
@@ -570,7 +999,8 @@ void sqlite3Pragma(
eMode = sqlite3PagerLockingMode(pPager, eMode);
}
- assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
+ assert( eMode==PAGER_LOCKINGMODE_NORMAL
+ || eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
zRet = "exclusive";
}
@@ -578,25 +1008,18 @@ void sqlite3Pragma(
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC);
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ break;
+ }
/*
** PRAGMA [database.]journal_mode
** PRAGMA [database.]journal_mode =
** (delete|persist|off|truncate|memory|wal|off)
*/
- if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
+ case PragTyp_JOURNAL_MODE: {
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
int ii; /* Loop counter */
- /* Force the schema to be loaded on all databases. This causes all
- ** database files to be opened and the journal_modes set. This is
- ** necessary because subsequent processing must know if the databases
- ** are in WAL mode. */
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
-
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
@@ -628,7 +1051,8 @@ void sqlite3Pragma(
}
}
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ break;
+ }
/*
** PRAGMA [database.]journal_size_limit
@@ -636,16 +1060,17 @@ void sqlite3Pragma(
**
** Get or set the size limit on rollback journal files.
*/
- if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
+ case PragTyp_JOURNAL_SIZE_LIMIT: {
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
- sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
+ sqlite3DecOrHexToI64(zRight, &iLimit);
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
returnSingleInt(pParse, "journal_size_limit", iLimit);
- }else
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
@@ -657,57 +1082,48 @@ void sqlite3Pragma(
** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"auto_vacuum")==0 ){
+ case PragTyp_AUTO_VACUUM: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( !zRight ){
- int auto_vacuum;
- if( ALWAYS(pBt) ){
- auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt);
- }else{
- auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM;
- }
- returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
+ returnSingleInt(pParse, "auto_vacuum", sqlite3BtreeGetAutoVacuum(pBt));
}else{
int eAuto = getAutoVacuum(zRight);
assert( eAuto>=0 && eAuto<=2 );
db->nextAutovac = (u8)eAuto;
- if( ALWAYS(eAuto>=0) ){
- /* Call SetAutoVacuum() to set initialize the internal auto and
- ** incr-vacuum flags. This is required in case this connection
- ** creates the database file. It is important that it is created
- ** as an auto-vacuum capable db.
+ /* Call SetAutoVacuum() to set initialize the internal auto and
+ ** incr-vacuum flags. This is required in case this connection
+ ** creates the database file. It is important that it is created
+ ** as an auto-vacuum capable db.
+ */
+ rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
+ if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
+ /* When setting the auto_vacuum mode to either "full" or
+ ** "incremental", write the value of meta[6] in the database
+ ** file. Before writing to meta[6], check that meta[3] indicates
+ ** that this really is an auto-vacuum capable database.
*/
- rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
- if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
- /* When setting the auto_vacuum mode to either "full" or
- ** "incremental", write the value of meta[6] in the database
- ** file. Before writing to meta[6], check that meta[3] indicates
- ** that this really is an auto-vacuum capable database.
- */
- static const VdbeOpList setMeta6[] = {
- { OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
- { OP_If, 1, 0, 0}, /* 2 */
- { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 1, 0}, /* 4 */
- { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
- };
- int iAddr;
- iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6);
- sqlite3VdbeChangeP1(v, iAddr, iDb);
- sqlite3VdbeChangeP1(v, iAddr+1, iDb);
- sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
- sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
- sqlite3VdbeChangeP1(v, iAddr+5, iDb);
- sqlite3VdbeUsesBtree(v, iDb);
- }
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList setMeta6[] = {
+ { OP_Transaction, 0, 1, 0}, /* 0 */
+ { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
+ { OP_If, 1, 0, 0}, /* 2 */
+ { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
+ { OP_Integer, 0, 1, 0}, /* 4 */
+ { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
+ };
+ int iAddr;
+ iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
+ sqlite3VdbeChangeP1(v, iAddr, iDb);
+ sqlite3VdbeChangeP1(v, iAddr+1, iDb);
+ sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
+ sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
+ sqlite3VdbeChangeP1(v, iAddr+5, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
}
}
- }else
+ break;
+ }
#endif
/*
@@ -716,22 +1132,20 @@ void sqlite3Pragma(
** Do N steps of incremental vacuuming on a database.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"incremental_vacuum")==0 ){
+ case PragTyp_INCREMENTAL_VACUUM: {
int iLimit, addr;
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){
iLimit = 0x7fffffff;
}
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3VdbeAddOp2(v, OP_Integer, iLimit, 1);
- addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb);
+ addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb); VdbeCoverage(v);
sqlite3VdbeAddOp1(v, OP_ResultRow, 1);
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
- sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr);
- }else
+ break;
+ }
#endif
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
@@ -746,8 +1160,7 @@ void sqlite3Pragma(
** number of pages is adjusted so that the cache uses -N kibibytes
** of memory.
*/
- if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ case PragTyp_CACHE_SIZE: {
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
@@ -756,7 +1169,8 @@ void sqlite3Pragma(
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
/*
** PRAGMA [database.]mmap_size(N)
@@ -772,12 +1186,13 @@ void sqlite3Pragma(
** as little or as much as it wants. Except, if N is set to 0 then the
** upper layers will never invoke the xFetch interfaces to the VFS.
*/
- if( sqlite3StrICmp(zLeft,"mmap_size")==0 ){
+ case PragTyp_MMAP_SIZE: {
sqlite3_int64 sz;
+#if SQLITE_MAX_MMAP_SIZE>0
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( zRight ){
int ii;
- sqlite3Atoi64(zRight, &sz, 1000, SQLITE_UTF8);
+ sqlite3DecOrHexToI64(zRight, &sz);
if( sz<0 ) sz = sqlite3GlobalConfig.szMmap;
if( pId2->n==0 ) db->szMmap = sz;
for(ii=db->nDb-1; ii>=0; ii--){
@@ -787,13 +1202,19 @@ void sqlite3Pragma(
}
}
sz = -1;
- if( sqlite3_file_control(db,zDb,SQLITE_FCNTL_MMAP_SIZE,&sz)==SQLITE_OK ){
-#if SQLITE_MAX_MMAP_SIZE==0
- sz = 0;
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz);
+#else
+ sz = 0;
+ rc = SQLITE_OK;
#endif
+ if( rc==SQLITE_OK ){
returnSingleInt(pParse, "mmap_size", sz);
+ }else if( rc!=SQLITE_NOTFOUND ){
+ pParse->nErr++;
+ pParse->rc = rc;
}
- }else
+ break;
+ }
/*
** PRAGMA temp_store
@@ -806,13 +1227,14 @@ void sqlite3Pragma(
** Note that it is possible for the library compile-time options to
** override this setting
*/
- if( sqlite3StrICmp(zLeft, "temp_store")==0 ){
+ case PragTyp_TEMP_STORE: {
if( !zRight ){
returnSingleInt(pParse, "temp_store", db->temp_store);
}else{
changeTempStorage(pParse, zRight);
}
- }else
+ break;
+ }
/*
** PRAGMA temp_store_directory
@@ -824,7 +1246,7 @@ void sqlite3Pragma(
** If temporary directory is changed, then invalidateTempStorage.
**
*/
- if( sqlite3StrICmp(zLeft, "temp_store_directory")==0 ){
+ case PragTyp_TEMP_STORE_DIRECTORY: {
if( !zRight ){
if( sqlite3_temp_directory ){
sqlite3VdbeSetNumCols(v, 1);
@@ -857,7 +1279,8 @@ void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
- }else
+ break;
+ }
#if SQLITE_OS_WIN
/*
@@ -873,7 +1296,7 @@ void sqlite3Pragma(
** by this setting, regardless of its value.
**
*/
- if( sqlite3StrICmp(zLeft, "data_store_directory")==0 ){
+ case PragTyp_DATA_STORE_DIRECTORY: {
if( !zRight ){
if( sqlite3_data_directory ){
sqlite3VdbeSetNumCols(v, 1);
@@ -900,26 +1323,20 @@ void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
- }else
+ break;
+ }
#endif
-#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
-# if defined(__APPLE__)
-# define SQLITE_ENABLE_LOCKING_STYLE 1
-# else
-# define SQLITE_ENABLE_LOCKING_STYLE 0
-# endif
-#endif
#if SQLITE_ENABLE_LOCKING_STYLE
/*
- ** PRAGMA [database.]lock_proxy_file
- ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
- **
- ** Return or set the value of the lock_proxy_file flag. Changing
- ** the value sets a specific file to be used for database access locks.
- **
- */
- if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){
+ ** PRAGMA [database.]lock_proxy_file
+ ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
+ **
+ ** Return or set the value of the lock_proxy_file flag. Changing
+ ** the value sets a specific file to be used for database access locks.
+ **
+ */
+ case PragTyp_LOCK_PROXY_FILE: {
if( !zRight ){
Pager *pPager = sqlite3BtreePager(pDb->pBt);
char *proxy_file_path = NULL;
@@ -950,7 +1367,8 @@ void sqlite3Pragma(
goto pragma_out;
}
}
- }else
+ break;
+ }
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
/*
@@ -962,8 +1380,7 @@ void sqlite3Pragma(
** default value will be restored the next time the database is
** opened.
*/
- if( sqlite3StrICmp(zLeft,"synchronous")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ case PragTyp_SYNCHRONOUS: {
if( !zRight ){
returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
}else{
@@ -972,16 +1389,42 @@ void sqlite3Pragma(
"Safety level may not be changed inside a transaction");
}else{
pDb->safety_level = getSafetyLevel(zRight,0,1)+1;
+ setAllPagerFlags(db);
}
}
- }else
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
#ifndef SQLITE_OMIT_FLAG_PRAGMAS
- if( flagPragma(pParse, zLeft, zRight) ){
- /* The flagPragma() subroutine also generates any necessary code
- ** there is nothing more to do here */
- }else
+ case PragTyp_FLAG: {
+ if( zRight==0 ){
+ returnSingleInt(pParse, aPragmaNames[mid].zName,
+ (db->flags & aPragmaNames[mid].iArg)!=0 );
+ }else{
+ int mask = aPragmaNames[mid].iArg; /* Mask of bits to set or clear. */
+ if( db->autoCommit==0 ){
+ /* Foreign key support may not be enabled or disabled while not
+ ** in auto-commit mode. */
+ mask &= ~(SQLITE_ForeignKeys);
+ }
+
+ if( sqlite3GetBoolean(zRight, 0) ){
+ db->flags |= mask;
+ }else{
+ db->flags &= ~mask;
+ if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
+ }
+
+ /* 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);
+ setAllPagerFlags(db);
+ }
+ break;
+ }
#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
@@ -997,16 +1440,14 @@ void sqlite3Pragma(
** notnull: True if 'NOT NULL' is part of column declaration
** dflt_value: The default value for the column, if any.
*/
- if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
+ case PragTyp_TABLE_INFO: if( zRight ){
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
int i, k;
int nHidden = 0;
Column *pCol;
- Index *pPk;
- for(pPk=pTab->pIndex; pPk && pPk->autoIndex!=2; pPk=pPk->pNext){}
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
sqlite3VdbeSetNumCols(v, 6);
pParse->nMem = 6;
sqlite3CodeVerifySchema(pParse, iDb);
@@ -1043,12 +1484,44 @@ void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){
+ case PragTyp_STATS: {
+ Index *pIdx;
+ HashElem *i;
+ v = sqlite3GetVdbe(pParse);
+ sqlite3VdbeSetNumCols(v, 4);
+ pParse->nMem = 4;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "table", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "index", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "width", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "height", SQLITE_STATIC);
+ for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
+ Table *pTab = sqliteHashData(i);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, 2);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pTab->szTabRow), 3);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pTab->nRowLogEst), 4);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pIdx->szIdxRow), 3);
+ sqlite3VdbeAddOp2(v, OP_Integer,
+ (int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]), 4);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ }
+ }
+ }
+ break;
+
+ case PragTyp_INDEX_INFO: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pIdx = sqlite3FindIndex(db, zRight, zDb);
if( pIdx ){
int i;
@@ -1059,8 +1532,8 @@ void sqlite3Pragma(
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC);
sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC);
sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC);
- for(i=0; i<pIdx->nColumn; i++){
- int cnum = pIdx->aiColumn[i];
+ for(i=0; i<pIdx->nKeyCol; i++){
+ i16 cnum = pIdx->aiColumn[i];
sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2);
assert( pTab->nCol>cnum );
@@ -1068,39 +1541,34 @@ void sqlite3Pragma(
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){
+ case PragTyp_INDEX_LIST: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ int i;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
v = sqlite3GetVdbe(pParse);
- pIdx = pTab->pIndex;
- if( pIdx ){
- int i = 0;
- sqlite3VdbeSetNumCols(v, 3);
- pParse->nMem = 3;
- sqlite3CodeVerifySchema(pParse, iDb);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
- while(pIdx){
- 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;
- }
+ sqlite3VdbeSetNumCols(v, 3);
+ pParse->nMem = 3;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
+ for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
+ sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp2(v, OP_Integer, IsUniqueIndex(pIdx), 3);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "database_list")==0 ){
+ case PragTyp_DATABASE_LIST: {
int i;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
@@ -1115,9 +1583,10 @@ void sqlite3Pragma(
sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "collation_list")==0 ){
+ case PragTyp_COLLATION_LIST: {
int i = 0;
HashElem *p;
sqlite3VdbeSetNumCols(v, 2);
@@ -1130,14 +1599,14 @@ void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */
#ifndef SQLITE_OMIT_FOREIGN_KEY
- if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){
+ case PragTyp_FOREIGN_KEY_LIST: if( zRight ){
FKey *pFK;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
v = sqlite3GetVdbe(pParse);
@@ -1177,12 +1646,13 @@ void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
#ifndef SQLITE_OMIT_FOREIGN_KEY
#ifndef SQLITE_OMIT_TRIGGER
- if( sqlite3StrICmp(zLeft, "foreign_key_check")==0 ){
+ case PragTyp_FOREIGN_KEY_CHECK: {
FKey *pFK; /* A foreign key constraint */
Table *pTab; /* Child table contain "REFERENCES" keyword */
Table *pParent; /* Parent table that child points to */
@@ -1198,7 +1668,6 @@ void sqlite3Pragma(
int addrOk; /* Jump here if the key is OK */
int *aiCols; /* child to parent column mapping */
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
regResult = pParse->nMem+1;
pParse->nMem += 4;
regKey = ++pParse->nMem;
@@ -1226,8 +1695,8 @@ void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult, 0, pTab->zName,
P4_TRANSIENT);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
- pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
- if( pParent==0 ) break;
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
+ if( pParent==0 ) continue;
pIdx = 0;
sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
@@ -1235,51 +1704,54 @@ void sqlite3Pragma(
if( pIdx==0 ){
sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
}else{
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
- sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
}
}else{
k = 0;
break;
}
}
+ assert( pParse->nErr>0 || pFK==0 );
if( pFK ) break;
if( pParse->nTab<i ) pParse->nTab = i;
- addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v);
for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
- pParent = sqlite3LocateTable(pParse, 0, pFK->zTo, zDb);
- assert( pParent!=0 );
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
pIdx = 0;
aiCols = 0;
- x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
- assert( x==0 );
+ if( pParent ){
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
+ assert( x==0 );
+ }
addrOk = sqlite3VdbeMakeLabel(v);
- if( pIdx==0 ){
+ if( pParent && pIdx==0 ){
int iKey = pFK->aCol[0].iFrom;
assert( iKey>=0 && iKey<pTab->nCol );
if( iKey!=pTab->iPKey ){
sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
sqlite3ColumnDefault(v, pTab, iKey, regRow);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk);
- sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
- sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, regRow,
+ sqlite3VdbeCurrentAddr(v)+3); VdbeCoverage(v);
}else{
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
}
- sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow);
+ sqlite3VdbeAddOp3(v, OP_NotExists, i, 0, regRow); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrOk);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}else{
for(j=0; j<pFK->nCol; j++){
sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
- aiCols ? aiCols[j] : pFK->aCol[0].iFrom, regRow+j);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk);
+ aiCols ? aiCols[j] : pFK->aCol[j].iFrom, regRow+j);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v);
+ }
+ if( pParent ){
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
+ sqlite3IndexAffinityStr(v,pIdx), pFK->nCol);
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ VdbeCoverage(v);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regRow, pFK->nCol, regKey);
- sqlite3VdbeChangeP4(v, -1,
- sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
- sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
}
sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
sqlite3VdbeAddOp4(v, OP_String8, 0, regResult+2, 0,
@@ -1289,15 +1761,16 @@ void sqlite3Pragma(
sqlite3VdbeResolveLabel(v, addrOk);
sqlite3DbFree(db, aiCols);
}
- sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1);
+ sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrTop);
}
- }else
+ }
+ break;
#endif /* !defined(SQLITE_OMIT_TRIGGER) */
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
#ifndef NDEBUG
- if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
+ case PragTyp_PARSER_TRACE: {
if( zRight ){
if( sqlite3GetBoolean(zRight, 0) ){
sqlite3ParserTrace(stderr, "parser: ");
@@ -1305,40 +1778,40 @@ void sqlite3Pragma(
sqlite3ParserTrace(0, 0);
}
}
- }else
+ }
+ break;
#endif
/* Reinstall the LIKE and GLOB functions. The variant of LIKE
** used will be case sensitive or not depending on the RHS.
*/
- if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
+ case PragTyp_CASE_SENSITIVE_LIKE: {
if( zRight ){
sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0));
}
- }else
+ }
+ break;
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
#endif
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
- /* Pragma "quick_check" is an experimental reduced version of
+ /* Pragma "quick_check" is 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
- ){
+ case PragTyp_INTEGRITY_CHECK: {
int i, j, addr, mxErr;
/* Code that appears at the end of the integrity check. If no error
** messages have been generated, output OK. Otherwise output the
** error message
*/
+ static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList endCode[] = {
- { OP_AddImm, 1, 0, 0}, /* 0 */
- { OP_IfNeg, 1, 0, 0}, /* 1 */
- { OP_String8, 0, 3, 0}, /* 2 */
+ { OP_IfNeg, 1, 0, 0}, /* 0 */
+ { OP_String8, 0, 3, 0}, /* 1 */
{ OP_ResultRow, 3, 1, 0},
};
@@ -1358,7 +1831,6 @@ void sqlite3Pragma(
if( pId2->z==0 ) iDb = -1;
/* Initialize the VDBE program */
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pParse->nMem = 6;
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC);
@@ -1384,6 +1856,7 @@ void sqlite3Pragma(
sqlite3CodeVerifySchema(pParse, i);
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
@@ -1397,27 +1870,29 @@ void sqlite3Pragma(
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
- sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
- cnt++;
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
+ VdbeComment((v, "%s", pTab->zName));
+ cnt++;
+ }
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
+ VdbeComment((v, "%s", pIdx->zName));
cnt++;
}
}
/* Make sure sufficient number of registers have been allocated */
- if( pParse->nMem < cnt+4 ){
- pParse->nMem = cnt+4;
- }
+ pParse->nMem = MAX( pParse->nMem, cnt+8 );
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
sqlite3VdbeChangeP5(v, (u8)i);
- addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2);
+ addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
P4_DYNAMIC);
- sqlite3VdbeAddOp2(v, OP_Move, 2, 4);
+ sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
sqlite3VdbeAddOp2(v, OP_ResultRow, 2, 1);
sqlite3VdbeJumpHere(v, addr);
@@ -1426,77 +1901,127 @@ void sqlite3Pragma(
*/
for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
- Index *pIdx;
+ Index *pIdx, *pPk;
+ Index *pPrior = 0;
int loopTop;
+ int iDataCur, iIdxCur;
+ int r1 = -1;
if( pTab->pIndex==0 ) continue;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
- sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
- 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 */
+ sqlite3ExprCacheClear(pParse);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead,
+ 1, 0, &iDataCur, &iIdxCur);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- int jmp2;
- int r1;
- static const VdbeOpList idxErr[] = {
- { 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},
- };
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0);
- jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1);
- addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
- sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
- sqlite3VdbeJumpHere(v, addr+9);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
+ }
+ pParse->nMem = MAX(pParse->nMem, 8+j);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
+ loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
+ /* Verify that all NOT NULL columns really are NOT NULL */
+ for(j=0; j<pTab->nCol; j++){
+ char *zErr;
+ int jmp2, jmp3;
+ if( j==pTab->iPKey ) continue;
+ if( pTab->aCol[j].notNull==0 ) continue;
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
+ pTab->aCol[j].zName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
+ jmp3 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v);
+ sqlite3VdbeAddOp0(v, OP_Halt);
sqlite3VdbeJumpHere(v, jmp2);
+ sqlite3VdbeJumpHere(v, jmp3);
}
- sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop+1);
- sqlite3VdbeJumpHere(v, loopTop);
+ /* Validate index entries for the current row */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- static const VdbeOpList cntIdx[] = {
- { OP_Integer, 0, 3, 0},
- { OP_Rewind, 0, 0, 0}, /* 1 */
- { OP_AddImm, 3, 1, 0},
- { OP_Next, 0, 0, 0}, /* 3 */
- { 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},
- };
- 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+4);
- sqlite3VdbeChangeP4(v, addr+6,
+ int jmp2, jmp3, jmp4, jmp5;
+ int ckUniq = sqlite3VdbeMakeLabel(v);
+ if( pPk==pIdx ) continue;
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
+ pPrior, r1);
+ pPrior = pIdx;
+ sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */
+ /* Verify that an index entry exists for the current table row */
+ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
+ pIdx->nColumn); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC);
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
+ " missing from index ", P4_STATIC);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+ jmp5 = sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
+ pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
+ jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v);
+ sqlite3VdbeAddOp0(v, OP_Halt);
+ sqlite3VdbeJumpHere(v, jmp2);
+ /* For UNIQUE indexes, verify that only one entry exists with the
+ ** current key. The entry is unique if (1) any column is NULL
+ ** or (2) the next entry has a different key */
+ if( IsUniqueIndex(pIdx) ){
+ int uniqOk = sqlite3VdbeMakeLabel(v);
+ int jmp6;
+ int kk;
+ for(kk=0; kk<pIdx->nKeyCol; kk++){
+ int iCol = pIdx->aiColumn[kk];
+ assert( iCol>=0 && iCol<pTab->nCol );
+ if( pTab->aCol[iCol].notNull ) continue;
+ sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
+ VdbeCoverage(v);
+ }
+ jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, uniqOk);
+ sqlite3VdbeJumpHere(v, jmp6);
+ sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
+ pIdx->nKeyCol); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
+ "non-unique entry in index ", P4_STATIC);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, jmp5);
+ sqlite3VdbeResolveLabel(v, uniqOk);
+ }
+ sqlite3VdbeJumpHere(v, jmp4);
+ sqlite3ResolvePartIdxLabel(pParse, jmp3);
+ }
+ sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, loopTop-1);
+#ifndef SQLITE_OMIT_BTREECOUNT
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0,
"wrong # of entries in index ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT);
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ if( pPk==pIdx ) continue;
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
+ sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 7, 1);
}
+#endif /* SQLITE_OMIT_BTREECOUNT */
}
}
- addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
- sqlite3VdbeChangeP2(v, addr, -mxErr);
- sqlite3VdbeJumpHere(v, addr+1);
- sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
- }else
+ addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
+ sqlite3VdbeChangeP3(v, addr, -mxErr);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeChangeP4(v, addr+1, "ok", P4_STATIC);
+ }
+ break;
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_UTF16
@@ -1522,7 +2047,7 @@ void sqlite3Pragma(
** new database files created using this database handle. It is only
** useful if invoked immediately after the main database i
*/
- if( sqlite3StrICmp(zLeft, "encoding")==0 ){
+ case PragTyp_ENCODING: {
static const struct EncName {
char *zName;
u8 enc;
@@ -1569,7 +2094,8 @@ void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_UTF16 */
#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
@@ -1603,11 +2129,7 @@ void sqlite3Pragma(
** The user-version is not used internally by SQLite. It may be used by
** applications for any purpose.
*/
- if( sqlite3StrICmp(zLeft, "schema_version")==0
- || sqlite3StrICmp(zLeft, "user_version")==0
- || sqlite3StrICmp(zLeft, "freelist_count")==0
- || sqlite3StrICmp(zLeft, "application_id")==0
- ){
+ case PragTyp_HEADER_VALUE: {
int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */
sqlite3VdbeUsesBtree(v, iDb);
switch( zLeft[0] ){
@@ -1632,7 +2154,7 @@ void sqlite3Pragma(
{ OP_Integer, 0, 1, 0}, /* 1 */
{ OP_SetCookie, 0, 0, 1}, /* 2 */
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie);
+ int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
sqlite3VdbeChangeP1(v, addr, iDb);
sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
sqlite3VdbeChangeP1(v, addr+2, iDb);
@@ -1644,14 +2166,15 @@ void sqlite3Pragma(
{ OP_ReadCookie, 0, 1, 0}, /* 1 */
{ OP_ResultRow, 1, 1, 0}
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie);
+ int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie, 0);
sqlite3VdbeChangeP1(v, addr, iDb);
sqlite3VdbeChangeP1(v, addr+1, iDb);
sqlite3VdbeChangeP3(v, addr+1, iCookie);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
@@ -1661,7 +2184,7 @@ void sqlite3Pragma(
** Return the names of all compile-time options used in this build,
** one option per row.
*/
- if( sqlite3StrICmp(zLeft, "compile_options")==0 ){
+ case PragTyp_COMPILE_OPTIONS: {
int i = 0;
const char *zOpt;
sqlite3VdbeSetNumCols(v, 1);
@@ -1671,7 +2194,8 @@ void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
#ifndef SQLITE_OMIT_WAL
@@ -1680,7 +2204,7 @@ void sqlite3Pragma(
**
** Checkpoint the database.
*/
- if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
+ case PragTyp_WAL_CHECKPOINT: {
int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
@@ -1690,7 +2214,6 @@ void sqlite3Pragma(
eMode = SQLITE_CHECKPOINT_RESTART;
}
}
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
@@ -1699,7 +2222,8 @@ void sqlite3Pragma(
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
- }else
+ }
+ break;
/*
** PRAGMA wal_autocheckpoint
@@ -1709,14 +2233,15 @@ void sqlite3Pragma(
** after accumulating N frames in the log. Or query for the current value
** of N.
*/
- if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
+ case PragTyp_WAL_AUTOCHECKPOINT: {
if( zRight ){
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
returnSingleInt(pParse, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
- }else
+ }
+ break;
#endif
/*
@@ -1725,9 +2250,10 @@ void sqlite3Pragma(
** This pragma attempts to free as much memory as possible from the
** current database connection.
*/
- if( sqlite3StrICmp(zLeft, "shrink_memory")==0 ){
+ case PragTyp_SHRINK_MEMORY: {
sqlite3_db_release_memory(db);
- }else
+ break;
+ }
/*
** PRAGMA busy_timeout
@@ -1738,18 +2264,36 @@ void sqlite3Pragma(
** then 0 is returned. Setting the busy_timeout to 0 or negative
** disables the timeout.
*/
- if( sqlite3StrICmp(zLeft, "busy_timeout")==0 ){
+ /*case PragTyp_BUSY_TIMEOUT*/ default: {
+ assert( aPragmaNames[mid].ePragTyp==PragTyp_BUSY_TIMEOUT );
if( zRight ){
sqlite3_busy_timeout(db, sqlite3Atoi(zRight));
}
returnSingleInt(pParse, "timeout", db->busyTimeout);
- }else
+ break;
+ }
+
+ /*
+ ** PRAGMA soft_heap_limit
+ ** PRAGMA soft_heap_limit = N
+ **
+ ** Call sqlite3_soft_heap_limit64(N). Return the result. If N is omitted,
+ ** use -1.
+ */
+ case PragTyp_SOFT_HEAP_LIMIT: {
+ sqlite3_int64 N;
+ if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){
+ sqlite3_soft_heap_limit64(N);
+ }
+ returnSingleInt(pParse, "soft_heap_limit", sqlite3_soft_heap_limit64(-1));
+ break;
+ }
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Report the current state of file logs for all databases
*/
- if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
+ case PragTyp_LOCK_STATUS: {
static const char *const azLockName[] = {
"unlocked", "shared", "reserved", "pending", "exclusive"
};
@@ -1774,35 +2318,39 @@ void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
-
- }else
+ break;
+ }
#endif
#ifdef SQLITE_HAS_CODEC
- if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){
- sqlite3_key(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( sqlite3StrICmp(zLeft, "rekey")==0 && zRight ){
- sqlite3_rekey(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( zRight && (sqlite3StrICmp(zLeft, "hexkey")==0 ||
- sqlite3StrICmp(zLeft, "hexrekey")==0) ){
- int i, h1, h2;
- char zKey[40];
- for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){
- h1 += 9*(1&(h1>>6));
- h2 += 9*(1&(h2>>6));
- zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4);
- }
- if( (zLeft[3] & 0xf)==0xb ){
- sqlite3_key(db, zKey, i/2);
- }else{
- sqlite3_rekey(db, zKey, i/2);
+ case PragTyp_KEY: {
+ if( zRight ) sqlite3_key_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
+ break;
+ }
+ case PragTyp_REKEY: {
+ if( zRight ) sqlite3_rekey_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
+ break;
+ }
+ case PragTyp_HEXKEY: {
+ if( zRight ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zRight[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ if( (zLeft[3] & 0xf)==0xb ){
+ sqlite3_key_v2(db, zDb, zKey, i/2);
+ }else{
+ sqlite3_rekey_v2(db, zDb, zKey, i/2);
+ }
}
- }else
+ break;
+ }
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
- if( sqlite3StrICmp(zLeft, "activate_extensions")==0 && zRight ){
+ case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){
#ifdef SQLITE_HAS_CODEC
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
sqlite3_activate_see(&zRight[4]);
@@ -1813,23 +2361,12 @@ void sqlite3Pragma(
sqlite3_activate_cerod(&zRight[6]);
}
#endif
- }else
+ }
+ break;
#endif
-
- {/* Empty ELSE clause */}
+ } /* End of the PRAGMA switch */
- /*
- ** Reset the safety level, in case the fullfsync flag or synchronous
- ** setting changed.
- */
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- if( db->autoCommit ){
- sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
- (db->flags&SQLITE_FullFSync)!=0,
- (db->flags&SQLITE_CkptFullFSync)!=0);
- }
-#endif
pragma_out:
sqlite3DbFree(db, zLeft);
sqlite3DbFree(db, zRight);
diff --git a/src/prepare.c b/src/prepare.c
index d78d83c..c7ba53a 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -525,6 +525,17 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
}
/*
+** Free all memory allocations in the pParse object
+*/
+void sqlite3ParserReset(Parse *pParse){
+ if( pParse ){
+ sqlite3 *db = pParse->db;
+ sqlite3DbFree(db, pParse->aLabel);
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
+ }
+}
+
+/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare(
@@ -592,7 +603,7 @@ static int sqlite3Prepare(
sqlite3VtabUnlockList(db);
pParse->db = db;
- pParse->nQueryLoop = (double)1;
+ pParse->nQueryLoop = 0; /* Logarithmic, so 0 really means 1 */
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
@@ -614,7 +625,7 @@ static int sqlite3Prepare(
}else{
sqlite3RunParser(pParse, zSql, &zErrMsg);
}
- assert( 1==(int)pParse->nQueryLoop );
+ assert( 0==pParse->nQueryLoop );
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM;
@@ -681,6 +692,7 @@ static int sqlite3Prepare(
end_prepare:
+ sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
@@ -810,6 +822,12 @@ static int sqlite3Prepare16(
if( !sqlite3SafetyCheckOk(db) ){
return SQLITE_MISUSE_BKPT;
}
+ if( nBytes>=0 ){
+ int sz;
+ const char *z = (const char*)zSql;
+ for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){}
+ nBytes = sz;
+ }
sqlite3_mutex_enter(db->mutex);
zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE);
if( zSql8 ){
diff --git a/src/printf.c b/src/printf.c
index 9f68d20..3791080 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -135,20 +135,31 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
-** Append N space characters to the given string buffer.
+** Set the StrAccum object to an error mode.
*/
-void sqlite3AppendSpace(StrAccum *pAccum, int N){
- static const char zSpaces[] = " ";
- while( N>=(int)sizeof(zSpaces)-1 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
- N -= sizeof(zSpaces)-1;
- }
- if( N>0 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, N);
- }
+static void setStrAccumError(StrAccum *p, u8 eError){
+ p->accError = eError;
+ p->nAlloc = 0;
}
/*
+** Extra argument values from a PrintfArguments object
+*/
+static sqlite3_int64 getIntArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return sqlite3_value_int64(p->apArg[p->nUsed++]);
+}
+static double getDoubleArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0.0;
+ return sqlite3_value_double(p->apArg[p->nUsed++]);
+}
+static char *getTextArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
+}
+
+
+/*
** On machines with a small stack size, you can redefine the
** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
*/
@@ -161,10 +172,10 @@ void sqlite3AppendSpace(StrAccum *pAccum, int N){
** Render a string given by "fmt" into the StrAccum object.
*/
void sqlite3VXPrintf(
- StrAccum *pAccum, /* Accumulate results here */
- int useExtended, /* Allow extended %-conversions */
- const char *fmt, /* Format string */
- va_list ap /* arguments */
+ StrAccum *pAccum, /* Accumulate results here */
+ u32 bFlags, /* SQLITE_PRINTF_* flags */
+ const char *fmt, /* Format string */
+ va_list ap /* arguments */
){
int c; /* Next character in the format string */
char *bufpt; /* Pointer to the conversion buffer */
@@ -182,6 +193,8 @@ void sqlite3VXPrintf(
etByte flag_longlong; /* True if the "ll" flag is present */
etByte done; /* Loop termination flag */
etByte xtype = 0; /* Conversion paradigm */
+ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
+ u8 useIntern; /* Ok to use internal conversions (ex: %T) */
char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
LONGDOUBLE_TYPE realvalue; /* Value for real types */
@@ -196,16 +209,23 @@ void sqlite3VXPrintf(
etByte flag_dp; /* True if decimal point should be shown */
etByte flag_rtz; /* True if trailing zeros should be removed */
#endif
+ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
char buf[etBUFSIZE]; /* Conversion buffer */
bufpt = 0;
+ if( bFlags ){
+ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
+ pArgList = va_arg(ap, PrintfArguments*);
+ }
+ useIntern = bFlags & SQLITE_PRINTF_INTERNAL;
+ }else{
+ bArgList = useIntern = 0;
+ }
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
- int amt;
bufpt = (char *)fmt;
- amt = 1;
- while( (c=(*++fmt))!='%' && c!=0 ) amt++;
- sqlite3StrAccumAppend(pAccum, bufpt, amt);
+ while( (c=(*++fmt))!='%' && c!=0 ){};
+ sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
if( c==0 ) break;
}
if( (c=(*++fmt))==0 ){
@@ -230,7 +250,11 @@ void sqlite3VXPrintf(
/* Get the field width */
width = 0;
if( c=='*' ){
- width = va_arg(ap,int);
+ if( bArgList ){
+ width = (int)getIntArg(pArgList);
+ }else{
+ width = va_arg(ap,int);
+ }
if( width<0 ){
flag_leftjustify = 1;
width = -width;
@@ -247,7 +271,11 @@ void sqlite3VXPrintf(
precision = 0;
c = *++fmt;
if( c=='*' ){
- precision = va_arg(ap,int);
+ if( bArgList ){
+ precision = (int)getIntArg(pArgList);
+ }else{
+ precision = va_arg(ap,int);
+ }
if( precision<0 ) precision = -precision;
c = *++fmt;
}else{
@@ -278,7 +306,7 @@ void sqlite3VXPrintf(
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
- if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
+ if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
return;
@@ -318,7 +346,9 @@ void sqlite3VXPrintf(
case etRADIX:
if( infop->flags & FLAG_SIGNED ){
i64 v;
- if( flag_longlong ){
+ if( bArgList ){
+ v = getIntArg(pArgList);
+ }else if( flag_longlong ){
v = va_arg(ap,i64);
}else if( flag_long ){
v = va_arg(ap,long int);
@@ -339,7 +369,9 @@ void sqlite3VXPrintf(
else prefix = 0;
}
}else{
- if( flag_longlong ){
+ if( bArgList ){
+ longvalue = (u64)getIntArg(pArgList);
+ }else if( flag_longlong ){
longvalue = va_arg(ap,u64);
}else if( flag_long ){
longvalue = va_arg(ap,unsigned long int);
@@ -359,7 +391,7 @@ void sqlite3VXPrintf(
nOut = precision + 10;
zOut = zExtra = sqlite3Malloc( nOut );
if( zOut==0 ){
- pAccum->mallocFailed = 1;
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
return;
}
}
@@ -374,10 +406,8 @@ void sqlite3VXPrintf(
*(--bufpt) = zOrd[x*2];
}
{
- register const char *cset; /* Use registers for speed */
- register int base;
- cset = &aDigits[infop->charset];
- base = infop->base;
+ const char *cset = &aDigits[infop->charset];
+ u8 base = infop->base;
do{ /* Convert to ascii */
*(--bufpt) = cset[longvalue%base];
longvalue = longvalue/base;
@@ -399,7 +429,11 @@ void sqlite3VXPrintf(
case etFLOAT:
case etEXP:
case etGENERIC:
- realvalue = va_arg(ap,double);
+ if( bArgList ){
+ realvalue = getDoubleArg(pArgList);
+ }else{
+ realvalue = va_arg(ap,double);
+ }
#ifdef SQLITE_OMIT_FLOATING_POINT
length = 0;
#else
@@ -413,13 +447,7 @@ void sqlite3VXPrintf(
else prefix = 0;
}
if( xtype==etGENERIC && precision>0 ) precision--;
-#if 0
- /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
- for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
-#else
- /* It makes more sense to use 0.5 */
for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
-#endif
if( xtype==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
@@ -474,10 +502,10 @@ void sqlite3VXPrintf(
}else{
e2 = exp;
}
- if( e2+precision+width > etBUFSIZE - 15 ){
- bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 );
+ if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){
+ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
return;
}
}
@@ -560,7 +588,9 @@ void sqlite3VXPrintf(
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
break;
case etSIZE:
- *(va_arg(ap,int*)) = pAccum->nChar;
+ if( !bArgList ){
+ *(va_arg(ap,int*)) = pAccum->nChar;
+ }
length = width = 0;
break;
case etPERCENT:
@@ -569,7 +599,12 @@ void sqlite3VXPrintf(
length = 1;
break;
case etCHARX:
- c = va_arg(ap,int);
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ c = bufpt ? bufpt[0] : 0;
+ }else{
+ c = va_arg(ap,int);
+ }
buf[0] = (char)c;
if( precision>=0 ){
for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
@@ -581,10 +616,14 @@ void sqlite3VXPrintf(
break;
case etSTRING:
case etDYNSTRING:
- bufpt = va_arg(ap,char*);
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ }else{
+ bufpt = va_arg(ap,char*);
+ }
if( bufpt==0 ){
bufpt = "";
- }else if( xtype==etDYNSTRING ){
+ }else if( xtype==etDYNSTRING && !bArgList ){
zExtra = bufpt;
}
if( precision>=0 ){
@@ -600,7 +639,13 @@ void sqlite3VXPrintf(
int needQuote;
char ch;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
- char *escarg = va_arg(ap,char*);
+ char *escarg;
+
+ if( bArgList ){
+ escarg = getTextArg(pArgList);
+ }else{
+ escarg = va_arg(ap,char*);
+ }
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
k = precision;
@@ -612,7 +657,7 @@ void sqlite3VXPrintf(
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
return;
}
}else{
@@ -635,7 +680,8 @@ void sqlite3VXPrintf(
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
- if( pToken ){
+ assert( bArgList==0 );
+ if( pToken && pToken->n ){
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
@@ -645,12 +691,13 @@ void sqlite3VXPrintf(
SrcList *pSrc = va_arg(ap, SrcList*);
int k = va_arg(ap, int);
struct SrcList_item *pItem = &pSrc->a[k];
+ assert( bArgList==0 );
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase ){
- sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
sqlite3StrAccumAppend(pAccum, ".", 1);
}
- sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zName);
length = width = 0;
break;
}
@@ -664,77 +711,99 @@ void sqlite3VXPrintf(
** "length" characters long. The field width is "width". Do
** the output.
*/
- if( !flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- sqlite3AppendSpace(pAccum, nspace);
- }
- }
- if( length>0 ){
- sqlite3StrAccumAppend(pAccum, bufpt, length);
- }
- if( flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- sqlite3AppendSpace(pAccum, nspace);
- }
- }
- sqlite3_free(zExtra);
+ width -= length;
+ if( width>0 && !flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
+ if( width>0 && flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
+
+ if( zExtra ) sqlite3_free(zExtra);
}/* End for loop over the format string */
} /* End of function */
/*
-** Append N bytes of text from z to the StrAccum object.
+** Enlarge the memory allocation on a StrAccum object so that it is
+** able to accept at least N more bytes of text.
+**
+** Return the number of bytes of text that StrAccum is able to accept
+** after the attempted enlargement. The value returned might be zero.
*/
-void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
- assert( z!=0 || N==0 );
- if( p->tooBig | p->mallocFailed ){
- testcase(p->tooBig);
- testcase(p->mallocFailed);
- return;
+static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
+ char *zNew;
+ assert( p->nChar+N >= p->nAlloc ); /* Only called if really needed */
+ if( p->accError ){
+ testcase(p->accError==STRACCUM_TOOBIG);
+ testcase(p->accError==STRACCUM_NOMEM);
+ return 0;
}
- assert( p->zText!=0 || p->nChar==0 );
- if( N<0 ){
- N = sqlite3Strlen30(z);
+ if( !p->useMalloc ){
+ N = p->nAlloc - p->nChar - 1;
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return N;
+ }else{
+ char *zOld = (p->zText==p->zBase ? 0 : p->zText);
+ i64 szNew = p->nChar;
+ szNew += N + 1;
+ if( szNew > p->mxAlloc ){
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return 0;
+ }else{
+ p->nAlloc = (int)szNew;
+ }
+ if( p->useMalloc==1 ){
+ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
+ }else{
+ zNew = sqlite3_realloc(zOld, p->nAlloc);
+ }
+ if( zNew ){
+ assert( p->zText!=0 || p->nChar==0 );
+ if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
+ p->zText = zNew;
+ }else{
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_NOMEM);
+ return 0;
+ }
}
- if( N==0 || NEVER(z==0) ){
- return;
+ return N;
+}
+
+/*
+** Append N space characters to the given string buffer.
+*/
+void sqlite3AppendSpace(StrAccum *p, int N){
+ if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return;
+ while( (N--)>0 ) p->zText[p->nChar++] = ' ';
+}
+
+/*
+** The StrAccum "p" is not large enough to accept N new bytes of z[].
+** So enlarge if first, then do the append.
+**
+** This is a helper routine to sqlite3StrAccumAppend() that does special-case
+** work (enlarging the buffer) using tail recursion, so that the
+** sqlite3StrAccumAppend() routine can use fast calling semantics.
+*/
+static void enlargeAndAppend(StrAccum *p, const char *z, int N){
+ N = sqlite3StrAccumEnlarge(p, N);
+ if( N>0 ){
+ memcpy(&p->zText[p->nChar], z, N);
+ p->nChar += N;
}
+}
+
+/*
+** Append N bytes of text from z to the StrAccum object. Increase the
+** size of the memory allocation for StrAccum if necessary.
+*/
+void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
+ assert( z!=0 );
+ assert( p->zText!=0 || p->nChar==0 || p->accError );
+ assert( N>=0 );
+ assert( p->accError==0 || p->nAlloc==0 );
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{
- char *zOld = (p->zText==p->zBase ? 0 : p->zText);
- i64 szNew = p->nChar;
- szNew += N + 1;
- if( szNew > p->mxAlloc ){
- sqlite3StrAccumReset(p);
- p->tooBig = 1;
- return;
- }else{
- p->nAlloc = (int)szNew;
- }
- if( p->useMalloc==1 ){
- zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
- }else{
- zNew = sqlite3_realloc(zOld, p->nAlloc);
- }
- if( zNew ){
- if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
- p->zText = zNew;
- }else{
- p->mallocFailed = 1;
- sqlite3StrAccumReset(p);
- return;
- }
- }
+ enlargeAndAppend(p,z,N);
+ return;
}
assert( p->zText );
memcpy(&p->zText[p->nChar], z, N);
@@ -742,6 +811,14 @@ void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
}
/*
+** Append the complete text of zero-terminated string z[] to the p string.
+*/
+void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
+ sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
+}
+
+
+/*
** 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.
@@ -758,7 +835,7 @@ char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
}else{
- p->mallocFailed = 1;
+ setStrAccumError(p, STRACCUM_NOMEM);
}
}
}
@@ -789,8 +866,7 @@ void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){
p->nAlloc = n;
p->mxAlloc = mx;
p->useMalloc = 1;
- p->tooBig = 0;
- p->mallocFailed = 0;
+ p->accError = 0;
}
/*
@@ -805,9 +881,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
acc.db = db;
- sqlite3VXPrintf(&acc, 1, zFormat, ap);
+ sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
- if( acc.mallocFailed ){
+ if( acc.accError==STRACCUM_NOMEM ){
db->mallocFailed = 1;
}
return z;
@@ -961,14 +1037,12 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
}
#endif
-#ifndef SQLITE_OMIT_TRACE
/*
** variable-argument wrapper around sqlite3VXPrintf().
*/
-void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
+void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
- sqlite3VXPrintf(p, 1, zFormat, ap);
+ sqlite3VXPrintf(p, bFlags, zFormat, ap);
va_end(ap);
}
-#endif
diff --git a/src/random.c b/src/random.c
index 234ebdf..b825665 100644
--- a/src/random.c
+++ b/src/random.c
@@ -28,24 +28,11 @@ static SQLITE_WSD struct sqlite3PrngType {
} sqlite3Prng;
/*
-** Get a single 8-bit random value from the RC4 PRNG. The Mutex
-** must be held while executing this routine.
-**
-** Why not just use a library random generator like lrand48() for this?
-** Because the OP_NewRowid opcode in the VDBE depends on having a very
-** good source of random numbers. The lrand48() library function may
-** well be good enough. But maybe not. Or maybe lrand48() has some
-** subtle problems on some systems that could cause problems. It is hard
-** to know. To minimize the risk of problems due to bad lrand48()
-** implementations, SQLite uses this random number generator based
-** on RC4, which we know works very well.
-**
-** (Later): Actually, OP_NewRowid does not depend on a good source of
-** randomness any more. But we will leave this code in all the same.
+** Return N random bytes.
*/
-static u8 randomByte(void){
+void sqlite3_randomness(int N, void *pBuf){
unsigned char t;
-
+ unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
** state vector. If writable static data is unsupported on the target,
@@ -60,6 +47,16 @@ static u8 randomByte(void){
# define wsdPrng sqlite3Prng
#endif
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
+ sqlite3_mutex_enter(mutex);
+#endif
+
+ if( N<=0 ){
+ wsdPrng.isInit = 0;
+ sqlite3_mutex_leave(mutex);
+ return;
+ }
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
@@ -88,29 +85,16 @@ static u8 randomByte(void){
wsdPrng.isInit = 1;
}
- /* Generate and return single random byte
- */
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- return wsdPrng.s[t];
-}
-
-/*
-** Return N random bytes.
-*/
-void sqlite3_randomness(int N, void *pBuf){
- unsigned char *zBuf = pBuf;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
-#endif
- sqlite3_mutex_enter(mutex);
- while( N-- ){
- *(zBuf++) = randomByte();
- }
+ assert( N>0 );
+ do{
+ wsdPrng.i++;
+ t = wsdPrng.s[wsdPrng.i];
+ wsdPrng.j += t;
+ wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
+ wsdPrng.s[wsdPrng.j] = t;
+ t += wsdPrng.s[wsdPrng.i];
+ *(zBuf++) = wsdPrng.s[t];
+ }while( --N );
sqlite3_mutex_leave(mutex);
}
@@ -139,7 +123,4 @@ void sqlite3PrngRestoreState(void){
sizeof(sqlite3Prng)
);
}
-void sqlite3PrngResetState(void){
- GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
-}
#endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --git a/src/resolve.c b/src/resolve.c
index 91efcaa..935d311 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -55,7 +55,7 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){
** column reference is so that the column reference will be recognized as
** usable by indices within the WHERE clause processing logic.
**
-** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means
+** The TK_AS operator is inhibited if zType[0]=='G'. This means
** that in a GROUP BY clause, the expression is evaluated twice. Hence:
**
** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
@@ -65,8 +65,9 @@ static void incrAggFunctionDepth(Expr *pExpr, int N){
** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
**
** The result of random()%5 in the GROUP BY clause is probably different
-** from the result in the result-set. We might fix this someday. Or
-** then again, we might not...
+** from the result in the result-set. On the other hand Standard SQL does
+** not allow the GROUP BY clause to contain references to result-set columns.
+** So this should never come up in well-formed queries.
**
** If the reference is followed by a COLLATE operator, then make sure
** the COLLATE operator is preserved. For example:
@@ -106,10 +107,11 @@ static void resolveAlias(
incrAggFunctionDepth(pDup, nSubquery);
pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
if( pDup==0 ) return;
- if( pEList->a[iCol].iAlias==0 ){
- pEList->a[iCol].iAlias = (u16)(++pParse->nAlias);
+ ExprSetProperty(pDup, EP_Skip);
+ if( pEList->a[iCol].u.x.iAlias==0 ){
+ pEList->a[iCol].u.x.iAlias = (u16)(++pParse->nAlias);
}
- pDup->iTable = pEList->a[iCol].iAlias;
+ pDup->iTable = pEList->a[iCol].u.x.iAlias;
}
if( pExpr->op==TK_COLLATE ){
pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
@@ -128,7 +130,7 @@ static void resolveAlias(
if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
- pExpr->flags2 |= EP2_MallocedToken;
+ pExpr->flags |= EP_MemToken;
}
sqlite3DbFree(db, pDup);
}
@@ -224,27 +226,38 @@ static int lookupName(
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
- int isTrigger = 0;
+ int isTrigger = 0; /* True if resolved to a trigger column */
+ Table *pTab = 0; /* Table hold the row */
+ Column *pCol; /* A column of pTab */
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
/* Initialize the node to no-match */
pExpr->iTable = -1;
pExpr->pTab = 0;
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
/* Translate the schema name in zDb into a pointer to the corresponding
** schema. If not found, pSchema will remain NULL and nothing will match
** resulting in an appropriate error message toward the end of this routine
*/
if( zDb ){
- for(i=0; i<db->nDb; i++){
- assert( db->aDb[i].zName );
- if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
- pSchema = db->aDb[i].pSchema;
- break;
+ testcase( pNC->ncFlags & NC_PartIdx );
+ testcase( pNC->ncFlags & NC_IsCheck );
+ if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){
+ /* Silently ignore database qualifiers inside CHECK constraints and partial
+ ** indices. Do not raise errors because that might break legacy and
+ ** because it does not hurt anything to just ignore the database name. */
+ zDb = 0;
+ }else{
+ for(i=0; i<db->nDb; i++){
+ assert( db->aDb[i].zName );
+ if( sqlite3StrICmp(db->aDb[i].zName,zDb)==0 ){
+ pSchema = db->aDb[i].pSchema;
+ break;
+ }
}
}
}
@@ -256,9 +269,6 @@ static int lookupName(
if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
- Table *pTab;
- Column *pCol;
-
pTab = pItem->pTab;
assert( pTab!=0 && pTab->zName!=0 );
assert( pTab->nCol>0 );
@@ -318,9 +328,8 @@ static int lookupName(
/* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference
*/
- if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
+ if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
- Table *pTab = 0;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
@@ -328,14 +337,15 @@ static int lookupName(
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0;
pTab = pParse->pTriggerTab;
+ }else{
+ pTab = 0;
}
if( pTab ){
int iCol;
pSchema = pTab->pSchema;
cntTab++;
- for(iCol=0; iCol<pTab->nCol; iCol++){
- Column *pCol = &pTab->aCol[iCol];
+ for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
if( iCol==pTab->iPKey ){
iCol = -1;
@@ -343,8 +353,10 @@ static int lookupName(
break;
}
}
- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){
- iCol = -1; /* IMP: R-44911-55124 */
+ if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && HasRowid(pTab) ){
+ /* IMP: R-51414-32910 */
+ /* IMP: R-44911-55124 */
+ iCol = -1;
}
if( iCol<pTab->nCol ){
cnt++;
@@ -370,7 +382,8 @@ static int lookupName(
/*
** Perhaps the name is a reference to the ROWID
*/
- if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
+ if( cnt==0 && cntTab==1 && pMatch && sqlite3IsRowid(zCol)
+ && HasRowid(pMatch->pTab) ){
cnt = 1;
pExpr->iColumn = -1; /* IMP: R-44911-55124 */
pExpr->affinity = SQLITE_AFF_INTEGER;
@@ -387,10 +400,16 @@ static int lookupName(
** forms the result set entry ("a+b" in the example) and return immediately.
** Note that the expression in the result set should have already been
** resolved by the time the WHERE clause is resolved.
+ **
+ ** The ability to use an output result-set column in the WHERE, GROUP BY,
+ ** or HAVING clauses, or as part of a larger expression in the ORDRE BY
+ ** clause is not standard SQL. This is a (goofy) SQLite extension, that
+ ** is supported for backwards compatibility only. TO DO: Issue a warning
+ ** on sqlite3_log() whenever the capability is used.
*/
if( (pEList = pNC->pEList)!=0
&& zTab==0
- && ((pNC->ncFlags & NC_AsMaybe)==0 || cnt==0)
+ && cnt==0
){
for(j=0; j<pEList->nExpr; j++){
char *zAs = pEList->a[j].zName;
@@ -523,6 +542,52 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){
}
/*
+** Report an error that an expression is not valid for a partial index WHERE
+** clause.
+*/
+static void notValidPartIdxWhere(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg /* Type of error */
+){
+ if( (pNC->ncFlags & NC_PartIdx)!=0 ){
+ sqlite3ErrorMsg(pParse, "%s prohibited in partial index WHERE clauses",
+ zMsg);
+ }
+}
+
+#ifndef SQLITE_OMIT_CHECK
+/*
+** Report an error that an expression is not valid for a CHECK constraint.
+*/
+static void notValidCheckConstraint(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg /* Type of error */
+){
+ if( (pNC->ncFlags & NC_IsCheck)!=0 ){
+ sqlite3ErrorMsg(pParse,"%s prohibited in CHECK constraints", zMsg);
+ }
+}
+#else
+# define notValidCheckConstraint(P,N,M)
+#endif
+
+/*
+** Expression p should encode a floating point value between 1.0 and 0.0.
+** Return 1024 times this value. Or return -1 if p is not a floating point
+** value between 1.0 and 0.0.
+*/
+static int exprProbability(Expr *p){
+ double r = -1.0;
+ if( p->op!=TK_FLOAT ) return -1;
+ sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
+ assert( r>=0.0 );
+ if( r>1.0 ) return -1;
+ return (int)(r*1000.0);
+}
+
+/*
** This routine is callback for sqlite3WalkExpr().
**
** Resolve symbolic names into TK_COLUMN operators for the current
@@ -542,7 +607,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pParse = pNC->pParse;
assert( pParse==pWalker->pParse );
- if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune;
+ if( ExprHasProperty(pExpr, EP_Resolved) ) return WRC_Prune;
ExprSetProperty(pExpr, EP_Resolved);
#ifndef NDEBUG
if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
@@ -606,7 +671,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
/* Resolve function names
*/
- case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pList = pExpr->x.pList; /* The argument list */
int n = pList ? pList->nExpr : 0; /* Number of arguments */
@@ -619,8 +683,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
- testcase( pExpr->op==TK_CONST_FUNC );
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ notValidPartIdxWhere(pParse, pNC, "functions");
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
@@ -633,6 +697,28 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
}else{
is_agg = pDef->xFunc==0;
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
+ if( n==2 ){
+ pExpr->iTable = exprProbability(pList->a[1].pExpr);
+ if( pExpr->iTable<0 ){
+ sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a "
+ "constant between 0.0 and 1.0");
+ pNC->nErr++;
+ }
+ }else{
+ /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to
+ ** likelihood(X, 0.0625).
+ ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for
+ ** likelihood(X,0.0625).
+ ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand for
+ ** likelihood(X,0.9375).
+ ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent to
+ ** likelihood(X,0.9375). */
+ /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
+ pExpr->iTable = pDef->zName[0]=='u' ? 62 : 938;
+ }
+ }
}
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pDef ){
@@ -646,6 +732,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->op = TK_NULL;
return WRC_Prune;
}
+ if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant);
}
#endif
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
@@ -686,11 +773,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
-#ifndef SQLITE_OMIT_CHECK
- if( (pNC->ncFlags & NC_IsCheck)!=0 ){
- sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
- }
-#endif
+ notValidCheckConstraint(pParse, pNC, "subqueries");
+ notValidPartIdxWhere(pParse, pNC, "subqueries");
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
@@ -699,14 +783,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
break;
}
-#ifndef SQLITE_OMIT_CHECK
case TK_VARIABLE: {
- if( (pNC->ncFlags & NC_IsCheck)!=0 ){
- sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints");
- }
+ notValidCheckConstraint(pParse, pNC, "parameters");
+ notValidPartIdxWhere(pParse, pNC, "parameters");
break;
}
-#endif
}
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
@@ -797,7 +878,7 @@ static int resolveOrderByTermToExprList(
** result-set entry.
*/
for(i=0; i<pEList->nExpr; i++){
- if( sqlite3ExprCompare(pEList->a[i].pExpr, pE)<2 ){
+ if( sqlite3ExprCompare(pEList->a[i].pExpr, pE, -1)<2 ){
return i+1;
}
}
@@ -903,7 +984,7 @@ static int resolveCompoundOrderBy(
pItem->pExpr->pLeft = pNew;
}
sqlite3ExprDelete(db, pE);
- pItem->iOrderByCol = (u16)iCol;
+ pItem->u.x.iOrderByCol = (u16)iCol;
pItem->done = 1;
}else{
moreToDo = 1;
@@ -924,8 +1005,8 @@ static int resolveCompoundOrderBy(
/*
** Check every term in the ORDER BY or GROUP BY clause pOrderBy of
** the SELECT statement pSelect. If any term is reference to a
-** result set expression (as determined by the ExprList.a.iCol field)
-** then convert that term into a copy of the corresponding result set
+** result set expression (as determined by the ExprList.a.u.x.iOrderByCol
+** field) then convert that term into a copy of the corresponding result set
** column.
**
** If any errors are detected, add an error message to pParse and
@@ -952,12 +1033,12 @@ int sqlite3ResolveOrderGroupBy(
pEList = pSelect->pEList;
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
- if( pItem->iOrderByCol ){
- if( pItem->iOrderByCol>pEList->nExpr ){
+ if( pItem->u.x.iOrderByCol ){
+ if( pItem->u.x.iOrderByCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0);
+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0);
}
}
return 0;
@@ -972,7 +1053,7 @@ int sqlite3ResolveOrderGroupBy(
** If the order-by term is an integer I between 1 and N (where N is the
** number of columns in the result set of the SELECT) then the expression
** in the resolution is a copy of the I-th result-set expression. If
-** the order-by term is an identify that corresponds to the AS-name of
+** the order-by term is an identifier that corresponds to the AS-name of
** a result-set expression, then the term resolves to a copy of the
** result-set expression. Otherwise, the expression is resolved in
** the usual way - using sqlite3ResolveExprNames().
@@ -998,16 +1079,19 @@ static int resolveOrderGroupBy(
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- iCol = resolveAsName(pParse, pSelect->pEList, pE);
- if( iCol>0 ){
- /* If an AS-name match is found, mark this ORDER BY column as being
- ** a copy of the iCol-th result-set column. The subsequent call to
- ** sqlite3ResolveOrderGroupBy() will convert the expression to a
- ** copy of the iCol-th result-set expression. */
- pItem->iOrderByCol = (u16)iCol;
- continue;
+ Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ if( zType[0]!='G' ){
+ iCol = resolveAsName(pParse, pSelect->pEList, pE2);
+ if( iCol>0 ){
+ /* If an AS-name match is found, mark this ORDER BY column as being
+ ** a copy of the iCol-th result-set column. The subsequent call to
+ ** sqlite3ResolveOrderGroupBy() will convert the expression to a
+ ** copy of the iCol-th result-set expression. */
+ pItem->u.x.iOrderByCol = (u16)iCol;
+ continue;
+ }
}
- if( sqlite3ExprIsInteger(sqlite3ExprSkipCollate(pE), &iCol) ){
+ if( sqlite3ExprIsInteger(pE2, &iCol) ){
/* The ORDER BY term is an integer constant. Again, set the column
** number so that sqlite3ResolveOrderGroupBy() will convert the
** order-by term to a copy of the result-set expression */
@@ -1015,18 +1099,18 @@ static int resolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, nResult);
return 1;
}
- pItem->iOrderByCol = (u16)iCol;
+ pItem->u.x.iOrderByCol = (u16)iCol;
continue;
}
/* Otherwise, treat the ORDER BY term as an ordinary expression */
- pItem->iOrderByCol = 0;
+ pItem->u.x.iOrderByCol = 0;
if( sqlite3ResolveExprNames(pNC, pE) ){
return 1;
}
for(j=0; j<pSelect->pEList->nExpr; j++){
- if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr)==0 ){
- pItem->iOrderByCol = j+1;
+ if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
+ pItem->u.x.iOrderByCol = j+1;
}
}
}
@@ -1150,7 +1234,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
return WRC_Abort;
}
- /* Add the expression list to the name-context before parsing the
+ /* Add the output column list to the name-context before parsing the
** other expressions in the SELECT statement. This is so that
** expressions in the WHERE clause (etc.) can refer to expressions by
** aliases in the result set.
@@ -1159,10 +1243,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** re-evaluated for each reference to it.
*/
sNC.pEList = p->pEList;
- sNC.ncFlags |= NC_AsMaybe;
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
- sNC.ncFlags &= ~NC_AsMaybe;
/* The ORDER BY and GROUP BY clauses may not refer to terms in
** outer queries
@@ -1331,3 +1413,45 @@ void sqlite3ResolveSelectNames(
w.u.pNC = pOuterNC;
sqlite3WalkSelect(&w, p);
}
+
+/*
+** Resolve names in expressions that can only reference a single table:
+**
+** * CHECK constraints
+** * WHERE clauses on partial indices
+**
+** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
+** is set to -1 and the Expr.iColumn value is set to the column number.
+**
+** Any errors cause an error message to be set in pParse.
+*/
+void sqlite3ResolveSelfReference(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* The table being referenced */
+ int type, /* NC_IsCheck or NC_PartIdx */
+ Expr *pExpr, /* Expression to resolve. May be NULL. */
+ ExprList *pList /* Expression list to resolve. May be NUL. */
+){
+ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
+ NameContext sNC; /* Name context for pParse->pNewTable */
+ int i; /* Loop counter */
+
+ assert( type==NC_IsCheck || type==NC_PartIdx );
+ memset(&sNC, 0, sizeof(sNC));
+ memset(&sSrc, 0, sizeof(sSrc));
+ sSrc.nSrc = 1;
+ sSrc.a[0].zName = pTab->zName;
+ sSrc.a[0].pTab = pTab;
+ sSrc.a[0].iCursor = -1;
+ sNC.pParse = pParse;
+ sNC.pSrcList = &sSrc;
+ sNC.ncFlags = type;
+ if( sqlite3ResolveExprNames(&sNC, pExpr) ) return;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
+ return;
+ }
+ }
+ }
+}
diff --git a/src/rowset.c b/src/rowset.c
index 5761f98..ba2e056 100644
--- a/src/rowset.c
+++ b/src/rowset.c
@@ -112,8 +112,8 @@ struct RowSet {
struct RowSetEntry *pFresh; /* Source of new entry objects */
struct RowSetEntry *pForest; /* List of binary trees of entries */
u16 nFresh; /* Number of objects on pFresh */
- u8 rsFlags; /* Various flags */
- u8 iBatch; /* Current insert batch */
+ u16 rsFlags; /* Various flags */
+ int iBatch; /* Current insert batch */
};
/*
@@ -447,7 +447,7 @@ int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
** on pRowSet->pEntry, then sort those entires into the forest at
** pRowSet->pForest so that they can be tested.
*/
-int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
+int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
struct RowSetEntry *p, *pTree;
/* This routine is never called after sqlite3RowSetNext() */
diff --git a/src/select.c b/src/select.c
index f3f1490..932874d 100644
--- a/src/select.c
+++ b/src/select.c
@@ -14,6 +14,34 @@
*/
#include "sqliteInt.h"
+/*
+** An instance of the following object is used to record information about
+** how to process the DISTINCT keyword, to simplify passing that information
+** into the selectInnerLoop() routine.
+*/
+typedef struct DistinctCtx DistinctCtx;
+struct DistinctCtx {
+ u8 isTnct; /* True if the DISTINCT keyword is present */
+ u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
+ int tabTnct; /* Ephemeral table used for DISTINCT processing */
+ int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
+};
+
+/*
+** An instance of the following object is used to record information about
+** the ORDER BY (or GROUP BY) clause of query is being coded.
+*/
+typedef struct SortCtx SortCtx;
+struct SortCtx {
+ ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
+ int nOBSat; /* Number of ORDER BY terms satisfied by indices */
+ int iECursor; /* Cursor number for the sorter */
+ int regReturn; /* Register holding block-output return address */
+ int labelBkOut; /* Start label for the block-output subroutine */
+ int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
+ u8 sortFlags; /* Zero or more SORTFLAG_* bits */
+};
+#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
/*
** Delete all the content of a Select structure but do not deallocate
@@ -29,6 +57,7 @@ static void clearSelect(sqlite3 *db, Select *p){
sqlite3SelectDelete(db, p->pPrior);
sqlite3ExprDelete(db, p->pLimit);
sqlite3ExprDelete(db, p->pOffset);
+ sqlite3WithDelete(db, p->pWith);
}
/*
@@ -86,7 +115,6 @@ Select *sqlite3SelectNew(
assert( pOffset==0 || pLimit!=0 );
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
- pNew->addrOpenEphm[2] = -1;
if( db->mallocFailed ) {
clearSelect(db, pNew);
if( pNew!=&standin ) sqlite3DbFree(db, pNew);
@@ -109,7 +137,15 @@ void sqlite3SelectDelete(sqlite3 *db, Select *p){
}
/*
-** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
+** Return a pointer to the right-most SELECT statement in a compound.
+*/
+static Select *findRightmost(Select *p){
+ while( p->pNext ) p = p->pNext;
+ return p;
+}
+
+/*
+** Given 1 to 3 identifiers preceding the JOIN keyword, determine the
** type of join. Return an integer constant that expresses that type
** in terms of the following bit values:
**
@@ -264,8 +300,8 @@ static void addWhereTerm(
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0);
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
- assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pEq);
+ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->iRightJoinTable = (i16)pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq);
@@ -300,8 +336,8 @@ static void addWhereTerm(
static void setJoinExpr(Expr *p, int iTable){
while( p ){
ExprSetProperty(p, EP_FromJoin);
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(p);
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(p, EP_NoReduce);
p->iRightJoinTable = (i16)iTable;
setJoinExpr(p->pLeft, iTable);
p = p->pRight;
@@ -410,34 +446,73 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
return 0;
}
+/* Forward reference */
+static KeyInfo *keyInfoFromExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* Form the KeyInfo object from this ExprList */
+ int iStart, /* Begin with this column of pList */
+ int nExtra /* Add this many extra columns to the end */
+);
+
/*
-** Insert code into "v" that will push the record on the top of the
-** stack into the sorter.
+** Insert code into "v" that will push the record in register regData
+** into the sorter.
*/
static void pushOntoSorter(
Parse *pParse, /* Parser context */
- ExprList *pOrderBy, /* The ORDER BY clause */
+ SortCtx *pSort, /* Information about the ORDER BY clause */
Select *pSelect, /* The whole SELECT statement */
int regData /* Register holding data to be sorted */
){
Vdbe *v = pParse->pVdbe;
- int nExpr = pOrderBy->nExpr;
- int regBase = sqlite3GetTempRange(pParse, nExpr+2);
- int regRecord = sqlite3GetTempReg(pParse);
+ int nExpr = pSort->pOrderBy->nExpr;
+ int regRecord = ++pParse->nMem;
+ int regBase = pParse->nMem+1;
+ int nOBSat = pSort->nOBSat;
int op;
+
+ pParse->nMem += nExpr+2; /* nExpr+2 registers allocated at regBase */
sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
- sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
+ sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, 0);
+ sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
- if( pSelect->selFlags & SF_UseSorter ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nExpr+2-nOBSat,regRecord);
+ if( nOBSat>0 ){
+ int regPrevKey; /* The first nOBSat columns of the previous row */
+ int addrFirst; /* Address of the OP_IfNot opcode */
+ int addrJmp; /* Address of the OP_Jump opcode */
+ VdbeOp *pOp; /* Opcode that opens the sorter */
+ int nKey; /* Number of sorting key columns, including OP_Sequence */
+ KeyInfo *pKI; /* Original KeyInfo on the sorter table */
+
+ regPrevKey = pParse->nMem+1;
+ pParse->nMem += pSort->nOBSat;
+ nKey = nExpr - pSort->nOBSat + 1;
+ addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
+ pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
+ if( pParse->db->mallocFailed ) return;
+ pOp->p2 = nKey + 1;
+ pKI = pOp->p4.pKeyInfo;
+ memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
+ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
+ pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1);
+ addrJmp = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
+ pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
+ pSort->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
+ sqlite3VdbeJumpHere(v, addrFirst);
+ sqlite3VdbeAddOp3(v, OP_Move, regBase, regPrevKey, pSort->nOBSat);
+ sqlite3VdbeJumpHere(v, addrJmp);
+ }
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
op = OP_SorterInsert;
}else{
op = OP_IdxInsert;
}
- sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
- sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
+ sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
if( pSelect->iLimit ){
int addr1, addr2;
int iLimit;
@@ -446,12 +521,12 @@ static void pushOntoSorter(
}else{
iLimit = pSelect->iLimit;
}
- addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit);
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1);
addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
- sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
+ sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
+ sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
sqlite3VdbeJumpHere(v, addr2);
}
}
@@ -461,13 +536,12 @@ static void pushOntoSorter(
*/
static void codeOffset(
Vdbe *v, /* Generate code into this VM */
- Select *p, /* The SELECT statement being coded */
+ int iOffset, /* Register holding the offset counter */
int iContinue /* Jump here to skip the current record */
){
- if( p->iOffset && iContinue!=0 ){
+ if( iOffset>0 ){
int addr;
- sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1);
- addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset);
+ addr = sqlite3VdbeAddOp3(v, OP_IfNeg, iOffset, 0, -1); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
VdbeComment((v, "skip OFFSET records"));
sqlite3VdbeJumpHere(v, addr);
@@ -495,7 +569,7 @@ static void codeDistinct(
v = pParse->pVdbe;
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
sqlite3ReleaseTempReg(pParse, r1);
@@ -526,34 +600,20 @@ static int checkForMultiColumnSelectError(
#endif
/*
-** An instance of the following object is used to record information about
-** how to process the DISTINCT keyword, to simplify passing that information
-** into the selectInnerLoop() routine.
-*/
-typedef struct DistinctCtx DistinctCtx;
-struct DistinctCtx {
- u8 isTnct; /* True if the DISTINCT keyword is present */
- u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
- int tabTnct; /* Ephemeral table used for DISTINCT processing */
- int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
-};
-
-/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
**
-** If srcTab and nColumn are both zero, then the pEList expressions
-** are evaluated in order to get the data for this row. If nColumn>0
-** then data is pulled from srcTab and pEList is used only to get the
-** datatypes for each column.
+** If srcTab is negative, then the pEList expressions
+** are evaluated in order to get the data for this row. If srcTab is
+** zero or more, then data is pulled from srcTab and pEList is used only
+** to get number columns and the datatype for each column.
*/
static void selectInnerLoop(
Parse *pParse, /* The parser context */
Select *p, /* The complete select statement being coded */
ExprList *pEList, /* List of values being extracted */
int srcTab, /* Pull data from this table */
- int nColumn, /* Number of columns in the source table */
- ExprList *pOrderBy, /* If not NULL, sort results using this key */
+ SortCtx *pSort, /* If not NULL, info on how to process ORDER BY */
DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */
SelectDest *pDest, /* How to dispose of the results */
int iContinue, /* Jump here to continue with next row */
@@ -568,48 +628,49 @@ static void selectInnerLoop(
int nResultCol; /* Number of result columns */
assert( v );
- if( NEVER(v==0) ) return;
assert( pEList!=0 );
hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP;
- if( pOrderBy==0 && !hasDistinct ){
- codeOffset(v, p, iContinue);
+ if( pSort && pSort->pOrderBy==0 ) pSort = 0;
+ if( pSort==0 && !hasDistinct ){
+ assert( iContinue!=0 );
+ codeOffset(v, p->iOffset, iContinue);
}
/* Pull the requested columns.
*/
- if( nColumn>0 ){
- nResultCol = nColumn;
- }else{
- nResultCol = pEList->nExpr;
- }
+ nResultCol = pEList->nExpr;
+
if( pDest->iSdst==0 ){
pDest->iSdst = pParse->nMem+1;
- pDest->nSdst = nResultCol;
pParse->nMem += nResultCol;
- }else{
- assert( pDest->nSdst==nResultCol );
+ }else if( pDest->iSdst+nResultCol > pParse->nMem ){
+ /* This is an error condition that can result, for example, when a SELECT
+ ** on the right-hand side of an INSERT contains more result columns than
+ ** there are columns in the table on the left. The error will be caught
+ ** and reported later. But we need to make sure enough memory is allocated
+ ** to avoid other spurious errors in the meantime. */
+ pParse->nMem += nResultCol;
}
+ pDest->nSdst = nResultCol;
regResult = pDest->iSdst;
- if( nColumn>0 ){
- for(i=0; i<nColumn; i++){
+ if( srcTab>=0 ){
+ for(i=0; i<nResultCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
+ VdbeComment((v, "%s", pEList->a[i].zName));
}
}else if( eDest!=SRT_Exists ){
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
- sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output);
+ sqlite3ExprCodeExprList(pParse, pEList, regResult,
+ (eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0);
}
- 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
** part of the result.
*/
if( hasDistinct ){
- assert( pEList!=0 );
- assert( pEList->nExpr==nColumn );
switch( pDistinct->eTnctType ){
case WHERE_DISTINCT_ORDERED: {
VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
@@ -618,7 +679,7 @@ static void selectInnerLoop(
/* Allocate space for the previous row */
regPrev = pParse->nMem+1;
- pParse->nMem += nColumn;
+ pParse->nMem += nResultCol;
/* Change the OP_OpenEphemeral coded earlier to an OP_Null
** sets the MEM_Cleared bit on the first register of the
@@ -632,19 +693,21 @@ static void selectInnerLoop(
pOp->p1 = 1;
pOp->p2 = regPrev;
- iJump = sqlite3VdbeCurrentAddr(v) + nColumn;
- for(i=0; i<nColumn; i++){
+ iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
+ for(i=0; i<nResultCol; i++){
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr);
- if( i<nColumn-1 ){
+ if( i<nResultCol-1 ){
sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i);
+ VdbeCoverage(v);
}else{
sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i);
- }
+ VdbeCoverage(v);
+ }
sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
}
- assert( sqlite3VdbeCurrentAddr(v)==iJump );
- sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nColumn-1);
+ assert( sqlite3VdbeCurrentAddr(v)==iJump || pParse->db->mallocFailed );
+ sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1);
break;
}
@@ -655,12 +718,12 @@ static void selectInnerLoop(
default: {
assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED );
- codeDistinct(pParse, pDistinct->tabTnct, iContinue, nColumn, regResult);
+ codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol, regResult);
break;
}
}
- if( pOrderBy==0 ){
- codeOffset(v, p, iContinue);
+ if( pSort==0 ){
+ codeOffset(v, p->iOffset, iContinue);
}
}
@@ -672,7 +735,7 @@ static void selectInnerLoop(
case SRT_Union: {
int r1;
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
break;
@@ -683,21 +746,36 @@ static void selectInnerLoop(
** the temporary table iParm.
*/
case SRT_Except: {
- sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol);
break;
}
-#endif
+#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/* Store the result as data using a unique key.
*/
+ case SRT_Fifo:
+ case SRT_DistFifo:
case SRT_Table:
case SRT_EphemTab: {
int r1 = sqlite3GetTempReg(pParse);
testcase( eDest==SRT_Table );
testcase( eDest==SRT_EphemTab );
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
- if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p, r1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
+#ifndef SQLITE_OMIT_CTE
+ if( eDest==SRT_DistFifo ){
+ /* If the destination is DistFifo, then cursor (iParm+1) is open
+ ** on an ephemeral index. If the current row is already present
+ ** in the index, do not write it to the output. If not, add the
+ ** current row to the index and proceed with writing it to the
+ ** output table as well. */
+ int addr = sqlite3VdbeCurrentAddr(v) + 4;
+ sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
+ assert( pSort==0 );
+ }
+#endif
+ if( pSort ){
+ pushOntoSorter(pParse, pSort, p, r1);
}else{
int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@@ -715,15 +793,15 @@ static void selectInnerLoop(
** item into the set table with bogus data.
*/
case SRT_Set: {
- assert( nColumn==1 );
+ assert( nResultCol==1 );
pDest->affSdst =
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
- if( pOrderBy ){
+ if( pSort ){
/* 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, regResult);
+ pushOntoSorter(pParse, pSort, p, regResult);
}else{
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
@@ -747,9 +825,9 @@ static void selectInnerLoop(
** of the scan loop.
*/
case SRT_Mem: {
- assert( nColumn==1 );
- if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p, regResult);
+ assert( nResultCol==1 );
+ if( pSort ){
+ pushOntoSorter(pParse, pSort, p, regResult);
}else{
sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
/* The LIMIT clause will jump out of the loop for us */
@@ -758,27 +836,72 @@ static void selectInnerLoop(
}
#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:
- case SRT_Output: {
+ case SRT_Coroutine: /* Send data to a co-routine */
+ case SRT_Output: { /* Return the results */
testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
- if( pOrderBy ){
+ if( pSort ){
int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
- pushOntoSorter(pParse, pOrderBy, p, r1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
+ pushOntoSorter(pParse, pSort, p, r1);
sqlite3ReleaseTempReg(pParse, r1);
}else if( eDest==SRT_Coroutine ){
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{
- sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn);
- sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
+ }
+ break;
+ }
+
+#ifndef SQLITE_OMIT_CTE
+ /* Write the results into a priority queue that is order according to
+ ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an
+ ** index with pSO->nExpr+2 columns. Build a key using pSO for the first
+ ** pSO->nExpr columns, then make sure all keys are unique by adding a
+ ** final OP_Sequence column. The last column is the record as a blob.
+ */
+ case SRT_DistQueue:
+ case SRT_Queue: {
+ int nKey;
+ int r1, r2, r3;
+ int addrTest = 0;
+ ExprList *pSO;
+ pSO = pDest->pOrderBy;
+ assert( pSO );
+ nKey = pSO->nExpr;
+ r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3GetTempRange(pParse, nKey+2);
+ r3 = r2+nKey+1;
+ if( eDest==SRT_DistQueue ){
+ /* If the destination is DistQueue, then cursor (iParm+1) is open
+ ** on a second ephemeral index that holds all values every previously
+ ** added to the queue. */
+ addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0,
+ regResult, nResultCol);
+ VdbeCoverage(v);
}
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3);
+ if( eDest==SRT_DistQueue ){
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ }
+ for(i=0; i<nKey; i++){
+ sqlite3VdbeAddOp2(v, OP_SCopy,
+ regResult + pSO->a[i].u.x.iOrderByCol - 1,
+ r2+i);
+ }
+ sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ if( addrTest ) sqlite3VdbeJumpHere(v, addrTest);
+ sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempRange(pParse, r2, nKey+2);
break;
}
+#endif /* SQLITE_OMIT_CTE */
+
+
#if !defined(SQLITE_OMIT_TRIGGER)
/* Discard the results. This is used for SELECT statements inside
@@ -797,12 +920,64 @@ static void selectInnerLoop(
** there is a sorter, in which case the sorter has already limited
** the output for us.
*/
- if( pOrderBy==0 && p->iLimit ){
- sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
+ if( pSort==0 && p->iLimit ){
+ sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v);
+ }
+}
+
+/*
+** Allocate a KeyInfo object sufficient for an index of N key columns and
+** X extra columns.
+*/
+KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
+ KeyInfo *p = sqlite3DbMallocZero(0,
+ sizeof(KeyInfo) + (N+X)*(sizeof(CollSeq*)+1));
+ if( p ){
+ p->aSortOrder = (u8*)&p->aColl[N+X];
+ p->nField = (u16)N;
+ p->nXField = (u16)X;
+ p->enc = ENC(db);
+ p->db = db;
+ p->nRef = 1;
+ }else{
+ db->mallocFailed = 1;
+ }
+ return p;
+}
+
+/*
+** Deallocate a KeyInfo object
+*/
+void sqlite3KeyInfoUnref(KeyInfo *p){
+ if( p ){
+ assert( p->nRef>0 );
+ p->nRef--;
+ if( p->nRef==0 ) sqlite3DbFree(0, p);
}
}
/*
+** Make a new pointer to a KeyInfo object
+*/
+KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){
+ if( p ){
+ assert( p->nRef>0 );
+ p->nRef++;
+ }
+ return p;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Return TRUE if a KeyInfo object can be change. The KeyInfo object
+** can only be changed if this is just a single reference to the object.
+**
+** This routine is used only inside of assert() statements.
+*/
+int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
+#endif /* SQLITE_DEBUG */
+
+/*
** Given an expression list, generate a KeyInfo structure that records
** the collating sequence for each expression in that expression list.
**
@@ -814,31 +989,30 @@ static void 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 P4 field of an opcode using
-** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
+** freed.
*/
-static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
- sqlite3 *db = pParse->db;
+static KeyInfo *keyInfoFromExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* Form the KeyInfo object from this ExprList */
+ int iStart, /* Begin with this column of pList */
+ int nExtra /* Add this many extra columns to the end */
+){
int nExpr;
KeyInfo *pInfo;
struct ExprList_item *pItem;
+ sqlite3 *db = pParse->db;
int i;
nExpr = pList->nExpr;
- pInfo = sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) );
+ pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1);
if( pInfo ){
- pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr];
- pInfo->nField = (u16)nExpr;
- pInfo->enc = ENC(db);
- pInfo->db = db;
- for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
+ assert( sqlite3KeyInfoIsWriteable(pInfo) );
+ for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
CollSeq *pColl;
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- if( !pColl ){
- pColl = db->pDfltColl;
- }
- pInfo->aColl[i] = pColl;
- pInfo->aSortOrder[i] = pItem->sortOrder;
+ if( !pColl ) pColl = db->pDfltColl;
+ pInfo->aColl[i-iStart] = pColl;
+ pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
}
}
return pInfo;
@@ -940,24 +1114,31 @@ static void explainComposite(
static void generateSortTail(
Parse *pParse, /* Parsing context */
Select *p, /* The SELECT statement */
- Vdbe *v, /* Generate code into this VDBE */
+ SortCtx *pSort, /* Information on the ORDER BY clause */
int nColumn, /* Number of columns of data */
SelectDest *pDest /* Write the sorted results here */
){
+ Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
int addr;
+ int addrOnce = 0;
int iTab;
int pseudoTab = 0;
- ExprList *pOrderBy = p->pOrderBy;
-
+ ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
int iParm = pDest->iSDParm;
-
int regRow;
int regRowid;
+ int nKey;
- iTab = pOrderBy->iECursor;
+ if( pSort->labelBkOut ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
+ sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
+ addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ }
+ iTab = pSort->iECursor;
regRow = sqlite3GetTempReg(pParse);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++;
@@ -966,19 +1147,23 @@ static void generateSortTail(
}else{
regRowid = sqlite3GetTempReg(pParse);
}
- if( p->selFlags & SF_UseSorter ){
+ nKey = pOrderBy->nExpr - pSort->nOBSat;
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
int ptab2 = pParse->nTab++;
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, nKey+2);
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
+ VdbeCoverage(v);
+ codeOffset(v, p->iOffset, addrContinue);
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
- sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
+ sqlite3VdbeAddOp3(v, OP_Column, ptab2, nKey+1, regRow);
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
- addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
- sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
+ codeOffset(v, p->iOffset, addrContinue);
+ sqlite3VdbeAddOp3(v, OP_Column, iTab, nKey+1, regRow);
}
switch( eDest ){
case SRT_Table:
@@ -1033,21 +1218,22 @@ static void generateSortTail(
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
- if( p->selFlags & SF_UseSorter ){
- sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr);
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); VdbeCoverage(v);
}else{
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
}
+ if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak);
- if( eDest==SRT_Output || eDest==SRT_Coroutine ){
- sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
- }
}
/*
** Return a pointer to a string containing the 'declaration type' of the
** expression pExpr. The string may be treated as static by the caller.
**
+** Also try to estimate the size of the returned value and return that
+** result in *pEstWidth.
+**
** The declaration type is the exact datatype definition extracted from the
** original CREATE TABLE statement if the expression is a column. The
** declaration type for a ROWID field is INTEGER. Exactly when an expression
@@ -1061,21 +1247,36 @@ static void generateSortTail(
** SELECT abc FROM (SELECT col AS abc FROM tbl);
**
** The declaration type for any expression other than a column is NULL.
+**
+** This routine has either 3 or 6 parameters depending on whether or not
+** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used.
*/
-static const char *columnType(
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
+static const char *columnTypeImpl(
NameContext *pNC,
Expr *pExpr,
- const char **pzOriginDb,
- const char **pzOriginTab,
- const char **pzOriginCol
+ const char **pzOrigDb,
+ const char **pzOrigTab,
+ const char **pzOrigCol,
+ u8 *pEstWidth
){
+ char const *zOrigDb = 0;
+ char const *zOrigTab = 0;
+ char const *zOrigCol = 0;
+#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
+static const char *columnTypeImpl(
+ NameContext *pNC,
+ Expr *pExpr,
+ u8 *pEstWidth
+){
+#endif /* !defined(SQLITE_ENABLE_COLUMN_METADATA) */
char const *zType = 0;
- char const *zOriginDb = 0;
- char const *zOriginTab = 0;
- char const *zOriginCol = 0;
int j;
- if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
+ u8 estWidth = 1;
+ if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
@@ -1136,25 +1337,35 @@ static const char *columnType(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth);
}
- }else if( ALWAYS(pTab->pSchema) ){
+ }else if( pTab->pSchema ){
/* A real table */
assert( !pS );
if( iCol<0 ) iCol = pTab->iPKey;
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
if( iCol<0 ){
zType = "INTEGER";
- zOriginCol = "rowid";
+ zOrigCol = "rowid";
}else{
zType = pTab->aCol[iCol].zType;
- zOriginCol = pTab->aCol[iCol].zName;
+ zOrigCol = pTab->aCol[iCol].zName;
+ estWidth = pTab->aCol[iCol].szEst;
}
- zOriginTab = pTab->zName;
+ zOrigTab = pTab->zName;
if( pNC->pParse ){
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
- zOriginDb = pNC->pParse->db->aDb[iDb].zName;
+ zOrigDb = pNC->pParse->db->aDb[iDb].zName;
}
+#else
+ if( iCol<0 ){
+ zType = "INTEGER";
+ }else{
+ zType = pTab->aCol[iCol].zType;
+ estWidth = pTab->aCol[iCol].szEst;
+ }
+#endif
}
break;
}
@@ -1171,18 +1382,21 @@ static const char *columnType(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &estWidth);
break;
}
#endif
}
-
- if( pzOriginDb ){
- assert( pzOriginTab && pzOriginCol );
- *pzOriginDb = zOriginDb;
- *pzOriginTab = zOriginTab;
- *pzOriginCol = zOriginCol;
+
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ if( pzOrigDb ){
+ assert( pzOrigTab && pzOrigCol );
+ *pzOrigDb = zOrigDb;
+ *pzOrigTab = zOrigTab;
+ *pzOrigCol = zOrigCol;
}
+#endif
+ if( pEstWidth ) *pEstWidth = estWidth;
return zType;
}
@@ -1208,7 +1422,7 @@ static void generateColumnTypes(
const char *zOrigDb = 0;
const char *zOrigTab = 0;
const char *zOrigCol = 0;
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, 0);
/* The vdbe must make its own copy of the column-type and other
** column specific strings, in case the schema is reset before this
@@ -1218,11 +1432,11 @@ static void generateColumnTypes(
sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
#else
- zType = columnType(&sNC, p, 0, 0, 0);
+ zType = columnType(&sNC, p, 0, 0, 0, 0);
#endif
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
}
-#endif /* SQLITE_OMIT_DECLTYPE */
+#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}
/*
@@ -1286,8 +1500,9 @@ static void generateColumnNames(
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT);
}
}else{
- sqlite3VdbeSetColName(v, i, COLNAME_NAME,
- sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC);
+ const char *z = pEList->a[i].zSpan;
+ z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z);
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC);
}
}
generateColumnTypes(pParse, pTabList, pEList);
@@ -1375,7 +1590,7 @@ static int selectColumnsFromExprList(
char *zNewName;
int k;
for(k=nName-1; k>1 && sqlite3Isdigit(zName[k]); k--){}
- if( zName[k]==':' ) nName = k;
+ if( k>=0 && zName[k]==':' ) nName = k;
zName[nName] = 0;
zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
sqlite3DbFree(db, zName);
@@ -1411,8 +1626,7 @@ static int selectColumnsFromExprList(
*/
static void selectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
- int nCol, /* Number of columns */
- Column *aCol, /* List of columns */
+ Table *pTab, /* Add column type information to this table */
Select *pSelect /* SELECT used to determine types and collations */
){
sqlite3 *db = pParse->db;
@@ -1422,17 +1636,19 @@ static void selectAddColumnTypeAndCollation(
int i;
Expr *p;
struct ExprList_item *a;
+ u64 szAll = 0;
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
- assert( nCol==pSelect->pEList->nExpr || db->mallocFailed );
+ assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
if( db->mallocFailed ) return;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
a = pSelect->pEList->a;
- for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
p = a[i].pExpr;
- pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
+ pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p,0,0,0, &pCol->szEst));
+ szAll += pCol->szEst;
pCol->affinity = sqlite3ExprAffinity(p);
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
pColl = sqlite3ExprCollSeq(pParse, p);
@@ -1440,6 +1656,7 @@ static void selectAddColumnTypeAndCollation(
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
}
}
+ pTab->szTabRow = sqlite3LogEst(szAll*4);
}
/*
@@ -1467,9 +1684,9 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
assert( db->lookaside.bEnabled==0 );
pTab->nRef = 1;
pTab->zName = 0;
- pTab->nRowEst = 1000000;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
+ selectAddColumnTypeAndCollation(pParse, pTab, pSelect);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -1485,12 +1702,14 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
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);
+ v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
+ if( v ) sqlite3VdbeAddOp0(v, OP_Init);
+ if( pParse->pToplevel==0
+ && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
+ ){
+ pParse->okConstFactor = 1;
}
-#endif
+
}
return v;
}
@@ -1507,8 +1726,13 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
**
** This routine changes the values of iLimit and iOffset only if
** a limit or offset is defined by pLimit and pOffset. iLimit and
-** iOffset should have been preset to appropriate default values
-** (usually but not always -1) prior to calling this routine.
+** iOffset should have been preset to appropriate default values (zero)
+** prior to calling this routine.
+**
+** The iOffset register (if it exists) is initialized to the value
+** of the OFFSET. The iLimit register is initialized to LIMIT. Register
+** iOffset+1 is initialized to LIMIT+OFFSET.
+**
** Only if pLimit!=0 or pOffset!=0 do the limit registers get
** redefined. The UNION ALL operator uses this property to force
** the reuse of the same limit and offset registers across multiple
@@ -1523,7 +1747,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
/*
** "LIMIT -1" always shows all rows. There is some
- ** contraversy about what the correct behavior should be.
+ ** controversy about what the correct behavior should be.
** The current implementation interprets "LIMIT 0" to mean
** no rows.
*/
@@ -1532,33 +1756,33 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
if( p->pLimit ){
p->iLimit = iLimit = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
- if( NEVER(v==0) ) return; /* VDBE should have already been allocated */
+ assert( v!=0 );
if( sqlite3ExprIsInteger(p->pLimit, &n) ){
sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
VdbeComment((v, "LIMIT counter"));
if( n==0 ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
- }else{
- if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n;
+ }else if( n>=0 && p->nSelectRow>(u64)n ){
+ p->nSelectRow = n;
}
}else{
sqlite3ExprCode(pParse, p->pLimit, iLimit);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v);
VdbeComment((v, "LIMIT counter"));
- sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak);
+ sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); VdbeCoverage(v);
}
if( p->pOffset ){
p->iOffset = iOffset = ++pParse->nMem;
pParse->nMem++; /* Allocate an extra register for limit+offset */
sqlite3ExprCode(pParse, p->pOffset, iOffset);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
VdbeComment((v, "OFFSET counter"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset);
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
VdbeComment((v, "LIMIT+OFFSET"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit);
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
sqlite3VdbeJumpHere(v, addr1);
}
@@ -1587,9 +1811,210 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
}
return pRet;
}
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
-/* Forward reference */
+/*
+** The select statement passed as the second parameter is a compound SELECT
+** with an ORDER BY clause. This function allocates and returns a KeyInfo
+** structure suitable for implementing the ORDER BY.
+**
+** Space to hold the KeyInfo structure is obtained from malloc. The calling
+** function is responsible for ensuring that this structure is eventually
+** freed.
+*/
+static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
+ ExprList *pOrderBy = p->pOrderBy;
+ int nOrderBy = p->pOrderBy->nExpr;
+ sqlite3 *db = pParse->db;
+ KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1);
+ if( pRet ){
+ int i;
+ for(i=0; i<nOrderBy; i++){
+ struct ExprList_item *pItem = &pOrderBy->a[i];
+ Expr *pTerm = pItem->pExpr;
+ CollSeq *pColl;
+
+ if( pTerm->flags & EP_Collate ){
+ pColl = sqlite3ExprCollSeq(pParse, pTerm);
+ }else{
+ pColl = multiSelectCollSeq(pParse, p, pItem->u.x.iOrderByCol-1);
+ if( pColl==0 ) pColl = db->pDfltColl;
+ pOrderBy->a[i].pExpr =
+ sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName);
+ }
+ assert( sqlite3KeyInfoIsWriteable(pRet) );
+ pRet->aColl[i] = pColl;
+ pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ }
+ }
+
+ return pRet;
+}
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** This routine generates VDBE code to compute the content of a WITH RECURSIVE
+** query of the form:
+**
+** <recursive-table> AS (<setup-query> UNION [ALL] <recursive-query>)
+** \___________/ \_______________/
+** p->pPrior p
+**
+**
+** There is exactly one reference to the recursive-table in the FROM clause
+** of recursive-query, marked with the SrcList->a[].isRecursive flag.
+**
+** The setup-query runs once to generate an initial set of rows that go
+** into a Queue table. Rows are extracted from the Queue table one by
+** one. Each row extracted from Queue is output to pDest. Then the single
+** extracted row (now in the iCurrent table) becomes the content of the
+** recursive-table for a recursive-query run. The output of the recursive-query
+** is added back into the Queue table. Then another row is extracted from Queue
+** and the iteration continues until the Queue table is empty.
+**
+** If the compound query operator is UNION then no duplicate rows are ever
+** inserted into the Queue table. The iDistinct table keeps a copy of all rows
+** that have ever been inserted into Queue and causes duplicates to be
+** discarded. If the operator is UNION ALL, then duplicates are allowed.
+**
+** If the query has an ORDER BY, then entries in the Queue table are kept in
+** ORDER BY order and the first entry is extracted for each cycle. Without
+** an ORDER BY, the Queue table is just a FIFO.
+**
+** If a LIMIT clause is provided, then the iteration stops after LIMIT rows
+** have been output to pDest. A LIMIT of zero means to output no rows and a
+** negative LIMIT means to output all rows. If there is also an OFFSET clause
+** with a positive value, then the first OFFSET outputs are discarded rather
+** than being sent to pDest. The LIMIT count does not begin until after OFFSET
+** rows have been skipped.
+*/
+static void generateWithRecursiveQuery(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The recursive SELECT to be coded */
+ SelectDest *pDest /* What to do with query results */
+){
+ SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */
+ int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
+ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
+ Select *pSetup = p->pPrior; /* The setup query */
+ int addrTop; /* Top of the loop */
+ int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
+ int iCurrent = 0; /* The Current table */
+ int regCurrent; /* Register holding Current table */
+ int iQueue; /* The Queue table */
+ int iDistinct = 0; /* To ensure unique results if UNION */
+ int eDest = SRT_Fifo; /* How to write to Queue */
+ SelectDest destQueue; /* SelectDest targetting the Queue table */
+ int i; /* Loop counter */
+ int rc; /* Result code */
+ ExprList *pOrderBy; /* The ORDER BY clause */
+ Expr *pLimit, *pOffset; /* Saved LIMIT and OFFSET */
+ int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */
+
+ /* Obtain authorization to do a recursive query */
+ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
+
+ /* Process the LIMIT and OFFSET clauses, if they exist */
+ addrBreak = sqlite3VdbeMakeLabel(v);
+ computeLimitRegisters(pParse, p, addrBreak);
+ pLimit = p->pLimit;
+ pOffset = p->pOffset;
+ regLimit = p->iLimit;
+ regOffset = p->iOffset;
+ p->pLimit = p->pOffset = 0;
+ p->iLimit = p->iOffset = 0;
+ pOrderBy = p->pOrderBy;
+
+ /* Locate the cursor number of the Current table */
+ for(i=0; ALWAYS(i<pSrc->nSrc); i++){
+ if( pSrc->a[i].isRecursive ){
+ iCurrent = pSrc->a[i].iCursor;
+ break;
+ }
+ }
+
+ /* Allocate cursors numbers for Queue and Distinct. The cursor number for
+ ** the Distinct table must be exactly one greater than Queue in order
+ ** for the SRT_DistFifo and SRT_DistQueue destinations to work. */
+ iQueue = pParse->nTab++;
+ if( p->op==TK_UNION ){
+ eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
+ iDistinct = pParse->nTab++;
+ }else{
+ eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
+ }
+ sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+
+ /* Allocate cursors for Current, Queue, and Distinct. */
+ regCurrent = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
+ if( pOrderBy ){
+ KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1);
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ destQueue.pOrderBy = pOrderBy;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol);
+ }
+ VdbeComment((v, "Queue table"));
+ if( iDistinct ){
+ p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
+ p->selFlags |= SF_UsesEphemeral;
+ }
+
+ /* Detach the ORDER BY clause from the compound SELECT */
+ p->pOrderBy = 0;
+
+ /* Store the results of the setup-query in Queue. */
+ pSetup->pNext = 0;
+ rc = sqlite3Select(pParse, pSetup, &destQueue);
+ pSetup->pNext = p;
+ if( rc ) goto end_of_recursive_query;
+
+ /* Find the next row in the Queue and output that row */
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); VdbeCoverage(v);
+
+ /* Transfer the next row in Queue over to Current */
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */
+ if( pOrderBy ){
+ sqlite3VdbeAddOp3(v, OP_Column, iQueue, pOrderBy->nExpr+1, regCurrent);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent);
+ }
+ sqlite3VdbeAddOp1(v, OP_Delete, iQueue);
+
+ /* Output the single row in Current */
+ addrCont = sqlite3VdbeMakeLabel(v);
+ codeOffset(v, regOffset, addrCont);
+ selectInnerLoop(pParse, p, p->pEList, iCurrent,
+ 0, 0, pDest, addrCont, addrBreak);
+ if( regLimit ){
+ sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeResolveLabel(v, addrCont);
+
+ /* Execute the recursive SELECT taking the single row in Current as
+ ** the value for the recursive-table. Store the results in the Queue.
+ */
+ p->pPrior = 0;
+ sqlite3Select(pParse, p, &destQueue);
+ assert( p->pPrior==0 );
+ p->pPrior = pSetup;
+
+ /* Keep running the loop until the Queue is empty */
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
+ sqlite3VdbeResolveLabel(v, addrBreak);
+
+end_of_recursive_query:
+ sqlite3ExprListDelete(pParse->db, p->pOrderBy);
+ p->pOrderBy = pOrderBy;
+ p->pLimit = pLimit;
+ p->pOffset = pOffset;
+ return;
+}
+#endif /* SQLITE_OMIT_CTE */
+
+/* Forward references */
static int multiSelectOrderBy(
Parse *pParse, /* Parsing context */
Select *p, /* The right-most of SELECTs to be coded */
@@ -1597,7 +2022,6 @@ static int multiSelectOrderBy(
);
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** This routine is called to process a compound query form from
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
@@ -1641,18 +2065,17 @@ static int multiSelect(
Select *pDelete = 0; /* Chain of simple selects to delete */
sqlite3 *db; /* Database connection */
#ifndef SQLITE_OMIT_EXPLAIN
- int iSub1; /* EQP id of left-hand query */
- int iSub2; /* EQP id of right-hand query */
+ int iSub1 = 0; /* EQP id of left-hand query */
+ int iSub2 = 0; /* EQP id of right-hand query */
#endif
/* 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.
*/
assert( p && p->pPrior ); /* Calling function guarantees this much */
+ assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
db = pParse->db;
pPrior = p->pPrior;
- assert( pPrior->pRightmost!=pPrior );
- assert( pPrior->pRightmost==p->pRightmost );
dest = *pDest;
if( pPrior->pOrderBy ){
sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
@@ -1694,11 +2117,17 @@ static int multiSelect(
goto multi_select_end;
}
+#ifndef SQLITE_OMIT_CTE
+ if( p->selFlags & SF_Recursive ){
+ generateWithRecursiveQuery(pParse, p, &dest);
+ }else
+#endif
+
/* Compound SELECTs that have an ORDER BY clause are handled separately.
*/
if( p->pOrderBy ){
return multiSelectOrderBy(pParse, p, pDest);
- }
+ }else
/* Generate code for the left and right SELECT statements.
*/
@@ -1722,7 +2151,7 @@ static int multiSelect(
p->iLimit = pPrior->iLimit;
p->iOffset = pPrior->iOffset;
if( p->iLimit ){
- addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
+ addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit); VdbeCoverage(v);
VdbeComment((v, "Jump ahead if LIMIT reached"));
}
explainSetInteger(iSub2, pParse->iNextSelectId);
@@ -1733,9 +2162,9 @@ static int multiSelect(
p->nSelectRow += pPrior->nSelectRow;
if( pPrior->pLimit
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
- && p->nSelectRow > (double)nLimit
+ && nLimit>0 && p->nSelectRow > (u64)nLimit
){
- p->nSelectRow = (double)nLimit;
+ p->nSelectRow = nLimit;
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
@@ -1754,12 +2183,10 @@ static int multiSelect(
testcase( p->op==TK_EXCEPT );
testcase( p->op==TK_UNION );
priorOp = SRT_Union;
- if( dest.eDest==priorOp && ALWAYS(!p->pLimit &&!p->pOffset) ){
+ if( dest.eDest==priorOp ){
/* We can reuse a temporary table generated by a SELECT to our
** right.
*/
- assert( p->pRightmost!=p ); /* Can only happen for leftward elements
- ** of a 3-way or more compound */
assert( p->pLimit==0 ); /* Not allowed on leftward elements */
assert( p->pOffset==0 ); /* Not allowed on leftward elements */
unionTab = dest.iSDParm;
@@ -1772,7 +2199,7 @@ static int multiSelect(
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->selFlags |= SF_UsesEphemeral;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
}
@@ -1831,12 +2258,12 @@ static int multiSelect(
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
iStart = sqlite3VdbeCurrentAddr(v);
- selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
+ selectInnerLoop(pParse, p, p->pEList, unionTab,
0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
}
@@ -1861,7 +2288,7 @@ static int multiSelect(
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->selFlags |= SF_UsesEphemeral;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
/* Code the SELECTs to our left into temporary table "tab1".
@@ -1906,15 +2333,15 @@ static int multiSelect(
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
r1 = sqlite3GetTempReg(pParse);
iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, r1);
- selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
+ selectInnerLoop(pParse, p, p->pEList, tab1,
0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
@@ -1940,25 +2367,19 @@ static int multiSelect(
CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */
int nCol; /* Number of columns in result set */
- assert( p->pRightmost==p );
+ assert( p->pNext==0 );
nCol = p->pEList->nExpr;
- pKeyInfo = sqlite3DbMallocZero(db,
- sizeof(*pKeyInfo)+nCol*(sizeof(CollSeq*) + 1));
+ pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
rc = SQLITE_NOMEM;
goto multi_select_end;
}
-
- pKeyInfo->enc = ENC(db);
- pKeyInfo->nField = (u16)nCol;
-
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
*apColl = multiSelectCollSeq(pParse, p, i);
if( 0==*apColl ){
*apColl = db->pDfltColl;
}
}
- pKeyInfo->aSortOrder = (u8*)apColl;
for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
for(i=0; i<2; i++){
@@ -1970,11 +2391,12 @@ static int multiSelect(
break;
}
sqlite3VdbeChangeP2(v, addr, nCol);
- sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo),
+ P4_KEYINFO);
pLoop->addrOpenEphm[i] = -1;
}
}
- sqlite3DbFree(db, pKeyInfo);
+ sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
@@ -2013,7 +2435,6 @@ static int generateOutputSubroutine(
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;
@@ -2027,10 +2448,10 @@ static int generateOutputSubroutine(
*/
if( regPrev ){
int j1, j2;
- j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev);
+ j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
- (char*)pKeyInfo, p4type);
- sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2);
+ (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, j1);
sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
@@ -2039,7 +2460,7 @@ static int generateOutputSubroutine(
/* Suppress the first OFFSET entries if there is an OFFSET clause
*/
- codeOffset(v, p, iContinue);
+ codeOffset(v, p->iOffset, iContinue);
switch( pDest->eDest ){
/* Store the result as data using a unique key.
@@ -2131,7 +2552,7 @@ static int generateOutputSubroutine(
/* Jump to the end of the loop if the LIMIT is reached.
*/
if( p->iLimit ){
- sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
+ sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v);
}
/* Generate the subroutine return
@@ -2239,9 +2660,7 @@ static int multiSelectOrderBy(
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 */
@@ -2249,6 +2668,7 @@ static int multiSelectOrderBy(
int addrOutA; /* Address of the output-A subroutine */
int addrOutB = 0; /* Address of the output-B subroutine */
int addrEofA; /* Address of the select-A-exhausted subroutine */
+ int addrEofA_noB; /* Alternate addrEofA if B is uninitialized */
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 */
@@ -2299,8 +2719,8 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
- assert( pItem->iOrderByCol>0 );
- if( pItem->iOrderByCol==i ) break;
+ assert( pItem->u.x.iOrderByCol>0 );
+ if( pItem->u.x.iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
@@ -2308,7 +2728,7 @@ static int multiSelectOrderBy(
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
- if( pOrderBy ) pOrderBy->a[nOrderBy++].iOrderByCol = (u16)i;
+ if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i;
}
}
}
@@ -2324,30 +2744,11 @@ static int multiSelectOrderBy(
if( aPermute ){
struct ExprList_item *pItem;
for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
- assert( pItem->iOrderByCol>0 && pItem->iOrderByCol<=p->pEList->nExpr );
- aPermute[i] = pItem->iOrderByCol - 1;
- }
- pKeyMerge =
- sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
- if( pKeyMerge ){
- pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy];
- pKeyMerge->nField = (u16)nOrderBy;
- pKeyMerge->enc = ENC(db);
- for(i=0; i<nOrderBy; i++){
- CollSeq *pColl;
- Expr *pTerm = pOrderBy->a[i].pExpr;
- if( pTerm->flags & EP_Collate ){
- pColl = sqlite3ExprCollSeq(pParse, pTerm);
- }else{
- pColl = multiSelectCollSeq(pParse, p, aPermute[i]);
- if( pColl==0 ) pColl = db->pDfltColl;
- pOrderBy->a[i].pExpr =
- sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName);
- }
- pKeyMerge->aColl[i] = pColl;
- pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder;
- }
+ assert( pItem->u.x.iOrderByCol>0
+ && pItem->u.x.iOrderByCol<=p->pEList->nExpr );
+ aPermute[i] = pItem->u.x.iOrderByCol - 1;
}
+ pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
}else{
pKeyMerge = 0;
}
@@ -2369,12 +2770,9 @@ static int multiSelectOrderBy(
regPrev = pParse->nMem+1;
pParse->nMem += nExpr+1;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev);
- pKeyDup = sqlite3DbMallocZero(db,
- sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) );
+ pKeyDup = sqlite3KeyInfoAlloc(db, nExpr, 1);
if( pKeyDup ){
- pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr];
- pKeyDup->nField = (u16)nExpr;
- pKeyDup->enc = ENC(db);
+ assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
pKeyDup->aSortOrder[i] = 0;
@@ -2385,6 +2783,7 @@ static int multiSelectOrderBy(
/* Separate the left and the right query from one another
*/
p->pPrior = 0;
+ pPrior->pNext = 0;
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
if( pPrior->pPrior==0 ){
sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
@@ -2407,37 +2806,30 @@ static int multiSelectOrderBy(
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"));
+ addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
+ j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
+ VdbeComment((v, "left SELECT"));
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- VdbeNoopComment((v, "End coroutine for left SELECT"));
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrA);
+ sqlite3VdbeJumpHere(v, j1);
/* Generate a coroutine to evaluate the SELECT statement on
** the right - the "B" select
*/
- addrSelectB = sqlite3VdbeCurrentAddr(v);
- VdbeNoopComment((v, "Begin coroutine for right SELECT"));
+ addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
+ j1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
+ VdbeComment((v, "right SELECT"));
savedLimit = p->iLimit;
savedOffset = p->iOffset;
p->iLimit = regLimitB;
@@ -2446,9 +2838,7 @@ static int multiSelectOrderBy(
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
- VdbeNoopComment((v, "End coroutine for right SELECT"));
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regAddrB);
/* Generate a subroutine that outputs the current row of the A
** select as the next output row of the compound select.
@@ -2456,7 +2846,7 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "Output routine for A"));
addrOutA = generateOutputSubroutine(pParse,
p, &destA, pDest, regOutA,
- regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd);
+ regPrev, pKeyDup, labelEnd);
/* Generate a subroutine that outputs the current row of the B
** select as the next output row of the compound select.
@@ -2465,19 +2855,20 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "Output routine for B"));
addrOutB = generateOutputSubroutine(pParse,
p, &destB, pDest, regOutB,
- regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd);
+ regPrev, pKeyDup, labelEnd);
}
+ sqlite3KeyInfoUnref(pKeyDup);
/* 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);
+ addrEofA_noB = addrEofA = labelEnd;
}else{
- addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd);
- sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
+ VdbeNoopComment((v, "eof-A subroutine"));
+ addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+ addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
p->nSelectRow += pPrior->nSelectRow;
}
@@ -2490,9 +2881,8 @@ static int multiSelectOrderBy(
if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
}else{
VdbeNoopComment((v, "eof-B subroutine"));
- addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd);
- sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
+ addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB);
}
@@ -2500,8 +2890,7 @@ static int multiSelectOrderBy(
*/
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_Yield, regAddrA, addrEofA); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
/* Generate code to handle the case of A==B
@@ -2514,8 +2903,7 @@ static int multiSelectOrderBy(
}else{
VdbeNoopComment((v, "A-eq-B subroutine"));
addrAeqB =
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
}
@@ -2526,28 +2914,23 @@ static int multiSelectOrderBy(
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_Yield, regAddrB, addrEofB); VdbeCoverage(v);
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);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
/* Implement the main merge loop
*/
sqlite3VdbeResolveLabel(v, labelCmpr);
sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy,
- (char*)pKeyMerge, P4_KEYINFO_HANDOFF);
+ (char*)pKeyMerge, P4_KEYINFO);
sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE);
- sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
+ sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v);
/* Jump to the this point in order to terminate the query.
*/
@@ -2567,6 +2950,7 @@ static int multiSelectOrderBy(
sqlite3SelectDelete(db, p->pPrior);
}
p->pPrior = pPrior;
+ pPrior->pNext = p;
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
@@ -2772,6 +3156,14 @@ static void substSelect(
** (21) The subquery does not use LIMIT or the outer query is not
** DISTINCT. (See ticket [752e1646fc]).
**
+** (22) The subquery is not a recursive CTE.
+**
+** (23) The parent is not a recursive CTE, or the sub-query is not a
+** compound query. This restriction is because transforming the
+** parent to a compound query confuses the code that handles
+** recursive queries in multiSelect().
+**
+**
** 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.
@@ -2824,7 +3216,7 @@ static int flattenSubquery(
** and (14). */
if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */
if( pSub->pOffset ) return 0; /* Restriction (14) */
- if( p->pRightmost && pSub->pLimit ){
+ if( (p->selFlags & SF_Compound)!=0 && pSub->pLimit ){
return 0; /* Restriction (15) */
}
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
@@ -2843,6 +3235,8 @@ static int flattenSubquery(
if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){
return 0; /* Restriction (21) */
}
+ if( pSub->selFlags & SF_Recursive ) return 0; /* Restriction (22) */
+ if( (p->selFlags & SF_Recursive) && pSub->pPrior ) return 0; /* (23) */
/* OBSOLETE COMMENT 1:
** Restriction 3: If the subquery is a join, make sure the subquery is
@@ -2910,7 +3304,7 @@ static int flattenSubquery(
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
- if( p->pOrderBy->a[ii].iOrderByCol==0 ) return 0;
+ if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
}
}
@@ -2973,14 +3367,14 @@ static int flattenSubquery(
p->pOrderBy = pOrderBy;
p->pSrc = pSrc;
p->op = TK_ALL;
- p->pRightmost = 0;
if( pNew==0 ){
- pNew = pPrior;
+ p->pPrior = pPrior;
}else{
pNew->pPrior = pPrior;
- pNew->pRightmost = 0;
+ if( pPrior ) pPrior->pNext = pNew;
+ pNew->pNext = p;
+ p->pPrior = pNew;
}
- p->pPrior = pNew;
if( db->mallocFailed ) return 1;
}
@@ -3229,7 +3623,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( IsVirtual(pTab) ) return 0;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
- if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
+ if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
return pTab;
@@ -3319,11 +3713,207 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
pNew->pHaving = 0;
pNew->pOrderBy = 0;
p->pPrior = 0;
+ p->pNext = 0;
+ p->selFlags &= ~SF_Compound;
+ assert( pNew->pPrior!=0 );
+ pNew->pPrior->pNext = pNew;
pNew->pLimit = 0;
pNew->pOffset = 0;
return WRC_Continue;
}
+#ifndef SQLITE_OMIT_CTE
+/*
+** Argument pWith (which may be NULL) points to a linked list of nested
+** WITH contexts, from inner to outermost. If the table identified by
+** FROM clause element pItem is really a common-table-expression (CTE)
+** then return a pointer to the CTE definition for that table. Otherwise
+** return NULL.
+**
+** If a non-NULL value is returned, set *ppContext to point to the With
+** object that the returned CTE belongs to.
+*/
+static struct Cte *searchWith(
+ With *pWith, /* Current outermost WITH clause */
+ struct SrcList_item *pItem, /* FROM clause element to resolve */
+ With **ppContext /* OUT: WITH clause return value belongs to */
+){
+ const char *zName;
+ if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
+ With *p;
+ for(p=pWith; p; p=p->pOuter){
+ int i;
+ for(i=0; i<p->nCte; i++){
+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+ *ppContext = p;
+ return &p->a[i];
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* The code generator maintains a stack of active WITH clauses
+** with the inner-most WITH clause being at the top of the stack.
+**
+** This routine pushes the WITH clause passed as the second argument
+** onto the top of the stack. If argument bFree is true, then this
+** WITH clause will never be popped from the stack. In this case it
+** should be freed along with the Parse object. In other cases, when
+** bFree==0, the With object will be freed along with the SELECT
+** statement with which it is associated.
+*/
+void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
+ assert( bFree==0 || pParse->pWith==0 );
+ if( pWith ){
+ pWith->pOuter = pParse->pWith;
+ pParse->pWith = pWith;
+ pParse->bFreeWith = bFree;
+ }
+}
+
+/*
+** This function checks if argument pFrom refers to a CTE declared by
+** a WITH clause on the stack currently maintained by the parser. And,
+** if currently processing a CTE expression, if it is a recursive
+** reference to the current CTE.
+**
+** If pFrom falls into either of the two categories above, pFrom->pTab
+** and other fields are populated accordingly. The caller should check
+** (pFrom->pTab!=0) to determine whether or not a successful match
+** was found.
+**
+** Whether or not a match is found, SQLITE_OK is returned if no error
+** occurs. If an error does occur, an error message is stored in the
+** parser and some error code other than SQLITE_OK returned.
+*/
+static int withExpand(
+ Walker *pWalker,
+ struct SrcList_item *pFrom
+){
+ Parse *pParse = pWalker->pParse;
+ sqlite3 *db = pParse->db;
+ struct Cte *pCte; /* Matched CTE (or NULL if no match) */
+ With *pWith; /* WITH clause that pCte belongs to */
+
+ assert( pFrom->pTab==0 );
+
+ pCte = searchWith(pParse->pWith, pFrom, &pWith);
+ if( pCte ){
+ Table *pTab;
+ ExprList *pEList;
+ Select *pSel;
+ Select *pLeft; /* Left-most SELECT statement */
+ int bMayRecursive; /* True if compound joined by UNION [ALL] */
+ With *pSavedWith; /* Initial value of pParse->pWith */
+
+ /* If pCte->zErr is non-NULL at this point, then this is an illegal
+ ** recursive reference to CTE pCte. Leave an error in pParse and return
+ ** early. If pCte->zErr is NULL, then this is not a recursive reference.
+ ** In this case, proceed. */
+ if( pCte->zErr ){
+ sqlite3ErrorMsg(pParse, pCte->zErr, pCte->zName);
+ return SQLITE_ERROR;
+ }
+
+ assert( pFrom->pTab==0 );
+ pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return WRC_Abort;
+ pTab->nRef = 1;
+ pTab->zName = sqlite3DbStrDup(db, pCte->zName);
+ pTab->iPKey = -1;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
+ pTab->tabFlags |= TF_Ephemeral;
+ pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
+ if( db->mallocFailed ) return SQLITE_NOMEM;
+ assert( pFrom->pSelect );
+
+ /* Check if this is a recursive CTE. */
+ pSel = pFrom->pSelect;
+ bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
+ if( bMayRecursive ){
+ int i;
+ SrcList *pSrc = pFrom->pSelect->pSrc;
+ for(i=0; i<pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &pSrc->a[i];
+ if( pItem->zDatabase==0
+ && pItem->zName!=0
+ && 0==sqlite3StrICmp(pItem->zName, pCte->zName)
+ ){
+ pItem->pTab = pTab;
+ pItem->isRecursive = 1;
+ pTab->nRef++;
+ pSel->selFlags |= SF_Recursive;
+ }
+ }
+ }
+
+ /* Only one recursive reference is permitted. */
+ if( pTab->nRef>2 ){
+ sqlite3ErrorMsg(
+ pParse, "multiple references to recursive table: %s", pCte->zName
+ );
+ return SQLITE_ERROR;
+ }
+ assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
+
+ pCte->zErr = "circular reference: %s";
+ pSavedWith = pParse->pWith;
+ pParse->pWith = pWith;
+ sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
+
+ for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
+ pEList = pLeft->pEList;
+ if( pCte->pCols ){
+ if( pEList->nExpr!=pCte->pCols->nExpr ){
+ sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns",
+ pCte->zName, pEList->nExpr, pCte->pCols->nExpr
+ );
+ pParse->pWith = pSavedWith;
+ return SQLITE_ERROR;
+ }
+ pEList = pCte->pCols;
+ }
+
+ selectColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
+ if( bMayRecursive ){
+ if( pSel->selFlags & SF_Recursive ){
+ pCte->zErr = "multiple recursive references: %s";
+ }else{
+ pCte->zErr = "recursive reference in a subquery: %s";
+ }
+ sqlite3WalkSelect(pWalker, pSel);
+ }
+ pCte->zErr = 0;
+ pParse->pWith = pSavedWith;
+ }
+
+ return SQLITE_OK;
+}
+#endif
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** If the SELECT passed as the second argument has an associated WITH
+** clause, pop it from the stack stored as part of the Parse object.
+**
+** This function is used as the xSelectCallback2() callback by
+** sqlite3SelectExpand() when walking a SELECT tree to resolve table
+** names and other FROM clause elements.
+*/
+static void selectPopWith(Walker *pWalker, Select *p){
+ Parse *pParse = pWalker->pParse;
+ With *pWith = findRightmost(p)->pWith;
+ if( pWith!=0 ){
+ assert( pParse->pWith==pWith );
+ pParse->pWith = pWith->pOuter;
+ }
+}
+#else
+#define selectPopWith 0
+#endif
+
/*
** This routine is a Walker callback for "expanding" a SELECT statement.
** "Expanding" means to do the following:
@@ -3367,6 +3957,7 @@ static int selectExpander(Walker *pWalker, Select *p){
}
pTabList = p->pSrc;
pEList = p->pEList;
+ sqlite3WithPush(pParse, findRightmost(p)->pWith, 0);
/* Make sure cursor numbers have been assigned to all entries in
** the FROM clause of the SELECT statement.
@@ -3379,12 +3970,21 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
+ assert( pFrom->isRecursive==0 || pFrom->pTab );
+ if( pFrom->isRecursive ) continue;
if( pFrom->pTab!=0 ){
/* This statement has already been prepared. There is no need
** to go further. */
assert( i==0 );
+#ifndef SQLITE_OMIT_CTE
+ selectPopWith(pWalker, p);
+#endif
return WRC_Prune;
}
+#ifndef SQLITE_OMIT_CTE
+ if( withExpand(pWalker, pFrom) ) return WRC_Abort;
+ if( pFrom->pTab ) {} else
+#endif
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
Select *pSel = pFrom->pSelect;
@@ -3395,11 +3995,11 @@ static int selectExpander(Walker *pWalker, Select *p){
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return WRC_Abort;
pTab->nRef = 1;
- pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
+ pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
while( pSel->pPrior ){ pSel = pSel->pPrior; }
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
pTab->iPKey = -1;
- pTab->nRowEst = 1000000;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral;
#endif
}else{
@@ -3640,11 +4240,14 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
memset(&w, 0, sizeof(w));
- w.xSelectCallback = convertCompoundSelectToSubquery;
w.xExprCallback = exprWalkNoop;
w.pParse = pParse;
- sqlite3WalkSelect(&w, pSelect);
+ if( pParse->hasCompound ){
+ w.xSelectCallback = convertCompoundSelectToSubquery;
+ sqlite3WalkSelect(&w, pSelect);
+ }
w.xSelectCallback = selectExpander;
+ w.xSelectCallback2 = selectPopWith;
sqlite3WalkSelect(&w, pSelect);
}
@@ -3663,7 +4266,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
** at that point because identifiers had not yet been resolved. This
** routine is called after identifier resolution.
*/
-static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
+static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
int i;
SrcList *pTabList;
@@ -3679,13 +4282,13 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){
/* A sub-query in the FROM clause of a SELECT */
Select *pSel = pFrom->pSelect;
- assert( pSel );
- while( pSel->pPrior ) pSel = pSel->pPrior;
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel);
+ if( pSel ){
+ while( pSel->pPrior ) pSel = pSel->pPrior;
+ selectAddColumnTypeAndCollation(pParse, pTab, pSel);
+ }
}
}
}
- return WRC_Continue;
}
#endif
@@ -3701,10 +4304,9 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
#ifndef SQLITE_OMIT_SUBQUERY
Walker w;
memset(&w, 0, sizeof(w));
- w.xSelectCallback = selectAddSubqueryTypeInfo;
+ w.xSelectCallback2 = selectAddSubqueryTypeInfo;
w.xExprCallback = exprWalkNoop;
w.pParse = pParse;
- w.bSelectDepthFirst = 1;
sqlite3WalkSelect(&w, pSelect);
#endif
}
@@ -3751,14 +4353,23 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
Vdbe *v = pParse->pVdbe;
int i;
struct AggInfo_func *pFunc;
- if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){
- return;
- }
+ int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
+ if( nReg==0 ) return;
+#ifdef SQLITE_DEBUG
+ /* Verify that all AggInfo registers are within the range specified by
+ ** AggInfo.mnReg..AggInfo.mxReg */
+ assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
for(i=0; i<pAggInfo->nColumn; i++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem);
+ assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
+ && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
+ }
+ for(i=0; i<pAggInfo->nFunc; i++){
+ assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
+ && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
}
+#endif
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pExpr;
assert( !ExprHasProperty(pE, EP_xIsSelect) );
@@ -3767,9 +4378,9 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
"argument");
pFunc->iDistinct = -1;
}else{
- KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
+ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ (char*)pKeyInfo, P4_KEYINFO);
}
}
}
@@ -3804,7 +4415,6 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
struct AggInfo_col *pC;
pAggInfo->directMode = 1;
- sqlite3ExprCacheClear(pParse);
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
int addrNext = 0;
@@ -3814,7 +4424,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
- sqlite3ExprCodeExprList(pParse, pList, regAgg, 1);
+ sqlite3ExprCodeExprList(pParse, pList, regAgg, SQLITE_ECEL_DUP);
}else{
nArg = 0;
regAgg = 0;
@@ -3824,7 +4434,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
assert( nArg==1 );
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
}
- if( pF->pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl = 0;
struct ExprList_item *pItem;
int j;
@@ -3860,7 +4470,7 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
** values to an OP_Copy.
*/
if( regHit ){
- addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit);
+ addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
}
sqlite3ExprCacheClear(pParse);
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
@@ -3884,11 +4494,11 @@ static void explainSimpleCount(
Index *pIdx /* Index used to optimize scan, or NULL */
){
if( pParse->explain==2 ){
- char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)",
- pTab->zName,
- pIdx ? "USING COVERING INDEX " : "",
- pIdx ? pIdx->zName : "",
- pTab->nRowEst
+ int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
+ char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
+ pTab->zName,
+ bCover ? " USING COVERING INDEX " : "",
+ bCover ? pIdx->zName : ""
);
sqlite3VdbeAddOp4(
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
@@ -3902,50 +4512,8 @@ static void explainSimpleCount(
/*
** Generate code for the SELECT statement given in the p argument.
**
-** The results are distributed in various ways depending on the
-** contents of the SelectDest structure pointed to by argument pDest
-** as follows:
-**
-** pDest->eDest Result
-** ------------ -------------------------------------------
-** SRT_Output Generate a row of output (using the OP_ResultRow
-** opcode) for each row in the result set.
-**
-** SRT_Mem Only valid if the result is a single column.
-** Store the first column of the first result row
-** in register pDest->iSDParm then abandon the rest
-** of the query. This destination implies "LIMIT 1".
-**
-** SRT_Set The result must be a single column. Store each
-** row of result as the key in table pDest->iSDParm.
-** Apply the affinity pDest->affSdst before storing
-** results. Used to implement "IN (SELECT ...)".
-**
-** SRT_Union Store results as a key in a temporary table
-** identified by pDest->iSDParm.
-**
-** SRT_Except Remove results from the temporary table pDest->iSDParm.
-**
-** SRT_Table Store results in temporary table pDest->iSDParm.
-** This is like SRT_EphemTab except that the table
-** is assumed to already be open.
-**
-** SRT_EphemTab Create an temporary table pDest->iSDParm and store
-** the result there. The cursor is left open after
-** returning. This is like SRT_Table except that
-** this destination uses OP_OpenEphemeral to create
-** the table first.
-**
-** SRT_Coroutine Generate a co-routine that returns a new row of
-** results each time it is invoked. The entry point
-** of the co-routine is stored in register pDest->iSDParm.
-**
-** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result
-** set is not empty.
-**
-** SRT_Discard Throw the results away. This is used by SELECT
-** statements within triggers whose only purpose is
-** the side-effects of functions.
+** The results are returned according to the SelectDest structure.
+** See comments in sqliteInt.h for further information.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
@@ -3966,12 +4534,11 @@ int sqlite3Select(
ExprList *pEList; /* List of columns to extract. */
SrcList *pTabList; /* List of tables to select from */
Expr *pWhere; /* The WHERE clause. May be NULL */
- ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
Expr *pHaving; /* The HAVING clause. May be NULL */
int rc = 1; /* Value to return from this function */
- int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */
+ SortCtx sSort; /* Info on how to code the ORDER BY clause */
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
@@ -3988,9 +4555,15 @@ int sqlite3Select(
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
memset(&sAggInfo, 0, sizeof(sAggInfo));
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue );
if( IgnorableOrderby(pDest) ){
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
- pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard);
+ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
+ pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo ||
+ pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo);
/* If ORDER BY makes no difference in the output then neither does
** DISTINCT so it can be removed too. */
sqlite3ExprListDelete(db, p->pOrderBy);
@@ -3998,7 +4571,8 @@ int sqlite3Select(
p->selFlags &= ~SF_Distinct;
}
sqlite3SelectPrep(pParse, p, 0);
- pOrderBy = p->pOrderBy;
+ memset(&sSort, 0, sizeof(sSort));
+ sSort.pOrderBy = p->pOrderBy;
pTabList = p->pSrc;
pEList = p->pEList;
if( pParse->nErr || db->mallocFailed ){
@@ -4046,7 +4620,7 @@ int sqlite3Select(
}
/* Increment Parse.nHeight by the height of the largest expression
- ** tree refered to by this, the parent select. The child select
+ ** tree referred to by this, the parent select. The child select
** may contain expression trees of at most
** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit
** more conservative than necessary, but much easier than enforcing
@@ -4062,42 +4636,24 @@ int sqlite3Select(
p->selFlags |= SF_Aggregate;
}
i = -1;
- }else if( pTabList->nSrc==1 && (p->selFlags & SF_Materialize)==0
- && OptimizationEnabled(db, SQLITE_SubqCoroutine)
+ }else if( pTabList->nSrc==1
+ && OptimizationEnabled(db, SQLITE_SubqCoroutine)
){
/* Implement a co-routine that will return a single row of the result
** set on each invocation.
*/
- int addrTop;
- int addrEof;
+ int addrTop = sqlite3VdbeCurrentAddr(v)+1;
pItem->regReturn = ++pParse->nMem;
- addrEof = ++pParse->nMem;
- /* Before coding the OP_Goto to jump to the start of the main routine,
- ** ensure that the jump to the verify-schema routine has already
- ** been coded. Otherwise, the verify-schema would likely be coded as
- ** part of the co-routine. If the main routine then accessed the
- ** database before invoking the co-routine for the first time (for
- ** example to initialize a LIMIT register from a sub-select), it would
- ** be doing so without having verified the schema version and obtained
- ** the required db locks. See ticket d6b36be38. */
- sqlite3CodeVerifySchema(pParse, -1);
- sqlite3VdbeAddOp0(v, OP_Goto);
- addrTop = sqlite3VdbeAddOp1(v, OP_OpenPseudo, pItem->iCursor);
- sqlite3VdbeChangeP5(v, 1);
- VdbeComment((v, "coroutine for %s", pItem->pTab->zName));
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
+ VdbeComment((v, "%s", pItem->pTab->zName));
pItem->addrFillSub = addrTop;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, addrEof);
- sqlite3VdbeChangeP5(v, 1);
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
+ pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
pItem->viaCoroutine = 1;
- sqlite3VdbeChangeP2(v, addrTop, dest.iSdst);
- sqlite3VdbeChangeP3(v, addrTop, dest.nSdst);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, addrEof);
- sqlite3VdbeAddOp1(v, OP_Yield, pItem->regReturn);
- VdbeComment((v, "end %s", pItem->pTab->zName));
+ pItem->regResult = dest.iSdst;
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, pItem->regReturn);
sqlite3VdbeJumpHere(v, addrTop-1);
sqlite3ClearTempRegCache(pParse);
}else{
@@ -4113,17 +4669,19 @@ int sqlite3Select(
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
- VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
if( pItem->isCorrelated==0 ){
/* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
- onceAddr = sqlite3CodeOnce(pParse);
+ onceAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+ VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
+ }else{
+ VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
+ pItem->pTab->nRowLogEst = sqlite3LogEst(pSub->nSelectRow);
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
@@ -4136,7 +4694,7 @@ int sqlite3Select(
pParse->nHeight -= sqlite3SelectExprHeight(p);
pTabList = p->pSrc;
if( !IgnorableOrderby(pDest) ){
- pOrderBy = p->pOrderBy;
+ sSort.pOrderBy = p->pOrderBy;
}
}
pEList = p->pEList;
@@ -4150,39 +4708,12 @@ int sqlite3Select(
/* If there is are a sequence of queries, do the earlier ones first.
*/
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");
- goto select_end;
- }
- }
rc = multiSelect(pParse, p, pDest);
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
return rc;
}
#endif
- /* If there is both a GROUP BY and an ORDER BY clause and they are
- ** identical, then disable the ORDER BY clause since the GROUP BY
- ** will cause elements to come out in the correct order. This is
- ** an optimization - the correct answer should result regardless.
- ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER
- ** to disable this optimization for testing purposes.
- */
- if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0
- && OptimizationEnabled(db, SQLITE_GroupByOrder) ){
- pOrderBy = 0;
- }
-
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
** if the select-list is the same as the ORDER BY list, then this query
** can be rewritten as a GROUP BY. In other words, this:
@@ -4199,12 +4730,12 @@ int sqlite3Select(
** BY and DISTINCT, and an index or separate temp-table for the other.
*/
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
- && sqlite3ExprListCompare(pOrderBy, p->pEList)==0
+ && sqlite3ExprListCompare(sSort.pOrderBy, p->pEList, -1)==0
){
p->selFlags &= ~SF_Distinct;
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
pGroupBy = p->pGroupBy;
- pOrderBy = 0;
+ sSort.pOrderBy = 0;
/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
** the sDistinct.isTnct is still set. Hence, isTnct represents the
** original setting of the SF_Distinct flag, not the current setting */
@@ -4218,16 +4749,16 @@ int sqlite3Select(
** we figure out that the sorting index is not needed. The addrSortIndex
** variable is used to facilitate that change.
*/
- if( pOrderBy ){
+ if( sSort.pOrderBy ){
KeyInfo *pKeyInfo;
- pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
- pOrderBy->iECursor = pParse->nTab++;
- p->addrOpenEphm[2] = addrSortIndex =
+ pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0);
+ sSort.iECursor = pParse->nTab++;
+ sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
- pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ sSort.iECursor, sSort.pOrderBy->nExpr+2, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
}else{
- addrSortIndex = -1;
+ sSort.addrSortIndex = -1;
}
/* If the output is destined for a temporary table, open that table.
@@ -4239,11 +4770,11 @@ int sqlite3Select(
/* Set the limiter.
*/
iEnd = sqlite3VdbeMakeLabel(v);
- p->nSelectRow = (double)LARGEST_INT64;
+ p->nSelectRow = LARGEST_INT64;
computeLimitRegisters(pParse, p, iEnd);
- if( p->iLimit==0 && addrSortIndex>=0 ){
- sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
- p->selFlags |= SF_UseSorter;
+ if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
+ sqlite3VdbeGetOp(v, sSort.addrSortIndex)->opcode = OP_SorterOpen;
+ sSort.sortFlags |= SORTFLAG_UseSorter;
}
/* Open a virtual index to use for the distinct set.
@@ -4252,8 +4783,8 @@ int sqlite3Select(
sDistinct.tabTnct = pParse->nTab++;
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
sDistinct.tabTnct, 0, 0,
- (char*)keyInfoFromExprList(pParse, p->pEList),
- P4_KEYINFO_HANDOFF);
+ (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
+ P4_KEYINFO);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
}else{
@@ -4262,27 +4793,37 @@ int sqlite3Select(
if( !isAgg && pGroupBy==0 ){
/* No aggregate functions and no GROUP BY clause */
- ExprList *pDist = (sDistinct.isTnct ? p->pEList : 0);
+ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
/* Begin the database scan. */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, pDist, 0,0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
+ p->pEList, wctrlFlags, 0);
if( pWInfo==0 ) goto select_end;
- if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
- if( pWInfo->eDistinct ) sDistinct.eTnctType = pWInfo->eDistinct;
- if( pOrderBy && pWInfo->nOBSat==pOrderBy->nExpr ) pOrderBy = 0;
+ if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
+ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
+ }
+ if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){
+ sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo);
+ }
+ if( sSort.pOrderBy ){
+ sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
+ if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
+ sSort.pOrderBy = 0;
+ }
+ }
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
** into an OP_Noop.
*/
- if( addrSortIndex>=0 && pOrderBy==0 ){
- sqlite3VdbeChangeToNoop(v, addrSortIndex);
- p->addrOpenEphm[2] = -1;
+ if( sSort.addrSortIndex>=0 && sSort.pOrderBy==0 ){
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
}
/* Use the standard inner loop. */
- selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, &sDistinct, pDest,
- pWInfo->iContinue, pWInfo->iBreak);
+ selectInnerLoop(pParse, p, pEList, -1, &sSort, &sDistinct, pDest,
+ sqlite3WhereContinueLabel(pWInfo),
+ sqlite3WhereBreakLabel(pWInfo));
/* End the database scan loop.
*/
@@ -4301,6 +4842,7 @@ int sqlite3Select(
int addrEnd; /* End of processing for this SELECT */
int sortPTab = 0; /* Pseudotable used to decode sorting results */
int sortOut = 0; /* Output register from the sorter */
+ int orderByGrp = 0; /* True if the GROUP BY and ORDER BY are the same */
/* Remove any and all aliases between the result set and the
** GROUP BY clause.
@@ -4310,16 +4852,28 @@ int sqlite3Select(
struct ExprList_item *pItem; /* For looping over expression in a list */
for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){
- pItem->iAlias = 0;
+ pItem->u.x.iAlias = 0;
}
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
- pItem->iAlias = 0;
+ pItem->u.x.iAlias = 0;
}
- if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100;
+ if( p->nSelectRow>100 ) p->nSelectRow = 100;
}else{
- p->nSelectRow = (double)1;
+ p->nSelectRow = 1;
}
+
+ /* If there is both a GROUP BY and an ORDER BY clause and they are
+ ** identical, then it may be possible to disable the ORDER BY clause
+ ** on the grounds that the GROUP BY will cause elements to come out
+ ** in the correct order. It also may not - the GROUP BY may use a
+ ** database index that causes rows to be grouped together as required
+ ** but not actually sorted. Either way, record the fact that the
+ ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
+ ** variable. */
+ if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
+ orderByGrp = 1;
+ }
/* Create a label to jump to when we want to abort the query */
addrEnd = sqlite3VdbeMakeLabel(v);
@@ -4332,10 +4886,11 @@ int sqlite3Select(
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
sNC.pAggInfo = &sAggInfo;
+ sAggInfo.mnReg = pParse->nMem+1;
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
sAggInfo.pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
- sqlite3ExprAnalyzeAggList(&sNC, pOrderBy);
+ sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
if( pHaving ){
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
}
@@ -4346,6 +4901,7 @@ int sqlite3Select(
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
sNC.ncFlags &= ~NC_InAggFunc;
}
+ sAggInfo.mxReg = pParse->nMem;
if( db->mallocFailed ) goto select_end;
/* Processing for aggregates with GROUP BY is very different and
@@ -4368,10 +4924,10 @@ int sqlite3Select(
** will be converted into a Noop.
*/
sAggInfo.sortingIdx = pParse->nTab++;
- pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
+ pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0);
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
- 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ 0, (char*)pKeyInfo, P4_KEYINFO);
/* Initialize memory locations used by GROUP BY aggregate processing
*/
@@ -4397,9 +4953,11 @@ int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
+ WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
+ );
if( pWInfo==0 ) goto select_end;
- if( pWInfo->nOBSat==pGroupBy->nExpr ){
+ if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
** cancelled later because we still need to use the pKeyInfo
@@ -4459,9 +5017,24 @@ int sqlite3Select(
sortOut = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
- VdbeComment((v, "GROUP BY sort"));
+ VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
sAggInfo.useSortingIdx = 1;
sqlite3ExprCacheClear(pParse);
+
+ }
+
+ /* If the index or temporary table used by the GROUP BY sort
+ ** will naturally deliver rows in the order required by the ORDER BY
+ ** clause, cancel the ephemeral table open coded earlier.
+ **
+ ** This is an optimization - the correct answer should result regardless.
+ ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to
+ ** disable this optimization for testing purposes. */
+ if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder)
+ && (groupBySort || sqlite3WhereIsSorted(pWInfo))
+ ){
+ sSort.pOrderBy = 0;
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
}
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
@@ -4484,9 +5057,9 @@ int sqlite3Select(
}
}
sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
- (char*)pKeyInfo, P4_KEYINFO);
+ (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1);
+ sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1); VdbeCoverage(v);
/* Generate code that runs whenever the GROUP BY changes.
** Changes in the GROUP BY are detected by the previous code
@@ -4500,7 +5073,7 @@ int sqlite3Select(
sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr);
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
VdbeComment((v, "output one row"));
- sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd);
+ sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v);
VdbeComment((v, "check abort flag"));
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
VdbeComment((v, "reset accumulator"));
@@ -4517,6 +5090,7 @@ int sqlite3Select(
*/
if( groupBySort ){
sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
+ VdbeCoverage(v);
}else{
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
@@ -4544,12 +5118,12 @@ int sqlite3Select(
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
sqlite3VdbeResolveLabel(v, addrOutputRow);
addrOutputRow = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
+ sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2); VdbeCoverage(v);
VdbeComment((v, "Groupby result generator entry point"));
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
finalizeAggFunctions(pParse, &sAggInfo);
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
- selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
+ selectInnerLoop(pParse, p, p->pEList, -1, &sSort,
&sDistinct, pDest,
addrOutputRow+1, addrSetAbort);
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
@@ -4590,33 +5164,34 @@ int sqlite3Select(
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- /* Search for the index that has the least amount of columns. If
- ** there is such an index, and it has less columns than the table
- ** does, then we can assume that it consumes less space on disk and
- ** will therefore be cheaper to scan to determine the query result.
- ** In this case set iRoot to the root page number of the index b-tree
- ** and pKeyInfo to the KeyInfo structure required to navigate the
- ** index.
+ /* Search for the index that has the lowest scan cost.
**
** (2011-04-15) Do not do a full scan of an unordered index.
**
+ ** (2013-10-03) Do not count the entries in a partial index.
+ **
** In practice the KeyInfo structure will not be used. It is only
** passed to keep OP_OpenRead happy.
*/
+ if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){
+ if( pIdx->bUnordered==0
+ && pIdx->szIdxRow<pTab->szTabRow
+ && pIdx->pPartIdxWhere==0
+ && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
+ ){
pBest = pIdx;
}
}
- if( pBest && pBest->nColumn<pTab->nCol ){
+ if( pBest ){
iRoot = pBest->tnum;
- pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
+ pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
}
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
- sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb);
+ sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1);
if( pKeyInfo ){
- sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
}
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
@@ -4680,8 +5255,8 @@ int sqlite3Select(
}
updateAccumulator(pParse, &sAggInfo);
assert( pMinMax==0 || pMinMax->nExpr==1 );
- if( pWInfo->nOBSat>0 ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
+ if( sqlite3WhereIsOrdered(pWInfo)>0 ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo));
VdbeComment((v, "%s() by index",
(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
@@ -4689,9 +5264,9 @@ int sqlite3Select(
finalizeAggFunctions(pParse, &sAggInfo);
}
- pOrderBy = 0;
+ sSort.pOrderBy = 0;
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
- selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, 0,
+ selectInnerLoop(pParse, p, p->pEList, -1, 0, 0,
pDest, addrEnd, addrEnd);
sqlite3ExprListDelete(db, pDel);
}
@@ -4706,9 +5281,9 @@ int sqlite3Select(
/* If there is an ORDER BY clause, then we need to sort the results
** and send them to the callback one by one.
*/
- if( pOrderBy ){
- explainTempTable(pParse, "ORDER BY");
- generateSortTail(pParse, p, v, pEList->nExpr, pDest);
+ if( sSort.pOrderBy ){
+ explainTempTable(pParse, sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
+ generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
/* Jump here to skip this query
@@ -4816,10 +5391,6 @@ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
sqlite3ExplainPrintf(pVdbe, "(null-select)");
return;
}
- while( p->pPrior ){
- p->pPrior->pNext = p;
- p = p->pPrior;
- }
sqlite3ExplainPush(pVdbe);
while( p ){
explainOneSelect(pVdbe, p);
diff --git a/src/shell.c b/src/shell.c
index faaec80..2730603 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -45,15 +45,17 @@
# include <sys/types.h>
#endif
-#ifdef HAVE_EDITLINE
-# include <editline/editline.h>
-#endif
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
+#if defined(HAVE_READLINE) && HAVE_READLINE!=0
# include <readline/readline.h>
# include <readline/history.h>
+#else
+# undef HAVE_READLINE
#endif
-#if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1)
-# define readline(p) local_getline(p,stdin,0)
+#if defined(HAVE_EDITLINE) && !defined(HAVE_READLINE)
+# define HAVE_READLINE 1
+# include <editline/readline.h>
+#endif
+#if !defined(HAVE_READLINE)
# define add_history(X)
# define read_history(X)
# define write_history(X)
@@ -62,16 +64,24 @@
#if defined(_WIN32) || defined(WIN32)
# include <io.h>
+# include <fcntl.h>
#define isatty(h) _isatty(h)
-#define access(f,m) _access((f),(m))
+#ifndef access
+# define access(f,m) _access((f),(m))
+#endif
#undef popen
-#define popen(a,b) _popen((a),(b))
+#define popen _popen
#undef pclose
-#define pclose(x) _pclose(x)
+#define pclose _pclose
#else
/* Make sure isatty() has a prototype.
*/
extern int isatty(int);
+
+/* popen and pclose are not C89 functions and so are sometimes omitted from
+** the <stdio.h> header */
+extern FILE *popen(const char*,const char*);
+extern int pclose(FILE*);
#endif
#if defined(_WIN32_WCE)
@@ -82,21 +92,38 @@ extern int isatty(int);
#define isatty(x) 1
#endif
-/* True if the timer is enabled */
-static int enableTimer = 0;
-
/* ctype macros that work with signed characters */
#define IsSpace(X) isspace((unsigned char)X)
#define IsDigit(X) isdigit((unsigned char)X)
#define ToLower(X) (char)tolower((unsigned char)X)
+
+/* True if the timer is enabled */
+static int enableTimer = 0;
+
+/* Return the current wall-clock time */
+static sqlite3_int64 timeOfDay(void){
+ static sqlite3_vfs *clockVfs = 0;
+ sqlite3_int64 t;
+ if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
+ if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){
+ clockVfs->xCurrentTimeInt64(clockVfs, &t);
+ }else{
+ double r;
+ clockVfs->xCurrentTime(clockVfs, &r);
+ t = (sqlite3_int64)(r*86400000.0);
+ }
+ return t;
+}
+
#if !defined(_WIN32) && !defined(WIN32) && !defined(_WRS_KERNEL) \
&& !defined(__minux)
#include <sys/time.h>
#include <sys/resource.h>
/* Saved resource information for the beginning of an operation */
-static struct rusage sBegin;
+static struct rusage sBegin; /* CPU time at start */
+static sqlite3_int64 iBegin; /* Wall-clock time at start */
/*
** Begin timing an operation
@@ -104,6 +131,7 @@ static struct rusage sBegin;
static void beginTimer(void){
if( enableTimer ){
getrusage(RUSAGE_SELF, &sBegin);
+ iBegin = timeOfDay();
}
}
@@ -119,8 +147,10 @@ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){
static void endTimer(void){
if( enableTimer ){
struct rusage sEnd;
+ sqlite3_int64 iEnd = timeOfDay();
getrusage(RUSAGE_SELF, &sEnd);
- printf("CPU Time: user %f sys %f\n",
+ printf("Run Time: real %.3f user %f sys %f\n",
+ (iEnd - iBegin)*0.001,
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
@@ -138,6 +168,7 @@ static void endTimer(void){
static HANDLE hProcess;
static FILETIME ftKernelBegin;
static FILETIME ftUserBegin;
+static sqlite3_int64 ftWallBegin;
typedef BOOL (WINAPI *GETPROCTIMES)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
static GETPROCTIMES getProcessTimesAddr = NULL;
@@ -175,6 +206,7 @@ static void beginTimer(void){
if( enableTimer && getProcessTimesAddr ){
FILETIME ftCreation, ftExit;
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelBegin, &ftUserBegin);
+ ftWallBegin = timeOfDay();
}
}
@@ -191,8 +223,10 @@ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){
static void endTimer(void){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
+ sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess, &ftCreation, &ftExit, &ftKernelEnd, &ftUserEnd);
- printf("CPU Time: user %f sys %f\n",
+ printf("Run Time: real %.3f user %f sys %f\n",
+ (ftWallEnd - ftWallBegin)*0.001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
}
@@ -332,23 +366,13 @@ static void shellstaticFunc(
** 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.
+** If zLine is not NULL then it is a malloced buffer returned from
+** a previous call to this routine that may be reused.
*/
-static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
- char *zLine;
- int nLine;
- int n;
- int inQuote = 0;
+static char *local_getline(char *zLine, FILE *in){
+ int nLine = zLine==0 ? 0 : 100;
+ int n = 0;
- if( zPrompt && *zPrompt ){
- printf("%s",zPrompt);
- fflush(stdout);
- }
- nLine = 100;
- zLine = malloc( nLine );
- if( zLine==0 ) return 0;
- n = 0;
while( 1 ){
if( n+100>nLine ){
nLine = nLine*2 + 100;
@@ -363,42 +387,48 @@ static char *local_getline(char *zPrompt, FILE *in, int csvFlag){
zLine[n] = 0;
break;
}
- while( zLine[n] ){
- if( zLine[n]=='"' ) inQuote = !inQuote;
- n++;
- }
- if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){
+ while( zLine[n] ) n++;
+ if( n>0 && zLine[n-1]=='\n' ){
n--;
if( n>0 && zLine[n-1]=='\r' ) n--;
zLine[n] = 0;
break;
}
}
- zLine = realloc( zLine, n+1 );
return zLine;
}
/*
** Retrieve a single line of input text.
**
-** zPrior is a string of prior text retrieved. If not the empty
-** string, then issue a continuation prompt.
+** If in==0 then read from standard input and prompt before each line.
+** If isContinuation is true, then a continuation prompt is appropriate.
+** If isContinuation is zero, then the main prompt should be used.
+**
+** If zPrior is not NULL then it is a buffer from a prior call to this
+** routine that can be reused.
+**
+** The result is stored in space obtained from malloc() and must either
+** be freed by the caller or else passed back into this routine via the
+** zPrior argument for reuse.
*/
-static char *one_input_line(const char *zPrior, FILE *in){
+static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
char *zPrompt;
char *zResult;
if( in!=0 ){
- return local_getline(0, in, 0);
- }
- if( zPrior && zPrior[0] ){
- zPrompt = continuePrompt;
+ zResult = local_getline(zPrior, in);
}else{
- zPrompt = mainPrompt;
- }
- zResult = readline(zPrompt);
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
- if( zResult && *zResult ) add_history(zResult);
+ zPrompt = isContinuation ? continuePrompt : mainPrompt;
+#if defined(HAVE_READLINE)
+ free(zPrior);
+ zResult = readline(zPrompt);
+ if( zResult && *zResult ) add_history(zResult);
+#else
+ printf("%s", zPrompt);
+ fflush(stdout);
+ zResult = local_getline(zPrior, stdin);
#endif
+ }
return zResult;
}
@@ -417,7 +447,9 @@ struct previous_mode_data {
struct callback_data {
sqlite3 *db; /* The database */
int echoOn; /* True to echo input commands */
+ int autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
int statsOn; /* True to display memory stats before each finalize */
+ int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
@@ -427,6 +459,7 @@ struct callback_data {
int showHeader; /* True to show column names in List or Column mode */
char *zDestTable; /* Name of destination table when MODE_Insert */
char separator[20]; /* Separator character for MODE_List */
+ char newline[20]; /* Record separator in MODE_Csv */
int colWidth[100]; /* Requested width of each column when in column mode*/
int actualWidth[100]; /* Actual width of each column */
char nullvalue[20]; /* The text to print when a NULL comes back from
@@ -436,9 +469,13 @@ struct callback_data {
** .explain ON */
char outfile[FILENAME_MAX]; /* Filename for *out */
const char *zDbFilename; /* name of the database file */
+ char *zFreeOnClose; /* Filename to free when closing */
const char *zVfs; /* Name of VFS to use */
sqlite3_stmt *pStmt; /* Current statement if any. */
FILE *pLog; /* Write log output here */
+ int *aiIndent; /* Array of indents used in MODE_Explain */
+ int nIndent; /* Size of array aiIndent[] */
+ int iIndent; /* Index of current op in aiIndent[] */
};
/*
@@ -554,7 +591,7 @@ static void output_c_string(FILE *out, const char *z){
}else if( c=='\r' ){
fputc('\\', out);
fputc('r', out);
- }else if( !isprint(c) ){
+ }else if( !isprint(c&0xff) ){
fprintf(out, "\\%03o", c&0xff);
}else{
fputc(c, out);
@@ -569,6 +606,7 @@ static void output_c_string(FILE *out, const char *z){
*/
static void output_html_string(FILE *out, const char *z){
int i;
+ if( z==0 ) z = "";
while( *z ){
for(i=0; z[i]
&& z[i]!='<'
@@ -623,7 +661,8 @@ static const char needCsvQuote[] = {
/*
** Output a single term of CSV. Actually, p->separator is used for
** the separator, which may or may not be a comma. p->nullvalue is
-** the null value. Strings are quoted if necessary.
+** the null value. Strings are quoted if necessary. The separator
+** is only issued if bSep is true.
*/
static void output_csv(struct callback_data *p, const char *z, int bSep){
FILE *out = p->out;
@@ -662,7 +701,8 @@ static void output_csv(struct callback_data *p, const char *z, int bSep){
*/
static void interrupt_handler(int NotUsed){
UNUSED_PARAMETER(NotUsed);
- seenInterrupt = 1;
+ seenInterrupt++;
+ if( seenInterrupt>2 ) exit(1);
if( db ) sqlite3_interrupt(db);
}
#endif
@@ -740,10 +780,15 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
}else{
w = 10;
}
- if( p->mode==MODE_Explain && azArg[i] &&
- strlen30(azArg[i])>w ){
+ if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){
w = strlen30(azArg[i]);
}
+ if( i==1 && p->aiIndent && p->pStmt ){
+ if( p->iIndent<p->nIndent ){
+ fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
+ }
+ p->iIndent++;
+ }
if( w<0 ){
fprintf(p->out,"%*.*s%s",-w,-w,
azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
@@ -813,17 +858,26 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
break;
}
case MODE_Csv: {
+#if defined(WIN32) || defined(_WIN32)
+ fflush(p->out);
+ _setmode(_fileno(p->out), _O_BINARY);
+#endif
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
}
- fprintf(p->out,"\n");
+ fprintf(p->out,"%s",p->newline);
}
- if( azArg==0 ) break;
- for(i=0; i<nArg; i++){
- output_csv(p, azArg[i], i<nArg-1);
+ if( azArg>0 ){
+ for(i=0; i<nArg; i++){
+ output_csv(p, azArg[i], i<nArg-1);
+ }
+ fprintf(p->out,"%s",p->newline);
}
- fprintf(p->out,"\n");
+#if defined(WIN32) || defined(_WIN32)
+ fflush(p->out);
+ _setmode(_fileno(p->out), _O_TEXT);
+#endif
break;
}
case MODE_Insert: {
@@ -837,7 +891,8 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int
}else if( aiType && aiType[i]==SQLITE_TEXT ){
if( zSep[0] ) fprintf(p->out,"%s",zSep);
output_quoted_string(p->out, azArg[i]);
- }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){
+ }else if( aiType && (aiType[i]==SQLITE_INTEGER
+ || aiType[i]==SQLITE_FLOAT) ){
fprintf(p->out,"%s%s",zSep, azArg[i]);
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
@@ -971,10 +1026,10 @@ static int run_table_dump_query(
int nResult;
int i;
const char *z;
- rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0);
+ rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
- p->nErr++;
+ if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
return rc;
}
rc = sqlite3_step(pSelect);
@@ -1001,7 +1056,7 @@ static int run_table_dump_query(
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
- p->nErr++;
+ if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
return rc;
}
@@ -1109,12 +1164,113 @@ static int display_stats(
fprintf(pArg->out, "Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX, bReset);
fprintf(pArg->out, "Autoindex Inserts: %d\n", iCur);
+ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
+ fprintf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
}
return 0;
}
/*
+** Parameter azArray points to a zero-terminated array of strings. zStr
+** points to a single nul-terminated string. Return non-zero if zStr
+** is equal, according to strcmp(), to any of the strings in the array.
+** Otherwise, return zero.
+*/
+static int str_in_array(const char *zStr, const char **azArray){
+ int i;
+ for(i=0; azArray[i]; i++){
+ if( 0==strcmp(zStr, azArray[i]) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** If compiled statement pSql appears to be an EXPLAIN statement, allocate
+** and populate the callback_data.aiIndent[] array with the number of
+** spaces each opcode should be indented before it is output.
+**
+** The indenting rules are:
+**
+** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent
+** all opcodes that occur between the p2 jump destination and the opcode
+** itself by 2 spaces.
+**
+** * For each "Goto", if the jump destination is earlier in the program
+** and ends on one of:
+** Yield SeekGt SeekLt RowSetRead Rewind
+** or if the P1 parameter is one instead of zero,
+** then indent all opcodes between the earlier instruction
+** and "Goto" by 2 spaces.
+*/
+static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){
+ const char *zSql; /* The text of the SQL statement */
+ const char *z; /* Used to check if this is an EXPLAIN */
+ int *abYield = 0; /* True if op is an OP_Yield */
+ int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */
+ int iOp; /* Index of operation in p->aiIndent[] */
+
+ const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", "SorterNext",
+ "NextIfOpen", "PrevIfOpen", 0 };
+ const char *azYield[] = { "Yield", "SeekLT", "SeekGT", "RowSetRead", "Rewind", 0 };
+ const char *azGoto[] = { "Goto", 0 };
+
+ /* Try to figure out if this is really an EXPLAIN statement. If this
+ ** cannot be verified, return early. */
+ zSql = sqlite3_sql(pSql);
+ if( zSql==0 ) return;
+ for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++);
+ if( sqlite3_strnicmp(z, "explain", 7) ) return;
+
+ for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){
+ int i;
+ int iAddr = sqlite3_column_int(pSql, 0);
+ const char *zOp = (const char*)sqlite3_column_text(pSql, 1);
+
+ /* Set p2 to the P2 field of the current opcode. Then, assuming that
+ ** p2 is an instruction address, set variable p2op to the index of that
+ ** instruction in the aiIndent[] array. p2 and p2op may be different if
+ ** the current instruction is part of a sub-program generated by an
+ ** SQL trigger or foreign key. */
+ int p2 = sqlite3_column_int(pSql, 3);
+ int p2op = (p2 + (iOp-iAddr));
+
+ /* Grow the p->aiIndent array as required */
+ if( iOp>=nAlloc ){
+ nAlloc += 100;
+ p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int));
+ abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int));
+ }
+ abYield[iOp] = str_in_array(zOp, azYield);
+ p->aiIndent[iOp] = 0;
+ p->nIndent = iOp+1;
+
+ if( str_in_array(zOp, azNext) ){
+ for(i=p2op; i<iOp; i++) p->aiIndent[i] += 2;
+ }
+ if( str_in_array(zOp, azGoto) && p2op<p->nIndent
+ && (abYield[p2op] || sqlite3_column_int(pSql, 2))
+ ){
+ for(i=p2op+1; i<iOp; i++) p->aiIndent[i] += 2;
+ }
+ }
+
+ p->iIndent = 0;
+ sqlite3_free(abYield);
+ sqlite3_reset(pSql);
+}
+
+/*
+** Free the array allocated by explain_data_prepare().
+*/
+static void explain_data_delete(struct callback_data *p){
+ sqlite3_free(p->aiIndent);
+ p->aiIndent = 0;
+ p->nIndent = 0;
+ p->iIndent = 0;
+}
+
+/*
** Execute a statement or set of statements. Print
** any result rows/columns depending on the current mode
** set via the supplied callback.
@@ -1166,6 +1322,23 @@ static int shell_exec(
fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
+ /* Show the EXPLAIN QUERY PLAN if .eqp is on */
+ if( pArg && pArg->autoEQP ){
+ sqlite3_stmt *pExplain;
+ char *zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", sqlite3_sql(pStmt));
+ rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
+ if( rc==SQLITE_OK ){
+ while( sqlite3_step(pExplain)==SQLITE_ROW ){
+ fprintf(pArg->out,"--EQP-- %d,", sqlite3_column_int(pExplain, 0));
+ fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1));
+ fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
+ fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
+ }
+ }
+ sqlite3_finalize(pExplain);
+ sqlite3_free(zEQP);
+ }
+
/* Output TESTCTRL_EXPLAIN text of requested */
if( pArg && pArg->mode==MODE_Explain ){
const char *zExplain = 0;
@@ -1175,6 +1348,12 @@ static int shell_exec(
}
}
+ /* If the shell is currently in ".explain" mode, gather the extra
+ ** data required to add indents to the output.*/
+ if( pArg && pArg->mode==MODE_Explain ){
+ explain_data_prepare(pArg, pStmt);
+ }
+
/* perform the first step. this will tell us if we
** have a result set or not and how wide it is.
*/
@@ -1192,7 +1371,7 @@ static int shell_exec(
char **azCols = (char **)pData; /* Names of result columns */
char **azVals = &azCols[nCol]; /* Results */
int *aiTypes = (int *)&azVals[nCol]; /* Result types */
- int i;
+ int i, x;
assert(sizeof(int) <= sizeof(char *));
/* save off ptrs to column names */
for(i=0; i<nCol; i++){
@@ -1201,8 +1380,12 @@ static int shell_exec(
do{
/* extract the data and data types */
for(i=0; i<nCol; i++){
- azVals[i] = (char *)sqlite3_column_text(pStmt, i);
- aiTypes[i] = sqlite3_column_type(pStmt, i);
+ aiTypes[i] = x = sqlite3_column_type(pStmt, i);
+ if( x==SQLITE_BLOB && pArg && pArg->mode==MODE_Insert ){
+ azVals[i] = "";
+ }else{
+ azVals[i] = (char*)sqlite3_column_text(pStmt, i);
+ }
if( !azVals[i] && (aiTypes[i]!=SQLITE_NULL) ){
rc = SQLITE_NOMEM;
break; /* from for */
@@ -1228,6 +1411,8 @@ static int shell_exec(
}
}
+ explain_data_delete(pArg);
+
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
display_stats(db, pArg, 0);
@@ -1278,7 +1463,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
if( strcmp(zTable, "sqlite_sequence")==0 ){
zPrepStmt = "DELETE FROM sqlite_sequence;\n";
- }else if( strcmp(zTable, "sqlite_stat1")==0 ){
+ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
fprintf(p->out, "ANALYZE sqlite_master;\n");
}else if( strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
@@ -1310,7 +1495,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zTableInfo = appendText(zTableInfo, zTable, '"');
zTableInfo = appendText(zTableInfo, ");", 0);
- rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
+ rc = sqlite3_prepare_v2(p->db, zTableInfo, -1, &pTableInfo, 0);
free(zTableInfo);
if( rc!=SQLITE_OK || !pTableInfo ){
return 1;
@@ -1399,16 +1584,19 @@ static int run_schema_dump_query(
*/
static char zHelp[] =
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
- ".bail ON|OFF Stop after hitting an error. Default OFF\n"
+ ".bail on|off Stop after hitting an error. Default OFF\n"
+ ".clone NEWDB Clone data into NEWDB from the existing database\n"
".databases List names and files of attached databases\n"
".dump ?TABLE? ... Dump the database in an SQL text format\n"
" If TABLE specified, only dump tables matching\n"
" LIKE pattern TABLE.\n"
- ".echo ON|OFF Turn command echo on or off\n"
+ ".echo on|off Turn command echo on or off\n"
+ ".eqp on|off Enable or disable automatic EXPLAIN QUERY PLAN\n"
".exit Exit this program\n"
- ".explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.\n"
+ ".explain ?on|off? Turn output mode suitable for EXPLAIN on or off.\n"
" With no args, it turns EXPLAIN on.\n"
- ".header(s) ON|OFF Turn display of headers on or off\n"
+ ".fullschema Show schema and the content of sqlite_stat tables\n"
+ ".headers on|off Turn display of headers on or off\n"
".help Show this message\n"
".import FILE TABLE Import data from FILE into TABLE\n"
".indices ?TABLE? Show names of all indices\n"
@@ -1431,40 +1619,103 @@ static char zHelp[] =
" tabs Tab-separated values\n"
" tcl TCL list elements\n"
".nullvalue STRING Use STRING in place of NULL values\n"
- ".output FILENAME Send output to FILENAME\n"
- ".output stdout Send output to the screen\n"
+ ".once FILENAME Output for the next SQL command only to FILENAME\n"
+ ".open ?FILENAME? Close existing database and reopen FILENAME\n"
+ ".output ?FILENAME? Send output to FILENAME or stdout\n"
".print STRING... Print literal STRING\n"
".prompt MAIN CONTINUE Replace the standard prompts\n"
".quit Exit this program\n"
".read FILENAME Execute SQL in FILENAME\n"
".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n"
+ ".save FILE Write in-memory database into FILE\n"
".schema ?TABLE? Show the CREATE statements\n"
" If TABLE specified, only show tables matching\n"
" LIKE pattern TABLE.\n"
- ".separator STRING Change separator used by output mode and .import\n"
+ ".separator STRING ?NL? Change separator used by output mode and .import\n"
+ " NL is the end-of-line mark for CSV\n"
+ ".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\n"
- ".stats ON|OFF Turn stats on or off\n"
+ ".stats on|off Turn stats on or off\n"
+ ".system CMD ARGS... Run CMD ARGS... in a system shell\n"
".tables ?TABLE? List names of tables\n"
" If TABLE specified, only list tables matching\n"
" LIKE pattern TABLE.\n"
".timeout MS Try opening locked tables for MS milliseconds\n"
+ ".timer on|off Turn SQL timer on or off\n"
".trace FILE|off Output each SQL statement as it is run\n"
".vfsname ?AUX? Print the name of the VFS stack\n"
".width NUM1 NUM2 ... Set column widths for \"column\" mode\n"
-;
-
-static char zTimerHelp[] =
- ".timer ON|OFF Turn the CPU timer measurement on or off\n"
+ " Negative values right-justify\n"
;
/* Forward reference */
static int process_input(struct callback_data *p, FILE *in);
+/*
+** Implementation of the "readfile(X)" SQL function. The entire content
+** of the file named X is read and returned as a BLOB. NULL is returned
+** if the file does not exist or is unreadable.
+*/
+static void readfileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *zName;
+ FILE *in;
+ long nIn;
+ void *pBuf;
+
+ zName = (const char*)sqlite3_value_text(argv[0]);
+ if( zName==0 ) return;
+ in = fopen(zName, "rb");
+ if( in==0 ) return;
+ fseek(in, 0, SEEK_END);
+ nIn = ftell(in);
+ rewind(in);
+ pBuf = sqlite3_malloc( nIn );
+ if( pBuf && 1==fread(pBuf, nIn, 1, in) ){
+ sqlite3_result_blob(context, pBuf, nIn, sqlite3_free);
+ }else{
+ sqlite3_free(pBuf);
+ }
+ fclose(in);
+}
+
+/*
+** Implementation of the "writefile(X,Y)" SQL function. The argument Y
+** is written into file X. The number of bytes written is returned. Or
+** NULL is returned if something goes wrong, such as being unable to open
+** file X for writing.
+*/
+static void writefileFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ FILE *out;
+ const char *z;
+ sqlite3_int64 rc;
+ const char *zFile;
+
+ zFile = (const char*)sqlite3_value_text(argv[0]);
+ if( zFile==0 ) return;
+ out = fopen(zFile, "wb");
+ if( out==0 ) return;
+ z = (const char*)sqlite3_value_blob(argv[1]);
+ if( z==0 ){
+ rc = 0;
+ }else{
+ rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
+ }
+ fclose(out);
+ sqlite3_result_int64(context, rc);
+}
/*
** Make sure the database is open. If it is not, then open it. If
** the database fails to open, print an error message and exit.
*/
-static void open_db(struct callback_data *p){
+static void open_db(struct callback_data *p, int keepAlive){
if( p->db==0 ){
sqlite3_initialize();
sqlite3_open(p->zDbFilename, &p->db);
@@ -1476,11 +1727,16 @@ static void open_db(struct callback_data *p){
if( db==0 || SQLITE_OK!=sqlite3_errcode(db) ){
fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(db));
+ if( keepAlive ) return;
exit(1);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
+ sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
+ readfileFunc, 0, 0);
+ sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
+ writefileFunc, 0, 0);
}
}
@@ -1490,12 +1746,14 @@ static void open_db(struct callback_data *p){
** \t -> tab
** \n -> newline
** \r -> carriage return
+** \" -> "
** \NNN -> ascii character NNN in octal
** \\ -> backslash
*/
static void resolve_backslashes(char *z){
int i, j;
char c;
+ while( *z && *z!='\\' ) z++;
for(i=j=0; (c = z[i])!=0; i++, j++){
if( c=='\\' ){
c = z[++i];
@@ -1505,6 +1763,8 @@ static void resolve_backslashes(char *z){
c = '\t';
}else if( c=='r' ){
c = '\r';
+ }else if( c=='\\' ){
+ c = '\\';
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
@@ -1519,25 +1779,18 @@ static void resolve_backslashes(char *z){
}
z[j] = c;
}
- z[j] = 0;
+ if( j<i ) z[j] = 0;
}
/*
-** Interpret zArg as a boolean value. Return either 0 or 1.
+** Return the value of a hexadecimal digit. Return -1 if the input
+** is not a hex digit.
*/
-static int booleanValue(char *zArg){
- int i;
- for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
- if( i>0 && zArg[i]==0 ) return atoi(zArg);
- if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
- return 1;
- }
- if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
- return 0;
- }
- fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
- zArg);
- return 0;
+static int hexDigitValue(char c){
+ if( c>='0' && c<='9' ) return c - '0';
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
+ return -1;
}
/*
@@ -1564,11 +1817,20 @@ static sqlite3_int64 integerValue(const char *zArg){
}else if( zArg[0]=='+' ){
zArg++;
}
- while( isdigit(zArg[0]) ){
- v = v*10 + zArg[0] - '0';
- zArg++;
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ int x;
+ zArg += 2;
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
+ v = (v<<4) + x;
+ zArg++;
+ }
+ }else{
+ while( IsDigit(zArg[0]) ){
+ v = v*10 + zArg[0] - '0';
+ zArg++;
+ }
}
- for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
+ for(i=0; i<ArraySize(aMult); i++){
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
v *= aMult[i].iMult;
break;
@@ -1578,6 +1840,29 @@ static sqlite3_int64 integerValue(const char *zArg){
}
/*
+** Interpret zArg as either an integer or a boolean value. Return 1 or 0
+** for TRUE and FALSE. Return the integer value if appropriate.
+*/
+static int booleanValue(char *zArg){
+ int i;
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ for(i=2; hexDigitValue(zArg[i])>=0; i++){}
+ }else{
+ for(i=0; zArg[i]>='0' && zArg[i]<='9'; i++){}
+ }
+ if( i>0 && zArg[i]==0 ) return (int)(integerValue(zArg) & 0xffffffff);
+ if( sqlite3_stricmp(zArg, "on")==0 || sqlite3_stricmp(zArg,"yes")==0 ){
+ return 1;
+ }
+ if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
+ return 0;
+ }
+ fprintf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
+ zArg);
+ return 0;
+}
+
+/*
** Close an output file, assuming it is not stderr or stdout
*/
static void output_file_close(FILE *f){
@@ -1624,6 +1909,334 @@ static void test_breakpoint(void){
}
/*
+** An object used to read a CSV file
+*/
+typedef struct CSVReader CSVReader;
+struct CSVReader {
+ const char *zFile; /* Name of the input file */
+ FILE *in; /* Read the CSV text from this input stream */
+ char *z; /* Accumulated text for a field */
+ int n; /* Number of bytes in z */
+ int nAlloc; /* Space allocated for z[] */
+ int nLine; /* Current line number */
+ int cTerm; /* Character that terminated the most recent field */
+ int cSeparator; /* The separator character. (Usually ",") */
+};
+
+/* Append a single byte to z[] */
+static void csv_append_char(CSVReader *p, int c){
+ if( p->n+1>=p->nAlloc ){
+ p->nAlloc += p->nAlloc + 100;
+ p->z = sqlite3_realloc(p->z, p->nAlloc);
+ if( p->z==0 ){
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+ p->z[p->n++] = (char)c;
+}
+
+/* Read a single field of CSV text. Compatible with rfc4180 and extended
+** with the option of having a separator other than ",".
+**
+** + Input comes from p->in.
+** + Store results in p->z of length p->n. Space to hold p->z comes
+** from sqlite3_malloc().
+** + Use p->cSep as the separator. The default is ",".
+** + Keep track of the line number in p->nLine.
+** + Store the character that terminates the field in p->cTerm. Store
+** EOF on end-of-file.
+** + Report syntax errors on stderr
+*/
+static char *csv_read_one_field(CSVReader *p){
+ int c, pc, ppc;
+ int cSep = p->cSeparator;
+ p->n = 0;
+ c = fgetc(p->in);
+ if( c==EOF || seenInterrupt ){
+ p->cTerm = EOF;
+ return 0;
+ }
+ if( c=='"' ){
+ int startLine = p->nLine;
+ int cQuote = c;
+ pc = ppc = 0;
+ while( 1 ){
+ c = fgetc(p->in);
+ if( c=='\n' ) p->nLine++;
+ if( c==cQuote ){
+ if( pc==cQuote ){
+ pc = 0;
+ continue;
+ }
+ }
+ if( (c==cSep && pc==cQuote)
+ || (c=='\n' && pc==cQuote)
+ || (c=='\n' && pc=='\r' && ppc==cQuote)
+ || (c==EOF && pc==cQuote)
+ ){
+ do{ p->n--; }while( p->z[p->n]!=cQuote );
+ p->cTerm = c;
+ break;
+ }
+ if( pc==cQuote && c!='\r' ){
+ fprintf(stderr, "%s:%d: unescaped %c character\n",
+ p->zFile, p->nLine, cQuote);
+ }
+ if( c==EOF ){
+ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n",
+ p->zFile, startLine, cQuote);
+ p->cTerm = EOF;
+ break;
+ }
+ csv_append_char(p, c);
+ ppc = pc;
+ pc = c;
+ }
+ }else{
+ while( c!=EOF && c!=cSep && c!='\n' ){
+ csv_append_char(p, c);
+ c = fgetc(p->in);
+ }
+ if( c=='\n' ){
+ p->nLine++;
+ if( p->n>0 && p->z[p->n-1]=='\r' ) p->n--;
+ }
+ p->cTerm = c;
+ }
+ if( p->z ) p->z[p->n] = 0;
+ return p->z;
+}
+
+/*
+** Try to transfer data for table zTable. If an error is seen while
+** moving forward, try to go backwards. The backwards movement won't
+** work for WITHOUT ROWID tables.
+*/
+static void tryToCloneData(
+ struct callback_data *p,
+ sqlite3 *newDb,
+ const char *zTable
+){
+ sqlite3_stmt *pQuery = 0;
+ sqlite3_stmt *pInsert = 0;
+ char *zQuery = 0;
+ char *zInsert = 0;
+ int rc;
+ int i, j, n;
+ int nTable = (int)strlen(zTable);
+ int k = 0;
+ int cnt = 0;
+ const int spinRate = 10000;
+
+ zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_data_xfer;
+ }
+ n = sqlite3_column_count(pQuery);
+ zInsert = sqlite3_malloc(200 + nTable + n*3);
+ if( zInsert==0 ){
+ fprintf(stderr, "out of memory\n");
+ goto end_data_xfer;
+ }
+ sqlite3_snprintf(200+nTable,zInsert,
+ "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
+ i = (int)strlen(zInsert);
+ for(j=1; j<n; j++){
+ memcpy(zInsert+i, ",?", 2);
+ i += 2;
+ }
+ memcpy(zInsert+i, ");", 3);
+ rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
+ if( rc ){
+ fprintf(stderr, "Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb),
+ zQuery);
+ goto end_data_xfer;
+ }
+ for(k=0; k<2; k++){
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ for(i=0; i<n; i++){
+ switch( sqlite3_column_type(pQuery, i) ){
+ case SQLITE_NULL: {
+ sqlite3_bind_null(pInsert, i+1);
+ break;
+ }
+ case SQLITE_INTEGER: {
+ sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i));
+ break;
+ }
+ case SQLITE_TEXT: {
+ sqlite3_bind_text(pInsert, i+1,
+ (const char*)sqlite3_column_text(pQuery,i),
+ -1, SQLITE_STATIC);
+ break;
+ }
+ case SQLITE_BLOB: {
+ sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i),
+ sqlite3_column_bytes(pQuery,i),
+ SQLITE_STATIC);
+ break;
+ }
+ }
+ } /* End for */
+ rc = sqlite3_step(pInsert);
+ if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+ fprintf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb),
+ sqlite3_errmsg(newDb));
+ }
+ sqlite3_reset(pInsert);
+ cnt++;
+ if( (cnt%spinRate)==0 ){
+ printf("%c\b", "|/-\\"[(cnt/spinRate)%4]);
+ fflush(stdout);
+ }
+ } /* End while */
+ if( rc==SQLITE_DONE ) break;
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+ zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;",
+ zTable);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Warning: cannot step \"%s\" backwards", zTable);
+ break;
+ }
+ } /* End for(k=0...) */
+
+end_data_xfer:
+ sqlite3_finalize(pQuery);
+ sqlite3_finalize(pInsert);
+ sqlite3_free(zQuery);
+ sqlite3_free(zInsert);
+}
+
+
+/*
+** Try to transfer all rows of the schema that match zWhere. For
+** each row, invoke xForEach() on the object defined by that row.
+** If an error is encountered while moving forward through the
+** sqlite_master table, try again moving backwards.
+*/
+static void tryToCloneSchema(
+ struct callback_data *p,
+ sqlite3 *newDb,
+ const char *zWhere,
+ void (*xForEach)(struct callback_data*,sqlite3*,const char*)
+){
+ sqlite3_stmt *pQuery = 0;
+ char *zQuery = 0;
+ int rc;
+ const unsigned char *zName;
+ const unsigned char *zSql;
+ char *zErrMsg = 0;
+
+ zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master"
+ " WHERE %s", zWhere);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error: (%d) %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_schema_xfer;
+ }
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ zName = sqlite3_column_text(pQuery, 0);
+ zSql = sqlite3_column_text(pQuery, 1);
+ printf("%s... ", zName); fflush(stdout);
+ sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }
+ if( xForEach ){
+ xForEach(p, newDb, (const char*)zName);
+ }
+ printf("done\n");
+ }
+ if( rc!=SQLITE_DONE ){
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+ zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_master"
+ " WHERE %s ORDER BY rowid DESC", zWhere);
+ rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
+ if( rc ){
+ fprintf(stderr, "Error: (%d) %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
+ zQuery);
+ goto end_schema_xfer;
+ }
+ while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
+ zName = sqlite3_column_text(pQuery, 0);
+ zSql = sqlite3_column_text(pQuery, 1);
+ printf("%s... ", zName); fflush(stdout);
+ sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }
+ if( xForEach ){
+ xForEach(p, newDb, (const char*)zName);
+ }
+ printf("done\n");
+ }
+ }
+end_schema_xfer:
+ sqlite3_finalize(pQuery);
+ sqlite3_free(zQuery);
+}
+
+/*
+** Open a new database file named "zNewDb". Try to recover as much information
+** as possible out of the main database (which might be corrupt) and write it
+** into zNewDb.
+*/
+static void tryToClone(struct callback_data *p, const char *zNewDb){
+ int rc;
+ sqlite3 *newDb = 0;
+ if( access(zNewDb,0)==0 ){
+ fprintf(stderr, "File \"%s\" already exists.\n", zNewDb);
+ return;
+ }
+ rc = sqlite3_open(zNewDb, &newDb);
+ if( rc ){
+ fprintf(stderr, "Cannot create output database: %s\n",
+ sqlite3_errmsg(newDb));
+ }else{
+ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
+ sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0);
+ tryToCloneSchema(p, newDb, "type='table'", tryToCloneData);
+ tryToCloneSchema(p, newDb, "type!='table'", 0);
+ sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
+ sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
+ }
+ sqlite3_close(newDb);
+}
+
+/*
+** Change the output file back to stdout
+*/
+static void output_reset(struct callback_data *p){
+ if( p->outfile[0]=='|' ){
+ pclose(p->out);
+ }else{
+ output_file_close(p->out);
+ }
+ p->outfile[0] = 0;
+ p->out = stdout;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -1644,7 +2257,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( zLine[i]=='\'' || zLine[i]=='"' ){
int delim = zLine[i++];
azArg[nArg++] = &zLine[i];
- while( zLine[i] && zLine[i]!=delim ){ i++; }
+ while( zLine[i] && zLine[i]!=delim ){
+ if( zLine[i]=='\\' && delim=='"' && zLine[i+1]!=0 ) i++;
+ i++;
+ }
if( zLine[i]==delim ){
zLine[i++] = 0;
}
@@ -1662,10 +2278,11 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( nArg==0 ) return 0; /* no tokens, no error */
n = strlen30(azArg[0]);
c = azArg[0][0];
- if( c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0 ){
+ if( (c=='b' && n>=3 && strncmp(azArg[0], "backup", n)==0)
+ || (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
+ ){
const char *zDestFile = 0;
const char *zDb = 0;
- const char *zKey = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
@@ -1673,9 +2290,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
const char *z = azArg[j];
if( z[0]=='-' ){
while( z[0]=='-' ) z++;
- if( strcmp(z,"key")==0 && j<nArg-1 ){
- zKey = azArg[++j];
- }else
+ /* No options to process at this time */
{
fprintf(stderr, "unknown option: %s\n", azArg[j]);
return 1;
@@ -1701,12 +2316,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_close(pDest);
return 1;
}
-#ifdef SQLITE_HAS_CODEC
- sqlite3_key(pDest, zKey, (int)strlen(zKey));
-#else
- (void)zKey;
-#endif
- open_db(p);
+ open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
@@ -1724,8 +2334,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_close(pDest);
}else
- if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 && nArg>1 && nArg<3 ){
- bail_on_error = booleanValue(azArg[1]);
+ if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
+ if( nArg==2 ){
+ bail_on_error = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .bail on|off\n");
+ rc = 1;
+ }
}else
/* The undocumented ".breakpoint" command causes a call to the no-op
@@ -1735,10 +2350,19 @@ static int do_meta_command(char *zLine, struct callback_data *p){
test_breakpoint();
}else
- if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){
+ if( c=='c' && strncmp(azArg[0], "clone", n)==0 ){
+ if( nArg==2 ){
+ tryToClone(p, azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .clone FILENAME\n");
+ rc = 1;
+ }
+ }else
+
+ if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
- open_db(p);
+ open_db(p, 0);
memcpy(&data, p, sizeof(data));
data.showHeader = 1;
data.mode = MODE_Column;
@@ -1754,11 +2378,16 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='d' && strncmp(azArg[0], "dump", n)==0 && nArg<3 ){
- open_db(p);
+ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
+ open_db(p, 0);
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
+ if( nArg!=1 && nArg!=2 ){
+ fprintf(stderr, "Usage: .dump ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
fprintf(p->out, "PRAGMA foreign_keys=OFF;\n");
fprintf(p->out, "BEGIN TRANSACTION;\n");
p->writableSchema = 0;
@@ -1803,16 +2432,30 @@ static int do_meta_command(char *zLine, struct callback_data *p){
fprintf(p->out, p->nErr ? "ROLLBACK; -- due to errors\n" : "COMMIT;\n");
}else
- if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 && nArg<3 ){
- p->echoOn = booleanValue(azArg[1]);
+ if( c=='e' && strncmp(azArg[0], "echo", n)==0 ){
+ if( nArg==2 ){
+ p->echoOn = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .echo on|off\n");
+ rc = 1;
+ }
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
+ if( nArg==2 ){
+ p->autoEQP = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .eqp on|off\n");
+ rc = 1;
+ }
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
- if( nArg>1 && (rc = atoi(azArg[1]))!=0 ) exit(rc);
+ if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
rc = 2;
}else
- if( c=='e' && strncmp(azArg[0], "explain", n)==0 && nArg<3 ){
+ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
int val = nArg>=2 ? booleanValue(azArg[1]) : 1;
if(val == 1) {
if(!p->explainPrev.valid) {
@@ -1830,7 +2473,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
*/
p->mode = MODE_Explain;
p->showHeader = 1;
- memset(p->colWidth,0,ArraySize(p->colWidth));
+ memset(p->colWidth,0,sizeof(p->colWidth));
p->colWidth[0] = 4; /* addr */
p->colWidth[1] = 13; /* opcode */
p->colWidth[2] = 4; /* P1 */
@@ -1847,62 +2490,171 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='h' && (strncmp(azArg[0], "header", n)==0 ||
- strncmp(azArg[0], "headers", n)==0) && nArg>1 && nArg<3 ){
- p->showHeader = booleanValue(azArg[1]);
+ if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ int doStats = 0;
+ if( nArg!=1 ){
+ fprintf(stderr, "Usage: .fullschema\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ open_db(p, 0);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_Semi;
+ rc = sqlite3_exec(p->db,
+ "SELECT sql FROM"
+ " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
+ " FROM sqlite_master UNION ALL"
+ " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
+ "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'"
+ "ORDER BY rowid",
+ callback, &data, &zErrMsg
+ );
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare_v2(p->db,
+ "SELECT rowid FROM sqlite_master"
+ " WHERE name GLOB 'sqlite_stat[134]'",
+ -1, &pStmt, 0);
+ doStats = sqlite3_step(pStmt)==SQLITE_ROW;
+ sqlite3_finalize(pStmt);
+ }
+ if( doStats==0 ){
+ fprintf(p->out, "/* No STAT tables available */\n");
+ }else{
+ fprintf(p->out, "ANALYZE sqlite_master;\n");
+ sqlite3_exec(p->db, "SELECT 'ANALYZE sqlite_master'",
+ callback, &data, &zErrMsg);
+ data.mode = MODE_Insert;
+ data.zDestTable = "sqlite_stat1";
+ shell_exec(p->db, "SELECT * FROM sqlite_stat1",
+ shell_callback, &data,&zErrMsg);
+ data.zDestTable = "sqlite_stat3";
+ shell_exec(p->db, "SELECT * FROM sqlite_stat3",
+ shell_callback, &data,&zErrMsg);
+ data.zDestTable = "sqlite_stat4";
+ shell_exec(p->db, "SELECT * FROM sqlite_stat4",
+ shell_callback, &data, &zErrMsg);
+ fprintf(p->out, "ANALYZE sqlite_master;\n");
+ }
+ }else
+
+ if( c=='h' && strncmp(azArg[0], "headers", n)==0 ){
+ if( nArg==2 ){
+ p->showHeader = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .headers on|off\n");
+ rc = 1;
+ }
}else
if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
- fprintf(stderr,"%s",zHelp);
- if( HAS_TIMER ){
- fprintf(stderr,"%s",zTimerHelp);
- }
+ fprintf(p->out, "%s", zHelp);
}else
- if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
- char *zTable = azArg[2]; /* Insert data into this table */
- char *zFile = azArg[1]; /* The file from which to extract data */
+ if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
+ char *zTable; /* Insert data into this table */
+ char *zFile; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* 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 needCommit; /* True to COMMIT or ROLLBACK at end */
int nSep; /* Number of bytes in p->separator[] */
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 */
+ CSVReader sCsv; /* Reader context */
+ int (*xCloser)(FILE*); /* Procedure to close th3 connection */
- open_db(p);
+ if( nArg!=3 ){
+ fprintf(stderr, "Usage: .import FILE TABLE\n");
+ goto meta_command_exit;
+ }
+ zFile = azArg[1];
+ zTable = azArg[2];
+ seenInterrupt = 0;
+ memset(&sCsv, 0, sizeof(sCsv));
+ open_db(p, 0);
nSep = strlen30(p->separator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null separator required for import\n");
return 1;
}
+ if( nSep>1 ){
+ fprintf(stderr, "Error: multi-character separators not allowed"
+ " for import\n");
+ return 1;
+ }
+ sCsv.zFile = zFile;
+ sCsv.nLine = 1;
+ if( sCsv.zFile[0]=='|' ){
+ sCsv.in = popen(sCsv.zFile+1, "r");
+ sCsv.zFile = "<pipe>";
+ xCloser = pclose;
+ }else{
+ sCsv.in = fopen(sCsv.zFile, "rb");
+ xCloser = fclose;
+ }
+ if( sCsv.in==0 ){
+ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ return 1;
+ }
+ sCsv.cSeparator = p->separator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
nByte = strlen30(zSql);
- rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ csv_append_char(&sCsv, 0); /* To ensure sCsv.z is allocated */
+ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
+ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
+ char cSep = '(';
+ while( csv_read_one_field(&sCsv) ){
+ zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z);
+ cSep = ',';
+ if( sCsv.cTerm!=sCsv.cSeparator ) break;
+ }
+ if( cSep=='(' ){
+ sqlite3_free(zCreate);
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ fprintf(stderr,"%s: empty file\n", sCsv.zFile);
+ return 1;
+ }
+ zCreate = sqlite3_mprintf("%z\n)", zCreate);
+ rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+ sqlite3_free(zCreate);
+ if( rc ){
+ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
+ sqlite3_errmsg(db));
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ return 1;
+ }
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ }
sqlite3_free(zSql);
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ xCloser(sCsv.in);
return 1;
}
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
- zSql = malloc( nByte + 20 + nCol*2 );
+ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
@@ -1910,86 +2662,59 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
zSql[j++] = ')';
zSql[j] = 0;
- rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
- free(zSql);
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if (pStmt) sqlite3_finalize(pStmt);
+ xCloser(sCsv.in);
return 1;
}
- in = fopen(zFile, "rb");
- if( in==0 ){
- fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
- sqlite3_finalize(pStmt);
- return 1;
- }
- azCol = malloc( sizeof(azCol[0])*(nCol+1) );
- if( azCol==0 ){
- fprintf(stderr, "Error: out of memory\n");
- fclose(in);
- sqlite3_finalize(pStmt);
- return 1;
- }
- sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
- zCommit = "COMMIT";
- while( (zLine = local_getline(0, in, 1))!=0 ){
- char *z, c;
- int inQuote = 0;
- lineno++;
- azCol[0] = zLine;
- for(i=0, z=zLine; (c = *z)!=0; z++){
- if( c=='"' ) inQuote = !inQuote;
- if( c=='\n' ) lineno++;
- if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
- *z = 0;
+ needCommit = sqlite3_get_autocommit(db);
+ if( needCommit ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
+ do{
+ int startLine = sCsv.nLine;
+ for(i=0; i<nCol; i++){
+ char *z = csv_read_one_field(&sCsv);
+ if( z==0 && i==0 ) break;
+ sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
+ if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "filling the rest with NULL\n",
+ sCsv.zFile, startLine, nCol, i+1);
i++;
- if( i<nCol ){
- azCol[i] = &z[nSep];
- z += nSep-1;
- }
+ while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
- } /* end for */
- *z = 0;
- if( i+1!=nCol ){
- fprintf(stderr,
- "Error: %s line %d: expected %d columns of data but found %d\n",
- zFile, lineno, nCol, i+1);
- zCommit = "ROLLBACK";
- free(zLine);
- rc = 1;
- break; /* from while */
}
- for(i=0; i<nCol; i++){
- if( azCol[i][0]=='"' ){
- int k;
- for(z=azCol[i], j=1, k=0; z[j]; j++){
- if( z[j]=='"' ){ j++; if( z[j]==0 ) break; }
- z[k++] = z[j];
- }
- z[k] = 0;
+ if( sCsv.cTerm==sCsv.cSeparator ){
+ do{
+ csv_read_one_field(&sCsv);
+ i++;
+ }while( sCsv.cTerm==sCsv.cSeparator );
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "extras ignored\n",
+ sCsv.zFile, startLine, nCol, i);
+ }
+ if( i>=nCol ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine,
+ sqlite3_errmsg(db));
}
- sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
- }
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
- free(zLine);
- if( rc!=SQLITE_OK ){
- fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
- zCommit = "ROLLBACK";
- rc = 1;
- break; /* from while */
}
- } /* end while */
- free(azCol);
- fclose(in);
+ }while( sCsv.cTerm!=EOF );
+
+ xCloser(sCsv.in);
+ sqlite3_free(sCsv.z);
sqlite3_finalize(pStmt);
- sqlite3_exec(p->db, zCommit, 0, 0, 0);
+ if( needCommit ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
}else
- if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
+ if( c=='i' && strncmp(azArg[0], "indices", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
- open_db(p);
+ open_db(p, 0);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_List;
@@ -2003,7 +2728,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
"ORDER BY 1",
callback, &data, &zErrMsg
);
- }else{
+ }else if( nArg==2 ){
zShellStatic = azArg[1];
rc = sqlite3_exec(p->db,
"SELECT name FROM sqlite_master "
@@ -2015,6 +2740,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
callback, &data, &zErrMsg
);
zShellStatic = 0;
+ }else{
+ fprintf(stderr, "Usage: .indices ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
@@ -2050,12 +2779,17 @@ static int do_meta_command(char *zLine, struct callback_data *p){
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
- if( c=='l' && strncmp(azArg[0], "load", n)==0 && nArg>=2 ){
+ if( c=='l' && strncmp(azArg[0], "load", n)==0 ){
const char *zFile, *zProc;
char *zErrMsg = 0;
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
zFile = azArg[1];
zProc = nArg>=3 ? azArg[2] : 0;
- open_db(p);
+ open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
fprintf(stderr, "Error: %s\n", zErrMsg);
@@ -2065,38 +2799,42 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}else
#endif
- if( c=='l' && strncmp(azArg[0], "log", n)==0 && nArg>=2 ){
- const char *zFile = azArg[1];
- output_file_close(p->pLog);
- p->pLog = output_file_open(zFile);
+ if( c=='l' && strncmp(azArg[0], "log", n)==0 ){
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .log FILENAME\n");
+ rc = 1;
+ }else{
+ const char *zFile = azArg[1];
+ output_file_close(p->pLog);
+ p->pLog = output_file_open(zFile);
+ }
}else
- if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){
- int n2 = strlen30(azArg[1]);
- if( (n2==4 && strncmp(azArg[1],"line",n2)==0)
- ||
- (n2==5 && strncmp(azArg[1],"lines",n2)==0) ){
+ if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){
+ const char *zMode = nArg>=2 ? azArg[1] : "";
+ int n2 = (int)strlen(zMode);
+ int c2 = zMode[0];
+ if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
p->mode = MODE_Line;
- }else if( (n2==6 && strncmp(azArg[1],"column",n2)==0)
- ||
- (n2==7 && strncmp(azArg[1],"columns",n2)==0) ){
+ }else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
p->mode = MODE_Column;
- }else if( n2==4 && strncmp(azArg[1],"list",n2)==0 ){
+ }else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
p->mode = MODE_List;
- }else if( n2==4 && strncmp(azArg[1],"html",n2)==0 ){
+ }else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
p->mode = MODE_Html;
- }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){
+ }else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
p->mode = MODE_Tcl;
sqlite3_snprintf(sizeof(p->separator), p->separator, " ");
- }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){
+ }else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
p->mode = MODE_Csv;
sqlite3_snprintf(sizeof(p->separator), p->separator, ",");
- }else if( n2==4 && strncmp(azArg[1],"tabs",n2)==0 ){
+ sqlite3_snprintf(sizeof(p->newline), p->newline, "\r\n");
+ }else if( c2=='t' && strncmp(azArg[1],"tabs",n2)==0 ){
p->mode = MODE_List;
sqlite3_snprintf(sizeof(p->separator), p->separator, "\t");
- }else if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
+ }else if( c2=='i' && strncmp(azArg[1],"insert",n2)==0 ){
p->mode = MODE_Insert;
- set_table_name(p, "table");
+ set_table_name(p, nArg>=3 ? azArg[2] : "table");
}else {
fprintf(stderr,"Error: mode should be one of: "
"column csv html insert line list tabs tcl\n");
@@ -2104,49 +2842,75 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==3 ){
- int n2 = strlen30(azArg[1]);
- if( n2==6 && strncmp(azArg[1],"insert",n2)==0 ){
- p->mode = MODE_Insert;
- set_table_name(p, azArg[2]);
- }else {
- fprintf(stderr, "Error: invalid arguments: "
- " \"%s\". Enter \".help\" for help\n", azArg[2]);
+ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 ){
+ if( nArg==2 ){
+ sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
+ "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .nullvalue STRING\n");
rc = 1;
}
}else
- if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
- sqlite3_snprintf(sizeof(p->nullvalue), p->nullvalue,
- "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
+ if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
+ sqlite3 *savedDb = p->db;
+ const char *zSavedFilename = p->zDbFilename;
+ char *zNewFilename = 0;
+ p->db = 0;
+ if( nArg>=2 ){
+ p->zDbFilename = zNewFilename = sqlite3_mprintf("%s", azArg[1]);
+ }
+ open_db(p, 1);
+ if( p->db!=0 ){
+ sqlite3_close(savedDb);
+ sqlite3_free(p->zFreeOnClose);
+ p->zFreeOnClose = zNewFilename;
+ }else{
+ sqlite3_free(zNewFilename);
+ p->db = savedDb;
+ p->zDbFilename = zSavedFilename;
+ }
}else
- if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
- if( p->outfile[0]=='|' ){
- pclose(p->out);
+ if( c=='o'
+ && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0)
+ ){
+ const char *zFile = nArg>=2 ? azArg[1] : "stdout";
+ if( nArg>2 ){
+ fprintf(stderr, "Usage: .%s FILE\n", azArg[0]);
+ rc = 1;
+ goto meta_command_exit;
+ }
+ if( n>1 && strncmp(azArg[0], "once", n)==0 ){
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .once FILE\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ p->outCount = 2;
}else{
- output_file_close(p->out);
+ p->outCount = 0;
}
- p->outfile[0] = 0;
- if( azArg[1][0]=='|' ){
- p->out = popen(&azArg[1][1], "w");
+ output_reset(p);
+ if( zFile[0]=='|' ){
+ p->out = popen(zFile + 1, "w");
if( p->out==0 ){
- fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]);
+ fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
p->out = stdout;
rc = 1;
}else{
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}else{
- p->out = output_file_open(azArg[1]);
+ p->out = output_file_open(zFile);
if( p->out==0 ){
- if( strcmp(azArg[1],"off")!=0 ){
- fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]);
+ if( strcmp(zFile,"off")!=0 ){
+ fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile);
}
p->out = stdout;
rc = 1;
} else {
- sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]);
+ sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
}else
@@ -2160,7 +2924,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
fprintf(p->out, "\n");
}else
- if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
+ if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
@@ -2169,12 +2933,18 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='q' && strncmp(azArg[0], "quit", n)==0 && nArg==1 ){
+ if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
- if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
- FILE *alt = fopen(azArg[1], "rb");
+ if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
+ FILE *alt;
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .read FILE\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ alt = fopen(azArg[1], "rb");
if( alt==0 ){
fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
@@ -2184,7 +2954,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 && nArg>1 && nArg<4){
+ if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
sqlite3 *pSrc;
@@ -2194,9 +2964,13 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( nArg==2 ){
zSrcFile = azArg[1];
zDb = "main";
- }else{
+ }else if( nArg==3 ){
zSrcFile = azArg[2];
zDb = azArg[1];
+ }else{
+ fprintf(stderr, "Usage: .restore ?DB? FILE\n");
+ rc = 1;
+ goto meta_command_exit;
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
@@ -2204,7 +2978,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_close(pSrc);
return 1;
}
- open_db(p);
+ open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
@@ -2231,14 +3005,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
sqlite3_close(pSrc);
}else
- if( c=='s' && strncmp(azArg[0], "schema", n)==0 && nArg<3 ){
+ if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
struct callback_data data;
char *zErrMsg = 0;
- open_db(p);
+ open_db(p, 0);
memcpy(&data, p, sizeof(data));
data.showHeader = 0;
data.mode = MODE_Semi;
- if( nArg>1 ){
+ if( nArg==2 ){
int i;
for(i=0; azArg[1][i]; i++) azArg[1][i] = ToLower(azArg[1][i]);
if( strcmp(azArg[1],"sqlite_master")==0 ){
@@ -2282,7 +3056,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
callback, &data, &zErrMsg);
zShellStatic = 0;
}
- }else{
+ }else if( nArg==1 ){
rc = sqlite3_exec(p->db,
"SELECT sql FROM "
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
@@ -2292,6 +3066,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
"ORDER BY rowid",
callback, &data, &zErrMsg
);
+ }else{
+ fprintf(stderr, "Usage: .schema ?LIKE-PATTERN?\n");
+ rc = 1;
+ goto meta_command_exit;
}
if( zErrMsg ){
fprintf(stderr,"Error: %s\n", zErrMsg);
@@ -2305,14 +3083,71 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
- sqlite3_snprintf(sizeof(p->separator), p->separator,
- "%.*s", (int)sizeof(p->separator)-1, azArg[1]);
+#ifdef SQLITE_DEBUG
+ /* Undocumented commands for internal testing. Subject to change
+ ** without notice. */
+ if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){
+ if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){
+ int i, v;
+ for(i=1; i<nArg; i++){
+ v = booleanValue(azArg[i]);
+ fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
+ }
+ }
+ if( strncmp(azArg[0]+9, "integer", n-9)==0 ){
+ int i; sqlite3_int64 v;
+ for(i=1; i<nArg; i++){
+ char zBuf[200];
+ v = integerValue(azArg[i]);
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
+ fprintf(p->out, "%s", zBuf);
+ }
+ }
+ }else
+#endif
+
+ if( c=='s' && strncmp(azArg[0], "separator", n)==0 ){
+ if( nArg<2 || nArg>3 ){
+ fprintf(stderr, "Usage: .separator SEPARATOR ?NEWLINE?\n");
+ rc = 1;
+ }
+ if( nArg>=2 ){
+ sqlite3_snprintf(sizeof(p->separator), p->separator, azArg[1]);
+ }
+ if( nArg>=3 ){
+ sqlite3_snprintf(sizeof(p->newline), p->newline, azArg[2]);
+ }
}else
- if( c=='s' && strncmp(azArg[0], "show", n)==0 && nArg==1 ){
+ if( c=='s'
+ && (strncmp(azArg[0], "shell", n)==0 || strncmp(azArg[0],"system",n)==0)
+ ){
+ char *zCmd;
+ int i, x;
+ if( nArg<2 ){
+ fprintf(stderr, "Usage: .system COMMAND\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
+ for(i=2; i<nArg; i++){
+ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
+ zCmd, azArg[i]);
+ }
+ x = system(zCmd);
+ sqlite3_free(zCmd);
+ if( x ) fprintf(stderr, "System command returns %d\n", x);
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
int i;
+ if( nArg!=1 ){
+ fprintf(stderr, "Usage: .show\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","eqp", p->autoEQP ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
@@ -2323,6 +3158,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){
strlen30(p->outfile) ? p->outfile : "stdout");
fprintf(p->out,"%9.9s: ", "separator");
output_c_string(p->out, p->separator);
+ fprintf(p->out," ");
+ output_c_string(p->out, p->newline);
fprintf(p->out, "\n");
fprintf(p->out,"%9.9s: %s\n","stats", p->statsOn ? "on" : "off");
fprintf(p->out,"%9.9s: ","width");
@@ -2332,17 +3169,22 @@ static int do_meta_command(char *zLine, struct callback_data *p){
fprintf(p->out,"\n");
}else
- if( c=='s' && strncmp(azArg[0], "stats", n)==0 && nArg>1 && nArg<3 ){
- p->statsOn = booleanValue(azArg[1]);
+ if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
+ if( nArg==2 ){
+ p->statsOn = booleanValue(azArg[1]);
+ }else{
+ fprintf(stderr, "Usage: .stats on|off\n");
+ rc = 1;
+ }
}else
- if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){
+ if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
sqlite3_stmt *pStmt;
char **azResult;
int nRow, nAlloc;
char *zSql = 0;
int ii;
- open_db(p);
+ open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ) return rc;
zSql = sqlite3_mprintf(
@@ -2438,11 +3280,12 @@ static int do_meta_command(char *zLine, struct callback_data *p){
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS },
{ "iskeyword", SQLITE_TESTCTRL_ISKEYWORD },
{ "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC },
+ { "byteorder", SQLITE_TESTCTRL_BYTEORDER },
};
int testctrl = -1;
int rc = 0;
int i, n;
- open_db(p);
+ open_db(p, 0);
/* convert testctrl text option to value. allow any unique prefix
** of the option name, or a numerical value. */
@@ -2458,7 +3301,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}
}
- if( testctrl<0 ) testctrl = atoi(azArg[1]);
+ if( testctrl<0 ) testctrl = (int)integerValue(azArg[1]);
if( (testctrl<SQLITE_TESTCTRL_FIRST) || (testctrl>SQLITE_TESTCTRL_LAST) ){
fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]);
}else{
@@ -2478,9 +3321,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
break;
/* sqlite3_test_control(int) */
- case SQLITE_TESTCTRL_PRNG_SAVE:
- case SQLITE_TESTCTRL_PRNG_RESTORE:
+ case SQLITE_TESTCTRL_PRNG_SAVE:
+ case SQLITE_TESTCTRL_PRNG_RESTORE:
case SQLITE_TESTCTRL_PRNG_RESET:
+ case SQLITE_TESTCTRL_BYTEORDER:
if( nArg==2 ){
rc = sqlite3_test_control(testctrl);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
@@ -2492,7 +3336,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
/* sqlite3_test_control(int, uint) */
case SQLITE_TESTCTRL_PENDING_BYTE:
if( nArg==3 ){
- unsigned int opt = (unsigned int)integerValue(azArg[2]);
+ unsigned int opt = (unsigned int)integerValue(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
} else {
@@ -2505,7 +3349,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
case SQLITE_TESTCTRL_ASSERT:
case SQLITE_TESTCTRL_ALWAYS:
if( nArg==3 ){
- int opt = atoi(azArg[2]);
+ int opt = booleanValue(azArg[2]);
rc = sqlite3_test_control(testctrl, opt);
fprintf(p->out, "%d (0x%08x)\n", rc, rc);
} else {
@@ -2540,20 +3384,32 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
- if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 && nArg==2 ){
- open_db(p);
- sqlite3_busy_timeout(p->db, atoi(azArg[1]));
+ if( c=='t' && n>4 && strncmp(azArg[0], "timeout", n)==0 ){
+ open_db(p, 0);
+ sqlite3_busy_timeout(p->db, nArg>=2 ? (int)integerValue(azArg[1]) : 0);
}else
- if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0
- && nArg==2
- ){
- enableTimer = booleanValue(azArg[1]);
+ if( c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 ){
+ if( nArg==2 ){
+ enableTimer = booleanValue(azArg[1]);
+ if( enableTimer && !HAS_TIMER ){
+ fprintf(stderr, "Error: timer not available on this system.\n");
+ enableTimer = 0;
+ }
+ }else{
+ fprintf(stderr, "Usage: .timer on|off\n");
+ rc = 1;
+ }
}else
- if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){
- open_db(p);
+ if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
+ open_db(p, 0);
output_file_close(p->traceOut);
+ if( nArg!=2 ){
+ fprintf(stderr, "Usage: .trace FILE|off\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
p->traceOut = output_file_open(azArg[1]);
#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT)
if( p->traceOut==0 ){
@@ -2584,15 +3440,15 @@ static int do_meta_command(char *zLine, struct callback_data *p){
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
extern int sqlite3WhereTrace;
- sqlite3WhereTrace = booleanValue(azArg[1]);
+ sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;
}else
#endif
- if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
+ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
int j;
assert( nArg<=ArraySize(azArg) );
for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
- p->colWidth[j-1] = atoi(azArg[j]);
+ p->colWidth[j-1] = (int)integerValue(azArg[j]);
}
}else
@@ -2602,6 +3458,11 @@ static int do_meta_command(char *zLine, struct callback_data *p){
rc = 1;
}
+meta_command_exit:
+ if( p->outCount ){
+ p->outCount--;
+ if( p->outCount==0 ) output_reset(p);
+ }
return rc;
}
@@ -2609,7 +3470,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
** Return TRUE if a semicolon occurs anywhere in the first N characters
** of string z[].
*/
-static int _contains_semicolon(const char *z, int N){
+static int line_contains_semicolon(const char *z, int N){
int i;
for(i=0; i<N; i++){ if( z[i]==';' ) return 1; }
return 0;
@@ -2644,7 +3505,7 @@ static int _all_whitespace(const char *z){
** than a semi-colon. The SQL Server style "go" command is understood
** as is the Oracle "/".
*/
-static int _is_command_terminator(const char *zLine){
+static int line_is_command_terminator(const char *zLine){
while( IsSpace(zLine[0]) ){ zLine++; };
if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ){
return 1; /* Oracle */
@@ -2660,7 +3521,7 @@ static int _is_command_terminator(const char *zLine){
** Return true if zSql is a complete SQL statement. Return false if it
** ends in the middle of a string literal or C-style comment.
*/
-static int _is_complete(char *zSql, int nSql){
+static int line_is_complete(char *zSql, int nSql){
int rc;
if( zSql==0 ) return 1;
zSql[nSql] = ';';
@@ -2680,20 +3541,21 @@ static int _is_complete(char *zSql, int nSql){
** Return the number of errors.
*/
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;
- int lineno = 0;
- int startline = 0;
+ char *zLine = 0; /* A single input line */
+ char *zSql = 0; /* Accumulated SQL text */
+ int nLine; /* Length of current line */
+ int nSql = 0; /* Bytes of zSql[] used */
+ int nAlloc = 0; /* Allocated zSql[] space */
+ int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
+ char *zErrMsg; /* Error message returned */
+ int rc; /* Error code */
+ int errCnt = 0; /* Number of errors seen */
+ int lineno = 0; /* Current line number */
+ int startline = 0; /* Line number for start of current input */
while( errCnt==0 || !bail_on_error || (in==0 && stdin_is_interactive) ){
fflush(p->out);
- free(zLine);
- zLine = one_input_line(zSql, in);
+ zLine = one_input_line(in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
if( stdin_is_interactive ) printf("\n");
@@ -2704,7 +3566,10 @@ static int process_input(struct callback_data *p, FILE *in){
seenInterrupt = 0;
}
lineno++;
- if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
+ if( nSql==0 && _all_whitespace(zLine) ){
+ if( p->echoOn ) printf("%s\n", zLine);
+ continue;
+ }
if( zLine && zLine[0]=='.' && nSql==0 ){
if( p->echoOn ) printf("%s\n", zLine);
rc = do_meta_command(zLine, p);
@@ -2715,38 +3580,35 @@ static int process_input(struct callback_data *p, FILE *in){
}
continue;
}
- if( _is_command_terminator(zLine) && _is_complete(zSql, nSql) ){
+ if( line_is_command_terminator(zLine) && line_is_complete(zSql, nSql) ){
memcpy(zLine,";",2);
}
+ nLine = strlen30(zLine);
+ if( nSql+nLine+2>=nAlloc ){
+ nAlloc = nSql+nLine+100;
+ zSql = realloc(zSql, nAlloc);
+ if( zSql==0 ){
+ fprintf(stderr, "Error: out of memory\n");
+ exit(1);
+ }
+ }
nSqlPrior = nSql;
- if( zSql==0 ){
+ if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
- if( zLine[i]!=0 ){
- nSql = strlen30(zLine);
- zSql = malloc( nSql+3 );
- if( zSql==0 ){
- fprintf(stderr, "Error: out of memory\n");
- exit(1);
- }
- memcpy(zSql, zLine, nSql+1);
- startline = lineno;
- }
+ assert( nAlloc>0 && zSql!=0 );
+ memcpy(zSql, zLine+i, nLine+1-i);
+ startline = lineno;
+ nSql = nLine-i;
}else{
- int len = strlen30(zLine);
- zSql = realloc( zSql, nSql + len + 4 );
- if( zSql==0 ){
- fprintf(stderr,"Error: out of memory\n");
- exit(1);
- }
zSql[nSql++] = '\n';
- memcpy(&zSql[nSql], zLine, len+1);
- nSql += len;
+ memcpy(zSql+nSql, zLine, nLine+1);
+ nSql += nLine;
}
- if( zSql && _contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
+ if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
&& sqlite3_complete(zSql) ){
p->cnt = 0;
- open_db(p);
+ open_db(p, 0);
BEGIN_TIMER;
rc = shell_exec(p->db, zSql, shell_callback, p, &zErrMsg);
END_TIMER;
@@ -2767,16 +3629,17 @@ static int process_input(struct callback_data *p, FILE *in){
}
errCnt++;
}
- free(zSql);
- zSql = 0;
nSql = 0;
- }else if( zSql && _all_whitespace(zSql) ){
- free(zSql);
- zSql = 0;
+ if( p->outCount ){
+ output_reset(p);
+ p->outCount = 0;
+ }
+ }else if( nSql && _all_whitespace(zSql) ){
+ if( p->echoOn ) printf("%s\n", zSql);
nSql = 0;
}
}
- if( zSql ){
+ if( nSql ){
if( !_all_whitespace(zSql) ){
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
}
@@ -2913,6 +3776,7 @@ static const char zOptions[] =
#ifdef SQLITE_ENABLE_MULTIPLEX
" -multiplex enable the multiplexor VFS\n"
#endif
+ " -newline SEP set newline character(s) for CSV\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -separator SEP set output field separator. Default: '|'\n"
" -stats print memory stats before each finalize\n"
@@ -2942,6 +3806,7 @@ static void main_init(struct callback_data *data) {
memset(data, 0, sizeof(*data));
data->mode = MODE_List;
memcpy(data->separator,"|", 2);
+ memcpy(data->newline,"\r\n", 3);
data->showHeader = 0;
sqlite3_config(SQLITE_CONFIG_URI, 1);
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
@@ -2951,6 +3816,26 @@ static void main_init(struct callback_data *data) {
}
/*
+** Output text to the console in a font that attracts extra attention.
+*/
+#ifdef _WIN32
+static void printBold(const char *zText){
+ HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
+ GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
+ SetConsoleTextAttribute(out,
+ FOREGROUND_RED|FOREGROUND_INTENSITY
+ );
+ printf("%s", zText);
+ SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
+}
+#else
+static void printBold(const char *zText){
+ printf("\033[1m%s\033[0m", zText);
+}
+#endif
+
+/*
** Get the argument to an --option. Throw an error and die if no argument
** is available.
*/
@@ -2970,12 +3855,15 @@ int main(int argc, char **argv){
char *zFirstCmd = 0;
int i;
int rc = 0;
+ int warnInmemoryDb = 0;
+#if USE_SYSTEM_SQLITE+0!=1
if( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)!=0 ){
fprintf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
}
+#endif
Argv0 = argv[0];
main_init(&data);
stdin_is_interactive = isatty(0);
@@ -3011,6 +3899,7 @@ int main(int argc, char **argv){
if( z[1]=='-' ) z++;
if( strcmp(z,"-separator")==0
|| strcmp(z,"-nullvalue")==0
+ || strcmp(z,"-newline")==0
|| strcmp(z,"-cmd")==0
){
(void)cmdline_option_value(argc, argv, ++i);
@@ -3024,7 +3913,6 @@ int main(int argc, char **argv){
stdin_is_interactive = 0;
}else if( strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
- int j, c;
const char *zSize;
sqlite3_int64 szHeap;
@@ -3065,10 +3953,16 @@ int main(int argc, char **argv){
if( data.zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
data.zDbFilename = ":memory:";
+ warnInmemoryDb = argc==1;
#else
fprintf(stderr,"%s: Error: no database filename specified\n", Argv0);
return 1;
#endif
+#ifdef SQLITE_SHELL_DBNAME_PROC
+ { extern void SQLITE_SHELL_DBNAME_PROC(const char**);
+ SQLITE_SHELL_DBNAME_PROC(&data.zDbFilename);
+ warnInmemoryDb = 0; }
+#endif
}
data.out = stdout;
@@ -3078,7 +3972,7 @@ int main(int argc, char **argv){
** to the sqlite command-line tool.
*/
if( access(data.zDbFilename, 0)==0 ){
- open_db(&data);
+ open_db(&data, 0);
}
/* Process the initialization file if there is one. If no -init option
@@ -3115,6 +4009,9 @@ int main(int argc, char **argv){
}else if( strcmp(z,"-separator")==0 ){
sqlite3_snprintf(sizeof(data.separator), data.separator,
"%s",cmdline_option_value(argc,argv,++i));
+ }else if( strcmp(z,"-newline")==0 ){
+ sqlite3_snprintf(sizeof(data.newline), data.newline,
+ "%s",cmdline_option_value(argc,argv,++i));
}else if( strcmp(z,"-nullvalue")==0 ){
sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue,
"%s",cmdline_option_value(argc,argv,++i));
@@ -3124,6 +4021,8 @@ int main(int argc, char **argv){
data.showHeader = 0;
}else if( strcmp(z,"-echo")==0 ){
data.echoOn = 1;
+ }else if( strcmp(z,"-eqp")==0 ){
+ data.autoEQP = 1;
}else if( strcmp(z,"-stats")==0 ){
data.statsOn = 1;
}else if( strcmp(z,"-bail")==0 ){
@@ -3158,7 +4057,7 @@ int main(int argc, char **argv){
rc = do_meta_command(z, &data);
if( rc && bail_on_error ) return rc==2 ? 0 : rc;
}else{
- open_db(&data);
+ open_db(&data, 0);
rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg);
if( zErrMsg!=0 ){
fprintf(stderr,"Error: %s\n", zErrMsg);
@@ -3182,7 +4081,7 @@ int main(int argc, char **argv){
rc = do_meta_command(zFirstCmd, &data);
if( rc==2 ) rc = 0;
}else{
- open_db(&data);
+ open_db(&data, 0);
rc = shell_exec(data.db, zFirstCmd, shell_callback, &data, &zErrMsg);
if( zErrMsg!=0 ){
fprintf(stderr,"Error: %s\n", zErrMsg);
@@ -3211,6 +4110,12 @@ int main(int argc, char **argv){
"Enter SQL statements terminated with a \";\"\n",
sqlite3_libversion(), sqlite3_sourceid()
);
+ if( warnInmemoryDb ){
+ printf("Connected to a ");
+ printBold("transient in-memory database");
+ printf(".\nUse \".open FILENAME\" to reopen on a "
+ "persistent database.\n");
+ }
zHome = find_home_dir();
if( zHome ){
nHistory = strlen30(zHome) + 20;
@@ -3218,7 +4123,7 @@ int main(int argc, char **argv){
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
-#if defined(HAVE_READLINE) && HAVE_READLINE==1
+#if defined(HAVE_READLINE)
if( zHistory ) read_history(zHistory);
#endif
rc = process_input(&data, 0);
@@ -3235,5 +4140,6 @@ int main(int argc, char **argv){
if( data.db ){
sqlite3_close(data.db);
}
+ sqlite3_free(data.zFreeOnClose);
return rc;
}
diff --git a/src/sqlcipher.h b/src/sqlcipher.h
index 41f8f83..37ecf3b 100644
--- a/src/sqlcipher.h
+++ b/src/sqlcipher.h
@@ -43,7 +43,7 @@ typedef struct {
int (*add_random)(void *ctx, void *buffer, int length);
int (*random)(void *ctx, void *buffer, int length);
int (*hmac)(void *ctx, unsigned char *hmac_key, int key_sz, unsigned char *in, int in_sz, unsigned char *in2, int in2_sz, unsigned char *out);
- int (*kdf)(void *ctx, const char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key);
+ int (*kdf)(void *ctx, const unsigned char *pass, int pass_sz, unsigned char* salt, int salt_sz, int workfactor, int key_sz, unsigned char *key);
int (*cipher)(void *ctx, int mode, unsigned char *key, int key_sz, unsigned char *iv, unsigned char *in, int in_sz, unsigned char *out);
int (*set_cipher)(void *ctx, const char *cipher_name);
const char* (*get_cipher)(void *ctx);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 6608823..230f8d4 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -264,7 +264,7 @@ typedef sqlite_uint64 sqlite3_uint64;
**
** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
** for the [sqlite3] object.
-** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
**
@@ -272,7 +272,7 @@ typedef sqlite_uint64 sqlite3_uint64;
** statements or unfinished sqlite3_backup objects then sqlite3_close()
** will leave the database connection open and return [SQLITE_BUSY].
** ^If sqlite3_close_v2() is called with unfinalized prepared statements
-** and unfinished sqlite3_backups, then the database connection becomes
+** and/or unfinished sqlite3_backups, then the database connection becomes
** an unusable "zombie" which will automatically be deallocated when the
** last prepared statement is finalized or the last sqlite3_backup is
** finished. The sqlite3_close_v2() interface is intended for use with
@@ -285,7 +285,7 @@ typedef sqlite_uint64 sqlite3_uint64;
** with the [sqlite3] object prior to attempting to close the object. ^If
** sqlite3_close_v2() is called on a [database connection] that still has
** outstanding [prepared statements], [BLOB handles], and/or
-** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
+** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
** of resources is deferred until all [prepared statements], [BLOB handles],
** and [sqlite3_backup] objects are also destroyed.
**
@@ -365,7 +365,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** <ul>
** <li> The application must insure that the 1st parameter to sqlite3_exec()
** is a valid and open [database connection].
-** <li> The application must not close [database connection] specified by
+** <li> The application must not close the [database connection] specified by
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
@@ -381,16 +381,14 @@ int sqlite3_exec(
/*
** CAPI3REF: Result Codes
-** KEYWORDS: SQLITE_OK {error code} {error codes}
-** KEYWORDS: {result code} {result codes}
+** KEYWORDS: {result code definitions}
**
** Many SQLite functions return an integer result code from the set shown
** here in order to indicate success or failure.
**
** New error codes may be added in future versions of SQLite.
**
-** See also: [SQLITE_IOERR_READ | extended result codes],
-** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
+** See also: [extended result code definitions]
*/
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
@@ -428,26 +426,19 @@ int sqlite3_exec(
/*
** CAPI3REF: Extended Result Codes
-** KEYWORDS: {extended error code} {extended error codes}
-** KEYWORDS: {extended result code} {extended result codes}
+** KEYWORDS: {extended result code definitions}
**
-** In its default configuration, SQLite API routines return one of 26 integer
-** [SQLITE_OK | result codes]. However, experience has shown that many of
+** In its default configuration, SQLite API routines return one of 30 integer
+** [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
+** about errors. These [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.
+** [sqlite3_extended_result_codes()] API. Or, the extended code for
+** the most recent error can be obtained using
+** [sqlite3_extended_errcode()].
*/
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
@@ -473,15 +464,20 @@ int sqlite3_exec(
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
+#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
+#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
+#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
+#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
@@ -492,8 +488,10 @@ int sqlite3_exec(
#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
+#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -547,7 +545,11 @@ int sqlite3_exec(
** after reboot following a crash or power loss, the only bytes in a
** file that were written at the application level might have changed
** and that adjacent bytes, even bytes within the same sector are
-** guaranteed to be unchanged.
+** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
+** flag indicate that a file cannot be deleted when open. The
+** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
+** read-only media and cannot be changed even by processes with
+** elevated privileges.
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@@ -562,6 +564,7 @@ int sqlite3_exec(
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
+#define SQLITE_IOCAP_IMMUTABLE 0x00002000
/*
** CAPI3REF: File Locking Levels
@@ -668,7 +671,7 @@ struct sqlite3_file {
** 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.
+** A [file control opcodes | list of opcodes] less than 100 is available.
** Applications that define a custom xFileControl method should use opcodes
** greater than 100 to avoid conflicts. VFS implementations should
** return [SQLITE_NOTFOUND] for file control opcodes that they do not
@@ -741,6 +744,7 @@ struct sqlite3_io_methods {
/*
** CAPI3REF: Standard File Control Opcodes
+** KEYWORDS: {file control opcodes} {file control opcode}
**
** These integer constants are opcodes for the xFileControl method
** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]
@@ -778,15 +782,29 @@ struct sqlite3_io_methods {
** additional information.
**
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
-** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
-** SQLite and sent to all VFSes in place of a call to the xSync method
-** when the database connection has [PRAGMA synchronous] set to OFF.)^
-** Some specialized VFSes need this signal in order to operate correctly
-** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
-** VFSes do not need this signal and should silently ignore this opcode.
-** Applications should not call [sqlite3_file_control()] with this
-** opcode as doing so may disrupt the operation of the specialized VFSes
-** that do require it.
+** No longer in use.
+**
+** <li>[[SQLITE_FCNTL_SYNC]]
+** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
+** sent to the VFS immediately before the xSync method is invoked on a
+** database file descriptor. Or, if the xSync method is not invoked
+** because the user has configured SQLite with
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
+** of the xSync method. In most cases, the pointer argument passed with
+** this file-control is NULL. However, if the database file is being synced
+** as part of a multi-database commit, the argument points to a nul-terminated
+** string containing the transactions master-journal file name. VFSes that
+** do not need this signal should silently ignore this opcode. Applications
+** should not call [sqlite3_file_control()] with this opcode as doing so may
+** disrupt the operation of the specialized VFSes that do require it.
+**
+** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
+** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
+** and sent to the VFS after a transaction has been committed immediately
+** but before the database is unlocked. VFSes that do not need this signal
+** should silently ignore this opcode. Applications should not call
+** [sqlite3_file_control()] with this opcode as doing so may disrupt the
+** operation of the specialized VFSes that do require it.
**
** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
@@ -902,6 +920,26 @@ struct sqlite3_io_methods {
** can be queried by passing in a pointer to a negative number. This
** file-control is used internally to implement [PRAGMA mmap_size].
**
+** <li>[[SQLITE_FCNTL_TRACE]]
+** The [SQLITE_FCNTL_TRACE] file control provides advisory information
+** to the VFS about what the higher layers of the SQLite stack are doing.
+** This file control is used by some VFS activity tracing [shims].
+** The argument is a zero-terminated string. Higher layers in the
+** SQLite stack may generate instances of this file control if
+** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
+**
+** <li>[[SQLITE_FCNTL_HAS_MOVED]]
+** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
+** pointer to an integer and it writes a boolean into that integer depending
+** on whether or not the file has been renamed, moved, or deleted since it
+** was first opened.
+**
+** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
+** opcode causes the xFileControl method to swap the file handle with the one
+** pointed to by the pArg argument. This capability is used during testing
+** and only needs to be supported when SQLITE_TEST is defined.
+**
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE 1
@@ -921,6 +959,11 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_BUSYHANDLER 15
#define SQLITE_FCNTL_TEMPFILENAME 16
#define SQLITE_FCNTL_MMAP_SIZE 18
+#define SQLITE_FCNTL_TRACE 19
+#define SQLITE_FCNTL_HAS_MOVED 20
+#define SQLITE_FCNTL_SYNC 21
+#define SQLITE_FCNTL_COMMIT_PHASETWO 22
+#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
/*
** CAPI3REF: Mutex Handle
@@ -1365,7 +1408,7 @@ int sqlite3_db_config(sqlite3*, int op, ...);
** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
** that causes the corresponding memory allocation to fail.
**
-** The xInit method initializes the memory allocator. (For example,
+** 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
@@ -1607,27 +1650,27 @@ struct sqlite3_mem_methods {
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
-** <dd> This option takes a single argument of type int. If non-zero, then
+** <dd>^(This option takes a single argument of type int. If non-zero, then
** URI handling is globally enabled. If the parameter is zero, then URI handling
-** is globally disabled. If URI handling is globally enabled, all filenames
+** is globally disabled.)^ ^If URI handling is globally enabled, all filenames
** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
-** connection is opened. If it is globally disabled, filenames are
+** connection is opened. ^If it is globally disabled, filenames are
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
-** database connection is opened. By default, URI handling is globally
+** database connection is opened. ^(By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
-** [SQLITE_USE_URI] symbol defined.
+** [SQLITE_USE_URI] symbol defined.)^
**
** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
-** <dd> This option takes a single integer argument which is interpreted as
+** <dd>^This option takes a single integer argument which is interpreted as
** a boolean in order to enable or disable the use of covering indices for
-** full table scans in the query optimizer. The default setting is determined
+** full table scans in the query optimizer. ^The default setting is determined
** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
** if that compile-time option is omitted.
** The ability to disable the use of covering indices for full table scans
** is because some incorrectly coded legacy applications might malfunction
-** malfunction when the optimization is enabled. Providing the ability to
+** when the optimization is enabled. Providing the ability to
** disable the optimization allows the older, buggy application code to work
** without change even with newer versions of SQLite.
**
@@ -1656,17 +1699,24 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_MMAP_SIZE]]
** <dt>SQLITE_CONFIG_MMAP_SIZE
-** <dd>SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
+** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
** that are the default mmap size limit (the default setting for
** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
-** The default setting can be overridden by each database connection using
+** ^The default setting can be overridden by each database connection using
** either the [PRAGMA mmap_size] command, or by using the
-** [SQLITE_FCNTL_MMAP_SIZE] file control. The maximum allowed mmap size
+** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
** cannot be changed at run-time. Nor may the maximum allowed mmap size
** exceed the compile-time maximum mmap size set by the
-** [SQLITE_MAX_MMAP_SIZE] compile-time option.
-** If either argument to this option is negative, then that argument is
+** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
** changed to its compile-time default.
+**
+** [[SQLITE_CONFIG_WIN32_HEAPSIZE]]
+** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE
+** <dd>^This option is only available if SQLite is compiled for Windows
+** with the [SQLITE_WIN32_MALLOC] pre-processor macro defined.
+** SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
+** that specifies the maximum size of the created heap.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1691,6 +1741,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
+#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -1767,19 +1818,21 @@ int sqlite3_extended_result_codes(sqlite3*, int onoff);
/*
** CAPI3REF: Last Insert Rowid
**
-** ^Each entry in an SQLite table has a unique 64-bit signed
+** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
+** has a unique 64-bit signed
** integer key called the [ROWID | "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. ^As of SQLite version 3.7.7, this routines
-** records the last insert rowid of both ordinary tables and [virtual tables].
-** ^If no successful [INSERT]s
-** have ever occurred on that database connection, zero is returned.
+** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the
+** most recent successful [INSERT] into a rowid table or [virtual table]
+** on database connection D.
+** ^Inserts into [WITHOUT ROWID] tables are not recorded.
+** ^If no successful [INSERT]s into rowid tables
+** have ever occurred on the database connection D,
+** then sqlite3_last_insert_rowid(D) returns zero.
**
** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
** method, then this routine will return the [rowid] of the inserted
@@ -1971,27 +2024,33 @@ int sqlite3_complete16(const void *sql);
/*
** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
**
-** ^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.
+** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X
+** that might be invoked with argument P whenever
+** an attempt is made to access a database table associated with
+** [database connection] D when another thread
+** or process has the table locked.
+** The sqlite3_busy_handler() interface is used to implement
+** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout].
**
-** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]
+** ^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 might be invoked with two arguments.
**
** ^The first argument to the busy handler is a copy of the void* pointer which
** is the third argument to sqlite3_busy_handler(). ^The second argument to
** the busy handler callback is the number of times that the busy handler has
-** been invoked for this locking event. ^If the
+** been invoked for the same 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.
+** access the database and [SQLITE_BUSY] is returned
+** to the application.
** ^If the callback returns non-zero, then another attempt
-** is made to open the database for reading and the cycle repeats.
+** is made to access the database 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.
+** to the application 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
@@ -2005,28 +2064,15 @@ int sqlite3_complete16(const void *sql);
**
** ^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.
+** or evaluating [PRAGMA busy_timeout=N] will change the
+** busy handler and thus clear any previously set busy handler.
**
** The busy callback should not take any actions which modify the
-** database connection that invoked the busy handler. Any such actions
+** database connection that invoked the busy handler. In other words,
+** the busy handler is not reentrant. Any such actions
** result in undefined behavior.
**
** A busy handler must not close the database connection
@@ -2042,7 +2088,7 @@ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
** will sleep multiple times until at least "ms" milliseconds of sleeping
** have accumulated. ^After at least "ms" milliseconds of sleeping,
** the handler returns 0 which causes [sqlite3_step()] to return
-** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
+** [SQLITE_BUSY].
**
** ^Calling this routine with an argument less than or equal to zero
** turns off all busy handlers.
@@ -2051,6 +2097,8 @@ int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
** [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.)^
+**
+** See also: [PRAGMA busy_timeout]
*/
int sqlite3_busy_timeout(sqlite3*, int ms);
@@ -2345,11 +2393,13 @@ sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
** applications to access the same PRNG for other purposes.
**
** ^A call to this routine stores N bytes of randomness into buffer P.
+** ^If N is less than one, then P can be a NULL pointer.
**
-** ^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
+** ^If this routine has not been previously called or if the previous
+** call had N less than one, then the PRNG is seeded using randomness
+** obtained from the xRandomness method of the default [sqlite3_vfs] object.
+** ^If the previous call to this routine had an N of 1 or more then
+** the pseudo-randomness is generated
** internally and without recourse to the [sqlite3_vfs] xRandomness
** method.
*/
@@ -2450,8 +2500,8 @@ int sqlite3_set_authorizer(
** [sqlite3_set_authorizer | authorizer documentation] for additional
** information.
**
-** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
-** from the [sqlite3_vtab_on_conflict()] interface.
+** Note that SQLITE_IGNORE is also used as a [conflict resolution mode]
+** returned from the [sqlite3_vtab_on_conflict()] interface.
*/
#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 */
@@ -2509,6 +2559,7 @@ int sqlite3_set_authorizer(
#define SQLITE_FUNCTION 31 /* NULL Function Name */
#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */
#define SQLITE_COPY 0 /* No longer used */
+#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
** CAPI3REF: Tracing And Profiling Functions
@@ -2552,9 +2603,10 @@ SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
** interface is to keep a GUI updated during a large query.
**
** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the number of
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
-** invocations of the callback X.
+** invocations of the callback X. ^If N is less than one then the progress
+** handler is disabled.
**
** ^Only a single progress handler may be defined at one time per
** [database connection]; setting a new progress handler cancels the
@@ -2720,6 +2772,30 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
** a URI filename, its value overrides any behavior requested by setting
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
+**
+** <li> <b>psow</b>: ^The psow parameter may be "true" (or "on" or "yes" or
+** "1") or "false" (or "off" or "no" or "0") to indicate that the
+** [powersafe overwrite] property does or does not apply to the
+** storage media on which the database file resides. ^The psow query
+** parameter only works for the built-in unix and Windows VFSes.
+**
+** <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter
+** which if set disables file locking in rollback journal modes. This
+** is useful for accessing a database on a filesystem that does not
+** support locking. Caution: Database corruption might result if two
+** or more processes write to the same database and any one of those
+** processes uses nolock=1.
+**
+** <li> <b>immutable</b>: ^The immutable parameter is a boolean query
+** parameter that indicates that the database file is stored on
+** read-only media. ^When immutable is set, SQLite assumes that the
+** database file cannot be changed, even by a process with higher
+** privilege, and so the database is opened read-only and all locking
+** and change detection is disabled. Caution: Setting the immutable
+** property on a database file that does in fact change can result
+** in incorrect query results and/or [SQLITE_CORRUPT] errors.
+** See also: [SQLITE_IOCAP_IMMUTABLE].
+**
** </ul>
**
** ^Specifying an unknown parameter in the query component of a URI is not an
@@ -2749,8 +2825,9 @@ void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** Open file "data.db" in the current directory for read-only access.
** Regardless of whether or not shared-cache mode is enabled by
** default, use a private cache.
-** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
-** Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
+** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
+** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
+** that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
** </table>
@@ -3088,7 +3165,6 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
-** the
** </li>
** </ol>
*/
@@ -3750,19 +3826,19 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
**
** <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> NULL <td> TEXT <td> Result is a NULL pointer
+** <tr><td> NULL <td> BLOB <td> Result is a 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> INTEGER <td> [CAST] 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> FLOAT <td> BLOB <td> [CAST] to BLOB
+** <tr><td> TEXT <td> INTEGER <td> [CAST] to INTEGER
+** <tr><td> TEXT <td> FLOAT <td> [CAST] to REAL
** <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> INTEGER <td> [CAST] to INTEGER
+** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
** </table>
** </blockquote>)^
@@ -3818,7 +3894,7 @@ int sqlite3_data_count(sqlite3_stmt *pStmt);
** 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
+** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
** [sqlite3_free()].
**
** ^(If a memory allocation error occurs during the evaluation of any
@@ -3927,15 +4003,24 @@ int sqlite3_reset(sqlite3_stmt *pStmt);
**
** ^The fourth parameter, eTextRep, specifies what
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
-** its parameters. Every SQL function implementation must be able to work
-** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
-** more efficient with one encoding than another. ^An application may
-** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
-** times with the same function but with different values of eTextRep.
+** its parameters. The application should set this parameter to
+** [SQLITE_UTF16LE] if the function implementation invokes
+** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
+** implementation invokes [sqlite3_value_text16be()] on an input, or
+** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
+** otherwise. ^The same SQL function may be registered multiple times using
+** different preferred text encodings, with different implementations for
+** each encoding.
** ^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 fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC]
+** to signal that the function will always return the same result given
+** the same inputs within a single SQL statement. Most SQL functions are
+** deterministic. The built-in [random()] SQL function is an example of a
+** function that is not deterministic. The SQLite query planner is able to
+** perform additional optimizations on deterministic functions, so use
+** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
@@ -4021,10 +4106,20 @@ int sqlite3_create_function_v2(
#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_ANY 5 /* Deprecated */
#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
/*
+** CAPI3REF: Function Flags
+**
+** These constants may be ORed together with the
+** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
+** to [sqlite3_create_function()], [sqlite3_create_function16()], or
+** [sqlite3_create_function_v2()].
+*/
+#define SQLITE_DETERMINISTIC 0x800
+
+/*
** CAPI3REF: Deprecated Functions
** DEPRECATED
**
@@ -4174,41 +4269,49 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
**
-** The following two functions may be used by scalar SQL functions to
+** These functions may be used by (non-aggregate) 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.
+** some circumstances the associated metadata may be preserved. An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
**
** ^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.
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this sqlite3_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function. ^Subsequent
+** calls to sqlite3_get_auxdata(C,N) return P from the most recent
+** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> when the corresponding function parameter changes, or
+** <li> when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
+** SQL statement, or
+** <li> when sqlite3_set_auxdata() is invoked again on the same parameter, or
+** <li> during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs. </ul>)^
+**
+** Note the last bullet in particular. The destructor X in
+** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
+** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** sqlite3_set_auxdata() has been called.
**
** ^(In practice, metadata is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and [parameters].)^
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
**
** These routines must be called from the same thread in which
** the SQL function is running.
@@ -4513,6 +4616,11 @@ int sqlite3_key(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The key */
);
+int sqlite3_key_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The key */
+);
/*
** Change the key on an open database. If the current database is not
@@ -4526,6 +4634,11 @@ int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */
);
+int sqlite3_rekey_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The new key */
+);
/*
** Specify the activation key for a SEE database. Unless
@@ -4575,6 +4688,13 @@ int sqlite3_sleep(int);
** is a NULL pointer, then SQLite performs a search for an appropriate
** temporary file directory.
**
+** Applications are strongly discouraged from using this global variable.
+** It is required to set a temporary folder on Windows Runtime (WinRT).
+** But for all other platforms, it is highly recommended that applications
+** neither read nor write this variable. This global variable is a relic
+** that exists for backwards compatibility of legacy applications and should
+** be avoided in new projects.
+**
** It is not safe to read or modify this variable in more than one
** thread at a time. It is not safe to read or modify this variable
** if a [database connection] is being used at the same time in a separate
@@ -4593,6 +4713,11 @@ int sqlite3_sleep(int);
** Hence, if this variable is modified directly, either it should be
** made NULL or made to point to memory obtained from [sqlite3_malloc]
** or else the use of the [temp_store_directory pragma] should be avoided.
+** Except when requested by the [temp_store_directory pragma], SQLite
+** does not free the memory that sqlite3_temp_directory points to. If
+** the application wants that memory to be freed, it must do
+** so itself, taking care to only do so after all [database connection]
+** objects have been destroyed.
**
** <b>Note to Windows Runtime users:</b> The temporary directory must be set
** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various
@@ -4777,12 +4902,13 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
**
** ^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.
+** to be invoked whenever a row is updated, inserted or deleted in
+** a rowid table.
** ^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.
+** row is updated, inserted or deleted in a rowid table.
** ^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],
@@ -4795,6 +4921,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
**
** ^(The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).)^
+** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
**
** ^In the current implementation, the update hook
** is not invoked when duplication rows are deleted because of an
@@ -4876,8 +5003,8 @@ int sqlite3_release_memory(int);
**
** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
** memory as possible from database connection D. Unlike the
-** [sqlite3_release_memory()] interface, this interface is effect even
-** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
+** [sqlite3_release_memory()] interface, this interface is in effect even
+** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
** omitted.
**
** See also: [sqlite3_release_memory()]
@@ -5111,11 +5238,24 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
** on the list of automatic extensions is a harmless no-op. ^No entry point
** will be called more than once for each database connection that is opened.
**
-** See also: [sqlite3_reset_auto_extension()].
+** See also: [sqlite3_reset_auto_extension()]
+** and [sqlite3_cancel_auto_extension()]
*/
int sqlite3_auto_extension(void (*xEntryPoint)(void));
/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
+*/
+int sqlite3_cancel_auto_extension(void (*xEntryPoint)(void));
+
+/*
** CAPI3REF: Reset Automatic Extension Loading
**
** ^This interface disables all automatic extensions previously
@@ -5239,10 +5379,22 @@ struct sqlite3_module {
** 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).
+** ^The estimatedCost value is an estimate of the cost of a particular
+** strategy. A cost of N indicates that the cost of the strategy is similar
+** to a linear scan of an SQLite table with N rows. A cost of log(N)
+** indicates that the expense of the operation is similar to that of a
+** binary search on a unique indexed field of an SQLite table with N rows.
+**
+** ^The estimatedRows value is an estimate of the number of rows that
+** will be returned by the strategy.
+**
+** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
+** structure for SQLite version 3.8.2. If a virtual table extension is
+** used with an SQLite version earlier than 3.8.2, the results of attempting
+** to read or write the estimatedRows field are undefined (but are likely
+** to included crashing the application). The estimatedRows field should
+** therefore only be used if [sqlite3_libversion_number()] returns a
+** value greater than or equal to 3008002.
*/
struct sqlite3_index_info {
/* Inputs */
@@ -5267,7 +5419,9 @@ struct sqlite3_index_info {
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 */
+ double estimatedCost; /* Estimated cost of using this index */
+ /* Fields below are only available in SQLite 3.8.2 and later */
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
};
/*
@@ -5471,6 +5625,9 @@ typedef struct sqlite3_blob sqlite3_blob;
** interface. Use the [UPDATE] SQL command to change the size of a
** blob.
**
+** ^The [sqlite3_blob_open()] interface will fail for a [WITHOUT ROWID]
+** table. Incremental BLOB I/O is not possible on [WITHOUT ROWID] tables.
+**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
** and the built-in [zeroblob] SQL function can be used, if desired,
** to create an empty, zero-filled blob in which to read or write using
@@ -5695,10 +5852,12 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
+** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
** </ul>)^
**
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
@@ -5902,6 +6061,9 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
+#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
/*
** CAPI3REF: Retrieve the mutex for a database connection
@@ -5994,7 +6156,11 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19
-#define SQLITE_TESTCTRL_LAST 19
+#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
+#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
+#define SQLITE_TESTCTRL_BYTEORDER 22
+#define SQLITE_TESTCTRL_ISINIT 23
+#define SQLITE_TESTCTRL_LAST 23
/*
** CAPI3REF: SQLite Runtime Status
@@ -6227,6 +6393,12 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^ ^The highwater mark is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -6239,7 +6411,8 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
#define SQLITE_DBSTATUS_CACHE_HIT 7
#define SQLITE_DBSTATUS_CACHE_MISS 8
#define SQLITE_DBSTATUS_CACHE_WRITE 9
-#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_DEFERRED_FKS 10
+#define SQLITE_DBSTATUS_MAX 10 /* Largest defined DBSTATUS */
/*
@@ -6293,11 +6466,21 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** A non-zero value in this counter may indicate an opportunity to
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
+**
+** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647. The number of virtual machine operations can be
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
#define SQLITE_STMTSTATUS_AUTOINDEX 3
+#define SQLITE_STMTSTATUS_VM_STEP 4
/*
** CAPI3REF: Custom Page Cache Object
@@ -6961,6 +7144,9 @@ void *sqlite3_wal_hook(
** ^The [wal_autocheckpoint pragma] can be used to invoke this interface
** from SQL.
**
+** ^Checkpoints initiated by this mechanism are
+** [sqlite3_wal_checkpoint_v2|PASSIVE].
+**
** ^Every new [database connection] defaults to having the auto-checkpoint
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
** pages. The use of this interface
@@ -6977,6 +7163,10 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
** empty string, then a checkpoint is run on all databases of
** connection D. ^If the database connection D is not in
** [WAL | write-ahead log mode] then this interface is a harmless no-op.
+** ^The [sqlite3_wal_checkpoint(D,X)] interface initiates a
+** [sqlite3_wal_checkpoint_v2|PASSIVE] checkpoint.
+** Use the [sqlite3_wal_checkpoint_v2()] interface to get a FULL
+** or RESET checkpoint.
**
** ^The [wal_checkpoint pragma] can be used to invoke this interface
** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the
@@ -6999,10 +7189,12 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
** Checkpoint as many frames as possible without waiting for any database
** readers or writers to finish. Sync the db file if all frames in the log
** are checkpointed. This mode is the same as calling
-** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
+** sqlite3_wal_checkpoint(). The [sqlite3_busy_handler|busy-handler callback]
+** is never invoked.
**
** <dt>SQLITE_CHECKPOINT_FULL<dd>
-** This mode blocks (calls the busy-handler callback) until there is no
+** This mode blocks (it invokes the
+** [sqlite3_busy_handler|busy-handler callback]) until there is no
** database writer and all readers are reading from the most recent database
** snapshot. It then checkpoints all frames in the log file and syncs the
** database file. This call blocks database writers while it is running,
@@ -7010,7 +7202,8 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after
-** checkpointing the log file it blocks (calls the busy-handler callback)
+** checkpointing the log file it blocks (calls the
+** [sqlite3_busy_handler|busy-handler callback])
** until all readers are reading from the database file only. This ensures
** that the next client to write to the database file restarts the log file
** from the beginning. This call blocks database writers while it is running,
@@ -7148,6 +7341,7 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Conflict resolution modes
+** KEYWORDS: {conflict resolution mode}
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
** inform a [virtual table] implementation what the [ON CONFLICT] mode
@@ -7176,4 +7370,4 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* _SQLITE3_H_ */
diff --git a/src/sqlite3.rc b/src/sqlite3.rc
index 969876d..04dd086 100644
--- a/src/sqlite3.rc
+++ b/src/sqlite3.rc
@@ -17,7 +17,11 @@
#include "winresrc.h"
#else
#include "windows.h"
-#endif
+#endif /* !defined(_WIN32_WCE) */
+
+#if !defined(VS_FF_NONE)
+# define VS_FF_NONE 0x00000000L
+#endif /* !defined(VS_FF_NONE) */
#include "sqlite3.h"
#include "sqlite3rc.h"
@@ -26,10 +30,18 @@
* English (U.S.) resources
*/
-#ifdef _WIN32
+#if defined(_WIN32)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
-#endif /* _WIN32 */
+#endif /* defined(_WIN32) */
+
+/*
+ * Icon
+ */
+
+#define IDI_SQLITE 101
+
+IDI_SQLITE ICON "..\\art\\sqlite370.ico"
/*
* Version
@@ -38,14 +50,14 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
FILEVERSION SQLITE_RESOURCE_VERSION
PRODUCTVERSION SQLITE_RESOURCE_VERSION
- FILEFLAGSMASK 0x3F
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#if defined(_DEBUG)
- FILEFLAGS 0x1L
+ FILEFLAGS VS_FF_DEBUG
#else
- FILEFLAGS 0x0L
-#endif
+ FILEFLAGS VS_FF_NONE
+#endif /* defined(_DEBUG) */
FILEOS VOS__WINDOWS32
- FILETYPE VFT_APP
+ FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
@@ -64,6 +76,6 @@ BEGIN
END
BLOCK "VarFileInfo"
BEGIN
- VALUE "Translation", 0x409, 1200
+ VALUE "Translation", 0x409, 0x4b0
END
END
diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h
index 928bb3b..ecf93f6 100644
--- a/src/sqlite3ext.h
+++ b/src/sqlite3ext.h
@@ -474,11 +474,14 @@ struct sqlite3_api_routines {
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* _SQLITE3EXT_H_ */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 5950f23..ee52487 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -32,6 +32,11 @@
** in Red Hat 6.0, so the code won't work. Hence, for maximum binary
** portability you should omit LFS.
**
+** The previous paragraph was written in 2005. (This paragraph is written
+** on 2008-11-28.) These days, all Linux kernels support large files, so
+** you should probably leave LFS enabled. But some embedded platforms might
+** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
+**
** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later.
*/
#ifndef SQLITE_DISABLE_LFS
@@ -43,6 +48,44 @@
#endif
/*
+** For MinGW, check to see if we can include the header file containing its
+** version information, among other things. Normally, this internal MinGW
+** header file would [only] be included automatically by other MinGW header
+** files; however, the contained version information is now required by this
+** header file to work around binary compatibility issues (see below) and
+** this is the only known way to reliably obtain it. This entire #if block
+** would be completely unnecessary if there was any other way of detecting
+** MinGW via their preprocessor (e.g. if they customized their GCC to define
+** some MinGW-specific macros). When compiling for MinGW, either the
+** _HAVE_MINGW_H or _HAVE__MINGW_H (note the extra underscore) macro must be
+** defined; otherwise, detection of conditions specific to MinGW will be
+** disabled.
+*/
+#if defined(_HAVE_MINGW_H)
+# include "mingw.h"
+#elif defined(_HAVE__MINGW_H)
+# include "_mingw.h"
+#endif
+
+/*
+** For MinGW version 4.x (and higher), check to see if the _USE_32BIT_TIME_T
+** define is required to maintain binary compatibility with the MSVC runtime
+** library in use (e.g. for Windows XP).
+*/
+#if !defined(_USE_32BIT_TIME_T) && !defined(_USE_64BIT_TIME_T) && \
+ defined(_WIN32) && !defined(_WIN64) && \
+ defined(__MINGW_MAJOR_VERSION) && __MINGW_MAJOR_VERSION >= 4 && \
+ defined(__MSVCRT__)
+# define _USE_32BIT_TIME_T
+#endif
+
+/* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear
+** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for
+** MinGW.
+*/
+#include "sqlite3.h"
+
+/*
** Include the configuration header output by 'configure' if we're using the
** autoconf-based build
*/
@@ -114,7 +157,7 @@
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
-** level of threadsafety. 2 means the libary is multithreaded - multiple
+** level of threadsafety. 2 means the library is multithreaded - multiple
** threads can use SQLite as long as no two threads try to use the same
** database connection at the same time.
**
@@ -161,9 +204,6 @@
** will cause HeapValidate to be called. If heap validation should fail, an
** assertion will be triggered.
**
-** (Historical note: There used to be several other options, but we've
-** pared it down to just these three.)
-**
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
** the default.
*/
@@ -193,27 +233,12 @@
/*
** 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.
+** recursive mutexes on most Unix systems and fchmod() on OpenBSD.
+** But _XOPEN_SOURCE define causes problems for Mac OS X, so omit
+** it.
*/
-#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) \
- && !defined(__APPLE__) && SQLITE_THREADSAFE
-# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */
-#endif
-
-/*
-** The TCL headers are only needed when compiling the TCL bindings.
-*/
-#if defined(SQLITE_TCL) || defined(TCLSH)
-# include <tcl.h>
+#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__)
+# define _XOPEN_SOURCE 600
#endif
/*
@@ -221,8 +246,8 @@
** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
** make it true by defining or undefining NDEBUG.
**
-** Setting NDEBUG makes the code smaller and run faster by disabling the
-** number assert() statements in the code. So we want the default action
+** Setting NDEBUG makes the code smaller and faster by disabling the
+** assert() statements in the code. So we want the default action
** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
** feature.
@@ -235,6 +260,13 @@
#endif
/*
+** Enable SQLITE_ENABLE_EXPLAIN_COMMENTS if SQLITE_DEBUG is turned on.
+*/
+#if !defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) && defined(SQLITE_DEBUG)
+# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
+#endif
+
+/*
** The testcase() macro is used to aid in coverage testing. When
** doing coverage testing, the condition inside the argument to
** testcase() must be evaluated both true and false in order to
@@ -292,7 +324,7 @@
** In other words, ALWAYS and NEVER are added for defensive code.
**
** When doing coverage testing ALWAYS and NEVER are hard-coded to
-** be true and false so that the unreachable code then specify will
+** be true and false so that the unreachable code they specify will
** not be counted as untested code.
*/
#if defined(SQLITE_COVERAGE_TEST)
@@ -316,18 +348,13 @@
/*
** 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.
+** a boolean expression that is usually true. These hints could,
+** in theory, be used by the compiler to generate better code, but
+** currently they are just comments for human readers.
*/
-#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
+#define likely(X) (X)
+#define unlikely(X) (X)
-#include "sqlite3.h"
#include "hash.h"
#include "parse.h"
#include <stdio.h>
@@ -404,6 +431,12 @@
#endif
/*
+** Macros to compute minimum and maximum of two numbers.
+*/
+#define MIN(A,B) ((A)<(B)?(A):(B))
+#define MAX(A,B) ((A)>(B)?(A):(B))
+
+/*
** Check to see if this machine uses EBCDIC. (Yes, believe it or
** not, there are still machines out there that use EBCDIC.)
*/
@@ -487,23 +520,65 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
#endif
/*
+** Estimated quantities used for query planning are stored as 16-bit
+** logarithms. For quantity X, the value stored is 10*log2(X). This
+** gives a possible range of values of approximately 1.0e986 to 1e-986.
+** But the allowed values are "grainy". Not every value is representable.
+** For example, quantities 16 and 17 are both represented by a LogEst
+** of 40. However, since LogEst quantaties are suppose to be estimates,
+** not exact values, this imprecision is not a problem.
+**
+** "LogEst" is short for "Logarithmic Estimate".
+**
+** Examples:
+** 1 -> 0 20 -> 43 10000 -> 132
+** 2 -> 10 25 -> 46 25000 -> 146
+** 3 -> 16 100 -> 66 1000000 -> 199
+** 4 -> 20 1000 -> 99 1048576 -> 200
+** 10 -> 33 1024 -> 100 4294967296 -> 320
+**
+** The LogEst can be negative to indicate fractional values.
+** Examples:
+**
+** 0.5 -> -10 0.1 -> -33 0.0625 -> -40
+*/
+typedef INT16_TYPE LogEst;
+
+/*
** Macros to determine whether the machine is big or little endian,
-** evaluated at runtime.
+** and whether or not that determination is run-time or compile-time.
+**
+** For best performance, an attempt is made to guess at the byte-order
+** using C-preprocessor macros. If that is unsuccessful, or if
+** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
+** at run-time.
*/
#ifdef SQLITE_AMALGAMATION
const int sqlite3one = 1;
#else
extern const int sqlite3one;
#endif
-#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
- || defined(__x86_64) || defined(__x86_64__)
+#if (defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
+ defined(__arm__)) && !defined(SQLITE_RUNTIME_BYTEORDER)
+# define SQLITE_BYTEORDER 1234
# define SQLITE_BIGENDIAN 0
# define SQLITE_LITTLEENDIAN 1
# define SQLITE_UTF16NATIVE SQLITE_UTF16LE
-#else
+#endif
+#if (defined(sparc) || defined(__ppc__)) \
+ && !defined(SQLITE_RUNTIME_BYTEORDER)
+# define SQLITE_BYTEORDER 4321
+# define SQLITE_BIGENDIAN 1
+# define SQLITE_LITTLEENDIAN 0
+# define SQLITE_UTF16NATIVE SQLITE_UTF16BE
+#endif
+#if !defined(SQLITE_BYTEORDER)
+# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0)
# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1)
-# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE)
+# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE)
#endif
/*
@@ -585,6 +660,20 @@ extern const int sqlite3one;
#endif
/*
+** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
+** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
+** define SQLITE_ENABLE_STAT3_OR_STAT4
+*/
+#ifdef SQLITE_ENABLE_STAT4
+# undef SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3_OR_STAT4
+# undef SQLITE_ENABLE_STAT3_OR_STAT4
+#endif
+
+/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
@@ -712,6 +801,7 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
+typedef struct PrintfArguments PrintfArguments;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
@@ -728,9 +818,8 @@ typedef struct UnpackedRecord UnpackedRecord;
typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
-typedef struct WherePlan WherePlan;
typedef struct WhereInfo WhereInfo;
-typedef struct WhereLevel WhereLevel;
+typedef struct With With;
/*
** Defer sourcing vdbe.h and btree.h until after the "u8" and
@@ -756,7 +845,6 @@ typedef struct WhereLevel WhereLevel;
struct Db {
char *zName; /* Name of this database */
Btree *pBt; /* The B*Tree structure for this database file */
- u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
u8 safety_level; /* How aggressive at syncing data to disk */
Schema *pSchema; /* Pointer to database schema (possibly shared) */
};
@@ -788,7 +876,7 @@ struct Schema {
Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */
u8 file_format; /* Schema format version for this file */
u8 enc; /* Text encoding used by this database */
- u16 flags; /* Flags associated with this schema */
+ u16 schemaFlags; /* Flags associated with this schema */
int cache_size; /* Number of pages to use in the cache */
};
@@ -796,10 +884,10 @@ struct Schema {
** These macros can be used to test, set, or clear bits in the
** Db.pSchema->flags field.
*/
-#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P))
-#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0)
-#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P)
-#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P)
+#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))==(P))
+#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))!=0)
+#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->schemaFlags|=(P)
+#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->schemaFlags&=~(P)
/*
** Allowed values for the DB.pSchema->flags field.
@@ -902,9 +990,10 @@ struct sqlite3 {
u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
} init;
- int activeVdbeCnt; /* Number of VDBEs currently executing */
- int writeVdbeCnt; /* Number of active VDBEs that are writing */
- int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
+ int nVdbeActive; /* Number of VDBEs currently running */
+ int nVdbeRead; /* Number of active VDBEs that read or write */
+ int nVdbeWrite; /* Number of active VDBEs that read and write */
+ int nVdbeExec; /* Number of nested calls to VdbeExec() */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
void (*xTrace)(void*,const char*); /* Trace function */
@@ -925,8 +1014,6 @@ struct sqlite3 {
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;
sqlite3_value *pErr; /* Most recent error message */
- char *zErrMsg; /* Most recent error message (UTF-8 encoded) */
- char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */
union {
volatile int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
@@ -940,7 +1027,7 @@ struct sqlite3 {
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int (*xProgress)(void *); /* The progress callback */
void *pProgressArg; /* Argument to the progress callback */
- int nProgressOps; /* Number of opcodes for progress callback */
+ unsigned nProgressOps; /* Number of opcodes for progress callback */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
int nVTrans; /* Allocated size of aVTrans */
@@ -958,6 +1045,7 @@ struct sqlite3 {
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
+ i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -989,30 +1077,35 @@ struct sqlite3 {
*/
#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
-#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
-#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */
-#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */
+#define SQLITE_FullFSync 0x00000004 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000008 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000010 /* OK to spill pager cache */
+#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
+#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
-#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */
+#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
-#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */
-#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */
-#define SQLITE_VdbeAddopTrace 0x00000200 /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */
-#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */
-#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */
-#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */
-#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */
-#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */
-#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */
-#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */
-#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */
-#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */
-#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */
-#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */
+#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */
+#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */
+#define SQLITE_VdbeAddopTrace 0x00001000 /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x0004000 /* For shared-cache mode */
+#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */
+#define SQLITE_RecoveryMode 0x00010000 /* Ignore schema errors */
+#define SQLITE_ReverseOrder 0x00020000 /* Reverse unordered SELECTs */
+#define SQLITE_RecTriggers 0x00040000 /* Enable recursive triggers */
+#define SQLITE_ForeignKeys 0x00080000 /* Enforce foreign key constraints */
+#define SQLITE_AutoIndex 0x00100000 /* Enable automatic indexes */
+#define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */
+#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
+#define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */
+#define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */
+#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
+#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
+
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
@@ -1023,12 +1116,15 @@ struct sqlite3 {
#define SQLITE_ColumnCache 0x0002 /* Column cache */
#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
-#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */
+/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
+#define SQLITE_Stat3 0x0800 /* Use the SQLITE_STAT3 table */
+#define SQLITE_AdjustOutEst 0x1000 /* Adjust output estimates using WHERE */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
@@ -1043,6 +1139,12 @@ struct sqlite3 {
#endif
/*
+** Return true if it OK to factor constant expressions into the initialization
+** code. The argument is a Parse object for the code generator.
+*/
+#define ConstFactorOk(P) ((P)->okConstFactor)
+
+/*
** Possible values for the sqlite.magic field.
** The numbers are obtained at random and have no special meaning, other
** than being distinct from one another.
@@ -1062,8 +1164,7 @@ struct sqlite3 {
*/
struct FuncDef {
i16 nArg; /* Number of arguments. -1 means unlimited */
- u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
- u8 flags; /* Some combination of SQLITE_FUNC_* */
+ u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
@@ -1099,14 +1200,17 @@ struct FuncDestructor {
** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There
** are assert() statements in the code to verify this.
*/
-#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
-#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
-#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */
-#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */
-#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */
+#define SQLITE_FUNC_ENCMASK 0x003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
+#define SQLITE_FUNC_LIKE 0x004 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x008 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x010 /* Ephemeral. Delete with VDBE */
+#define SQLITE_FUNC_NEEDCOLL 0x020 /* sqlite3GetFuncCollSeq() might be called */
+#define SQLITE_FUNC_LENGTH 0x040 /* Built-in length() function */
+#define SQLITE_FUNC_TYPEOF 0x080 /* Built-in typeof() function */
+#define SQLITE_FUNC_COUNT 0x100 /* Built-in count(*) aggregate */
+#define SQLITE_FUNC_COALESCE 0x200 /* Built-in coalesce() or ifnull() */
+#define SQLITE_FUNC_UNLIKELY 0x400 /* Built-in unlikely() function */
+#define SQLITE_FUNC_CONSTANT 0x800 /* Constant inputs give a constant output */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -1119,6 +1223,9 @@ struct FuncDestructor {
** as the user-data (sqlite3_user_data()) for the function. If
** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set.
**
+** VFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag.
+**
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
** Used to create an aggregate function definition implemented by
** the C functions xStep and xFinal. The first four parameters
@@ -1134,18 +1241,22 @@ struct FuncDestructor {
** parameter.
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
- {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
+ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
pArg, 0, xFunc, 0, 0, #zName, 0, 0}
#define LIKEFUNC(zName, nArg, arg, flags) \
- {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
+ (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
- {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
/*
@@ -1157,6 +1268,7 @@ struct FuncDestructor {
struct Savepoint {
char *zName; /* Savepoint name (nul-terminated) */
i64 nDeferredCons; /* Number of deferred fk violations */
+ i64 nDeferredImmCons; /* Number of deferred imm fk. */
Savepoint *pNext; /* Parent savepoint (if any) */
};
@@ -1193,7 +1305,8 @@ struct Column {
char *zColl; /* Collating sequence. If NULL, use the default */
u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
- u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
+ u8 szEst; /* Estimated size of this column. INT==1 */
+ u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
/* Allowed values for Column.colFlags:
@@ -1255,10 +1368,16 @@ struct CollSeq {
/*
** Additional bit values that can be ORed with an affinity without
** changing the affinity.
+**
+** The SQLITE_NOTNULL flag is a combination of NULLEQ and JUMPIFNULL.
+** It causes an assert() to fire if either operand to a comparison
+** operator is NULL. It is added to certain comparison operators to
+** prove that the operands are always NOT NULL.
*/
#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */
#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
+#define SQLITE_NOTNULL 0x88 /* Assert that operands are never NULL */
/*
** An object of this type is created for each virtual table present in
@@ -1352,11 +1471,15 @@ struct Table {
#ifndef SQLITE_OMIT_CHECK
ExprList *pCheck; /* All CHECK constraints */
#endif
- tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
+ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
int tnum; /* Root BTree node for this table (see note above) */
i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */
i16 nCol; /* Number of columns in this table */
u16 nRef; /* Number of pointers to this Table */
+ LogEst szTabRow; /* Estimated size of each table row in bytes */
+#ifdef SQLITE_ENABLE_COSTMULT
+ LogEst costMult; /* Cost multiplier for using this table */
+#endif
u8 tabFlags; /* Mask of TF_* values */
u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
#ifndef SQLITE_OMIT_ALTERTABLE
@@ -1373,13 +1496,14 @@ struct Table {
};
/*
-** Allowed values for Tabe.tabFlags.
+** Allowed values for Table.tabFlags.
*/
#define TF_Readonly 0x01 /* Read-only system table */
#define TF_Ephemeral 0x02 /* An ephemeral table */
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
#define TF_Virtual 0x10 /* Is a virtual table */
+#define TF_WithoutRowid 0x20 /* No rowid used. PRIMARY KEY is the key */
/*
@@ -1395,6 +1519,9 @@ struct Table {
# define IsHiddenColumn(X) 0
#endif
+/* Does the table have a rowid */
+#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -1409,26 +1536,35 @@ struct Table {
** );
**
** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
+** Equivalent names:
+**
+** from-table == child-table
+** to-table == parent-table
**
** Each REFERENCES clause generates an instance of the following structure
** which is attached to the from-table. The to-table need not exist when
** the from-table is created. The existence of the to-table is not checked.
+**
+** The list of all parents for child Table X is held at X.pFKey.
+**
+** A list of all children for a table named Z (which might not even exist)
+** is held in Schema.fkeyHash with a hash key of Z.
*/
struct FKey {
Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */
- FKey *pNextFrom; /* Next foreign key in pFrom */
+ FKey *pNextFrom; /* Next FKey with the same in pFrom. Next parent of pFrom */
char *zTo; /* Name of table that the key points to (aka: Parent) */
- FKey *pNextTo; /* Next foreign key on table named zTo */
- FKey *pPrevTo; /* Previous foreign key on table named zTo */
+ FKey *pNextTo; /* Next with the same zTo. Next child of zTo. */
+ FKey *pPrevTo; /* Previous with the same zTo */
int nCol; /* Number of columns in this key */
/* EV: R-30323-21917 */
- u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
- u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
- Trigger *apTrigger[2]; /* Triggers for aAction[] actions */
- struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
- int iFrom; /* Index of column in pFrom */
- char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
- } aCol[1]; /* One entry for each of nCol column s */
+ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
+ u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
+ Trigger *apTrigger[2];/* Triggers for aAction[] actions */
+ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
+ int iFrom; /* Index of column in pFrom */
+ char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
+ } aCol[1]; /* One entry for each of nCol columns */
};
/*
@@ -1468,19 +1604,25 @@ struct FKey {
#define OE_SetDflt 8 /* Set the foreign key value to its default */
#define OE_Cascade 9 /* Cascade the changes */
-#define OE_Default 99 /* Do whatever the default action is */
+#define OE_Default 10 /* Do whatever the default action is */
/*
** An instance of the following structure is passed as the first
** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
+**
+** Note that aSortOrder[] and aColl[] have nField+1 slots. There
+** are nField slots for the columns of an index then one extra slot
+** for the rowid at the end.
*/
struct KeyInfo {
- sqlite3 *db; /* The database connection */
+ u32 nRef; /* Number of references to this KeyInfo object */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
- u16 nField; /* Number of entries in aColl[] */
- u8 *aSortOrder; /* Sort order for each column. May be NULL */
+ u16 nField; /* Number of key columns in the index */
+ u16 nXField; /* Number of columns beyond the key columns */
+ sqlite3 *db; /* The database connection */
+ u8 *aSortOrder; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
@@ -1497,21 +1639,20 @@ struct KeyInfo {
**
** This structure holds a record that has already been disassembled
** into its constituent fields.
+**
+** The r1 and r2 member variables are only used by the optimized comparison
+** functions vdbeRecordCompareInt() and vdbeRecordCompareString().
*/
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
u16 nField; /* Number of entries in apMem[] */
- u8 flags; /* Boolean settings. UNPACKED_... below */
- i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
+ i8 default_rc; /* Comparison result if keys are equal */
+ u8 isCorrupt; /* Corruption detected by xRecordCompare() */
Mem *aMem; /* Values */
+ int r1; /* Value to return if (lhs > rhs) */
+ int r2; /* Value to return if (rhs < lhs) */
};
-/*
-** Allowed values of UnpackedRecord.flags
-*/
-#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */
-#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */
-#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */
/*
** Each SQL index is represented in memory by an
@@ -1541,42 +1682,58 @@ struct UnpackedRecord {
*/
struct Index {
char *zName; /* Name of this index */
- int *aiColumn; /* Which columns are used by this index. 1st is 0 */
- tRowcnt *aiRowEst; /* From ANALYZE: Est. rows selected by each column */
+ i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
+ LogEst *aiRowLogEst; /* From ANALYZE: Est. rows selected by each column */
Table *pTable; /* The SQL table being indexed */
char *zColAff; /* String defining the affinity of each column */
Index *pNext; /* The next index associated with the same table */
Schema *pSchema; /* Schema containing this index */
u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
char **azColl; /* Array of collation sequence names for index */
+ Expr *pPartIdxWhere; /* WHERE clause for partial indices */
+ KeyInfo *pKeyInfo; /* A KeyInfo object suitable for this index */
int tnum; /* DB Page containing root of this index */
- u16 nColumn; /* Number of columns in table used by this index */
+ LogEst szIdxRow; /* Estimated average row size in bytes */
+ u16 nKeyCol; /* Number of columns forming the key */
+ u16 nColumn; /* Number of columns stored in the index */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- unsigned autoIndex:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
+ unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
-#ifdef SQLITE_ENABLE_STAT3
+ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
+ unsigned isResized:1; /* True if resizeIndexObject() has been called */
+ unsigned isCovering:1; /* True if this is a covering index */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
int nSample; /* Number of elements in aSample[] */
- tRowcnt avgEq; /* Average nEq value for key values not in aSample */
+ int nSampleCol; /* Size of IndexSample.anEq[] and so on */
+ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
IndexSample *aSample; /* Samples of the left-most key */
#endif
};
/*
+** Allowed values for Index.idxType
+*/
+#define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */
+#define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */
+#define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */
+
+/* Return true if index X is a PRIMARY KEY index */
+#define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY)
+
+/* Return true if index X is a UNIQUE index */
+#define IsUniqueIndex(X) ((X)->onError!=OE_None)
+
+/*
** Each sample stored in the sqlite_stat3 table is represented in memory
** using a structure of this type. See documentation at the top of the
** analyze.c source file for additional information.
*/
struct IndexSample {
- union {
- char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
- double r; /* Value if eType is SQLITE_FLOAT */
- i64 i; /* Value if eType is SQLITE_INTEGER */
- } u;
- u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
- int nByte; /* Size in byte of text or blob. */
- tRowcnt nEq; /* Est. number of rows where the key equals this sample */
- tRowcnt nLt; /* Est. number of rows where key is less than this sample */
- tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
+ void *p; /* Pointer to sampled record */
+ int n; /* Size of record in bytes */
+ tRowcnt *anEq; /* Est. number of rows where the key equals this sample */
+ tRowcnt *anLt; /* Est. number of rows where key is less than this sample */
+ tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */
};
/*
@@ -1613,6 +1770,7 @@ struct AggInfo {
int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */
int nSortingColumn; /* Number of columns in the sorting index */
+ int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
@@ -1717,7 +1875,7 @@ typedef int ynVar;
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
- u16 flags; /* Various flags. EP_* See below */
+ u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
int iValue; /* Non-negative integer value if EP_IntValue */
@@ -1731,8 +1889,8 @@ struct Expr {
Expr *pLeft; /* Left subnode */
Expr *pRight; /* Right subnode */
union {
- ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
- Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
+ ExprList *pList; /* op = IN, EXISTS, SELECT, CASE, FUNCTION, BETWEEN */
+ Select *pSelect; /* EP_xIsSelect and op = IN, EXISTS, SELECT */
} x;
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
@@ -1745,12 +1903,12 @@ struct Expr {
#endif
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
- ** TK_TRIGGER: 1 -> new, 0 -> old */
+ ** TK_TRIGGER: 1 -> new, 0 -> old
+ ** EP_Unlikely: 1000 times likelihood */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1). */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
- u8 flags2; /* Second set of flags. EP2_... */
u8 op2; /* TK_REGISTER: original value of Expr.op
** TK_COLUMN: the value of p5 for OP_Column
** TK_AGG_FUNCTION: nesting depth */
@@ -1761,51 +1919,47 @@ struct Expr {
/*
** The following are the meanings of bits in the Expr.flags field.
*/
-#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_DblQuoted 0x0040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_Collate 0x0100 /* Tree contains a TK_COLLATE opeartor */
-#define EP_FixedDest 0x0200 /* Result needed in a specific register */
-#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-#define EP_Hint 0x1000 /* Not used */
-#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */
+#define EP_FromJoin 0x000001 /* Originated in ON or USING clause of a join */
+#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
+#define EP_Resolved 0x000004 /* IDs have been resolved to COLUMNs */
+#define EP_Error 0x000008 /* Expression contains one or more errors */
+#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
+#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
+#define EP_Constant 0x080000 /* Node is a constant */
/*
-** The following are the meanings of bits in the Expr.flags2 field.
+** These macros can be used to test, set, or clear bits in the
+** Expr.flags field.
*/
-#define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP2_Irreducible 0x0002 /* Cannot EXPRDUP_REDUCE this Expr */
+#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
+#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
+#define ExprSetProperty(E,P) (E)->flags|=(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(P)
-/*
-** The pseudo-routine sqlite3ExprSetIrreducible sets the EP2_Irreducible
-** flag on an expression structure. This flag is used for VV&A only. The
-** routine is implemented as a macro that only works when in debugging mode,
-** so as not to burden production code.
+/* The ExprSetVVAProperty() macro is used for Verification, Validation,
+** and Accreditation only. It works like ExprSetProperty() during VVA
+** processes but is a no-op for delivery.
*/
#ifdef SQLITE_DEBUG
-# define ExprSetIrreducible(X) (X)->flags2 |= EP2_Irreducible
+# define ExprSetVVAProperty(E,P) (E)->flags|=(P)
#else
-# define ExprSetIrreducible(X)
+# define ExprSetVVAProperty(E,P)
#endif
/*
-** These macros can be used to test, set, or clear bits in the
-** Expr.flags field.
-*/
-#define ExprHasProperty(E,P) (((E)->flags&(P))==(P))
-#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0)
-#define ExprSetProperty(E,P) (E)->flags|=(P)
-#define ExprClearProperty(E,P) (E)->flags&=~(P)
-
-/*
** Macros to determine the number of bytes required by a normal Expr
** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
@@ -1838,7 +1992,6 @@ struct Expr {
*/
struct ExprList {
int nExpr; /* Number of expressions on the list */
- int iECursor; /* VDBE Cursor associated with this ExprList */
struct ExprList_item { /* For each expression in the list */
Expr *pExpr; /* The list of expressions */
char *zName; /* Token associated with this expression */
@@ -1846,8 +1999,14 @@ struct ExprList {
u8 sortOrder; /* 1 for DESC or 0 for ASC */
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
- u16 iOrderByCol; /* For ORDER BY, column number in result set */
- u16 iAlias; /* Index into Parse.aAlias[] for zName */
+ unsigned reusable :1; /* Constant expression is reusable */
+ union {
+ struct {
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
+ u16 iAlias; /* Index into Parse.aAlias[] for zName */
+ } x;
+ int iConstExprReg; /* Register in which Expr value is cached */
+ } u;
} *a; /* Alloc a power of two greater or equal to nExpr */
};
@@ -1900,6 +2059,12 @@ typedef u64 Bitmask;
#define BMS ((int)(sizeof(Bitmask)*8))
/*
+** A bit in a Bitmask
+*/
+#define MASKBIT(n) (((Bitmask)1)<<(n))
+#define MASKBIT32(n) (((unsigned int)1)<<(n))
+
+/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
** the SrcList.a[] array.
@@ -1919,8 +2084,8 @@ typedef u64 Bitmask;
** contains more than 63 columns and the 64-th or later column is used.
*/
struct SrcList {
- i16 nSrc; /* Number of tables or subqueries in the FROM clause */
- i16 nAlloc; /* Number of entries allocated in a[] below */
+ int nSrc; /* Number of tables or subqueries in the FROM clause */
+ u32 nAlloc; /* Number of entries allocated in a[] below */
struct SrcList_item {
Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
@@ -1930,10 +2095,12 @@ struct SrcList {
Select *pSelect; /* A SELECT statement used in place of a table name */
int addrFillSub; /* Address of subroutine to manifest a subquery */
int regReturn; /* Register holding return address of addrFillSub */
+ int regResult; /* Registers holding results of a co-routine */
u8 jointype; /* Type of join between this able and the previous */
unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
unsigned isCorrelated :1; /* True if sub-query is correlated */
unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
@@ -1959,79 +2126,6 @@ struct SrcList {
/*
-** A WherePlan object holds information that describes a lookup
-** strategy.
-**
-** This object is intended to be opaque outside of the where.c module.
-** It is included here only so that that compiler will know how big it
-** is. None of the fields in this object should be used outside of
-** the where.c module.
-**
-** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true.
-** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx
-** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the
-** case that more than one of these conditions is true.
-*/
-struct WherePlan {
- u32 wsFlags; /* WHERE_* flags that describe the strategy */
- u16 nEq; /* Number of == constraints */
- u16 nOBSat; /* Number of ORDER BY terms satisfied */
- double nRow; /* Estimated number of rows (for EQP) */
- union {
- Index *pIdx; /* Index when WHERE_INDEXED is true */
- struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
- sqlite3_index_info *pVtabIdx; /* Virtual table index to use */
- } u;
-};
-
-/*
-** For each nested loop in a WHERE clause implementation, the WhereInfo
-** structure contains a single instance of this structure. This structure
-** is intended to be private to the where.c module and should not be
-** access or modified by other modules.
-**
-** The pIdxInfo field is used to help pick the best index on a
-** virtual table. The pIdxInfo pointer contains indexing
-** information for the i-th table in the FROM clause before reordering.
-** All the pIdxInfo pointers are freed by whereInfoFree() in where.c.
-** All other information in the i-th WhereLevel object for the i-th table
-** after FROM clause ordering.
-*/
-struct WhereLevel {
- WherePlan plan; /* query plan for this element of the FROM clause */
- int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
- int iTabCur; /* The VDBE cursor used to access the table */
- int iIdxCur; /* The VDBE cursor used to access pIdx */
- int addrBrk; /* Jump here to break out of the loop */
- int addrNxt; /* Jump here to start the next IN combination */
- int addrCont; /* Jump here to continue with the next loop cycle */
- int addrFirst; /* First instruction of interior of the loop */
- u8 iFrom; /* Which entry in the FROM clause */
- u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
- union { /* Information that depends on plan.wsFlags */
- struct {
- int nIn; /* Number of entries in aInLoop[] */
- struct InLoop {
- int iCur; /* The VDBE cursor used by this IN operator */
- int addrInTop; /* Top of the IN loop */
- u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
- } *aInLoop; /* Information about each nested IN operator */
- } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
- Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
- } u;
- double rOptCost; /* "Optimal" cost for this level */
-
- /* The following field is really not part of the current level. But
- ** we need a place to cache virtual table index information for each
- ** virtual table in the FROM clause and the WhereLevel structure is
- ** a convenient place since there is one WhereLevel for each FROM clause
- ** element.
- */
- sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */
-};
-
-/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
** and the WhereInfo.wctrlFlags member.
*/
@@ -2044,33 +2138,14 @@ struct WhereLevel {
#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
+#define WHERE_GROUPBY 0x0100 /* pOrderBy is really a GROUP BY */
+#define WHERE_DISTINCTBY 0x0200 /* pOrderby is really a DISTINCT clause */
+#define WHERE_WANT_DISTINCT 0x0400 /* All output needs to be distinct */
+#define WHERE_SORTBYGROUP 0x0800 /* Support sqlite3WhereIsSorted() */
+#define WHERE_REOPEN_IDX 0x1000 /* Try to use OP_ReopenIdx */
-/*
-** 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
-** this structure is returned by the first half and passed
-** into the second half to give some continuity.
-*/
-struct WhereInfo {
- Parse *pParse; /* Parsing and code generating context */
- SrcList *pTabList; /* List of tables in the join */
- u16 nOBSat; /* Number of ORDER BY terms satisfied by indices */
- u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
- u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
- u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
- u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
- int iTop; /* The very beginning of the WHERE loop */
- int iContinue; /* Jump here to continue with next record */
- int iBreak; /* Jump here to break out of the loop */
- int nLevel; /* Number of nested loop */
- struct WhereClause *pWC; /* Decomposition of the WHERE clause */
- double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
- double nRowOut; /* Estimated number of output rows */
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
-};
-
-/* Allowed values for WhereInfo.eDistinct and DistinctCtx.eTnctType */
+/* Allowed return values from sqlite3WhereIsDistinct()
+*/
#define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */
#define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */
#define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */
@@ -2100,7 +2175,7 @@ struct WhereInfo {
struct NameContext {
Parse *pParse; /* The parser */
SrcList *pSrcList; /* One or more tables used to resolve names */
- ExprList *pEList; /* Optional list of named expressions */
+ ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
@@ -2115,8 +2190,7 @@ struct NameContext {
#define NC_HasAgg 0x02 /* One or more aggregate functions seen */
#define NC_IsCheck 0x04 /* True if resolving names in a CHECK constraint */
#define NC_InAggFunc 0x08 /* True if analyzing arguments to an agg func */
-#define NC_AsMaybe 0x10 /* Resolve to AS terms of the result set only
- ** if no other resolution is available */
+#define NC_PartIdx 0x10 /* True if resolving a partial index WHERE */
/*
** An instance of the following structure contains all information
@@ -2143,8 +2217,8 @@ struct Select {
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
u16 selFlags; /* Various SF_* values */
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
- int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
- double nSelectRow; /* Estimated number of result rows */
+ int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
+ u64 nSelectRow; /* Estimated number of result rows */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -2152,9 +2226,9 @@ struct Select {
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. */
+ With *pWith; /* WITH clause attached to this select. Or NULL. */
};
/*
@@ -2167,41 +2241,109 @@ struct Select {
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
-#define SF_UseSorter 0x0040 /* Sort using a sorter */
+ /* 0x0040 NOT USED */
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
-#define SF_Materialize 0x0100 /* Force materialization of views */
+ /* 0x0100 NOT USED */
#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
+#define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */
+#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */
+#define SF_Compound 0x1000 /* Part of a compound query */
/*
-** The results of a select can be distributed in several ways. The
-** "SRT" prefix means "SELECT Result Type".
+** The results of a SELECT can be distributed in several ways, as defined
+** by one of the following macros. The "SRT" prefix means "SELECT Result
+** Type".
+**
+** SRT_Union Store results as a key in a temporary index
+** identified by pDest->iSDParm.
+**
+** SRT_Except Remove results from the temporary index pDest->iSDParm.
+**
+** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result
+** set is not empty.
+**
+** SRT_Discard Throw the results away. This is used by SELECT
+** statements within triggers whose only purpose is
+** the side-effects of functions.
+**
+** All of the above are free to ignore their ORDER BY clause. Those that
+** follow must honor the ORDER BY clause.
+**
+** SRT_Output Generate a row of output (using the OP_ResultRow
+** opcode) for each row in the result set.
+**
+** SRT_Mem Only valid if the result is a single column.
+** Store the first column of the first result row
+** in register pDest->iSDParm then abandon the rest
+** of the query. This destination implies "LIMIT 1".
+**
+** SRT_Set The result must be a single column. Store each
+** row of result as the key in table pDest->iSDParm.
+** Apply the affinity pDest->affSdst before storing
+** results. Used to implement "IN (SELECT ...)".
+**
+** SRT_EphemTab Create an temporary table pDest->iSDParm and store
+** the result there. The cursor is left open after
+** returning. This is like SRT_Table except that
+** this destination uses OP_OpenEphemeral to create
+** the table first.
+**
+** SRT_Coroutine Generate a co-routine that returns a new row of
+** results each time it is invoked. The entry point
+** of the co-routine is stored in register pDest->iSDParm
+** and the result row is stored in pDest->nDest registers
+** starting with pDest->iSdst.
+**
+** SRT_Table Store results in temporary table pDest->iSDParm.
+** SRT_Fifo This is like SRT_EphemTab except that the table
+** is assumed to already be open. SRT_Fifo has
+** the additional property of being able to ignore
+** the ORDER BY clause.
+**
+** SRT_DistFifo Store results in a temporary table pDest->iSDParm.
+** But also use temporary table pDest->iSDParm+1 as
+** a record of all prior results and ignore any duplicate
+** rows. Name means: "Distinct Fifo".
+**
+** SRT_Queue Store results in priority queue pDest->iSDParm (really
+** an index). Append a sequence number so that all entries
+** are distinct.
+**
+** SRT_DistQueue Store results in priority queue pDest->iSDParm only if
+** the same record has never been stored before. The
+** index at pDest->iSDParm+1 hold all prior stores.
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
#define SRT_Exists 3 /* Store 1 if the result is not empty */
#define SRT_Discard 4 /* Do not save the results anywhere */
+#define SRT_Fifo 5 /* Store result as data with an automatic rowid */
+#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */
+#define SRT_Queue 7 /* Store result in an queue */
+#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */
/* The ORDER BY clause is ignored for all of the above */
-#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard)
+#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue)
-#define SRT_Output 5 /* Output 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 */
+#define SRT_Output 9 /* Output each row of result */
+#define SRT_Mem 10 /* Store result in a memory cell */
+#define SRT_Set 11 /* Store results as keys in an index */
+#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
+#define SRT_Coroutine 13 /* Generate a single row of result */
+#define SRT_Table 14 /* Store result as data with an automatic rowid */
/*
** An instance of this object describes where to put of the results of
** a SELECT statement.
*/
struct SelectDest {
- u8 eDest; /* How to dispose of the results. On of SRT_* above. */
- char affSdst; /* Affinity used when eDest==SRT_Set */
- int iSDParm; /* A parameter used by the eDest disposal method */
- int iSdst; /* Base register where results are written */
- int nSdst; /* Number of registers allocated */
+ u8 eDest; /* How to dispose of the results. On of SRT_* above. */
+ char affSdst; /* Affinity used when eDest==SRT_Set */
+ int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSdst; /* Base register where results are written */
+ int nSdst; /* Number of registers allocated */
+ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
/*
@@ -2257,9 +2399,19 @@ struct TriggerPrg {
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED>30
- typedef sqlite3_uint64 yDbMask;
+ typedef unsigned char yDbMask[(SQLITE_MAX_ATTACHED+9)/8];
+# define DbMaskTest(M,I) (((M)[(I)/8]&(1<<((I)&7)))!=0)
+# define DbMaskZero(M) memset((M),0,sizeof(M))
+# define DbMaskSet(M,I) (M)[(I)/8]|=(1<<((I)&7))
+# define DbMaskAllZero(M) sqlite3DbMaskAllZero(M)
+# define DbMaskNonZero(M) (sqlite3DbMaskAllZero(M)==0)
#else
typedef unsigned int yDbMask;
+# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
+# define DbMaskZero(M) (M)=0
+# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
+# define DbMaskAllZero(M) (M)==0
+# define DbMaskNonZero(M) (M)!=0
#endif
/*
@@ -2287,11 +2439,10 @@ struct Parse {
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
- u8 nTempInUse; /* Number of aTempReg[] currently checked out */
- u8 nColCache; /* Number of entries in aColCache[] */
- u8 iColCache; /* Next entry in aColCache[] to replace */
u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
+ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
+ u8 okConstFactor; /* OK to factor out constants */
int aTempReg[8]; /* Holding area for temporary registers */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
@@ -2300,25 +2451,30 @@ struct Parse {
int nMem; /* Number of memory cells used so far */
int nSet; /* Number of sets used so far */
int nOnce; /* Number of OP_Once instructions so far */
+ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
+ int iFixedOp; /* Never back out opcodes iFixedOp-1 or earlier */
int ckBase; /* Base register of data during check constraints */
+ int iPartIdxTab; /* Table corresponding to a partial index */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
+ int nLabel; /* Number of labels used */
+ int *aLabel; /* Space to hold the labels */
struct yColCache {
int iTable; /* Table cursor number */
- int iColumn; /* Table column number */
+ i16 iColumn; /* Table column number */
u8 tempReg; /* iReg is a temp register that needs to be freed */
int iLevel; /* Nesting level */
int iReg; /* Reg with value of this column. 0 means none. */
int lru; /* Least recently used entry has the smallest value */
} aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
+ ExprList *pConstExpr;/* Constant expressions */
+ Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
- int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
int regRowid; /* Register holding rowid of CREATE TABLE entry */
int regRoot; /* Register holding root page number for new objects */
int nMaxArg; /* Max args passed to user function by sub-program */
- Token constraintName;/* Name of the constraint currently being parsed */
#ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */
@@ -2328,18 +2484,26 @@ struct Parse {
/* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
- double nQueryLoop; /* Estimated number of iterations of a query */
+ int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
+ int addrSkipPK; /* Address of instruction to skip PRIMARY KEY index */
+ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
- /* Above is constant between recursions. Below is reset before and after
- ** each recursion */
+ /************************************************************************
+ ** Above is constant between recursions. Below is reset before and after
+ ** each recursion. The boundary between these two regions is determined
+ ** using offsetof(Parse,nVar) so the nVar field must be the first field
+ ** in the recursive region.
+ ************************************************************************/
int nVar; /* Number of '?' variables seen in the SQL so far */
int nzVar; /* Number of available slots in azVar[] */
+ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
+ u8 bFreeWith; /* True if pWith should be freed with parser */
u8 explain; /* True if the EXPLAIN flag is found on the query */
#ifndef SQLITE_OMIT_VIRTUALTABLE
u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
@@ -2353,7 +2517,6 @@ struct Parse {
#endif
char **azVar; /* Pointers to names of parameters */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
- int *aAlias; /* Register used to hold aliased result */
const char *zTail; /* All SQL text past the last semicolon parsed */
Table *pNewTable; /* A table being constructed by CREATE TABLE */
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
@@ -2366,6 +2529,7 @@ struct Parse {
#endif
Table *pZombieTab; /* List of Table objects to delete after code gen */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
+ With *pWith; /* Current WITH clause, or NULL */
};
/*
@@ -2485,7 +2649,7 @@ struct TriggerStep {
Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */
Token target; /* Target table for DELETE, UPDATE, INSERT */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */
+ ExprList *pExprList; /* SET clause for UPDATE. */
IdList *pIdList; /* Column names for INSERT */
TriggerStep *pNext; /* Next in the link-list */
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
@@ -2500,6 +2664,7 @@ typedef struct DbFixer DbFixer;
struct DbFixer {
Parse *pParse; /* The parsing context. Error messages written here */
Schema *pSchema; /* Fix items to this schema */
+ int bVarOnly; /* Check for variable references only */
const char *zDb; /* Make sure all objects are contained in this database */
const char *zType; /* Type of the container - used for error messages */
const Token *pName; /* Name of the container - used for error messages */
@@ -2516,10 +2681,11 @@ struct StrAccum {
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; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */
- u8 tooBig; /* Becomes true if string size exceeds limits */
+ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
};
+#define STRACCUM_NOMEM 1
+#define STRACCUM_TOOBIG 2
/*
** A pointer to this structure is used to communicate information
@@ -2544,6 +2710,7 @@ struct Sqlite3Config {
int bOpenUri; /* True to interpret filenames as URIs */
int bUseCis; /* Use covering indices for full-scans */
int mxStrlen; /* Maximum string length */
+ int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
sqlite3_mem_methods m; /* Low-level memory allocation interface */
@@ -2569,26 +2736,54 @@ struct Sqlite3Config {
int isMutexInit; /* True after mutexes are initialized */
int isMallocInit; /* True after malloc is initialized */
int isPCacheInit; /* True after malloc is initialized */
- sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
int nRefInitMutex; /* Number of users of pInitMutex */
+ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
void (*xLog)(void*,int,const char*); /* Function for logging */
void *pLogArg; /* First argument to xLog() */
- int bLocaltimeFault; /* True to fail localtime() calls */
#ifdef SQLITE_ENABLE_SQLLOG
void(*xSqllog)(void*,sqlite3*,const char*, int);
void *pSqllogArg;
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ /* The following callback (if not NULL) is invoked on every VDBE branch
+ ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE.
+ */
+ void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */
+ void *pVdbeBranchArg; /* 1st argument */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
+#endif
+ int bLocaltimeFault; /* True to fail localtime() calls */
};
/*
+** This macro is used inside of assert() statements to indicate that
+** the assert is only valid on a well-formed database. Instead of:
+**
+** assert( X );
+**
+** One writes:
+**
+** assert( X || CORRUPT_DB );
+**
+** CORRUPT_DB is true during normal operation. CORRUPT_DB does not indicate
+** that the database is definitely corrupt, only that it might be corrupt.
+** For most test cases, CORRUPT_DB is set to false using a special
+** sqlite3_test_control(). This enables assert() statements to prove
+** things that are always true for well-formed databases.
+*/
+#define CORRUPT_DB (sqlite3Config.neverCorrupt==0)
+
+/*
** Context pointer passed down through the tree-walk.
*/
struct Walker {
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
+ void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
Parse *pParse; /* Parser context. */
int walkerDepth; /* Number of subqueries */
- u8 bSelectDepthFirst; /* Do subqueries first */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
int i; /* Integer value */
@@ -2613,6 +2808,21 @@ int sqlite3WalkSelectFrom(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
+** An instance of this structure represents a set of one or more CTEs
+** (common table expressions) created by a single WITH clause.
+*/
+struct With {
+ int nCte; /* Number of CTEs in the WITH clause */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ struct Cte { /* For each CTE in the WITH clause.... */
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zErr; /* Error message for circular references */
+ } a[1];
+};
+
+/*
** 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.
*/
@@ -2751,10 +2961,20 @@ void sqlite3StatusSet(int, int);
# define sqlite3IsNaN(X) 0
#endif
-void sqlite3VXPrintf(StrAccum*, int, const char*, va_list);
-#ifndef SQLITE_OMIT_TRACE
-void sqlite3XPrintf(StrAccum*, const char*, ...);
-#endif
+/*
+** An instance of the following structure holds information about SQL
+** functions arguments that are the parameters to the printf() function.
+*/
+struct PrintfArguments {
+ int nArg; /* Total number of arguments */
+ int nUsed; /* Number of arguments used so far */
+ sqlite3_value **apArg; /* The argument values */
+};
+
+#define SQLITE_PRINTF_INTERNAL 0x01
+#define SQLITE_PRINTF_SQLFUNC 0x02
+void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list);
+void sqlite3XPrintf(StrAccum*, u32, const char*, ...);
char *sqlite3MPrintf(sqlite3*,const char*, ...);
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
@@ -2820,6 +3040,8 @@ void sqlite3BeginParse(Parse*,int);
void sqlite3CommitInternalChanges(sqlite3*);
Table *sqlite3ResultSetOfSelect(Parse*,Select*);
void sqlite3OpenMasterTable(Parse *, int);
+Index *sqlite3PrimaryKeyIndex(Table*);
+i16 sqlite3ColumnOfIndex(Index*, i16);
void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
void sqlite3AddColumn(Parse*,Token*);
void sqlite3AddNotNull(Parse*, int);
@@ -2828,12 +3050,18 @@ void sqlite3AddCheckConstraint(Parse*, Expr*);
void sqlite3AddColumnType(Parse*,Token*);
void sqlite3AddDefaultValue(Parse*,ExprSpan*);
void sqlite3AddCollateType(Parse*, Token*);
-void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
int sqlite3CodeOnce(Parse *);
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+# define sqlite3FaultSim(X) SQLITE_OK
+#else
+ int sqlite3FaultSim(int);
+#endif
+
Bitvec *sqlite3BitvecCreate(u32);
int sqlite3BitvecTest(Bitvec*, u32);
int sqlite3BitvecSet(Bitvec*, u32);
@@ -2845,7 +3073,7 @@ int sqlite3BitvecBuiltinTest(int,int*);
RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
void sqlite3RowSetClear(RowSet*);
void sqlite3RowSetInsert(RowSet*, i64);
-int sqlite3RowSetTest(RowSet*, u8 iBatch, i64);
+int sqlite3RowSetTest(RowSet*, int iBatch, i64);
int sqlite3RowSetNext(RowSet*, i64*);
void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
@@ -2856,6 +3084,9 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
# define sqlite3ViewGetColumnNames(A,B) 0
#endif
+#if SQLITE_MAX_ATTACHED>30
+ int sqlite3DbMaskAllZero(yDbMask);
+#endif
void sqlite3DropTable(Parse*, SrcList*, int, int);
void sqlite3CodeDropTable(Parse*, Table*, int, int);
void sqlite3DeleteTable(sqlite3*, Table*);
@@ -2866,8 +3097,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
-int sqlite3CodeCoroutine(Parse*, Select*, SelectDest*);
-void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
+void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
@@ -2881,8 +3111,9 @@ void sqlite3SrcListShiftJoinType(SrcList*);
void sqlite3SrcListAssignCursors(Parse*, SrcList*);
void sqlite3IdListDelete(sqlite3*, IdList*);
void sqlite3SrcListDelete(sqlite3*, SrcList*);
+Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
- Token*, int, int);
+ Expr*, int, int);
void sqlite3DropIndex(Parse*, SrcList*, int);
int sqlite3Select(Parse*, Select*, SelectDest*);
Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
@@ -2898,21 +3129,31 @@ void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
void sqlite3WhereEnd(WhereInfo*);
+u64 sqlite3WhereOutputRowCount(WhereInfo*);
+int sqlite3WhereIsDistinct(WhereInfo*);
+int sqlite3WhereIsOrdered(WhereInfo*);
+int sqlite3WhereIsSorted(WhereInfo*);
+int sqlite3WhereContinueLabel(WhereInfo*);
+int sqlite3WhereBreakLabel(WhereInfo*);
+int sqlite3WhereOkOnePass(WhereInfo*, int*);
int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
void sqlite3ExprCodeMove(Parse*, int, int, int);
void sqlite3ExprCacheStore(Parse*, int, int, int);
void sqlite3ExprCachePush(Parse*);
-void sqlite3ExprCachePop(Parse*, int);
+void sqlite3ExprCachePop(Parse*);
void sqlite3ExprCacheRemove(Parse*, int, int);
void sqlite3ExprCacheClear(Parse*);
void sqlite3ExprCacheAffinityChange(Parse*, int, int);
-int sqlite3ExprCode(Parse*, Expr*, int);
+void sqlite3ExprCode(Parse*, Expr*, int);
+void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
+void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
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 sqlite3ExprCodeAndCache(Parse*, Expr*, int);
+int sqlite3ExprCodeExprList(Parse*, ExprList*, int, u8);
+#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
+#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
Table *sqlite3FindTable(sqlite3*,const char*, const char*);
@@ -2924,15 +3165,15 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
void sqlite3Vacuum(Parse*);
int sqlite3RunVacuum(char**, sqlite3*);
char *sqlite3NameFromToken(sqlite3*, Token*);
-int sqlite3ExprCompare(Expr*, Expr*);
-int sqlite3ExprListCompare(ExprList*, ExprList*);
+int sqlite3ExprCompare(Expr*, Expr*, int);
+int sqlite3ExprListCompare(ExprList*, ExprList*, int);
+int sqlite3ExprImpliesExpr(Expr*, Expr*, int);
void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
Vdbe *sqlite3GetVdbe(Parse*);
void sqlite3PrngSaveState(void);
void sqlite3PrngRestoreState(void);
-void sqlite3PrngResetState(void);
void sqlite3RollbackAll(sqlite3*,int);
void sqlite3CodeVerifySchema(Parse*, int);
void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
@@ -2947,20 +3188,22 @@ int sqlite3ExprIsConstantNotJoin(Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*);
int sqlite3ExprIsInteger(Expr*, int*);
int sqlite3ExprCanBeNull(const Expr*);
-void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int);
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
int sqlite3IsRowid(const char*);
-void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, 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,int*);
-void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
-int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
+void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8);
+void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
+int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
+void sqlite3ResolvePartIdxLabel(Parse*,int);
+void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
+ u8,u8,int,int*);
+void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
+int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, u8*, int*, int*);
void sqlite3BeginWriteOperation(Parse*, int, int);
void sqlite3MultiWrite(Parse*);
void sqlite3MayAbort(Parse*);
-void sqlite3HaltConstraint(Parse*, int, int, char*, int);
+void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
+void sqlite3UniqueConstraint(Parse*, int, Index*);
+void sqlite3RowidConstraint(Parse*, int, Table*);
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
@@ -2994,7 +3237,7 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, int);
void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
- ExprList*,Select*,u8);
+ Select*,u8);
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
void sqlite3DeleteTrigger(sqlite3*, Trigger*);
@@ -3030,7 +3273,7 @@ void sqlite3DeferForeignKey(Parse*, int);
#endif
void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
void sqlite3Detach(Parse*, Expr*);
-int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
+void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
int sqlite3FixSrcList(DbFixer*, SrcList*);
int sqlite3FixSelect(DbFixer*, Select*);
int sqlite3FixExpr(DbFixer*, Expr*);
@@ -3042,6 +3285,12 @@ int sqlite3Atoi(const char*);
int sqlite3Utf16ByteLen(const void *pData, int nChar);
int sqlite3Utf8CharLen(const char *pData, int nByte);
u32 sqlite3Utf8Read(const u8**);
+LogEst sqlite3LogEst(u64);
+LogEst sqlite3LogEstAdd(LogEst,LogEst);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+LogEst sqlite3LogEstFromDouble(double);
+#endif
+u64 sqlite3LogEstToInt(LogEst);
/*
** Routines to read and write variable-length integers. These used to
@@ -3083,18 +3332,18 @@ int sqlite3VarintLen(u64 v);
const char *sqlite3IndexAffinityStr(Vdbe *, Index *);
-void sqlite3TableAffinityStr(Vdbe *, Table *);
+void sqlite3TableAffinity(Vdbe*, Table*, int);
char sqlite3CompareAffinity(Expr *pExpr, char aff2);
int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
char sqlite3ExprAffinity(Expr *pExpr);
int sqlite3Atoi64(const char*, i64*, int, u8);
+int sqlite3DecOrHexToI64(const char*, i64*);
void sqlite3Error(sqlite3*, int, const char*,...);
void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
u8 sqlite3HexToInt(int h);
int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
-#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) || \
- defined(SQLITE_DEBUG_OS_TRACE)
+#if defined(SQLITE_TEST)
const char *sqlite3ErrName(int);
#endif
@@ -3103,7 +3352,7 @@ int sqlite3ReadSchema(Parse *pParse);
CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, Token*);
+Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*);
Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
Expr *sqlite3ExprSkipCollate(Expr*);
int sqlite3CheckCollSeq(Parse *, CollSeq *);
@@ -3118,18 +3367,16 @@ void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
-u8 sqlite3GetBoolean(const char *z,int);
+u8 sqlite3GetBoolean(const char *z,u8);
const void *sqlite3ValueText(sqlite3_value*, u8);
int sqlite3ValueBytes(sqlite3_value*, u8);
void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void(*)(void*));
+void sqlite3ValueSetNull(sqlite3_value*);
void sqlite3ValueFree(sqlite3_value*);
sqlite3_value *sqlite3ValueNew(sqlite3 *);
char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
-#ifdef SQLITE_ENABLE_STAT3
-char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
-#endif
int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
@@ -3155,12 +3402,13 @@ void sqlite3SelectPrep(Parse*, Select*, NameContext*);
int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
int sqlite3ResolveExprNames(NameContext*, Expr*);
void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
+void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
void sqlite3AlterFinishAddColumn(Parse *, Token *);
void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
-char sqlite3AffinityType(const char*);
+char sqlite3AffinityType(const char*, u8*);
void sqlite3Analyze(Parse*, Token*, Token*);
int sqlite3InvokeBusyHandler(BusyHandler*);
int sqlite3FindDb(sqlite3*, Token*);
@@ -3174,7 +3422,13 @@ void sqlite3MinimumFileFormat(Parse*, int, int);
void sqlite3SchemaClear(void *);
Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
-KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
+KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
+void sqlite3KeyInfoUnref(KeyInfo*);
+KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
+KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
+#ifdef SQLITE_DEBUG
+int sqlite3KeyInfoIsWriteable(KeyInfo*);
+#endif
int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
@@ -3185,6 +3439,7 @@ int sqlite3OpenTempDatabase(Parse *);
void sqlite3StrAccumInit(StrAccum*, char*, int, int);
void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+void sqlite3StrAccumAppendAll(StrAccum*,const char*);
void sqlite3AppendSpace(StrAccum*,int);
char *sqlite3StrAccumFinish(StrAccum*);
void sqlite3StrAccumReset(StrAccum*);
@@ -3194,6 +3449,14 @@ Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
void sqlite3BackupRestart(sqlite3_backup *);
void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+void sqlite3AnalyzeFunctions(void);
+int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
+int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
+void sqlite3Stat4ProbeFree(UnpackedRecord*);
+int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
+#endif
+
/*
** The interface to the LEMON-generated parser
*/
@@ -3235,13 +3498,14 @@ void sqlite3AutoLoadExtensions(sqlite3*);
#else
void sqlite3VtabClear(sqlite3 *db, Table*);
void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
- int sqlite3VtabSync(sqlite3 *db, char **);
+ int sqlite3VtabSync(sqlite3 *db, Vdbe*);
int sqlite3VtabRollback(sqlite3 *db);
int sqlite3VtabCommit(sqlite3 *db);
void sqlite3VtabLock(VTable *);
void sqlite3VtabUnlock(VTable *);
void sqlite3VtabUnlockList(sqlite3*);
int sqlite3VtabSavepoint(sqlite3 *, int, int);
+ void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
@@ -3256,8 +3520,10 @@ int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
int sqlite3VtabBegin(sqlite3 *, VTable *);
FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
+void sqlite3ParserReset(Parse*);
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
@@ -3267,6 +3533,14 @@ const char *sqlite3JournalModename(int);
int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
+#ifndef SQLITE_OMIT_CTE
+ With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+ void sqlite3WithDelete(sqlite3*,With*);
+ void sqlite3WithPush(Parse*, With*, u8);
+#else
+#define sqlite3WithPush(x,y,z)
+#define sqlite3WithDelete(x,y)
+#endif
/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
@@ -3276,18 +3550,18 @@ const char *sqlite3JournalModename(int);
** provided (enforcement of FK constraints requires the triggers sub-system).
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- void sqlite3FkCheck(Parse*, Table*, int, int);
+ void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
void sqlite3FkDropTable(Parse*, SrcList *, Table*);
- void sqlite3FkActions(Parse*, Table*, ExprList*, int);
+ void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
int sqlite3FkRequired(Parse*, Table*, int*, int);
u32 sqlite3FkOldmask(Parse*, Table*);
FKey *sqlite3FkReferences(Table *);
#else
- #define sqlite3FkActions(a,b,c,d)
- #define sqlite3FkCheck(a,b,c,d)
+ #define sqlite3FkActions(a,b,c,d,e,f)
+ #define sqlite3FkCheck(a,b,c,d,e,f)
#define sqlite3FkDropTable(a,b,c)
- #define sqlite3FkOldmask(a,b) 0
- #define sqlite3FkRequired(a,b,c,d) 0
+ #define sqlite3FkOldmask(a,b) 0
+ #define sqlite3FkRequired(a,b,c,d) 0
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
void sqlite3FkDelete(sqlite3 *, Table*);
@@ -3317,11 +3591,21 @@ const char *sqlite3JournalModename(int);
#define sqlite3EndBenignMalloc()
#endif
-#define IN_INDEX_ROWID 1
-#define IN_INDEX_EPH 2
-#define IN_INDEX_INDEX_ASC 3
-#define IN_INDEX_INDEX_DESC 4
-int sqlite3FindInIndex(Parse *, Expr *, int*);
+/*
+** Allowed return values from sqlite3FindInIndex()
+*/
+#define IN_INDEX_ROWID 1 /* Search the rowid of the table */
+#define IN_INDEX_EPH 2 /* Search an ephemeral b-tree */
+#define IN_INDEX_INDEX_ASC 3 /* Existing index ASCENDING */
+#define IN_INDEX_INDEX_DESC 4 /* Existing index DESCENDING */
+#define IN_INDEX_NOOP 5 /* No table available. Use comparisons */
+/*
+** Allowed flags for the 3rd parameter to sqlite3FindInIndex().
+*/
+#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
+#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
+#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
+int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
diff --git a/src/status.c b/src/status.c
index 28349e6..5fcb68d 100644
--- a/src/status.c
+++ b/src/status.c
@@ -243,6 +243,16 @@ int sqlite3_db_status(
break;
}
+ /* Set *pCurrent to non-zero if there are unresolved deferred foreign
+ ** key constraints. Set *pCurrent to zero if all foreign key constraints
+ ** have been satisfied. The *pHighwater is always set to zero.
+ */
+ case SQLITE_DBSTATUS_DEFERRED_FKS: {
+ *pHighwater = 0;
+ *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
}
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index f1bb292..9b977e5 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -41,6 +41,18 @@
#endif
#include <ctype.h>
+/* Used to get the current process ID */
+#if !defined(_WIN32)
+# include <unistd.h>
+# define GETPID getpid
+#elif !defined(_WIN32_WCE)
+# ifndef SQLITE_AMALGAMATION
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# endif
+# define GETPID (int)GetCurrentProcessId
+#endif
+
/*
* Windows needs to know which symbols to export. Unix does not.
* BUILD_sqlite should be undefined for Unix.
@@ -412,13 +424,12 @@ static int safeToUseEvalObjv(Tcl_Interp *interp, Tcl_Obj *pCmd){
*/
static SqlFunc *findSqlFunc(SqliteDb *pDb, const char *zName){
SqlFunc *p, *pNew;
- int i;
- pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + strlen30(zName) + 1 );
+ int nName = strlen30(zName);
+ pNew = (SqlFunc*)Tcl_Alloc( sizeof(*pNew) + nName + 1 );
pNew->zName = (char*)&pNew[1];
- for(i=0; zName[i]; i++){ pNew->zName[i] = tolower(zName[i]); }
- pNew->zName[i] = 0;
+ memcpy(pNew->zName, zName, nName+1);
for(p=pDb->pFunc; p; p=p->pNext){
- if( strcmp(p->zName, pNew->zName)==0 ){
+ if( sqlite3_stricmp(p->zName, pNew->zName)==0 ){
Tcl_Free((char*)pNew);
return p;
}
@@ -862,7 +873,7 @@ static int auth_callback(
const char *zArg3,
const char *zArg4
){
- char *zCode;
+ const char *zCode;
Tcl_DString str;
int rc;
const char *zReply;
@@ -903,6 +914,7 @@ static int auth_callback(
case SQLITE_DROP_VTABLE : zCode="SQLITE_DROP_VTABLE"; break;
case SQLITE_FUNCTION : zCode="SQLITE_FUNCTION"; break;
case SQLITE_SAVEPOINT : zCode="SQLITE_SAVEPOINT"; break;
+ case SQLITE_RECURSIVE : zCode="SQLITE_RECURSIVE"; break;
default : zCode="????"; break;
}
Tcl_DStringInit(&str);
@@ -987,7 +999,7 @@ static int DbTransPostCmd(
Tcl_Interp *interp, /* Tcl interpreter */
int result /* Result of evaluating SCRIPT */
){
- static const char *azEnd[] = {
+ static const char *const azEnd[] = {
"RELEASE _tcl_transaction", /* rc==TCL_ERROR, nTransaction!=0 */
"COMMIT", /* rc!=TCL_ERROR, nTransaction==0 */
"ROLLBACK TO _tcl_transaction ; RELEASE _tcl_transaction",
@@ -1013,7 +1025,7 @@ static int DbTransPostCmd(
** this method's logic. Not clear how this would be best handled.
*/
if( rc!=TCL_ERROR ){
- Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
rc = TCL_ERROR;
}
sqlite3_exec(pDb->db, "ROLLBACK", 0, 0, 0);
@@ -1071,13 +1083,14 @@ static int dbPrepareAndBind(
int nSql; /* Length of zSql in bytes */
int nVar; /* Number of variables in statement */
int iParm = 0; /* Next free entry in apParm */
+ char c;
int i;
Tcl_Interp *interp = pDb->interp;
*ppPreStmt = 0;
/* Trim spaces from the start of zSql and calculate the remaining length. */
- while( isspace(zSql[0]) ){ zSql++; }
+ while( (c = zSql[0])==' ' || c=='\t' || c=='\r' || c=='\n' ){ zSql++; }
nSql = strlen30(zSql);
for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
@@ -1518,9 +1531,9 @@ static int DbUseNre(void){
*/
# define SQLITE_TCL_NRE 0
# define DbUseNre() 0
-# define Tcl_NRAddCallback(a,b,c,d,e,f) 0
+# define Tcl_NRAddCallback(a,b,c,d,e,f) (void)0
# define Tcl_NREvalObj(a,b,c) 0
-# define Tcl_NRCreateCommand(a,b,c,d,e,f) 0
+# define Tcl_NRCreateCommand(a,b,c,d,e,f) (void)0
#endif
/*
@@ -1662,7 +1675,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
*/
case DB_AUTHORIZER: {
#ifdef SQLITE_OMIT_AUTHORIZATION
- Tcl_AppendResult(interp, "authorization not available in this build", 0);
+ Tcl_AppendResult(interp, "authorization not available in this build",
+ (char*)0);
return TCL_ERROR;
#else
if( objc>3 ){
@@ -1670,7 +1684,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zAuth ){
- Tcl_AppendResult(interp, pDb->zAuth, 0);
+ Tcl_AppendResult(interp, pDb->zAuth, (char*)0);
}
}else{
char *zAuth;
@@ -1756,7 +1770,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zBusy ){
- Tcl_AppendResult(interp, pDb->zBusy, 0);
+ Tcl_AppendResult(interp, pDb->zBusy, (char*)0);
}
}else{
char *zBusy;
@@ -1810,7 +1824,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
if( TCL_ERROR==Tcl_GetIntFromObj(interp, objv[3], &n) ){
Tcl_AppendResult( interp, "cannot convert \"",
- Tcl_GetStringFromObj(objv[3],0), "\" to integer", 0);
+ Tcl_GetStringFromObj(objv[3],0), "\" to integer", (char*)0);
return TCL_ERROR;
}else{
if( n<0 ){
@@ -1824,7 +1838,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
}else{
Tcl_AppendResult( interp, "bad option \"",
- Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size", 0);
+ Tcl_GetStringFromObj(objv[2],0), "\": must be flush or size",
+ (char*)0);
return TCL_ERROR;
}
break;
@@ -1921,10 +1936,10 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zCommit ){
- Tcl_AppendResult(interp, pDb->zCommit, 0);
+ Tcl_AppendResult(interp, pDb->zCommit, (char*)0);
}
}else{
- char *zCommit;
+ const char *zCommit;
int len;
if( pDb->zCommit ){
Tcl_Free(pDb->zCommit);
@@ -1997,14 +2012,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
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 */
+ const 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;
+ const char *zSep;
+ const char *zNull;
if( objc<5 || objc>7 ){
Tcl_WrongNumArgs(interp, 2, objv,
"CONFLICT-ALGORITHM TABLE FILENAME ?SEPARATOR? ?NULLINDICATOR?");
@@ -2026,7 +2041,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
nSep = strlen30(zSep);
nNull = strlen30(zNull);
if( nSep==0 ){
- Tcl_AppendResult(interp,"Error: non-null separator required for copy",0);
+ Tcl_AppendResult(interp,"Error: non-null separator required for copy",
+ (char*)0);
return TCL_ERROR;
}
if(strcmp(zConflict, "rollback") != 0 &&
@@ -2036,19 +2052,19 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
strcmp(zConflict, "replace" ) != 0 ) {
Tcl_AppendResult(interp, "Error: \"", zConflict,
"\", conflict-algorithm must be one of: rollback, "
- "abort, fail, ignore, or replace", 0);
+ "abort, fail, ignore, or replace", (char*)0);
return TCL_ERROR;
}
zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
if( zSql==0 ){
- Tcl_AppendResult(interp, "Error: no such table: ", zTable, 0);
+ Tcl_AppendResult(interp, "Error: no such table: ", zTable, (char*)0);
return TCL_ERROR;
}
nByte = strlen30(zSql);
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ){
- Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0);
nCol = 0;
}else{
nCol = sqlite3_column_count(pStmt);
@@ -2059,7 +2075,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
zSql = malloc( nByte + 50 + nCol*2 );
if( zSql==0 ) {
- Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+ Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
return TCL_ERROR;
}
sqlite3_snprintf(nByte+50, zSql, "INSERT OR %q INTO '%q' VALUES(?",
@@ -2074,7 +2090,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
rc = sqlite3_prepare(pDb->db, zSql, -1, &pStmt, 0);
free(zSql);
if( rc ){
- Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp, "Error: ", sqlite3_errmsg(pDb->db), (char*)0);
sqlite3_finalize(pStmt);
return TCL_ERROR;
}
@@ -2086,7 +2102,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
azCol = malloc( sizeof(azCol[0])*(nCol+1) );
if( azCol==0 ) {
- Tcl_AppendResult(interp, "Error: can't malloc()", 0);
+ Tcl_AppendResult(interp, "Error: can't malloc()", (char*)0);
fclose(in);
return TCL_ERROR;
}
@@ -2114,7 +2130,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
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);
+ Tcl_AppendResult(interp, zErr, (char*)0);
free(zErr);
}
zCommit = "ROLLBACK";
@@ -2134,7 +2150,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
rc = sqlite3_reset(pStmt);
free(zLine);
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp,"Error: ", sqlite3_errmsg(pDb->db), (char*)0);
zCommit = "ROLLBACK";
break;
}
@@ -2152,7 +2168,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
/* failure, append lineno where failed */
sqlite3_snprintf(sizeof(zLineNum), zLineNum,"%d",lineno);
- Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,0);
+ Tcl_AppendResult(interp,", failed while processing line: ",zLineNum,
+ (char*)0);
rc = TCL_ERROR;
}
break;
@@ -2178,7 +2195,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
break;
#else
Tcl_AppendResult(interp, "extension loading is turned off at compile-time",
- 0);
+ (char*)0);
return TCL_ERROR;
#endif
}
@@ -2336,7 +2353,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
*/
case DB_INCRBLOB: {
#ifdef SQLITE_OMIT_INCRBLOB
- Tcl_AppendResult(interp, "incrblob not available in this build", 0);
+ Tcl_AppendResult(interp, "incrblob not available in this build", (char*)0);
return TCL_ERROR;
#else
int isReadonly = 0;
@@ -2443,7 +2460,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
case DB_PROGRESS: {
if( objc==2 ){
if( pDb->zProgress ){
- Tcl_AppendResult(interp, pDb->zProgress, 0);
+ Tcl_AppendResult(interp, pDb->zProgress, (char*)0);
}
}else if( objc==4 ){
char *zProgress;
@@ -2489,7 +2506,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zProfile ){
- Tcl_AppendResult(interp, pDb->zProfile, 0);
+ Tcl_AppendResult(interp, pDb->zProfile, (char*)0);
}
}else{
char *zProfile;
@@ -2534,7 +2551,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
pKey = Tcl_GetByteArrayFromObj(objv[2], &nKey);
rc = sqlite3_rekey(pDb->db, pKey, nKey);
if( rc ){
- Tcl_AppendResult(interp, sqlite3_errstr(rc), 0);
+ Tcl_AppendResult(interp, sqlite3_errstr(rc), (char*)0);
rc = TCL_ERROR;
}
#endif
@@ -2675,7 +2692,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
return TCL_ERROR;
}else if( objc==2 ){
if( pDb->zTrace ){
- Tcl_AppendResult(interp, pDb->zTrace, 0);
+ Tcl_AppendResult(interp, pDb->zTrace, (char*)0);
}
}else{
char *zTrace;
@@ -2746,7 +2763,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
rc = sqlite3_exec(pDb->db, zBegin, 0, 0, 0);
pDb->disableAuth--;
if( rc!=SQLITE_OK ){
- Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
return TCL_ERROR;
}
pDb->nTransaction++;
@@ -2758,7 +2775,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** or savepoint. */
if( DbUseNre() ){
Tcl_NRAddCallback(interp, DbTransPostCmd, cd, 0, 0, 0);
- Tcl_NREvalObj(interp, pScript, 0);
+ (void)Tcl_NREvalObj(interp, pScript, 0);
}else{
rc = DbTransPostCmd(&cd, interp, Tcl_EvalObjEx(interp, pScript, 0));
}
@@ -2770,7 +2787,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
*/
case DB_UNLOCK_NOTIFY: {
#ifndef SQLITE_ENABLE_UNLOCK_NOTIFY
- Tcl_AppendResult(interp, "unlock_notify not available in this build", 0);
+ Tcl_AppendResult(interp, "unlock_notify not available in this build",
+ (char*)0);
rc = TCL_ERROR;
#else
if( objc!=2 && objc!=3 ){
@@ -2793,7 +2811,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
if( sqlite3_unlock_notify(pDb->db, xNotify, pNotifyArg) ){
- Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
+ Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
rc = TCL_ERROR;
}
}
@@ -2922,14 +2940,14 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
if( objc==2 ){
zArg = Tcl_GetStringFromObj(objv[1], 0);
if( strcmp(zArg,"-version")==0 ){
- Tcl_AppendResult(interp,sqlite3_version,0);
+ Tcl_AppendResult(interp,sqlite3_libversion(), (char*)0);
return TCL_OK;
}
if( strcmp(zArg,"-has-codec")==0 ){
#ifdef SQLITE_HAS_CODEC
- Tcl_AppendResult(interp,"1",0);
+ Tcl_AppendResult(interp,"1",(char*)0);
#else
- Tcl_AppendResult(interp,"0",0);
+ Tcl_AppendResult(interp,"0",(char*)0);
#endif
return TCL_OK;
}
@@ -3004,7 +3022,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
zErrMsg = 0;
p = (SqliteDb*)Tcl_Alloc( sizeof(*p) );
if( p==0 ){
- Tcl_SetResult(interp, "malloc failed", TCL_STATIC);
+ Tcl_SetResult(interp, (char *)"malloc failed", TCL_STATIC);
return TCL_ERROR;
}
memset(p, 0, sizeof(*p));
@@ -3050,7 +3068,7 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
*/
#ifndef USE_TCL_STUBS
# undef Tcl_InitStubs
-# define Tcl_InitStubs(a,b,c)
+# define Tcl_InitStubs(a,b,c) TCL_VERSION
#endif
/*
@@ -3074,19 +3092,18 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
** The EXTERN macros are required by TCL in order to work on windows.
*/
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);
-
+ int rc = Tcl_InitStubs(interp, "8.4", 0)==0 ? TCL_ERROR : TCL_OK;
+ if( rc==TCL_OK ){
+ Tcl_CreateObjCommand(interp, "sqlite3", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#ifndef SQLITE_3_SUFFIX_ONLY
- /* The "sqlite" alias is undocumented. It is here only to support
- ** legacy scripts. All new scripts should use only the "sqlite3"
- ** command.
- */
- Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
+ /* The "sqlite" alias is undocumented. It is here only to support
+ ** legacy scripts. All new scripts should use only the "sqlite3"
+ ** command. */
+ Tcl_CreateObjCommand(interp, "sqlite", (Tcl_ObjCmdProc*)DbMain, 0, 0);
#endif
-
- return TCL_OK;
+ rc = Tcl_PkgProvide(interp, "sqlite3", PACKAGE_VERSION);
+ }
+ return rc;
}
EXTERN int Tclsqlite3_Init(Tcl_Interp *interp){ return Sqlite3_Init(interp); }
EXTERN int Sqlite3_Unload(Tcl_Interp *interp, int flags){ return TCL_OK; }
@@ -3360,13 +3377,11 @@ static void MD5Final(unsigned char digest[16], MD5Context *ctx){
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
- ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
- ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+ memcpy(ctx->in + 14*4, ctx->bits, 8);
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 */
}
/*
@@ -3414,7 +3429,7 @@ static int md5_cmd(void*cd, Tcl_Interp *interp, int argc, const char **argv){
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
- " TEXT\"", 0);
+ " TEXT\"", (char*)0);
return TCL_ERROR;
}
MD5Init(&ctx);
@@ -3439,13 +3454,13 @@ static int md5file_cmd(void*cd, Tcl_Interp*interp, int argc, const char **argv){
if( argc!=2 ){
Tcl_AppendResult(interp,"wrong # args: should be \"", argv[0],
- " FILENAME\"", 0);
+ " FILENAME\"", (char*)0);
return TCL_ERROR;
}
in = fopen(argv[1],"rb");
if( in==0 ){
Tcl_AppendResult(interp,"unable to open file \"", argv[1],
- "\" for reading", 0);
+ "\" for reading", (char*)0);
return TCL_ERROR;
}
MD5Init(&ctx);
@@ -3747,13 +3762,23 @@ static void init_all(Tcl_Interp *interp){
#define TCLSH_MAIN main /* Needed to fake out mktclapp */
int TCLSH_MAIN(int argc, char **argv){
Tcl_Interp *interp;
-
+
+#if !defined(_WIN32_WCE)
+ if( getenv("BREAK") ){
+ fprintf(stderr,
+ "attach debugger to process %d and press any key to continue.\n",
+ GETPID());
+ fgetc(stdin);
+ }
+#endif
+
/* Call sqlite3_shutdown() once before doing anything else. This is to
** test that sqlite3_shutdown() can be safely called by a process before
** sqlite3_initialize() is. */
sqlite3_shutdown();
Tcl_FindExecutable(argv[0]);
+ Tcl_SetSystemEncoding(NULL, "utf-8");
interp = Tcl_CreateInterp();
#if TCLSH==2
diff --git a/src/test1.c b/src/test1.c
index a638e48..56487f6 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -14,6 +14,10 @@
** testing of the SQLite library.
*/
#include "sqliteInt.h"
+#if SQLITE_OS_WIN
+# include "os_win.h"
+#endif
+
#include "vdbeInt.h"
#include "tcl.h"
#include <stdlib.h>
@@ -113,6 +117,16 @@ int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
return TCL_OK;
}
+#if SQLITE_OS_WIN
+/*
+** Decode a Win32 HANDLE object.
+*/
+int getWin32Handle(Tcl_Interp *interp, const char *zA, LPHANDLE phFile){
+ *phFile = (HANDLE)sqlite3TestTextToPtr(zA);
+ return TCL_OK;
+}
+#endif
+
extern const char *sqlite3ErrName(int);
#define t1ErrorName sqlite3ErrName
@@ -242,7 +256,28 @@ static int test_io_trace(
return TCL_OK;
}
-
+/*
+** Usage: clang_sanitize_address
+**
+** Returns true if the program was compiled using clang with the
+** -fsanitize=address switch on the command line. False otherwise.
+*/
+static int clang_sanitize_address(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ char **argv /* Text of each argument */
+){
+ int res = 0;
+#if defined(__has_feature)
+# if __has_feature(address_sanitizer)
+ res = 1;
+# endif
+#endif
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
+ return TCL_OK;
+}
+
/*
** Usage: sqlite3_exec_printf DB FORMAT STRING
**
@@ -942,9 +977,21 @@ static void ptrChngFunction(
sqlite3_result_int(context, p1!=p2);
}
+/*
+** This SQL function returns a different answer each time it is called, even if
+** the arguments are the same.
+*/
+static void nondeterministicFunction(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ static int cnt = 0;
+ sqlite3_result_int(context, cnt++);
+}
/*
-** Usage: sqlite_test_create_function DB
+** Usage: sqlite3_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
@@ -973,16 +1020,16 @@ static int test_create_function(
return TCL_ERROR;
}
if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
- rc = sqlite3_create_function(db, "x_coalesce", -1, SQLITE_ANY, 0,
+ rc = sqlite3_create_function(db, "x_coalesce", -1, SQLITE_UTF8, 0,
t1_ifnullFunc, 0, 0);
if( rc==SQLITE_OK ){
- rc = sqlite3_create_function(db, "hex8", 1, SQLITE_ANY, 0,
- hex8Func, 0, 0);
+ rc = sqlite3_create_function(db, "hex8", 1, SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+ 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);
+ rc = sqlite3_create_function(db, "hex16", 1, SQLITE_UTF16 | SQLITE_DETERMINISTIC,
+ 0, hex16Func, 0, 0);
}
#endif
if( rc==SQLITE_OK ){
@@ -994,6 +1041,19 @@ static int test_create_function(
ptrChngFunction, 0, 0);
}
+ /* Functions counter1() and counter2() have the same implementation - they
+ ** both return an ascending integer with each call. But counter1() is marked
+ ** as non-deterministic and counter2() is marked as deterministic.
+ */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "counter1", -1, SQLITE_UTF8,
+ 0, nondeterministicFunction, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "counter2", -1, SQLITE_UTF8|SQLITE_DETERMINISTIC,
+ 0, nondeterministicFunction, 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. */
@@ -2218,6 +2278,7 @@ static int test_stmt_status(
{ "SQLITE_STMTSTATUS_FULLSCAN_STEP", SQLITE_STMTSTATUS_FULLSCAN_STEP },
{ "SQLITE_STMTSTATUS_SORT", SQLITE_STMTSTATUS_SORT },
{ "SQLITE_STMTSTATUS_AUTOINDEX", SQLITE_STMTSTATUS_AUTOINDEX },
+ { "SQLITE_STMTSTATUS_VM_STEP", SQLITE_STMTSTATUS_VM_STEP },
};
if( objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG");
@@ -2469,7 +2530,7 @@ 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
+** Sets the value of the IDX-th occurrence 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
@@ -5159,6 +5220,7 @@ static int file_control_lockproxy_test(
return TCL_OK;
}
+#if SQLITE_OS_WIN
/*
** tclcmd: file_control_win32_av_retry DB NRETRY DELAY
**
@@ -5193,6 +5255,42 @@ static int file_control_win32_av_retry(
}
/*
+** tclcmd: file_control_win32_set_handle DB HANDLE
+**
+** This TCL command runs the sqlite3_file_control interface with
+** the SQLITE_FCNTL_WIN32_SET_HANDLE opcode.
+*/
+static int file_control_win32_set_handle(
+ 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;
+ HANDLE hFile = NULL;
+ char z[100];
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB HANDLE", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
+ return TCL_ERROR;
+ }
+ if( getWin32Handle(interp, Tcl_GetString(objv[2]), &hFile) ){
+ return TCL_ERROR;
+ }
+ rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_WIN32_SET_HANDLE,
+ (void*)&hFile);
+ sqlite3_snprintf(sizeof(z), z, "%d %p", rc, (void*)hFile);
+ Tcl_AppendResult(interp, z, (char*)0);
+ return TCL_OK;
+}
+#endif
+
+/*
** tclcmd: file_control_persist_wal DB PERSIST-FLAG
**
** This TCL command runs the sqlite3_file_control interface with
@@ -5453,6 +5551,37 @@ static int reset_prng_state(
}
/*
+** tclcmd: database_may_be_corrupt
+**
+** Indicate that database files might be corrupt. In other words, set the normal
+** state of operation.
+*/
+static int database_may_be_corrupt(
+ 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_NEVER_CORRUPT, 0);
+ return TCL_OK;
+}
+/*
+** tclcmd: database_never_corrupt
+**
+** Indicate that database files are always well-formed. This enables extra assert()
+** statements that test conditions that are always true for well-formed databases.
+*/
+static int database_never_corrupt(
+ 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_NEVER_CORRUPT, 1);
+ return TCL_OK;
+}
+
+/*
** tclcmd: pcache_stats
*/
static int test_pcache_stats(
@@ -5563,7 +5692,7 @@ static int test_wal_checkpoint(
**
** Otherwise, this command returns a list of three integers. The first integer
** is 1 if SQLITE_BUSY was returned, or 0 otherwise. The following two integers
-** are the values returned via the output paramaters by wal_checkpoint_v2() -
+** are the values returned via the output parameters by wal_checkpoint_v2() -
** the number of frames in the log and the number of frames in the log
** that have been checkpointed.
*/
@@ -5933,6 +6062,145 @@ static int win32_file_lock(
CloseHandle(ev);
return TCL_OK;
}
+
+/*
+** exists_win32_path PATH
+**
+** Returns non-zero if the specified path exists, whose fully qualified name
+** may exceed 260 characters if it is prefixed with "\\?\".
+*/
+static int win32_exists_path(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PATH");
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
+ GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES ));
+ return TCL_OK;
+}
+
+/*
+** find_win32_file PATTERN
+**
+** Returns a list of entries in a directory that match the specified pattern,
+** whose fully qualified name may exceed 248 characters if it is prefixed with
+** "\\?\".
+*/
+static int win32_find_file(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ HANDLE hFindFile = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATAW findData;
+ Tcl_Obj *listObj;
+ DWORD lastErrno;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PATTERN");
+ return TCL_ERROR;
+ }
+ hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData);
+ if( hFindFile==INVALID_HANDLE_VALUE ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ listObj = Tcl_NewObj();
+ Tcl_IncrRefCount(listObj);
+ do {
+ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj(
+ findData.cFileName, -1));
+ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(
+ findData.dwFileAttributes));
+ } while( FindNextFileW(hFindFile, &findData) );
+ lastErrno = GetLastError();
+ if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){
+ FindClose(hFindFile);
+ Tcl_DecrRefCount(listObj);
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ FindClose(hFindFile);
+ Tcl_SetObjResult(interp, listObj);
+ return TCL_OK;
+}
+
+/*
+** delete_win32_file FILENAME
+**
+** Deletes the specified file, whose fully qualified name may exceed 260
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_delete_file(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
+ return TCL_ERROR;
+ }
+ if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+/*
+** make_win32_dir DIRECTORY
+**
+** Creates the specified directory, whose fully qualified name may exceed 248
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_mkdir(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
+ return TCL_ERROR;
+ }
+ if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+/*
+** remove_win32_dir DIRECTORY
+**
+** Removes the specified directory, whose fully qualified name may exceed 248
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_rmdir(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
+ return TCL_ERROR;
+ }
+ if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
#endif
@@ -5958,15 +6226,19 @@ static int optimization_control(
const char *zOptName;
int mask;
} aOpt[] = {
- { "all", SQLITE_AllOpts },
- { "query-flattener", SQLITE_QueryFlattener },
- { "column-cache", SQLITE_ColumnCache },
- { "groupby-order", SQLITE_GroupByOrder },
- { "factor-constants", SQLITE_FactorOutConst },
- { "real-as-int", SQLITE_IdxRealAsInt },
- { "distinct-opt", SQLITE_DistinctOpt },
- { "cover-idx-scan", SQLITE_CoverIdxScan },
- { "order-by-idx-join",SQLITE_OrderByIdxJoin },
+ { "all", SQLITE_AllOpts },
+ { "none", 0 },
+ { "query-flattener", SQLITE_QueryFlattener },
+ { "column-cache", SQLITE_ColumnCache },
+ { "groupby-order", SQLITE_GroupByOrder },
+ { "factor-constants", SQLITE_FactorOutConst },
+ { "distinct-opt", SQLITE_DistinctOpt },
+ { "cover-idx-scan", SQLITE_CoverIdxScan },
+ { "order-by-idx-join", SQLITE_OrderByIdxJoin },
+ { "transitive", SQLITE_Transitive },
+ { "subquery-coroutine", SQLITE_SubqCoroutine },
+ { "omit-noop-join", SQLITE_OmitNoopJoin },
+ { "stat3", SQLITE_Stat3 },
};
if( objc!=4 ){
@@ -5987,7 +6259,7 @@ static int optimization_control(
Tcl_AppendResult(interp, "unknown optimization - should be one of:",
(char*)0);
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
- Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
+ Tcl_AppendResult(interp, " ", aOpt[i].zOptName, (char*)0);
}
return TCL_ERROR;
}
@@ -6009,11 +6281,14 @@ static int tclLoadStaticExtensionCmd(
){
extern int sqlite3_amatch_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_closure_init(sqlite3*,char**,const sqlite3_api_routines*);
+ extern int sqlite3_fileio_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_fuzzer_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_ieee_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_nextchar_init(sqlite3*,char**,const sqlite3_api_routines*);
+ extern int sqlite3_percentile_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*);
+ extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*);
static const struct {
const char *zExtName;
@@ -6021,11 +6296,14 @@ static int tclLoadStaticExtensionCmd(
} aExtension[] = {
{ "amatch", sqlite3_amatch_init },
{ "closure", sqlite3_closure_init },
+ { "fileio", sqlite3_fileio_init },
{ "fuzzer", sqlite3_fuzzer_init },
{ "ieee754", sqlite3_ieee_init },
{ "nextchar", sqlite3_nextchar_init },
+ { "percentile", sqlite3_percentile_init },
{ "regexp", sqlite3_regexp_init },
{ "spellfix", sqlite3_spellfix_init },
+ { "totype", sqlite3_totype_init },
{ "wholenumber", sqlite3_wholenumber_init },
};
sqlite3 *db;
@@ -6119,6 +6397,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_busy_timeout", (Tcl_CmdProc*)test_busy_timeout },
{ "printf", (Tcl_CmdProc*)test_printf },
{ "sqlite3IoTrace", (Tcl_CmdProc*)test_io_trace },
+ { "clang_sanitize_address", (Tcl_CmdProc*)clang_sanitize_address },
};
static struct {
char *zName;
@@ -6182,9 +6461,16 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "save_prng_state", save_prng_state, 0 },
{ "restore_prng_state", restore_prng_state, 0 },
{ "reset_prng_state", reset_prng_state, 0 },
+ { "database_never_corrupt", database_never_corrupt, 0},
+ { "database_may_be_corrupt", database_may_be_corrupt, 0},
{ "optimization_control", optimization_control,0},
#if SQLITE_OS_WIN
{ "lock_win32_file", win32_file_lock, 0 },
+ { "exists_win32_path", win32_exists_path, 0 },
+ { "find_win32_file", win32_find_file, 0 },
+ { "delete_win32_file", win32_delete_file, 0 },
+ { "make_win32_dir", win32_mkdir, 0 },
+ { "remove_win32_dir", win32_rmdir, 0 },
#endif
{ "tcl_objproc", runAsObjProc, 0 },
@@ -6235,7 +6521,10 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
+#if SQLITE_OS_WIN
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
+ { "file_control_win32_set_handle", file_control_win32_set_handle, 0 },
+#endif
{ "file_control_persist_wal", file_control_persist_wal, 0 },
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
{ "file_control_vfsname", file_control_vfsname, 0 },
@@ -6292,7 +6581,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern int sqlite3_pager_writedb_count;
extern int sqlite3_pager_writej_count;
#if SQLITE_OS_WIN
- extern int sqlite3_os_type;
+ extern LONG volatile sqlite3_os_type;
#endif
#ifdef SQLITE_DEBUG
extern int sqlite3WhereTrace;
@@ -6300,8 +6589,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern int sqlite3WalTrace;
#endif
#ifdef SQLITE_TEST
- extern char sqlite3_query_plan[];
- static char *query_plan = sqlite3_query_plan;
#ifdef SQLITE_ENABLE_FTS3
extern int sqlite3_fts3_enable_parentheses;
#endif
@@ -6352,11 +6639,14 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#endif
#if SQLITE_OS_WIN
Tcl_LinkVar(interp, "sqlite_os_type",
- (char*)&sqlite3_os_type, TCL_LINK_INT);
+ (char*)&sqlite3_os_type, TCL_LINK_LONG);
#endif
#ifdef SQLITE_TEST
- Tcl_LinkVar(interp, "sqlite_query_plan",
- (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
+ {
+ static const char *query_plan = "*** OBSOLETE VARIABLE ***";
+ Tcl_LinkVar(interp, "sqlite_query_plan",
+ (char*)&query_plan, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
+ }
#endif
#ifdef SQLITE_DEBUG
Tcl_LinkVar(interp, "sqlite_where_trace",
diff --git a/src/test2.c b/src/test2.c
index d130e9d..58f271f 100644
--- a/src/test2.c
+++ b/src/test2.c
@@ -568,7 +568,90 @@ static int testPendingByte(
rc = sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, pbyte);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
return TCL_OK;
-}
+}
+
+/*
+** The sqlite3FaultSim() callback:
+*/
+static Tcl_Interp *faultSimInterp = 0;
+static int faultSimScriptSize = 0;
+static char *faultSimScript;
+static int faultSimCallback(int x){
+ char zInt[30];
+ int i;
+ int isNeg;
+ int rc;
+ if( x==0 ){
+ memcpy(faultSimScript+faultSimScriptSize, "0", 2);
+ }else{
+ /* Convert x to text without using any sqlite3 routines */
+ if( x<0 ){
+ isNeg = 1;
+ x = -x;
+ }else{
+ isNeg = 0;
+ }
+ zInt[sizeof(zInt)-1] = 0;
+ for(i=sizeof(zInt)-2; i>0 && x>0; i--, x /= 10){
+ zInt[i] = (x%10) + '0';
+ }
+ if( isNeg ) zInt[i--] = '-';
+ memcpy(faultSimScript+faultSimScriptSize, zInt+i+1, sizeof(zInt)-i);
+ }
+ rc = Tcl_Eval(faultSimInterp, faultSimScript);
+ if( rc ){
+ fprintf(stderr, "fault simulator script failed: [%s]", faultSimScript);
+ rc = SQLITE_ERROR;
+ }else{
+ rc = atoi(Tcl_GetStringResult(faultSimInterp));
+ }
+ Tcl_ResetResult(faultSimInterp);
+ return rc;
+}
+
+/*
+** sqlite3_test_control_fault_install SCRIPT
+**
+** Arrange to invoke SCRIPT with the integer argument to sqlite3FaultSim()
+** appended, whenever sqlite3FaultSim() is called. Or, if SCRIPT is the
+** empty string, cancel the sqlite3FaultSim() callback.
+*/
+static int faultInstallCmd(
+ void *NotUsed,
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int argc, /* Number of arguments */
+ const char **argv /* Text of each argument */
+){
+ const char *zScript;
+ int nScript;
+ int rc;
+ if( argc!=1 && argc!=2 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " SCRIPT\"", (void*)0);
+ }
+ zScript = argc==2 ? argv[1] : "";
+ nScript = (int)strlen(zScript);
+ if( faultSimScript ){
+ free(faultSimScript);
+ faultSimScript = 0;
+ }
+ if( nScript==0 ){
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, 0);
+ }else{
+ faultSimScript = malloc( nScript+100 );
+ if( faultSimScript==0 ){
+ Tcl_AppendResult(interp, "out of memory", (void*)0);
+ return SQLITE_ERROR;
+ }
+ memcpy(faultSimScript, zScript, nScript);
+ faultSimScript[nScript] = ' ';
+ faultSimScriptSize = nScript+1;
+ faultSimInterp = interp;
+ rc = sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL, faultSimCallback);
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
/*
** sqlite3BitvecBuiltinTest SIZE PROGRAM
@@ -638,7 +721,8 @@ int Sqlitetest2_Init(Tcl_Interp *interp){
{ "fake_big_file", (Tcl_CmdProc*)fake_big_file },
#endif
{ "sqlite3BitvecBuiltinTest",(Tcl_CmdProc*)testBitvecBuiltinTest },
- { "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
+ { "sqlite3_test_control_pending_byte", (Tcl_CmdProc*)testPendingByte },
+ { "sqlite3_test_control_fault_install", (Tcl_CmdProc*)faultInstallCmd },
};
int i;
for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
diff --git a/src/test5.c b/src/test5.c
index 303d120..952e332 100644
--- a/src/test5.c
+++ b/src/test5.c
@@ -76,7 +76,6 @@ static int test_value_overhead(
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++){
diff --git a/src/test6.c b/src/test6.c
index c151ea4..306482d 100644
--- a/src/test6.c
+++ b/src/test6.c
@@ -133,9 +133,9 @@ struct CrashFile {
** 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 */
+ int nData; /* Size of buffer allocated at zData */
+ i64 iSize; /* Size of file in bytes */
};
struct CrashGlobal {
@@ -173,9 +173,6 @@ static void *crash_realloc(void *p, int n){
static int writeDbFile(CrashFile *p, u8 *z, i64 iAmt, i64 iOff){
int rc = SQLITE_OK;
int iSkip = 0;
- if( iOff==PENDING_BYTE && (p->flags&SQLITE_OPEN_MAIN_DB) ){
- iSkip = 512;
- }
if( (iAmt-iSkip)>0 ){
rc = sqlite3OsWrite(p->pRealFile, &z[iSkip], (int)(iAmt-iSkip), iOff+iSkip);
}
@@ -409,13 +406,17 @@ static int cfRead(
sqlite_int64 iOfst
){
CrashFile *pCrash = (CrashFile *)pFile;
+ int nCopy = (int)MIN((i64)iAmt, (pCrash->iSize - iOfst));
+
+ if( nCopy>0 ){
+ memcpy(zBuf, &pCrash->zData[iOfst], nCopy);
+ }
/* Check the file-size to see if this is a short-read */
- if( pCrash->iSize<(iOfst+iAmt) ){
+ if( nCopy<iAmt ){
return SQLITE_IOERR_SHORT_READ;
}
- memcpy(zBuf, &pCrash->zData[iOfst], iAmt);
return SQLITE_OK;
}
@@ -621,7 +622,7 @@ static int cfOpen(
pWrapper->flags = flags;
}
if( rc==SQLITE_OK ){
- pWrapper->nData = (4096 + pWrapper->iSize);
+ pWrapper->nData = (int)(4096 + pWrapper->iSize);
pWrapper->zData = crash_malloc(pWrapper->nData);
if( pWrapper->zData ){
/* os_unix.c contains an assert() that fails if the caller attempts
@@ -632,14 +633,12 @@ static int cfOpen(
** UPDATE: It also contains an assert() verifying that each call
** to the xRead() method reads less than 128KB of data.
*/
- const int isDb = (flags&SQLITE_OPEN_MAIN_DB);
i64 iOff;
memset(pWrapper->zData, 0, pWrapper->nData);
for(iOff=0; iOff<pWrapper->iSize; iOff += 512){
- int nRead = pWrapper->iSize - (int)iOff;
+ int nRead = (int)(pWrapper->iSize - iOff);
if( nRead>512 ) nRead = 512;
- if( isDb && iOff==PENDING_BYTE ) continue;
rc = sqlite3OsRead(pReal, &pWrapper->zData[iOff], nRead, iOff);
}
}else{
diff --git a/src/test7.c b/src/test7.c
index 3cd4a22..93bf1e4 100644
--- a/src/test7.c
+++ b/src/test7.c
@@ -40,6 +40,7 @@ int sqlite3_client_finalize(sqlite3_stmt*);
int sqlite3_client_close(sqlite3*);
int sqlite3_server_start(void);
int sqlite3_server_stop(void);
+void sqlite3_server_start2(int *pnDecr);
/*
** Each thread is controlled by an instance of the following
@@ -68,6 +69,13 @@ struct Thread {
int argc; /* number of columns in result */
const char *argv[100]; /* result columns */
const char *colv[100]; /* result column names */
+
+ /* Initialized to 1 by the supervisor thread when the client is
+ ** created, and then deemed read-only to the supervisor thread.
+ ** Is set to 0 by the server thread belonging to this client
+ ** just before it exits.
+ */
+ int nServer; /* Number of server threads running */
};
/*
@@ -175,7 +183,10 @@ static int tcl_client_create(
return TCL_ERROR;
}
pthread_detach(x);
- sqlite3_server_start();
+ if( threadset[i].nServer==0 ){
+ threadset[i].nServer = 1;
+ sqlite3_server_start2(&threadset[i].nServer);
+ }
return TCL_OK;
}
@@ -268,6 +279,11 @@ static int tcl_client_halt(
for(i=0; i<N_THREAD && threadset[i].busy==0; i++){}
if( i>=N_THREAD ){
sqlite3_server_stop();
+ while( 1 ){
+ for(i=0; i<N_THREAD && threadset[i].nServer==0; i++);
+ if( i==N_THREAD ) break;
+ sched_yield();
+ }
}
return TCL_OK;
}
diff --git a/src/test8.c b/src/test8.c
index c573933..8bc835d 100644
--- a/src/test8.c
+++ b/src/test8.c
@@ -265,6 +265,7 @@ static int getIndexArray(
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zIdx = (const char *)sqlite3_column_text(pStmt, 1);
sqlite3_stmt *pStmt2 = 0;
+ if( zIdx==0 ) continue;
zSql = sqlite3_mprintf("PRAGMA index_info(%s)", zIdx);
if( !zSql ){
rc = SQLITE_NOMEM;
@@ -891,7 +892,7 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->estimatedCost = cost;
}else if( useIdx ){
/* Approximation of log2(nRow). */
- for( ii=0; ii<(sizeof(int)*8); ii++ ){
+ for( ii=0; ii<(sizeof(int)*8)-1; ii++ ){
if( nRow & (1<<ii) ){
pIdxInfo->estimatedCost = (double)ii;
}
diff --git a/src/test_autoext.c b/src/test_autoext.c
index b5013f3..a5236d2 100644
--- a/src/test_autoext.c
+++ b/src/test_autoext.c
@@ -99,6 +99,22 @@ static int autoExtSqrObjCmd(
}
/*
+** tclcmd: sqlite3_cancel_auto_extension_sqr
+**
+** Unregister the "sqr" extension.
+*/
+static int cancelAutoExtSqrObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_cancel_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.
@@ -115,6 +131,22 @@ static int autoExtCubeObjCmd(
}
/*
+** tclcmd: sqlite3_cancel_auto_extension_cube
+**
+** Unregister the "cube" extension.
+*/
+static int cancelAutoExtCubeObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_cancel_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.
@@ -130,6 +162,22 @@ static int autoExtBrokenObjCmd(
return SQLITE_OK;
}
+/*
+** tclcmd: sqlite3_cancel_auto_extension_broken
+**
+** Unregister the broken extension.
+*/
+static int cancelAutoExtBrokenObjCmd(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc = sqlite3_cancel_auto_extension((void*)broken_init);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
+ return SQLITE_OK;
+}
+
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
@@ -160,6 +208,12 @@ int Sqlitetest_autoext_Init(Tcl_Interp *interp){
autoExtCubeObjCmd, 0, 0);
Tcl_CreateObjCommand(interp, "sqlite3_auto_extension_broken",
autoExtBrokenObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_sqr",
+ cancelAutoExtSqrObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_cube",
+ cancelAutoExtCubeObjCmd, 0, 0);
+ Tcl_CreateObjCommand(interp, "sqlite3_cancel_auto_extension_broken",
+ cancelAutoExtBrokenObjCmd, 0, 0);
#endif
Tcl_CreateObjCommand(interp, "sqlite3_reset_auto_extension",
resetAutoExtObjCmd, 0, 0);
diff --git a/src/test_btree.c b/src/test_btree.c
index db72889..dfe7705 100644
--- a/src/test_btree.c
+++ b/src/test_btree.c
@@ -51,7 +51,7 @@ void sqlite3BtreeCursorList(Btree *p){
BtShared *pBt = p->pBt;
for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
MemPage *pPage = pCur->apPage[pCur->iPage];
- char *zMode = pCur->wrFlag ? "rw" : "ro";
+ char *zMode = (pCur->curFlags & BTCF_WriteFlag) ? "rw" : "ro";
sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
pCur, pCur->pgnoRoot, zMode,
pPage ? pPage->pgno : 0, pCur->aiIdx[pCur->iPage],
diff --git a/src/test_config.c b/src/test_config.c
index 534727a..9ce7f6f 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -20,6 +20,10 @@
#include "sqliteLimit.h"
#include "sqliteInt.h"
+#if SQLITE_OS_WIN
+# include "os_win.h"
+#endif
+
#include "tcl.h"
#include <stdlib.h>
#include <string.h>
@@ -63,6 +67,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "curdir", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_WIN32_MALLOC
+ Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "win32malloc", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_DEBUG
Tcl_SetVar2(interp, "sqlite_options", "debug", "1", TCL_GLOBAL_ONLY);
#else
@@ -219,6 +229,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "check", "1", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_OMIT_CTE
+ Tcl_SetVar2(interp, "sqlite_options", "cte", "0", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "cte", "1", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_ENABLE_COLUMN_METADATA
Tcl_SetVar2(interp, "sqlite_options", "columnmetadata", "1", TCL_GLOBAL_ONLY);
#else
@@ -252,12 +268,7 @@ static void set_options(Tcl_Interp *interp){
#endif
Tcl_SetVar2(interp, "sqlite_options", "conflict", "1", TCL_GLOBAL_ONLY);
-
-#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);
@@ -319,7 +330,7 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
#endif
-#if defined(SQLITE_ENABLE_FTS3) && defined(SQLITE_ENABLE_FTS4_UNICODE61)
+#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_DISABLE_FTS3_UNICODE)
Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "fts3_unicode", "0", TCL_GLOBAL_ONLY);
@@ -458,7 +469,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "schema_version", "1", TCL_GLOBAL_ONLY);
#endif
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT4
+ Tcl_SetVar2(interp, "sqlite_options", "stat4", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "stat4", "0", TCL_GLOBAL_ONLY);
+#endif
+#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
diff --git a/src/test_demovfs.c b/src/test_demovfs.c
index 6376270..c63b0a8 100644
--- a/src/test_demovfs.c
+++ b/src/test_demovfs.c
@@ -536,7 +536,7 @@ static int demoFullPathname(
if( zPath[0]=='/' ){
zDir[0] = '\0';
}else{
- getcwd(zDir, sizeof(zDir));
+ if( getcwd(zDir, sizeof(zDir))==0 ) return SQLITE_IOERR;
}
zDir[MAXPATHNAME] = '\0';
diff --git a/src/test_fs.c b/src/test_fs.c
index 478cad8..417c81b 100644
--- a/src/test_fs.c
+++ b/src/test_fs.c
@@ -195,6 +195,7 @@ static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
const char *zFile = (const char *)sqlite3_column_text(pCur->pStmt, 1);
struct stat sbuf;
int fd;
+ int n;
fd = open(zFile, O_RDONLY);
if( fd<0 ) return SQLITE_IOERR;
@@ -214,8 +215,9 @@ static int fsColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
pCur->nAlloc = nNew;
}
- read(fd, pCur->zBuf, sbuf.st_size);
+ n = (int)read(fd, pCur->zBuf, sbuf.st_size);
close(fd);
+ if( n!=sbuf.st_size ) return SQLITE_ERROR;
pCur->nBuf = sbuf.st_size;
pCur->zBuf[pCur->nBuf] = '\0';
diff --git a/src/test_func.c b/src/test_func.c
index 6f9bb03..9cf2f80 100644
--- a/src/test_func.c
+++ b/src/test_func.c
@@ -18,6 +18,9 @@
#include <string.h>
#include <assert.h>
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
/*
** Allocate nByte bytes of space using sqlite3_malloc(). If the
@@ -458,6 +461,145 @@ static void real2hex(
sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
}
+/*
+** tclcmd: test_extract(record, field)
+**
+** This function implements an SQL user-function that accepts a blob
+** containing a formatted database record as the first argument. The
+** second argument is the index of the field within that record to
+** extract and return.
+*/
+static void test_extract(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ u8 *pRec;
+ u8 *pEndHdr; /* Points to one byte past record header */
+ u8 *pHdr; /* Current point in record header */
+ u8 *pBody; /* Current point in record data */
+ u64 nHdr; /* Bytes in record header */
+ int iIdx; /* Required field */
+ int iCurrent = 0; /* Current field */
+
+ assert( argc==2 );
+ pRec = (u8*)sqlite3_value_blob(argv[0]);
+ iIdx = sqlite3_value_int(argv[1]);
+
+ pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
+ pBody = pEndHdr = &pRec[nHdr];
+
+ for(iCurrent=0; pHdr<pEndHdr && iCurrent<=iIdx; iCurrent++){
+ u64 iSerialType;
+ Mem mem;
+
+ memset(&mem, 0, sizeof(mem));
+ mem.db = db;
+ mem.enc = ENC(db);
+ pHdr += sqlite3GetVarint(pHdr, &iSerialType);
+ pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
+
+ if( iCurrent==iIdx ){
+ sqlite3_result_value(context, &mem);
+ }
+
+ sqlite3DbFree(db, mem.zMalloc);
+ }
+}
+
+/*
+** tclcmd: test_decode(record)
+**
+** This function implements an SQL user-function that accepts a blob
+** containing a formatted database record as its only argument. It returns
+** a tcl list (type SQLITE_TEXT) containing each of the values stored
+** in the record.
+*/
+static void test_decode(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ u8 *pRec;
+ u8 *pEndHdr; /* Points to one byte past record header */
+ u8 *pHdr; /* Current point in record header */
+ u8 *pBody; /* Current point in record data */
+ u64 nHdr; /* Bytes in record header */
+ Tcl_Obj *pRet; /* Return value */
+
+ pRet = Tcl_NewObj();
+ Tcl_IncrRefCount(pRet);
+
+ assert( argc==1 );
+ pRec = (u8*)sqlite3_value_blob(argv[0]);
+
+ pHdr = pRec + sqlite3GetVarint(pRec, &nHdr);
+ pBody = pEndHdr = &pRec[nHdr];
+ while( pHdr<pEndHdr ){
+ Tcl_Obj *pVal = 0;
+ u64 iSerialType;
+ Mem mem;
+
+ memset(&mem, 0, sizeof(mem));
+ mem.db = db;
+ mem.enc = ENC(db);
+ pHdr += sqlite3GetVarint(pHdr, &iSerialType);
+ pBody += sqlite3VdbeSerialGet(pBody, (u32)iSerialType, &mem);
+
+ switch( sqlite3_value_type(&mem) ){
+ case SQLITE_TEXT:
+ pVal = Tcl_NewStringObj((const char*)sqlite3_value_text(&mem), -1);
+ break;
+
+ case SQLITE_BLOB: {
+ char hexdigit[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+ int n = sqlite3_value_bytes(&mem);
+ u8 *z = (u8*)sqlite3_value_blob(&mem);
+ int i;
+ pVal = Tcl_NewStringObj("x'", -1);
+ for(i=0; i<n; i++){
+ char hex[3];
+ hex[0] = hexdigit[((z[i] >> 4) & 0x0F)];
+ hex[1] = hexdigit[(z[i] & 0x0F)];
+ hex[2] = '\0';
+ Tcl_AppendStringsToObj(pVal, hex, 0);
+ }
+ Tcl_AppendStringsToObj(pVal, "'", 0);
+ break;
+ }
+
+ case SQLITE_FLOAT:
+ pVal = Tcl_NewDoubleObj(sqlite3_value_double(&mem));
+ break;
+
+ case SQLITE_INTEGER:
+ pVal = Tcl_NewWideIntObj(sqlite3_value_int64(&mem));
+ break;
+
+ case SQLITE_NULL:
+ pVal = Tcl_NewStringObj("NULL", -1);
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ Tcl_ListObjAppendElement(0, pRet, pVal);
+
+ if( mem.zMalloc ){
+ sqlite3DbFree(db, mem.zMalloc);
+ }
+ }
+
+ sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
+ Tcl_DecrRefCount(pRet);
+}
+
static int registerTestFunctions(sqlite3 *db){
static const struct {
@@ -482,6 +624,8 @@ static int registerTestFunctions(sqlite3 *db){
{ "test_isolation", 2, SQLITE_UTF8, test_isolation},
{ "test_counter", 1, SQLITE_UTF8, counterFunc},
{ "real2hex", 1, SQLITE_UTF8, real2hex},
+ { "test_decode", 1, SQLITE_UTF8, test_decode},
+ { "test_extract", 2, SQLITE_UTF8, test_extract},
};
int i;
diff --git a/src/test_init.c b/src/test_init.c
index e3724d8..502d95c 100644
--- a/src/test_init.c
+++ b/src/test_init.c
@@ -219,7 +219,6 @@ static int init_wrapper_uninstall(
return TCL_ERROR;
}
- memset(&wrapped, 0, sizeof(&wrapped));
sqlite3_shutdown();
sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped.mutex);
sqlite3_config(SQLITE_CONFIG_MALLOC, &wrapped.mem);
diff --git a/src/test_intarray.h b/src/test_intarray.h
index 691337d..6d26235 100644
--- a/src/test_intarray.h
+++ b/src/test_intarray.h
@@ -75,6 +75,8 @@
** action to free the intarray objects.
*/
#include "sqlite3.h"
+#ifndef _INTARRAY_H_
+#define _INTARRAY_H_
/*
** Make sure we can call this stuff from C++.
@@ -123,3 +125,4 @@ int sqlite3_intarray_bind(
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
+#endif /* _INTARRAY_H_ */
diff --git a/src/test_loadext.c b/src/test_loadext.c
index 1137e3a..5a1f46d 100644
--- a/src/test_loadext.c
+++ b/src/test_loadext.c
@@ -91,6 +91,9 @@ static void statusFunc(
/*
** Extension load function.
*/
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
int testloadext_init(
sqlite3 *db,
char **pzErrMsg,
@@ -109,6 +112,9 @@ int testloadext_init(
/*
** Another extension entry point. This one always fails.
*/
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
int testbrokenext_init(
sqlite3 *db,
char **pzErrMsg,
diff --git a/src/test_malloc.c b/src/test_malloc.c
index cf98a8f..e3cfcaa 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -749,7 +749,7 @@ static void test_memdebug_callback(int nByte, int nFrame, void **aFrame){
int isNew;
int aKey[MALLOC_LOG_KEYINTS];
- int nKey = sizeof(int)*MALLOC_LOG_KEYINTS;
+ unsigned int nKey = sizeof(int)*MALLOC_LOG_KEYINTS;
memset(aKey, 0, nKey);
if( (sizeof(void*)*nFrame)<nKey ){
@@ -1131,6 +1131,33 @@ static int test_config_heap(
}
/*
+** Usage: sqlite3_config_heap_size NBYTE
+*/
+static int test_config_heap_size(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nByte; /* Size to pass to sqlite3_config() */
+ int rc; /* Return code of sqlite3_config() */
+
+ Tcl_Obj * CONST *aArg = &objv[1];
+ int nArg = objc-1;
+
+ if( nArg!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, aArg[0], &nByte) ) return TCL_ERROR;
+
+ rc = sqlite3_config(SQLITE_CONFIG_WIN32_HEAPSIZE, nByte);
+
+ Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
** Usage: sqlite3_config_error [DB]
**
** Invoke sqlite3_config() or sqlite3_db_config() with invalid
@@ -1349,7 +1376,8 @@ static int test_db_status(
{ "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
{ "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
{ "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS },
- { "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE }
+ { "CACHE_WRITE", SQLITE_DBSTATUS_CACHE_WRITE },
+ { "DEFERRED_FKS", SQLITE_DBSTATUS_DEFERRED_FKS }
};
Tcl_Obj *pResult;
if( objc!=4 ){
@@ -1472,6 +1500,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){
{ "sqlite3_db_status", test_db_status ,0 },
{ "install_malloc_faultsim", test_install_malloc_faultsim ,0 },
{ "sqlite3_config_heap", test_config_heap ,0 },
+ { "sqlite3_config_heap_size", test_config_heap_size ,0 },
{ "sqlite3_config_memstatus", test_config_memstatus ,0 },
{ "sqlite3_config_lookaside", test_config_lookaside ,0 },
{ "sqlite3_config_error", test_config_error ,0 },
diff --git a/src/test_multiplex.c b/src/test_multiplex.c
index 624541b..427cc65 100644
--- a/src/test_multiplex.c
+++ b/src/test_multiplex.c
@@ -755,9 +755,11 @@ static int multiplexRead(
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
- multiplexEnter();
+ int nMutex = 0;
+ multiplexEnter(); nMutex++;
if( !pGroup->bEnabled ){
sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
+ multiplexLeave(); nMutex--;
if( pSubOpen==0 ){
rc = SQLITE_IOERR_READ;
}else{
@@ -766,7 +768,10 @@ static int multiplexRead(
}else{
while( iAmt > 0 ){
int i = (int)(iOfst / pGroup->szChunk);
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
+ sqlite3_file *pSubOpen;
+ if( nMutex==0 ){ multiplexEnter(); nMutex++; }
+ pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
+ multiplexLeave(); nMutex--;
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
if( extra<0 ) extra = 0;
@@ -783,7 +788,8 @@ static int multiplexRead(
}
}
}
- multiplexLeave();
+ assert( nMutex==0 || nMutex==1 );
+ if( nMutex ) multiplexLeave();
return rc;
}
@@ -1170,14 +1176,20 @@ int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining multiplex groups.
*/
-int sqlite3_multiplex_shutdown(void){
+int sqlite3_multiplex_shutdown(int eForce){
+ int rc = SQLITE_OK;
if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
- if( gMultiplex.pGroups ) return SQLITE_MISUSE;
+ if( gMultiplex.pGroups ){
+ sqlite3_log(SQLITE_MISUSE, "sqlite3_multiplex_shutdown() called "
+ "while database connections are still open");
+ if( !eForce ) return SQLITE_MISUSE;
+ rc = SQLITE_MISUSE;
+ }
gMultiplex.isInitialized = 0;
sqlite3_mutex_free(gMultiplex.pMutex);
sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
memset(&gMultiplex, 0, sizeof(gMultiplex));
- return SQLITE_OK;
+ return rc;
}
/***************************** Test Code ***********************************/
@@ -1230,13 +1242,16 @@ static int test_multiplex_shutdown(
UNUSED_PARAMETER(clientData);
- if( objc!=1 ){
- Tcl_WrongNumArgs(interp, 1, objv, "");
+ if( objc==2 && strcmp(Tcl_GetString(objv[1]),"-force")!=0 ){
+ objc = 3;
+ }
+ if( (objc!=1 && objc!=2) ){
+ Tcl_WrongNumArgs(interp, 1, objv, "?-force?");
return TCL_ERROR;
}
/* Call sqlite3_multiplex_shutdown() */
- rc = sqlite3_multiplex_shutdown();
+ rc = sqlite3_multiplex_shutdown(objc==2);
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
return TCL_OK;
diff --git a/src/test_multiplex.h b/src/test_multiplex.h
index b7e1afe..d973e4a 100644
--- a/src/test_multiplex.h
+++ b/src/test_multiplex.h
@@ -90,10 +90,10 @@ extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefaul
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining multiplex groups.
*/
-extern int sqlite3_multiplex_shutdown(void);
+extern int sqlite3_multiplex_shutdown(int eForce);
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* _TEST_MULTIPLEX_H */
diff --git a/src/test_osinst.c b/src/test_osinst.c
index 5314333..1701def 100644
--- a/src/test_osinst.c
+++ b/src/test_osinst.c
@@ -70,6 +70,12 @@
*/
#include "sqlite3.h"
+
+#include "os_setup.h"
+#if SQLITE_OS_WIN
+# include "os_win.h"
+#endif
+
#include <string.h>
#include <assert.h>
@@ -221,7 +227,6 @@ static sqlite3_uint64 vfslog_time(){
return sTime.tv_usec + (sqlite3_uint64)sTime.tv_sec * 1000000;
}
#elif SQLITE_OS_WIN
-#include <windows.h>
#include <time.h>
static sqlite3_uint64 vfslog_time(){
FILETIME ft;
diff --git a/src/test_quota.c b/src/test_quota.c
index e590996..80ebd05 100644
--- a/src/test_quota.c
+++ b/src/test_quota.c
@@ -44,49 +44,13 @@
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */
-
-/*
-** 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, and SQLITE_OS_OTHER
-** will defined to either 1 or 0. One of the four will be 1. The other
-** three will be 0.
-*/
-#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
-# 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
-# else
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 1
-# endif
-# else
-# define SQLITE_OS_UNIX 0
-# endif
-#else
-# ifndef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# endif
-#endif
+#include "os_setup.h"
#if SQLITE_OS_UNIX
# include <unistd.h>
#endif
#if SQLITE_OS_WIN
-# include <windows.h>
+# include "os_win.h"
# include <io.h>
#endif
diff --git a/src/test_quota.h b/src/test_quota.h
index 2d0767a..c17e15a 100644
--- a/src/test_quota.h
+++ b/src/test_quota.h
@@ -31,12 +31,6 @@
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
-#if SQLITE_OS_UNIX
-# include <unistd.h>
-#endif
-#if SQLITE_OS_WIN
-# include <windows.h>
-#endif
/* Make this callable from C++ */
#ifdef __cplusplus
diff --git a/src/test_rtree.c b/src/test_rtree.c
index f54ae9b..9d19fa0 100644
--- a/src/test_rtree.c
+++ b/src/test_rtree.c
@@ -14,6 +14,7 @@
*/
#include <sqlite3.h>
+#include <tcl.h>
/* Solely for the UNUSED_PARAMETER() macro. */
#include "sqliteInt.h"
@@ -34,6 +35,8 @@ struct Circle {
double centerx;
double centery;
double radius;
+ double mxArea;
+ int eScoreType;
};
/*
@@ -49,11 +52,7 @@ static void circle_del(void *p){
static int circle_geom(
sqlite3_rtree_geometry *p,
int nCoord,
-#ifdef SQLITE_RTREE_INT_ONLY
- sqlite3_int64 *aCoord,
-#else
- double *aCoord,
-#endif
+ sqlite3_rtree_dbl *aCoord,
int *pRes
){
int i; /* Iterator variable */
@@ -61,7 +60,12 @@ static int circle_geom(
double xmin, xmax; /* X dimensions of box being tested */
double ymin, ymax; /* X dimensions of box being tested */
- if( p->pUser==0 ){
+ xmin = aCoord[0];
+ xmax = aCoord[1];
+ ymin = aCoord[2];
+ ymax = aCoord[3];
+ pCircle = (Circle *)p->pUser;
+ if( pCircle==0 ){
/* If pUser is still 0, then the parameter values have not been tested
** for correctness or stored into a Circle structure yet. Do this now. */
@@ -107,14 +111,9 @@ static int circle_geom(
pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
pCircle->aBox[1].ymin = pCircle->centery;
pCircle->aBox[1].ymax = pCircle->centery;
+ pCircle->mxArea = (xmax - xmin)*(ymax - ymin) + 1.0;
}
- pCircle = (Circle *)p->pUser;
- xmin = aCoord[0];
- xmax = aCoord[1];
- ymin = aCoord[2];
- ymax = aCoord[3];
-
/* Check if any of the 4 corners of the bounding-box being tested lie
** inside the circular region. If they do, then the bounding-box does
** intersect the region of interest. Set the output variable to true and
@@ -153,6 +152,170 @@ static int circle_geom(
return SQLITE_OK;
}
+/*
+** Implementation of "circle" r-tree geometry callback using the
+** 2nd-generation interface that allows scoring.
+*/
+static int circle_query_func(sqlite3_rtree_query_info *p){
+ int i; /* Iterator variable */
+ Circle *pCircle; /* Structure defining circular region */
+ double xmin, xmax; /* X dimensions of box being tested */
+ double ymin, ymax; /* X dimensions of box being tested */
+ int nWithin = 0; /* Number of corners inside the circle */
+
+ xmin = p->aCoord[0];
+ xmax = p->aCoord[1];
+ ymin = p->aCoord[2];
+ ymax = p->aCoord[3];
+ pCircle = (Circle *)p->pUser;
+ if( pCircle==0 ){
+ /* If pUser is still 0, then the parameter values have not been tested
+ ** for correctness or stored into a Circle structure yet. Do this now. */
+
+ /* This geometry callback is for use with a 2-dimensional r-tree table.
+ ** Return an error if the table does not have exactly 2 dimensions. */
+ if( p->nCoord!=4 ) return SQLITE_ERROR;
+
+ /* Test that the correct number of parameters (4) have been supplied,
+ ** and that the parameters are in range (that the radius of the circle
+ ** radius is greater than zero). */
+ if( p->nParam!=4 || p->aParam[2]<0.0 ) return SQLITE_ERROR;
+
+ /* Allocate a structure to cache parameter data in. Return SQLITE_NOMEM
+ ** if the allocation fails. */
+ pCircle = (Circle *)(p->pUser = sqlite3_malloc(sizeof(Circle)));
+ if( !pCircle ) return SQLITE_NOMEM;
+ p->xDelUser = circle_del;
+
+ /* Record the center and radius of the circular region. One way that
+ ** tested bounding boxes that intersect the circular region are detected
+ ** is by testing if each corner of the bounding box lies within radius
+ ** units of the center of the circle. */
+ pCircle->centerx = p->aParam[0];
+ pCircle->centery = p->aParam[1];
+ pCircle->radius = p->aParam[2];
+ pCircle->eScoreType = (int)p->aParam[3];
+
+ /* Define two bounding box regions. The first, aBox[0], extends to
+ ** infinity in the X dimension. It covers the same range of the Y dimension
+ ** as the circular region. The second, aBox[1], extends to infinity in
+ ** the Y dimension and is constrained to the range of the circle in the
+ ** X dimension.
+ **
+ ** Then imagine each box is split in half along its short axis by a line
+ ** that intersects the center of the circular region. A bounding box
+ ** being tested can be said to intersect the circular region if it contains
+ ** points from each half of either of the two infinite bounding boxes.
+ */
+ pCircle->aBox[0].xmin = pCircle->centerx;
+ pCircle->aBox[0].xmax = pCircle->centerx;
+ pCircle->aBox[0].ymin = pCircle->centery + pCircle->radius;
+ pCircle->aBox[0].ymax = pCircle->centery - pCircle->radius;
+ pCircle->aBox[1].xmin = pCircle->centerx + pCircle->radius;
+ pCircle->aBox[1].xmax = pCircle->centerx - pCircle->radius;
+ pCircle->aBox[1].ymin = pCircle->centery;
+ pCircle->aBox[1].ymax = pCircle->centery;
+ pCircle->mxArea = 200.0*200.0;
+ }
+
+ /* Check if any of the 4 corners of the bounding-box being tested lie
+ ** inside the circular region. If they do, then the bounding-box does
+ ** intersect the region of interest. Set the output variable to true and
+ ** return SQLITE_OK in this case. */
+ for(i=0; i<4; i++){
+ double x = (i&0x01) ? xmax : xmin;
+ double y = (i&0x02) ? ymax : ymin;
+ double d2;
+
+ d2 = (x-pCircle->centerx)*(x-pCircle->centerx);
+ d2 += (y-pCircle->centery)*(y-pCircle->centery);
+ if( d2<(pCircle->radius*pCircle->radius) ) nWithin++;
+ }
+
+ /* Check if the bounding box covers any other part of the circular region.
+ ** See comments above for a description of how this test works. If it does
+ ** cover part of the circular region, set the output variable to true
+ ** and return SQLITE_OK. */
+ if( nWithin==0 ){
+ for(i=0; i<2; i++){
+ if( xmin<=pCircle->aBox[i].xmin
+ && xmax>=pCircle->aBox[i].xmax
+ && ymin<=pCircle->aBox[i].ymin
+ && ymax>=pCircle->aBox[i].ymax
+ ){
+ nWithin = 1;
+ break;
+ }
+ }
+ }
+
+ if( pCircle->eScoreType==1 ){
+ /* Depth first search */
+ p->rScore = p->iLevel;
+ }else if( pCircle->eScoreType==2 ){
+ /* Breadth first search */
+ p->rScore = 100 - p->iLevel;
+ }else if( pCircle->eScoreType==3 ){
+ /* Depth-first search, except sort the leaf nodes by area with
+ ** the largest area first */
+ if( p->iLevel==1 ){
+ p->rScore = 1.0 - (xmax-xmin)*(ymax-ymin)/pCircle->mxArea;
+ if( p->rScore<0.01 ) p->rScore = 0.01;
+ }else{
+ p->rScore = 0.0;
+ }
+ }else if( pCircle->eScoreType==4 ){
+ /* Depth-first search, except exclude odd rowids */
+ p->rScore = p->iLevel;
+ if( p->iRowid&1 ) nWithin = 0;
+ }else{
+ /* Breadth-first search, except exclude odd rowids */
+ p->rScore = 100 - p->iLevel;
+ if( p->iRowid&1 ) nWithin = 0;
+ }
+ if( nWithin==0 ){
+ p->eWithin = NOT_WITHIN;
+ }else if( nWithin>=4 ){
+ p->eWithin = FULLY_WITHIN;
+ }else{
+ p->eWithin = PARTLY_WITHIN;
+ }
+ return SQLITE_OK;
+}
+/*
+** Implementation of "breadthfirstsearch" r-tree geometry callback using the
+** 2nd-generation interface that allows scoring.
+**
+** ... WHERE id MATCH breadthfirstsearch($x0,$x1,$y0,$y1) ...
+**
+** It returns all entries whose bounding boxes overlap with $x0,$x1,$y0,$y1.
+*/
+static int bfs_query_func(sqlite3_rtree_query_info *p){
+ double x0,x1,y0,y1; /* Dimensions of box being tested */
+ double bx0,bx1,by0,by1; /* Boundary of the query function */
+
+ if( p->nParam!=4 ) return SQLITE_ERROR;
+ x0 = p->aCoord[0];
+ x1 = p->aCoord[1];
+ y0 = p->aCoord[2];
+ y1 = p->aCoord[3];
+ bx0 = p->aParam[0];
+ bx1 = p->aParam[1];
+ by0 = p->aParam[2];
+ by1 = p->aParam[3];
+ p->rScore = 100 - p->iLevel;
+ if( p->eParentWithin==FULLY_WITHIN ){
+ p->eWithin = FULLY_WITHIN;
+ }else if( x0>=bx0 && x1<=bx1 && y0>=by0 && y1<=by1 ){
+ p->eWithin = FULLY_WITHIN;
+ }else if( x1>=bx0 && x0<=bx1 && y1>=by0 && y0<=by1 ){
+ p->eWithin = PARTLY_WITHIN;
+ }else{
+ p->eWithin = NOT_WITHIN;
+ }
+ return SQLITE_OK;
+}
+
/* END of implementation of "circle" geometry callback.
**************************************************************************
*************************************************************************/
@@ -193,11 +356,7 @@ static int gHere = 42;
static int cube_geom(
sqlite3_rtree_geometry *p,
int nCoord,
-#ifdef SQLITE_RTREE_INT_ONLY
- sqlite3_int64 *aCoord,
-#else
- double *aCoord,
-#endif
+ sqlite3_rtree_dbl *aCoord,
int *piRes
){
Cube *pCube = (Cube *)p->pUser;
@@ -292,6 +451,14 @@ static int register_circle_geom(
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
rc = sqlite3_rtree_geometry_callback(db, "circle", circle_geom, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_rtree_query_callback(db, "Qcircle",
+ circle_query_func, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_rtree_query_callback(db, "breadthfirstsearch",
+ bfs_query_func, 0, 0);
+ }
Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
#endif
return TCL_OK;
diff --git a/src/test_schema.c b/src/test_schema.c
index 1264446..00a9f4d 100644
--- a/src/test_schema.c
+++ b/src/test_schema.c
@@ -344,7 +344,10 @@ int Sqlitetestschema_Init(Tcl_Interp *interp){
/*
** Extension load function.
*/
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_schema_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/src/test_server.c b/src/test_server.c
index ed0818e..4eb1cf1 100644
--- a/src/test_server.c
+++ b/src/test_server.c
@@ -473,6 +473,32 @@ void sqlite3_server_start(void){
}
/*
+** A wrapper around sqlite3_server() that decrements the int variable
+** pointed to by the first argument after the sqlite3_server() call
+** returns.
+*/
+static void *serverWrapper(void *pnDecr){
+ void *p = sqlite3_server(0);
+ (*(int*)pnDecr)--;
+ return p;
+}
+
+/*
+** This function is the similar to sqlite3_server_start(), except that
+** the integer pointed to by the first argument is decremented when
+** the server thread exits.
+*/
+void sqlite3_server_start2(int *pnDecr){
+ pthread_t x;
+ int rc;
+ g.serverHalt = 0;
+ rc = pthread_create(&x, 0, serverWrapper, (void*)pnDecr);
+ 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.
**
diff --git a/src/test_stat.c b/src/test_stat.c
index d4c902b..615df3d 100644
--- a/src/test_stat.c
+++ b/src/test_stat.c
@@ -397,6 +397,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
sqlite3_free(pCsr->zPath);
pCsr->zPath = 0;
+statNextRestart:
if( pCsr->aPage[0].pPg==0 ){
rc = sqlite3_step(pCsr->pStmt);
if( rc==SQLITE_ROW ){
@@ -448,11 +449,11 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
p->iCell++;
}
- while( !p->iRightChildPg || p->iCell>p->nCell ){
+ if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
if( pCsr->iPage==0 ) return statNext(pCursor);
pCsr->iPage--;
- p = &pCsr->aPage[pCsr->iPage];
+ goto statNextRestart; /* Tail recursion */
}
pCsr->iPage++;
assert( p==&pCsr->aPage[pCsr->iPage-1] );
diff --git a/src/test_syscall.c b/src/test_syscall.c
index 7c0873c..0dac2e8 100644
--- a/src/test_syscall.c
+++ b/src/test_syscall.c
@@ -67,6 +67,11 @@
** test_syscall list
** Return a list of all system calls. The list is constructed using
** the xNextSystemCall() VFS method.
+**
+** test_syscall pagesize PGSZ
+** If PGSZ is a power of two greater than 256, install a wrapper around
+** OS function getpagesize() that reports the system page size as PGSZ.
+** Or, if PGSZ is less than zero, remove any wrapper already installed.
*/
#include "sqliteInt.h"
@@ -89,7 +94,9 @@ static struct TestSyscallGlobal {
int bPersist; /* 1 for persistent errors, 0 for transient */
int nCount; /* Fail after this many more calls */
int nFail; /* Number of failures that have occurred */
-} gSyscall = { 0, 0 };
+ int pgsz;
+ sqlite3_syscall_ptr orig_getpagesize;
+} gSyscall = { 0, 0, 0, 0, 0 };
static int ts_open(const char *, int, int);
static int ts_close(int fd);
@@ -650,6 +657,45 @@ static int test_syscall_defaultvfs(
return TCL_OK;
}
+static int ts_getpagesize(void){
+ return gSyscall.pgsz;
+}
+
+static int test_syscall_pagesize(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ int pgsz;
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "PGSZ");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[2], &pgsz) ){
+ return TCL_ERROR;
+ }
+
+ if( pgsz<0 ){
+ if( gSyscall.orig_getpagesize ){
+ pVfs->xSetSystemCall(pVfs, "getpagesize", gSyscall.orig_getpagesize);
+ }
+ }else{
+ if( pgsz<512 || (pgsz & (pgsz-1)) ){
+ Tcl_AppendResult(interp, "pgsz out of range", 0);
+ return TCL_ERROR;
+ }
+ gSyscall.orig_getpagesize = pVfs->xGetSystemCall(pVfs, "getpagesize");
+ gSyscall.pgsz = pgsz;
+ pVfs->xSetSystemCall(
+ pVfs, "getpagesize", (sqlite3_syscall_ptr)ts_getpagesize
+ );
+ }
+
+ return TCL_OK;
+}
+
static int test_syscall(
void * clientData,
Tcl_Interp *interp,
@@ -668,6 +714,7 @@ static int test_syscall(
{ "exists", test_syscall_exists },
{ "list", test_syscall_list },
{ "defaultvfs", test_syscall_defaultvfs },
+ { "pagesize", test_syscall_pagesize },
{ 0, 0 }
};
int iCmd;
diff --git a/src/test_vfs.c b/src/test_vfs.c
index fcd5774..7ee2a93 100644
--- a/src/test_vfs.c
+++ b/src/test_vfs.c
@@ -28,6 +28,7 @@
#include "sqlite3.h"
#include "sqliteInt.h"
+#include <tcl.h>
typedef struct Testvfs Testvfs;
typedef struct TestvfsShm TestvfsShm;
@@ -126,8 +127,10 @@ struct Testvfs {
#define TESTVFS_FULLPATHNAME_MASK 0x00008000
#define TESTVFS_READ_MASK 0x00010000
#define TESTVFS_UNLOCK_MASK 0x00020000
+#define TESTVFS_LOCK_MASK 0x00040000
+#define TESTVFS_CKLOCK_MASK 0x00080000
-#define TESTVFS_ALL_MASK 0x0003FFFF
+#define TESTVFS_ALL_MASK 0x000FFFFF
#define TESTVFS_MAX_PAGES 1024
@@ -190,8 +193,11 @@ static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
static void tvfsShmBarrier(sqlite3_file*);
static int tvfsShmUnmap(sqlite3_file*, int);
+static int tvfsFetch(sqlite3_file*, sqlite3_int64, int, void**);
+static int tvfsUnfetch(sqlite3_file*, sqlite3_int64, void*);
+
static sqlite3_io_methods tvfs_io_methods = {
- 2, /* iVersion */
+ 3, /* iVersion */
tvfsClose, /* xClose */
tvfsRead, /* xRead */
tvfsWrite, /* xWrite */
@@ -207,7 +213,9 @@ static sqlite3_io_methods tvfs_io_methods = {
tvfsShmMap, /* xShmMap */
tvfsShmLock, /* xShmLock */
tvfsShmBarrier, /* xShmBarrier */
- tvfsShmUnmap /* xShmUnmap */
+ tvfsShmUnmap, /* xShmUnmap */
+ tvfsFetch,
+ tvfsUnfetch
};
static int tvfsResultCode(Testvfs *p, int *pRc){
@@ -460,8 +468,15 @@ static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
** Lock an tvfs-file.
*/
static int tvfsLock(sqlite3_file *pFile, int eLock){
- TestvfsFd *p = tvfsGetFd(pFile);
- return sqlite3OsLock(p->pReal, eLock);
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+ if( p->pScript && p->mask&TESTVFS_LOCK_MASK ){
+ char zLock[30];
+ sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);
+ tvfsExecTcl(p, "xLock", Tcl_NewStringObj(pFd->zFilename, -1),
+ Tcl_NewStringObj(zLock, -1), 0, 0);
+ }
+ return sqlite3OsLock(pFd->pReal, eLock);
}
/*
@@ -470,6 +485,12 @@ static int tvfsLock(sqlite3_file *pFile, int eLock){
static int tvfsUnlock(sqlite3_file *pFile, int eLock){
TestvfsFd *pFd = tvfsGetFd(pFile);
Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+ if( p->pScript && p->mask&TESTVFS_UNLOCK_MASK ){
+ char zLock[30];
+ sqlite3_snprintf(sizeof(zLock),zLock,"%d",eLock);
+ tvfsExecTcl(p, "xUnlock", Tcl_NewStringObj(pFd->zFilename, -1),
+ Tcl_NewStringObj(zLock, -1), 0, 0);
+ }
if( p->mask&TESTVFS_WRITE_MASK && tvfsInjectIoerr(p) ){
return SQLITE_IOERR_UNLOCK;
}
@@ -480,8 +501,13 @@ static int tvfsUnlock(sqlite3_file *pFile, int eLock){
** Check if another file-handle holds a RESERVED lock on an tvfs-file.
*/
static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
- TestvfsFd *p = tvfsGetFd(pFile);
- return sqlite3OsCheckReservedLock(p->pReal, pResOut);
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+ if( p->pScript && p->mask&TESTVFS_CKLOCK_MASK ){
+ tvfsExecTcl(p, "xCheckReservedLock", Tcl_NewStringObj(pFd->zFilename, -1),
+ 0, 0, 0);
+ }
+ return sqlite3OsCheckReservedLock(pFd->pReal, pResOut);
}
/*
@@ -618,7 +644,10 @@ static int tvfsOpen(
pMethods = (sqlite3_io_methods *)ckalloc(nByte);
memcpy(pMethods, &tvfs_io_methods, nByte);
- pMethods->iVersion = pVfs->iVersion;
+ pMethods->iVersion = pFd->pReal->pMethods->iVersion;
+ if( pMethods->iVersion>pVfs->iVersion ){
+ pMethods->iVersion = pVfs->iVersion;
+ }
if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){
pMethods->xShmUnmap = 0;
pMethods->xShmLock = 0;
@@ -993,6 +1022,21 @@ static int tvfsShmUnmap(
return rc;
}
+static int tvfsFetch(
+ sqlite3_file *pFile,
+ sqlite3_int64 iOfst,
+ int iAmt,
+ void **pp
+){
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ return sqlite3OsFetch(pFd->pReal, iOfst, iAmt, pp);
+}
+
+static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ return sqlite3OsUnfetch(pFd->pReal, iOfst, p);
+}
+
static int testvfs_obj_cmd(
ClientData cd,
Tcl_Interp *interp,
@@ -1087,26 +1131,32 @@ static int testvfs_obj_cmd(
break;
}
+ /* TESTVFS filter METHOD-LIST
+ **
+ ** Activate special processing for those methods contained in the list
+ */
case CMD_FILTER: {
static struct VfsMethod {
char *zName;
int mask;
} vfsmethod [] = {
- { "xShmOpen", TESTVFS_SHMOPEN_MASK },
- { "xShmLock", TESTVFS_SHMLOCK_MASK },
- { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
- { "xShmUnmap", TESTVFS_SHMCLOSE_MASK },
- { "xShmMap", TESTVFS_SHMMAP_MASK },
- { "xSync", TESTVFS_SYNC_MASK },
- { "xDelete", TESTVFS_DELETE_MASK },
- { "xWrite", TESTVFS_WRITE_MASK },
- { "xRead", TESTVFS_READ_MASK },
- { "xTruncate", TESTVFS_TRUNCATE_MASK },
- { "xOpen", TESTVFS_OPEN_MASK },
- { "xClose", TESTVFS_CLOSE_MASK },
- { "xAccess", TESTVFS_ACCESS_MASK },
- { "xFullPathname", TESTVFS_FULLPATHNAME_MASK },
- { "xUnlock", TESTVFS_UNLOCK_MASK },
+ { "xShmOpen", TESTVFS_SHMOPEN_MASK },
+ { "xShmLock", TESTVFS_SHMLOCK_MASK },
+ { "xShmBarrier", TESTVFS_SHMBARRIER_MASK },
+ { "xShmUnmap", TESTVFS_SHMCLOSE_MASK },
+ { "xShmMap", TESTVFS_SHMMAP_MASK },
+ { "xSync", TESTVFS_SYNC_MASK },
+ { "xDelete", TESTVFS_DELETE_MASK },
+ { "xWrite", TESTVFS_WRITE_MASK },
+ { "xRead", TESTVFS_READ_MASK },
+ { "xTruncate", TESTVFS_TRUNCATE_MASK },
+ { "xOpen", TESTVFS_OPEN_MASK },
+ { "xClose", TESTVFS_CLOSE_MASK },
+ { "xAccess", TESTVFS_ACCESS_MASK },
+ { "xFullPathname", TESTVFS_FULLPATHNAME_MASK },
+ { "xUnlock", TESTVFS_UNLOCK_MASK },
+ { "xLock", TESTVFS_LOCK_MASK },
+ { "xCheckReservedLock", TESTVFS_CKLOCK_MASK },
};
Tcl_Obj **apElem = 0;
int nElem = 0;
@@ -1138,6 +1188,12 @@ static int testvfs_obj_cmd(
break;
}
+ /*
+ ** TESTVFS script ?SCRIPT?
+ **
+ ** Query or set the script to be run when filtered VFS events
+ ** occur.
+ */
case CMD_SCRIPT: {
if( objc==3 ){
int nByte;
@@ -1224,6 +1280,7 @@ static int testvfs_obj_cmd(
{ "safe_append", SQLITE_IOCAP_SAFE_APPEND },
{ "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
+ { "immutable", SQLITE_IOCAP_IMMUTABLE },
{ 0, 0 }
};
Tcl_Obj *pRet;
@@ -1343,7 +1400,7 @@ static int testvfs_cmd(
Tcl_Obj *CONST objv[]
){
static sqlite3_vfs tvfs_vfs = {
- 2, /* iVersion */
+ 3, /* iVersion */
0, /* szOsFile */
0, /* mxPathname */
0, /* pNext */
@@ -1369,6 +1426,9 @@ static int testvfs_cmd(
tvfsCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
0, /* xCurrentTimeInt64 */
+ 0, /* xSetSystemCall */
+ 0, /* xGetSystemCall */
+ 0, /* xNextSystemCall */
};
Testvfs *p; /* New object */
@@ -1382,7 +1442,7 @@ static int testvfs_cmd(
int isDefault = 0; /* True if -default is passed */
int szOsFile = 0; /* Value passed to -szosfile */
int mxPathname = -1; /* Value passed to -mxpathname */
- int iVersion = 2; /* Value passed to -iversion */
+ int iVersion = 3; /* Value passed to -iversion */
if( objc<2 || 0!=(objc%2) ) goto bad_args;
for(i=2; i<objc; i += 2){
diff --git a/src/test_vfstrace.c b/src/test_vfstrace.c
index 0aacc01..d0bc29f 100644
--- a/src/test_vfstrace.c
+++ b/src/test_vfstrace.c
@@ -258,6 +258,11 @@ static void vfstrace_print_errcode(
case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break;
case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break;
case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break;
+ case SQLITE_IOERR_SHMMAP: zVal = "SQLITE_IOERR_SHMMAP"; break;
+ case SQLITE_IOERR_SEEK: zVal = "SQLITE_IOERR_SEEK"; break;
+ case SQLITE_IOERR_GETTEMPPATH: zVal = "SQLITE_IOERR_GETTEMPPATH"; break;
+ case SQLITE_IOERR_CONVPATH: zVal = "SQLITE_IOERR_CONVPATH"; break;
+ case SQLITE_READONLY_DBMOVED: zVal = "SQLITE_READONLY_DBMOVED"; break;
case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break;
case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
@@ -673,7 +678,7 @@ static int vfstraceAccess(
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
- vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
+ vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
pInfo->zVfsName, zPath, flags);
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
diff --git a/src/tokenize.c b/src/tokenize.c
index faea5f2..4017c3b 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -123,7 +123,6 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
case '-': {
if( z[1]=='-' ){
- /* IMP: R-50417-27976 -- syntax diagram for comments */
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
@@ -156,7 +155,6 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SLASH;
return 1;
}
- /* IMP: R-50417-27976 -- syntax diagram for comments */
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
if( c ) i++;
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
@@ -272,6 +270,12 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
testcase( z[0]=='9' );
*tokenType = TK_INTEGER;
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
+ for(i=3; sqlite3Isxdigit(z[i]); i++){}
+ return i;
+ }
+#endif
for(i=0; sqlite3Isdigit(z[i]); i++){}
#ifndef SQLITE_OMIT_FLOATING_POINT
if( z[i]=='.' ){
@@ -305,24 +309,15 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
for(i=1; sqlite3Isdigit(z[i]); i++){}
return i;
}
- case '#': {
- for(i=1; sqlite3Isdigit(z[i]); i++){}
- if( i>1 ){
- /* Parameters of the form #NNN (where NNN is a number) are used
- ** internally by sqlite3NestedParse. */
- *tokenType = TK_REGISTER;
- return i;
- }
- /* Fall through into the next case if the '#' is not followed by
- ** a digit. Try to match #AAAA where AAAA is a parameter name. */
- }
#ifndef SQLITE_OMIT_TCL_VARIABLE
case '$':
#endif
case '@': /* For compatibility with MS SQL Server */
+ case '#':
case ':': {
int n = 0;
- testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' );
+ testcase( z[0]=='$' ); testcase( z[0]=='@' );
+ testcase( z[0]==':' ); testcase( z[0]=='#' );
*tokenType = TK_VARIABLE;
for(i=1; (c=z[i])!=0; i++){
if( IdChar(c) ){
@@ -396,7 +391,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
pParse->rc = SQLITE_OK;
@@ -505,10 +500,10 @@ abort_parse:
sqlite3DeleteTable(db, pParse->pNewTable);
}
+ if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith);
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
sqlite3DbFree(db, pParse->azVar);
- sqlite3DbFree(db, pParse->aAlias);
while( pParse->pAinc ){
AutoincInfo *p = pParse->pAinc;
pParse->pAinc = p->pNext;
diff --git a/src/trigger.c b/src/trigger.c
index f1ff766..01f7b21 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -148,8 +148,8 @@ void sqlite3BeginTrigger(
/* Ensure the table name matches database name and that the table exists */
if( db->mallocFailed ) goto trigger_cleanup;
assert( pTableName->nSrc==1 );
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
- sqlite3FixSrcList(&sFix, pTableName) ){
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName);
+ if( sqlite3FixSrcList(&sFix, pTableName) ){
goto trigger_cleanup;
}
pTab = sqlite3SrcListLookup(pParse, pTableName);
@@ -291,8 +291,10 @@ void sqlite3FinishTrigger(
}
nameToken.z = pTrig->zName;
nameToken.n = sqlite3Strlen30(nameToken.z);
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
- && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
+ if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
+ || sqlite3FixExpr(&sFix, pTrig->pWhen)
+ ){
goto triggerfinish_cleanup;
}
@@ -395,25 +397,21 @@ 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 */
u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
){
TriggerStep *pTriggerStep;
- assert(pEList == 0 || pSelect == 0);
- assert(pEList != 0 || pSelect != 0 || db->mallocFailed);
+ assert(pSelect != 0 || db->mallocFailed);
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
if( pTriggerStep ){
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
pTriggerStep->pIdList = pColumn;
- pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
pTriggerStep->orconf = orconf;
}else{
sqlite3IdListDelete(db, pColumn);
}
- sqlite3ExprListDelete(db, pEList);
sqlite3SelectDelete(db, pSelect);
return pTriggerStep;
@@ -568,6 +566,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
int base;
+ static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList dropTrigger[] = {
{ OP_Rewind, 0, ADDR(9), 0},
{ OP_String8, 0, 1, 0}, /* 1 */
@@ -582,7 +581,7 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3OpenMasterTable(pParse, iDb);
- base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
+ base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger, iLn);
sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT);
sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
sqlite3ChangeCookie(pParse, iDb);
@@ -728,15 +727,7 @@ static int codeTriggerProgram(
** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy
*/
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
-
- /* Clear the cookieGoto flag. When coding triggers, the cookieGoto
- ** variable is used as a flag to indicate to sqlite3ExprCodeConstants()
- ** that it is not safe to refactor constants (this happens after the
- ** start of the first loop in the SQL statement is coded - at that
- ** point code may be conditionally executed, so it is no longer safe to
- ** initialize constant register values). */
- assert( pParse->cookieGoto==0 || pParse->cookieGoto==-1 );
- pParse->cookieGoto = 0;
+ assert( pParse->okConstFactor==0 );
switch( pStep->op ){
case TK_UPDATE: {
@@ -751,7 +742,6 @@ static int codeTriggerProgram(
case TK_INSERT: {
sqlite3Insert(pParse,
targetSrcList(pParse, pStep),
- sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
pParse->eOrconf
@@ -782,7 +772,7 @@ static int codeTriggerProgram(
return 0;
}
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
/*
** This function is used to add VdbeComment() annotations to a VDBE
** program. It is not used in production code, only for debugging.
@@ -922,6 +912,7 @@ static TriggerPrg *codeRowTrigger(
assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
+ sqlite3ParserReset(pSubParse);
sqlite3StackFree(db, pSubParse);
return pPrg;
@@ -1002,7 +993,7 @@ void sqlite3CodeRowTriggerDirect(
/*
** This is called to code the required FOR EACH ROW triggers for an operation
** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
-** is given by the op paramater. The tr_tm parameter determines whether the
+** is given by the op parameter. The tr_tm parameter determines whether the
** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
** parameter pChanges is passed the list of columns being modified.
**
diff --git a/src/update.c b/src/update.c
index 3ab1ab2..e152e90 100644
--- a/src/update.c
+++ b/src/update.c
@@ -61,7 +61,7 @@ static void updateVirtualTable(
void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
assert( pTab!=0 );
if( !pTab->pSelect ){
- sqlite3_value *pValue;
+ sqlite3_value *pValue = 0;
u8 enc = ENC(sqlite3VdbeDb(v));
Column *pCol = &pTab->aCol[i];
VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
@@ -72,7 +72,7 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM);
}
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
}
#endif
@@ -95,25 +95,32 @@ void sqlite3Update(
){
int i, j; /* Loop counters */
Table *pTab; /* The table to be updated */
- int addr = 0; /* VDBE instruction address of the start of the loop */
+ int addrTop = 0; /* VDBE instruction address of the start of the loop */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Vdbe *v; /* The virtual database engine */
Index *pIdx; /* For looping over indices */
+ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
int nIdx; /* Number of indices that need updating */
- int iCur; /* VDBE Cursor number of pTab */
+ int iBaseCur; /* Base cursor number */
+ int iDataCur; /* Cursor for the canonical data btree */
+ int iIdxCur; /* Cursor for the first index */
sqlite3 *db; /* The database structure */
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. */
- int chngRowid; /* True if the record number is being changed */
+ u8 *aToOpen; /* 1 for tables and indices to be opened */
+ u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */
+ u8 chngRowid; /* Rowid changed in a normal table */
+ u8 chngKey; /* Either chngPk or chngRowid */
Expr *pRowidExpr = 0; /* Expression defining the new record number */
- int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */
int iDb; /* Database containing the table being updated */
int okOnePass; /* True for one-pass algorithm without the FIFO */
int hasFK; /* True if foreign key processing is required */
+ int labelBreak; /* Jump here to break out of UPDATE loop */
+ int labelContinue; /* Jump here to continue next step of UPDATE loop */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True when updating a view (INSTEAD OF trigger) */
@@ -121,6 +128,9 @@ void sqlite3Update(
int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
#endif
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
+ int iEph = 0; /* Ephemeral table holding all primary key values */
+ int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */
+ int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
@@ -129,6 +139,7 @@ void sqlite3Update(
int regNew; /* Content of the NEW.* table in triggers */
int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
+ int regKey = 0; /* composite PRIMARY KEY value */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -166,20 +177,34 @@ void sqlite3Update(
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
goto update_cleanup;
}
- aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol );
- if( aXRef==0 ) goto update_cleanup;
- for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
/* Allocate a cursors for the main database table and for all indices.
** The index cursors might not be used, but if they are used they
** need to occur right after the database cursor. So go ahead and
** allocate enough space, just in case.
*/
- pTabList->a[0].iCursor = iCur = pParse->nTab++;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
+ iIdxCur = iDataCur+1;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
+ if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
+ iDataCur = pParse->nTab;
+ pTabList->a[0].iCursor = iDataCur;
+ }
pParse->nTab++;
}
+ /* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
+ ** Initialize aXRef[] and aToOpen[] to their default values.
+ */
+ aXRef = sqlite3DbMallocRaw(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
+ if( aXRef==0 ) goto update_cleanup;
+ aRegIdx = aXRef+pTab->nCol;
+ aToOpen = (u8*)(aRegIdx+nIdx);
+ memset(aToOpen, 1, nIdx+1);
+ aToOpen[nIdx+1] = 0;
+ for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
+
/* Initialize the name-context */
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
@@ -191,7 +216,7 @@ void sqlite3Update(
** column to be updated, make sure we have authorization to change
** that column.
*/
- chngRowid = 0;
+ chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
@@ -201,13 +226,15 @@ void sqlite3Update(
if( j==pTab->iPKey ){
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
+ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ chngPk = 1;
}
aXRef[j] = i;
break;
}
}
if( j>=pTab->nCol ){
- if( sqlite3IsRowid(pChanges->a[i].zName) ){
+ if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
j = -1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
@@ -231,32 +258,36 @@ void sqlite3Update(
}
#endif
}
+ assert( (chngRowid & chngPk)==0 );
+ assert( chngRowid==0 || chngRowid==1 );
+ assert( chngPk==0 || chngPk==1 );
+ chngKey = chngRowid + chngPk;
- hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid);
+ /* The SET expressions are not actually used inside the WHERE loop.
+ ** So reset the colUsed mask
+ */
+ pTabList->a[0].colUsed = 0;
- /* 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.
+ hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
+
+ /* There is one entry in the aRegIdx[] array for each index on the table
+ ** being updated. Fill in aRegIdx[] with a register number that will hold
+ ** the key for accessing each index.
*/
- 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(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
- if( hasFK || chngRowid ){
+ if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
reg = ++pParse->nMem;
}else{
reg = 0;
- for(i=0; i<pIdx->nColumn; i++){
+ for(i=0; i<pIdx->nKeyCol; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ){
reg = ++pParse->nMem;
break;
}
}
}
+ if( reg==0 ) aToOpen[j+1] = 0;
aRegIdx[j] = reg;
}
@@ -280,11 +311,11 @@ void sqlite3Update(
/* Allocate required registers. */
regRowSet = ++pParse->nMem;
regOldRowid = regNewRowid = ++pParse->nMem;
- if( pTrigger || hasFK ){
+ if( chngPk || pTrigger || hasFK ){
regOld = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
}
- if( chngRowid || pTrigger || hasFK ){
+ if( chngKey || pTrigger || hasFK ){
regNewRowid = ++pParse->nMem;
}
regNew = pParse->nMem + 1;
@@ -300,7 +331,7 @@ void sqlite3Update(
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
- sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
}
#endif
@@ -313,24 +344,58 @@ void sqlite3Update(
/* Begin the database scan
*/
- sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
- pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
- );
- if( pWInfo==0 ) goto update_cleanup;
- okOnePass = pWInfo->okOnePass;
-
- /* Remember the rowid of every item to be updated.
- */
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
- if( !okOnePass ){
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
+ pWInfo = sqlite3WhereBegin(
+ pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, iIdxCur
+ );
+ if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+
+ /* Remember the rowid of every item to be updated.
+ */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
+ if( !okOnePass ){
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+ }else{
+ int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
+ i16 nPk; /* Number of components of the PRIMARY KEY */
+ int addrOpen; /* Address of the OpenEphemeral instruction */
+
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+ iPk = pParse->nMem+1;
+ pParse->nMem += nPk;
+ regKey = ++pParse->nMem;
+ iEph = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
+ addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
+ WHERE_ONEPASS_DESIRED, iIdxCur);
+ if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ for(i=0; i<nPk; i++){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
+ iPk+i);
+ }
+ if( okOnePass ){
+ sqlite3VdbeChangeToNoop(v, addrOpen);
+ nKey = nPk;
+ regKey = iPk;
+ }else{
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
+ sqlite3IndexAffinityStr(v, pPk), nPk);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
+ }
+ sqlite3WhereEnd(pWInfo);
}
- /* End the database scan loop.
- */
- sqlite3WhereEnd(pWInfo);
-
/* Initialize the count of updated rows
*/
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
@@ -338,6 +403,7 @@ void sqlite3Update(
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
+ labelBreak = sqlite3VdbeMakeLabel(v);
if( !isView ){
/*
** Open every index that needs updating. Note that if any
@@ -345,68 +411,78 @@ void sqlite3Update(
** action, then we need to open all indices because we might need
** to be deleting some records.
*/
- if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
if( onError==OE_Replace ){
- openAll = 1;
+ memset(aToOpen, 1, nIdx+1);
}else{
- openAll = 0;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->onError==OE_Replace ){
- openAll = 1;
+ memset(aToOpen, 1, nIdx+1);
break;
}
}
}
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- assert( aRegIdx );
- 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 );
- }
+ if( okOnePass ){
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
}
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, iBaseCur, aToOpen,
+ 0, 0);
}
/* Top of the update loop */
if( okOnePass ){
- int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, a1);
+ if( aToOpen[iDataCur-iBaseCur] ){
+ assert( pPk!=0 );
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
+ VdbeCoverageNeverTaken(v);
+ }
+ labelContinue = labelBreak;
+ sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
+ VdbeCoverageIf(v, pPk==0);
+ VdbeCoverageIf(v, pPk!=0);
+ }else if( pPk ){
+ labelContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
+ addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
+ VdbeCoverage(v);
}else{
- addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
+ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
+ regOldRowid);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
+ VdbeCoverage(v);
}
- /* Make cursor iCur point to the record that is being updated. If
- ** this record does not exist for some reason (deleted by a trigger,
- ** for example, then jump to the next iteration of the RowSet loop. */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
/* If the record number will change, set register regNewRowid to
** contain the new value. If the record number is not being modified,
** then regNewRowid is the same register as regOldRowid, which is
** already populated. */
- assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid );
+ assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
if( chngRowid ){
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v);
}
- /* If there are triggers on this table, populate an array of registers
- ** with the required old.* column data. */
- if( hasFK || pTrigger ){
+ /* Compute the old pre-UPDATE content of the row being changed, if that
+ ** information is needed */
+ if( chngPk || hasFK || pTrigger ){
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
oldmask |= sqlite3TriggerColmask(pParse,
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
);
for(i=0; i<pTab->nCol; i++){
- if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i);
+ if( oldmask==0xffffffff
+ || (i<32 && (oldmask & MASKBIT32(i))!=0)
+ || (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
+ ){
+ testcase( oldmask!=0xffffffff && i==31 );
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}
- if( chngRowid==0 ){
+ if( chngRowid==0 && pPk==0 ){
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
@@ -427,15 +503,15 @@ void sqlite3Update(
newmask = sqlite3TriggerColmask(
pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError
);
- sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);
+ /*sqlite3VdbeAddOp3(v, OP_Null, 0, regNew, regNew+pTab->nCol-1);*/
for(i=0; i<pTab->nCol; i++){
if( i==pTab->iPKey ){
- /*sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);*/
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}else{
j = aXRef[i];
if( j>=0 ){
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
- }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){
+ }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
/* This branch loads the value of a column that will not be changed
** into a register. This is done if there are no BEFORE triggers, or
** if there are one or more BEFORE triggers that use this value via
@@ -443,8 +519,9 @@ void sqlite3Update(
*/
testcase( i==31 );
testcase( i==32 );
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
- sqlite3ColumnDefault(v, pTab, i, regNew+i);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}
}
}
@@ -453,10 +530,9 @@ void sqlite3Update(
** verified. One could argue that this is wrong.
*/
if( tmask&TRIGGER_BEFORE ){
- sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
- sqlite3TableAffinityStr(v, pTab);
+ sqlite3TableAffinity(v, pTab, regNew);
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
+ TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
/* The row-trigger may have deleted the row being updated. In this
** case, jump to the next row. No updates or AFTER triggers are
@@ -464,7 +540,13 @@ void sqlite3Update(
** is deleted or renamed by a BEFORE trigger - is left undefined in the
** documentation.
*/
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
+ VdbeCoverage(v);
+ }
/* If it did not delete it, the row-trigger may still have modified
** some of the columns of the row being updated. Load the values for
@@ -473,46 +555,57 @@ void sqlite3Update(
*/
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]<0 && i!=pTab->iPKey ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
- sqlite3ColumnDefault(v, pTab, i, regNew+i);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
}
}
}
if( !isView ){
- int j1; /* Address of jump instruction */
+ int j1 = 0; /* Address of jump instruction */
+ int bReplace = 0; /* True if REPLACE conflict resolution might happen */
/* Do constraint checks. */
- sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
- aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
+ assert( regOldRowid>0 );
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+ regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace);
/* Do FK constraint checks. */
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
}
/* Delete the index entries associated with the current record. */
- j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
- sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
+ if( bReplace || chngKey ){
+ if( pPk ){
+ j1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
+ }else{
+ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
+ }
+ VdbeCoverageNeverTaken(v);
+ }
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
/* If changing the record number, delete the old record. */
- if( hasFK || chngRowid ){
- sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
+ if( hasFK || chngKey || pPk!=0 ){
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
+ }
+ if( bReplace || chngKey ){
+ sqlite3VdbeJumpHere(v, j1);
}
- sqlite3VdbeJumpHere(v, j1);
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
+ sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
}
/* Insert the new index entries and the new record. */
- sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0);
+ sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
+ regNewRowid, aRegIdx, 1, 0, 0);
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just updated. */
if( hasFK ){
- sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
+ sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
}
}
@@ -523,22 +616,29 @@ void sqlite3Update(
}
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, regOldRowid, onError, addr);
+ TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
- sqlite3VdbeJumpHere(v, addr);
+ if( okOnePass ){
+ /* Nothing to do at end-of-loop for a single-pass */
+ }else if( pPk ){
+ sqlite3VdbeResolveLabel(v, labelContinue);
+ sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, labelContinue);
+ }
+ sqlite3VdbeResolveLabel(v, labelBreak);
/* Close all tables */
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
assert( aRegIdx );
- if( openAll || aRegIdx[i]>0 ){
- sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
+ if( aToOpen[i+1] ){
+ sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0);
}
}
- sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
+ if( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0);
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
@@ -561,8 +661,7 @@ void sqlite3Update(
update_cleanup:
sqlite3AuthContextPop(&sContext);
- sqlite3DbFree(db, aRegIdx);
- sqlite3DbFree(db, aXRef);
+ sqlite3DbFree(db, aXRef); /* Also frees aRegIdx[] and aToOpen[] */
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pChanges);
sqlite3ExprDelete(db, pWhere);
@@ -655,7 +754,7 @@ static void updateVirtualTable(
/* Generate code to scan the ephemeral table and call VUpdate. */
iReg = ++pParse->nMem;
pParse->nMem += pTab->nCol+1;
- addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
+ addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
for(i=0; i<pTab->nCol; i++){
@@ -665,7 +764,7 @@ static void updateVirtualTable(
sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
+ sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr);
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
diff --git a/src/utf.c b/src/utf.c
index 6d5b1bf..9789874 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -148,8 +148,8 @@ static const unsigned char sqlite3Utf8Trans1[] = {
** and rendered as themselves even though they are technically
** invalid characters.
**
-** * This routine accepts an infinite number of different UTF8 encodings
-** for unicode values 0x80 and greater. It do not change over-length
+** * This routine accepts over-length UTF8 encodings
+** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \
@@ -317,7 +317,7 @@ int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
sqlite3VdbeMemRelease(pMem);
pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
pMem->enc = desiredEnc;
- pMem->flags |= (MEM_Term|MEM_Dyn);
+ pMem->flags |= (MEM_Term);
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
@@ -445,38 +445,11 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
}
assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
- assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed );
assert( m.z || db->mallocFailed );
return m.z;
}
/*
-** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
-** enc. A pointer to the new string is returned, and the value of *pnOut
-** is set to the length of the returned string in bytes. The call should
-** arrange to call sqlite3DbFree() on the returned pointer when it is
-** no longer required.
-**
-** If a malloc failure occurs, NULL is returned and the db.mallocFailed
-** flag set.
-*/
-#ifdef SQLITE_ENABLE_STAT3
-char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
- Mem m;
- memset(&m, 0, sizeof(m));
- m.db = db;
- sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
- if( sqlite3VdbeMemTranslate(&m, enc) ){
- assert( db->mallocFailed );
- return 0;
- }
- assert( m.z==m.zMalloc );
- *pnOut = m.n;
- return m.z;
-}
-#endif
-
-/*
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
** Return the number of bytes in the first nChar unicode characters
** in pZ. nChar must be non-negative.
diff --git a/src/util.c b/src/util.c
index d83a630..619af7f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -31,6 +31,24 @@ void sqlite3Coverage(int x){
}
#endif
+/*
+** Give a callback to the test harness that can be used to simulate faults
+** in places where it is difficult or expensive to do so purely by means
+** of inputs.
+**
+** The intent of the integer argument is to let the fault simulator know
+** which of multiple sqlite3FaultSim() calls has been hit.
+**
+** Return whatever integer value the test callback returns, or return
+** SQLITE_OK if no test callback is installed.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+int sqlite3FaultSim(int iTest){
+ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback;
+ return xCallback ? xCallback(iTest) : SQLITE_OK;
+}
+#endif
+
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).
@@ -115,18 +133,17 @@ int sqlite3Strlen30(const char *z){
** to NULL.
*/
void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
- 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(db, zFormat, ap);
- va_end(ap);
- sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
- }else{
- sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
- }
+ assert( db!=0 );
+ db->errCode = err_code;
+ if( zFormat && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
+ char *z;
+ va_list ap;
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(db, zFormat, ap);
+ va_end(ap);
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
+ }else if( db->pErr ){
+ sqlite3ValueSetNull(db->pErr);
}
}
@@ -193,7 +210,8 @@ int sqlite3Dequote(char *z){
case '[': quote = ']'; break; /* For MS SqlServer compatibility */
default: return -1;
}
- for(i=1, j=0; ALWAYS(z[i]); i++){
+ for(i=1, j=0;; i++){
+ assert( z[i] );
if( z[i]==quote ){
if( z[i+1]==quote ){
z[j++] = quote;
@@ -457,19 +475,19 @@ static int compare2pow63(const char *zNum, int incr){
return c;
}
-
/*
-** Convert zNum to a 64-bit signed integer.
+** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
+** routine does *not* accept hexadecimal notation.
**
** If the zNum value is representable as a 64-bit twos-complement
** integer, then write that value into *pNum and return 0.
**
-** If zNum is exactly 9223372036854665808, return 2. This special
-** case is broken out because while 9223372036854665808 cannot be a
-** signed 64-bit integer, its negative -9223372036854665808 can be.
+** If zNum is exactly 9223372036854775808, return 2. This special
+** case is broken out because while 9223372036854775808 cannot be a
+** signed 64-bit integer, its negative -9223372036854775808 can be.
**
** If zNum is too big for a 64-bit integer and is not
-** 9223372036854665808 or if zNum contains any non-numeric text,
+** 9223372036854775808 or if zNum contains any non-numeric text,
** then return 1.
**
** length is the number of bytes in the string (bytes, not characters).
@@ -511,7 +529,7 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
u = u*10 + c - '0';
}
if( u>LARGEST_INT64 ){
- *pNum = SMALLEST_INT64;
+ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
}else if( neg ){
*pNum = -(i64)u;
}else{
@@ -542,16 +560,49 @@ int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
/* zNum is exactly 9223372036854775808. Fits if negative. The
** special case 2 overflow if positive */
assert( u-1==LARGEST_INT64 );
- assert( (*pNum)==SMALLEST_INT64 );
return neg ? 0 : 2;
}
}
}
/*
+** Transform a UTF-8 integer literal, in either decimal or hexadecimal,
+** into a 64-bit signed integer. This routine accepts hexadecimal literals,
+** whereas sqlite3Atoi64() does not.
+**
+** Returns:
+**
+** 0 Successful transformation. Fits in a 64-bit signed integer.
+** 1 Integer too large for a 64-bit signed integer or is malformed
+** 2 Special case of 9223372036854775808
+*/
+int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( z[0]=='0'
+ && (z[1]=='x' || z[1]=='X')
+ && sqlite3Isxdigit(z[2])
+ ){
+ u64 u = 0;
+ int i, k;
+ for(i=2; z[i]=='0'; i++){}
+ for(k=i; sqlite3Isxdigit(z[k]); k++){
+ u = u*16 + sqlite3HexToInt(z[k]);
+ }
+ memcpy(pOut, &u, 8);
+ return (z[k]==0 && k-i<=16) ? 0 : 1;
+ }else
+#endif /* SQLITE_OMIT_HEX_INTEGER */
+ {
+ return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
+ }
+}
+
+/*
** If zNum represents an integer that will fit in 32-bits, then set
** *pValue to that integer and return true. Otherwise return false.
**
+** This routine accepts both decimal and hexadecimal notation for integers.
+**
** Any non-numeric characters that following zNum are ignored.
** This is different from sqlite3Atoi64() which requires the
** input number to be zero-terminated.
@@ -566,7 +617,25 @@ int sqlite3GetInt32(const char *zNum, int *pValue){
}else if( zNum[0]=='+' ){
zNum++;
}
- while( zNum[0]=='0' ) zNum++;
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ else if( zNum[0]=='0'
+ && (zNum[1]=='x' || zNum[1]=='X')
+ && sqlite3Isxdigit(zNum[2])
+ ){
+ u32 u = 0;
+ zNum += 2;
+ while( zNum[0]=='0' ) zNum++;
+ for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){
+ u = u*16 + sqlite3HexToInt(zNum[i]);
+ }
+ if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
+ memcpy(pValue, &u, 4);
+ return 1;
+ }else{
+ return 0;
+ }
+ }
+#endif
for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
v = v*10 + c;
}
@@ -1002,7 +1071,8 @@ int sqlite3VarintLen(u64 v){
** Read or write a four-byte big-endian integer value.
*/
u32 sqlite3Get4byte(const u8 *p){
- return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+ testcase( p[0]&0x80 );
+ return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}
void sqlite3Put4byte(unsigned char *p, u32 v){
p[0] = (u8)(v>>24);
@@ -1123,13 +1193,12 @@ int sqlite3AddInt64(i64 *pA, i64 iB){
testcase( iA>0 && LARGEST_INT64 - iA == iB );
testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
- *pA += iB;
}else{
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
- *pA += iB;
}
+ *pA += iB;
return 0;
}
int sqlite3SubInt64(i64 *pA, i64 iB){
@@ -1153,9 +1222,18 @@ int sqlite3MulInt64(i64 *pA, i64 iB){
iA0 = iA % TWOPOWER32;
iB1 = iB/TWOPOWER32;
iB0 = iB % TWOPOWER32;
- if( iA1*iB1 != 0 ) return 1;
- assert( iA1*iB0==0 || iA0*iB1==0 );
- r = iA1*iB0 + iA0*iB1;
+ if( iA1==0 ){
+ if( iB1==0 ){
+ *pA *= iB;
+ return 0;
+ }
+ r = iA0*iB1;
+ }else if( iB1==0 ){
+ r = iA1*iB0;
+ }else{
+ /* If both iA1 and iB1 are non-zero, overflow will result */
+ return 1;
+ }
testcase( r==(-TWOPOWER31)-1 );
testcase( r==(-TWOPOWER31) );
testcase( r==TWOPOWER31 );
@@ -1207,3 +1285,82 @@ void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
}
}
#endif
+
+/*
+** Find (an approximate) sum of two LogEst values. This computation is
+** not a simple "+" operator because LogEst is stored as a logarithmic
+** value.
+**
+*/
+LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a>=b ){
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+ }else{
+ if( b>a+49 ) return b;
+ if( b>a+31 ) return b+1;
+ return b+x[b-a];
+ }
+}
+
+/*
+** Convert an integer into a LogEst. In other words, compute an
+** approximation for 10*log2(x).
+*/
+LogEst sqlite3LogEst(u64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; }
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Convert a double into a LogEst
+** In other words, compute an approximation for 10*log2(x).
+*/
+LogEst sqlite3LogEstFromDouble(double x){
+ u64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=1 ) return 0;
+ if( x<=2000000000 ) return sqlite3LogEst((u64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Convert a LogEst into an integer.
+*/
+u64 sqlite3LogEstToInt(LogEst x){
+ u64 n;
+ if( x<10 ) return 1;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+ if( x>=3 ){
+ return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3);
+ }
+ return (n+8)>>(3-x);
+}
diff --git a/src/vacuum.c b/src/vacuum.c
index 4afb2cc..936a44a 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -72,14 +72,34 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
}
/*
-** The non-standard VACUUM command is used to clean up the database,
+** The VACUUM command is used to clean up the database,
** collapse free space, etc. It is modelled after the VACUUM command
-** in PostgreSQL.
+** in PostgreSQL. The VACUUM command works as follows:
**
-** In version 1.0.x of SQLite, the VACUUM command would call
-** gdbm_reorganize() on all the database tables. But beginning
-** with 2.0.0, SQLite no longer uses GDBM so this command has
-** become a no-op.
+** (1) Create a new transient database file
+** (2) Copy all content from the database being vacuumed into
+** the new transient database file
+** (3) Copy content from the transient database back into the
+** original database.
+**
+** The transient database requires temporary disk space approximately
+** equal to the size of the original database. The copy operation of
+** step (3) requires additional temporary disk space approximately equal
+** to the size of the original database for the rollback journal.
+** Hence, temporary disk space that is approximately 2x the size of the
+** orginal database is required. Every page of the database is written
+** approximately 3 times: Once for step (2) and twice for step (3).
+** Two writes per page are required in step (3) because the original
+** database content must be written into the rollback journal prior to
+** overwriting the database with the vacuumed content.
+**
+** Only 1x temporary space and only 1x writes would be required if
+** the copy of step (3) were replace by deleting the original database
+** and renaming the transient database as the original. But that will
+** not work if other processes are attached to the original database.
+** And a power loss in between deleting the original and renaming the
+** transient would cause the database file to appear to be deleted
+** following reboot.
*/
void sqlite3Vacuum(Parse *pParse){
Vdbe *v = sqlite3GetVdbe(pParse);
@@ -111,7 +131,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
return SQLITE_ERROR;
}
- if( db->activeVdbeCnt>1 ){
+ if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
@@ -214,7 +234,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
rc = execExecSql(db, pzErrMsg,
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
- " AND rootpage>0"
+ " AND coalesce(rootpage,1)>0"
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db, pzErrMsg,
@@ -235,7 +255,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
"|| ' SELECT * FROM main.' || quote(name) || ';'"
"FROM main.sqlite_master "
"WHERE type = 'table' AND name!='sqlite_sequence' "
- " AND rootpage>0"
+ " AND coalesce(rootpage,1)>0"
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
diff --git a/src/vdbe.c b/src/vdbe.c
index f343e13..61adb9c 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -9,33 +9,8 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** The code in this file implements execution method of the
-** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c")
-** handles housekeeping details such as creating and deleting
-** VDBE instances. This file is solely interested in executing
-** the VDBE program.
-**
-** In the external interface, an "sqlite3_stmt*" is an opaque pointer
-** to a VDBE.
-**
-** The SQL parser generates a program which is then executed by
-** 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 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()
-** function which does the work of interpreting a VDBE program.
-** But other routines are also provided to help in building up
-** a program instruction by instruction.
+** The code in this file implements the function that runs the
+** bytecode of a prepared statement.
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files. The formatting
@@ -49,7 +24,11 @@
/*
** Invoke this macro on memory cells just prior to changing the
** value of the cell. This macro verifies that shallow copies are
-** not misused.
+** not misused. A shallow copy of a string or blob just copies a
+** pointer to the string or blob, not the content. If the original
+** is changed while the copy is still in use, the string or blob might
+** be changed out from under the copy. This macro verifies that nothing
+** like that ever happens.
*/
#ifdef SQLITE_DEBUG
# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M)
@@ -108,7 +87,7 @@ static void updateMaxBlobsize(Mem *p){
#endif
/*
-** The next global variable is incremented each type the OP_Found opcode
+** The next global variable is incremented each time the OP_Found opcode
** is executed. This is used to test whether or not the foreign key
** operation implemented using OP_FkIsZero is working. This variable
** has no function other than to help verify the correct operation of the
@@ -129,6 +108,40 @@ int sqlite3_found_count = 0;
#endif
/*
+** Invoke the VDBE coverage callback, if that callback is defined. This
+** feature is used for test suite validation only and does not appear an
+** production builds.
+**
+** M is an integer, 2 or 3, that indices how many different ways the
+** branch can go. It is usually 2. "I" is the direction the branch
+** goes. 0 means falls through. 1 means branch is taken. 2 means the
+** second alternative branch is taken.
+**
+** iSrcLine is the source code line (from the __LINE__ macro) that
+** generated the VDBE instruction. This instrumentation assumes that all
+** source code is in a single file (the amalgamation). Special values 1
+** and 2 for the iSrcLine parameter mean that this particular branch is
+** always taken or never taken, respectively.
+*/
+#if !defined(SQLITE_VDBE_COVERAGE)
+# define VdbeBranchTaken(I,M)
+#else
+# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M)
+ static void vdbeTakeBranch(int iSrcLine, u8 I, u8 M){
+ if( iSrcLine<=2 && ALWAYS(iSrcLine>0) ){
+ M = iSrcLine;
+ /* Assert the truth of VdbeCoverageAlwaysTaken() and
+ ** VdbeCoverageNeverTaken() */
+ assert( (M & I)==I );
+ }else{
+ if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
+ sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg,
+ iSrcLine,I,M);
+ }
+ }
+#endif
+
+/*
** Convert the given register into a string if it isn't one
** already. Return non-zero if a malloc() fails.
*/
@@ -145,38 +158,14 @@ int sqlite3_found_count = 0;
**
** This routine converts an ephemeral string into a dynamically allocated
** string that the register itself controls. In other words, it
-** converts an MEM_Ephem string into an MEM_Dyn string.
+** converts an MEM_Ephem string into a string with P.z==P.zMalloc.
*/
#define Deephemeralize(P) \
if( ((P)->flags&MEM_Ephem)!=0 \
&& sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
-# define isSorter(x) ((x)->pSorter!=0)
-
-/*
-** 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.
-** This routine sets the pMem->type variable used by the sqlite3_value_*()
-** routines.
-*/
-void sqlite3VdbeMemStoreType(Mem *pMem){
- int flags = pMem->flags;
- if( flags & MEM_Null ){
- pMem->type = SQLITE_NULL;
- }
- else if( flags & MEM_Int ){
- pMem->type = SQLITE_INTEGER;
- }
- else if( flags & MEM_Real ){
- pMem->type = SQLITE_FLOAT;
- }
- else if( flags & MEM_Str ){
- pMem->type = SQLITE_TEXT;
- }else{
- pMem->type = SQLITE_BLOB;
- }
-}
+#define isSorter(x) ((x)->pSorter!=0)
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
@@ -212,9 +201,8 @@ static VdbeCursor *allocateCursor(
int nByte;
VdbeCursor *pCx = 0;
nByte =
- ROUND8(sizeof(VdbeCursor)) +
- (isBtreeCursor?sqlite3BtreeCursorSize():0) +
- 2*nField*sizeof(u32);
+ ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
+ (isBtreeCursor?sqlite3BtreeCursorSize():0);
assert( iCur<p->nCursor );
if( p->apCsr[iCur] ){
@@ -226,12 +214,9 @@ static VdbeCursor *allocateCursor(
memset(pCx, 0, sizeof(VdbeCursor));
pCx->iDb = iDb;
pCx->nField = nField;
- if( nField ){
- pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))];
- }
if( isBtreeCursor ){
pCx->pCursor = (BtCursor*)
- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)];
+ &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
sqlite3BtreeCursorZero(pCx->pCursor);
}
}
@@ -245,21 +230,21 @@ static VdbeCursor *allocateCursor(
** look like a number, leave it alone.
*/
static void applyNumericAffinity(Mem *pRec){
- if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
- double rValue;
- i64 iValue;
- u8 enc = pRec->enc;
- if( (pRec->flags&MEM_Str)==0 ) return;
- if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
- if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
- pRec->u.i = iValue;
- pRec->flags |= MEM_Int;
- }else{
- pRec->r = rValue;
- pRec->flags |= MEM_Real;
- }
+ double rValue;
+ i64 iValue;
+ u8 enc = pRec->enc;
+ if( (pRec->flags&MEM_Str)==0 ) return;
+ if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
+ if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
+ pRec->u.i = iValue;
+ pRec->flags |= MEM_Int;
+ }else{
+ pRec->r = rValue;
+ pRec->flags |= MEM_Real;
}
}
+#define ApplyNumericAffinity(X) \
+ if(((X)->flags&(MEM_Real|MEM_Int))==0){applyNumericAffinity(X);}
/*
** Processing is determine by the affinity parameter:
@@ -296,7 +281,7 @@ static void applyAffinity(
}else if( affinity!=SQLITE_AFF_NONE ){
assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
|| affinity==SQLITE_AFF_NUMERIC );
- applyNumericAffinity(pRec);
+ ApplyNumericAffinity(pRec);
if( pRec->flags & MEM_Real ){
sqlite3VdbeIntegerAffinity(pRec);
}
@@ -310,12 +295,13 @@ static void applyAffinity(
** loss of information and return the revised type of the argument.
*/
int sqlite3_value_numeric_type(sqlite3_value *pVal){
- Mem *pMem = (Mem*)pVal;
- if( pMem->type==SQLITE_TEXT ){
+ int eType = sqlite3_value_type(pVal);
+ if( eType==SQLITE_TEXT ){
+ Mem *pMem = (Mem*)pVal;
applyNumericAffinity(pMem);
- sqlite3VdbeMemStoreType(pMem);
+ eType = sqlite3_value_type(pVal);
}
- return pMem->type;
+ return eType;
}
/*
@@ -330,6 +316,29 @@ void sqlite3ValueApplyAffinity(
applyAffinity((Mem *)pVal, affinity, enc);
}
+/*
+** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
+** none.
+**
+** Unlike applyNumericAffinity(), this routine does not modify pMem->flags.
+** But it does set pMem->r and pMem->u.i appropriately.
+*/
+static u16 numericType(Mem *pMem){
+ if( pMem->flags & (MEM_Int|MEM_Real) ){
+ return pMem->flags & (MEM_Int|MEM_Real);
+ }
+ if( pMem->flags & (MEM_Str|MEM_Blob) ){
+ if( sqlite3AtoF(pMem->z, &pMem->r, pMem->n, pMem->enc)==0 ){
+ return 0;
+ }
+ if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==SQLITE_OK ){
+ return MEM_Int;
+ }
+ return MEM_Real;
+ }
+ return 0;
+}
+
#ifdef SQLITE_DEBUG
/*
** Write a nice string representation of the contents of cell pMem
@@ -417,37 +426,36 @@ void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
/*
** Print the value of a register for tracing purposes:
*/
-static void memTracePrint(FILE *out, Mem *p){
- if( p->flags & MEM_Invalid ){
- fprintf(out, " undefined");
+static void memTracePrint(Mem *p){
+ if( p->flags & MEM_Undefined ){
+ printf(" undefined");
}else if( p->flags & MEM_Null ){
- fprintf(out, " NULL");
+ printf(" NULL");
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
- fprintf(out, " si:%lld", p->u.i);
+ printf(" si:%lld", p->u.i);
}else if( p->flags & MEM_Int ){
- fprintf(out, " i:%lld", p->u.i);
+ printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- fprintf(out, " r:%g", p->r);
+ printf(" r:%g", p->r);
#endif
}else if( p->flags & MEM_RowSet ){
- fprintf(out, " (rowset)");
+ printf(" (rowset)");
}else{
char zBuf[200];
sqlite3VdbeMemPrettyPrint(p, zBuf);
- fprintf(out, " ");
- fprintf(out, "%s", zBuf);
+ printf(" %s", zBuf);
}
}
-static void registerTrace(FILE *out, int iReg, Mem *p){
- fprintf(out, "REG[%d] = ", iReg);
- memTracePrint(out, p);
- fprintf(out, "\n");
+static void registerTrace(int iReg, Mem *p){
+ printf("REG[%d] = ", iReg);
+ memTracePrint(p);
+ printf("\n");
}
#endif
#ifdef SQLITE_DEBUG
-# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M)
+# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
#else
# define REGISTER_TRACE(R,M)
#endif
@@ -463,20 +471,6 @@ static void registerTrace(FILE *out, int iReg, Mem *p){
#endif
-/*
-** The CHECK_FOR_INTERRUPT macro defined here looks to see if the
-** sqlite3_interrupt() routine has been called. If it has been, then
-** processing of the VDBE program is interrupted.
-**
-** This macro added to every instruction that does a jump in order to
-** implement a loop. This test used to be on every single instruction,
-** but that meant we more testing than we needed. By only testing the
-** flag on jump instructions, we get a (small) speed improvement.
-*/
-#define CHECK_FOR_INTERRUPT \
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
-
-
#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
@@ -497,50 +491,10 @@ static int checkSavepointCount(sqlite3 *db){
}
#endif
-/*
-** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
-** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
-** in memory obtained from sqlite3DbMalloc).
-*/
-static void importVtabErrMsg(Vdbe *p, sqlite3_vtab *pVtab){
- sqlite3 *db = p->db;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
- pVtab->zErrMsg = 0;
-}
-
/*
-** Execute as much of a VDBE program as we can then return.
-**
-** sqlite3VdbeMakeReady() must be called before this routine in order to
-** close the program with a final OP_Halt and to set up the callbacks
-** and the error message pointer.
-**
-** Whenever a row or result data is available, this routine will either
-** invoke the result callback (if there is one) or return with
-** SQLITE_ROW.
-**
-** If an attempt is made to open a locked database, then this routine
-** will either invoke the busy callback (if there is one) or it will
-** return SQLITE_BUSY.
-**
-** If an error occurs, an error message is written to memory obtained
-** 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
-** immediately. There will be no error message but the p->rc field is
-** set to SQLITE_ABORT and this routine will return SQLITE_ERROR.
-**
-** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this
-** routine to return SQLITE_ERROR.
-**
-** Other fatal errors return SQLITE_ERROR.
-**
-** After this routine has finished, sqlite3VdbeFinalize() should be
-** used to clean up the mess that was left behind.
+** Execute as much of a VDBE program as we can.
+** This is the core of sqlite3_step().
*/
int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
@@ -552,21 +506,20 @@ int sqlite3VdbeExec(
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
+ int iCompare = 0; /* Result of last OP_Compare operation */
+ unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- int checkProgress; /* True if progress callbacks are enabled */
- int nProgressOps = 0; /* Opcodes executed since progress callback. */
+ unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
#endif
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
- int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permutation of columns for OP_Compare */
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
#ifdef VDBE_PROFILE
u64 start; /* CPU clock count at start of opcode */
- int origPc; /* Program counter at start of opcode */
#endif
/*** INSERT STACK UNION HERE ***/
@@ -578,24 +531,49 @@ int sqlite3VdbeExec(
goto no_mem;
}
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
+ p->iCurrentTime = 0;
assert( p->explain==0 );
p->pResultSet = 0;
db->busyHandler.nBusy = 0;
- CHECK_FOR_INTERRUPT;
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- checkProgress = db->xProgress!=0;
+ if( db->xProgress ){
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = (unsigned)p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ if( nProgressLimit==0 ){
+ nProgressLimit = db->nProgressOps;
+ }else{
+ nProgressLimit %= (unsigned)db->nProgressOps;
+ }
+ }
#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
- if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){
+ if( p->pc==0
+ && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
+ ){
int i;
- printf("VDBE Program Listing:\n");
+ int once = 1;
sqlite3VdbePrintSql(p);
- for(i=0; i<p->nOp; i++){
- sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ if( p->db->flags & SQLITE_VdbeListing ){
+ printf("VDBE Program Listing:\n");
+ for(i=0; i<p->nOp; i++){
+ sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ }
+ }
+ if( p->db->flags & SQLITE_VdbeEQP ){
+ for(i=0; i<p->nOp; i++){
+ if( aOp[i].opcode==OP_Explain ){
+ if( once ) printf("VDBE Query Plan:\n");
+ printf("%s\n", aOp[i].p4.z);
+ once = 0;
+ }
+ }
}
+ if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n");
}
sqlite3EndBenignMalloc();
#endif
@@ -603,20 +581,16 @@ int sqlite3VdbeExec(
assert( pc>=0 && pc<p->nOp );
if( db->mallocFailed ) goto no_mem;
#ifdef VDBE_PROFILE
- origPc = pc;
start = sqlite3Hwtime();
#endif
+ nVmStep++;
pOp = &aOp[pc];
/* Only allow tracing if SQLITE_DEBUG is defined.
*/
#ifdef SQLITE_DEBUG
- if( p->trace ){
- if( pc==0 ){
- printf("VDBE Execution Trace:\n");
- sqlite3VdbePrintSql(p);
- }
- sqlite3VdbePrintOp(p->trace, pc, pOp);
+ if( db->flags & SQLITE_VdbeTrace ){
+ sqlite3VdbePrintOp(stdout, pc, pOp);
}
#endif
@@ -633,27 +607,6 @@ int sqlite3VdbeExec(
}
#endif
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Call the progress callback if it is configured and the required number
- ** of VDBE ops have been executed (either since this invocation of
- ** sqlite3VdbeExec() or since last time the progress callback was called).
- ** If the progress callback returns non-zero, exit the virtual machine with
- ** a return code SQLITE_ABORT.
- */
- if( checkProgress ){
- if( db->nProgressOps==nProgressOps ){
- int prc;
- prc = db->xProgress(db->pProgressArg);
- if( prc!=0 ){
- rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
- }
- nProgressOps = 0;
- }
- nProgressOps++;
- }
-#endif
-
/* On any opcode with the "out2-prerelease" tag, free any
** external allocations out of mem[p2] and set mem[p2] to be
** an undefined integer. Opcodes will either fill in the integer
@@ -662,7 +615,7 @@ int sqlite3VdbeExec(
assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] );
if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
VdbeMemRelease(pOut);
@@ -673,30 +626,33 @@ int sqlite3VdbeExec(
#ifdef SQLITE_DEBUG
if( (pOp->opflags & OPFLG_IN1)!=0 ){
assert( pOp->p1>0 );
- assert( pOp->p1<=p->nMem );
+ assert( pOp->p1<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p1]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) );
REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
}
if( (pOp->opflags & OPFLG_IN2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p2]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) );
REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_IN3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
assert( memIsValid(&aMem[pOp->p3]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) );
REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
}
if( (pOp->opflags & OPFLG_OUT2)!=0 ){
assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
+ assert( pOp->p2<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p2]);
}
if( (pOp->opflags & OPFLG_OUT3)!=0 ){
assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
memAboutToChange(p, &aMem[pOp->p3]);
}
#endif
@@ -744,10 +700,44 @@ int sqlite3VdbeExec(
** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
+**
+** The P1 parameter is not actually used by this opcode. However, it
+** is sometimes set to 1 instead of 0 as a hint to the command-line shell
+** that this Goto is the bottom of a loop and that the lines from P2 down
+** to the current line should be indented for EXPLAIN output.
*/
case OP_Goto: { /* jump */
- CHECK_FOR_INTERRUPT;
pc = pOp->p2 - 1;
+
+ /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev,
+ ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon
+ ** completion. Check to see if sqlite3_interrupt() has been called
+ ** or if the progress callback needs to be invoked.
+ **
+ ** This code uses unstructured "goto" statements and does not look clean.
+ ** But that is not due to sloppy coding habits. The code is written this
+ ** way for performance, to avoid having to run the interrupt and progress
+ ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
+ ** faster according to "valgrind --tool=cachegrind" */
+check_for_interrupt:
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Call the progress callback if it is configured and the required number
+ ** of VDBE ops have been executed (either since this invocation of
+ ** sqlite3VdbeExec() or since last time the progress callback was called).
+ ** If the progress callback returns non-zero, exit the virtual machine with
+ ** a return code SQLITE_ABORT.
+ */
+ if( db->xProgress!=0 && nVmStep>=nProgressLimit ){
+ assert( db->nProgressOps!=0 );
+ nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ if( db->xProgress(db->pProgressArg) ){
+ rc = SQLITE_INTERRUPT;
+ goto vdbe_error_halt;
+ }
+ }
+#endif
+
break;
}
@@ -757,9 +747,9 @@ case OP_Goto: { /* jump */
** and then jump to address P2.
*/
case OP_Gosub: { /* jump */
- assert( pOp->p1>0 && pOp->p1<=p->nMem );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
memAboutToChange(p, pIn1);
pIn1->flags = MEM_Int;
pIn1->u.i = pc;
@@ -770,23 +760,78 @@ case OP_Gosub: { /* jump */
/* Opcode: Return P1 * * * *
**
-** Jump to the next instruction after the address in register P1.
+** Jump to the next instruction after the address in register P1. After
+** the jump, register P1 becomes undefined.
*/
case OP_Return: { /* in1 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags & MEM_Int );
+ assert( pIn1->flags==MEM_Int );
pc = (int)pIn1->u.i;
+ pIn1->flags = MEM_Undefined;
+ break;
+}
+
+/* Opcode: InitCoroutine P1 P2 P3 * *
+**
+** Set up register P1 so that it will Yield to the coroutine
+** located at address P3.
+**
+** If P2!=0 then the coroutine implementation immediately follows
+** this opcode. So jump over the coroutine implementation to
+** address P2.
+**
+** See also: EndCoroutine
+*/
+case OP_InitCoroutine: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+ assert( pOp->p3>=0 && pOp->p3<p->nOp );
+ pOut = &aMem[pOp->p1];
+ assert( !VdbeMemDynamic(pOut) );
+ pOut->u.i = pOp->p3 - 1;
+ pOut->flags = MEM_Int;
+ if( pOp->p2 ) pc = pOp->p2 - 1;
break;
}
-/* Opcode: Yield P1 * * * *
+/* Opcode: EndCoroutine P1 * * * *
**
-** Swap the program counter with the value in register P1.
+** The instruction at the address in register P1 is a Yield.
+** Jump to the P2 parameter of that Yield.
+** After the jump, register P1 becomes undefined.
+**
+** See also: InitCoroutine
*/
-case OP_Yield: { /* in1 */
+case OP_EndCoroutine: { /* in1 */
+ VdbeOp *pCaller;
+ pIn1 = &aMem[pOp->p1];
+ assert( pIn1->flags==MEM_Int );
+ assert( pIn1->u.i>=0 && pIn1->u.i<p->nOp );
+ pCaller = &aOp[pIn1->u.i];
+ assert( pCaller->opcode==OP_Yield );
+ assert( pCaller->p2>=0 && pCaller->p2<p->nOp );
+ pc = pCaller->p2 - 1;
+ pIn1->flags = MEM_Undefined;
+ break;
+}
+
+/* Opcode: Yield P1 P2 * * *
+**
+** Swap the program counter with the value in register P1. This
+** has the effect of yielding to a coroutine.
+**
+** If the coroutine that is launched by this instruction ends with
+** Yield or Return then continue to the next instruction. But if
+** the coroutine launched by this instruction ends with
+** EndCoroutine, then jump to P2 rather than continuing with the
+** next instruction.
+**
+** See also: InitCoroutine
+*/
+case OP_Yield: { /* in1, jump */
int pcDest;
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
pIn1->flags = MEM_Int;
pcDest = (int)pIn1->u.i;
pIn1->u.i = pc;
@@ -795,11 +840,13 @@ case OP_Yield: { /* in1 */
break;
}
-/* Opcode: HaltIfNull P1 P2 P3 P4 *
+/* Opcode: HaltIfNull P1 P2 P3 P4 P5
+** Synopsis: if r[P3]=null halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
+** The P5 parameter should be 1.
*/
case OP_HaltIfNull: { /* in3 */
pIn3 = &aMem[pOp->p3];
@@ -807,7 +854,7 @@ case OP_HaltIfNull: { /* in3 */
/* Fall through into OP_Halt */
}
-/* Opcode: Halt P1 P2 * P4 *
+/* Opcode: Halt P1 P2 * P4 P5
**
** Exit immediately. All open cursors, etc are closed
** automatically.
@@ -822,11 +869,25 @@ case OP_HaltIfNull: { /* in3 */
**
** If P4 is not null then it is an error message string.
**
+** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
+**
+** 0: (no change)
+** 1: NOT NULL contraint failed: P4
+** 2: UNIQUE constraint failed: P4
+** 3: CHECK constraint failed: P4
+** 4: FOREIGN KEY constraint failed: P4
+**
+** If P5 is not zero and P4 is NULL, then everything after the ":" is
+** omitted.
+**
** 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: {
+ const char *zType;
+ const char *zLogFmt;
+
if( pOp->p1==SQLITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
VdbeFrame *pFrame = p->pFrame;
@@ -847,18 +908,33 @@ case OP_Halt: {
aMem = p->aMem;
break;
}
-
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
p->pc = pc;
- if( pOp->p4.z ){
- assert( p->rc!=SQLITE_OK );
- sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
- }else if( p->rc ){
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
+ if( p->rc ){
+ if( pOp->p5 ){
+ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
+ "FOREIGN KEY" };
+ assert( pOp->p5>=1 && pOp->p5<=4 );
+ testcase( pOp->p5==1 );
+ testcase( pOp->p5==2 );
+ testcase( pOp->p5==3 );
+ testcase( pOp->p5==4 );
+ zType = azType[pOp->p5-1];
+ }else{
+ zType = 0;
+ }
+ assert( zType!=0 || pOp->p4.z!=0 );
+ zLogFmt = "abort at %d in [%s]: %s";
+ if( zType && pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s",
+ zType, pOp->p4.z);
+ }else if( pOp->p4.z ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
+ }else{
+ sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType);
+ }
+ sqlite3_log(pOp->p1, zLogFmt, pc, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
@@ -866,13 +942,14 @@ case OP_Halt: {
p->rc = rc = SQLITE_BUSY;
}else{
assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT );
- assert( rc==SQLITE_OK || db->nDeferredCons>0 );
+ assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 );
rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
}
goto vdbe_return;
}
/* Opcode: Integer P1 P2 * * *
+** Synopsis: r[P2]=P1
**
** The 32-bit integer value P1 is written into register P2.
*/
@@ -882,6 +959,7 @@ case OP_Integer: { /* out2-prerelease */
}
/* Opcode: Int64 * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit integer value.
** Write that value into register P2.
@@ -894,6 +972,7 @@ case OP_Int64: { /* out2-prerelease */
#ifndef SQLITE_OMIT_FLOATING_POINT
/* Opcode: Real * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit floating point value.
** Write that value into register P2.
@@ -907,9 +986,12 @@ case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
#endif
/* Opcode: String8 * P2 * P4 *
+** Synopsis: r[P2]='P4'
**
** 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.
+** into a String before it is executed for the first time. During
+** this transformation, the length of string P4 is computed and stored
+** as the P1 parameter.
*/
case OP_String8: { /* same as TK_STRING, out2-prerelease */
assert( pOp->p4.z!=0 );
@@ -922,10 +1004,9 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
if( rc==SQLITE_TOOBIG ) goto too_big;
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
assert( pOut->zMalloc==pOut->z );
- assert( pOut->flags & MEM_Dyn );
+ assert( VdbeMemDynamic(pOut)==0 );
pOut->zMalloc = 0;
pOut->flags |= MEM_Static;
- pOut->flags &= ~MEM_Dyn;
if( pOp->p4type==P4_DYNAMIC ){
sqlite3DbFree(db, pOp->p4.z);
}
@@ -941,6 +1022,7 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
}
/* Opcode: String P1 P2 * P4 *
+** Synopsis: r[P2]='P4' (len=P1)
**
** The string value P4 of length P1 (bytes) is stored in register P2.
*/
@@ -955,6 +1037,7 @@ case OP_String: { /* out2-prerelease */
}
/* Opcode: Null P1 P2 P3 * *
+** Synopsis: r[P2..P3]=NULL
**
** Write a NULL into registers P2. If P3 greater than P2, then also write
** NULL into register P3 and every register in between P2 and P3. If P3
@@ -969,7 +1052,7 @@ case OP_Null: { /* out2-prerelease */
int cnt;
u16 nullFlag;
cnt = pOp->p3-pOp->p2;
- assert( pOp->p3<=p->nMem );
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
while( cnt>0 ){
pOut++;
@@ -981,8 +1064,23 @@ case OP_Null: { /* out2-prerelease */
break;
}
+/* Opcode: SoftNull P1 * * * *
+** Synopsis: r[P1]=NULL
+**
+** Set register P1 to have the value NULL as seen by the OP_MakeRecord
+** instruction, but do not free any string or blob memory associated with
+** the register, so that if the value was a string or blob that was
+** previously copied using OP_SCopy, the copies will continue to be valid.
+*/
+case OP_SoftNull: {
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
+ pOut = &aMem[pOp->p1];
+ pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
+ break;
+}
-/* Opcode: Blob P1 P2 * P4
+/* Opcode: Blob P1 P2 * P4 *
+** Synopsis: r[P2]=P4 (len=P1)
**
** P4 points to a blob of data P1 bytes long. Store this
** blob in register P2.
@@ -996,10 +1094,11 @@ case OP_Blob: { /* out2-prerelease */
}
/* Opcode: Variable P1 P2 * P4 *
+** Synopsis: r[P2]=parameter(P1,P4)
**
** Transfer the values of bound parameter P1 into register P2
**
-** If the parameter is named, then its name appears in P4 and P3==1.
+** If the parameter is named, then its name appears in P4.
** The P4 value is used by sqlite3_bind_parameter_name().
*/
case OP_Variable: { /* out2-prerelease */
@@ -1017,11 +1116,13 @@ case OP_Variable: { /* out2-prerelease */
}
/* Opcode: Move P1 P2 P3 * *
+** Synopsis: r[P2@P3]=r[P1@P3]
**
-** Move the values in register P1..P1+P3 over into
-** registers P2..P2+P3. Registers P1..P1+P3 are
+** Move the P3 values in register P1..P1+P3-1 over into
+** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
** left holding a NULL. It is an error for register ranges
-** P1..P1+P3 and P2..P2+P3 to overlap.
+** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error
+** for P3 to be less than 1.
*/
case OP_Move: {
char *zMalloc; /* Holding variable for allocated memory */
@@ -1029,7 +1130,7 @@ case OP_Move: {
int p1; /* Register to copy from */
int p2; /* Register to copy to */
- n = pOp->p3 + 1;
+ n = pOp->p3;
p1 = pOp->p1;
p2 = pOp->p2;
assert( n>0 && p1>0 && p2>0 );
@@ -1037,28 +1138,31 @@ case OP_Move: {
pIn1 = &aMem[p1];
pOut = &aMem[p2];
- while( n-- ){
- assert( pOut<=&aMem[p->nMem] );
- assert( pIn1<=&aMem[p->nMem] );
+ do{
+ assert( pOut<=&aMem[(p->nMem-p->nCursor)] );
+ assert( pIn1<=&aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
+ VdbeMemRelease(pOut);
zMalloc = pOut->zMalloc;
- pOut->zMalloc = 0;
- sqlite3VdbeMemMove(pOut, pIn1);
+ memcpy(pOut, pIn1, sizeof(Mem));
#ifdef SQLITE_DEBUG
if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<&aMem[p1+pOp->p3] ){
pOut->pScopyFrom += p1 - pOp->p2;
}
#endif
+ pIn1->flags = MEM_Undefined;
+ pIn1->xDel = 0;
pIn1->zMalloc = zMalloc;
REGISTER_TRACE(p2++, pOut);
pIn1++;
pOut++;
- }
+ }while( --n );
break;
}
/* Opcode: Copy P1 P2 P3 * *
+** Synopsis: r[P2@P3+1]=r[P1@P3+1]
**
** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
**
@@ -1087,6 +1191,7 @@ case OP_Copy: {
}
/* Opcode: SCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
**
** Make a shallow copy of register P1 into register P2.
**
@@ -1098,7 +1203,7 @@ case OP_Copy: {
** during the lifetime of the copy. Use OP_Copy to make a complete
** copy.
*/
-case OP_SCopy: { /* in1, out2 */
+case OP_SCopy: { /* out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
@@ -1106,24 +1211,36 @@ case OP_SCopy: { /* in1, out2 */
#ifdef SQLITE_DEBUG
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
#endif
- REGISTER_TRACE(pOp->p2, pOut);
break;
}
/* Opcode: ResultRow P1 P2 * * *
+** Synopsis: output=r[P1@P2]
**
** 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.
+** structure to provide access to the r(P1)..r(P1+P2-1) values as
+** the result row.
*/
case OP_ResultRow: {
Mem *pMem;
int i;
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
- assert( pOp->p1+pOp->p2<=p->nMem+1 );
+ assert( pOp->p1+pOp->p2<=(p->nMem-p->nCursor)+1 );
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Run the progress counter just before returning.
+ */
+ if( db->xProgress!=0
+ && nVmStep>=nProgressLimit
+ && db->xProgress(db->pProgressArg)!=0
+ ){
+ rc = SQLITE_INTERRUPT;
+ goto vdbe_error_halt;
+ }
+#endif
/* If this statement has violated immediate foreign key constraints, do
** not return the number of rows modified. And do not RELEASE the statement
@@ -1169,7 +1286,6 @@ case OP_ResultRow: {
assert( (pMem[i].flags & MEM_Ephem)==0
|| (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
sqlite3VdbeMemNulTerminate(&pMem[i]);
- sqlite3VdbeMemStoreType(&pMem[i]);
REGISTER_TRACE(pOp->p1+i, &pMem[i]);
}
if( db->mallocFailed ) goto no_mem;
@@ -1182,6 +1298,7 @@ case OP_ResultRow: {
}
/* Opcode: Concat P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]+r[P1]
**
** Add the text in register P1 onto the end of the text in
** register P2 and store the result in register P3.
@@ -1211,15 +1328,15 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- MemSetTypeFlag(pOut, MEM_Str);
if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
goto no_mem;
}
+ MemSetTypeFlag(pOut, MEM_Str);
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]=0;
pOut->z[nByte+1] = 0;
pOut->flags |= MEM_Term;
pOut->n = (int)nByte;
@@ -1229,12 +1346,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
}
/* Opcode: Add P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]+r[P2]
**
** 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 P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]*r[P2]
**
**
** Multiply the value in register P1 by the value in register P2
@@ -1242,12 +1361,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** If either input is NULL, the result is NULL.
*/
/* Opcode: Subtract P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]-r[P1]
**
** 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 P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
** and store the result in register P3 (P3=P2/P1). If the value in
@@ -1255,10 +1376,11 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]%r[P1]
**
-** 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.
+** Compute the remainder after integer register P2 is divided by
+** register P1 and store the result in register P3.
+** If the value in register P1 is zero the result is NULL.
** If either operand is NULL, the result is NULL.
*/
case OP_Add: /* same as TK_PLUS, in1, in2, out3 */
@@ -1267,20 +1389,22 @@ 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 */
char bIntint; /* Started out as two integer operands */
- int flags; /* Combined MEM_* flags from both inputs */
+ u16 flags; /* Combined MEM_* flags from both inputs */
+ u16 type1; /* Numeric type of left operand */
+ u16 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
i64 iB; /* Integer value of right operand */
double rA; /* Real value of left operand */
double rB; /* Real value of right operand */
pIn1 = &aMem[pOp->p1];
- applyNumericAffinity(pIn1);
+ type1 = numericType(pIn1);
pIn2 = &aMem[pOp->p2];
- applyNumericAffinity(pIn2);
+ type2 = numericType(pIn2);
pOut = &aMem[pOp->p3];
flags = pIn1->flags | pIn2->flags;
if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
- if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){
+ if( (type1 & type2 & MEM_Int)!=0 ){
iA = pIn1->u.i;
iB = pIn2->u.i;
bIntint = 1;
@@ -1336,7 +1460,7 @@ fp_math:
}
pOut->r = rB;
MemSetTypeFlag(pOut, MEM_Real);
- if( (flags & MEM_Real)==0 && !bIntint ){
+ if( ((type1|type2)&MEM_Real)==0 && !bIntint ){
sqlite3VdbeIntegerAffinity(pOut);
}
#endif
@@ -1372,6 +1496,7 @@ case OP_CollSeq: {
}
/* Opcode: Function P1 P2 P3 P4 P5
+** Synopsis: r[P3]=func(r[P2@P5])
**
** Invoke a user function (P4 is a pointer to a Function structure that
** defines the function) with P5 arguments taken from register P2 and
@@ -1397,44 +1522,37 @@ case OP_Function: {
n = pOp->p5;
apVal = p->apArg;
assert( apVal || n==0 );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
- assert( n==0 || (pOp->p2>0 && pOp->p2+n<=p->nMem+1) );
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem-p->nCursor)+1) );
assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
pArg = &aMem[pOp->p2];
for(i=0; i<n; i++, pArg++){
assert( memIsValid(pArg) );
apVal[i] = pArg;
Deephemeralize(pArg);
- sqlite3VdbeMemStoreType(pArg);
REGISTER_TRACE(pOp->p2+i, pArg);
}
- 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->p4.pVdbeFunc;
- ctx.pFunc = ctx.pVdbeFunc->pFunc;
- }
-
- ctx.s.flags = MEM_Null;
- ctx.s.db = db;
- ctx.s.xDel = 0;
- ctx.s.zMalloc = 0;
+ assert( pOp->p4type==P4_FUNCDEF );
+ ctx.pFunc = pOp->p4.pFunc;
+ ctx.iOp = pc;
+ ctx.pVdbe = p;
/* 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);
+ memcpy(&ctx.s, pOut, sizeof(Mem));
+ pOut->flags = MEM_Null;
+ pOut->xDel = 0;
+ pOut->zMalloc = 0;
MemSetTypeFlag(&ctx.s, MEM_Null);
- ctx.isError = 0;
- if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ ctx.fErrorOrAux = 0;
+ if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
@@ -1444,15 +1562,6 @@ case OP_Function: {
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
lastRowid = db->lastRowid;
- /* 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->p4.pVdbeFunc = ctx.pVdbeFunc;
- pOp->p4type = P4_VDBEFUNC;
- }
-
if( db->mallocFailed ){
/* Even though a malloc() has failed, the implementation of the
** user function may have called an sqlite3_result_XXX() function
@@ -1464,14 +1573,18 @@ case OP_Function: {
}
/* If the function returned an error, throw an exception */
- if( ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
- rc = ctx.isError;
+ if( ctx.fErrorOrAux ){
+ if( ctx.isError ){
+ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
+ rc = ctx.isError;
+ }
+ sqlite3VdbeDeleteAuxData(p, pc, pOp->p1);
}
/* Copy the result of the function into register P3 */
sqlite3VdbeChangeEncoding(&ctx.s, encoding);
- sqlite3VdbeMemMove(pOut, &ctx.s);
+ assert( pOut->flags==MEM_Null );
+ memcpy(pOut, &ctx.s, sizeof(Mem));
if( sqlite3VdbeMemTooBig(pOut) ){
goto too_big;
}
@@ -1490,18 +1603,21 @@ case OP_Function: {
}
/* Opcode: BitAnd P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]&r[P2]
**
** 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 P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]|r[P2]
**
** 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 P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]<<r[P1]
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in register P1.
@@ -1509,6 +1625,7 @@ case OP_Function: {
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftRight P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]>>r[P1]
**
** Shift the integer value in register P2 to the right by the
** number of bits specified by the integer in register P1.
@@ -1568,6 +1685,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
}
/* Opcode: AddImm P1 P2 * * *
+** Synopsis: r[P1]=r[P1]+P2
**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
@@ -1591,17 +1709,20 @@ case OP_AddImm: { /* in1 */
*/
case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
- 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{
- pc = pOp->p2 - 1;
+ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
+ VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
+ if( (pIn1->flags & MEM_Int)==0 ){
+ if( pOp->p2==0 ){
+ rc = SQLITE_MISMATCH;
+ goto abort_due_to_error;
+ }else{
+ pc = pOp->p2 - 1;
+ break;
+ }
}
- }else{
- MemSetTypeFlag(pIn1, MEM_Int);
}
+ MemSetTypeFlag(pIn1, MEM_Int);
break;
}
@@ -1629,7 +1750,7 @@ case OP_RealAffinity: { /* in1 */
**
** 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
+** equivalent of sprintf(). Blob values are unchanged and
** are afterwards simply interpreted as text.
**
** A NULL value is not changed by this routine. It remains NULL.
@@ -1726,6 +1847,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
#endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */
/* Opcode: Lt P1 P2 P3 P4 P5
+** Synopsis: if r[P1]<r[P3] goto P2
**
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
** jump to address P2.
@@ -1760,6 +1882,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** bit set.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
+** Synopsis: if r[P1]!=r[P3] goto P2
**
** 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
@@ -1772,6 +1895,7 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Eq P1 P2 P3 P4 P5
+** Synopsis: if r[P1]==r[P3] goto P2
**
** This works just like the Lt opcode except that the jump is taken if
** the operands in registers P1 and P3 are equal.
@@ -1784,18 +1908,21 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Le P1 P2 P3 P4 P5
+** Synopsis: if r[P1]<=r[P3] goto P2
**
** 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 P4 P5
+** Synopsis: if r[P1]>r[P3] goto P2
**
** 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 P4 P5
+** Synopsis: if r[P1]>=r[P3] goto P2
**
** 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
@@ -1825,6 +1952,7 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
*/
assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
assert( (flags1 & MEM_Cleared)==0 );
+ assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 );
if( (flags1&MEM_Null)!=0
&& (flags3&MEM_Null)!=0
&& (flags3&MEM_Cleared)==0
@@ -1842,8 +1970,11 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
pOut = &aMem[pOp->p2];
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
- }else if( pOp->p5 & SQLITE_JUMPIFNULL ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(2,3);
+ if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ pc = pOp->p2-1;
+ }
}
break;
}
@@ -1876,10 +2007,12 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
MemSetTypeFlag(pOut, MEM_Int);
pOut->u.i = res;
REGISTER_TRACE(pOp->p2, pOut);
- }else if( res ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ if( res ){
+ pc = pOp->p2-1;
+ }
}
-
/* Undo any changes made by applyAffinity() to the input registers. */
pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (flags1&MEM_TypeMask);
pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (flags3&MEM_TypeMask);
@@ -1903,6 +2036,7 @@ case OP_Permutation: {
}
/* Opcode: Compare P1 P2 P3 P4 P5
+** Synopsis: r[P1@P3] <-> r[P2@P3]
**
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
@@ -1942,11 +2076,11 @@ case OP_Compare: {
if( aPermute ){
int k, mx = 0;
for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
- assert( p1>0 && p1+mx<=p->nMem+1 );
- assert( p2>0 && p2+mx<=p->nMem+1 );
+ assert( p1>0 && p1+mx<=(p->nMem-p->nCursor)+1 );
+ assert( p2>0 && p2+mx<=(p->nMem-p->nCursor)+1 );
}else{
- assert( p1>0 && p1+n<=p->nMem+1 );
- assert( p2>0 && p2+n<=p->nMem+1 );
+ assert( p1>0 && p1+n<=(p->nMem-p->nCursor)+1 );
+ assert( p2>0 && p2+n<=(p->nMem-p->nCursor)+1 );
}
#endif /* SQLITE_DEBUG */
for(i=0; i<n; i++){
@@ -1976,16 +2110,17 @@ case OP_Compare: {
*/
case OP_Jump: { /* jump */
if( iCompare<0 ){
- pc = pOp->p1 - 1;
+ pc = pOp->p1 - 1; VdbeBranchTaken(0,3);
}else if( iCompare==0 ){
- pc = pOp->p2 - 1;
+ pc = pOp->p2 - 1; VdbeBranchTaken(1,3);
}else{
- pc = pOp->p3 - 1;
+ pc = pOp->p3 - 1; VdbeBranchTaken(2,3);
}
break;
}
/* Opcode: And P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] && r[P2])
**
** Take the logical AND of the values in registers P1 and P2 and
** write the result into register P3.
@@ -1995,6 +2130,7 @@ case OP_Jump: { /* jump */
** a NULL output.
*/
/* Opcode: Or P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] || r[P2])
**
** Take the logical OR of the values in register P1 and P2 and
** store the answer in register P3.
@@ -2038,6 +2174,7 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
}
/* Opcode: Not P1 P2 * * *
+** Synopsis: r[P2]= !r[P1]
**
** Interpret the value in register P1 as a boolean value. Store the
** boolean complement in register P2. If the value in register P1 is
@@ -2055,6 +2192,7 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */
}
/* Opcode: BitNot P1 P2 * * *
+** Synopsis: r[P1]= ~r[P1]
**
** Interpret the content of register P1 as an integer. Store the
** ones-complement of the P1 value into register P2. If P1 holds
@@ -2073,11 +2211,18 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
/* Opcode: Once P1 P2 * * *
**
-** Check if OP_Once flag P1 is set. If so, jump to instruction P2. Otherwise,
-** set the flag and fall through to the next instruction.
+** Check the "once" flag number P1. If it is set, jump to instruction P2.
+** Otherwise, set the flag and fall through to the next instruction.
+** In other words, this opcode causes all following opcodes up through P2
+** (but not including P2) to run just once and to be skipped on subsequent
+** times through the loop.
+**
+** All "once" flags are initially cleared whenever a prepared statement
+** first begins to run.
*/
case OP_Once: { /* jump */
assert( pOp->p1<p->nOnceFlag );
+ VdbeBranchTaken(p->aOnceFlag[pOp->p1]!=0, 2);
if( p->aOnceFlag[pOp->p1] ){
pc = pOp->p2-1;
}else{
@@ -2090,13 +2235,13 @@ case OP_Once: { /* jump */
**
** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
-** in P1 is NULL then take the jump if P3 is non-zero.
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is False. The value
** is considered false if it has a numeric value of zero. If the value
-** in P1 is NULL then take the jump if P3 is zero.
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
@@ -2112,6 +2257,7 @@ case OP_IfNot: { /* jump, in1 */
#endif
if( pOp->opcode==OP_IfNot ) c = !c;
}
+ VdbeBranchTaken(c!=0, 2);
if( c ){
pc = pOp->p2-1;
}
@@ -2119,11 +2265,13 @@ case OP_IfNot: { /* jump, in1 */
}
/* Opcode: IsNull P1 P2 * * *
+** Synopsis: if r[P1]==NULL goto P2
**
** Jump to P2 if the value in register P1 is NULL.
*/
case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2);
if( (pIn1->flags & MEM_Null)!=0 ){
pc = pOp->p2 - 1;
}
@@ -2131,11 +2279,13 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
}
/* Opcode: NotNull P1 P2 * * *
+** Synopsis: if r[P1]!=NULL goto P2
**
** Jump to P2 if the value in register P1 is not NULL.
*/
case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2);
if( (pIn1->flags & MEM_Null)==0 ){
pc = pOp->p2 - 1;
}
@@ -2143,6 +2293,7 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
}
/* Opcode: Column P1 P2 P3 P4 P5
+** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -2167,153 +2318,98 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** skipped for length() and all content loading can be skipped for typeof().
*/
case OP_Column: {
- u32 payloadSize; /* Number of bytes in the record */
i64 payloadSize64; /* Number of bytes in the record */
- int p1; /* P1 value of the opcode */
int p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
- char *zRec; /* Pointer to complete record-data */
BtCursor *pCrsr; /* The BTree cursor */
u32 *aType; /* aType[i] holds the numeric type of the i-th column */
u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int nField; /* number of fields in the record */
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 */
- u8 *zIdx; /* Index into header */
- u8 *zEndHdr; /* Pointer to first byte after the header */
+ const u8 *zData; /* Part of the record being decoded */
+ const u8 *zHdr; /* Next unparsed byte of the header */
+ const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset into the data */
u32 szField; /* Number of bytes in the content of a field */
- int szHdr; /* Size of the header size field at start of record */
- int avail; /* Number of bytes of available data */
+ u32 avail; /* Number of bytes of available data */
u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
-
- p1 = pOp->p1;
p2 = pOp->p2;
- pC = 0;
- memset(&sMem, 0, sizeof(sMem));
- assert( p1<p->nCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
- zRec = 0;
-
- /* This block sets the variable payloadSize to be the total number of
- ** bytes in the record.
- **
- ** zRec is set to be the complete text of the record if it is available.
- ** The complete record text is always available for pseudo-tables
- ** If the record is stored in a cursor, the complete record text
- ** might be available in the pC->aRow cache. Or it might not be.
- ** 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 VdbeCursor.nField element.
- */
- pC = p->apCsr[p1];
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+ assert( p2<pC->nField );
+ aType = pC->aType;
+ aOffset = aType + pC->nField;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- assert( pC->pVtabCursor==0 );
+ assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */
#endif
pCrsr = pC->pCursor;
- if( pCrsr!=0 ){
- /* The record is stored in a B-Tree */
- rc = sqlite3VdbeCursorMoveto(pC);
- if( rc ) goto abort_due_to_error;
- if( pC->nullRow ){
- payloadSize = 0;
- }else if( pC->cacheStatus==p->cacheCtr ){
- payloadSize = pC->payloadSize;
- zRec = (char*)pC->aRow;
- }else if( pC->isIndex ){
- assert( sqlite3BtreeCursorIsValid(pCrsr) );
- VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64);
- assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
- /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
- ** payload size, so it is impossible for payloadSize64 to be
- ** larger than 32 bits. */
- assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 );
- payloadSize = (u32)payloadSize64;
- }else{
- assert( sqlite3BtreeCursorIsValid(pCrsr) );
- VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &payloadSize);
- assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
- }
- }else if( ALWAYS(pC->pseudoTableReg>0) ){
- pReg = &aMem[pC->pseudoTableReg];
- if( pC->multiPseudo ){
- sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem);
- Deephemeralize(pDest);
- goto op_column_out;
- }
- assert( pReg->flags & MEM_Blob );
- assert( memIsValid(pReg) );
- payloadSize = pReg->n;
- zRec = pReg->z;
- pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
- assert( payloadSize==0 || zRec!=0 );
- }else{
- /* Consider the row to be NULL */
- payloadSize = 0;
- }
+ assert( pCrsr!=0 || pC->pseudoTableReg>0 ); /* pCrsr NULL on PseudoTables */
+ assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */
- /* If payloadSize is 0, then just store a NULL. This can happen because of
- ** nullRow or because of a corrupt database. */
- if( payloadSize==0 ){
- MemSetTypeFlag(pDest, MEM_Null);
- goto op_column_out;
- }
- assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
- if( payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
-
- nField = pC->nField;
- assert( p2<nField );
-
- /* Read and parse the table header. Store the results of the parse
- ** into the record header cache fields of the cursor.
- */
- aType = pC->aType;
- if( pC->cacheStatus==p->cacheCtr ){
- aOffset = pC->aOffset;
- }else{
- assert(aType);
- avail = 0;
- pC->aOffset = aOffset = &aType[nField];
- pC->payloadSize = payloadSize;
- pC->cacheStatus = p->cacheCtr;
-
- /* Figure out how many bytes are in the header */
- if( zRec ){
- zData = zRec;
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( rc ) goto abort_due_to_error;
+ if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){
+ if( pC->nullRow ){
+ if( pCrsr==0 ){
+ assert( pC->pseudoTableReg>0 );
+ pReg = &aMem[pC->pseudoTableReg];
+ assert( pReg->flags & MEM_Blob );
+ assert( memIsValid(pReg) );
+ pC->payloadSize = pC->szRow = avail = pReg->n;
+ pC->aRow = (u8*)pReg->z;
+ }else{
+ MemSetTypeFlag(pDest, MEM_Null);
+ goto op_column_out;
+ }
}else{
- if( pC->isIndex ){
- zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail);
+ assert( pCrsr );
+ if( pC->isTable==0 ){
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+ VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64);
+ assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
+ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
+ ** payload size, so it is impossible for payloadSize64 to be
+ ** larger than 32 bits. */
+ assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 );
+ pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail);
+ pC->payloadSize = (u32)payloadSize64;
}else{
- zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail);
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+ VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize);
+ assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
+ pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail);
}
- /* If KeyFetch()/DataFetch() managed to get the entire payload,
- ** save the payload in the pC->aRow cache. That will save us from
- ** having to make additional calls to fetch the content portion of
- ** the record.
- */
- assert( avail>=0 );
- if( payloadSize <= (u32)avail ){
- zRec = zData;
- pC->aRow = (u8*)zData;
+ assert( avail<=65536 ); /* Maximum page size is 64KiB */
+ if( pC->payloadSize <= (u32)avail ){
+ pC->szRow = pC->payloadSize;
}else{
- pC->aRow = 0;
+ pC->szRow = avail;
}
+ if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
+ }
+ }
+ pC->cacheStatus = p->cacheCtr;
+ pC->iHdrOffset = getVarint32(pC->aRow, offset);
+ pC->nHdrParsed = 0;
+ aOffset[0] = offset;
+ if( avail<offset ){
+ /* pC->aRow does not have to hold the entire row, but it does at least
+ ** need to cover the header of the record. If pC->aRow does not contain
+ ** the complete header, then set it to zero, forcing the header to be
+ ** dynamically allocated. */
+ pC->aRow = 0;
+ pC->szRow = 0;
}
- /* The following assert is true in all cases except when
- ** the database file has been corrupted externally.
- ** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */
- szHdr = getVarint32((u8*)zData, offset);
/* Make sure a corrupt database has not given us an oversize header.
** Do this now to avoid an oversize memory allocation.
@@ -2324,161 +2420,156 @@ case OP_Column: {
** 3-byte type for each of the maximum of 32768 columns plus three
** extra bytes for the header length itself. 32768*3 + 3 = 98307.
*/
- if( offset > 98307 ){
+ if( offset > 98307 || offset > pC->payloadSize ){
rc = SQLITE_CORRUPT_BKPT;
- goto op_column_out;
+ goto op_column_error;
}
+ }
- /* Compute in len the number of bytes of data we need to read in order
- ** to get nField type values. offset is an upper bound on this. But
- ** nField might be significantly less than the true number of columns
- ** in the table, and in that case, 5*nField+3 might be smaller than offset.
- ** We want to minimize len in order to limit the size of the memory
- ** allocation, especially if a corrupt database file has caused offset
- ** to be oversized. Offset is limited to 98307 above. But 98307 might
- ** still exceed Robson memory allocation limits on some configurations.
- ** On systems that cannot tolerate large memory allocations, nField*5+3
- ** will likely be much smaller since nField will likely be less than
- ** 20 or so. This insures that Robson memory allocation limits are
- ** not exceeded even for corrupt database files.
- */
- len = nField*5 + 3;
- if( len > (int)offset ) len = (int)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
- ** record header if the record header does not fit on a single page
- ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to
- ** acquire the complete header text.
+ /* Make sure at least the first p2+1 entries of the header have been
+ ** parsed and valid information is in aOffset[] and aType[].
+ */
+ if( pC->nHdrParsed<=p2 ){
+ /* If there is more header available for parsing in the record, try
+ ** to extract additional fields up through the p2+1-th field
*/
- if( !zRec && avail<len ){
- sMem.flags = 0;
- sMem.db = 0;
- rc = sqlite3VdbeMemFromBtree(pCrsr, 0, len, pC->isIndex, &sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_out;
+ if( pC->iHdrOffset<aOffset[0] ){
+ /* Make sure zData points to enough of the record to cover the header. */
+ if( pC->aRow==0 ){
+ memset(&sMem, 0, sizeof(sMem));
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0],
+ !pC->isTable, &sMem);
+ if( rc!=SQLITE_OK ){
+ goto op_column_error;
+ }
+ zData = (u8*)sMem.z;
+ }else{
+ zData = pC->aRow;
}
- zData = sMem.z;
- }
- zEndHdr = (u8 *)&zData[len];
- zIdx = (u8 *)&zData[szHdr];
-
- /* Scan the header and use it to fill in the aType[] and aOffset[]
- ** arrays. aType[i] will contain the type integer for the i-th
- ** column and aOffset[i] will contain the offset from the beginning
- ** of the record to the start of the data for the i-th column
- */
- for(i=0; i<nField; i++){
- if( zIdx<zEndHdr ){
- aOffset[i] = offset;
- if( zIdx[0]<0x80 ){
- t = zIdx[0];
- zIdx++;
+
+ /* Fill in aType[i] and aOffset[i] values through the p2-th field. */
+ i = pC->nHdrParsed;
+ offset = aOffset[i];
+ zHdr = zData + pC->iHdrOffset;
+ zEndHdr = zData + aOffset[0];
+ assert( i<=p2 && zHdr<zEndHdr );
+ do{
+ if( zHdr[0]<0x80 ){
+ t = zHdr[0];
+ zHdr++;
}else{
- zIdx += sqlite3GetVarint32(zIdx, &t);
+ zHdr += sqlite3GetVarint32(zHdr, &t);
}
aType[i] = t;
szField = sqlite3VdbeSerialTypeLen(t);
offset += szField;
if( offset<szField ){ /* True if offset overflows */
- zIdx = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
+ zHdr = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
break;
}
- }else{
- /* If i is less that nField, then there are fewer 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 store the default value
- ** for the column instead of deserializing a value from the record.
- */
- aOffset[i] = 0;
+ i++;
+ aOffset[i] = offset;
+ }while( i<=p2 && zHdr<zEndHdr );
+ pC->nHdrParsed = i;
+ pC->iHdrOffset = (u32)(zHdr - zData);
+ if( pC->aRow==0 ){
+ 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, 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( (zHdr > zEndHdr)
+ || (offset > pC->payloadSize)
+ || (zHdr==zEndHdr && offset!=pC->payloadSize)
+ ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto op_column_error;
}
}
- 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, 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 after trying to extra new entries from the header, nHdrParsed is
+ ** still not up to p2, that means that the record has fewer than p2
+ ** columns. So the result will be either the default value or a NULL.
*/
- if( (zIdx > zEndHdr) || (offset > payloadSize)
- || (zIdx==zEndHdr && offset!=payloadSize) ){
- rc = SQLITE_CORRUPT_BKPT;
+ if( pC->nHdrParsed<=p2 ){
+ if( pOp->p4type==P4_MEM ){
+ sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
+ }else{
+ MemSetTypeFlag(pDest, MEM_Null);
+ }
goto op_column_out;
}
}
- /* 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 P4 if P4 is
- ** a pointer to a Mem object.
+ /* Extract the content for the p2+1-th column. Control can only
+ ** reach this point if aOffset[p2], aOffset[p2+1], and aType[p2] are
+ ** all valid.
*/
- if( aOffset[p2] ){
- assert( rc==SQLITE_OK );
- if( zRec ){
- /* This is the common case where the whole row fits on a single page */
- VdbeMemRelease(pDest);
- sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
+ assert( p2<pC->nHdrParsed );
+ assert( rc==SQLITE_OK );
+ assert( sqlite3VdbeCheckMemInvariants(pDest) );
+ if( pC->szRow>=aOffset[p2+1] ){
+ /* This is the common case where the desired content fits on the original
+ ** page - where the content is not on an overflow page */
+ VdbeMemRelease(pDest);
+ sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest);
+ }else{
+ /* This branch happens only when content is on overflow pages */
+ t = aType[p2];
+ if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
+ && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
+ || (len = sqlite3VdbeSerialTypeLen(t))==0
+ ){
+ /* Content is irrelevant for the typeof() function and for
+ ** the length(X) function if X is a blob. So we might as well use
+ ** bogus content rather than reading content from disk. NULL works
+ ** for text and blob and whatever is in the payloadSize64 variable
+ ** will work for everything else. Content is also irrelevant if
+ ** the content length is 0. */
+ zData = t<=13 ? (u8*)&payloadSize64 : 0;
+ sMem.zMalloc = 0;
}else{
- /* This branch happens only when the row overflows onto multiple pages */
- t = aType[p2];
- if( (pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
- && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)
- ){
- /* Content is irrelevant for the typeof() function and for
- ** the length(X) function if X is a blob. So we might as well use
- ** bogus content rather than reading content from disk. NULL works
- ** for text and blob and whatever is in the payloadSize64 variable
- ** will work for everything else. */
- zData = t<12 ? (char*)&payloadSize64 : 0;
- }else{
- len = sqlite3VdbeSerialTypeLen(t);
- sqlite3VdbeMemMove(&sMem, pDest);
- rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex,
- &sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_out;
- }
- zData = sMem.z;
+ memset(&sMem, 0, sizeof(sMem));
+ sqlite3VdbeMemMove(&sMem, pDest);
+ rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable,
+ &sMem);
+ if( rc!=SQLITE_OK ){
+ goto op_column_error;
}
- sqlite3VdbeSerialGet((u8*)zData, t, pDest);
+ zData = (u8*)sMem.z;
}
- pDest->enc = encoding;
- }else{
- if( pOp->p4type==P4_MEM ){
- sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
- }else{
- MemSetTypeFlag(pDest, MEM_Null);
+ sqlite3VdbeSerialGet(zData, t, pDest);
+ /* 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 pDest structure.
+ ** This prevents a memory copy. */
+ if( sMem.zMalloc ){
+ assert( sMem.z==sMem.zMalloc );
+ assert( VdbeMemDynamic(pDest)==0 );
+ assert( (pDest->flags & (MEM_Blob|MEM_Str))==0 || pDest->z==sMem.z );
+ pDest->flags &= ~(MEM_Ephem|MEM_Static);
+ pDest->flags |= MEM_Term;
+ pDest->z = sMem.z;
+ pDest->zMalloc = sMem.zMalloc;
}
}
-
- /* 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 pDest structure.
- ** This prevents a memory copy.
- */
- 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;
- }
-
- rc = sqlite3VdbeMemMakeWriteable(pDest);
+ pDest->enc = encoding;
op_column_out:
+ Deephemeralize(pDest);
+op_column_error:
UPDATE_MAX_BLOBSIZE(pDest);
REGISTER_TRACE(pOp->p3, pDest);
break;
}
/* Opcode: Affinity P1 P2 * P4 *
+** Synopsis: affinity(r[P1@P2])
**
** Apply affinities to a range of P2 registers starting with P1.
**
@@ -2495,9 +2586,8 @@ case OP_Affinity: {
assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
while( (cAff = *(zAffinity++))!=0 ){
- assert( pIn1 <= &p->aMem[p->nMem] );
+ assert( pIn1 <= &p->aMem[(p->nMem-p->nCursor)] );
assert( memIsValid(pIn1) );
- ExpandBlob(pIn1);
applyAffinity(pIn1, cAff, encoding);
pIn1++;
}
@@ -2505,6 +2595,7 @@ case OP_Affinity: {
}
/* Opcode: MakeRecord P1 P2 P3 P4 *
+** Synopsis: r[P3]=mkrec(r[P1@P2])
**
** Convert P2 registers beginning with P1 into the [record format]
** use as a data record in a database table or as a key
@@ -2533,7 +2624,8 @@ case OP_MakeRecord: {
int nField; /* Number of fields in the record */
char *zAffinity; /* The affinity string for the record */
int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] */
+ int i; /* Space used in zNewRecord[] header */
+ int j; /* Space used in zNewRecord[] content */
int len; /* Length of a field */
/* Assuming the record contains N fields, the record format looks
@@ -2556,7 +2648,7 @@ case OP_MakeRecord: {
nZero = 0; /* Number of zero bytes at the end of the record */
nField = pOp->p1;
zAffinity = pOp->p4.z;
- assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=p->nMem+1 );
+ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem-p->nCursor)+1 );
pData0 = &aMem[nField];
nField = pOp->p2;
pLast = &pData0[nField-1];
@@ -2567,36 +2659,52 @@ case OP_MakeRecord: {
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
+ /* Apply the requested affinity to all inputs
+ */
+ assert( pData0<=pLast );
+ if( zAffinity ){
+ pRec = pData0;
+ do{
+ applyAffinity(pRec++, *(zAffinity++), encoding);
+ assert( zAffinity[0]==0 || pRec<=pLast );
+ }while( zAffinity[0] );
+ }
+
/* 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<=pLast; pRec++){
+ pRec = pLast;
+ do{
assert( memIsValid(pRec) );
- if( zAffinity ){
- applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
- }
- if( pRec->flags&MEM_Zero && pRec->n>0 ){
- sqlite3VdbeMemExpandBlob(pRec);
- }
serial_type = sqlite3VdbeSerialType(pRec, file_format);
len = sqlite3VdbeSerialTypeLen(serial_type);
- nData += len;
- nHdr += sqlite3VarintLen(serial_type);
if( pRec->flags & MEM_Zero ){
- /* Only pure zero-filled BLOBs can be input to this Opcode.
- ** We do not allow blobs with a prefix and a zero-filled tail. */
- nZero += pRec->u.nZero;
- }else if( len ){
- nZero = 0;
+ if( nData ){
+ sqlite3VdbeMemExpandBlob(pRec);
+ }else{
+ nZero += pRec->u.nZero;
+ len -= pRec->u.nZero;
+ }
}
- }
+ nData += len;
+ testcase( serial_type==127 );
+ testcase( serial_type==128 );
+ nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
+ }while( (--pRec)>=pData0 );
/* Add the initial header varint and total the size */
- nHdr += nVarint = sqlite3VarintLen(nHdr);
- if( nVarint<sqlite3VarintLen(nHdr) ){
- nHdr++;
+ testcase( nHdr==126 );
+ testcase( nHdr==127 );
+ if( nHdr<=126 ){
+ /* The common case */
+ nHdr += 1;
+ }else{
+ /* Rare case of a really large header */
+ nVarint = sqlite3VarintLen(nHdr);
+ nHdr += nVarint;
+ if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++;
}
- nByte = nHdr+nData-nZero;
+ nByte = nHdr+nData;
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
@@ -2613,18 +2721,20 @@ case OP_MakeRecord: {
/* Write the record */
i = putVarint32(zNewRecord, nHdr);
- for(pRec=pData0; pRec<=pLast; pRec++){
+ j = nHdr;
+ assert( pData0<=pLast );
+ pRec = pData0;
+ do{
serial_type = sqlite3VdbeSerialType(pRec, file_format);
- i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
- }
- for(pRec=pData0; pRec<=pLast; pRec++){ /* serial data */
- i += sqlite3VdbeSerialPut(&zNewRecord[i], (int)(nByte-i), pRec,file_format);
- }
- assert( i==nByte );
+ i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
+ j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
+ }while( (++pRec)<=pLast );
+ assert( i==nHdr );
+ assert( j==nByte );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pOut->n = (int)nByte;
- pOut->flags = MEM_Blob | MEM_Dyn;
+ pOut->flags = MEM_Blob;
pOut->xDel = 0;
if( nZero ){
pOut->u.nZero = nZero;
@@ -2637,6 +2747,7 @@ case OP_MakeRecord: {
}
/* Opcode: Count P1 P2 * * *
+** Synopsis: r[P2]=count()
**
** Store the number of entries (an integer value) in the table or index
** opened by cursor P1 in register P2
@@ -2647,11 +2758,9 @@ case OP_Count: { /* out2-prerelease */
BtCursor *pCrsr;
pCrsr = p->apCsr[pOp->p1]->pCursor;
- if( ALWAYS(pCrsr) ){
- rc = sqlite3BtreeCount(pCrsr, &nEntry);
- }else{
- nEntry = 0;
- }
+ assert( pCrsr );
+ nEntry = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3BtreeCount(pCrsr, &nEntry);
pOut->u.i = nEntry;
break;
}
@@ -2683,9 +2792,10 @@ case OP_Savepoint: {
assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
assert( db->pSavepoint || db->isTransactionSavepoint==0 );
assert( checkSavepointCount(db) );
+ assert( p->bIsReader );
if( p1==SAVEPOINT_BEGIN ){
- if( db->writeVdbeCnt>0 ){
+ if( db->nVdbeWrite>0 ){
/* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
@@ -2725,6 +2835,7 @@ case OP_Savepoint: {
pNew->pNext = db->pSavepoint;
db->pSavepoint = pNew;
pNew->nDeferredCons = db->nDeferredCons;
+ pNew->nDeferredImmCons = db->nDeferredImmCons;
}
}
}else{
@@ -2742,7 +2853,7 @@ case OP_Savepoint: {
if( !pSavepoint ){
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
- }else if( db->writeVdbeCnt>0 && p1==SAVEPOINT_RELEASE ){
+ }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
/* It is not possible to release (commit) a savepoint if there are
** active write statements.
*/
@@ -2812,6 +2923,7 @@ case OP_Savepoint: {
}
}else{
db->nDeferredCons = pSavepoint->nDeferredCons;
+ db->nDeferredImmCons = pSavepoint->nDeferredImmCons;
}
if( !isTransaction ){
@@ -2843,10 +2955,11 @@ case OP_AutoCommit: {
turnOnAC = desiredAutoCommit && !db->autoCommit;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
- assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
+ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
+ assert( p->bIsReader );
#if 0
- if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){
+ if( turnOnAC && iRollback && db->nVdbeActive>1 ){
/* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating
** that the other VMs must complete first.
@@ -2856,7 +2969,7 @@ case OP_AutoCommit: {
rc = SQLITE_BUSY;
}else
#endif
- if( turnOnAC && !iRollback && db->writeVdbeCnt>0 ){
+ if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
@@ -2898,25 +3011,19 @@ case OP_AutoCommit: {
break;
}
-/* Opcode: Transaction P1 P2 * * *
+/* Opcode: Transaction P1 P2 P3 P4 P5
**
-** Begin a transaction. The transaction ends when a Commit or Rollback
-** opcode is encountered. Depending on the ON CONFLICT setting, the
-** transaction might also be rolled back if an error is encountered.
+** Begin a transaction on database P1 if a transaction is not already
+** active.
+** If P2 is non-zero, then a write-transaction is started, or if a
+** read-transaction is already active, it is upgraded to a write-transaction.
+** If P2 is zero, then a read-transaction is started.
**
** 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. 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
-** other process can start another write transaction while this transaction is
-** underway. Starting a write transaction also creates a rollback journal. A
-** write transaction must be started before any changes can be made to the
-** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
-** on the file.
-**
** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
** true (this flag is set if the Vdbe may modify more than one row and may
** throw an ABORT exception), a statement transaction may also be opened.
@@ -2927,13 +3034,30 @@ case OP_AutoCommit: {
** entire transaction. If no error is encountered, the statement transaction
** will automatically commit when the VDBE halts.
**
-** If P2 is zero, then a read-lock is obtained on the database file.
+** If P5!=0 then this opcode also checks the schema cookie against P3
+** and the schema generation counter against P4.
+** The cookie changes its value whenever the database schema changes.
+** This operation is used to detect when that the cookie has changed
+** and that the current process needs to reread the schema. If the schema
+** cookie in P3 differs from the schema cookie in the database header or
+** if the schema generation counter in P4 differs from the current
+** generation counter, then an SQLITE_SCHEMA error is raised and execution
+** halts. The sqlite3_step() wrapper function might then reprepare the
+** statement and rerun it from the beginning.
*/
case OP_Transaction: {
Btree *pBt;
+ int iMeta;
+ int iGen;
+ assert( p->bIsReader );
+ assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
+ rc = SQLITE_READONLY;
+ goto abort_due_to_error;
+ }
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
@@ -2948,7 +3072,7 @@ case OP_Transaction: {
}
if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
assert( sqlite3BtreeIsInTrans(pBt) );
if( p->iStatement==0 ){
@@ -2966,7 +3090,37 @@ case OP_Transaction: {
** counter. If the statement transaction needs to be rolled back,
** the value of this counter needs to be restored too. */
p->nStmtDefCons = db->nDeferredCons;
+ p->nStmtDefImmCons = db->nDeferredImmCons;
}
+
+ /* Gather the schema version number for checking */
+ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
+ iGen = db->aDb[pOp->p1].pSchema->iGeneration;
+ }else{
+ iGen = iMeta = 0;
+ }
+ assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
+ if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){
+ 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 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
+ ** discard the database schema, as the user code implementing the
+ ** v-table would have to be ready for the sqlite3_vtab structure itself
+ ** to be invalidated whenever sqlite3_step() is called from within
+ ** a v-table method.
+ */
+ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
+ sqlite3ResetOneSchema(db, pOp->p1);
+ }
+ p->expired = 1;
+ rc = SQLITE_SCHEMA;
}
break;
}
@@ -2988,12 +3142,13 @@ case OP_ReadCookie: { /* out2-prerelease */
int iDb;
int iCookie;
+ assert( p->bIsReader );
iDb = pOp->p1;
iCookie = pOp->p3;
assert( pOp->p3<SQLITE_N_BTREE_META );
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
- assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
+ assert( DbMaskTest(p->btreeMask, iDb) );
sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta);
pOut->u.i = iMeta;
@@ -3014,7 +3169,8 @@ case OP_SetCookie: { /* in3 */
Db *pDb;
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
@@ -3039,66 +3195,8 @@ case OP_SetCookie: { /* in3 */
break;
}
-/* Opcode: VerifyCookie P1 P2 P3 * *
-**
-** Check the value of global database parameter number 0 (the
-** schema version) and make sure it is equal to P2 and that the
-** generation counter on the local schema parse equals P3.
-**
-** P1 is the database number which is 0 for the main database file
-** and 1 for the file holding temporary tables and some higher number
-** for auxiliary databases.
-**
-** The cookie changes its value whenever the database schema changes.
-** This operation is used to detect when that the cookie has changed
-** and that the current process needs to reread the schema.
-**
-** Either a transaction needs to have been started or an OP_Open needs
-** to be executed (to establish a read lock) before this opcode is
-** invoked.
-*/
-case OP_VerifyCookie: {
- int iMeta;
- int iGen;
- Btree *pBt;
-
- assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- pBt = db->aDb[pOp->p1].pBt;
- if( pBt ){
- sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
- iGen = db->aDb[pOp->p1].pSchema->iGeneration;
- }else{
- iGen = iMeta = 0;
- }
- if( iMeta!=pOp->p2 || iGen!=pOp->p3 ){
- 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 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
- ** discard the database schema, as the user code implementing the
- ** v-table would have to be ready for the sqlite3_vtab structure itself
- ** to be invalidated whenever sqlite3_step() is called from within
- ** a v-table method.
- */
- if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
- sqlite3ResetOneSchema(db, pOp->p1);
- }
-
- p->expired = 1;
- rc = SQLITE_SCHEMA;
- }
- break;
-}
-
/* Opcode: OpenRead P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read-only cursor for the database table whose root page is
** P2 in a database file. The database file is determined by P3.
@@ -3126,9 +3224,24 @@ case OP_VerifyCookie: {
** sequence of the index being opened. Otherwise, if P4 is an integer
** value, it is set to the number of columns in the table.
**
-** See also OpenWrite.
+** See also: OpenWrite, ReopenIdx
+*/
+/* Opcode: ReopenIdx P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
+**
+** The ReopenIdx opcode works exactly like ReadOpen except that it first
+** checks to see if the cursor on P1 is already open with a root page
+** number of P2 and if it is this opcode becomes a no-op. In other words,
+** if the cursor is already open, do not reopen it.
+**
+** The ReopenIdx opcode may only be used with P5==0 and with P4 being
+** a P4_KEYINFO object. Furthermore, the P3 value must be the same as
+** every other ReopenIdx or OpenRead for the same cursor number.
+**
+** See the OpenRead opcode documentation for additional information.
*/
/* Opcode: OpenWrite P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read/write cursor named P1 on the table or index whose root
** page is P2. Or if P5!=0 use the content of register P2 to find the
@@ -3147,6 +3260,19 @@ case OP_VerifyCookie: {
**
** See also OpenRead.
*/
+case OP_ReopenIdx: {
+ VdbeCursor *pCur;
+
+ assert( pOp->p5==0 );
+ assert( pOp->p4type==P4_KEYINFO );
+ pCur = p->apCsr[pOp->p1];
+ if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){
+ assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */
+ break;
+ }
+ /* If the cursor is not currently open or is open on a different
+ ** index, then fall through into OP_OpenRead to force a reopen */
+}
case OP_OpenRead:
case OP_OpenWrite: {
int nField;
@@ -3160,6 +3286,9 @@ case OP_OpenWrite: {
assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
+ assert( p->bIsReader );
+ assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
+ || p->readOnly==0 );
if( p->expired ){
rc = SQLITE_ABORT;
@@ -3171,7 +3300,7 @@ case OP_OpenWrite: {
p2 = pOp->p2;
iDb = pOp->p3;
assert( iDb>=0 && iDb<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
+ assert( DbMaskTest(p->btreeMask, iDb) );
pDb = &db->aDb[iDb];
pX = pDb->pBt;
assert( pX!=0 );
@@ -3186,7 +3315,7 @@ case OP_OpenWrite: {
}
if( pOp->p5 & OPFLAG_P2ISREG ){
assert( p2>0 );
- assert( p2<=p->nMem );
+ assert( p2<=(p->nMem-p->nCursor) );
pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
@@ -3203,16 +3332,20 @@ case OP_OpenWrite: {
}
if( pOp->p4type==P4_KEYINFO ){
pKeyInfo = pOp->p4.pKeyInfo;
- pKeyInfo->enc = ENC(p->db);
- nField = pKeyInfo->nField+1;
+ assert( pKeyInfo->enc==ENC(db) );
+ assert( pKeyInfo->db==db );
+ nField = pKeyInfo->nField+pKeyInfo->nXField;
}else if( pOp->p4type==P4_INT32 ){
nField = pOp->p4.i;
}
assert( pOp->p1>=0 );
+ assert( nField>=0 );
+ testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */
pCur = allocateCursor(p, pOp->p1, nField, iDb, 1);
if( pCur==0 ) goto no_mem;
pCur->nullRow = 1;
pCur->isOrdered = 1;
+ pCur->pgnoRoot = p2;
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
pCur->pKeyInfo = pKeyInfo;
assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
@@ -3222,16 +3355,16 @@ case OP_OpenWrite: {
** sqlite3BtreeCursor() may return is SQLITE_OK. */
assert( rc==SQLITE_OK );
- /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
+ /* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
** and report database corruption if they were not, but this check has
** since moved into the btree layer. */
pCur->isTable = pOp->p4type!=P4_KEYINFO;
- pCur->isIndex = !pCur->isTable;
break;
}
/* Opcode: OpenEphemeral P1 P2 * P4 P5
+** Synopsis: nColumn=P2
**
** Open a new cursor P1 to a transient table.
** The cursor is always opened read/write even if
@@ -3243,18 +3376,13 @@ case OP_OpenWrite: {
** 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
-** confusion because the term "temp table", might refer either
-** to a TEMP table at the SQL level, or to a table opened by
-** this opcode. Then this opcode was call OpenVirtual. But
-** that created confusion with the whole virtual-table idea.
-**
** The P5 parameter can be a mask of the BTREE_* flags defined
** in btree.h. These flags control aspects of the operation of
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
** added automatically.
*/
/* Opcode: OpenAutoindex P1 P2 * P4 *
+** Synopsis: nColumn=P2
**
** This opcode works the same as OP_OpenEphemeral. It has a
** different name to distinguish its use. Tables created using
@@ -3264,17 +3392,20 @@ case OP_OpenWrite: {
case OP_OpenAutoindex:
case OP_OpenEphemeral: {
VdbeCursor *pCx;
+ KeyInfo *pKeyInfo;
+
static const int vfsFlags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_TRANSIENT_DB;
-
assert( pOp->p1>=0 );
+ assert( pOp->p2>=0 );
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
+ pCx->isEphemeral = 1;
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
if( rc==SQLITE_OK ){
@@ -3286,16 +3417,16 @@ case OP_OpenEphemeral: {
** opening it. If a transient table is required, just use the
** automatically created table with root-page 1 (an BLOB_INTKEY table).
*/
- if( pOp->p4.pKeyInfo ){
+ if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
int pgno;
assert( pOp->p4type==P4_KEYINFO );
rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
if( rc==SQLITE_OK ){
assert( pgno==MASTER_ROOT+1 );
- rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1,
- (KeyInfo*)pOp->p4.z, pCx->pCursor);
- pCx->pKeyInfo = pOp->p4.pKeyInfo;
- pCx->pKeyInfo->enc = ENC(p->db);
+ assert( pKeyInfo->db==db );
+ assert( pKeyInfo->enc==ENC(db) );
+ pCx->pKeyInfo = pKeyInfo;
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, pKeyInfo, pCx->pCursor);
}
pCx->isTable = 0;
}else{
@@ -3304,7 +3435,6 @@ case OP_OpenEphemeral: {
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
- pCx->isIndex = !pCx->isTable;
break;
}
@@ -3317,22 +3447,24 @@ case OP_OpenEphemeral: {
case OP_SorterOpen: {
VdbeCursor *pCx;
+ assert( pOp->p1>=0 );
+ assert( pOp->p2>=0 );
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
if( pCx==0 ) goto no_mem;
pCx->pKeyInfo = pOp->p4.pKeyInfo;
- pCx->pKeyInfo->enc = ENC(p->db);
- pCx->isSorter = 1;
+ assert( pCx->pKeyInfo->db==db );
+ assert( pCx->pKeyInfo->enc==ENC(db) );
rc = sqlite3VdbeSorterInit(db, pCx);
break;
}
-/* Opcode: OpenPseudo P1 P2 P3 * P5
+/* Opcode: OpenPseudo P1 P2 P3 * *
+** Synopsis: P3 columns in r[P2]
**
** Open a new cursor that points to a fake table that contains a single
-** row of data. The content of that one row in the content of memory
-** register P2 when P5==0. In other words, cursor P1 becomes an alias for the
-** MEM_Blob content contained in register P2. When P5==1, then the
-** row is represented by P3 consecutive registers beginning with P2.
+** row of data. The content of that one row is the content of memory
+** register P2. In other words, cursor P1 becomes an alias for the
+** MEM_Blob content contained in register P2.
**
** A pseudo-table created by this opcode is used to hold a single
** row output from the sorter so that the row can be decomposed into
@@ -3346,13 +3478,13 @@ case OP_OpenPseudo: {
VdbeCursor *pCx;
assert( pOp->p1>=0 );
+ assert( pOp->p3>=0 );
pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
pCx->pseudoTableReg = pOp->p2;
pCx->isTable = 1;
- pCx->isIndex = 0;
- pCx->multiPseudo = pOp->p5;
+ assert( pOp->p5==0 );
break;
}
@@ -3368,7 +3500,8 @@ case OP_Close: {
break;
}
-/* Opcode: SeekGe P1 P2 P3 P4 *
+/* Opcode: SeekGE P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as the key. If cursor P1 refers
@@ -3379,9 +3512,14 @@ case OP_Close: {
** 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.
**
-** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
+**
+** See also: Found, NotFound, SeekLt, SeekGt, SeekLe
*/
-/* Opcode: SeekGt P1 P2 P3 P4 *
+/* Opcode: SeekGT P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -3392,9 +3530,14 @@ case OP_Close: {
** 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, SeekLt, SeekGe, SeekLe
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
+**
+** See also: Found, NotFound, SeekLt, SeekGe, SeekLe
*/
-/* Opcode: SeekLt P1 P2 P3 P4 *
+/* Opcode: SeekLT P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -3405,9 +3548,14 @@ case OP_Close: {
** 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, SeekGt, SeekGe, SeekLe
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
+**
+** See also: Found, NotFound, SeekGt, SeekGe, SeekLe
*/
-/* Opcode: SeekLe P1 P2 P3 P4 *
+/* Opcode: SeekLE P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -3418,12 +3566,16 @@ case OP_Close: {
** 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, SeekGt, SeekGe, SeekLt
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
+**
+** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLt: /* jump, in3 */
-case OP_SeekLe: /* jump, in3 */
-case OP_SeekGe: /* jump, in3 */
-case OP_SeekGt: { /* jump, in3 */
+case OP_SeekLT: /* jump, in3 */
+case OP_SeekLE: /* jump, in3 */
+case OP_SeekGE: /* jump, in3 */
+case OP_SeekGT: { /* jump, in3 */
int res;
int oc;
VdbeCursor *pC;
@@ -3436,143 +3588,135 @@ case OP_SeekGt: { /* jump, in3 */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->pseudoTableReg==0 );
- assert( OP_SeekLe == OP_SeekLt+1 );
- assert( OP_SeekGe == OP_SeekLt+2 );
- assert( OP_SeekGt == OP_SeekLt+3 );
+ assert( OP_SeekLE == OP_SeekLT+1 );
+ assert( OP_SeekGE == OP_SeekLT+2 );
+ assert( OP_SeekGT == OP_SeekLT+3 );
assert( pC->isOrdered );
- if( ALWAYS(pC->pCursor!=0) ){
- oc = pOp->opcode;
- pC->nullRow = 0;
- if( pC->isTable ){
- /* The input value in P3 might be of any type: integer, real, string,
- ** blob, or NULL. But it needs to be an integer before we can do
- ** the seek, so covert it. */
- pIn3 = &aMem[pOp->p3];
- applyNumericAffinity(pIn3);
- iKey = sqlite3VdbeIntValue(pIn3);
- pC->rowidIsValid = 0;
+ assert( pC->pCursor!=0 );
+ oc = pOp->opcode;
+ pC->nullRow = 0;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = pOp->opcode;
+#endif
+ if( pC->isTable ){
+ /* The input value in P3 might be of any type: integer, real, string,
+ ** blob, or NULL. But it needs to be an integer before we can do
+ ** the seek, so covert it. */
+ pIn3 = &aMem[pOp->p3];
+ ApplyNumericAffinity(pIn3);
+ iKey = sqlite3VdbeIntValue(pIn3);
+ pC->rowidIsValid = 0;
- /* If the P3 value could not be converted into an integer without
- ** loss of information, then special processing is required... */
- if( (pIn3->flags & MEM_Int)==0 ){
- if( (pIn3->flags & MEM_Real)==0 ){
- /* If the P3 value cannot be converted into any kind of a number,
- ** then the seek is not possible, so jump to P2 */
- pc = pOp->p2 - 1;
- break;
- }
- /* If we reach this point, then the P3 value must be a floating
- ** point number. */
- assert( (pIn3->flags & MEM_Real)!=0 );
-
- if( iKey==SMALLEST_INT64 && (pIn3->r<(double)iKey || pIn3->r>0) ){
- /* The P3 value is too large in magnitude to be expressed as an
- ** integer. */
- res = 1;
- if( pIn3->r<0 ){
- if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt );
- rc = sqlite3BtreeFirst(pC->pCursor, &res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- }
- }else{
- if( oc<=OP_SeekLe ){ assert( oc==OP_SeekLt || oc==OP_SeekLe );
- rc = sqlite3BtreeLast(pC->pCursor, &res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- }
- }
- if( res ){
- pc = pOp->p2 - 1;
- }
- break;
- }else if( oc==OP_SeekLt || oc==OP_SeekGe ){
- /* Use the ceiling() function to convert real->int */
- if( pIn3->r > (double)iKey ) iKey++;
- }else{
- /* Use the floor() function to convert real->int */
- assert( oc==OP_SeekLe || oc==OP_SeekGt );
- if( pIn3->r < (double)iKey ) iKey--;
- }
- }
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- if( res==0 ){
- pC->rowidIsValid = 1;
- pC->lastRowid = iKey;
+ /* If the P3 value could not be converted into an integer without
+ ** loss of information, then special processing is required... */
+ if( (pIn3->flags & MEM_Int)==0 ){
+ if( (pIn3->flags & MEM_Real)==0 ){
+ /* If the P3 value cannot be converted into any kind of a number,
+ ** then the seek is not possible, so jump to P2 */
+ pc = pOp->p2 - 1; VdbeBranchTaken(1,2);
+ break;
}
- }else{
- nField = pOp->p4.i;
- assert( pOp->p4type==P4_INT32 );
- assert( nField>0 );
- r.pKeyInfo = pC->pKeyInfo;
- r.nField = (u16)nField;
-
- /* The next line of code computes as follows, only faster:
- ** if( oc==OP_SeekGt || oc==OP_SeekLe ){
- ** r.flags = UNPACKED_INCRKEY;
- ** }else{
- ** r.flags = 0;
- ** }
+
+ /* If the approximation iKey is larger than the actual real search
+ ** term, substitute >= for > and < for <=. e.g. if the search term
+ ** is 4.9 and the integer approximation 5:
+ **
+ ** (x > 4.9) -> (x >= 5)
+ ** (x <= 4.9) -> (x < 5)
*/
- r.flags = (u8)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLt)));
- assert( oc!=OP_SeekGt || r.flags==UNPACKED_INCRKEY );
- assert( oc!=OP_SeekLe || r.flags==UNPACKED_INCRKEY );
- assert( oc!=OP_SeekGe || r.flags==0 );
- assert( oc!=OP_SeekLt || r.flags==0 );
+ if( pIn3->r<(double)iKey ){
+ assert( OP_SeekGE==(OP_SeekGT-1) );
+ assert( OP_SeekLT==(OP_SeekLE-1) );
+ assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) );
+ if( (oc & 0x0001)==(OP_SeekGT & 0x0001) ) oc--;
+ }
- r.aMem = &aMem[pOp->p3];
+ /* If the approximation iKey is smaller than the actual real search
+ ** term, substitute <= for < and > for >=. */
+ else if( pIn3->r>(double)iKey ){
+ assert( OP_SeekLE==(OP_SeekLT+1) );
+ assert( OP_SeekGT==(OP_SeekGE+1) );
+ assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) );
+ if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++;
+ }
+ }
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( res==0 ){
+ pC->rowidIsValid = 1;
+ pC->lastRowid = iKey;
+ }
+ }else{
+ nField = pOp->p4.i;
+ assert( pOp->p4type==P4_INT32 );
+ assert( nField>0 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)nField;
+
+ /* The next line of code computes as follows, only faster:
+ ** if( oc==OP_SeekGT || oc==OP_SeekLE ){
+ ** r.default_rc = -1;
+ ** }else{
+ ** r.default_rc = +1;
+ ** }
+ */
+ r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1);
+ assert( oc!=OP_SeekGT || r.default_rc==-1 );
+ assert( oc!=OP_SeekLE || r.default_rc==-1 );
+ assert( oc!=OP_SeekGE || r.default_rc==+1 );
+ assert( oc!=OP_SeekLT || r.default_rc==+1 );
+
+ r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
- ExpandBlob(r.aMem);
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- pC->rowidIsValid = 0;
+ ExpandBlob(r.aMem);
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, &r, 0, 0, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
}
- pC->deferredMoveto = 0;
- pC->cacheStatus = CACHE_STALE;
+ pC->rowidIsValid = 0;
+ }
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_TEST
- sqlite3_search_count++;
+ sqlite3_search_count++;
#endif
- if( oc>=OP_SeekGe ){ assert( oc==OP_SeekGe || oc==OP_SeekGt );
- if( res<0 || (res==0 && oc==OP_SeekGt) ){
- rc = sqlite3BtreeNext(pC->pCursor, &res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- pC->rowidIsValid = 0;
- }else{
- res = 0;
- }
+ if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT );
+ if( res<0 || (res==0 && oc==OP_SeekGT) ){
+ res = 0;
+ rc = sqlite3BtreeNext(pC->pCursor, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ pC->rowidIsValid = 0;
}else{
- assert( oc==OP_SeekLt || oc==OP_SeekLe );
- if( res>0 || (res==0 && oc==OP_SeekLt) ){
- rc = sqlite3BtreePrevious(pC->pCursor, &res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- pC->rowidIsValid = 0;
- }else{
- /* res might be negative because the table is empty. Check to
- ** see if this is the case.
- */
- res = sqlite3BtreeEof(pC->pCursor);
- }
- }
- assert( pOp->p2>0 );
- if( res ){
- pc = pOp->p2 - 1;
+ res = 0;
}
}else{
- /* 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).
- */
+ assert( oc==OP_SeekLT || oc==OP_SeekLE );
+ if( res>0 || (res==0 && oc==OP_SeekLT) ){
+ res = 0;
+ rc = sqlite3BtreePrevious(pC->pCursor, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ pC->rowidIsValid = 0;
+ }else{
+ /* res might be negative because the table is empty. Check to
+ ** see if this is the case.
+ */
+ res = sqlite3BtreeEof(pC->pCursor);
+ }
+ }
+ assert( pOp->p2>0 );
+ VdbeBranchTaken(res!=0,2);
+ if( res ){
pc = pOp->p2 - 1;
}
break;
}
/* Opcode: Seek P1 P2 * * *
+** Synopsis: intkey=r[P2]
**
** P1 is an open table cursor and P2 is a rowid integer. Arrange
** for P1 to move so that it points to the rowid given by P2.
@@ -3587,19 +3731,19 @@ case OP_Seek: { /* in2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- if( ALWAYS(pC->pCursor!=0) ){
- assert( pC->isTable );
- pC->nullRow = 0;
- pIn2 = &aMem[pOp->p2];
- pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- pC->rowidIsValid = 0;
- pC->deferredMoveto = 1;
- }
+ assert( pC->pCursor!=0 );
+ assert( pC->isTable );
+ pC->nullRow = 0;
+ pIn2 = &aMem[pOp->p2];
+ pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
+ pC->rowidIsValid = 0;
+ pC->deferredMoveto = 1;
break;
}
/* Opcode: Found P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
@@ -3608,8 +3752,15 @@ case OP_Seek: { /* in2 */
** Cursor P1 is on an index btree. If the record identified by P3 and P4
** is a prefix of any entry in P1 then a jump is made to P2 and
** P1 is left pointing at the matching entry.
+**
+** This operation leaves the cursor in a state where it can be
+** advanced in the forward direction. The Next instruction will work,
+** but not the Prev instruction.
+**
+** See also: NotFound, NoConflict, NotExists. SeekGe
*/
/* Opcode: NotFound P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
@@ -3621,169 +3772,134 @@ case OP_Seek: { /* in2 */
** falls through to the next instruction and P1 is left pointing at the
** matching entry.
**
-** See also: Found, NotExists, IsUnique
+** This operation leaves the cursor in a state where it cannot be
+** advanced in either direction. In other words, the Next and Prev
+** opcodes do not work after this operation.
+**
+** See also: Found, NotExists, NoConflict
*/
+/* Opcode: NoConflict P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
+**
+** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
+** P4>0 then register P3 is the first of P4 registers that form an unpacked
+** record.
+**
+** Cursor P1 is on an index btree. If the record identified by P3 and P4
+** contains any NULL value, jump immediately to P2. If all terms of the
+** record are not-NULL then a check is done to determine if any row in the
+** P1 index btree has a matching key prefix. If there are no matches, jump
+** immediately to P2. If there is a match, fall through and leave the P1
+** cursor pointing to the matching row.
+**
+** This opcode is similar to OP_NotFound with the exceptions that the
+** branch is always taken if any part of the search key input is NULL.
+**
+** This operation leaves the cursor in a state where it cannot be
+** advanced in either direction. In other words, the Next and Prev
+** opcodes do not work after this operation.
+**
+** See also: NotFound, Found, NotExists
+*/
+case OP_NoConflict: /* jump, in3 */
case OP_NotFound: /* jump, in3 */
case OP_Found: { /* jump, in3 */
int alreadyExists;
+ int ii;
VdbeCursor *pC;
int res;
char *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
- char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
+ char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*4 + 7];
#ifdef SQLITE_TEST
- sqlite3_found_count++;
+ if( pOp->opcode!=OP_NoConflict ) sqlite3_found_count++;
#endif
- alreadyExists = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p4type==P4_INT32 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+#ifdef SQLITE_DEBUG
+ pC->seekOp = pOp->opcode;
+#endif
pIn3 = &aMem[pOp->p3];
- if( ALWAYS(pC->pCursor!=0) ){
-
- assert( pC->isTable==0 );
- if( pOp->p4.i>0 ){
- r.pKeyInfo = pC->pKeyInfo;
- r.nField = (u16)pOp->p4.i;
- r.aMem = pIn3;
+ assert( pC->pCursor!=0 );
+ assert( pC->isTable==0 );
+ pFree = 0; /* Not needed. Only used to suppress a compiler warning. */
+ if( pOp->p4.i>0 ){
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p4.i;
+ r.aMem = pIn3;
+ for(ii=0; ii<r.nField; ii++){
+ assert( memIsValid(&r.aMem[ii]) );
+ ExpandBlob(&r.aMem[ii]);
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]);
#endif
- r.flags = UNPACKED_PREFIX_MATCH;
- pIdxKey = &r;
- }else{
- pIdxKey = sqlite3VdbeAllocUnpackedRecord(
- pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
- );
- if( pIdxKey==0 ) goto no_mem;
- assert( pIn3->flags & MEM_Blob );
- assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
- sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
- pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
- }
- rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
- if( pOp->p4.i==0 ){
- sqlite3DbFree(db, pFree);
}
- if( rc!=SQLITE_OK ){
- break;
+ pIdxKey = &r;
+ }else{
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
+ );
+ if( pIdxKey==0 ) goto no_mem;
+ assert( pIn3->flags & MEM_Blob );
+ assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
+ }
+ pIdxKey->default_rc = 0;
+ if( pOp->opcode==OP_NoConflict ){
+ /* For the OP_NoConflict opcode, take the jump if any of the
+ ** input fields are NULL, since any key with a NULL will not
+ ** conflict */
+ for(ii=0; ii<r.nField; ii++){
+ if( r.aMem[ii].flags & MEM_Null ){
+ pc = pOp->p2 - 1; VdbeBranchTaken(1,2);
+ break;
+ }
}
- alreadyExists = (res==0);
- pC->deferredMoveto = 0;
- pC->cacheStatus = CACHE_STALE;
}
+ rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
+ if( pOp->p4.i==0 ){
+ sqlite3DbFree(db, pFree);
+ }
+ if( rc!=SQLITE_OK ){
+ break;
+ }
+ pC->seekResult = res;
+ alreadyExists = (res==0);
+ pC->nullRow = 1-alreadyExists;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
if( pOp->opcode==OP_Found ){
+ VdbeBranchTaken(alreadyExists!=0,2);
if( alreadyExists ) pc = pOp->p2 - 1;
}else{
+ VdbeBranchTaken(alreadyExists==0,2);
if( !alreadyExists ) pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: IsUnique P1 P2 P3 P4 *
-**
-** Cursor P1 is open on an index b-tree - that is to say, a btree which
-** no data and where the key are records generated by OP_MakeRecord with
-** the list field being the integer ROWID of the entry that the index
-** entry refers to.
-**
-** The P3 register contains an integer record number. Call this record
-** number R. Register P4 is the first in a set of N contiguous registers
-** that make up an unpacked index key that can be used with cursor P1.
-** The value of N can be inferred from the cursor. N includes the rowid
-** value appended to the end of the index record. This rowid value may
-** or may not be the same as R.
-**
-** If any of the N registers beginning with register P4 contains a NULL
-** value, jump immediately to P2.
-**
-** Otherwise, this instruction checks if cursor P1 contains an entry
-** where the first (N-1) fields match but the rowid value at the end
-** of the index entry is not R. If there is no such entry, control jumps
-** to instruction P2. Otherwise, the rowid of the conflicting index
-** entry is copied to register P3 and control falls through to the next
-** instruction.
-**
-** See also: NotFound, NotExists, Found
-*/
-case OP_IsUnique: { /* jump, in3 */
- u16 ii;
- VdbeCursor *pCx;
- BtCursor *pCrsr;
- u16 nField;
- Mem *aMx;
- UnpackedRecord r; /* B-Tree index search key */
- i64 R; /* Rowid stored in register P3 */
-
- pIn3 = &aMem[pOp->p3];
- aMx = &aMem[pOp->p4.i];
- /* Assert that the values of parameters P1 and P4 are in range. */
- assert( pOp->p4type==P4_INT32 );
- assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
- assert( pOp->p1>=0 && pOp->p1<p->nCursor );
-
- /* Find the index cursor. */
- pCx = p->apCsr[pOp->p1];
- assert( pCx->deferredMoveto==0 );
- pCx->seekResult = 0;
- pCx->cacheStatus = CACHE_STALE;
- pCrsr = pCx->pCursor;
-
- /* If any of the values are NULL, take the jump. */
- nField = pCx->pKeyInfo->nField;
- for(ii=0; ii<nField; ii++){
- if( aMx[ii].flags & MEM_Null ){
- pc = pOp->p2 - 1;
- pCrsr = 0;
- break;
- }
- }
- assert( (aMx[nField].flags & MEM_Null)==0 );
-
- if( pCrsr!=0 ){
- /* Populate the index search key. */
- r.pKeyInfo = pCx->pKeyInfo;
- r.nField = nField + 1;
- r.flags = UNPACKED_PREFIX_SEARCH;
- r.aMem = aMx;
-#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
-#endif
-
- /* Extract the value of R from register P3. */
- sqlite3VdbeMemIntegerify(pIn3);
- R = pIn3->u.i;
-
- /* Search the B-Tree index. If no conflicting record is found, jump
- ** to P2. Otherwise, copy the rowid of the conflicting record to
- ** register P3 and fall through to the next instruction. */
- rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &pCx->seekResult);
- if( (r.flags & UNPACKED_PREFIX_SEARCH) || r.rowid==R ){
- pc = pOp->p2 - 1;
- }else{
- pIn3->u.i = r.rowid;
- }
- }
- break;
-}
-
/* Opcode: NotExists P1 P2 P3 * *
+** Synopsis: intkey=r[P3]
**
-** Use the content of register P3 as an 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 through. The cursor is left
-** pointing to the record if it exists.
+** P1 is the index of a cursor open on an SQL table btree (with integer
+** keys). P3 is an integer rowid. If P1 does not contain a record with
+** rowid P3 then jump immediately to P2. If P1 does contain a record
+** with rowid P3 then leave the cursor pointing at that record and fall
+** through to the next instruction.
**
-** 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.
+** The OP_NotFound opcode performs the same operation on index btrees
+** (with arbitrary multi-value keys).
**
-** See also: Found, NotFound, IsUnique
+** This opcode leaves the cursor in a state where it cannot be advanced
+** in either direction. In other words, the Next and Prev opcodes will
+** not work following this opcode.
+**
+** See also: Found, NotFound, NoConflict
*/
case OP_NotExists: { /* jump, in3 */
VdbeCursor *pC;
@@ -3796,35 +3912,32 @@ case OP_NotExists: { /* jump, in3 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+#ifdef SQLITE_DEBUG
+ pC->seekOp = 0;
+#endif
assert( pC->isTable );
assert( pC->pseudoTableReg==0 );
pCrsr = pC->pCursor;
- if( ALWAYS(pCrsr!=0) ){
- res = 0;
- iKey = pIn3->u.i;
- rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
- pC->lastRowid = pIn3->u.i;
- pC->rowidIsValid = res==0 ?1:0;
- pC->nullRow = 0;
- pC->cacheStatus = CACHE_STALE;
- pC->deferredMoveto = 0;
- if( res!=0 ){
- pc = pOp->p2 - 1;
- assert( pC->rowidIsValid==0 );
- }
- pC->seekResult = res;
- }else{
- /* This happens when an attempt to open a read cursor on the
- ** sqlite_master table returns SQLITE_EMPTY.
- */
+ assert( pCrsr!=0 );
+ res = 0;
+ iKey = pIn3->u.i;
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
+ pC->lastRowid = pIn3->u.i;
+ pC->rowidIsValid = res==0 ?1:0;
+ pC->nullRow = 0;
+ pC->cacheStatus = CACHE_STALE;
+ pC->deferredMoveto = 0;
+ VdbeBranchTaken(res!=0,2);
+ if( res!=0 ){
pc = pOp->p2 - 1;
assert( pC->rowidIsValid==0 );
- pC->seekResult = 0;
}
+ pC->seekResult = res;
break;
}
/* Opcode: Sequence P1 P2 * * *
+** Synopsis: r[P2]=cursor[P1].ctr++
**
** Find the next available sequence number for cursor P1.
** Write the sequence number into register P2.
@@ -3840,6 +3953,7 @@ case OP_Sequence: { /* out2-prerelease */
/* Opcode: NewRowid P1 P2 P3 * *
+** Synopsis: r[P2]=rowid
**
** 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
@@ -3895,59 +4009,54 @@ case OP_NewRowid: { /* out2-prerelease */
#endif
if( !pC->useRandomRowid ){
- v = sqlite3BtreeGetCachedRowid(pC->pCursor);
- if( v==0 ){
- rc = sqlite3BtreeLast(pC->pCursor, &res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- if( res ){
- v = 1; /* IMP: R-61914-48074 */
+ rc = sqlite3BtreeLast(pC->pCursor, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( res ){
+ v = 1; /* IMP: R-61914-48074 */
+ }else{
+ assert( sqlite3BtreeCursorIsValid(pC->pCursor) );
+ rc = sqlite3BtreeKeySize(pC->pCursor, &v);
+ assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
+ if( v>=MAX_ROWID ){
+ pC->useRandomRowid = 1;
}else{
- assert( sqlite3BtreeCursorIsValid(pC->pCursor) );
- rc = sqlite3BtreeKeySize(pC->pCursor, &v);
- assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
- if( v>=MAX_ROWID ){
- pC->useRandomRowid = 1;
- }else{
- v++; /* IMP: R-29538-34987 */
- }
+ v++; /* IMP: R-29538-34987 */
}
}
+ }
#ifndef SQLITE_OMIT_AUTOINCREMENT
- if( pOp->p3 ){
+ if( pOp->p3 ){
+ /* Assert that P3 is a valid memory cell. */
+ assert( pOp->p3>0 );
+ if( p->pFrame ){
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
/* Assert that P3 is a valid memory cell. */
- assert( pOp->p3>0 );
- if( p->pFrame ){
- for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
- /* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=pFrame->nMem );
- pMem = &pFrame->aMem[pOp->p3];
- }else{
- /* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=p->nMem );
- pMem = &aMem[pOp->p3];
- memAboutToChange(p, pMem);
- }
- assert( memIsValid(pMem) );
-
- REGISTER_TRACE(pOp->p3, pMem);
- sqlite3VdbeMemIntegerify(pMem);
- assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
- if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){
- rc = SQLITE_FULL; /* IMP: R-12275-61338 */
- goto abort_due_to_error;
- }
- if( v<pMem->u.i+1 ){
- v = pMem->u.i + 1;
- }
- pMem->u.i = v;
+ assert( pOp->p3<=pFrame->nMem );
+ pMem = &pFrame->aMem[pOp->p3];
+ }else{
+ /* Assert that P3 is a valid memory cell. */
+ assert( pOp->p3<=(p->nMem-p->nCursor) );
+ pMem = &aMem[pOp->p3];
+ memAboutToChange(p, pMem);
}
-#endif
+ assert( memIsValid(pMem) );
- sqlite3BtreeSetCachedRowid(pC->pCursor, v<MAX_ROWID ? v+1 : 0);
+ REGISTER_TRACE(pOp->p3, pMem);
+ sqlite3VdbeMemIntegerify(pMem);
+ assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
+ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){
+ rc = SQLITE_FULL; /* IMP: R-12275-61338 */
+ goto abort_due_to_error;
+ }
+ if( v<pMem->u.i+1 ){
+ v = pMem->u.i + 1;
+ }
+ pMem->u.i = v;
}
+#endif
if( pC->useRandomRowid ){
/* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the
** largest possible integer (9223372036854775807) then the database
@@ -3989,6 +4098,7 @@ case OP_NewRowid: { /* out2-prerelease */
}
/* Opcode: Insert P1 P2 P3 P4 P5
+** Synopsis: intkey=r[P3] data=r[P2]
**
** 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
@@ -4028,6 +4138,7 @@ case OP_NewRowid: { /* out2-prerelease */
** for indices is OP_IdxInsert.
*/
/* Opcode: InsertInt P1 P2 P3 P4 P5
+** Synopsis: intkey=P3 data=r[P2]
**
** This works exactly like OP_Insert except that the key is the
** integer value P3, not the value of the integer stored in register P3.
@@ -4079,10 +4190,9 @@ case OP_InsertInt: {
}else{
nZero = 0;
}
- sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
pData->z, pData->n, nZero,
- pOp->p5 & OPFLAG_APPEND, seekResult
+ (pOp->p5 & OPFLAG_APPEND)!=0, seekResult
);
pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
@@ -4107,7 +4217,7 @@ case OP_InsertInt: {
** The cursor will be left pointing at either the next or the previous
** record in the table. If it is left pointing at the next record, then
** the next Next instruction will be a no-op. Hence it is OK to delete
-** a record from within an Next loop.
+** a record from within a Next loop.
**
** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
** incremented (otherwise not).
@@ -4124,20 +4234,11 @@ case OP_Delete: {
i64 iKey;
VdbeCursor *pC;
- iKey = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
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->p4.z ){
- assert( pC->isTable );
- assert( pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */
- iKey = pC->lastRowid;
- }
+ iKey = pC->lastRowid; /* Only used for the update hook */
/* The OP_Delete opcode always follows an OP_NotExists or OP_Last or
** OP_Column on the same table without any intervening operations that
@@ -4150,15 +4251,13 @@ case OP_Delete: {
rc = sqlite3VdbeCursorMoveto(pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
- sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
rc = sqlite3BtreeDelete(pC->pCursor);
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;
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, iKey);
+ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z && pC->isTable ){
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE,
+ db->aDb[pC->iDb].zName, pOp->p4.z, iKey);
assert( pC->iDb>=0 );
}
if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
@@ -4177,21 +4276,33 @@ case OP_ResetCount: {
break;
}
-/* Opcode: SorterCompare P1 P2 P3
+/* Opcode: SorterCompare P1 P2 P3 P4
+** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
+**
+** P1 is a sorter cursor. This instruction compares a prefix of the
+** record blob in register P3 against a prefix of the entry that
+** the sorter cursor currently points to. Only the first P4 fields
+** of r[P3] and the sorter record are compared.
+**
+** If either P3 or the sorter contains a NULL in one of their significant
+** fields (not counting the P4 fields at the end which are ignored) then
+** the comparison is assumed to be equal.
**
-** P1 is a sorter cursor. This instruction compares the record blob in
-** register P3 with the entry that the sorter cursor currently points to.
-** If, excluding the rowid fields at the end, the two records are a match,
-** fall through to the next instruction. Otherwise, jump to instruction P2.
+** Fall through to next instruction if the two records compare equal to
+** each other. Jump to P2 if they are different.
*/
case OP_SorterCompare: {
VdbeCursor *pC;
int res;
+ int nKeyCol;
pC = p->apCsr[pOp->p1];
assert( isSorter(pC) );
+ assert( pOp->p4type==P4_INT32 );
pIn3 = &aMem[pOp->p3];
- rc = sqlite3VdbeSorterCompare(pC, pIn3, &res);
+ nKeyCol = pOp->p4.i;
+ rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res);
+ VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2-1;
}
@@ -4199,6 +4310,7 @@ case OP_SorterCompare: {
};
/* Opcode: SorterData P1 P2 * * *
+** Synopsis: r[P2]=data
**
** Write into register P2 the current sorter data for sorter cursor P1.
*/
@@ -4207,12 +4319,14 @@ case OP_SorterData: {
pOut = &aMem[pOp->p2];
pC = p->apCsr[pOp->p1];
- assert( pC->isSorter );
+ assert( isSorter(pC) );
rc = sqlite3VdbeSorterRowkey(pC, pOut);
+ assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
break;
}
/* Opcode: RowData P1 P2 * * *
+** Synopsis: r[P2]=data
**
** Write into register P2 the complete row data for cursor P1.
** There is no interpretation of the data.
@@ -4223,10 +4337,11 @@ case OP_SorterData: {
** of a real table, not a pseudo-table.
*/
/* Opcode: RowKey P1 P2 * * *
+** Synopsis: r[P2]=key
**
** 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
+** The key is copied onto the P2 register exactly as
** it is found in the database file.
**
** If the P1 cursor must be pointing to a valid row (not a NULL row)
@@ -4245,9 +4360,9 @@ case OP_RowData: {
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
- assert( pC->isSorter==0 );
+ assert( isSorter(pC)==0 );
assert( pC->isTable || pOp->opcode!=OP_RowData );
- assert( pC->isIndex || pOp->opcode==OP_RowData );
+ assert( pC->isTable==0 || pOp->opcode==OP_RowData );
assert( pC!=0 );
assert( pC->nullRow==0 );
assert( pC->pseudoTableReg==0 );
@@ -4264,7 +4379,7 @@ case OP_RowData: {
rc = sqlite3VdbeCursorMoveto(pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
- if( pC->isIndex ){
+ if( pC->isTable==0 ){
assert( !pC->isTable );
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &n64);
assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
@@ -4284,17 +4399,19 @@ case OP_RowData: {
}
pOut->n = n;
MemSetTypeFlag(pOut, MEM_Blob);
- if( pC->isIndex ){
+ if( pC->isTable==0 ){
rc = sqlite3BtreeKey(pCrsr, 0, n, pOut->z);
}else{
rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z);
}
pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
UPDATE_MAX_BLOBSIZE(pOut);
+ REGISTER_TRACE(pOp->p2, pOut);
break;
}
/* Opcode: Rowid P1 P2 * * *
+** Synopsis: r[P2]=rowid
**
** Store in register P2 an integer which is the key of the table entry that
** P1 is currently point to.
@@ -4324,7 +4441,7 @@ case OP_Rowid: { /* out2-prerelease */
pModule = pVtab->pModule;
assert( pModule->xRowid );
rc = pModule->xRowid(pC->pVtabCursor, &v);
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
assert( pC->pCursor!=0 );
@@ -4355,7 +4472,7 @@ case OP_NullRow: {
assert( pC!=0 );
pC->nullRow = 1;
pC->rowidIsValid = 0;
- assert( pC->pCursor || pC->pVtabCursor );
+ pC->cacheStatus = CACHE_STALE;
if( pC->pCursor ){
sqlite3BtreeClearCursor(pC->pCursor);
}
@@ -4364,11 +4481,15 @@ case OP_NullRow: {
/* Opcode: Last P1 P2 * * *
**
-** The next use of the Rowid or Column or Next instruction for P1
+** The next use of the Rowid or Column or Prev instruction for P1
** will refer to the last entry in the database table or index.
** If the table or index is empty and P2>0, then jump immediately to P2.
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
+**
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
*/
case OP_Last: { /* jump */
VdbeCursor *pC;
@@ -4380,15 +4501,18 @@ case OP_Last: { /* jump */
assert( pC!=0 );
pCrsr = pC->pCursor;
res = 0;
- if( ALWAYS(pCrsr!=0) ){
- rc = sqlite3BtreeLast(pCrsr, &res);
- }
+ assert( pCrsr!=0 );
+ rc = sqlite3BtreeLast(pCrsr, &res);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
pC->rowidIsValid = 0;
pC->cacheStatus = CACHE_STALE;
- if( pOp->p2>0 && res ){
- pc = pOp->p2 - 1;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = OP_Last;
+#endif
+ if( pOp->p2>0 ){
+ VdbeBranchTaken(res!=0,2);
+ if( res ) pc = pOp->p2 - 1;
}
break;
}
@@ -4412,7 +4536,7 @@ case OP_Sort: { /* jump */
sqlite3_sort_count++;
sqlite3_search_count--;
#endif
- p->aCounter[SQLITE_STMTSTATUS_SORT-1]++;
+ p->aCounter[SQLITE_STMTSTATUS_SORT]++;
/* Fall through into OP_Rewind */
}
/* Opcode: Rewind P1 P2 * * *
@@ -4422,6 +4546,10 @@ case OP_Sort: { /* jump */
** If the table or index is empty and P2>0, then jump immediately to P2.
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
+**
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
*/
case OP_Rewind: { /* jump */
VdbeCursor *pC;
@@ -4431,35 +4559,48 @@ case OP_Rewind: { /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->isSorter==(pOp->opcode==OP_SorterSort) );
+ assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
res = 1;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = OP_Rewind;
+#endif
if( isSorter(pC) ){
rc = sqlite3VdbeSorterRewind(db, pC, &res);
}else{
pCrsr = pC->pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
- pC->atFirst = res==0 ?1:0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
pC->rowidIsValid = 0;
}
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
+ VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: Next P1 P2 * P4 P5
+/* Opcode: Next P1 P2 P3 P4 P5
**
** 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.
+** The Next opcode is only valid following an SeekGT, SeekGE, or
+** OP_Rewind opcode used to position the cursor. Next is not allowed
+** to follow SeekLT, SeekLE, or OP_Last.
+**
+** The P1 cursor must be for a real table, not a pseudo-table. P1 must have
+** been opened prior to this opcode or the program will segfault.
+**
+** The P3 value is a hint to the btree implementation. If P3==1, that
+** means P1 is an SQL index and that this instruction could have been
+** omitted if that index had been unique. P3 is usually 0. P3 is
+** always either 0 or 1.
**
** P4 is always of type P4_ADVANCE. The function pointer points to
** sqlite3BtreeNext().
@@ -4467,16 +4608,32 @@ case OP_Rewind: { /* jump */
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
**
-** See also: Prev
+** See also: Prev, NextIfOpen
+*/
+/* Opcode: NextIfOpen P1 P2 P3 P4 P5
+**
+** This opcode works just like Next except that if cursor P1 is not
+** open it behaves a no-op.
*/
-/* Opcode: Prev P1 P2 * * P5
+/* Opcode: Prev P1 P2 P3 P4 P5
**
** 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.
+**
+** The Prev opcode is only valid following an SeekLT, SeekLE, or
+** OP_Last opcode used to position the cursor. Prev is not allowed
+** to follow SeekGT, SeekGE, or OP_Rewind.
+**
+** The P1 cursor must be for a real table, not a pseudo-table. If P1 is
+** not open then the behavior is undefined.
+**
+** The P3 value is a hint to the btree implementation. If P3==1, that
+** means P1 is an SQL index and that this instruction could have been
+** omitted if that index had been unique. P3 is usually 0. P3 is
+** always either 0 or 1.
**
** P4 is always of type P4_ADVANCE. The function pointer points to
** sqlite3BtreePrevious().
@@ -4484,45 +4641,69 @@ case OP_Rewind: { /* jump */
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
*/
-case OP_SorterNext: /* jump */
-case OP_Prev: /* jump */
-case OP_Next: { /* jump */
+/* Opcode: PrevIfOpen P1 P2 P3 P4 P5
+**
+** This opcode works just like Prev except that if cursor P1 is not
+** open it behaves a no-op.
+*/
+case OP_SorterNext: { /* jump */
VdbeCursor *pC;
int res;
- CHECK_FOR_INTERRUPT;
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ res = 0;
+ rc = sqlite3VdbeSorterNext(db, pC, &res);
+ goto next_tail;
+case OP_PrevIfOpen: /* jump */
+case OP_NextIfOpen: /* jump */
+ if( p->apCsr[pOp->p1]==0 ) break;
+ /* Fall through */
+case OP_Prev: /* jump */
+case OP_Next: /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<=ArraySize(p->aCounter) );
+ assert( pOp->p5<ArraySize(p->aCounter) );
pC = p->apCsr[pOp->p1];
- if( pC==0 ){
- break; /* See ticket #2273 */
- }
- assert( pC->isSorter==(pOp->opcode==OP_SorterNext) );
- if( isSorter(pC) ){
- assert( pOp->opcode==OP_SorterNext );
- rc = sqlite3VdbeSorterNext(db, pC, &res);
- }else{
- res = 1;
- assert( pC->deferredMoveto==0 );
- assert( pC->pCursor );
- assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
- assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
- rc = pOp->p4.xAdvance(pC->pCursor, &res);
- }
- pC->nullRow = (u8)res;
+ res = pOp->p3;
+ assert( pC!=0 );
+ assert( pC->deferredMoveto==0 );
+ assert( pC->pCursor );
+ assert( res==0 || (res==1 && pC->isTable==0) );
+ testcase( res==1 );
+ assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
+ assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious);
+
+ /* The Next opcode is only used after SeekGT, SeekGE, and Rewind.
+ ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
+ assert( pOp->opcode!=OP_Next || pOp->opcode!=OP_NextIfOpen
+ || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found);
+ assert( pOp->opcode!=OP_Prev || pOp->opcode!=OP_PrevIfOpen
+ || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
+ || pC->seekOp==OP_Last );
+
+ rc = pOp->p4.xAdvance(pC->pCursor, &res);
+next_tail:
pC->cacheStatus = CACHE_STALE;
+ VdbeBranchTaken(res==0,2);
if( res==0 ){
+ pC->nullRow = 0;
pc = pOp->p2 - 1;
- if( pOp->p5 ) p->aCounter[pOp->p5-1]++;
+ p->aCounter[pOp->p5]++;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
+ }else{
+ pC->nullRow = 1;
}
pC->rowidIsValid = 0;
- break;
+ goto check_for_interrupt;
}
/* Opcode: IdxInsert P1 P2 P3 * P5
+** Synopsis: key=r[P2]
**
** Register P2 holds an SQL index key made using the
** MakeRecord instructions. This opcode writes that key
@@ -4531,6 +4712,14 @@ case OP_Next: { /* jump */
** P3 is a flag that provides a hint to the b-tree layer that this
** insert is likely to be an append.
**
+** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is
+** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear,
+** then the change counter is unchanged.
+**
+** If P5 has the OPFLAG_USESEEKRESULT bit set, then the cursor must have
+** just done a seek to the spot where the new entry is to be inserted.
+** This flag avoids doing an extra seek.
+**
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
@@ -4544,31 +4733,32 @@ case OP_IdxInsert: { /* in2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
- assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) );
+ assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
pCrsr = pC->pCursor;
- if( ALWAYS(pCrsr!=0) ){
- assert( pC->isTable==0 );
- rc = ExpandBlob(pIn2);
- if( rc==SQLITE_OK ){
- if( isSorter(pC) ){
- rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
- }else{
- nKey = pIn2->n;
- zKey = pIn2->z;
- rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
- ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
- );
- assert( pC->deferredMoveto==0 );
- pC->cacheStatus = CACHE_STALE;
- }
+ if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
+ assert( pCrsr!=0 );
+ assert( pC->isTable==0 );
+ rc = ExpandBlob(pIn2);
+ if( rc==SQLITE_OK ){
+ if( isSorter(pC) ){
+ rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
+ }else{
+ nKey = pIn2->n;
+ zKey = pIn2->z;
+ rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
+ ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
+ );
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
}
}
break;
}
/* Opcode: IdxDelete P1 P2 P3 * *
+** Synopsis: key=r[P2@P3]
**
** The content of P3 registers starting at register P2 form
** an unpacked index key. This opcode removes that entry from the
@@ -4581,30 +4771,31 @@ case OP_IdxDelete: {
UnpackedRecord r;
assert( pOp->p3>0 );
- assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
+ assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem-p->nCursor)+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->pCursor;
- if( ALWAYS(pCrsr!=0) ){
- r.pKeyInfo = pC->pKeyInfo;
- r.nField = (u16)pOp->p3;
- r.flags = 0;
- r.aMem = &aMem[pOp->p2];
+ assert( pCrsr!=0 );
+ assert( pOp->p5==0 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p3;
+ r.default_rc = 0;
+ r.aMem = &aMem[pOp->p2];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
- rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
- if( rc==SQLITE_OK && res==0 ){
- rc = sqlite3BtreeDelete(pCrsr);
- }
- assert( pC->deferredMoveto==0 );
- pC->cacheStatus = CACHE_STALE;
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
+ if( rc==SQLITE_OK && res==0 ){
+ rc = sqlite3BtreeDelete(pCrsr);
}
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
break;
}
/* Opcode: IdxRowid P1 P2 * * *
+** Synopsis: r[P2]=rowid
**
** 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
@@ -4621,52 +4812,72 @@ case OP_IdxRowid: { /* out2-prerelease */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->pCursor;
+ assert( pCrsr!=0 );
pOut->flags = MEM_Null;
- if( ALWAYS(pCrsr!=0) ){
- rc = sqlite3VdbeCursorMoveto(pC);
- if( NEVER(rc) ) goto abort_due_to_error;
- assert( pC->deferredMoveto==0 );
- assert( pC->isTable==0 );
- if( !pC->nullRow ){
- rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- pOut->u.i = rowid;
- pOut->flags = MEM_Int;
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( NEVER(rc) ) goto abort_due_to_error;
+ assert( pC->deferredMoveto==0 );
+ assert( pC->isTable==0 );
+ if( !pC->nullRow ){
+ rowid = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
}
+ pOut->u.i = rowid;
+ pOut->flags = MEM_Int;
}
break;
}
/* Opcode: IdxGE P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
**
** 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.
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** fields at the end.
**
** 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.
+*/
+/* Opcode: IdxGT P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
+**
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** fields at the end.
**
-** 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.
+** If the P1 index entry is greater than the key value
+** then jump to P2. Otherwise fall through to the next instruction.
*/
/* Opcode: IdxLT P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
**
** 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.
+** key that omits the PRIMARY KEY or ROWID. Compare this key value against
+** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
+** ROWID on the P1 index.
**
** If the P1 index entry is less than the key value then jump to P2.
** Otherwise fall through to the next instruction.
+*/
+/* Opcode: IdxLE P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
+**
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY or ROWID. Compare this key value against
+** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
+** ROWID on the P1 index.
**
-** 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.
+** If the P1 index entry is less than or equal to the key value then jump
+** to P2. Otherwise fall through to the next instruction.
*/
+case OP_IdxLE: /* jump */
+case OP_IdxGT: /* jump */
case OP_IdxLT: /* jump */
-case OP_IdxGE: { /* jump */
+case OP_IdxGE: { /* jump */
VdbeCursor *pC;
int res;
UnpackedRecord r;
@@ -4675,31 +4886,36 @@ case OP_IdxGE: { /* jump */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->isOrdered );
- if( ALWAYS(pC->pCursor!=0) ){
- assert( pC->deferredMoveto==0 );
- assert( pOp->p5==0 || pOp->p5==1 );
- assert( pOp->p4type==P4_INT32 );
- r.pKeyInfo = pC->pKeyInfo;
- r.nField = (u16)pOp->p4.i;
- if( pOp->p5 ){
- r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH;
- }else{
- r.flags = UNPACKED_PREFIX_MATCH;
- }
- r.aMem = &aMem[pOp->p3];
+ assert( pC->pCursor!=0);
+ assert( pC->deferredMoveto==0 );
+ assert( pOp->p5==0 || pOp->p5==1 );
+ assert( pOp->p4type==P4_INT32 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p4.i;
+ if( pOp->opcode<OP_IdxLT ){
+ assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxGT );
+ r.default_rc = -1;
+ }else{
+ assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT );
+ r.default_rc = 0;
+ }
+ r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+ { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
- rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res);
- if( pOp->opcode==OP_IdxLT ){
- res = -res;
- }else{
- assert( pOp->opcode==OP_IdxGE );
- res++;
- }
- if( res>0 ){
- pc = pOp->p2 - 1 ;
- }
+ res = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3VdbeIdxKeyCompare(pC, &r, &res);
+ assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) );
+ if( (pOp->opcode&1)==(OP_IdxLT&1) ){
+ assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT );
+ res = -res;
+ }else{
+ assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxGT );
+ res++;
+ }
+ VdbeBranchTaken(res>0,2);
+ if( res>0 ){
+ pc = pOp->p2 - 1 ;
}
break;
}
@@ -4730,15 +4946,18 @@ case OP_Destroy: { /* out2-prerelease */
Vdbe *pVdbe;
int iDb;
+ assert( p->readOnly==0 );
#ifndef SQLITE_OMIT_VIRTUALTABLE
iCnt = 0;
for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){
- if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 ){
+ if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader
+ && pVdbe->inVtabMethod<2 && pVdbe->pc>=0
+ ){
iCnt++;
}
}
#else
- iCnt = db->activeVdbeCnt;
+ iCnt = db->nVdbeRead;
#endif
pOut->flags = MEM_Null;
if( iCnt>1 ){
@@ -4747,7 +4966,8 @@ case OP_Destroy: { /* out2-prerelease */
}else{
iDb = pOp->p3;
assert( iCnt==1 );
- assert( (p->btreeMask & (((yDbMask)1)<<iDb))!=0 );
+ assert( DbMaskTest(p->btreeMask, iDb) );
+ iMoved = 0; /* Not needed. Only to silence a warning. */
rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
pOut->flags = MEM_Int;
pOut->u.i = iMoved;
@@ -4785,7 +5005,8 @@ case OP_Clear: {
int nChange;
nChange = 0;
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
+ assert( p->readOnly==0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p2) );
rc = sqlite3BtreeClearTable(
db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
);
@@ -4800,7 +5021,31 @@ case OP_Clear: {
break;
}
+/* Opcode: ResetSorter P1 * * * *
+**
+** Delete all contents from the ephemeral table or sorter
+** that is open on cursor P1.
+**
+** This opcode only works for cursors used for sorting and
+** opened with OP_OpenEphemeral or OP_SorterOpen.
+*/
+case OP_ResetSorter: {
+ VdbeCursor *pC;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ if( pC->pSorter ){
+ sqlite3VdbeSorterReset(db, pC->pSorter);
+ }else{
+ assert( pC->isEphemeral );
+ rc = sqlite3BtreeClearTableOfCursor(pC->pCursor);
+ }
+ break;
+}
+
/* Opcode: CreateTable P1 P2 * * *
+** Synopsis: r[P2]=root iDb=P1
**
** 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
@@ -4814,6 +5059,7 @@ case OP_Clear: {
** See also: CreateIndex
*/
/* Opcode: CreateIndex P1 P2 * * *
+** Synopsis: r[P2]=root iDb=P1
**
** 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
@@ -4830,7 +5076,8 @@ case OP_CreateTable: { /* out2-prerelease */
pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
@@ -4917,7 +5164,8 @@ case OP_LoadAnalysis: {
**
** Remove the internal (in-memory) data structures that describe
** the table named P4 in database P1. This is called after a table
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode) in order to keep
+** the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropTable: {
@@ -4929,7 +5177,8 @@ case OP_DropTable: {
**
** Remove the internal (in-memory) data structures that describe
** the index named P4 in database P1. This is called after an index
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode)
+** in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropIndex: {
@@ -4941,7 +5190,8 @@ case OP_DropIndex: {
**
** Remove the internal (in-memory) data structures that describe
** the trigger named P4 in database P1. This is called after a trigger
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode) in order to keep
+** the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropTrigger: {
@@ -4978,12 +5228,13 @@ case OP_IntegrityCk: {
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
-
+
+ assert( p->bIsReader );
nRoot = pOp->p2;
assert( nRoot>0 );
aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
if( aRoot==0 ) goto no_mem;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pnErr = &aMem[pOp->p3];
assert( (pnErr->flags & MEM_Int)!=0 );
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
@@ -4993,7 +5244,7 @@ case OP_IntegrityCk: {
}
aRoot[j] = 0;
assert( pOp->p5<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p5) );
z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
(int)pnErr->u.i, &nErr);
sqlite3DbFree(db, aRoot);
@@ -5013,6 +5264,7 @@ case OP_IntegrityCk: {
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/* Opcode: RowSetAdd P1 P2 * * *
+** Synopsis: rowset(P1)=r[P2]
**
** Insert the integer value held by register P2 into a boolean index
** held in register P1.
@@ -5032,6 +5284,7 @@ case OP_RowSetAdd: { /* in1, in2 */
}
/* Opcode: RowSetRead P1 P2 P3 * *
+** Synopsis: r[P3]=rowset(P1)
**
** Extract the smallest value from boolean index P1 and put that value into
** register P3. Or, if boolean index P1 is initially empty, leave P3
@@ -5039,7 +5292,7 @@ case OP_RowSetAdd: { /* in1, in2 */
*/
case OP_RowSetRead: { /* jump, in1, out3 */
i64 val;
- CHECK_FOR_INTERRUPT;
+
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_RowSet)==0
|| sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0
@@ -5047,14 +5300,17 @@ case OP_RowSetRead: { /* jump, in1, out3 */
/* The boolean index is empty */
sqlite3VdbeMemSetNull(pIn1);
pc = pOp->p2 - 1;
+ VdbeBranchTaken(1,2);
}else{
/* A value was pulled from the index */
sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val);
+ VdbeBranchTaken(0,2);
}
- break;
+ goto check_for_interrupt;
}
/* Opcode: RowSetTest P1 P2 P3 P4
+** Synopsis: if r[P3] in rowset(P1) goto P2
**
** Register P3 is assumed to hold a 64-bit integer value. If register P1
** contains a RowSet object and that RowSet object contains
@@ -5097,9 +5353,8 @@ case OP_RowSetTest: { /* jump, in1, in3 */
assert( pOp->p4type==P4_INT32 );
assert( iSet==-1 || iSet>=0 );
if( iSet ){
- exists = sqlite3RowSetTest(pIn1->u.pRowSet,
- (u8)(iSet>=0 ? iSet & 0xf : 0xff),
- pIn3->u.i);
+ exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i);
+ VdbeBranchTaken(exists!=0,2);
if( exists ){
pc = pOp->p2 - 1;
break;
@@ -5114,7 +5369,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */
#ifndef SQLITE_OMIT_TRIGGER
-/* Opcode: Program P1 P2 P3 P4 *
+/* Opcode: Program P1 P2 P3 P4 P5
**
** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
**
@@ -5126,6 +5381,8 @@ case OP_RowSetTest: { /* jump, in1, in3 */
** memory required by the sub-vdbe at runtime.
**
** P4 is a pointer to the VM containing the trigger program.
+**
+** If P5 is non-zero, then recursive program invocation is enabled.
*/
case OP_Program: { /* jump */
int nMem; /* Number of memory registers for sub-program */
@@ -5203,7 +5460,7 @@ case OP_Program: { /* jump */
pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
- pMem->flags = MEM_Invalid;
+ pMem->flags = MEM_Undefined;
pMem->db = db;
}
}else{
@@ -5258,6 +5515,7 @@ case OP_Param: { /* out2-prerelease */
#ifndef SQLITE_OMIT_FOREIGN_KEY
/* Opcode: FkCounter P1 P2 * * *
+** Synopsis: fkctr[P1]+=P2
**
** Increment a "constraint counter" by P2 (P2 may be negative or positive).
** If P1 is non-zero, the database constraint counter is incremented
@@ -5265,7 +5523,9 @@ case OP_Param: { /* out2-prerelease */
** statement counter is incremented (immediate foreign key constraints).
*/
case OP_FkCounter: {
- if( pOp->p1 ){
+ if( db->flags & SQLITE_DeferFKs ){
+ db->nDeferredImmCons += pOp->p2;
+ }else if( pOp->p1 ){
db->nDeferredCons += pOp->p2;
}else{
p->nFkConstraint += pOp->p2;
@@ -5274,6 +5534,7 @@ case OP_FkCounter: {
}
/* Opcode: FkIfZero P1 P2 * * *
+** Synopsis: if fkctr[P1]==0 goto P2
**
** This opcode tests if a foreign key constraint-counter is currently zero.
** If so, jump to instruction P2. Otherwise, fall through to the next
@@ -5286,9 +5547,11 @@ case OP_FkCounter: {
*/
case OP_FkIfZero: { /* jump */
if( pOp->p1 ){
- if( db->nDeferredCons==0 ) pc = pOp->p2-1;
+ VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2);
+ if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1;
}else{
- if( p->nFkConstraint==0 ) pc = pOp->p2-1;
+ VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2);
+ if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) pc = pOp->p2-1;
}
break;
}
@@ -5296,6 +5559,7 @@ case OP_FkIfZero: { /* jump */
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* Opcode: MemMax P1 P2 * * *
+** Synopsis: r[P1]=max(r[P1],r[P2])
**
** P1 is a register in the root frame of this VM (the root frame is
** different from the current frame if this instruction is being executed
@@ -5306,7 +5570,6 @@ case OP_FkIfZero: { /* jump */
** an integer.
*/
case OP_MemMax: { /* in2 */
- Mem *pIn1;
VdbeFrame *pFrame;
if( p->pFrame ){
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
@@ -5326,6 +5589,7 @@ case OP_MemMax: { /* in2 */
#endif /* SQLITE_OMIT_AUTOINCREMENT */
/* Opcode: IfPos P1 P2 * * *
+** Synopsis: if r[P1]>0 goto P2
**
** If the value of register P1 is 1 or greater, jump to P2.
**
@@ -5335,22 +5599,24 @@ case OP_MemMax: { /* in2 */
case OP_IfPos: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
+ VdbeBranchTaken( pIn1->u.i>0, 2);
if( pIn1->u.i>0 ){
pc = pOp->p2 - 1;
}
break;
}
-/* Opcode: IfNeg P1 P2 * * *
-**
-** If the value of register P1 is less than zero, jump to P2.
+/* Opcode: IfNeg P1 P2 P3 * *
+** Synopsis: r[P1]+=P3, if r[P1]<0 goto P2
**
-** It is illegal to use this instruction on a register that does
-** not contain an integer. An assertion fault will result if you try.
+** Register P1 must contain an integer. Add literal P3 to the value in
+** register P1 then if the value of register P1 is less than zero, jump to P2.
*/
case OP_IfNeg: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
+ pIn1->u.i += pOp->p3;
+ VdbeBranchTaken(pIn1->u.i<0, 2);
if( pIn1->u.i<0 ){
pc = pOp->p2 - 1;
}
@@ -5358,17 +5624,16 @@ case OP_IfNeg: { /* jump, in1 */
}
/* Opcode: IfZero P1 P2 P3 * *
+** Synopsis: r[P1]+=P3, if r[P1]==0 goto P2
**
** The register P1 must contain an integer. Add literal P3 to the
** value in register P1. If the result is exactly 0, jump to P2.
-**
-** 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_IfZero: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
pIn1->u.i += pOp->p3;
+ VdbeBranchTaken(pIn1->u.i==0, 2);
if( pIn1->u.i==0 ){
pc = pOp->p2 - 1;
}
@@ -5376,6 +5641,7 @@ case OP_IfZero: { /* jump, in1 */
}
/* Opcode: AggStep * P2 P3 P4 P5
+** Synopsis: accum=r[P3] step(r[P2@P5])
**
** Execute the step function for an aggregate. The
** function has P5 arguments. P4 is a pointer to the FuncDef
@@ -5402,10 +5668,9 @@ case OP_AggStep: {
assert( memIsValid(pRec) );
apVal[i] = pRec;
memAboutToChange(p, pRec);
- sqlite3VdbeMemStoreType(pRec);
}
ctx.pFunc = pOp->p4.pFunc;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
ctx.pMem = pMem = &aMem[pOp->p3];
pMem->n++;
ctx.s.flags = MEM_Null;
@@ -5416,7 +5681,7 @@ case OP_AggStep: {
ctx.isError = 0;
ctx.pColl = 0;
ctx.skipFlag = 0;
- if( ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( ctx.pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
assert( pOp>p->aOp );
assert( pOp[-1].p4type==P4_COLLSEQ );
assert( pOp[-1].opcode==OP_CollSeq );
@@ -5439,6 +5704,7 @@ case OP_AggStep: {
}
/* Opcode: AggFinal P1 P2 * P4 *
+** Synopsis: accum=r[P1] N=P2
**
** Execute the finalizer function for an aggregate. P1 is
** the memory location that is the accumulator for the aggregate.
@@ -5452,7 +5718,7 @@ case OP_AggStep: {
*/
case OP_AggFinal: {
Mem *pMem;
- assert( pOp->p1>0 && pOp->p1<=p->nMem );
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem-p->nCursor) );
pMem = &aMem[pOp->p1];
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
@@ -5484,6 +5750,7 @@ case OP_Checkpoint: {
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
+ assert( p->readOnly==0 );
aRes[0] = 0;
aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
@@ -5503,7 +5770,7 @@ case OP_Checkpoint: {
#endif
#ifndef SQLITE_OMIT_PRAGMA
-/* Opcode: JournalMode P1 P2 P3 * P5
+/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
@@ -5533,6 +5800,7 @@ case OP_JournalMode: { /* out2-prerelease */
|| eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( p->readOnly==0 );
pBt = db->aDb[pOp->p1].pBt;
pPager = sqlite3BtreePager(pBt);
@@ -5556,7 +5824,7 @@ case OP_JournalMode: { /* out2-prerelease */
if( (eNew!=eOld)
&& (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
){
- if( !db->autoCommit || db->activeVdbeCnt>1 ){
+ if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db,
"cannot change %s wal mode from within a transaction",
@@ -5615,6 +5883,7 @@ case OP_JournalMode: { /* out2-prerelease */
** a transaction.
*/
case OP_Vacuum: {
+ assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db);
break;
}
@@ -5631,9 +5900,11 @@ case OP_IncrVacuum: { /* jump */
Btree *pBt;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(pBt);
+ VdbeBranchTaken(rc==SQLITE_DONE,2);
if( rc==SQLITE_DONE ){
pc = pOp->p2 - 1;
rc = SQLITE_OK;
@@ -5644,12 +5915,13 @@ case OP_IncrVacuum: { /* jump */
/* 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
-** (via sqlite3_step()).
+** Cause precompiled statements to expire. When an expired statement
+** is executed using sqlite3_step() it will either automatically
+** reprepare itself (if it was originally created using sqlite3_prepare_v2())
+** or it will fail with SQLITE_SCHEMA.
**
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
-** then only the currently executing statement is affected.
+** then only the currently executing statement is expired.
*/
case OP_Expire: {
if( !pOp->p1 ){
@@ -5662,6 +5934,7 @@ case OP_Expire: {
#ifndef SQLITE_OMIT_SHARED_CACHE
/* Opcode: TableLock P1 P2 P3 P4 *
+** Synopsis: iDb=P1 root=P2 write=P3
**
** Obtain a lock on a particular table. This instruction is only used when
** the shared-cache feature is enabled.
@@ -5680,7 +5953,7 @@ case OP_TableLock: {
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){
int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, p1) );
assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
if( (rc&0xFF)==SQLITE_LOCKED ){
@@ -5706,7 +5979,7 @@ case OP_VBegin: {
VTable *pVTab;
pVTab = pOp->p4.pVtab;
rc = sqlite3VtabBegin(db, pVTab);
- if( pVTab ) importVtabErrMsg(p, pVTab->pVtab);
+ if( pVTab ) sqlite3VtabImportErrmsg(p, pVTab->pVtab);
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -5750,13 +6023,14 @@ case OP_VOpen: {
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
+ assert( p->bIsReader );
pCur = 0;
pVtabCursor = 0;
pVtab = pOp->p4.pVtab->pVtab;
pModule = (sqlite3_module *)pVtab->pModule;
assert(pVtab && pModule);
rc = pModule->xOpen(pVtab, &pVtabCursor);
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( SQLITE_OK==rc ){
/* Initialize sqlite3_vtab_cursor base class */
pVtabCursor->pVtab = pVtab;
@@ -5765,7 +6039,6 @@ case OP_VOpen: {
pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
if( pCur ){
pCur->pVtabCursor = pVtabCursor;
- pCur->pModule = pVtabCursor->pVtab->pModule;
}else{
db->mallocFailed = 1;
pModule->xClose(pVtabCursor);
@@ -5777,6 +6050,7 @@ case OP_VOpen: {
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VFilter P1 P2 P3 P4 *
+** Synopsis: iplan=r[P3] zplan='P4'
**
** P1 is a cursor opened using VOpen. P2 is an address to jump to if
** the filtered result set is empty.
@@ -5828,17 +6102,16 @@ case OP_VFilter: { /* jump */
apArg = p->apArg;
for(i = 0; i<nArg; i++){
apArg[i] = &pArgc[i+1];
- sqlite3VdbeMemStoreType(apArg[i]);
}
p->inVtabMethod = 1;
rc = pModule->xFilter(pVtabCursor, iQuery, pOp->p4.z, nArg, apArg);
p->inVtabMethod = 0;
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( rc==SQLITE_OK ){
res = pModule->xEof(pVtabCursor);
}
-
+ VdbeBranchTaken(res!=0,2);
if( res ){
pc = pOp->p2 - 1;
}
@@ -5851,6 +6124,7 @@ case OP_VFilter: { /* jump */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VColumn P1 P2 P3 * *
+** Synopsis: r[P3]=vcolumn(P2)
**
** Store the value of the P2-th column of
** the row of the virtual-table that the
@@ -5864,7 +6138,7 @@ case OP_VColumn: {
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur->pVtabCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
if( pCur->nullRow ){
@@ -5885,7 +6159,7 @@ case OP_VColumn: {
MemSetTypeFlag(&sContext.s, MEM_Null);
rc = pModule->xColumn(pCur->pVtabCursor, &sContext, pOp->p2);
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( sContext.isError ){
rc = sContext.isError;
}
@@ -5938,16 +6212,16 @@ case OP_VNext: { /* jump */
p->inVtabMethod = 1;
rc = pModule->xNext(pCur->pVtabCursor);
p->inVtabMethod = 0;
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( rc==SQLITE_OK ){
res = pModule->xEof(pCur->pVtabCursor);
}
-
+ VdbeBranchTaken(!res,2);
if( !res ){
/* If there is data, jump to P2 */
pc = pOp->p2 - 1;
}
- break;
+ goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -5966,6 +6240,7 @@ case OP_VRename: {
pName = &aMem[pOp->p1];
assert( pVtab->pModule->xRename );
assert( memIsValid(pName) );
+ assert( p->readOnly==0 );
REGISTER_TRACE(pOp->p1, pName);
assert( pName->flags & MEM_Str );
testcase( pName->enc==SQLITE_UTF8 );
@@ -5974,7 +6249,7 @@ case OP_VRename: {
rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
if( rc==SQLITE_OK ){
rc = pVtab->pModule->xRename(pVtab, pName->z);
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
p->expired = 0;
}
break;
@@ -5982,7 +6257,8 @@ case OP_VRename: {
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VUpdate P1 P2 P3 P4 *
+/* Opcode: VUpdate P1 P2 P3 P4 P5
+** Synopsis: data=r[P3@P2]
**
** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** This opcode invokes the corresponding xUpdate method. P2 values
@@ -6004,6 +6280,9 @@ case OP_VRename: {
** P1 is a boolean flag. If it is set to true and the xUpdate call
** is successful, then the value returned by sqlite3_last_insert_rowid()
** is set to the value of the rowid for the row just inserted.
+**
+** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to
+** apply in the case of a constraint failure on an insert or update.
*/
case OP_VUpdate: {
sqlite3_vtab *pVtab;
@@ -6017,6 +6296,7 @@ case OP_VUpdate: {
assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
+ assert( p->readOnly==0 );
pVtab = pOp->p4.pVtab->pVtab;
pModule = (sqlite3_module *)pVtab->pModule;
nArg = pOp->p2;
@@ -6028,14 +6308,13 @@ case OP_VUpdate: {
for(i=0; i<nArg; i++){
assert( memIsValid(pX) );
memAboutToChange(p, pX);
- sqlite3VdbeMemStoreType(pX);
apArg[i] = pX;
pX++;
}
db->vtabOnConflict = pOp->p5;
rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
db->vtabOnConflict = vtabOnConflict;
- importVtabErrMsg(p, pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( rc==SQLITE_OK && pOp->p1 ){
assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
db->lastRowid = lastRowid = rowid;
@@ -6091,16 +6370,26 @@ case OP_MaxPgcnt: { /* out2-prerelease */
#endif
-#ifndef SQLITE_OMIT_TRACE
-/* Opcode: Trace * * * P4 *
+/* Opcode: Init * P2 * P4 *
+** Synopsis: Start at P2
+**
+** Programs contain a single instance of this opcode as the very first
+** opcode.
**
** If tracing is enabled (by the sqlite3_trace()) interface, then
** the UTF-8 string contained in P4 is emitted on the trace callback.
+** Or if P4 is blank, use the string returned by sqlite3_sql().
+**
+** If P2 is not zero, jump to instruction P2.
*/
-case OP_Trace: {
+case OP_Init: { /* jump */
char *zTrace;
char *z;
+ if( pOp->p2 ){
+ pc = pOp->p2 - 1;
+ }
+#ifndef SQLITE_OMIT_TRACE
if( db->xTrace
&& !p->doingRerun
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
@@ -6109,6 +6398,16 @@ case OP_Trace: {
db->xTrace(db->pTraceArg, z);
sqlite3DbFree(db, z);
}
+#ifdef SQLITE_USE_FCNTL_TRACE
+ zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
+ if( zTrace ){
+ int i;
+ for(i=0; i<db->nDb; i++){
+ if( DbMaskTest(p->btreeMask, i)==0 ) continue;
+ sqlite3_file_control(db, db->aDb[i].zName, SQLITE_FCNTL_TRACE, zTrace);
+ }
+ }
+#endif /* SQLITE_USE_FCNTL_TRACE */
#ifdef SQLITE_DEBUG
if( (db->flags & SQLITE_SqlTrace)!=0
&& (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
@@ -6116,9 +6415,9 @@ case OP_Trace: {
sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
}
#endif /* SQLITE_DEBUG */
+#endif /* SQLITE_OMIT_TRACE */
break;
}
-#endif
/* Opcode: Noop * * * * *
@@ -6147,13 +6446,9 @@ default: { /* This is really OP_Noop and OP_Explain */
#ifdef VDBE_PROFILE
{
- u64 elapsed = sqlite3Hwtime() - start;
- pOp->cycles += elapsed;
+ u64 endTime = sqlite3Hwtime();
+ if( endTime>start ) pOp->cycles += endTime - start;
pOp->cnt++;
-#if 0
- fprintf(stdout, "%10llu ", elapsed);
- sqlite3VdbePrintOp(stdout, origPc, &aOp[origPc]);
-#endif
}
#endif
@@ -6166,13 +6461,13 @@ default: { /* This is really OP_Noop and OP_Explain */
assert( pc>=-1 && pc<p->nOp );
#ifdef SQLITE_DEBUG
- if( p->trace ){
- if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc);
+ if( db->flags & SQLITE_VdbeTrace ){
+ if( rc!=0 ) printf("rc=%d\n",rc);
if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){
- registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]);
+ registerTrace(pOp->p2, &aMem[pOp->p2]);
}
if( pOp->opflags & OPFLG_OUT3 ){
- registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]);
+ registerTrace(pOp->p3, &aMem[pOp->p3]);
}
}
#endif /* SQLITE_DEBUG */
@@ -6200,6 +6495,8 @@ vdbe_error_halt:
** top. */
vdbe_return:
db->lastRowid = lastRowid;
+ testcase( nVmStep>0 );
+ p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
return rc;
diff --git a/src/vdbe.h b/src/vdbe.h
index fa7b31b..ef91010 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -30,7 +30,6 @@ 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 SubProgram SubProgram;
@@ -54,7 +53,6 @@ struct VdbeOp {
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 */
VTable *pVtab; /* Used when p4type is P4_VTAB */
@@ -63,13 +61,16 @@ struct VdbeOp {
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
int (*xAdvance)(BtCursor *, int *);
} p4;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
char *zComment; /* Comment to improve readability */
#endif
#ifdef VDBE_PROFILE
- int cnt; /* Number of times this instruction was executed */
+ u32 cnt; /* Number of times this instruction was executed */
u64 cycles; /* Total time spent executing this instruction */
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ int iSrcLine; /* Source-code line that generated this opcode */
+#endif
};
typedef struct VdbeOp VdbeOp;
@@ -108,7 +109,6 @@ typedef struct VdbeOpList VdbeOpList;
#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 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
@@ -120,15 +120,11 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
-/* 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 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 P4_KEYINFO_HANDOFF (-16)
-#define P4_KEYINFO_STATIC (-17)
+/* Error message codes for OP_Halt */
+#define P5_ConstraintNotNull 1
+#define P5_ConstraintUnique 2
+#define P5_ConstraintCheck 3
+#define P5_ConstraintFK 4
/*
** The Vdbe.aColName array contains 5n Mem structures, where n is the
@@ -167,14 +163,14 @@ typedef struct VdbeOpList VdbeOpList;
** Prototypes for the VDBE interface. See comments on the implementation
** for a description of what each of these routines does.
*/
-Vdbe *sqlite3VdbeCreate(sqlite3*);
+Vdbe *sqlite3VdbeCreate(Parse*);
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 sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
-int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
+int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
@@ -182,7 +178,9 @@ void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
void sqlite3VdbeJumpHere(Vdbe*, int addr);
void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
+int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
+void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
void sqlite3VdbeUsesBtree(Vdbe*, int);
VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
int sqlite3VdbeMakeLabel(Vdbe*);
@@ -195,7 +193,6 @@ void sqlite3VdbeResolveLabel(Vdbe*, int);
int sqlite3VdbeCurrentAddr(Vdbe*);
#ifdef SQLITE_DEBUG
int sqlite3VdbeAssertMayAbort(Vdbe *, int);
- void sqlite3VdbeTrace(Vdbe*,FILE*);
#endif
void sqlite3VdbeResetStepResult(Vdbe*);
void sqlite3VdbeRewind(Vdbe*);
@@ -207,29 +204,82 @@ sqlite3 *sqlite3VdbeDb(Vdbe*);
void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
void sqlite3VdbeSwap(Vdbe*,Vdbe*);
VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
-sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8);
+sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
+int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
-int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
+int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*,int);
UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
+typedef int (*RecordCompare)(int,const void*,UnpackedRecord*,int);
+RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
+
#ifndef SQLITE_OMIT_TRIGGER
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
#endif
-
-#ifndef NDEBUG
+/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
+** each VDBE opcode.
+**
+** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
+** comments in VDBE programs that show key decision points in the code
+** generator.
+*/
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
void sqlite3VdbeComment(Vdbe*, const char*, ...);
# define VdbeComment(X) sqlite3VdbeComment X
void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
# define VdbeNoopComment(X) sqlite3VdbeNoopComment X
+# ifdef SQLITE_ENABLE_MODULE_COMMENTS
+# define VdbeModuleComment(X) sqlite3VdbeNoopComment X
+# else
+# define VdbeModuleComment(X)
+# endif
#else
# define VdbeComment(X)
# define VdbeNoopComment(X)
+# define VdbeModuleComment(X)
+#endif
+
+/*
+** The VdbeCoverage macros are used to set a coverage testing point
+** for VDBE branch instructions. The coverage testing points are line
+** numbers in the sqlite3.c source file. VDBE branch coverage testing
+** only works with an amalagmation build. That's ok since a VDBE branch
+** coverage build designed for testing the test suite only. No application
+** should ever ship with VDBE branch coverage measuring turned on.
+**
+** VdbeCoverage(v) // Mark the previously coded instruction
+** // as a branch
+**
+** VdbeCoverageIf(v, conditional) // Mark previous if conditional true
+**
+** VdbeCoverageAlwaysTaken(v) // Previous branch is always taken
+**
+** VdbeCoverageNeverTaken(v) // Previous branch is never taken
+**
+** Every VDBE branch operation must be tagged with one of the macros above.
+** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and
+** -DSQLITE_DEBUG then an ALWAYS() will fail in the vdbeTakeBranch()
+** routine in vdbe.c, alerting the developer to the missed tag.
+*/
+#ifdef SQLITE_VDBE_COVERAGE
+ void sqlite3VdbeSetLineNumber(Vdbe*,int);
+# define VdbeCoverage(v) sqlite3VdbeSetLineNumber(v,__LINE__)
+# define VdbeCoverageIf(v,x) if(x)sqlite3VdbeSetLineNumber(v,__LINE__)
+# define VdbeCoverageAlwaysTaken(v) sqlite3VdbeSetLineNumber(v,2);
+# define VdbeCoverageNeverTaken(v) sqlite3VdbeSetLineNumber(v,1);
+# define VDBE_OFFSET_LINENO(x) (__LINE__+x)
+#else
+# define VdbeCoverage(v)
+# define VdbeCoverageIf(v,x)
+# define VdbeCoverageAlwaysTaken(v)
+# define VdbeCoverageNeverTaken(v)
+# define VDBE_OFFSET_LINENO(x) 0
#endif
#endif
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 3a5b402..141573e 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -36,7 +36,7 @@ typedef struct VdbeOp Op;
/*
** Boolean values
*/
-typedef unsigned char Bool;
+typedef unsigned Bool;
/* Opaque type used by code in vdbesort.c */
typedef struct VdbeSorter VdbeSorter;
@@ -44,12 +44,18 @@ typedef struct VdbeSorter VdbeSorter;
/* Opaque type used by the explainer */
typedef struct Explain Explain;
+/* Elements of the linked list at Vdbe.pAuxData */
+typedef struct AuxData AuxData;
+
/*
** A cursor is a pointer into a single BTree within a database file.
** The cursor can seek to a BTree entry with a particular key, or
** loop over all entries of the Btree. You can also insert new BTree
** entries or retrieve the key or data from the entry that the cursor
** is currently pointing to.
+**
+** Cursors can also point to virtual tables, sorters, or "pseudo-tables".
+** A pseudo-table is a single-row table implemented by registers.
**
** Every cursor that the virtual machine has open is represented by an
** instance of the following structure.
@@ -58,31 +64,28 @@ struct VdbeCursor {
BtCursor *pCursor; /* The cursor structure of the backend */
Btree *pBt; /* Separate file holding temporary table */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
- int iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ int seekResult; /* Result of previous sqlite3BtreeMoveto() */
int pseudoTableReg; /* Register holding pseudotable content. */
- int nField; /* Number of fields in the header */
- Bool zeroed; /* True if zeroed out and ready for reuse */
- Bool rowidIsValid; /* True if lastRowid is valid */
- Bool atFirst; /* True if pointing to first entry */
- Bool useRandomRowid; /* Generate new record numbers semi-randomly */
- Bool nullRow; /* True if pointing to a row with no data */
- 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 */
- Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
- Bool isSorter; /* True if a new-style sorter */
- Bool multiPseudo; /* Multi-register pseudo-cursor */
+ i16 nField; /* Number of fields in the header */
+ u16 nHdrParsed; /* Number of header fields parsed so far */
+#ifdef SQLITE_DEBUG
+ u8 seekOp; /* Most recent seek operation on this cursor */
+#endif
+ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ u8 nullRow; /* True if pointing to a row with no data */
+ u8 rowidIsValid; /* True if lastRowid is valid */
+ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
+ Bool isEphemeral:1; /* True for an ephemeral table */
+ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
+ Bool isTable:1; /* True if a table requiring integer keys */
+ Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */
+ Pgno pgnoRoot; /* Root page of the open btree cursor */
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
- const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
- i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
+ i64 lastRowid; /* Rowid being deleted by OP_Delete */
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
- /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
- ** OP_IsUnique opcode on this cursor. */
- int seekResult;
-
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
** Vdbe.cacheCtr. Vdbe.cacheCtr will never take on the value of
@@ -93,10 +96,14 @@ struct VdbeCursor {
** be NULL.
*/
u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
- int payloadSize; /* Total number of bytes in the record */
- u32 *aType; /* Type values for all entries in the record */
- u32 *aOffset; /* Cached offsets to the start of each columns data */
- u8 *aRow; /* Data for the current row, if all on one page */
+ u32 payloadSize; /* Total number of bytes in the record */
+ u32 szRow; /* Byte available in aRow */
+ u32 iHdrOffset; /* Offset to next unparsed byte of the header */
+ const u8 *aRow; /* Data for the current row, if all on one page */
+ u32 aType[1]; /* Type values for all entries in the record */
+ /* 2*nField extra array elements allocated for aType[], beyond the one
+ ** static element declared in the structure. nField total array slots for
+ ** aType[] and nField+1 array slots for aOffset[] */
};
typedef struct VdbeCursor VdbeCursor;
@@ -166,7 +173,6 @@ struct Mem {
} u;
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 */
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
@@ -193,9 +199,10 @@ struct Mem {
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
+#define MEM_AffMask 0x001f /* Mask of affinity bits */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
-#define MEM_Invalid 0x0080 /* Value is undefined */
+#define MEM_Undefined 0x0080 /* Value is undefined */
#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
#define MEM_TypeMask 0x01ff /* Mask of type bits */
@@ -206,7 +213,7 @@ struct Mem {
** string is \000 or \u0000 terminated
*/
#define MEM_Term 0x0200 /* String rep is nul terminated */
-#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */
+#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */
#define MEM_Static 0x0800 /* Mem.z points to a static string */
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
@@ -227,26 +234,22 @@ struct Mem {
** is for use inside assert() statements only.
*/
#ifdef SQLITE_DEBUG
-#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
+#define memIsValid(M) ((M)->flags & MEM_Undefined)==0
#endif
-
-/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
-** additional information about auxiliary information bound to arguments
-** of the function. This is used to implement the sqlite3_get_auxdata()
-** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
-** that can be associated with a constant argument to a function. This
-** allows functions such as "regexp" to compile their constant regular
-** expression argument once and reused the compiled code for multiple
-** invocations.
+/*
+** Each auxilliary data pointer stored by a user defined function
+** implementation calling sqlite3_set_auxdata() is stored in an instance
+** of this structure. All such structures associated with a single VM
+** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed
+** when the VM is halted (if not before).
*/
-struct VdbeFunc {
- FuncDef *pFunc; /* The definition of the function */
- int nAux; /* Number of entries allocated for apAux[] */
- struct AuxData {
- void *pAux; /* Aux data for the i-th argument */
- void (*xDelete)(void *); /* Destructor for the aux data */
- } apAux[1]; /* One slot for each function argument */
+struct AuxData {
+ int iOp; /* Instruction number of OP_Function opcode */
+ int iArg; /* Index of function argument. */
+ void *pAux; /* Aux data pointer */
+ void (*xDelete)(void *); /* Destructor for the aux data */
+ AuxData *pNext; /* Next element in list */
};
/*
@@ -264,12 +267,14 @@ struct VdbeFunc {
*/
struct sqlite3_context {
FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
- VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
Mem s; /* The return value is stored here */
Mem *pMem; /* Memory cell used to store aggregate context */
CollSeq *pColl; /* Collating sequence */
+ Vdbe *pVdbe; /* The VM that owns this context */
+ int iOp; /* Instruction number of OP_Function */
int isError; /* Error code returned by the function. */
- int skipFlag; /* Skip skip accumulator loading if true */
+ u8 skipFlag; /* Skip skip accumulator loading if true */
+ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
};
/*
@@ -311,12 +316,9 @@ struct Vdbe {
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
+ Parse *pParse; /* Parsing context used to create this Vdbe */
int nMem; /* Number of memory locations currently allocated */
int nOp; /* Number of instructions in the program */
- int nOpAlloc; /* Number of slots allocated for aOp[] */
- int nLabel; /* Number of labels used */
- int *aLabel; /* Space to hold the labels */
- u16 nResColumn; /* Number of columns in one row of the result set */
int nCursor; /* Number of slots in apCsr[] */
u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
@@ -329,6 +331,7 @@ struct Vdbe {
u32 cacheCtr; /* VdbeCursor row cache generation counter */
int pc; /* The program counter */
int rc; /* Value to return */
+ u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
bft explain:2; /* True if EXPLAIN present on SQL command */
@@ -337,24 +340,24 @@ struct Vdbe {
bft expired:1; /* True if the VM needs to be recompiled */
bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
- bft readOnly:1; /* True for read-only statements */
+ bft readOnly:1; /* True for statements that do not write */
+ bft bIsReader:1; /* True for statements that read */
bft isPrepareV2:1; /* True if prepared with prepare_v2() */
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
int nChange; /* Number of db changes made since last reset */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
int iStatement; /* Statement number (or 0 if has not opened stmt) */
- int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
+ u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */
#ifndef SQLITE_OMIT_TRACE
i64 startTime; /* Time when query started - used for profiling */
#endif
+ i64 iCurrentTime; /* Value of julianday('now') for this statement */
i64 nFkConstraint; /* Number of imm. FK constraints this VM */
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
-#ifdef SQLITE_DEBUG
- FILE *trace; /* Write an execution trace here, if not NULL */
-#endif
#ifdef SQLITE_ENABLE_TREE_EXPLAIN
Explain *pExplain; /* The explainer */
char *zExplain; /* Explanation of data structures */
@@ -366,6 +369,7 @@ struct Vdbe {
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
int nOnceFlag; /* Size of array aOnceFlag[] */
u8 *aOnceFlag; /* Flags for OP_Once */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
};
/*
@@ -387,14 +391,13 @@ void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
u32 sqlite3VdbeSerialTypeLen(u32);
u32 sqlite3VdbeSerialType(Mem*, int);
-u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int);
+u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
-void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
+void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *);
-int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3VdbeExec(Vdbe*);
int sqlite3VdbeList(Vdbe*);
int sqlite3VdbeHalt(Vdbe*);
@@ -422,28 +425,29 @@ double sqlite3VdbeRealValue(Mem*);
void sqlite3VdbeIntegerAffinity(Mem*);
int sqlite3VdbeMemRealify(Mem*);
int sqlite3VdbeMemNumerify(Mem*);
-int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
+int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
void sqlite3VdbeMemReleaseExternal(Mem *p);
+#define VdbeMemDynamic(X) \
+ (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
#define VdbeMemRelease(X) \
- if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
- sqlite3VdbeMemReleaseExternal(X);
+ if( VdbeMemDynamic(X) ) sqlite3VdbeMemReleaseExternal(X);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
int sqlite3VdbeCloseStatement(Vdbe *, int);
void sqlite3VdbeFrameDelete(VdbeFrame*);
int sqlite3VdbeFrameRestore(VdbeFrame *);
-void sqlite3VdbeMemStoreType(Mem *pMem);
int sqlite3VdbeTransferError(Vdbe *p);
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
+void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
int sqlite3VdbeSorterRewind(sqlite3 *, const VdbeCursor *, int *);
int sqlite3VdbeSorterWrite(sqlite3 *, const VdbeCursor *, Mem *);
-int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *);
+int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
void sqlite3VdbeEnter(Vdbe*);
@@ -455,6 +459,7 @@ int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int *);
#ifdef SQLITE_DEBUG
void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
+int sqlite3VdbeCheckMemInvariants(Mem*);
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 7c861e2..5e5bb81 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -135,7 +135,6 @@ const void *sqlite3_value_blob(sqlite3_value *pVal){
Mem *p = (Mem*)pVal;
if( p->flags & (MEM_Blob|MEM_Str) ){
sqlite3VdbeMemExpandBlob(p);
- p->flags &= ~MEM_Str;
p->flags |= MEM_Blob;
return p->n ? p->z : 0;
}else{
@@ -172,7 +171,41 @@ const void *sqlite3_value_text16le(sqlite3_value *pVal){
}
#endif /* SQLITE_OMIT_UTF16 */
int sqlite3_value_type(sqlite3_value* pVal){
- return pVal->type;
+ static const u8 aType[] = {
+ SQLITE_BLOB, /* 0x00 */
+ SQLITE_NULL, /* 0x01 */
+ SQLITE_TEXT, /* 0x02 */
+ SQLITE_NULL, /* 0x03 */
+ SQLITE_INTEGER, /* 0x04 */
+ SQLITE_NULL, /* 0x05 */
+ SQLITE_INTEGER, /* 0x06 */
+ SQLITE_NULL, /* 0x07 */
+ SQLITE_FLOAT, /* 0x08 */
+ SQLITE_NULL, /* 0x09 */
+ SQLITE_FLOAT, /* 0x0a */
+ SQLITE_NULL, /* 0x0b */
+ SQLITE_INTEGER, /* 0x0c */
+ SQLITE_NULL, /* 0x0d */
+ SQLITE_INTEGER, /* 0x0e */
+ SQLITE_NULL, /* 0x0f */
+ SQLITE_BLOB, /* 0x10 */
+ SQLITE_NULL, /* 0x11 */
+ SQLITE_TEXT, /* 0x12 */
+ SQLITE_NULL, /* 0x13 */
+ SQLITE_INTEGER, /* 0x14 */
+ SQLITE_NULL, /* 0x15 */
+ SQLITE_INTEGER, /* 0x16 */
+ SQLITE_NULL, /* 0x17 */
+ SQLITE_FLOAT, /* 0x18 */
+ SQLITE_NULL, /* 0x19 */
+ SQLITE_FLOAT, /* 0x1a */
+ SQLITE_NULL, /* 0x1b */
+ SQLITE_INTEGER, /* 0x1c */
+ SQLITE_NULL, /* 0x1d */
+ SQLITE_INTEGER, /* 0x1e */
+ SQLITE_NULL, /* 0x1f */
+ };
+ return aType[pVal->flags&MEM_AffMask];
}
/**************************** sqlite3_result_ *******************************
@@ -211,12 +244,14 @@ void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_ERROR;
+ pCtx->fErrorOrAux = 1;
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){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_ERROR;
+ pCtx->fErrorOrAux = 1;
sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
@@ -280,6 +315,7 @@ void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
}
void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
pCtx->isError = errCode;
+ pCtx->fErrorOrAux = 1;
if( pCtx->s.flags & MEM_Null ){
sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1,
SQLITE_UTF8, SQLITE_STATIC);
@@ -290,6 +326,7 @@ void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
void sqlite3_result_error_toobig(sqlite3_context *pCtx){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
+ pCtx->fErrorOrAux = 1;
sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1,
SQLITE_UTF8, SQLITE_STATIC);
}
@@ -299,6 +336,7 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
sqlite3VdbeMemSetNull(&pCtx->s);
pCtx->isError = SQLITE_NOMEM;
+ pCtx->fErrorOrAux = 1;
pCtx->s.db->mallocFailed = 1;
}
@@ -382,11 +420,13 @@ static int sqlite3Step(Vdbe *p){
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
** from interrupting a statement that has not yet started.
*/
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
- assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 );
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
+ || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ );
#ifndef SQLITE_OMIT_TRACE
if( db->xProfile && !db->init.busy ){
@@ -394,8 +434,9 @@ static int sqlite3Step(Vdbe *p){
}
#endif
- db->activeVdbeCnt++;
- if( p->readOnly==0 ) db->writeVdbeCnt++;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -404,9 +445,9 @@ static int sqlite3Step(Vdbe *p){
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
- db->vdbeExecCnt++;
+ db->nVdbeExec++;
rc = sqlite3VdbeExec(p);
- db->vdbeExecCnt--;
+ db->nVdbeExec--;
}
#ifndef SQLITE_OMIT_TRACE
@@ -478,7 +519,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
v->doingRerun = 1;
assert( v->expired==0 );
}
- if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
+ if( rc2!=SQLITE_OK ){
/* 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
@@ -488,6 +529,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
** sqlite3_errmsg() and sqlite3_errcode().
*/
const char *zErr = (const char *)sqlite3_value_text(db->pErr);
+ assert( zErr!=0 || db->mallocFailed );
sqlite3DbFree(db, v->zErrMsg);
if( !db->mallocFailed ){
v->zErrMsg = sqlite3DbStrDup(db, zErr);
@@ -502,6 +544,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
return rc;
}
+
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
@@ -527,6 +570,19 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
}
/*
+** Return the current time for a statement
+*/
+sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
+ Vdbe *v = p->pVdbe;
+ int rc;
+ if( v->iCurrentTime==0 ){
+ rc = sqlite3OsCurrentTimeInt64(p->s.db->pVfs, &v->iCurrentTime);
+ if( rc ) v->iCurrentTime = 0;
+ }
+ return v->iCurrentTime;
+}
+
+/*
** 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
@@ -581,14 +637,14 @@ 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;
+ AuxData *pAuxData;
assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
- return 0;
+ for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
}
- return pVdbeFunc->apAux[iArg].pAux;
+
+ return (pAuxData ? pAuxData->pAux : 0);
}
/*
@@ -602,29 +658,30 @@ void sqlite3_set_auxdata(
void *pAux,
void (*xDelete)(void*)
){
- struct AuxData *pAuxData;
- VdbeFunc *pVdbeFunc;
- if( iArg<0 ) goto failed;
+ AuxData *pAuxData;
+ Vdbe *pVdbe = pCtx->pVdbe;
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 = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc);
- if( !pVdbeFunc ){
- goto failed;
- }
- pCtx->pVdbeFunc = pVdbeFunc;
- memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux));
- pVdbeFunc->nAux = iArg+1;
- pVdbeFunc->pFunc = pCtx->pFunc;
- }
+ if( iArg<0 ) goto failed;
- pAuxData = &pVdbeFunc->apAux[iArg];
- if( pAuxData->pAux && pAuxData->xDelete ){
+ for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
+ }
+ if( pAuxData==0 ){
+ pAuxData = sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData));
+ if( !pAuxData ) goto failed;
+ pAuxData->iOp = pCtx->iOp;
+ pAuxData->iArg = iArg;
+ pAuxData->pNext = pVdbe->pAuxData;
+ pVdbe->pAuxData = pAuxData;
+ if( pCtx->fErrorOrAux==0 ){
+ pCtx->isError = 0;
+ pCtx->fErrorOrAux = 1;
+ }
+ }else if( pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}
+
pAuxData->pAux = pAux;
pAuxData->xDelete = xDelete;
return;
@@ -669,6 +726,30 @@ int sqlite3_data_count(sqlite3_stmt *pStmt){
return pVm->nResColumn;
}
+/*
+** Return a pointer to static memory containing an SQL NULL value.
+*/
+static const Mem *columnNullValue(void){
+ /* Even though the Mem structure contains an element
+ ** of type i64, on certain architectures (x86) with certain compiler
+ ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
+ ** instead of an 8-byte one. This all works fine, except that when
+ ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
+ ** that a Mem structure is located on an 8-byte boundary. To prevent
+ ** these assert()s from failing, when building with SQLITE_DEBUG defined
+ ** using gcc, we force nullMem to be 8-byte aligned using the magical
+ ** __attribute__((aligned(8))) macro. */
+ static const Mem nullMem
+#if defined(SQLITE_DEBUG) && defined(__GNUC__)
+ __attribute__((aligned(8)))
+#endif
+ = {0, "", (double)0, {0}, 0, MEM_Null, 0,
+#ifdef SQLITE_DEBUG
+ 0, 0, /* pScopyFrom, pFiller */
+#endif
+ 0, 0 };
+ return &nullMem;
+}
/*
** Check to see if column iCol of the given statement is valid. If
@@ -685,32 +766,11 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
sqlite3_mutex_enter(pVm->db->mutex);
pOut = &pVm->pResultSet[i];
}else{
- /* If the value passed as the second argument is out of range, return
- ** a pointer to the following static Mem object which contains the
- ** value SQL NULL. Even though the Mem structure contains an element
- ** of type i64, on certain architectures (x86) with certain compiler
- ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
- ** instead of an 8-byte one. This all works fine, except that when
- ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
- ** that a Mem structure is located on an 8-byte boundary. To prevent
- ** these assert()s from failing, when building with SQLITE_DEBUG defined
- ** using gcc, we force nullMem to be 8-byte aligned using the magical
- ** __attribute__((aligned(8))) macro. */
- static const Mem nullMem
-#if defined(SQLITE_DEBUG) && defined(__GNUC__)
- __attribute__((aligned(8)))
-#endif
- = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0,
-#ifdef SQLITE_DEBUG
- 0, 0, /* pScopyFrom, pFiller */
-#endif
- 0, 0 };
-
if( pVm && ALWAYS(pVm->db) ){
sqlite3_mutex_enter(pVm->db->mutex);
sqlite3Error(pVm->db, SQLITE_RANGE, 0);
}
- pOut = (Mem*)&nullMem;
+ pOut = (Mem*)columnNullValue();
}
return pOut;
}
@@ -813,13 +873,6 @@ int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
return iType;
}
-/* The following function is experimental and subject to change or
-** removal */
-/*int sqlite3_column_numeric_type(sqlite3_stmt *pStmt, int i){
-** return sqlite3_value_numeric_type( columnMem(pStmt,i) );
-**}
-*/
-
/*
** Convert the N-th element of pStmt->pColName[] into a string using
** xFunc() then return that string. If N is out of range, return 0.
@@ -1114,7 +1167,7 @@ int sqlite3_bind_text16(
#endif /* SQLITE_OMIT_UTF16 */
int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
int rc;
- switch( pValue->type ){
+ switch( sqlite3_value_type((sqlite3_value*)pValue) ){
case SQLITE_INTEGER: {
rc = sqlite3_bind_int64(pStmt, i, pValue->u.i);
break;
@@ -1270,7 +1323,7 @@ int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
*/
int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
Vdbe *v = (Vdbe*)pStmt;
- return v!=0 && v->pc>0 && v->magic==VDBE_MAGIC_RUN;
+ return v!=0 && v->pc>=0 && v->magic==VDBE_MAGIC_RUN;
}
/*
@@ -1296,7 +1349,7 @@ sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
*/
int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
- int v = pVdbe->aCounter[op-1];
- if( resetFlag ) pVdbe->aCounter[op-1] = 0;
- return v;
+ u32 v = pVdbe->aCounter[op];
+ if( resetFlag ) pVdbe->aCounter[op] = 0;
+ return (int)v;
}
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 2c4269a..fb3f7c3 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -20,7 +20,8 @@
/*
** Create a new virtual database engine.
*/
-Vdbe *sqlite3VdbeCreate(sqlite3 *db){
+Vdbe *sqlite3VdbeCreate(Parse *pParse){
+ sqlite3 *db = pParse->db;
Vdbe *p;
p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
if( p==0 ) return 0;
@@ -32,6 +33,10 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
p->pPrev = 0;
db->pVdbe = p;
p->magic = VDBE_MAGIC_INIT;
+ p->pParse = pParse;
+ assert( pParse->aLabel==0 );
+ assert( pParse->nLabel==0 );
+ assert( pParse->nOpAlloc==0 );
return p;
}
@@ -78,35 +83,55 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
pB->isPrepareV2 = pA->isPrepareV2;
}
-#ifdef SQLITE_DEBUG
-/*
-** Turn tracing on or off
-*/
-void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
- p->trace = trace;
-}
-#endif
-
/*
-** Resize the Vdbe.aOp array so that it is at least one op larger than
-** it was.
+** Resize the Vdbe.aOp array so that it is at least nOp elements larger
+** than its current size. nOp is guaranteed to be less than or equal
+** to 1024/sizeof(Op).
**
** If an out-of-memory error occurs while resizing the array, return
-** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
+** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain
** unchanged (this is so that any opcodes already allocated can be
** correctly deallocated along with the rest of the Vdbe).
*/
-static int growOpArray(Vdbe *p){
+static int growOpArray(Vdbe *v, int nOp){
VdbeOp *pNew;
+ Parse *p = v->pParse;
+
+ /* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force
+ ** more frequent reallocs and hence provide more opportunities for
+ ** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used
+ ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array
+ ** by the minimum* amount required until the size reaches 512. Normal
+ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current
+ ** size of the op array or add 1KB of space, whichever is smaller. */
+#ifdef SQLITE_TEST_REALLOC_STRESS
+ int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp);
+#else
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
- pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
+ UNUSED_PARAMETER(nOp);
+#endif
+
+ assert( nOp<=(1024/sizeof(Op)) );
+ assert( nNew>=(p->nOpAlloc+nOp) );
+ pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
if( pNew ){
p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
- p->aOp = pNew;
+ v->aOp = pNew;
}
return (pNew ? SQLITE_OK : SQLITE_NOMEM);
}
+#ifdef SQLITE_DEBUG
+/* This routine is just a convenient place to set a breakpoint that will
+** fire after each opcode is inserted and displayed using
+** "PRAGMA vdbe_addoptrace=on".
+*/
+static void test_addop_breakpoint(void){
+ static int n = 0;
+ n++;
+}
+#endif
+
/*
** Add a new instruction to the list of instructions current in the
** VDBE. Return the address of the new instruction.
@@ -130,8 +155,8 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
assert( op>0 && op<0xff );
- if( p->nOpAlloc<=i ){
- if( growOpArray(p) ){
+ if( p->pParse->nOpAlloc<=i ){
+ if( growOpArray(p, 1) ){
return 1;
}
}
@@ -144,16 +169,31 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
+#endif
+#ifdef SQLITE_DEBUG
if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ int jj, kk;
+ Parse *pParse = p->pParse;
+ for(jj=kk=0; jj<SQLITE_N_COLCACHE; jj++){
+ struct yColCache *x = pParse->aColCache + jj;
+ if( x->iLevel>pParse->iCacheLevel || x->iReg==0 ) continue;
+ printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn);
+ kk++;
+ }
+ if( kk ) printf("\n");
sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+ test_addop_breakpoint();
}
#endif
#ifdef VDBE_PROFILE
pOp->cycles = 0;
pOp->cnt = 0;
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOp->iSrcLine = 0;
+#endif
return i;
}
int sqlite3VdbeAddOp0(Vdbe *p, int op){
@@ -229,9 +269,10 @@ int sqlite3VdbeAddOp4Int(
**
** Zero is returned if a malloc() fails.
*/
-int sqlite3VdbeMakeLabel(Vdbe *p){
+int sqlite3VdbeMakeLabel(Vdbe *v){
+ Parse *p = v->pParse;
int i = p->nLabel++;
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( v->magic==VDBE_MAGIC_INIT );
if( (i & (i-1))==0 ){
p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
(i*2+1)*sizeof(p->aLabel[0]));
@@ -247,13 +288,15 @@ int sqlite3VdbeMakeLabel(Vdbe *p){
** be inserted. The parameter "x" must have been obtained from
** a prior call to sqlite3VdbeMakeLabel().
*/
-void sqlite3VdbeResolveLabel(Vdbe *p, int x){
+void sqlite3VdbeResolveLabel(Vdbe *v, int x){
+ Parse *p = v->pParse;
int j = -1-x;
- assert( p->magic==VDBE_MAGIC_INIT );
- assert( j>=0 && j<p->nLabel );
- if( p->aLabel ){
- p->aLabel[j] = p->nOp;
+ assert( v->magic==VDBE_MAGIC_INIT );
+ assert( j<p->nLabel );
+ if( ALWAYS(j>=0) && p->aLabel ){
+ p->aLabel[j] = v->nOp;
}
+ p->iFixedOp = v->nOp - 1;
}
/*
@@ -401,43 +444,79 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
int i;
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
- int *aLabel = p->aLabel;
+ Parse *pParse = p->pParse;
+ int *aLabel = pParse->aLabel;
p->readOnly = 1;
+ p->bIsReader = 0;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
- pOp->opflags = sqlite3OpcodeProperty[opcode];
- if( opcode==OP_Function || opcode==OP_AggStep ){
- if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
- p->readOnly = 0;
+ /* NOTE: Be sure to update mkopcodeh.awk when adding or removing
+ ** cases from this switch! */
+ switch( opcode ){
+ case OP_Function:
+ case OP_AggStep: {
+ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
+ break;
+ }
+ case OP_Transaction: {
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ /* fall thru */
+ }
+ case OP_AutoCommit:
+ case OP_Savepoint: {
+ p->bIsReader = 1;
+ break;
+ }
+#ifndef SQLITE_OMIT_WAL
+ case OP_Checkpoint:
+#endif
+ case OP_Vacuum:
+ case OP_JournalMode: {
+ p->readOnly = 0;
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate ){
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
- }else if( opcode==OP_VFilter ){
- int n;
- assert( p->nOp - i >= 3 );
- assert( pOp[-1].opcode==OP_Integer );
- n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
+ case OP_VUpdate: {
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ break;
+ }
+ case OP_VFilter: {
+ int n;
+ assert( p->nOp - i >= 3 );
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
+ if( n>nMaxArgs ) nMaxArgs = n;
+ break;
+ }
#endif
- }else if( opcode==OP_Next || opcode==OP_SorterNext ){
- pOp->p4.xAdvance = sqlite3BtreeNext;
- pOp->p4type = P4_ADVANCE;
- }else if( opcode==OP_Prev ){
- pOp->p4.xAdvance = sqlite3BtreePrevious;
- pOp->p4type = P4_ADVANCE;
+ case OP_Next:
+ case OP_NextIfOpen:
+ case OP_SorterNext: {
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ case OP_Prev:
+ case OP_PrevIfOpen: {
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
}
+ pOp->opflags = sqlite3OpcodeProperty[opcode];
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
- assert( -1-pOp->p2<p->nLabel );
+ assert( -1-pOp->p2<pParse->nLabel );
pOp->p2 = aLabel[-1-pOp->p2];
}
}
- sqlite3DbFree(p->db, p->aLabel);
- p->aLabel = 0;
-
+ sqlite3DbFree(p->db, pParse->aLabel);
+ pParse->aLabel = 0;
+ pParse->nLabel = 0;
*pMaxFuncArgs = nMaxArgs;
+ assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) );
}
/*
@@ -464,7 +543,7 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
assert( aOp && !p->db->mallocFailed );
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
- assert( p->btreeMask==0 );
+ assert( DbMaskAllZero(p->btreeMask) );
resolveP2Values(p, pnMaxArg);
*pnOp = p->nOp;
@@ -476,10 +555,10 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
** Add a whole list of operations to the operation stack. Return the
** address of the first operation added.
*/
-int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
+int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp, int iLineno){
int addr;
assert( p->magic==VDBE_MAGIC_INIT );
- if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){
+ if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
addr = p->nOp;
@@ -491,7 +570,8 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
VdbeOp *pOut = &p->aOp[i+addr];
pOut->opcode = pIn->opcode;
pOut->p1 = pIn->p1;
- if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){
+ if( p2<0 ){
+ assert( sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP );
pOut->p2 = addr + ADDR(p2);
}else{
pOut->p2 = p2;
@@ -500,8 +580,15 @@ int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
pOut->p4type = P4_NOTUSED;
pOut->p4.p = 0;
pOut->p5 = 0;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOut->zComment = 0;
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOut->iSrcLine = iLineno+i;
+#else
+ (void)iLineno;
+#endif
+#ifdef SQLITE_DEBUG
if( p->db->flags & SQLITE_VdbeAddopTrace ){
sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
}
@@ -563,8 +650,8 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- assert( addr>=0 || p->db->mallocFailed );
- if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp);
+ sqlite3VdbeChangeP2(p, addr, p->nOp);
+ p->pParse->iFixedOp = p->nOp - 1;
}
@@ -573,7 +660,7 @@ void sqlite3VdbeJumpHere(Vdbe *p, int addr){
** the FuncDef is not ephermal, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
- if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){
+ if( ALWAYS(pDef) && (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
sqlite3DbFree(db, pDef);
}
}
@@ -590,21 +677,16 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
case P4_REAL:
case P4_INT64:
case P4_DYNAMIC:
- case P4_KEYINFO:
- case P4_INTARRAY:
- case P4_KEYINFO_HANDOFF: {
+ case P4_INTARRAY: {
sqlite3DbFree(db, p4);
break;
}
- case P4_MPRINTF: {
- if( db->pnBytesFreed==0 ) sqlite3_free(p4);
+ case P4_KEYINFO: {
+ if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
break;
}
- case P4_VDBEFUNC: {
- VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
- freeEphemeralFunction(db, pVdbeFunc->pFunc);
- if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
- sqlite3DbFree(db, pVdbeFunc);
+ case P4_MPRINTF: {
+ if( db->pnBytesFreed==0 ) sqlite3_free(p4);
break;
}
case P4_FUNCDEF: {
@@ -639,7 +721,7 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
Op *pOp;
for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
freeP4(db, pOp->p4type, pOp->p4.p);
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
sqlite3DbFree(db, pOp->zComment);
#endif
}
@@ -661,12 +743,25 @@ void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
** Change the opcode at addr into OP_Noop
*/
void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
- if( p->aOp ){
+ if( addr<p->nOp ){
VdbeOp *pOp = &p->aOp[addr];
sqlite3 *db = p->db;
freeP4(db, pOp->p4type, pOp->p4.p);
memset(pOp, 0, sizeof(pOp[0]));
pOp->opcode = OP_Noop;
+ if( addr==p->nOp-1 ) p->nOp--;
+ }
+}
+
+/*
+** Remove the last opcode inserted
+*/
+int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
+ if( (p->nOp-1)>(p->pParse->iFixedOp) && p->aOp[p->nOp-1].opcode==op ){
+ sqlite3VdbeChangeToNoop(p, p->nOp-1);
+ return 1;
+ }else{
+ return 0;
}
}
@@ -680,14 +775,6 @@ void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
** 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==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
-** 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 (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
** to a string or structure that is guaranteed to exist for the lifetime of
@@ -702,7 +789,7 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
db = p->db;
assert( p->magic==VDBE_MAGIC_INIT );
if( p->aOp==0 || db->mallocFailed ){
- if ( n!=P4_KEYINFO && n!=P4_VTAB ) {
+ if( n!=P4_VTAB ){
freeP4(db, n, (void*)*(char**)&zP4);
}
return;
@@ -713,7 +800,9 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
addr = p->nOp - 1;
}
pOp = &p->aOp[addr];
- assert( pOp->p4type==P4_NOTUSED || pOp->p4type==P4_INT32 );
+ assert( pOp->p4type==P4_NOTUSED
+ || pOp->p4type==P4_INT32
+ || pOp->p4type==P4_KEYINFO );
freeP4(db, pOp->p4type, pOp->p4.p);
pOp->p4.p = 0;
if( n==P4_INT32 ){
@@ -725,26 +814,6 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
}else if( n==P4_KEYINFO ){
- KeyInfo *pKeyInfo;
- int nField, nByte;
-
- nField = ((KeyInfo*)zP4)->nField;
- nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqlite3DbMallocRaw(0, nByte);
- pOp->p4.pKeyInfo = pKeyInfo;
- if( pKeyInfo ){
- u8 *aSortOrder;
- memcpy((char*)pKeyInfo, zP4, nByte - nField);
- aSortOrder = pKeyInfo->aSortOrder;
- assert( aSortOrder!=0 );
- pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
- memcpy(pKeyInfo->aSortOrder, aSortOrder, nField);
- pOp->p4type = P4_KEYINFO;
- }else{
- p->db->mallocFailed = 1;
- pOp->p4type = P4_NOTUSED;
- }
- }else if( n==P4_KEYINFO_HANDOFF ){
pOp->p4.p = (void*)zP4;
pOp->p4type = P4_KEYINFO;
}else if( n==P4_VTAB ){
@@ -762,7 +831,19 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
}
}
-#ifndef NDEBUG
+/*
+** Set the P4 on the most recently added opcode to the KeyInfo for the
+** index given.
+*/
+void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ assert( pIdx!=0 );
+ sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx),
+ P4_KEYINFO);
+}
+
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
/*
** Change the comment on the most recently coded instruction. Or
** insert a No-op and add the comment to that new instruction. This
@@ -797,6 +878,15 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
}
#endif /* NDEBUG */
+#ifdef SQLITE_VDBE_COVERAGE
+/*
+** Set the value if the iSrcLine field for the previously coded instruction.
+*/
+void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){
+ sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine;
+}
+#endif /* SQLITE_VDBE_COVERAGE */
+
/*
** Return the opcode for a given address. If the address is -1, then
** return the most recently inserted opcode.
@@ -809,14 +899,6 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** this routine is a valid pointer. But because the dummy.opcode is 0,
** dummy will never be written to. This is verified by code inspection and
** by running with Valgrind.
-**
-** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called
-** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE,
-** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as
-** a new VDBE is created. So we are free to set addr to p->nOp-1 without
-** having to double-check to make sure that the result is non-negative. But
-** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to
-** check the value of p->nOp-1 before continuing.
*/
VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
@@ -824,9 +906,6 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
-#ifdef SQLITE_OMIT_TRACE
- if( p->nOp==0 ) return (VdbeOp*)&dummy;
-#endif
addr = p->nOp - 1;
}
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
@@ -837,6 +916,97 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
}
}
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
+/*
+** Return an integer value for one of the parameters to the opcode pOp
+** determined by character c.
+*/
+static int translateP(char c, const Op *pOp){
+ if( c=='1' ) return pOp->p1;
+ if( c=='2' ) return pOp->p2;
+ if( c=='3' ) return pOp->p3;
+ if( c=='4' ) return pOp->p4.i;
+ return pOp->p5;
+}
+
+/*
+** Compute a string for the "comment" field of a VDBE opcode listing.
+**
+** The Synopsis: field in comments in the vdbe.c source file gets converted
+** to an extra string that is appended to the sqlite3OpcodeName(). In the
+** absence of other comments, this synopsis becomes the comment on the opcode.
+** Some translation occurs:
+**
+** "PX" -> "r[X]"
+** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
+** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
+** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
+*/
+static int displayComment(
+ const Op *pOp, /* The opcode to be commented */
+ const char *zP4, /* Previously obtained value for P4 */
+ char *zTemp, /* Write result here */
+ int nTemp /* Space available in zTemp[] */
+){
+ const char *zOpName;
+ const char *zSynopsis;
+ int nOpName;
+ int ii, jj;
+ zOpName = sqlite3OpcodeName(pOp->opcode);
+ nOpName = sqlite3Strlen30(zOpName);
+ if( zOpName[nOpName+1] ){
+ int seenCom = 0;
+ char c;
+ zSynopsis = zOpName += nOpName + 1;
+ for(ii=jj=0; jj<nTemp-1 && (c = zSynopsis[ii])!=0; ii++){
+ if( c=='P' ){
+ c = zSynopsis[++ii];
+ if( c=='4' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", zP4);
+ }else if( c=='X' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", pOp->zComment);
+ seenCom = 1;
+ }else{
+ int v1 = translateP(c, pOp);
+ int v2;
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1);
+ if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){
+ ii += 3;
+ jj += sqlite3Strlen30(zTemp+jj);
+ v2 = translateP(zSynopsis[ii], pOp);
+ if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){
+ ii += 2;
+ v2++;
+ }
+ if( v2>1 ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1);
+ }
+ }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){
+ ii += 4;
+ }
+ }
+ jj += sqlite3Strlen30(zTemp+jj);
+ }else{
+ zTemp[jj++] = c;
+ }
+ }
+ if( !seenCom && jj<nTemp-5 && pOp->zComment ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment);
+ jj += sqlite3Strlen30(zTemp+jj);
+ }
+ if( jj<nTemp ) zTemp[jj] = 0;
+ }else if( pOp->zComment ){
+ sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment);
+ jj = sqlite3Strlen30(zTemp);
+ }else{
+ zTemp[0] = 0;
+ jj = 0;
+ }
+ return jj;
+}
+#endif /* SQLITE_DEBUG */
+
+
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
|| defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
@@ -847,17 +1017,20 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
char *zP4 = zTemp;
assert( nTemp>=20 );
switch( pOp->p4type ){
- case P4_KEYINFO_STATIC:
case P4_KEYINFO: {
int i, j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
- sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField);
+ sqlite3_snprintf(nTemp, zTemp, "k(%d", pKeyInfo->nField);
i = sqlite3Strlen30(zTemp);
for(j=0; j<pKeyInfo->nField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "nil";
int n = sqlite3Strlen30(zColl);
+ if( n==6 && memcmp(zColl,"BINARY",6)==0 ){
+ zColl = "B";
+ n = 1;
+ }
if( i+n>nTemp-6 ){
memcpy(&zTemp[i],",...",4);
break;
@@ -876,7 +1049,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName);
+ sqlite3_snprintf(nTemp, zTemp, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
@@ -955,9 +1128,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 );
assert( i<(int)sizeof(p->btreeMask)*8 );
- p->btreeMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->btreeMask, i);
if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){
- p->lockMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->lockMask, i);
}
}
@@ -985,16 +1158,15 @@ void sqlite3VdbeUsesBtree(Vdbe *p, int i){
*/
void sqlite3VdbeEnter(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeEnter(aDb[i].pBt);
}
}
@@ -1007,16 +1179,15 @@ void sqlite3VdbeEnter(Vdbe *p){
*/
void sqlite3VdbeLeave(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeLeave(aDb[i].pBt);
}
}
@@ -1030,16 +1201,21 @@ void sqlite3VdbeLeave(Vdbe *p){
void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
char *zP4;
char zPtr[50];
- static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
+ char zCom[100];
+ static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
if( pOut==0 ) pOut = stdout;
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 : ""
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ displayComment(pOp, zP4, zCom, sizeof(zCom));
#else
- ""
+ zCom[0] = 0;
#endif
+ /* NB: The sqlite3OpcodeName() function is implemented by code created
+ ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
+ ** information from the vdbe.c source text */
+ fprintf(pOut, zFormat1, pc,
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
+ zCom
);
fflush(pOut);
}
@@ -1061,6 +1237,7 @@ static void releaseMemArray(Mem *p, int N){
}
for(pEnd=&p[N]; p<pEnd; p++){
assert( (&p[1])==pEnd || p[0].db==p[1].db );
+ assert( sqlite3VdbeCheckMemInvariants(p) );
/* This block is really an inlined version of sqlite3VdbeMemRelease()
** that takes advantage of the fact that the memory cell value is
@@ -1074,6 +1251,10 @@ static void releaseMemArray(Mem *p, int N){
** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
+ testcase( p->flags & MEM_Agg );
+ testcase( p->flags & MEM_Dyn );
+ testcase( p->flags & MEM_Frame );
+ testcase( p->flags & MEM_RowSet );
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
sqlite3VdbeMemRelease(p);
}else if( p->zMalloc ){
@@ -1081,7 +1262,7 @@ static void releaseMemArray(Mem *p, int N){
p->zMalloc = 0;
}
- p->flags = MEM_Invalid;
+ p->flags = MEM_Undefined;
}
db->mallocFailed = malloc_failed;
}
@@ -1185,7 +1366,7 @@ int sqlite3VdbeList(
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
}else{
- char *z;
+ char *zP4;
Op *pOp;
if( i<p->nOp ){
/* The output line number is small enough that we are still in the
@@ -1203,15 +1384,13 @@ int sqlite3VdbeList(
}
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 */
+ pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
- pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
@@ -1237,33 +1416,29 @@ int sqlite3VdbeList(
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p2; /* P2 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
- pMem->type = SQLITE_INTEGER;
pMem++;
if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- 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);
+ pMem->flags = MEM_Str|MEM_Term;
+ zP4 = displayP4(pOp, pMem->z, 32);
+ if( zP4!=pMem->z ){
+ sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
}
- pMem->type = SQLITE_TEXT;
pMem++;
if( p->explain==1 ){
@@ -1271,26 +1446,23 @@ int sqlite3VdbeList(
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
+ pMem->flags = 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 = sqlite3Strlen30(pMem->z);
- pMem->enc = SQLITE_UTF8;
- pMem->type = SQLITE_TEXT;
- }else
-#endif
- {
- pMem->flags = MEM_Null; /* Comment */
- pMem->type = SQLITE_NULL;
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ if( sqlite3VdbeMemGrow(pMem, 500, 0) ){
+ assert( p->db->mallocFailed );
+ return SQLITE_ERROR;
}
+ pMem->flags = MEM_Str|MEM_Term;
+ pMem->n = displayComment(pOp, zP4, pMem->z, 500);
+ pMem->enc = SQLITE_UTF8;
+#else
+ pMem->flags = MEM_Null; /* Comment */
+#endif
}
p->nResColumn = 8 - 4*(p->explain-1);
@@ -1307,15 +1479,17 @@ int sqlite3VdbeList(
** Print the SQL that was used to generate a VDBE program.
*/
void sqlite3VdbePrintSql(Vdbe *p){
- int nOp = p->nOp;
- VdbeOp *pOp;
- if( nOp<1 ) return;
- pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
- const char *z = pOp->p4.z;
- while( sqlite3Isspace(*z) ) z++;
- printf("SQL: [%s]\n", z);
+ const char *z = 0;
+ if( p->zSql ){
+ z = p->zSql;
+ }else if( p->nOp>=1 ){
+ const VdbeOp *pOp = &p->aOp[0];
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
+ z = pOp->p4.z;
+ while( sqlite3Isspace(*z) ) z++;
+ }
}
+ if( z ) printf("SQL: [%s]\n", z);
}
#endif
@@ -1329,7 +1503,7 @@ void sqlite3VdbeIOTraceSql(Vdbe *p){
if( sqlite3IoTrace==0 ) return;
if( nOp<1 ) return;
pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
int i, j;
char z[1000];
sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z);
@@ -1466,6 +1640,7 @@ void sqlite3VdbeMakeReady(
assert( p->nOp>0 );
assert( pParse!=0 );
assert( p->magic==VDBE_MAGIC_INIT );
+ assert( pParse==p->pParse );
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
@@ -1489,8 +1664,8 @@ void sqlite3VdbeMakeReady(
/* Allocate space for memory registers, SQL variables, VDBE cursors and
** an array to marshal SQL function arguments in.
*/
- zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
- zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
+ zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
+ zEnd = (u8*)&p->aOp[pParse->nOpAlloc]; /* First byte past end of zCsr[] */
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
@@ -1546,7 +1721,7 @@ void sqlite3VdbeMakeReady(
p->aMem--; /* aMem[] goes from 1..nMem */
p->nMem = nMem; /* not from 0..nMem-1 */
for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Invalid;
+ p->aMem[n].flags = MEM_Undefined;
p->aMem[n].db = db;
}
}
@@ -1573,7 +1748,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pCx->pVtabCursor ){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
- const sqlite3_module *pModule = pCx->pModule;
+ const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
p->inVtabMethod = 1;
pModule->xClose(pVtabCursor);
p->inVtabMethod = 0;
@@ -1636,6 +1811,10 @@ static void closeAllCursors(Vdbe *p){
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
+
+ /* Delete any auxdata allocations made by the VM */
+ sqlite3VdbeDeleteAuxData(p, -1, 0);
+ assert( p->pAuxData==0 );
}
/*
@@ -1654,7 +1833,7 @@ static void Cleanup(Vdbe *p){
int i;
if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
if( p->aMem ){
- for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Invalid );
+ for(i=1; i<=p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
}
#endif
@@ -1744,7 +1923,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
- rc = sqlite3VtabSync(db, &p->zErrMsg);
+ rc = sqlite3VtabSync(db, p);
/* This loop determines (a) if the commit hook should be invoked and
** (b) how many database files have open write transactions, not
@@ -1963,7 +2142,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/*
-** This routine checks that the sqlite3.activeVdbeCnt count variable
+** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
@@ -1976,16 +2155,19 @@ static void checkActiveVdbeCnt(sqlite3 *db){
Vdbe *p;
int cnt = 0;
int nWrite = 0;
+ int nRead = 0;
p = db->pVdbe;
while( p ){
- if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
+ if( sqlite3_stmt_busy((sqlite3_stmt*)p) ){
cnt++;
if( p->readOnly==0 ) nWrite++;
+ if( p->bIsReader ) nRead++;
}
p = p->pNext;
}
- assert( cnt==db->activeVdbeCnt );
- assert( nWrite==db->writeVdbeCnt );
+ assert( cnt==db->nVdbeActive );
+ assert( nWrite==db->nVdbeWrite );
+ assert( nRead==db->nVdbeRead );
}
#else
#define checkActiveVdbeCnt(x)
@@ -1996,7 +2178,7 @@ static void checkActiveVdbeCnt(sqlite3 *db){
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
-** statement transaction is commtted.
+** statement transaction is committed.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
@@ -2050,6 +2232,7 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** the statement transaction was opened. */
if( eOp==SAVEPOINT_ROLLBACK ){
db->nDeferredCons = p->nStmtDefCons;
+ db->nDeferredImmCons = p->nStmtDefImmCons;
}
}
return rc;
@@ -2068,10 +2251,12 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
#ifndef SQLITE_OMIT_FOREIGN_KEY
int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
sqlite3 *db = p->db;
- if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){
+ if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
+ || (!deferred && p->nFkConstraint>0)
+ ){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
- sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
+ sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed");
return SQLITE_ERROR;
}
return SQLITE_OK;
@@ -2121,8 +2306,9 @@ int sqlite3VdbeHalt(Vdbe *p){
}
checkActiveVdbeCnt(db);
- /* No commit or rollback needed if the program never started */
- if( p->pc>=0 ){
+ /* No commit or rollback needed if the program never started or if the
+ ** SQL statement does not read or write a database file. */
+ if( p->pc>=0 && p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -2132,7 +2318,6 @@ int sqlite3VdbeHalt(Vdbe *p){
/* Check for one of the special errors */
mrc = p->rc & 0xff;
- assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
@@ -2175,7 +2360,7 @@ int sqlite3VdbeHalt(Vdbe *p){
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
- && db->writeVdbeCnt==(p->readOnly==0)
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -2200,6 +2385,8 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3RollbackAll(db, SQLITE_OK);
}else{
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else{
@@ -2256,11 +2443,12 @@ int sqlite3VdbeHalt(Vdbe *p){
/* We have successfully halted and closed the VM. Record this fact. */
if( p->pc>=0 ){
- db->activeVdbeCnt--;
- if( !p->readOnly ){
- db->writeVdbeCnt--;
- }
- assert( db->activeVdbeCnt>=db->writeVdbeCnt );
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
@@ -2276,7 +2464,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3ConnectionUnlocked(db);
}
- assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
+ assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
@@ -2303,6 +2491,7 @@ int sqlite3VdbeTransferError(Vdbe *p){
if( p->zErrMsg ){
u8 mallocFailed = db->mallocFailed;
sqlite3BeginBenignMalloc();
+ if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db);
sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
sqlite3EndBenignMalloc();
db->mallocFailed = mallocFailed;
@@ -2371,8 +2560,7 @@ int sqlite3VdbeReset(Vdbe *p){
** to sqlite3_step(). For consistency (since sqlite3_step() was
** 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);
+ sqlite3Error(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
@@ -2393,18 +2581,31 @@ int sqlite3VdbeReset(Vdbe *p){
fprintf(out, "%02x", p->aOp[i].opcode);
}
fprintf(out, "\n");
+ if( p->zSql ){
+ char c, pc = 0;
+ fprintf(out, "-- ");
+ for(i=0; (c = p->zSql[i])!=0; i++){
+ if( pc=='\n' ) fprintf(out, "-- ");
+ putc(c, out);
+ pc = c;
+ }
+ if( pc!='\n' ) fprintf(out, "\n");
+ }
for(i=0; i<p->nOp; i++){
- fprintf(out, "%6d %10lld %8lld ",
+ char zHdr[100];
+ sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
p->aOp[i].cnt,
p->aOp[i].cycles,
p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
);
+ fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]);
}
fclose(out);
}
}
#endif
+ p->iCurrentTime = 0;
p->magic = VDBE_MAGIC_INIT;
return p->rc & db->errMask;
}
@@ -2424,20 +2625,36 @@ int sqlite3VdbeFinalize(Vdbe *p){
}
/*
-** Call the destructor for each auxdata entry in pVdbeFunc for which
-** the corresponding bit in mask is clear. Auxdata entries beyond 31
-** are always destroyed. To destroy all auxdata entries, call this
-** routine with mask==0.
+** If parameter iOp is less than zero, then invoke the destructor for
+** all auxiliary data pointers currently cached by the VM passed as
+** the first argument.
+**
+** Or, if iOp is greater than or equal to zero, then the destructor is
+** only invoked for those auxiliary data pointers created by the user
+** function invoked by the OP_Function opcode at instruction iOp of
+** VM pVdbe, and only then if:
+**
+** * the associated function parameter is the 32nd or later (counting
+** from left to right), or
+**
+** * the corresponding bit in argument mask is clear (where the first
+** function parameter corrsponds to bit 0 etc.).
*/
-void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
- int i;
- for(i=0; i<pVdbeFunc->nAux; i++){
- struct AuxData *pAux = &pVdbeFunc->apAux[i];
- if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){
+void sqlite3VdbeDeleteAuxData(Vdbe *pVdbe, int iOp, int mask){
+ AuxData **pp = &pVdbe->pAuxData;
+ while( *pp ){
+ AuxData *pAux = *pp;
+ if( (iOp<0)
+ || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg))))
+ ){
+ testcase( pAux->iArg==31 );
if( pAux->xDelete ){
pAux->xDelete(pAux->pAux);
}
- pAux->pAux = 0;
+ *pp = pAux->pNext;
+ sqlite3DbFree(pVdbe->db, pAux);
+ }else{
+ pp= &pAux->pNext;
}
}
}
@@ -2463,7 +2680,6 @@ void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
}
for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
@@ -2527,13 +2743,13 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
#endif
p->deferredMoveto = 0;
p->cacheStatus = CACHE_STALE;
- }else if( ALWAYS(p->pCursor) ){
+ }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;
+ if( hasMoved==2 ) p->nullRow = 1;
}
}
return SQLITE_OK;
@@ -2586,7 +2802,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
*/
u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
int flags = pMem->flags;
- int n;
+ u32 n;
if( flags&MEM_Null ){
return 0;
@@ -2616,11 +2832,11 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
return 7;
}
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
- n = pMem->n;
+ assert( pMem->n>=0 );
+ n = (u32)pMem->n;
if( flags & MEM_Zero ){
n += pMem->u.nZero;
}
- assert( n>=0 );
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
@@ -2694,21 +2910,15 @@ static u64 floatSwap(u64 in){
** buf. It is assumed that the caller has allocated sufficient space.
** Return the number of bytes written.
**
-** nBuf is the amount of space left in buf[]. nBuf must always be
-** large enough to hold the entire field. Except, if the field is
-** a blob with a zero-filled tail, then buf[] might be just the right
-** size to hold everything except for the zero-filled tail. If buf[]
-** is only big enough to hold the non-zero prefix, then only write that
-** prefix into buf[]. But if buf[] is large enough to hold both the
-** prefix and the tail then write the prefix and set the tail to all
-** zeros.
+** nBuf is the amount of space left in buf[]. The caller is responsible
+** for allocating enough space to buf[] to hold the entire field, exclusive
+** of the pMem->u.nZero bytes for a MEM_Zero value.
**
** Return the number of bytes actually written into buf[]. The number
** of bytes in the zero-filled tail is included in the return value only
** if those bytes were zeroed in buf[].
*/
-u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
- u32 serial_type = sqlite3VdbeSerialType(pMem, file_format);
+u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
u32 len;
/* Integer and Real */
@@ -2723,7 +2933,6 @@ u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
v = pMem->u.i;
}
len = i = sqlite3VdbeSerialTypeLen(serial_type);
- assert( len<=(u32)nBuf );
while( i-- ){
buf[i] = (u8)(v&0xFF);
v >>= 8;
@@ -2735,17 +2944,8 @@ u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
if( serial_type>=12 ){
assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
== (int)sqlite3VdbeSerialTypeLen(serial_type) );
- assert( pMem->n<=nBuf );
len = pMem->n;
memcpy(buf, pMem->z, len);
- if( pMem->flags & MEM_Zero ){
- len += pMem->u.nZero;
- assert( nBuf>=0 );
- if( len > (u32)nBuf ){
- len = (u32)nBuf;
- }
- memset(&buf[pMem->n], 0, len-pMem->n);
- }
return len;
}
@@ -2753,6 +2953,14 @@ u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
return 0;
}
+/* Input "x" is a sequence of unsigned characters that represent a
+** big-endian integer. Return the equivalent native integer
+*/
+#define ONE_BYTE_INT(x) ((i8)(x)[0])
+#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1])
+#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2])
+#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
/*
** Deserialize the data blob pointed to by buf as serial type serial_type
** and store the result in pMem. Return the number of bytes read.
@@ -2762,6 +2970,8 @@ u32 sqlite3VdbeSerialGet(
u32 serial_type, /* Serial type to deserialize */
Mem *pMem /* Memory cell to write value into */
){
+ u64 x;
+ u32 y;
switch( serial_type ){
case 10: /* Reserved for future use */
case 11: /* Reserved for future use */
@@ -2770,37 +2980,38 @@ u32 sqlite3VdbeSerialGet(
break;
}
case 1: { /* 1-byte signed integer */
- pMem->u.i = (signed char)buf[0];
+ pMem->u.i = ONE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 1;
}
case 2: { /* 2-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<8) | buf[1];
+ pMem->u.i = TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 2;
}
case 3: { /* 3-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2];
+ pMem->u.i = THREE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 3;
}
case 4: { /* 4-byte signed integer */
- pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
+ y = FOUR_BYTE_UINT(buf);
+ pMem->u.i = (i64)*(int*)&y;
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 4;
}
case 5: { /* 6-byte signed integer */
- u64 x = (((signed char)buf[0])<<8) | buf[1];
- u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
- x = (x<<32) | y;
- pMem->u.i = *(i64*)&x;
+ pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 6;
}
case 6: /* 8-byte signed integer */
case 7: { /* IEEE floating point */
- u64 x;
- u32 y;
#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
/* Verify that integers and floating point values use the same
** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
@@ -2813,13 +3024,13 @@ u32 sqlite3VdbeSerialGet(
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];
- y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7];
+ x = FOUR_BYTE_UINT(buf);
+ y = FOUR_BYTE_UINT(buf+4);
x = (x<<32) | y;
if( serial_type==6 ){
pMem->u.i = *(i64*)&x;
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
}else{
assert( sizeof(x)==8 && sizeof(pMem->r)==8 );
swapMixedEndianFloat(x);
@@ -2835,15 +3046,12 @@ u32 sqlite3VdbeSerialGet(
return 0;
}
default: {
+ static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem };
u32 len = (serial_type-12)/2;
pMem->z = (char *)buf;
pMem->n = len;
pMem->xDel = 0;
- if( serial_type&0x01 ){
- pMem->flags = MEM_Str | MEM_Ephem;
- }else{
- pMem->flags = MEM_Blob | MEM_Ephem;
- }
+ pMem->flags = aFlag[serial_type&1];
return len;
}
}
@@ -2914,7 +3122,7 @@ void sqlite3VdbeRecordUnpack(
u32 szHdr;
Mem *pMem = p->aMem;
- p->flags = 0;
+ p->default_rc = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
idx = getVarint32(aKey, szHdr);
d = szHdr;
@@ -2935,32 +3143,23 @@ void sqlite3VdbeRecordUnpack(
p->nField = u;
}
+#if SQLITE_DEBUG
/*
-** 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 key1 is less than, equal to or
-** greater than key2. 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.
-** The key with fewer fields is usually compares less than the
-** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set
-** and the common prefixes are equal, then key1 is less than key2.
-** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
-** equal, then the keys are considered to be equal and
-** the parts beyond the common prefix are ignored.
+** This function compares two index or table record keys in the same way
+** as the sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(),
+** this function deserializes and compares values using the
+** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
+** in assert() statements to ensure that the optimized code in
+** sqlite3VdbeRecordCompare() returns results with these two primitives.
*/
-int sqlite3VdbeRecordCompare(
+static int vdbeRecordCompareDebug(
int nKey1, const void *pKey1, /* Left key */
- UnpackedRecord *pPKey2 /* Right key */
+ const UnpackedRecord *pPKey2 /* Right key */
){
- int d1; /* Offset into aKey[] of next data element */
+ 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;
KeyInfo *pKeyInfo;
@@ -2983,14 +3182,27 @@ int sqlite3VdbeRecordCompare(
idx1 = getVarint32(aKey1, szHdr1);
d1 = szHdr1;
- nField = pKeyInfo->nField;
+ assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->aSortOrder!=0 );
- while( idx1<szHdr1 && i<pPKey2->nField ){
+ assert( pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
u32 serial_type1;
/* Read the serial types for the next element in each key. */
idx1 += getVarint32( aKey1+idx1, serial_type1 );
- if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
+
+ /* Verify that there is enough key space remaining to avoid
+ ** a buffer overread. The "d1+serial_type1+2" subexpression will
+ ** always be greater than or equal to the amount of required key space.
+ ** Use that approximation to avoid the more expensive call to
+ ** sqlite3VdbeSerialTypeLen() in the common case.
+ */
+ if( d1+serial_type1+2>(u32)nKey1
+ && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1
+ ){
+ break;
+ }
/* Extract the values to be compared.
*/
@@ -2998,32 +3210,16 @@ int sqlite3VdbeRecordCompare(
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
- i<nField ? pKeyInfo->aColl[i] : 0);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
if( rc!=0 ){
assert( mem1.zMalloc==0 ); /* See comment below */
-
- /* Invert the result if we are using DESC sort order. */
- if( i<nField && pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc; /* Invert the result for DESC sort order. */
}
-
- /* If the PREFIX_SEARCH flag is set and all fields except the final
- ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
- ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
- ** This is used by the OP_IsUnique opcode.
- */
- if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
- assert( idx1==szHdr1 && rc );
- assert( mem1.flags & MEM_Int );
- pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
- pPKey2->rowid = mem1.u.i;
- }
-
return rc;
}
i++;
- }
+ }while( idx1<szHdr1 && i<pPKey2->nField );
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
@@ -3032,24 +3228,594 @@ int sqlite3VdbeRecordCompare(
assert( mem1.zMalloc==0 );
/* rc==0 here means that one of the keys ran out of fields and
- ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
- ** flag is set, then break the tie by treating key2 as larger.
- ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
- ** are considered to be equal. Otherwise, the longer key is the
- ** larger. As it happens, the pPKey2 will always be the longer
- ** if there is a difference.
+ ** all the fields up to that point were equal. Return the the default_rc
+ ** value. */
+ return pPKey2->default_rc;
+}
+#endif
+
+/*
+** Both *pMem1 and *pMem2 contain string values. Compare the two values
+** using the collation sequence pColl. As usual, return a negative , zero
+** or positive value if *pMem1 is less than, equal to or greater than
+** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
+*/
+static int vdbeCompareMemString(
+ const Mem *pMem1,
+ const Mem *pMem2,
+ const CollSeq *pColl
+){
+ if( pMem1->enc==pColl->enc ){
+ /* The strings are already in the correct encoding. Call the
+ ** comparison function directly */
+ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
+ }else{
+ int rc;
+ const void *v1, *v2;
+ int n1, n2;
+ Mem c1;
+ Mem c2;
+ memset(&c1, 0, sizeof(c1));
+ memset(&c2, 0, sizeof(c2));
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
+ n1 = v1==0 ? 0 : c1.n;
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
+ n2 = v2==0 ? 0 : c2.n;
+ rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
+ sqlite3VdbeMemRelease(&c1);
+ sqlite3VdbeMemRelease(&c2);
+ return rc;
+ }
+}
+
+/*
+** Compare the values contained by the two memory cells, returning
+** negative, zero or positive if pMem1 is less than, equal to, or greater
+** than pMem2. Sorting order is NULL's first, followed by numbers (integers
+** and reals) sorted numerically, followed by text ordered by the collating
+** sequence pColl and finally blob's ordered by memcmp().
+**
+** Two NULL values are considered equal by this function.
+*/
+int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
+ int rc;
+ int f1, f2;
+ int combined_flags;
+
+ f1 = pMem1->flags;
+ f2 = pMem2->flags;
+ combined_flags = f1|f2;
+ assert( (combined_flags & MEM_RowSet)==0 );
+
+ /* If one value is NULL, it is less than the other. If both values
+ ** are NULL, return 0.
*/
- assert( rc==0 );
- if( pPKey2->flags & UNPACKED_INCRKEY ){
- rc = -1;
- }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
- /* Leave rc==0 */
- }else if( idx1<szHdr1 ){
- rc = 1;
+ if( combined_flags&MEM_Null ){
+ return (f2&MEM_Null) - (f1&MEM_Null);
+ }
+
+ /* If one value is a number and the other is not, the number is less.
+ ** If both are numbers, compare as reals if one is a real, or as integers
+ ** if both values are integers.
+ */
+ if( combined_flags&(MEM_Int|MEM_Real) ){
+ double r1, r2;
+ if( (f1 & f2 & MEM_Int)!=0 ){
+ if( pMem1->u.i < pMem2->u.i ) return -1;
+ if( pMem1->u.i > pMem2->u.i ) return 1;
+ return 0;
+ }
+ if( (f1&MEM_Real)!=0 ){
+ r1 = pMem1->r;
+ }else if( (f1&MEM_Int)!=0 ){
+ r1 = (double)pMem1->u.i;
+ }else{
+ return 1;
+ }
+ if( (f2&MEM_Real)!=0 ){
+ r2 = pMem2->r;
+ }else if( (f2&MEM_Int)!=0 ){
+ r2 = (double)pMem2->u.i;
+ }else{
+ return -1;
+ }
+ if( r1<r2 ) return -1;
+ if( r1>r2 ) return 1;
+ return 0;
+ }
+
+ /* If one value is a string and the other is a blob, the string is less.
+ ** If both are strings, compare using the collating functions.
+ */
+ if( combined_flags&MEM_Str ){
+ if( (f1 & MEM_Str)==0 ){
+ return 1;
+ }
+ if( (f2 & MEM_Str)==0 ){
+ return -1;
+ }
+
+ assert( pMem1->enc==pMem2->enc );
+ assert( pMem1->enc==SQLITE_UTF8 ||
+ pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
+
+ /* The collation sequence must be defined at this point, even if
+ ** the user deletes the collation sequence after the vdbe program is
+ ** compiled (this was not always the case).
+ */
+ assert( !pColl || pColl->xCmp );
+
+ if( pColl ){
+ return vdbeCompareMemString(pMem1, pMem2, pColl);
+ }
+ /* If a NULL pointer was passed as the collate function, fall through
+ ** to the blob case and use memcmp(). */
+ }
+
+ /* Both values must be blobs. Compare using memcmp(). */
+ rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
+ if( rc==0 ){
+ rc = pMem1->n - pMem2->n;
}
return rc;
}
-
+
+
+/*
+** The first argument passed to this function is a serial-type that
+** corresponds to an integer - all values between 1 and 9 inclusive
+** except 7. The second points to a buffer containing an integer value
+** serialized according to serial_type. This function deserializes
+** and returns the value.
+*/
+static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
+ u32 y;
+ assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) );
+ switch( serial_type ){
+ case 0:
+ case 1:
+ testcase( aKey[0]&0x80 );
+ return ONE_BYTE_INT(aKey);
+ case 2:
+ testcase( aKey[0]&0x80 );
+ return TWO_BYTE_INT(aKey);
+ case 3:
+ testcase( aKey[0]&0x80 );
+ return THREE_BYTE_INT(aKey);
+ case 4: {
+ testcase( aKey[0]&0x80 );
+ y = FOUR_BYTE_UINT(aKey);
+ return (i64)*(int*)&y;
+ }
+ case 5: {
+ testcase( aKey[0]&0x80 );
+ return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ }
+ case 6: {
+ u64 x = FOUR_BYTE_UINT(aKey);
+ testcase( aKey[0]&0x80 );
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ return (i64)*(i64*)&x;
+ }
+ }
+
+ return (serial_type - 8);
+}
+
+/*
+** 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 key1 is less than, equal to or
+** greater than key2. 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.
+**
+** If argument bSkip is non-zero, it is assumed that the caller has already
+** determined that the first fields of the keys are equal.
+**
+** Key1 and Key2 do not have to contain the same number of fields. If all
+** fields that appear in both keys are equal, then pPKey2->default_rc is
+** returned.
+**
+** If database corruption is discovered, set pPKey2->isCorrupt to non-zero
+** and return 0.
+*/
+int sqlite3VdbeRecordCompare(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2, /* Right key */
+ int bSkip /* If true, skip the first field */
+){
+ u32 d1; /* Offset into aKey[] of next data element */
+ int i; /* Index of next field to compare */
+ u32 szHdr1; /* Size of record header in bytes */
+ u32 idx1; /* Offset of first type in header */
+ int rc = 0; /* Return value */
+ Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */
+ KeyInfo *pKeyInfo = pPKey2->pKeyInfo;
+ const unsigned char *aKey1 = (const unsigned char *)pKey1;
+ Mem mem1;
+
+ /* If bSkip is true, then the caller has already determined that the first
+ ** two elements in the keys are equal. Fix the various stack variables so
+ ** that this routine begins comparing at the second field. */
+ if( bSkip ){
+ u32 s1;
+ idx1 = 1 + getVarint32(&aKey1[1], s1);
+ szHdr1 = aKey1[0];
+ d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1);
+ i = 1;
+ pRhs++;
+ }else{
+ idx1 = getVarint32(aKey1, szHdr1);
+ d1 = szHdr1;
+ if( d1>(unsigned)nKey1 ){
+ pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ i = 0;
+ }
+
+ VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */
+ assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
+ || CORRUPT_DB );
+ assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
+ u32 serial_type;
+
+ /* RHS is an integer */
+ if( pRhs->flags & MEM_Int ){
+ serial_type = aKey1[idx1];
+ testcase( serial_type==12 );
+ if( serial_type>=12 ){
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else if( serial_type==7 ){
+ double rhs = (double)pRhs->u.i;
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ if( mem1.r<rhs ){
+ rc = -1;
+ }else if( mem1.r>rhs ){
+ rc = +1;
+ }
+ }else{
+ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
+ i64 rhs = pRhs->u.i;
+ if( lhs<rhs ){
+ rc = -1;
+ }else if( lhs>rhs ){
+ rc = +1;
+ }
+ }
+ }
+
+ /* RHS is real */
+ else if( pRhs->flags & MEM_Real ){
+ serial_type = aKey1[idx1];
+ if( serial_type>=12 ){
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else{
+ double rhs = pRhs->r;
+ double lhs;
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ if( serial_type==7 ){
+ lhs = mem1.r;
+ }else{
+ lhs = (double)mem1.u.i;
+ }
+ if( lhs<rhs ){
+ rc = -1;
+ }else if( lhs>rhs ){
+ rc = +1;
+ }
+ }
+ }
+
+ /* RHS is a string */
+ else if( pRhs->flags & MEM_Str ){
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 ){
+ rc = -1;
+ }else if( !(serial_type & 0x01) ){
+ rc = +1;
+ }else{
+ mem1.n = (serial_type - 12) / 2;
+ testcase( (d1+mem1.n)==(unsigned)nKey1 );
+ testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
+ if( (d1+mem1.n) > (unsigned)nKey1 ){
+ pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else if( pKeyInfo->aColl[i] ){
+ mem1.enc = pKeyInfo->enc;
+ mem1.db = pKeyInfo->db;
+ mem1.flags = MEM_Str;
+ mem1.z = (char*)&aKey1[d1];
+ rc = vdbeCompareMemString(&mem1, pRhs, pKeyInfo->aColl[i]);
+ }else{
+ int nCmp = MIN(mem1.n, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = mem1.n - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is a blob */
+ else if( pRhs->flags & MEM_Blob ){
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 || (serial_type & 0x01) ){
+ rc = -1;
+ }else{
+ int nStr = (serial_type - 12) / 2;
+ testcase( (d1+nStr)==(unsigned)nKey1 );
+ testcase( (d1+nStr+1)==(unsigned)nKey1 );
+ if( (d1+nStr) > (unsigned)nKey1 ){
+ pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else{
+ int nCmp = MIN(nStr, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = nStr - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is null */
+ else{
+ serial_type = aKey1[idx1];
+ rc = (serial_type!=0);
+ }
+
+ if( rc!=0 ){
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc;
+ }
+ assert( CORRUPT_DB
+ || (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
+ || (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
+ || pKeyInfo->db->mallocFailed
+ );
+ assert( mem1.zMalloc==0 ); /* See comment below */
+ return rc;
+ }
+
+ i++;
+ pRhs++;
+ d1 += sqlite3VdbeSerialTypeLen(serial_type);
+ idx1 += sqlite3VarintLen(serial_type);
+ }while( idx1<(unsigned)szHdr1 && i<pPKey2->nField && d1<=(unsigned)nKey1 );
+
+ /* No memory allocation is ever used on mem1. Prove this using
+ ** the following assert(). If the assert() fails, it indicates a
+ ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). */
+ assert( mem1.zMalloc==0 );
+
+ /* rc==0 here means that one or both of the keys ran out of fields and
+ ** all the fields up to that point were equal. Return the the default_rc
+ ** value. */
+ assert( CORRUPT_DB
+ || pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
+ || pKeyInfo->db->mallocFailed
+ );
+ return pPKey2->default_rc;
+}
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is an integer, and (b) the
+** size-of-header varint at the start of (pKey1/nKey1) fits in a single
+** byte (i.e. is less than 128).
+**
+** To avoid concerns about buffer overreads, this routine is only used
+** on schemas where the maximum valid header size is 63 bytes or less.
+*/
+static int vdbeRecordCompareInt(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2, /* Right key */
+ int bSkip /* Ignored */
+){
+ const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F];
+ int serial_type = ((const u8*)pKey1)[1];
+ int res;
+ u32 y;
+ u64 x;
+ i64 v = pPKey2->aMem[0].u.i;
+ i64 lhs;
+ UNUSED_PARAMETER(bSkip);
+
+ assert( bSkip==0 );
+ assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB );
+ switch( serial_type ){
+ case 1: { /* 1-byte signed integer */
+ lhs = ONE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 2: { /* 2-byte signed integer */
+ lhs = TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 3: { /* 3-byte signed integer */
+ lhs = THREE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 4: { /* 4-byte signed integer */
+ y = FOUR_BYTE_UINT(aKey);
+ lhs = (i64)*(int*)&y;
+ testcase( lhs<0 );
+ break;
+ }
+ case 5: { /* 6-byte signed integer */
+ lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 6: { /* 8-byte signed integer */
+ x = FOUR_BYTE_UINT(aKey);
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ lhs = *(i64*)&x;
+ testcase( lhs<0 );
+ break;
+ }
+ case 8:
+ lhs = 0;
+ break;
+ case 9:
+ lhs = 1;
+ break;
+
+ /* This case could be removed without changing the results of running
+ ** this code. Including it causes gcc to generate a faster switch
+ ** statement (since the range of switch targets now starts at zero and
+ ** is contiguous) but does not cause any duplicate code to be generated
+ ** (as gcc is clever enough to combine the two like cases). Other
+ ** compilers might be similar. */
+ case 0: case 7:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 0);
+
+ default:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 0);
+ }
+
+ if( v>lhs ){
+ res = pPKey2->r1;
+ }else if( v<lhs ){
+ res = pPKey2->r2;
+ }else if( pPKey2->nField>1 ){
+ /* The first fields of the two keys are equal. Compare the trailing
+ ** fields. */
+ res = sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 1);
+ }else{
+ /* The first fields of the two keys are equal and there are no trailing
+ ** fields. Return pPKey2->default_rc in this case. */
+ res = pPKey2->default_rc;
+ }
+
+ assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
+ || (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
+ || (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
+ || CORRUPT_DB
+ );
+ return res;
+}
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is a string, that (b) the first field
+** uses the collation sequence BINARY and (c) that the size-of-header varint
+** at the start of (pKey1/nKey1) fits in a single byte.
+*/
+static int vdbeRecordCompareString(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2, /* Right key */
+ int bSkip
+){
+ const u8 *aKey1 = (const u8*)pKey1;
+ int serial_type;
+ int res;
+ UNUSED_PARAMETER(bSkip);
+
+ assert( bSkip==0 );
+ getVarint32(&aKey1[1], serial_type);
+
+ if( serial_type<12 ){
+ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */
+ }else if( !(serial_type & 0x01) ){
+ res = pPKey2->r2; /* (pKey1/nKey1) is a blob */
+ }else{
+ int nCmp;
+ int nStr;
+ int szHdr = aKey1[0];
+
+ nStr = (serial_type-12) / 2;
+ if( (szHdr + nStr) > nKey1 ){
+ pPKey2->isCorrupt = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ nCmp = MIN( pPKey2->aMem[0].n, nStr );
+ res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
+
+ if( res==0 ){
+ res = nStr - pPKey2->aMem[0].n;
+ if( res==0 ){
+ if( pPKey2->nField>1 ){
+ res = sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2, 1);
+ }else{
+ res = pPKey2->default_rc;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }
+
+ assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
+ || (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
+ || (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
+ || CORRUPT_DB
+ || pPKey2->pKeyInfo->db->mallocFailed
+ );
+ return res;
+}
+
+/*
+** Return a pointer to an sqlite3VdbeRecordCompare() compatible function
+** suitable for comparing serialized records to the unpacked record passed
+** as the only argument.
+*/
+RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
+ /* varintRecordCompareInt() and varintRecordCompareString() both assume
+ ** that the size-of-header varint that occurs at the start of each record
+ ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt()
+ ** also assumes that it is safe to overread a buffer by at least the
+ ** maximum possible legal header size plus 8 bytes. Because there is
+ ** guaranteed to be at least 74 (but not 136) bytes of padding following each
+ ** buffer passed to varintRecordCompareInt() this makes it convenient to
+ ** limit the size of the header to 64 bytes in cases where the first field
+ ** is an integer.
+ **
+ ** The easiest way to enforce this limit is to consider only records with
+ ** 13 fields or less. If the first field is an integer, the maximum legal
+ ** header size is (12*5 + 1 + 1) bytes. */
+ if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
+ int flags = p->aMem[0].flags;
+ if( p->pKeyInfo->aSortOrder[0] ){
+ p->r1 = 1;
+ p->r2 = -1;
+ }else{
+ p->r1 = -1;
+ p->r2 = 1;
+ }
+ if( (flags & MEM_Int) ){
+ return vdbeRecordCompareInt;
+ }
+ testcase( flags & MEM_Real );
+ testcase( flags & MEM_Null );
+ testcase( flags & MEM_Blob );
+ if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){
+ assert( flags & MEM_Str );
+ return vdbeRecordCompareString;
+ }
+ }
+
+ return sqlite3VdbeRecordCompare;
+}
/*
** pCur points at an index entry created using the OP_MakeRecord opcode.
@@ -3081,7 +3847,7 @@ int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Read in the complete content of the index entry */
memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
@@ -3140,9 +3906,9 @@ idx_rowid_corruption:
** of the keys prior to the final rowid, not the entire key.
*/
int sqlite3VdbeIdxKeyCompare(
- VdbeCursor *pC, /* The cursor to compare against */
- UnpackedRecord *pUnpacked, /* Unpacked version of key to compare against */
- int *res /* Write the comparison result here */
+ VdbeCursor *pC, /* The cursor to compare against */
+ UnpackedRecord *pUnpacked, /* Unpacked version of key */
+ int *res /* Write the comparison result here */
){
i64 nCellKey = 0;
int rc;
@@ -3152,19 +3918,18 @@ int sqlite3VdbeIdxKeyCompare(
assert( sqlite3BtreeCursorIsValid(pCur) );
VVA_ONLY(rc =) sqlite3BtreeKeySize(pCur, &nCellKey);
assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
- /* nCellKey will always be between 0 and 0xffffffff because of the say
+ /* nCellKey will always be between 0 and 0xffffffff because of the way
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
if( nCellKey<=0 || nCellKey>0x7fffffff ){
*res = 0;
return SQLITE_CORRUPT_BKPT;
}
memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m);
+ rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
- assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH );
- *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
+ *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked, 0);
sqlite3VdbeMemRelease(&m);
return SQLITE_OK;
}
@@ -3219,7 +3984,7 @@ sqlite3 *sqlite3VdbeDb(Vdbe *v){
**
** The returned value must be freed by the caller using sqlite3ValueFree().
*/
-sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
+sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){
assert( iVar>0 );
if( v ){
Mem *pMem = &v->aVar[iVar-1];
@@ -3228,7 +3993,6 @@ sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
if( pRet ){
sqlite3VdbeMemCopy((Mem *)pRet, pMem);
sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8);
- sqlite3VdbeMemStoreType((Mem *)pRet);
}
return pRet;
}
@@ -3249,3 +4013,18 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
v->expmask |= ((u32)1 << (iVar-1));
}
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
+** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
+** in memory obtained from sqlite3DbMalloc).
+*/
+void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
+ sqlite3 *db = p->db;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
+ sqlite3_free(pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 2e8fd8e..083f3f4 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -64,7 +64,8 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
rc = sqlite3_step(p->pStmt);
if( rc==SQLITE_ROW ){
- u32 type = v->apCsr[0]->aType[p->iCol];
+ VdbeCursor *pC = v->apCsr[0];
+ u32 type = pC->aType[p->iCol];
if( type<12 ){
zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
type==0?"null": type==7?"real": "integer"
@@ -73,12 +74,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
sqlite3_finalize(p->pStmt);
p->pStmt = 0;
}else{
- p->iOffset = v->apCsr[0]->aOffset[p->iCol];
+ p->iOffset = pC->aType[p->iCol + pC->nField];
p->nByte = sqlite3VdbeSerialTypeLen(type);
- p->pCsr = v->apCsr[0]->pCursor;
- sqlite3BtreeEnterCursor(p->pCsr);
- sqlite3BtreeCacheOverflow(p->pCsr);
- sqlite3BtreeLeaveCursor(p->pCsr);
+ p->pCsr = pC->pCursor;
+ sqlite3BtreeIncrblobCursor(p->pCsr);
}
}
@@ -132,22 +131,20 @@ int sqlite3_blob_open(
** which closes the b-tree cursor and (possibly) commits the
** transaction.
*/
+ static const int iLn = VDBE_OFFSET_LINENO(4);
static const VdbeOpList openBlob[] = {
- {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
- {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
- {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */
-
+ /* {OP_Transaction, 0, 0, 0}, // 0: Inserted separately */
+ {OP_TableLock, 0, 0, 0}, /* 1: Acquire a read or write lock */
/* One of the following two instructions is replaced by an OP_Noop. */
- {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
- {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
-
- {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
- {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
- {OP_Column, 0, 0, 1}, /* 7 */
- {OP_ResultRow, 1, 0, 0}, /* 8 */
- {OP_Goto, 0, 5, 0}, /* 9 */
- {OP_Close, 0, 0, 0}, /* 10 */
- {OP_Halt, 0, 0, 0}, /* 11 */
+ {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */
+ {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */
+ {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */
+ {OP_NotExists, 0, 10, 1}, /* 5: Seek the cursor */
+ {OP_Column, 0, 0, 1}, /* 6 */
+ {OP_ResultRow, 1, 0, 0}, /* 7 */
+ {OP_Goto, 0, 4, 0}, /* 8 */
+ {OP_Close, 0, 0, 0}, /* 9 */
+ {OP_Halt, 0, 0, 0}, /* 10 */
};
int rc = SQLITE_OK;
@@ -178,6 +175,10 @@ int sqlite3_blob_open(
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
}
+ if( pTab && !HasRowid(pTab) ){
+ pTab = 0;
+ sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
+ }
#ifndef SQLITE_OMIT_VIEW
if( pTab && pTab->pSelect ){
pTab = 0;
@@ -235,7 +236,7 @@ int sqlite3_blob_open(
#endif
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int j;
- for(j=0; j<pIdx->nColumn; j++){
+ for(j=0; j<pIdx->nKeyCol; j++){
if( pIdx->aiColumn[j]==iCol ){
zFault = "indexed";
}
@@ -250,42 +251,37 @@ int sqlite3_blob_open(
}
}
- pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
+ pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
Vdbe *v = (Vdbe *)pBlob->pStmt;
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);
-
- /* Configure the OP_VerifyCookie */
- sqlite3VdbeChangeP1(v, 1, iDb);
- sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
- sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
+ sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
+ pTab->pSchema->schema_cookie,
+ pTab->pSchema->iGeneration);
+ sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
/* Make sure a mutex is held on the table to be accessed */
sqlite3VdbeUsesBtree(v, iDb);
/* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
- sqlite3VdbeChangeToNoop(v, 2);
+ sqlite3VdbeChangeToNoop(v, 1);
#else
- sqlite3VdbeChangeP1(v, 2, iDb);
- sqlite3VdbeChangeP2(v, 2, pTab->tnum);
- sqlite3VdbeChangeP3(v, 2, flags);
- sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
+ sqlite3VdbeChangeP1(v, 1, iDb);
+ sqlite3VdbeChangeP2(v, 1, pTab->tnum);
+ sqlite3VdbeChangeP3(v, 1, flags);
+ sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
#endif
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */
- sqlite3VdbeChangeToNoop(v, 4 - flags);
- sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
- sqlite3VdbeChangeP3(v, 3 + flags, iDb);
+ sqlite3VdbeChangeToNoop(v, 3 - flags);
+ sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum);
+ sqlite3VdbeChangeP3(v, 2 + flags, iDb);
/* Configure the number of columns. Configure the cursor to
** think that the table has one more column than it really
@@ -294,8 +290,8 @@ int sqlite3_blob_open(
** we can invoke OP_Column to fill in the vdbe cursors type
** and offset cache without causing any IO.
*/
- sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
- sqlite3VdbeChangeP2(v, 7, pTab->nCol);
+ sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
+ sqlite3VdbeChangeP2(v, 6, pTab->nCol);
if( !db->mallocFailed ){
pParse->nVar = 1;
pParse->nMem = 1;
@@ -324,6 +320,7 @@ blob_open_out:
}
sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 8fc222e..cf44aa7 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -18,6 +18,42 @@
#include "sqliteInt.h"
#include "vdbeInt.h"
+#ifdef SQLITE_DEBUG
+/*
+** Check invariants on a Mem object.
+**
+** This routine is intended for use inside of assert() statements, like
+** this: assert( sqlite3VdbeCheckMemInvariants(pMem) );
+*/
+int sqlite3VdbeCheckMemInvariants(Mem *p){
+ /* The MEM_Dyn bit is set if and only if Mem.xDel is a non-NULL destructor
+ ** function for Mem.z
+ */
+ assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 );
+ assert( (p->flags & MEM_Dyn)!=0 || p->xDel==0 );
+
+ /* If p holds a string or blob, the Mem.z must point to exactly
+ ** one of the following:
+ **
+ ** (1) Memory in Mem.zMalloc and managed by the Mem object
+ ** (2) Memory to be freed using Mem.xDel
+ ** (3) An ephermal string or blob
+ ** (4) A static string or blob
+ */
+ if( (p->flags & (MEM_Str|MEM_Blob)) && p->z!=0 ){
+ assert(
+ ((p->z==p->zMalloc)? 1 : 0) +
+ ((p->flags&MEM_Dyn)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Ephem)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1
+ );
+ }
+
+ return 1;
+}
+#endif
+
+
/*
** If pMem is an object with a valid string representation, this routine
** ensures the internal encoding for the string representation is
@@ -59,57 +95,51 @@ int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
/*
** Make sure pMem->z points to a writable allocation of at least
-** n bytes.
-**
-** If the third argument passed to this function is true, then memory
-** cell pMem must contain a string or blob. In this case the content is
-** preserved. Otherwise, if the third parameter to this function is false,
-** any current string or blob value may be discarded.
+** min(n,32) bytes.
**
-** 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.
+** If the bPreserve argument is true, then copy of the content of
+** pMem->z into the new allocation. pMem must be either a string or
+** blob if bPreserve is true. If bPreserve is false, any prior content
+** in pMem->z is discarded.
*/
-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)
- );
+int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
+ assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
- /* If the preserve flag is set to true, then the memory cell must already
+ /* If the bPreserve flag is set to true, then the memory cell must already
** contain a valid string or blob value. */
- assert( preserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
+ assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
+ testcase( bPreserve && pMem->z==0 );
- if( n<32 ) n = 32;
- if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)<n ){
- if( preserve && pMem->z==pMem->zMalloc ){
+ if( pMem->zMalloc==0 || sqlite3DbMallocSize(pMem->db, pMem->zMalloc)<n ){
+ if( n<32 ) n = 32;
+ if( bPreserve && pMem->z==pMem->zMalloc ){
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
- preserve = 0;
+ bPreserve = 0;
}else{
sqlite3DbFree(pMem->db, pMem->zMalloc);
pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n);
}
+ if( pMem->zMalloc==0 ){
+ VdbeMemRelease(pMem);
+ pMem->z = 0;
+ pMem->flags = MEM_Null;
+ return SQLITE_NOMEM;
+ }
}
- if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){
+ if( pMem->z && bPreserve && pMem->z!=pMem->zMalloc ){
memcpy(pMem->zMalloc, pMem->z, pMem->n);
}
- if( pMem->flags&MEM_Dyn && pMem->xDel ){
- assert( pMem->xDel!=SQLITE_DYNAMIC );
+ if( (pMem->flags&MEM_Dyn)!=0 ){
+ assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC );
pMem->xDel((void *)(pMem->z));
}
pMem->z = pMem->zMalloc;
- if( pMem->z==0 ){
- pMem->flags = MEM_Null;
- }else{
- pMem->flags &= ~(MEM_Ephem|MEM_Static);
- }
+ pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static);
pMem->xDel = 0;
- return (pMem->z ? SQLITE_OK : SQLITE_NOMEM);
+ return SQLITE_OK;
}
/*
@@ -276,9 +306,9 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
sqlite3VdbeMemFinalize(p, p->u.pDef);
assert( (p->flags & MEM_Agg)==0 );
sqlite3VdbeMemRelease(p);
- }else if( p->flags&MEM_Dyn && p->xDel ){
+ }else if( p->flags&MEM_Dyn ){
assert( (p->flags&MEM_RowSet)==0 );
- assert( p->xDel!=SQLITE_DYNAMIC );
+ assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
p->xDel((void *)p->z);
p->xDel = 0;
}else if( p->flags&MEM_RowSet ){
@@ -291,27 +321,23 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
/*
** 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).
+** (Mem.flags==MEM_Str).
*/
void sqlite3VdbeMemRelease(Mem *p){
+ assert( sqlite3VdbeCheckMemInvariants(p) );
VdbeMemRelease(p);
- sqlite3DbFree(p->db, p->zMalloc);
+ if( p->zMalloc ){
+ sqlite3DbFree(p->db, p->zMalloc);
+ p->zMalloc = 0;
+ }
p->z = 0;
- p->zMalloc = 0;
- p->xDel = 0;
+ assert( p->xDel==0 ); /* Zeroed by VdbeMemRelease() above */
}
/*
** 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.
+** If the double is out of range of a 64-bit signed integer then
+** return the closest available 64-bit signed integer.
*/
static i64 doubleToInt64(double r){
#ifdef SQLITE_OMIT_FLOATING_POINT
@@ -328,14 +354,10 @@ static i64 doubleToInt64(double r){
static const i64 maxInt = LARGEST_INT64;
static const i64 minInt = SMALLEST_INT64;
- if( r<(double)minInt ){
- return minInt;
- }else if( r>(double)maxInt ){
- /* minInt is correct here - not maxInt. It turns out that assigning
- ** a very large positive number to an integer results in a very large
- ** negative integer. This makes no sense, but it is what x86 hardware
- ** does so for compatibility we will do the same in software. */
+ if( r<=(double)minInt ){
return minInt;
+ }else if( r>=(double)maxInt ){
+ return maxInt;
}else{
return (i64)r;
}
@@ -417,17 +439,11 @@ void sqlite3VdbeIntegerAffinity(Mem *pMem){
**
** The second and third terms in the following conditional enforces
** the second condition under the assumption that addition overflow causes
- ** values to wrap around. On x86 hardware, the third term is always
- ** true and could be omitted. But we leave it in because other
- ** architectures might behave differently.
+ ** values to wrap around.
*/
if( pMem->r==(double)pMem->u.i
&& pMem->u.i>SMALLEST_INT64
-#if defined(__i486__) || defined(__x86_64__)
- && ALWAYS(pMem->u.i<LARGEST_INT64)
-#else
&& pMem->u.i<LARGEST_INT64
-#endif
){
pMem->flags |= MEM_Int;
}
@@ -497,7 +513,9 @@ void sqlite3VdbeMemSetNull(Mem *pMem){
sqlite3RowSetClear(pMem->u.pRowSet);
}
MemSetTypeFlag(pMem, MEM_Null);
- pMem->type = SQLITE_NULL;
+}
+void sqlite3ValueSetNull(sqlite3_value *p){
+ sqlite3VdbeMemSetNull((Mem*)p);
}
/*
@@ -507,7 +525,6 @@ void sqlite3VdbeMemSetNull(Mem *pMem){
void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
sqlite3VdbeMemRelease(pMem);
pMem->flags = MEM_Blob|MEM_Zero;
- pMem->type = SQLITE_BLOB;
pMem->n = 0;
if( n<0 ) n = 0;
pMem->u.nZero = n;
@@ -530,7 +547,6 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
sqlite3VdbeMemRelease(pMem);
pMem->u.i = val;
pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
}
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -545,7 +561,6 @@ void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
sqlite3VdbeMemRelease(pMem);
pMem->r = val;
pMem->flags = MEM_Real;
- pMem->type = SQLITE_FLOAT;
}
}
#endif
@@ -601,7 +616,7 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
Mem *pX;
for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
- pX->flags |= MEM_Invalid;
+ pX->flags |= MEM_Undefined;
pX->pScopyFrom = 0;
}
}
@@ -612,7 +627,7 @@ void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
/*
** Size of struct Mem not including the Mem.zMalloc member.
*/
-#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc))
+#define MEMCELLSIZE offsetof(Mem,zMalloc)
/*
** Make an shallow copy of pFrom into pTo. Prior contents of
@@ -643,6 +658,7 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
VdbeMemRelease(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
+ pTo->xDel = 0;
if( pTo->flags&(MEM_Str|MEM_Blob) ){
if( 0==(pFrom->flags&MEM_Static) ){
@@ -753,7 +769,6 @@ int sqlite3VdbeMemSetStr(
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
if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
@@ -769,124 +784,6 @@ int sqlite3VdbeMemSetStr(
}
/*
-** Compare the values contained by the two memory cells, returning
-** negative, zero or positive if pMem1 is less than, equal to, or greater
-** than pMem2. Sorting order is NULL's first, followed by numbers (integers
-** and reals) sorted numerically, followed by text ordered by the collating
-** sequence pColl and finally blob's ordered by memcmp().
-**
-** Two NULL values are considered equal by this function.
-*/
-int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
- int rc;
- int f1, f2;
- int combined_flags;
-
- f1 = pMem1->flags;
- f2 = pMem2->flags;
- combined_flags = f1|f2;
- assert( (combined_flags & MEM_RowSet)==0 );
-
- /* If one value is NULL, it is less than the other. If both values
- ** are NULL, return 0.
- */
- if( combined_flags&MEM_Null ){
- return (f2&MEM_Null) - (f1&MEM_Null);
- }
-
- /* If one value is a number and the other is not, the number is less.
- ** If both are numbers, compare as reals if one is a real, or as integers
- ** if both values are integers.
- */
- if( combined_flags&(MEM_Int|MEM_Real) ){
- if( !(f1&(MEM_Int|MEM_Real)) ){
- return 1;
- }
- if( !(f2&(MEM_Int|MEM_Real)) ){
- return -1;
- }
- if( (f1 & f2 & MEM_Int)==0 ){
- double r1, r2;
- if( (f1&MEM_Real)==0 ){
- r1 = (double)pMem1->u.i;
- }else{
- r1 = pMem1->r;
- }
- if( (f2&MEM_Real)==0 ){
- r2 = (double)pMem2->u.i;
- }else{
- r2 = pMem2->r;
- }
- if( r1<r2 ) return -1;
- if( r1>r2 ) return 1;
- return 0;
- }else{
- assert( f1&MEM_Int );
- assert( f2&MEM_Int );
- if( pMem1->u.i < pMem2->u.i ) return -1;
- if( pMem1->u.i > pMem2->u.i ) return 1;
- return 0;
- }
- }
-
- /* If one value is a string and the other is a blob, the string is less.
- ** If both are strings, compare using the collating functions.
- */
- if( combined_flags&MEM_Str ){
- if( (f1 & MEM_Str)==0 ){
- return 1;
- }
- if( (f2 & MEM_Str)==0 ){
- return -1;
- }
-
- assert( pMem1->enc==pMem2->enc );
- assert( pMem1->enc==SQLITE_UTF8 ||
- pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
-
- /* The collation sequence must be defined at this point, even if
- ** the user deletes the collation sequence after the vdbe program is
- ** compiled (this was not always the case).
- */
- assert( !pColl || pColl->xCmp );
-
- if( pColl ){
- if( pMem1->enc==pColl->enc ){
- /* The strings are already in the correct encoding. Call the
- ** comparison function directly */
- return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
- }else{
- const void *v1, *v2;
- int n1, n2;
- Mem c1;
- Mem c2;
- memset(&c1, 0, sizeof(c1));
- memset(&c2, 0, sizeof(c2));
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
- n1 = v1==0 ? 0 : c1.n;
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
- n2 = v2==0 ? 0 : c2.n;
- rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
- sqlite3VdbeMemRelease(&c1);
- sqlite3VdbeMemRelease(&c2);
- return rc;
- }
- }
- /* If a NULL pointer was passed as the collate function, fall through
- ** to the blob case and use memcmp(). */
- }
-
- /* Both values must be blobs. Compare using memcmp(). */
- rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
- if( rc==0 ){
- rc = pMem1->n - pMem2->n;
- }
- return rc;
-}
-
-/*
** Move data out of a btree key or data field and into a Mem structure.
** The data or key is taken from the entry that pCur is currently pointing
** to. offset and amt determine what portion of the data or key to retrieve.
@@ -901,13 +798,13 @@ int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
*/
int sqlite3VdbeMemFromBtree(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
- int offset, /* Offset from the start of data to return bytes from. */
- int amt, /* Number of bytes to return. */
+ u32 offset, /* Offset from the start of data to return bytes from. */
+ u32 amt, /* Number of bytes to return. */
int key, /* If true, retrieve from the btree key, not data. */
Mem *pMem /* OUT: Return data in this Mem structure. */
){
char *zData; /* Data from the btree layer */
- int available = 0; /* Number of bytes available on the local btree page */
+ u32 available = 0; /* Number of bytes available on the local btree page */
int rc = SQLITE_OK; /* Return code */
assert( sqlite3BtreeCursorIsValid(pCur) );
@@ -922,26 +819,26 @@ int sqlite3VdbeMemFromBtree(
}
assert( zData!=0 );
- if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){
+ if( offset+amt<=available ){
sqlite3VdbeMemRelease(pMem);
pMem->z = &zData[offset];
pMem->flags = MEM_Blob|MEM_Ephem;
+ pMem->n = (int)amt;
}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, pMem->z);
}else{
rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
}
- pMem->z[amt] = 0;
- pMem->z[amt+1] = 0;
- if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_OK ){
+ pMem->z[amt] = 0;
+ pMem->z[amt+1] = 0;
+ pMem->flags = MEM_Blob|MEM_Term;
+ pMem->n = (int)amt;
+ }else{
sqlite3VdbeMemRelease(pMem);
}
}
- pMem->n = amt;
return rc;
}
@@ -999,50 +896,105 @@ 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;
}
/*
-** Create a new sqlite3_value object, containing the value of pExpr.
+** Context object passed by sqlite3Stat4ProbeSetValue() through to
+** valueNew(). See comments above valueNew() for details.
+*/
+struct ValueNewStat4Ctx {
+ Parse *pParse;
+ Index *pIdx;
+ UnpackedRecord **ppRec;
+ int iVal;
+};
+
+/*
+** Allocate and return a pointer to a new sqlite3_value object. If
+** the second argument to this function is NULL, the object is allocated
+** by calling sqlite3ValueNew().
**
-** This only works for very simple expressions that consist of one constant
-** 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.
+** Otherwise, if the second argument is non-zero, then this function is
+** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
+** already been allocated, allocate the UnpackedRecord structure that
+** that function will return to its caller here. Then return a pointer
+** an sqlite3_value within the UnpackedRecord.a[] array.
*/
-int sqlite3ValueFromExpr(
- 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 */
+static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( p ){
+ UnpackedRecord *pRec = p->ppRec[0];
+
+ if( pRec==0 ){
+ Index *pIdx = p->pIdx; /* Index being probed */
+ int nByte; /* Bytes of space to allocate */
+ int i; /* Counter variable */
+ int nCol = pIdx->nColumn; /* Number of index columns including rowid */
+
+ nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord));
+ pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
+ if( pRec ){
+ pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
+ if( pRec->pKeyInfo ){
+ assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
+ assert( pRec->pKeyInfo->enc==ENC(db) );
+ pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
+ for(i=0; i<nCol; i++){
+ pRec->aMem[i].flags = MEM_Null;
+ pRec->aMem[i].db = db;
+ }
+ }else{
+ sqlite3DbFree(db, pRec);
+ pRec = 0;
+ }
+ }
+ if( pRec==0 ) return 0;
+ p->ppRec[0] = pRec;
+ }
+
+ pRec->nField = p->iVal+1;
+ return &pRec->aMem[p->iVal];
+ }
+#else
+ UNUSED_PARAMETER(p);
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+ return sqlite3ValueNew(db);
+}
+
+/*
+** Extract a value from the supplied expression in the manner described
+** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
+** using valueNew().
+**
+** If pCtx is NULL and an error occurs after the sqlite3_value object
+** has been allocated, it is freed before returning. Or, if pCtx is not
+** NULL, it is assumed that the caller will free any allocated object
+** in all cases.
+*/
+static int valueFromExpr(
+ 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 */
+ struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
+ int rc = SQLITE_OK;
if( !pExpr ){
*ppVal = 0;
return SQLITE_OK;
}
op = pExpr->op;
-
- /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
- ** The ifdef here is to enable us to achieve 100% branch test coverage even
- ** when SQLITE_ENABLE_STAT3 is omitted.
- */
-#ifdef SQLITE_ENABLE_STAT3
- if( op==TK_REGISTER ) op = pExpr->op2;
-#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
-#endif
/* Handle negative integers in a single step. This is needed in the
** case when the value is -9223372036854775808.
@@ -1056,7 +1008,7 @@ int sqlite3ValueFromExpr(
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
@@ -1064,7 +1016,6 @@ int sqlite3ValueFromExpr(
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
- if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
}
if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
@@ -1073,16 +1024,18 @@ int sqlite3ValueFromExpr(
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
- sqlite3VdbeChangeEncoding(pVal, enc);
+ rc = sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
- if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
+ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal)
+ && pVal!=0
+ ){
sqlite3VdbeMemNumerify(pVal);
if( pVal->u.i==SMALLEST_INT64 ){
- pVal->flags &= MEM_Int;
+ pVal->flags &= ~MEM_Int;
pVal->flags |= MEM_Real;
- pVal->r = (double)LARGEST_INT64;
+ pVal->r = (double)SMALLEST_INT64;
}else{
pVal->u.i = -pVal->u.i;
}
@@ -1090,7 +1043,7 @@ int sqlite3ValueFromExpr(
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_NULL ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
@@ -1098,7 +1051,7 @@ int sqlite3ValueFromExpr(
int nVal;
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( !pVal ) goto no_mem;
zVal = &pExpr->u.zToken[2];
nVal = sqlite3Strlen30(zVal)-1;
@@ -1108,21 +1061,301 @@ int sqlite3ValueFromExpr(
}
#endif
- if( pVal ){
- sqlite3VdbeMemStoreType(pVal);
- }
*ppVal = pVal;
- return SQLITE_OK;
+ return rc;
no_mem:
db->mallocFailed = 1;
sqlite3DbFree(db, zVal);
- sqlite3ValueFree(pVal);
- *ppVal = 0;
+ assert( *ppVal==0 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pCtx==0 ) sqlite3ValueFree(pVal);
+#else
+ assert( pCtx==0 ); sqlite3ValueFree(pVal);
+#endif
return SQLITE_NOMEM;
}
/*
+** 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", "'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(
+ 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 */
+){
+ return valueFromExpr(db, pExpr, enc, affinity, ppVal, 0);
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** The implementation of the sqlite_record() function. This function accepts
+** a single argument of any type. The return value is a formatted database
+** record (a blob) containing the argument value.
+**
+** This is used to convert the value stored in the 'sample' column of the
+** sqlite_stat3 table to the record format SQLite uses internally.
+*/
+static void recordFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const int file_format = 1;
+ int iSerial; /* Serial type */
+ int nSerial; /* Bytes of space for iSerial as varint */
+ int nVal; /* Bytes of space required for argv[0] */
+ int nRet;
+ sqlite3 *db;
+ u8 *aRet;
+
+ UNUSED_PARAMETER( argc );
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format);
+ nSerial = sqlite3VarintLen(iSerial);
+ nVal = sqlite3VdbeSerialTypeLen(iSerial);
+ db = sqlite3_context_db_handle(context);
+
+ nRet = 1 + nSerial + nVal;
+ aRet = sqlite3DbMallocRaw(db, nRet);
+ if( aRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ aRet[0] = nSerial+1;
+ sqlite3PutVarint(&aRet[1], iSerial);
+ sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
+ sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, aRet);
+ }
+}
+
+/*
+** Register built-in functions used to help read ANALYZE data.
+*/
+void sqlite3AnalyzeFunctions(void){
+ static SQLITE_WSD FuncDef aAnalyzeTableFuncs[] = {
+ FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
+ };
+ int i;
+ FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
+ FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAnalyzeTableFuncs);
+ for(i=0; i<ArraySize(aAnalyzeTableFuncs); i++){
+ sqlite3FuncDefInsert(pHash, &aFunc[i]);
+ }
+}
+
+/*
+** Attempt to extract a value from pExpr and use it to construct *ppVal.
+**
+** If pAlloc is not NULL, then an UnpackedRecord object is created for
+** pAlloc if one does not exist and the new value is added to the
+** UnpackedRecord object.
+**
+** A value is extracted in the following cases:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The expression is a literal value.
+**
+** On success, *ppVal is made to point to the extracted value. The caller
+** is responsible for ensuring that the value is eventually freed.
+*/
+static int stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ int rc = SQLITE_OK;
+ sqlite3_value *pVal = 0;
+ sqlite3 *db = pParse->db;
+
+ /* Skip over any TK_COLLATE nodes */
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+
+ if( !pExpr ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ sqlite3VdbeMemSetNull((Mem*)pVal);
+ }
+ }else if( pExpr->op==TK_VARIABLE
+ || NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
+ ){
+ Vdbe *v;
+ int iBindVar = pExpr->iColumn;
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
+ if( (v = pParse->pReprepare)!=0 ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
+ if( rc==SQLITE_OK ){
+ sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
+ }
+ pVal->db = pParse->db;
+ }
+ }
+ }else{
+ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc);
+ }
+
+ assert( pVal==0 || pVal->db==db );
+ *ppVal = pVal;
+ return rc;
+}
+
+/*
+** This function is used to allocate and populate UnpackedRecord
+** structures intended to be compared against sample index keys stored
+** in the sqlite_stat4 table.
+**
+** A single call to this function attempts to populates field iVal (leftmost
+** is 0 etc.) of the unpacked record with a value extracted from expression
+** pExpr. Extraction of values is possible if:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The sqlite3ValueFromExpr() function is able to extract a value
+** from the expression (i.e. the expression is a literal value).
+**
+** If a value can be extracted, the affinity passed as the 5th argument
+** is applied to it before it is copied into the UnpackedRecord. Output
+** parameter *pbOk is set to true if a value is extracted, or false
+** otherwise.
+**
+** When this function is called, *ppRec must either point to an object
+** allocated by an earlier call to this function, or must be NULL. If it
+** is NULL and a value can be successfully extracted, a new UnpackedRecord
+** is allocated (and *ppRec set to point to it) before returning.
+**
+** Unless an error is encountered, SQLITE_OK is returned. It is not an
+** error if a value cannot be extracted from pExpr. If an error does
+** occur, an SQLite error code is returned.
+*/
+int sqlite3Stat4ProbeSetValue(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Index being probed */
+ UnpackedRecord **ppRec, /* IN/OUT: Probe record */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ int iVal, /* Array element to populate */
+ int *pbOk /* OUT: True if value was extracted */
+){
+ int rc;
+ sqlite3_value *pVal = 0;
+ struct ValueNewStat4Ctx alloc;
+
+ alloc.pParse = pParse;
+ alloc.pIdx = pIdx;
+ alloc.ppRec = ppRec;
+ alloc.iVal = iVal;
+
+ rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
+ assert( pVal==0 || pVal->db==pParse->db );
+ *pbOk = (pVal!=0);
+ return rc;
+}
+
+/*
+** Attempt to extract a value from expression pExpr using the methods
+** as described for sqlite3Stat4ProbeSetValue() above.
+**
+** If successful, set *ppVal to point to a new value object and return
+** SQLITE_OK. If no value can be extracted, but no other error occurs
+** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
+** does occur, return an SQLite error code. The final value of *ppVal
+** is undefined in this case.
+*/
+int sqlite3Stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal);
+}
+
+/*
+** Extract the iCol-th column from the nRec-byte record in pRec. Write
+** the column value into *ppVal. If *ppVal is initially NULL then a new
+** sqlite3_value object is allocated.
+**
+** If *ppVal is initially NULL then the caller is responsible for
+** ensuring that the value written into *ppVal is eventually freed.
+*/
+int sqlite3Stat4Column(
+ sqlite3 *db, /* Database handle */
+ const void *pRec, /* Pointer to buffer containing record */
+ int nRec, /* Size of buffer pRec in bytes */
+ int iCol, /* Column to extract */
+ sqlite3_value **ppVal /* OUT: Extracted value */
+){
+ u32 t; /* a column type code */
+ int nHdr; /* Size of the header in the record */
+ int iHdr; /* Next unread header byte */
+ int iField; /* Next unread data byte */
+ int szField; /* Size of the current data field */
+ int i; /* Column index */
+ u8 *a = (u8*)pRec; /* Typecast byte array */
+ Mem *pMem = *ppVal; /* Write result into this Mem object */
+
+ assert( iCol>0 );
+ iHdr = getVarint32(a, nHdr);
+ if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
+ iField = nHdr;
+ for(i=0; i<=iCol; i++){
+ iHdr += getVarint32(&a[iHdr], t);
+ testcase( iHdr==nHdr );
+ testcase( iHdr==nHdr+1 );
+ if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
+ szField = sqlite3VdbeSerialTypeLen(t);
+ iField += szField;
+ }
+ testcase( iField==nRec );
+ testcase( iField==nRec+1 );
+ if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
+ if( pMem==0 ){
+ pMem = *ppVal = sqlite3ValueNew(db);
+ if( pMem==0 ) return SQLITE_NOMEM;
+ }
+ sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
+ pMem->enc = ENC(db);
+ return SQLITE_OK;
+}
+
+/*
+** Unless it is NULL, the argument must be an UnpackedRecord object returned
+** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
+** the object.
+*/
+void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
+ if( pRec ){
+ int i;
+ int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
+ Mem *aMem = pRec->aMem;
+ sqlite3 *db = aMem[0].db;
+ for(i=0; i<nCol; i++){
+ sqlite3DbFree(db, aMem[i].zMalloc);
+ }
+ sqlite3KeyInfoUnref(pRec->pKeyInfo);
+ sqlite3DbFree(db, pRec);
+ }
+}
+#endif /* ifdef SQLITE_ENABLE_STAT4 */
+
+/*
** Change the string value of an sqlite3_value object
*/
void sqlite3ValueSetStr(
diff --git a/src/vdbesort.c b/src/vdbesort.c
index fdfc4a7..6a5855f 100644
--- a/src/vdbesort.c
+++ b/src/vdbesort.c
@@ -54,7 +54,7 @@ typedef struct FileWriter FileWriter;
** other key value. If the keys are equal (only possible with two EOF
** values), it doesn't matter which index is stored.
**
-** The (N/4) elements of aTree[] that preceed the final (N/2) described
+** The (N/4) elements of aTree[] that precede the final (N/2) described
** above contains the index of the smallest of each block of 4 iterators.
** And so on. So that aTree[1] contains the index of the iterator that
** currently points to the smallest key value. aTree[0] is unused.
@@ -350,7 +350,6 @@ static int vdbeSorterIterInit(
rc = sqlite3OsRead(
pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
);
- assert( rc!=SQLITE_IOERR_SHORT_READ );
}
if( rc==SQLITE_OK ){
@@ -386,7 +385,7 @@ static int vdbeSorterIterInit(
*/
static void vdbeSorterCompare(
const VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
- int bOmitRowid, /* Ignore rowid field at end of keys */
+ int nKeyCol, /* Num of columns. 0 means "all" */
const void *pKey1, int nKey1, /* Left side of comparison */
const void *pKey2, int nKey2, /* Right side of comparison */
int *pRes /* OUT: Result of comparison */
@@ -400,19 +399,18 @@ static void vdbeSorterCompare(
sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
}
- if( bOmitRowid ){
- r2->nField = pKeyInfo->nField;
- assert( r2->nField>0 );
- for(i=0; i<r2->nField; i++){
+ if( nKeyCol ){
+ r2->nField = nKeyCol;
+ for(i=0; i<nKeyCol; i++){
if( r2->aMem[i].flags & MEM_Null ){
*pRes = -1;
return;
}
}
- r2->flags |= UNPACKED_PREFIX_MATCH;
+ assert( r2->default_rc==0 );
}
- *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
+ *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2, 0);
}
/*
@@ -505,22 +503,39 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
}
/*
+** Reset a sorting cursor back to its original empty state.
+*/
+void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
+ if( pSorter->aIter ){
+ int i;
+ for(i=0; i<pSorter->nTree; i++){
+ vdbeSorterIterZero(db, &pSorter->aIter[i]);
+ }
+ sqlite3DbFree(db, pSorter->aIter);
+ pSorter->aIter = 0;
+ }
+ if( pSorter->pTemp1 ){
+ sqlite3OsCloseFree(pSorter->pTemp1);
+ pSorter->pTemp1 = 0;
+ }
+ vdbeSorterRecordFree(db, pSorter->pRecord);
+ pSorter->pRecord = 0;
+ pSorter->iWriteOff = 0;
+ pSorter->iReadOff = 0;
+ pSorter->nInMemory = 0;
+ pSorter->nTree = 0;
+ pSorter->nPMA = 0;
+ pSorter->aTree = 0;
+}
+
+
+/*
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
*/
void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
VdbeSorter *pSorter = pCsr->pSorter;
if( pSorter ){
- if( pSorter->aIter ){
- int i;
- for(i=0; i<pSorter->nTree; i++){
- vdbeSorterIterZero(db, &pSorter->aIter[i]);
- }
- sqlite3DbFree(db, pSorter->aIter);
- }
- if( pSorter->pTemp1 ){
- sqlite3OsCloseFree(pSorter->pTemp1);
- }
- vdbeSorterRecordFree(db, pSorter->pRecord);
+ sqlite3VdbeSorterReset(db, pSorter);
sqlite3DbFree(db, pSorter->pUnpacked);
sqlite3DbFree(db, pSorter);
pCsr->pSorter = 0;
@@ -956,14 +971,55 @@ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
if( pSorter->aTree ){
int iPrev = pSorter->aTree[1];/* Index of iterator to advance */
- int i; /* Index of aTree[] to recalculate */
-
rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
- for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
- rc = vdbeSorterDoCompare(pCsr, i);
- }
+ if( rc==SQLITE_OK ){
+ int i; /* Index of aTree[] to recalculate */
+ VdbeSorterIter *pIter1; /* First iterator to compare */
+ VdbeSorterIter *pIter2; /* Second iterator to compare */
+ u8 *pKey2; /* To pIter2->aKey, or 0 if record cached */
+
+ /* Find the first two iterators to compare. The one that was just
+ ** advanced (iPrev) and the one next to it in the array. */
+ pIter1 = &pSorter->aIter[(iPrev & 0xFFFE)];
+ pIter2 = &pSorter->aIter[(iPrev | 0x0001)];
+ pKey2 = pIter2->aKey;
+
+ for(i=(pSorter->nTree+iPrev)/2; i>0; i=i/2){
+ /* Compare pIter1 and pIter2. Store the result in variable iRes. */
+ int iRes;
+ if( pIter1->pFile==0 ){
+ iRes = +1;
+ }else if( pIter2->pFile==0 ){
+ iRes = -1;
+ }else{
+ vdbeSorterCompare(pCsr, 0,
+ pIter1->aKey, pIter1->nKey, pKey2, pIter2->nKey, &iRes
+ );
+ }
- *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ /* If pIter1 contained the smaller value, set aTree[i] to its index.
+ ** Then set pIter2 to the next iterator to compare to pIter1. In this
+ ** case there is no cache of pIter2 in pSorter->pUnpacked, so set
+ ** pKey2 to point to the record belonging to pIter2.
+ **
+ ** Alternatively, if pIter2 contains the smaller of the two values,
+ ** set aTree[i] to its index and update pIter1. If vdbeSorterCompare()
+ ** was actually called above, then pSorter->pUnpacked now contains
+ ** a value equivalent to pIter2. So set pKey2 to NULL to prevent
+ ** vdbeSorterCompare() from decoding pIter2 again. */
+ if( iRes<=0 ){
+ pSorter->aTree[i] = (int)(pIter1 - pSorter->aIter);
+ pIter2 = &pSorter->aIter[ pSorter->aTree[i ^ 0x0001] ];
+ pKey2 = pIter2->aKey;
+ }else{
+ if( pIter1->pFile ) pKey2 = 0;
+ pSorter->aTree[i] = (int)(pIter2 - pSorter->aIter);
+ pIter1 = &pSorter->aIter[ pSorter->aTree[i ^ 0x0001] ];
+ }
+
+ }
+ *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ }
}else{
SorterRecord *pFree = pSorter->pRecord;
pSorter->pRecord = pFree->pNext;
@@ -1027,12 +1083,13 @@ int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){
int sqlite3VdbeSorterCompare(
const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal, /* Value to compare to current sorter key */
+ int nKeyCol, /* Only compare this many fields */
int *pRes /* OUT: Result of comparison */
){
VdbeSorter *pSorter = pCsr->pSorter;
void *pKey; int nKey; /* Sorter key to compare pVal with */
pKey = vdbeSorterRowkey(pSorter, &nKey);
- vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
+ vdbeSorterCompare(pCsr, nKeyCol, pVal->z, pVal->n, pKey, nKey, pRes);
return SQLITE_OK;
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index 356277e..4a39e26 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -47,9 +47,9 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
-** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
+** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
@@ -87,11 +87,12 @@ char *sqlite3VdbeExpandSql(
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
out.db = db;
- if( db->vdbeExecCnt>1 ){
+ if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3StrAccumAppend(&out, "-- ", 3);
+ assert( (zRawSql - zStart) > 0 );
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
}
}else{
@@ -124,9 +125,9 @@ char *sqlite3VdbeExpandSql(
if( pVar->flags & MEM_Null ){
sqlite3StrAccumAppend(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
- sqlite3XPrintf(&out, "%lld", pVar->u.i);
+ sqlite3XPrintf(&out, 0, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, "%!.15g", pVar->r);
+ sqlite3XPrintf(&out, 0, "%!.15g", pVar->r);
}else if( pVar->flags & MEM_Str ){
int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
@@ -147,15 +148,17 @@ char *sqlite3VdbeExpandSql(
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
}
#endif
- sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
+ sqlite3XPrintf(&out, 0, "'%.*q'", nOut, pVar->z);
#ifdef SQLITE_TRACE_SIZE_LIMIT
- if( nOut<pVar->n ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ }
#endif
#ifndef SQLITE_OMIT_UTF16
if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
#endif
}else if( pVar->flags & MEM_Zero ){
- sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
+ sqlite3XPrintf(&out, 0, "zeroblob(%d)", pVar->u.nZero);
}else{
int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
@@ -165,11 +168,13 @@ char *sqlite3VdbeExpandSql(
if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
#endif
for(i=0; i<nOut; i++){
- sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
+ sqlite3XPrintf(&out, 0, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
#ifdef SQLITE_TRACE_SIZE_LIMIT
- if( nOut<pVar->n ) sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, 0, "/*+%d bytes*/", pVar->n-nOut);
+ }
#endif
}
}
@@ -228,7 +233,7 @@ void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
}
va_start(ap, zFormat);
- sqlite3VXPrintf(&p->str, 1, zFormat, ap);
+ sqlite3VXPrintf(&p->str, SQLITE_PRINTF_INTERNAL, zFormat, ap);
va_end(ap);
}
}
diff --git a/src/vtab.c b/src/vtab.c
index 958202c..ca0db21 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -738,6 +738,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3VdbeFinalize(pParse->pVdbe);
}
sqlite3DeleteTable(db, pParse->pNewTable);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
}
@@ -810,10 +811,9 @@ static void callFinaliser(sqlite3 *db, int offset){
** 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.
+** If an error message is available, leave it in p->zErrMsg.
*/
-int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
+int sqlite3VtabSync(sqlite3 *db, Vdbe *p){
int i;
int rc = SQLITE_OK;
VTable **aVTrans = db->aVTrans;
@@ -824,9 +824,7 @@ int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
sqlite3_vtab *pVtab = aVTrans[i]->pVtab;
if( pVtab && (x = pVtab->pModule->xSync)!=0 ){
rc = x(pVtab);
- sqlite3DbFree(db, *pzErrmsg);
- *pzErrmsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
+ sqlite3VtabImportErrmsg(p, pVtab);
}
}
db->aVTrans = aVTrans;
@@ -1016,7 +1014,7 @@ FuncDef *sqlite3VtabOverloadFunction(
memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1);
pNew->xFunc = xFunc;
pNew->pUserData = pArg;
- pNew->flags |= SQLITE_FUNC_EPHEM;
+ pNew->funcFlags |= SQLITE_FUNC_EPHEM;
return pNew;
}
diff --git a/src/wal.c b/src/wal.c
index e642ea2..70395f8 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -1306,7 +1306,7 @@ int sqlite3WalOpen(
sqlite3OsClose(pRet->pWalFd);
sqlite3_free(pRet);
}else{
- int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
+ int iDC = sqlite3OsDeviceCharacteristics(pDbFd);
if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
pRet->padToSectorBoundary = 0;
@@ -2096,8 +2096,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
** an subsequent retries, the delays start becoming longer and longer,
- ** so that on the 100th (and last) RETRY we delay for 21 milliseconds.
- ** The total delay time before giving up is less than 1 second.
+ ** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
+ ** The total delay time before giving up is less than 10 seconds.
*/
if( cnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
@@ -2105,7 +2105,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */
+ if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
sqlite3OsSleep(pWal->pVfs, nDelay);
}
@@ -2463,7 +2463,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY;
+ rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
@@ -2677,7 +2677,7 @@ static int walWriteToLog(
iAmt -= iFirstAmt;
pContent = (void*)(iFirstAmt + (char*)pContent);
assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
- rc = sqlite3OsSync(p->pFd, p->syncFlags);
+ rc = sqlite3OsSync(p->pFd, p->syncFlags & SQLITE_SYNC_MASK);
if( iAmt==0 || rc ) return rc;
}
rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
diff --git a/src/walker.c b/src/walker.c
index e71ed2a..016ae77 100644
--- a/src/walker.c
+++ b/src/walker.c
@@ -43,7 +43,7 @@ int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
testcase( ExprHasProperty(pExpr, EP_Reduced) );
rc = pWalker->xExprCallback(pWalker, pExpr);
if( rc==WRC_Continue
- && !ExprHasAnyProperty(pExpr,EP_TokenOnly) ){
+ && !ExprHasProperty(pExpr,EP_TokenOnly) ){
if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
@@ -113,9 +113,12 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
/*
** Call sqlite3WalkExpr() for every expression in Select statement p.
** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and
-** on the compound select chain, p->pPrior. Invoke the xSelectCallback()
-** either before or after the walk of expressions and FROM clause, depending
-** on whether pWalker->bSelectDepthFirst is false or true, respectively.
+** on the compound select chain, p->pPrior.
+**
+** If it is not NULL, the xSelectCallback() callback is invoked before
+** the walk of the expressions and FROM clause. The xSelectCallback2()
+** method, if it is not NULL, is invoked following the walk of the
+** expressions and FROM clause.
**
** Return WRC_Continue under normal conditions. Return WRC_Abort if
** there is an abort request.
@@ -125,11 +128,13 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
*/
int sqlite3WalkSelect(Walker *pWalker, Select *p){
int rc;
- if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue;
+ if( p==0 || (pWalker->xSelectCallback==0 && pWalker->xSelectCallback2==0) ){
+ return WRC_Continue;
+ }
rc = WRC_Continue;
pWalker->walkerDepth++;
while( p ){
- if( !pWalker->bSelectDepthFirst ){
+ if( pWalker->xSelectCallback ){
rc = pWalker->xSelectCallback(pWalker, p);
if( rc ) break;
}
@@ -139,12 +144,8 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){
pWalker->walkerDepth--;
return WRC_Abort;
}
- if( pWalker->bSelectDepthFirst ){
- rc = pWalker->xSelectCallback(pWalker, p);
- /* Depth-first search is currently only used for
- ** selectAddSubqueryTypeInfo() and that routine always returns
- ** WRC_Continue (0). So the following branch is never taken. */
- if( NEVER(rc) ) break;
+ if( pWalker->xSelectCallback2 ){
+ pWalker->xSelectCallback2(pWalker, p);
}
p = p->pPrior;
}
diff --git a/src/where.c b/src/where.c
index e614f4a..9c30136 100644
--- a/src/where.c
+++ b/src/where.c
@@ -17,290 +17,114 @@
** indices, you might also think of this module as the "query optimizer".
*/
#include "sqliteInt.h"
-
-
-/*
-** Trace output macros
-*/
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-/***/ int sqlite3WhereTrace = 0;
-#endif
-#if defined(SQLITE_DEBUG) \
- && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
-# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X
-#else
-# define WHERETRACE(X)
-#endif
-
-/* Forward reference
-*/
-typedef struct WhereClause WhereClause;
-typedef struct WhereMaskSet WhereMaskSet;
-typedef struct WhereOrInfo WhereOrInfo;
-typedef struct WhereAndInfo WhereAndInfo;
-typedef struct WhereCost WhereCost;
-
-/*
-** The query generator uses an array of instances of this structure to
-** help it analyze the subexpressions of the WHERE clause. Each WHERE
-** clause subexpression is separated from the others by AND operators,
-** usually, or sometimes subexpressions separated by OR.
-**
-** All WhereTerms are collected into a single WhereClause structure.
-** The following identity holds:
-**
-** WhereTerm.pWC->a[WhereTerm.idx] == WhereTerm
-**
-** When a term is of the form:
-**
-** X <op> <expr>
-**
-** where X is a column name and <op> is one of certain operators,
-** then WhereTerm.leftCursor and WhereTerm.u.leftColumn record the
-** cursor number and column number for X. WhereTerm.eOperator records
-** the <op> using a bitmask encoding defined by WO_xxx below. The
-** use of a bitmask encoding for the operator allows us to search
-** quickly for terms that match any of several different operators.
-**
-** A WhereTerm might also be two or more subterms connected by OR:
-**
-** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR ....
-**
-** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR
-** and the WhereTerm.u.pOrInfo field points to auxiliary information that
-** is collected about the
-**
-** If a term in the WHERE clause does not match either of the two previous
-** categories, then eOperator==0. The WhereTerm.pExpr field is still set
-** to the original subexpression content and wtFlags is set up appropriately
-** but no other fields in the WhereTerm object are meaningful.
-**
-** When eOperator!=0, prereqRight and prereqAll record sets of cursor numbers,
-** but they do so indirectly. A single WhereMaskSet structure translates
-** cursor number into bits and the translated bit is stored in the prereq
-** fields. The translation is used in order to maximize the number of
-** bits that will fit in a Bitmask. The VDBE cursor numbers might be
-** spread out over the non-negative integers. For example, the cursor
-** numbers might be 3, 8, 9, 10, 20, 23, 41, and 45. The WhereMaskSet
-** translates these sparse cursor numbers into consecutive integers
-** beginning with 0 in order to make the best possible use of the available
-** bits in the Bitmask. So, in the example above, the cursor numbers
-** would be mapped into integers 0 through 7.
-**
-** The number of terms in a join is limited by the number of bits
-** in prereqRight and prereqAll. The default is 64 bits, hence SQLite
-** is only able to process joins with 64 or fewer tables.
-*/
-typedef struct WhereTerm WhereTerm;
-struct WhereTerm {
- Expr *pExpr; /* Pointer to the subexpression that is this term */
- int iParent; /* Disable pWC->a[iParent] when this term disabled */
- int leftCursor; /* Cursor number of X in "X <op> <expr>" */
- union {
- int leftColumn; /* Column number of X in "X <op> <expr>" */
- WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
- WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
- } u;
- u16 eOperator; /* A WO_xx value describing <op> */
- u8 wtFlags; /* TERM_xxx bit flags. See below */
- u8 nChild; /* Number of children that must disable us */
- WhereClause *pWC; /* The clause this term is part of */
- Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
- Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
-};
-
-/*
-** Allowed values of WhereTerm.wtFlags
-*/
-#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 */
-#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
-#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
-#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT3
-# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
-#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
-#endif
-
-/*
-** An instance of the following structure holds all information about a
-** WHERE clause. Mostly this is a container for one or more WhereTerms.
-**
-** Explanation of pOuter: For a WHERE clause of the form
-**
-** a AND ((b AND c) OR (d AND e)) AND f
-**
-** There are separate WhereClause objects for the whole clause and for
-** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
-** subclauses points to the WhereClause object for the whole clause.
-*/
-struct WhereClause {
- Parse *pParse; /* The parser context */
- WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
- WhereClause *pOuter; /* Outer conjunction */
- u8 op; /* Split operator. TK_AND or TK_OR */
- u16 wctrlFlags; /* Might include WHERE_AND_ONLY */
- int nTerm; /* Number of terms */
- int nSlot; /* Number of entries in a[] */
- WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
-#if defined(SQLITE_SMALL_STACK)
- WhereTerm aStatic[1]; /* Initial static space for a[] */
-#else
- WhereTerm aStatic[8]; /* Initial static space for a[] */
-#endif
-};
+#include "whereInt.h"
/*
-** A WhereTerm with eOperator==WO_OR has its u.pOrInfo pointer set to
-** a dynamically allocated instance of the following structure.
+** Return the estimated number of output rows from a WHERE clause
*/
-struct WhereOrInfo {
- WhereClause wc; /* Decomposition into subterms */
- Bitmask indexable; /* Bitmask of all indexable tables in the clause */
-};
+u64 sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
+ return sqlite3LogEstToInt(pWInfo->nRowOut);
+}
/*
-** A WhereTerm with eOperator==WO_AND has its u.pAndInfo pointer set to
-** a dynamically allocated instance of the following structure.
+** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this
+** WHERE clause returns outputs for DISTINCT processing.
*/
-struct WhereAndInfo {
- WhereClause wc; /* The subexpression broken out */
-};
+int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
+ return pWInfo->eDistinct;
+}
/*
-** An instance of the following structure keeps track of a mapping
-** between VDBE cursor numbers and bits of the bitmasks in WhereTerm.
-**
-** The VDBE cursor numbers are small integers contained in
-** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE
-** clause, the cursor numbers might not begin with 0 and they might
-** contain gaps in the numbering sequence. But we want to make maximum
-** use of the bits in our bitmasks. This structure provides a mapping
-** from the sparse cursor numbers into consecutive integers beginning
-** with 0.
-**
-** If WhereMaskSet.ix[A]==B it means that The A-th bit of a Bitmask
-** corresponds VDBE cursor number B. The A-th bit of a bitmask is 1<<A.
-**
-** For example, if the WHERE clause expression used these VDBE
-** cursors: 4, 5, 8, 29, 57, 73. Then the WhereMaskSet structure
-** would map those cursor numbers into bits 0 through 5.
-**
-** Note that the mapping is not necessarily ordered. In the example
-** above, the mapping might go like this: 4->3, 5->1, 8->2, 29->0,
-** 57->5, 73->4. Or one of 719 other combinations might be used. It
-** does not really matter. What is important is that sparse cursor
-** numbers all get mapped into bit numbers that begin with 0 and contain
-** no gaps.
+** Return TRUE if the WHERE clause returns rows in ORDER BY order.
+** Return FALSE if the output needs to be sorted.
*/
-struct WhereMaskSet {
- int n; /* Number of assigned cursor values */
- int ix[BMS]; /* Cursor assigned to each bit */
-};
+int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
+ return pWInfo->nOBSat;
+}
/*
-** A WhereCost object records a lookup strategy and the estimated
-** cost of pursuing that strategy.
+** Return the VDBE address or label to jump to in order to continue
+** immediately with the next row of a WHERE clause.
*/
-struct WhereCost {
- WherePlan plan; /* The lookup strategy */
- double rCost; /* Overall cost of pursuing this search strategy */
- Bitmask used; /* Bitmask of cursors used by this plan */
-};
+int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
+ assert( pWInfo->iContinue!=0 );
+ return pWInfo->iContinue;
+}
/*
-** Bitmasks for the operators that indices are able to exploit. An
-** OR-ed combination of these values can be used when searching for
-** terms in the where clause.
+** Return the VDBE address or label to jump to in order to break
+** out of a WHERE loop.
*/
-#define WO_IN 0x001
-#define WO_EQ 0x002
-#define WO_LT (WO_EQ<<(TK_LT-TK_EQ))
-#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
-#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
-#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
-#define WO_MATCH 0x040
-#define WO_ISNULL 0x080
-#define WO_OR 0x100 /* Two or more OR-connected terms */
-#define WO_AND 0x200 /* Two or more AND-connected terms */
-#define WO_EQUIV 0x400 /* Of the form A==B, both columns */
-#define WO_NOOP 0x800 /* This term does not restrict search space */
-
-#define WO_ALL 0xfff /* Mask of all possible WO_* values */
-#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
+int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
+ return pWInfo->iBreak;
+}
/*
-** Value for wsFlags returned by bestIndex() and stored in
-** WhereLevel.wsFlags. These flags determine which search
-** strategies are appropriate.
-**
-** The least significant 12 bits is reserved as a mask for WO_ values above.
-** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL.
-** But if the table is the right table of a left join, WhereLevel.wsFlags
-** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as
-** the "op" parameter to findTerm when we are resolving equality constraints.
-** ISNULL constraints will then not be used on the right table of a left
-** join. Tickets #2177 and #2189.
+** Return TRUE if an UPDATE or DELETE statement can operate directly on
+** the rowids returned by a WHERE clause. Return FALSE if doing an
+** UPDATE or DELETE might change subsequent WHERE clause results.
+**
+** If the ONEPASS optimization is used (if this routine returns true)
+** then also write the indices of open cursors used by ONEPASS
+** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data
+** table and iaCur[1] gets the cursor used by an auxiliary index.
+** Either value may be -1, indicating that cursor is not used.
+** Any cursors returned will have been opened for writing.
+**
+** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is
+** unable to use the ONEPASS optimization.
*/
-#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */
-#define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */
-#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */
-#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
-#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
-#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
-#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
-#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
-#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
-#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
-#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
-#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
-#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
-#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
-#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
-#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
-#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
-#define WHERE_OB_UNIQUE 0x00004000 /* Values in ORDER BY columns are
- ** different for every output row */
-#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
-#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
-#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
-#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
-#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
+int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
+ memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
+ return pWInfo->okOnePass;
+}
/*
-** This module contains many separate subroutines that work together to
-** find the best indices to use for accessing a particular table in a query.
-** An instance of the following structure holds context information about the
-** index search so that it can be more easily passed between the various
-** routines.
+** Move the content of pSrc into pDest
*/
-typedef struct WhereBestIdx WhereBestIdx;
-struct WhereBestIdx {
- Parse *pParse; /* Parser context */
- WhereClause *pWC; /* The WHERE clause */
- struct SrcList_item *pSrc; /* The FROM clause term to search */
- Bitmask notReady; /* Mask of cursors not available */
- Bitmask notValid; /* Cursors not available for any purpose */
- ExprList *pOrderBy; /* The ORDER BY clause */
- ExprList *pDistinct; /* The select-list if query is DISTINCT */
- sqlite3_index_info **ppIdxInfo; /* Index information passed to xBestIndex */
- int i, n; /* Which loop is being coded; # of loops */
- WhereLevel *aLevel; /* Info about outer loops */
- WhereCost cost; /* Lowest cost query plan */
-};
+static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
+ pDest->n = pSrc->n;
+ memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0]));
+}
/*
-** Return TRUE if the probe cost is less than the baseline cost
+** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet.
+**
+** The new entry might overwrite an existing entry, or it might be
+** appended, or it might be discarded. Do whatever is the right thing
+** so that pSet keeps the N_OR_COST best entries seen so far.
*/
-static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
- if( pProbe->rCost<pBaseline->rCost ) return 1;
- if( pProbe->rCost>pBaseline->rCost ) return 0;
- if( pProbe->plan.nOBSat>pBaseline->plan.nOBSat ) return 1;
- if( pProbe->plan.nRow<pBaseline->plan.nRow ) return 1;
- return 0;
+static int whereOrInsert(
+ WhereOrSet *pSet, /* The WhereOrSet to be updated */
+ Bitmask prereq, /* Prerequisites of the new entry */
+ LogEst rRun, /* Run-cost of the new entry */
+ LogEst nOut /* Number of outputs for the new entry */
+){
+ u16 i;
+ WhereOrCost *p;
+ for(i=pSet->n, p=pSet->a; i>0; i--, p++){
+ if( rRun<=p->rRun && (prereq & p->prereq)==prereq ){
+ goto whereOrInsert_done;
+ }
+ if( p->rRun<=rRun && (p->prereq & prereq)==p->prereq ){
+ return 0;
+ }
+ }
+ if( pSet->n<N_OR_COST ){
+ p = &pSet->a[pSet->n++];
+ p->nOut = nOut;
+ }else{
+ p = pSet->a;
+ for(i=1; i<pSet->n; i++){
+ if( p->rRun>pSet->a[i].rRun ) p = pSet->a + i;
+ }
+ if( p->rRun<=rRun ) return 0;
+ }
+whereOrInsert_done:
+ p->prereq = prereq;
+ p->rRun = rRun;
+ if( p->nOut>nOut ) p->nOut = nOut;
+ return 1;
}
/*
@@ -308,17 +132,13 @@ static int compareCost(const WhereCost *pProbe, const WhereCost *pBaseline){
*/
static void whereClauseInit(
WhereClause *pWC, /* The WhereClause to be initialized */
- Parse *pParse, /* The parsing context */
- WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */
- u16 wctrlFlags /* Might include WHERE_AND_ONLY */
+ WhereInfo *pWInfo /* The WHERE processing context */
){
- pWC->pParse = pParse;
- pWC->pMaskSet = pMaskSet;
+ pWC->pWInfo = pWInfo;
pWC->pOuter = 0;
pWC->nTerm = 0;
pWC->nSlot = ArraySize(pWC->aStatic);
pWC->a = pWC->aStatic;
- pWC->wctrlFlags = wctrlFlags;
}
/* Forward reference */
@@ -347,7 +167,7 @@ static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
static void whereClauseClear(WhereClause *pWC){
int i;
WhereTerm *a;
- sqlite3 *db = pWC->pParse->db;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
if( a->wtFlags & TERM_DYNAMIC ){
sqlite3ExprDelete(db, a->pExpr);
@@ -385,10 +205,10 @@ static void whereClauseClear(WhereClause *pWC){
static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
WhereTerm *pTerm;
int idx;
- testcase( wtFlags & TERM_VIRTUAL ); /* EV: R-00211-15100 */
+ testcase( wtFlags & TERM_VIRTUAL );
if( pWC->nTerm>=pWC->nSlot ){
WhereTerm *pOld = pWC->a;
- sqlite3 *db = pWC->pParse->db;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
if( pWC->a==0 ){
if( wtFlags & TERM_DYNAMIC ){
@@ -404,6 +224,11 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
}
pTerm = &pWC->a[idx = pWC->nTerm++];
+ if( p && ExprHasProperty(p, EP_Unlikely) ){
+ pTerm->truthProb = sqlite3LogEst(p->iTable) - 99;
+ }else{
+ pTerm->truthProb = 1;
+ }
pTerm->pExpr = sqlite3ExprSkipCollate(p);
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
@@ -428,8 +253,8 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
** the WhereClause.a[] array. The slot[] array grows as needed to contain
** all terms of the WHERE clause.
*/
-static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
- pWC->op = (u8)op;
+static void whereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
+ pWC->op = op;
if( pExpr==0 ) return;
if( pExpr->op!=op ){
whereClauseInsert(pWC, pExpr, 0);
@@ -440,9 +265,9 @@ static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
}
/*
-** Initialize an expression mask set (a WhereMaskSet object)
+** Initialize a WhereMaskSet object
*/
-#define initMaskSet(P) memset(P, 0, sizeof(*P))
+#define initMaskSet(P) (P)->n=0
/*
** Return the bitmask for the given cursor number. Return 0 if
@@ -453,7 +278,7 @@ static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){
assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
for(i=0; i<pMaskSet->n; i++){
if( pMaskSet->ix[i]==iCursor ){
- return ((Bitmask)1)<<i;
+ return MASKBIT(i);
}
}
return 0;
@@ -473,18 +298,9 @@ static void createMask(WhereMaskSet *pMaskSet, int iCursor){
}
/*
-** This routine walks (recursively) an expression tree and generates
+** These routines walk (recursively) an expression tree and generate
** a bitmask indicating which tables are used in that expression
** tree.
-**
-** In order for this routine to work, the calling function must have
-** previously invoked sqlite3ResolveExprNames() on the expression. See
-** the header comment on that routine for additional information.
-** The sqlite3ResolveExprNames() routines looks for column names and
-** sets their opcodes to TK_COLUMN and their Expr.iTable fields to
-** the VDBE cursor number of the table. This routine just has to
-** translate the cursor numbers into bitmask values and OR all
-** the bitmasks together.
*/
static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*);
static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*);
@@ -538,14 +354,7 @@ static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){
/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
-** "=", "<", ">", "<=", ">=", and "IN".
-**
-** IMPLEMENTATION-OF: R-59926-26393 To be usable by an index a term must be
-** of one of the following forms: column = expression column > expression
-** column >= expression column < expression column <= expression
-** expression = column expression > column expression >= column
-** expression < column expression <= column column IN
-** (expression-list) column IN (subquery) column IS NULL
+** "=", "<", ">", "<=", ">=", "IN", and "IS NULL"
*/
static int allowedOp(int op){
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
@@ -565,10 +374,9 @@ static int allowedOp(int op){
** are converted into "Y op X".
**
** If left/right precedence rules come into play when determining the
-** collating
-** side of the comparison, it remains associated with the same side after
-** the commutation. So "Y collate NOCASE op X" becomes
-** "X op Y". This is because any collation sequence on
+** collating sequence, then COLLATE operators are adjusted to ensure
+** that the collating sequence does not change. For example:
+** "Y collate NOCASE op X" becomes "X op Y" because any collation sequence on
** the left hand side of a comparison overrides any collation sequence
** attached to the right. For the same reason the EP_Collate flag
** is not commuted.
@@ -626,6 +434,133 @@ static u16 operatorMask(int op){
}
/*
+** Advance to the next WhereTerm that matches according to the criteria
+** established when the pScan object was initialized by whereScanInit().
+** Return NULL if there are no more matching WhereTerms.
+*/
+static WhereTerm *whereScanNext(WhereScan *pScan){
+ int iCur; /* The cursor on the LHS of the term */
+ int iColumn; /* The column on the LHS of the term. -1 for IPK */
+ Expr *pX; /* An expression being tested */
+ WhereClause *pWC; /* Shorthand for pScan->pWC */
+ WhereTerm *pTerm; /* The term being tested */
+ int k = pScan->k; /* Where to start scanning */
+
+ while( pScan->iEquiv<=pScan->nEquiv ){
+ iCur = pScan->aEquiv[pScan->iEquiv-2];
+ iColumn = pScan->aEquiv[pScan->iEquiv-1];
+ while( (pWC = pScan->pWC)!=0 ){
+ for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
+ if( pTerm->leftCursor==iCur
+ && pTerm->u.leftColumn==iColumn
+ && (pScan->iEquiv<=2 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ ){
+ if( (pTerm->eOperator & WO_EQUIV)!=0
+ && pScan->nEquiv<ArraySize(pScan->aEquiv)
+ ){
+ int j;
+ pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
+ assert( pX->op==TK_COLUMN );
+ for(j=0; j<pScan->nEquiv; j+=2){
+ if( pScan->aEquiv[j]==pX->iTable
+ && pScan->aEquiv[j+1]==pX->iColumn ){
+ break;
+ }
+ }
+ if( j==pScan->nEquiv ){
+ pScan->aEquiv[j] = pX->iTable;
+ pScan->aEquiv[j+1] = pX->iColumn;
+ pScan->nEquiv += 2;
+ }
+ }
+ if( (pTerm->eOperator & pScan->opMask)!=0 ){
+ /* Verify the affinity and collating sequence match */
+ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
+ CollSeq *pColl;
+ Parse *pParse = pWC->pWInfo->pParse;
+ pX = pTerm->pExpr;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3BinaryCompareCollSeq(pParse,
+ pX->pLeft, pX->pRight);
+ if( pColl==0 ) pColl = pParse->db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+ continue;
+ }
+ }
+ if( (pTerm->eOperator & WO_EQ)!=0
+ && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
+ && pX->iTable==pScan->aEquiv[0]
+ && pX->iColumn==pScan->aEquiv[1]
+ ){
+ continue;
+ }
+ pScan->k = k+1;
+ return pTerm;
+ }
+ }
+ }
+ pScan->pWC = pScan->pWC->pOuter;
+ k = 0;
+ }
+ pScan->pWC = pScan->pOrigWC;
+ k = 0;
+ pScan->iEquiv += 2;
+ }
+ return 0;
+}
+
+/*
+** Initialize a WHERE clause scanner object. Return a pointer to the
+** first match. Return NULL if there are no matches.
+**
+** The scanner will be searching the WHERE clause pWC. It will look
+** for terms of the form "X <op> <expr>" where X is column iColumn of table
+** iCur. The <op> must be one of the operators described by opMask.
+**
+** If the search is for X and the WHERE clause contains terms of the
+** form X=Y then this routine might also return terms of the form
+** "Y <op> <expr>". The number of levels of transitivity is limited,
+** but is enough to handle most commonly occurring SQL statements.
+**
+** If X is not the INTEGER PRIMARY KEY then X must be compatible with
+** index pIdx.
+*/
+static WhereTerm *whereScanInit(
+ WhereScan *pScan, /* The WhereScan object being initialized */
+ WhereClause *pWC, /* The WHERE clause to be scanned */
+ int iCur, /* Cursor to scan for */
+ int iColumn, /* Column to scan for */
+ u32 opMask, /* Operator(s) to scan for */
+ Index *pIdx /* Must be compatible with this index */
+){
+ int j;
+
+ /* memset(pScan, 0, sizeof(*pScan)); */
+ pScan->pOrigWC = pWC;
+ pScan->pWC = pWC;
+ if( pIdx && iColumn>=0 ){
+ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
+ if( NEVER(j>pIdx->nColumn) ) return 0;
+ }
+ pScan->zCollName = pIdx->azColl[j];
+ }else{
+ pScan->idxaff = 0;
+ pScan->zCollName = 0;
+ }
+ pScan->opMask = opMask;
+ pScan->k = 0;
+ pScan->aEquiv[0] = iCur;
+ pScan->aEquiv[1] = iColumn;
+ pScan->nEquiv = 2;
+ pScan->iEquiv = 2;
+ return whereScanNext(pScan);
+}
+
+/*
** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
** where X is a reference to the iColumn of table iCur and <op> is one of
** the WO_xx operator codes specified by the op parameter.
@@ -656,84 +591,20 @@ static WhereTerm *findTerm(
u32 op, /* Mask of WO_xx values describing operator */
Index *pIdx /* Must be compatible with this index, if not NULL */
){
- WhereTerm *pTerm; /* Term being examined as possible result */
- WhereTerm *pResult = 0; /* The answer to return */
- WhereClause *pWCOrig = pWC; /* Original pWC value */
- int j, k; /* Loop counters */
- Expr *pX; /* Pointer to an expression */
- Parse *pParse; /* Parsing context */
- int iOrigCol = iColumn; /* Original value of iColumn */
- int nEquiv = 2; /* Number of entires in aEquiv[] */
- int iEquiv = 2; /* Number of entries of aEquiv[] processed so far */
- int aEquiv[22]; /* iCur,iColumn and up to 10 other equivalents */
-
- assert( iCur>=0 );
- aEquiv[0] = iCur;
- aEquiv[1] = iColumn;
- for(;;){
- for(pWC=pWCOrig; pWC; pWC=pWC->pOuter){
- for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
- if( pTerm->leftCursor==iCur
- && pTerm->u.leftColumn==iColumn
- ){
- if( (pTerm->prereqRight & notReady)==0
- && (pTerm->eOperator & op & WO_ALL)!=0
- ){
- if( iOrigCol>=0 && pIdx && (pTerm->eOperator & WO_ISNULL)==0 ){
- CollSeq *pColl;
- char idxaff;
-
- pX = pTerm->pExpr;
- pParse = pWC->pParse;
- idxaff = pIdx->pTable->aCol[iOrigCol].affinity;
- if( !sqlite3IndexAffinityOk(pX, idxaff) ){
- continue;
- }
-
- /* Figure out the collation sequence required from an index for
- ** it to be useful for optimising expression pX. Store this
- ** value in variable pColl.
- */
- assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse,pX->pLeft,pX->pRight);
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
-
- for(j=0; pIdx->aiColumn[j]!=iOrigCol; j++){
- if( NEVER(j>=pIdx->nColumn) ) return 0;
- }
- if( sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ){
- continue;
- }
- }
- if( pTerm->prereqRight==0 && (pTerm->eOperator&WO_EQ)!=0 ){
- pResult = pTerm;
- goto findTerm_success;
- }else if( pResult==0 ){
- pResult = pTerm;
- }
- }
- if( (pTerm->eOperator & WO_EQUIV)!=0
- && nEquiv<ArraySize(aEquiv)
- ){
- pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight);
- assert( pX->op==TK_COLUMN );
- for(j=0; j<nEquiv; j+=2){
- if( aEquiv[j]==pX->iTable && aEquiv[j+1]==pX->iColumn ) break;
- }
- if( j==nEquiv ){
- aEquiv[j] = pX->iTable;
- aEquiv[j+1] = pX->iColumn;
- nEquiv += 2;
- }
- }
- }
+ WhereTerm *pResult = 0;
+ WhereTerm *p;
+ WhereScan scan;
+
+ p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
+ while( p ){
+ if( (p->prereqRight & notReady)==0 ){
+ if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){
+ return p;
}
+ if( pResult==0 ) pResult = p;
}
- if( iEquiv>=nEquiv ) break;
- iCur = aEquiv[iEquiv++];
- iColumn = aEquiv[iEquiv++];
+ p = whereScanNext(&scan);
}
-findTerm_success:
return pResult;
}
@@ -742,8 +613,6 @@ static void exprAnalyze(SrcList*, WhereClause*, int);
/*
** Call exprAnalyze on all terms in a WHERE clause.
-**
-**
*/
static void exprAnalyzeAll(
SrcList *pTabList, /* the FROM clause */
@@ -799,15 +668,12 @@ static int isLikeOrGlob(
}
assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */
- pRight = pList->a[0].pExpr;
+ pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr);
op = pRight->op;
- if( op==TK_REGISTER ){
- op = pRight->op2;
- }
if( op==TK_VARIABLE ){
Vdbe *pReprepare = pParse->pReprepare;
int iCol = pRight->iColumn;
- pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
+ pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_NONE);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
@@ -889,8 +755,10 @@ static int isMatchOfColumn(
** a join, then transfer the appropriate markings over to derived.
*/
static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
- pDerived->flags |= pBase->flags & EP_FromJoin;
- pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ if( pDerived ){
+ pDerived->flags |= pBase->flags & EP_FromJoin;
+ pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ }
}
#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -949,10 +817,10 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
** From another point of view, "indexable" means that the subterm could
** potentially be used with an index if an appropriate index exists.
** This analysis does not consider whether or not the index exists; that
-** is something the bestIndex() routine will determine. This analysis
-** only looks at whether subterms appropriate for indexing exist.
+** is decided elsewhere. This analysis only looks at whether subterms
+** appropriate for indexing exist.
**
-** All examples A through E above all satisfy case 2. But if a term
+** All examples A through E above satisfy case 2. But if a term
** also statisfies case 1 (such as B) we know that the optimizer will
** always prefer case 1, so in that case we pretend that case 2 is not
** satisfied.
@@ -975,11 +843,11 @@ static void exprAnalyzeOrTerm(
WhereClause *pWC, /* the complete WHERE clause */
int idxTerm /* Index of the OR-term to be analyzed */
){
- Parse *pParse = pWC->pParse; /* Parser context */
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
+ Parse *pParse = pWInfo->pParse; /* Parser context */
sqlite3 *db = pParse->db; /* Database connection */
WhereTerm *pTerm = &pWC->a[idxTerm]; /* The term to be analyzed */
Expr *pExpr = pTerm->pExpr; /* The expression of the term */
- WhereMaskSet *pMaskSet = pWC->pMaskSet; /* Table use masks */
int i; /* Loop counters */
WhereClause *pOrWc; /* Breakup of pTerm into subterms */
WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */
@@ -998,7 +866,7 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
- whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags);
+ whereClauseInit(pOrWc, pWInfo);
whereSplit(pOrWc, pExpr, TK_OR);
exprAnalyzeAll(pSrc, pOrWc);
if( db->mallocFailed ) return;
@@ -1024,7 +892,7 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
- whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags);
+ whereClauseInit(pAndWC, pWC->pWInfo);
whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
exprAnalyzeAll(pSrc, pAndWC);
pAndWC->pOuter = pWC;
@@ -1033,7 +901,7 @@ static void exprAnalyzeOrTerm(
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
if( allowedOp(pAndTerm->pExpr->op) ){
- b |= getMask(pMaskSet, pAndTerm->leftCursor);
+ b |= getMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
}
}
}
@@ -1044,10 +912,10 @@ static void exprAnalyzeOrTerm(
** corresponding TERM_VIRTUAL term */
}else{
Bitmask b;
- b = getMask(pMaskSet, pOrTerm->leftCursor);
+ b = getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
- b |= getMask(pMaskSet, pOther->leftCursor);
+ b |= getMask(&pWInfo->sMaskSet, pOther->leftCursor);
}
indexable &= b;
if( (pOrTerm->eOperator & WO_EQ)==0 ){
@@ -1109,7 +977,7 @@ static void exprAnalyzeOrTerm(
assert( j==1 );
continue;
}
- if( (chngToIN & getMask(pMaskSet, pOrTerm->leftCursor))==0 ){
+ if( (chngToIN & getMask(&pWInfo->sMaskSet, pOrTerm->leftCursor))==0 ){
/* This term must be of the form t1.a==t2.b where t2 is in the
** chngToIN set but t1 is not. This term will be either preceeded
** or follwed by an inverted copy (t2.b==t1.a). Skip this term
@@ -1128,7 +996,7 @@ static void exprAnalyzeOrTerm(
** on the second iteration */
assert( j==1 );
assert( IsPowerOfTwo(chngToIN) );
- assert( chngToIN==getMask(pMaskSet, iCursor) );
+ assert( chngToIN==getMask(&pWInfo->sMaskSet, iCursor) );
break;
}
testcase( j==1 );
@@ -1162,8 +1030,6 @@ static void exprAnalyzeOrTerm(
/* At this point, okToChngToIN is true if original pTerm satisfies
** case 1. In that case, construct a new virtual term that is
** pTerm converted into an IN operator.
- **
- ** EV: R-00211-15100
*/
if( okToChngToIN ){
Expr *pDup; /* A transient duplicate expression */
@@ -1177,7 +1043,7 @@ static void exprAnalyzeOrTerm(
assert( pOrTerm->leftCursor==iCursor );
assert( pOrTerm->u.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
- pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
+ pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup);
pLeft = pOrTerm->pExpr->pLeft;
}
assert( pLeft!=0 );
@@ -1226,6 +1092,7 @@ static void exprAnalyze(
WhereClause *pWC, /* the WHERE clause */
int idxTerm /* Index of the term to be analyzed */
){
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
WhereTerm *pTerm; /* The term to be analyzed */
WhereMaskSet *pMaskSet; /* Set of table index masks */
Expr *pExpr; /* The expression to be analyzed */
@@ -1236,14 +1103,14 @@ static void exprAnalyze(
int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */
int noCase = 0; /* LIKE/GLOB distinguishes case */
int op; /* Top-level operator. pExpr->op */
- Parse *pParse = pWC->pParse; /* Parsing context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
sqlite3 *db = pParse->db; /* Database connection */
if( db->mallocFailed ){
return;
}
pTerm = &pWC->a[idxTerm];
- pMaskSet = pWC->pMaskSet;
+ pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
@@ -1348,6 +1215,7 @@ static void exprAnalyze(
pNewExpr = sqlite3PExpr(pParse, ops[i],
sqlite3ExprDup(db, pExpr->pLeft, 0),
sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
+ transferJoinMarkings(pNewExpr, pExpr);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
@@ -1404,9 +1272,7 @@ static void exprAnalyze(
** inequality. To avoid this, make sure to also run the full
** LIKE on all candidate expressions by clearing the isComplete flag
*/
- if( c=='A'-1 ) isComplete = 0; /* EV: R-64339-08207 */
-
-
+ if( c=='A'-1 ) isComplete = 0;
c = sqlite3UpperToLower[c];
}
*pC = c + 1;
@@ -1417,6 +1283,7 @@ static void exprAnalyze(
pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
sqlite3ExprAddCollateToken(pParse,pNewExpr1,&sCollSeqName),
pStr1, 0);
+ transferJoinMarkings(pNewExpr1, pExpr);
idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1);
@@ -1424,6 +1291,7 @@ static void exprAnalyze(
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
sqlite3ExprAddCollateToken(pParse,pNewExpr2,&sCollSeqName),
pStr2, 0);
+ transferJoinMarkings(pNewExpr2, pExpr);
idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2);
@@ -1473,7 +1341,7 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
@@ -1487,6 +1355,7 @@ static void exprAnalyze(
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
+ && OptimizationEnabled(db, SQLITE_Stat3)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -1512,7 +1381,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -1521,11 +1390,8 @@ static void exprAnalyze(
}
/*
-** This function searches the expression list passed as the second argument
-** for an expression of type TK_COLUMN that refers to the same column and
-** uses the same collation sequence as the iCol'th column of index pIdx.
-** Argument iBase is the cursor number used for the table that pIdx refers
-** to.
+** This function searches pList for a entry that matches the iCol-th column
+** of index pIdx.
**
** If such an expression is found, its index in pList->a[] is returned. If
** no expression is found, -1 is returned.
@@ -1557,76 +1423,17 @@ static int findIndexCol(
}
/*
-** This routine determines if pIdx can be used to assist in processing a
-** DISTINCT qualifier. In other words, it tests whether or not using this
-** index for the outer loop guarantees that rows with equal values for
-** all expressions in the pDistinct list are delivered grouped together.
-**
-** For example, the query
-**
-** SELECT DISTINCT a, b, c FROM tbl WHERE a = ?
-**
-** can benefit from any index on columns "b" and "c".
-*/
-static int isDistinctIndex(
- Parse *pParse, /* Parsing context */
- WhereClause *pWC, /* The WHERE clause */
- Index *pIdx, /* The index being considered */
- int base, /* Cursor number for the table pIdx is on */
- ExprList *pDistinct, /* The DISTINCT expressions */
- int nEqCol /* Number of index columns with == */
-){
- Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
- int i; /* Iterator variable */
-
- assert( pDistinct!=0 );
- if( pIdx->zName==0 || pDistinct->nExpr>=BMS ) return 0;
- testcase( pDistinct->nExpr==BMS-1 );
-
- /* Loop through all the expressions in the distinct list. If any of them
- ** are not simple column references, return early. Otherwise, test if the
- ** WHERE clause contains a "col=X" clause. If it does, the expression
- ** can be ignored. If it does not, and the column does not belong to the
- ** same table as index pIdx, return early. Finally, if there is no
- ** matching "col=X" expression and the column is on the same table as pIdx,
- ** set the corresponding bit in variable mask.
- */
- for(i=0; i<pDistinct->nExpr; i++){
- WhereTerm *pTerm;
- Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
- if( p->op!=TK_COLUMN ) return 0;
- pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0);
- if( pTerm ){
- Expr *pX = pTerm->pExpr;
- CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- CollSeq *p2 = sqlite3ExprCollSeq(pParse, p);
- if( p1==p2 ) continue;
- }
- if( p->iTable!=base ) return 0;
- mask |= (((Bitmask)1) << i);
- }
-
- for(i=nEqCol; mask && i<pIdx->nColumn; i++){
- int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i);
- if( iExpr<0 ) break;
- mask &= ~(((Bitmask)1) << iExpr);
- }
-
- return (mask==0);
-}
-
-
-/*
** Return true if the DISTINCT expression-list passed as the third argument
-** is redundant. A DISTINCT list is redundant if the database contains a
-** UNIQUE index that guarantees that the result of the query will be distinct
-** anyway.
+** is redundant.
+**
+** A DISTINCT list is redundant if the database contains some subset of
+** columns that are unique and non-null.
*/
static int isDistinctRedundant(
- Parse *pParse,
- SrcList *pTabList,
- WhereClause *pWC,
- ExprList *pDistinct
+ Parse *pParse, /* Parsing context */
+ SrcList *pTabList, /* The FROM clause */
+ WhereClause *pWC, /* The WHERE clause */
+ ExprList *pDistinct /* The result set that needs to be DISTINCT */
){
Table *pTab;
Index *pIdx;
@@ -1663,17 +1470,17 @@ static int isDistinctRedundant(
** contain a "col=X" term are subject to a NOT NULL constraint.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->onError==OE_None ) continue;
- for(i=0; i<pIdx->nColumn; i++){
- int iCol = pIdx->aiColumn[i];
+ if( !IsUniqueIndex(pIdx) ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ i16 iCol = pIdx->aiColumn[i];
if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx) ){
int iIdxCol = findIndexCol(pParse, pDistinct, iBase, pIdx, i);
- if( iIdxCol<0 || pTab->aCol[pIdx->aiColumn[i]].notNull==0 ){
+ if( iIdxCol<0 || pTab->aCol[iCol].notNull==0 ){
break;
}
}
}
- if( i==pIdx->nColumn ){
+ if( i==pIdx->nKeyCol ){
/* This index implies that the DISTINCT qualifier is redundant. */
return 1;
}
@@ -1682,21 +1489,12 @@ static int isDistinctRedundant(
return 0;
}
+
/*
-** 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 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.
+** Estimate the logarithm of the input value to base 2.
*/
-static double estLog(double N){
- double logN = 1;
- double x = 10;
- while( N>x ){
- logN += 1;
- x *= 10;
- }
- return logN;
+static LogEst estLog(LogEst N){
+ return N<=10 ? 0 : sqlite3LogEst(N) - 33;
}
/*
@@ -1705,7 +1503,7 @@ static double estLog(double N){
** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines
** are no-ops.
*/
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG)
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
int i;
if( !sqlite3WhereTrace ) return;
@@ -1737,113 +1535,13 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr);
sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed);
sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost);
+ sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows);
}
#else
#define TRACE_IDX_INPUTS(A)
#define TRACE_IDX_OUTPUTS(A)
#endif
-/*
-** Required because bestIndex() is called by bestOrClauseIndex()
-*/
-static void bestIndex(WhereBestIdx*);
-
-/*
-** This routine attempts to find an scanning strategy that can be used
-** to optimize an 'OR' expression that is part of a WHERE clause.
-**
-** The table associated with FROM clause term pSrc may be either a
-** regular B-Tree table or a virtual table.
-*/
-static void bestOrClauseIndex(WhereBestIdx *p){
-#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- const int iCur = pSrc->iCursor; /* The cursor of the table */
- const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */
- WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-
- /* The OR-clause optimization is disallowed if the INDEXED BY or
- ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */
- if( pSrc->notIndexed || pSrc->pIndex!=0 ){
- return;
- }
- if( pWC->wctrlFlags & WHERE_AND_ONLY ){
- return;
- }
-
- /* Search the WHERE clause terms for a usable WO_OR term. */
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( (pTerm->eOperator & WO_OR)!=0
- && ((pTerm->prereqAll & ~maskSrc) & p->notReady)==0
- && (pTerm->u.pOrInfo->indexable & maskSrc)!=0
- ){
- WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
- WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
- WhereTerm *pOrTerm;
- int flags = WHERE_MULTI_OR;
- double rTotal = 0;
- double nRow = 0;
- Bitmask used = 0;
- WhereBestIdx sBOI;
-
- sBOI = *p;
- sBOI.pOrderBy = 0;
- sBOI.pDistinct = 0;
- sBOI.ppIdxInfo = 0;
- for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
- WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
- (pOrTerm - pOrWC->a), (pTerm - pWC->a)
- ));
- if( (pOrTerm->eOperator& WO_AND)!=0 ){
- sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
- bestIndex(&sBOI);
- }else if( pOrTerm->leftCursor==iCur ){
- WhereClause tempWC;
- tempWC.pParse = pWC->pParse;
- tempWC.pMaskSet = pWC->pMaskSet;
- tempWC.pOuter = pWC;
- tempWC.op = TK_AND;
- tempWC.a = pOrTerm;
- tempWC.wctrlFlags = 0;
- tempWC.nTerm = 1;
- sBOI.pWC = &tempWC;
- bestIndex(&sBOI);
- }else{
- continue;
- }
- rTotal += sBOI.cost.rCost;
- nRow += sBOI.cost.plan.nRow;
- used |= sBOI.cost.used;
- if( rTotal>=p->cost.rCost ) break;
- }
-
- /* If there is an ORDER BY clause, increase the scan cost to account
- ** for the cost of the sort. */
- if( p->pOrderBy!=0 ){
- WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n",
- rTotal, rTotal+nRow*estLog(nRow)));
- rTotal += nRow*estLog(nRow);
- }
-
- /* If the cost of scanning using this OR term for optimization is
- ** less than the current cost stored in pCost, replace the contents
- ** of pCost. */
- WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
- if( rTotal<p->cost.rCost ){
- p->cost.rCost = rTotal;
- p->cost.used = used;
- p->cost.plan.nRow = nRow;
- p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
- p->cost.plan.wsFlags = flags;
- p->cost.plan.u.pTerm = pTerm;
- }
- }
- }
-#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
-}
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
** Return TRUE if the WHERE clause term pTerm is of a form where it
@@ -1859,88 +1557,13 @@ static int termCanDriveIndex(
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
+ if( pTerm->u.leftColumn<0 ) return 0;
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
return 1;
}
#endif
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
-/*
-** If the query plan for pSrc specified in pCost is a full table scan
-** and indexing is allows (if there is no NOT INDEXED clause) and it
-** possible to construct a transient index that would perform better
-** than a full table scan even when the cost of constructing the index
-** is taken into account, then alter the query plan to use the
-** transient index.
-*/
-static void bestAutomaticIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- double nTableRow; /* Rows in the input table */
- double logN; /* log(nTableRow) */
- double costTempIdx; /* per-query cost of the transient index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
- WhereTerm *pWCEnd; /* End of pWC->a[] */
- Table *pTable; /* Table tht might be indexed */
-
- if( pParse->nQueryLoop<=(double)1 ){
- /* There is no point in building an automatic index for a single scan */
- return;
- }
- if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){
- /* Automatic indices are disabled at run-time */
- return;
- }
- if( (p->cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0
- && (p->cost.plan.wsFlags & WHERE_COVER_SCAN)==0
- ){
- /* We already have some kind of index in use for this query. */
- return;
- }
- if( pSrc->viaCoroutine ){
- /* Cannot index a co-routine */
- return;
- }
- if( pSrc->notIndexed ){
- /* The NOT INDEXED clause appears in the SQL. */
- return;
- }
- if( pSrc->isCorrelated ){
- /* The source is a correlated sub-query. No point in indexing it. */
- return;
- }
-
- assert( pParse->nQueryLoop >= (double)1 );
- pTable = pSrc->pTab;
- nTableRow = pTable->nRowEst;
- logN = estLog(nTableRow);
- costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
- if( costTempIdx>=p->cost.rCost ){
- /* The cost of creating the transient table would be greater than
- ** doing the full table scan */
- return;
- }
-
- /* Search for any equality comparison term */
- pWCEnd = &pWC->a[pWC->nTerm];
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( termCanDriveIndex(pTerm, pSrc, p->notReady) ){
- WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n",
- p->cost.rCost, costTempIdx));
- p->cost.rCost = costTempIdx;
- p->cost.plan.nRow = logN + 1;
- p->cost.plan.wsFlags = WHERE_TEMP_INDEX;
- p->cost.used = pTerm->prereqRight;
- break;
- }
- }
-}
-#else
-# define bestAutomaticIndex(A) /* no-op */
-#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
@@ -1955,50 +1578,61 @@ static void constructAutomaticIndex(
Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
- int nColumn; /* Number of columns in the constructed index */
+ int nKeyCol; /* Number of columns in the constructed index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
WhereTerm *pWCEnd; /* End of pWC->a[] */
- int nByte; /* Byte of memory needed for pIdx */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
- KeyInfo *pKeyinfo; /* Key information for the index */
int addrTop; /* Top of the index fill loop */
int regRecord; /* Register holding an index record */
int n; /* Column counter */
int i; /* Loop counter */
int mxBitCol; /* Maximum column in pSrc->colUsed */
CollSeq *pColl; /* Collating sequence to on a column */
+ WhereLoop *pLoop; /* The Loop object */
+ char *zNotUsed; /* Extra space on the end of pIdx */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
+ u8 sentWarning = 0; /* True if a warnning has been issued */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
- addrInit = sqlite3CodeOnce(pParse);
+ addrInit = sqlite3CodeOnce(pParse); VdbeCoverage(v);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
- nColumn = 0;
+ nKeyCol = 0;
pTable = pSrc->pTab;
pWCEnd = &pWC->a[pWC->nTerm];
+ pLoop = pLevel->pWLoop;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
+ if( !sentWarning ){
+ sqlite3_log(SQLITE_WARNING_AUTOINDEX,
+ "automatic index on %s(%s)", pTable->zName,
+ pTable->aCol[iCol].zName);
+ sentWarning = 1;
+ }
if( (idxCols & cMask)==0 ){
- nColumn++;
+ if( whereLoopResize(pParse->db, pLoop, nKeyCol+1) ) return;
+ pLoop->aLTerm[nKeyCol++] = pTerm;
idxCols |= cMask;
}
}
}
- assert( nColumn>0 );
- pLevel->plan.nEq = nColumn;
+ assert( nKeyCol>0 );
+ pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol;
+ pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
+ | WHERE_AUTO_INDEX;
/* Count the number of additional columns needed to create a
** covering index. A "covering index" is an index that contains all
@@ -2008,38 +1642,32 @@ static void constructAutomaticIndex(
** original table changes and the index and table cannot both be used
** if they go out of sync.
*/
- extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1)));
+ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol;
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ) nColumn++;
+ if( extraCols & MASKBIT(i) ) nKeyCol++;
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
- nColumn += pTable->nCol - BMS + 1;
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
+ nKeyCol += pTable->nCol - BMS + 1;
}
- pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ;
+ pLoop->wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY;
/* Construct the Index object to describe this index */
- nByte = sizeof(Index);
- nByte += nColumn*sizeof(int); /* Index.aiColumn */
- nByte += nColumn*sizeof(char*); /* Index.azColl */
- nByte += nColumn; /* Index.aSortOrder */
- pIdx = sqlite3DbMallocZero(pParse->db, nByte);
+ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
if( pIdx==0 ) return;
- pLevel->plan.u.pIdx = pIdx;
- pIdx->azColl = (char**)&pIdx[1];
- pIdx->aiColumn = (int*)&pIdx->azColl[nColumn];
- pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn];
+ pLoop->u.btree.pIndex = pIdx;
pIdx->zName = "auto-index";
- pIdx->nColumn = nColumn;
pIdx->pTable = pTable;
n = 0;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
+ testcase( iCol==BMS-1 );
+ testcase( iCol==BMS );
if( (idxCols & cMask)==0 ){
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
@@ -2050,40 +1678,42 @@ static void constructAutomaticIndex(
}
}
}
- assert( (u32)n==pLevel->plan.nEq );
+ assert( (u32)n==pLoop->u.btree.nEq );
/* Add additional columns needed to make the automatic index into
** a covering index */
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ){
+ if( extraCols & MASKBIT(i) ){
pIdx->aiColumn[n] = i;
pIdx->azColl[n] = "BINARY";
n++;
}
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
for(i=BMS-1; i<pTable->nCol; i++){
pIdx->aiColumn[n] = i;
pIdx->azColl[n] = "BINARY";
n++;
}
}
- assert( n==nColumn );
+ assert( n==nKeyCol );
+ pIdx->aiColumn[n] = -1;
+ pIdx->azColl[n] = "BINARY";
/* Create the automatic index */
- pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
assert( pLevel->iIdxCur>=0 );
- sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0,
- (char*)pKeyinfo, P4_KEYINFO_HANDOFF);
+ pLevel->iIdxCur = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "for %s", pTable->zName));
/* Fill the automatic index with content */
- addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1);
+ sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
- sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
+ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
@@ -2099,11 +1729,12 @@ static void constructAutomaticIndex(
** responsibility of the caller to eventually release the structure
** by passing the pointer returned by this function to sqlite3_free().
*/
-static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
- Parse *pParse = p->pParse;
- WhereClause *pWC = p->pWC;
- struct SrcList_item *pSrc = p->pSrc;
- ExprList *pOrderBy = p->pOrderBy;
+static sqlite3_index_info *allocateIndexInfo(
+ Parse *pParse,
+ WhereClause *pWC,
+ struct SrcList_item *pSrc,
+ ExprList *pOrderBy
+){
int i, j;
int nTerm;
struct sqlite3_index_constraint *pIdxCons;
@@ -2113,8 +1744,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
int nOrderBy;
sqlite3_index_info *pIdxInfo;
- WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));
-
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
@@ -2122,7 +1751,8 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
testcase( pTerm->eOperator & WO_ISNULL );
- if( pTerm->eOperator & (WO_ISNULL) ) continue;
+ testcase( pTerm->eOperator & WO_ALL );
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
nTerm++;
}
@@ -2150,7 +1780,6 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
+ sizeof(*pIdxOrderBy)*nOrderBy );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return 0;
}
@@ -2175,7 +1804,8 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
testcase( pTerm->eOperator & WO_IN );
testcase( pTerm->eOperator & WO_ISNULL );
- if( pTerm->eOperator & (WO_ISNULL) ) continue;
+ testcase( pTerm->eOperator & WO_ALL );
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
pIdxCons[j].iColumn = pTerm->u.leftColumn;
pIdxCons[j].iTermOffset = i;
@@ -2206,8 +1836,8 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
/*
** The table object reference passed as the second argument to this function
** must represent a virtual table. This function invokes the xBestIndex()
-** method of the virtual table with the sqlite3_index_info pointer passed
-** as the argument.
+** method of the virtual table with the sqlite3_index_info object that
+** comes in as the 3rd argument to this function.
**
** If an error occurs, pParse is populated with an error message and a
** non-zero value is returned. Otherwise, 0 is returned and the output
@@ -2222,7 +1852,6 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
int i;
int rc;
- WHERETRACE(("xBestIndex for %s\n", pTab->zName));
TRACE_IDX_INPUTS(p);
rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p);
@@ -2248,209 +1877,10 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
return pParse->nErr;
}
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-/*
-** Compute the best index for a virtual table.
-**
-** The best index is computed by the xBestIndex method of the virtual
-** table module. This routine is really just a wrapper that sets up
-** the sqlite3_index_info structure that is used to communicate with
-** xBestIndex.
-**
-** In a join, this routine might be called multiple times for the
-** same virtual table. The sqlite3_index_info structure is created
-** and initialized on the first invocation and reused on all subsequent
-** invocations. The sqlite3_index_info structure is also used when
-** code is generated to access the virtual table. The whereInfoDelete()
-** routine takes care of freeing the sqlite3_index_info structure after
-** everybody has finished with it.
-*/
-static void bestVirtualIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- Table *pTab = pSrc->pTab;
- sqlite3_index_info *pIdxInfo;
- struct sqlite3_index_constraint *pIdxCons;
- struct sqlite3_index_constraint_usage *pUsage;
- WhereTerm *pTerm;
- int i, j;
- int nOrderBy;
- int bAllowIN; /* Allow IN optimizations */
- double rCost;
-
- /* Make sure wsFlags is initialized to some sane value. Otherwise, if the
- ** malloc in allocateIndexInfo() fails and this function returns leaving
- ** wsFlags in an uninitialized state, the caller may behave unpredictably.
- */
- memset(&p->cost, 0, sizeof(p->cost));
- p->cost.plan.wsFlags = WHERE_VIRTUALTABLE;
-
- /* If the sqlite3_index_info structure has not been previously
- ** allocated and initialized, then allocate and initialize it now.
- */
- pIdxInfo = *p->ppIdxInfo;
- if( pIdxInfo==0 ){
- *p->ppIdxInfo = pIdxInfo = allocateIndexInfo(p);
- }
- if( pIdxInfo==0 ){
- return;
- }
-
- /* At this point, the sqlite3_index_info structure that pIdxInfo points
- ** to will have been initialized, either during the current invocation or
- ** during some prior invocation. Now we just have to customize the
- ** details of pIdxInfo for the current invocation and pass it to
- ** xBestIndex.
- */
-
- /* The module name must be defined. Also, by this point there must
- ** be a pointer to an sqlite3_vtab structure. Otherwise
- ** sqlite3ViewGetColumnNames() would have picked up the error.
- */
- assert( pTab->azModuleArg && pTab->azModuleArg[0] );
- assert( sqlite3GetVTable(pParse->db, pTab) );
-
- /* Try once or twice. On the first attempt, allow IN optimizations.
- ** If an IN optimization is accepted by the virtual table xBestIndex
- ** method, but the pInfo->aConstrainUsage.omit flag is not set, then
- ** the query will not work because it might allow duplicate rows in
- ** output. In that case, run the xBestIndex method a second time
- ** without the IN constraints. Usually this loop only runs once.
- ** The loop will exit using a "break" statement.
- */
- for(bAllowIN=1; 1; bAllowIN--){
- assert( bAllowIN==0 || bAllowIN==1 );
-
- /* Set the aConstraint[].usable fields and initialize all
- ** output variables to zero.
- **
- ** aConstraint[].usable is true for constraints where the right-hand
- ** side contains only references to tables to the left of the current
- ** table. In other words, if the constraint is of the form:
- **
- ** column = expr
- **
- ** and we are evaluating a join, then the constraint on column is
- ** only valid if all tables referenced in expr occur to the left
- ** of the table containing column.
- **
- ** The aConstraints[] array contains entries for all constraints
- ** on the current table. That way we only have to compute it once
- ** even though we might try to pick the best index multiple times.
- ** For each attempt at picking an index, the order of tables in the
- ** join might be different so we have to recompute the usable flag
- ** each time.
- */
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- pUsage = pIdxInfo->aConstraintUsage;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- if( (pTerm->prereqRight&p->notReady)==0
- && (bAllowIN || (pTerm->eOperator & WO_IN)==0)
- ){
- pIdxCons->usable = 1;
- }else{
- pIdxCons->usable = 0;
- }
- }
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
- if( pIdxInfo->needToFreeIdxStr ){
- sqlite3_free(pIdxInfo->idxStr);
- }
- pIdxInfo->idxStr = 0;
- pIdxInfo->idxNum = 0;
- pIdxInfo->needToFreeIdxStr = 0;
- pIdxInfo->orderByConsumed = 0;
- /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
- nOrderBy = pIdxInfo->nOrderBy;
- if( !p->pOrderBy ){
- pIdxInfo->nOrderBy = 0;
- }
-
- if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
- return;
- }
-
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- if( pUsage[i].argvIndex>0 ){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- p->cost.used |= pTerm->prereqRight;
- if( (pTerm->eOperator & WO_IN)!=0 ){
- if( pUsage[i].omit==0 ){
- /* Do not attempt to use an IN constraint if the virtual table
- ** says that the equivalent EQ constraint cannot be safely omitted.
- ** If we do attempt to use such a constraint, some rows might be
- ** repeated in the output. */
- break;
- }
- /* A virtual table that is constrained by an IN clause may not
- ** consume the ORDER BY clause because (1) the order of IN terms
- ** is not necessarily related to the order of output terms and
- ** (2) Multiple outputs from a single IN value will not merge
- ** together. */
- pIdxInfo->orderByConsumed = 0;
- }
- }
- }
- if( i>=pIdxInfo->nConstraint ) break;
- }
-
- /* The orderByConsumed signal is only valid if all outer loops collectively
- ** generate just a single row of output.
- */
- if( pIdxInfo->orderByConsumed ){
- for(i=0; i<p->i; i++){
- if( (p->aLevel[i].plan.wsFlags & WHERE_UNIQUE)==0 ){
- pIdxInfo->orderByConsumed = 0;
- }
- }
- }
-
- /* If there is an ORDER BY clause, and the selected virtual table index
- ** does not satisfy it, increase the cost of the scan accordingly. This
- ** matches the processing for non-virtual tables in bestBtreeIndex().
- */
- rCost = pIdxInfo->estimatedCost;
- if( p->pOrderBy && pIdxInfo->orderByConsumed==0 ){
- rCost += estLog(rCost)*rCost;
- }
-
- /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the
- ** inital value of lowestCost in this loop. If it is, then the
- ** (cost<lowestCost) test below will never be true.
- **
- ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT
- ** is defined.
- */
- if( (SQLITE_BIG_DBL/((double)2))<rCost ){
- p->cost.rCost = (SQLITE_BIG_DBL/((double)2));
- }else{
- p->cost.rCost = rCost;
- }
- p->cost.plan.u.pVtabIdx = pIdxInfo;
- if( pIdxInfo->orderByConsumed ){
- p->cost.plan.wsFlags |= WHERE_ORDERED;
- p->cost.plan.nOBSat = nOrderBy;
- }else{
- p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
- }
- p->cost.plan.nEq = 0;
- pIdxInfo->nOrderBy = nOrderBy;
-
- /* Try to find a more efficient access pattern by using multiple indexes
- ** to optimize an OR expression within the WHERE clause.
- */
- bestOrClauseIndex(p);
-}
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
@@ -2460,140 +1890,76 @@ static void bestVirtualIndex(WhereBestIdx *p){
**
** Return SQLITE_OK on success.
*/
-static int whereKeyStats(
+static void whereKeyStats(
Parse *pParse, /* Database connection */
Index *pIdx, /* Index to consider domain of */
- sqlite3_value *pVal, /* Value to consider */
+ UnpackedRecord *pRec, /* Vector of values to consider */
int roundUp, /* Round up if true. Round down if false */
tRowcnt *aStat /* OUT: stats written here */
){
- tRowcnt n;
- IndexSample *aSample;
- int i, eType;
- int isEq = 0;
- i64 v;
- double r, rS;
-
- assert( roundUp==0 || roundUp==1 );
+ IndexSample *aSample = pIdx->aSample;
+ int iCol; /* Index of required stats in anEq[] etc. */
+ int iMin = 0; /* Smallest sample not yet tested */
+ int i = pIdx->nSample; /* Smallest sample larger than or equal to pRec */
+ int iTest; /* Next sample to test */
+ int res; /* Result of comparison operation */
+
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( pParse );
+#endif
+ assert( pRec!=0 );
+ iCol = pRec->nField - 1;
assert( pIdx->nSample>0 );
- if( pVal==0 ) return SQLITE_ERROR;
- n = pIdx->aiRowEst[0];
- aSample = pIdx->aSample;
- eType = sqlite3_value_type(pVal);
-
- if( eType==SQLITE_INTEGER ){
- v = sqlite3_value_int64(pVal);
- r = (i64)v;
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( aSample[i].eType==SQLITE_INTEGER ){
- if( aSample[i].u.i>=v ){
- isEq = aSample[i].u.i==v;
- break;
- }
- }else{
- assert( aSample[i].eType==SQLITE_FLOAT );
- if( aSample[i].u.r>=r ){
- isEq = aSample[i].u.r==r;
- break;
- }
- }
- }
- }else if( eType==SQLITE_FLOAT ){
- r = sqlite3_value_double(pVal);
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( aSample[i].eType==SQLITE_FLOAT ){
- rS = aSample[i].u.r;
- }else{
- rS = aSample[i].u.i;
- }
- if( rS>=r ){
- isEq = rS==r;
- break;
- }
+ assert( pRec->nField>0 && iCol<pIdx->nSampleCol );
+ do{
+ iTest = (iMin+i)/2;
+ res = sqlite3VdbeRecordCompare(aSample[iTest].n, aSample[iTest].p, pRec, 0);
+ if( res<0 ){
+ iMin = iTest+1;
+ }else{
+ i = iTest;
}
- }else if( eType==SQLITE_NULL ){
- i = 0;
- if( aSample[0].eType==SQLITE_NULL ) isEq = 1;
+ }while( res && iMin<i );
+
+#ifdef SQLITE_DEBUG
+ /* The following assert statements check that the binary search code
+ ** above found the right answer. This block serves no purpose other
+ ** than to invoke the asserts. */
+ if( res==0 ){
+ /* If (res==0) is true, then sample $i must be equal to pRec */
+ assert( i<pIdx->nSample );
+ assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec, 0)
+ || pParse->db->mallocFailed );
}else{
- assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
- for(i=0; i<pIdx->nSample; i++){
- if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
- break;
- }
- }
- if( i<pIdx->nSample ){
- sqlite3 *db = pParse->db;
- CollSeq *pColl;
- const u8 *z;
- if( eType==SQLITE_BLOB ){
- z = (const u8 *)sqlite3_value_blob(pVal);
- pColl = db->pDfltColl;
- assert( pColl->enc==SQLITE_UTF8 );
- }else{
- pColl = sqlite3GetCollSeq(pParse, SQLITE_UTF8, 0, *pIdx->azColl);
- if( pColl==0 ){
- return SQLITE_ERROR;
- }
- z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
- if( !z ){
- return SQLITE_NOMEM;
- }
- assert( z && pColl && pColl->xCmp );
- }
- n = sqlite3ValueBytes(pVal, pColl->enc);
-
- for(; i<pIdx->nSample; i++){
- int c;
- int eSampletype = aSample[i].eType;
- if( eSampletype<eType ) continue;
- if( eSampletype!=eType ) break;
-#ifndef SQLITE_OMIT_UTF16
- if( pColl->enc!=SQLITE_UTF8 ){
- int nSample;
- char *zSample = sqlite3Utf8to16(
- db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample
- );
- if( !zSample ){
- assert( db->mallocFailed );
- return SQLITE_NOMEM;
- }
- c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
- sqlite3DbFree(db, zSample);
- }else
-#endif
- {
- c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
- }
- if( c>=0 ){
- if( c==0 ) isEq = 1;
- break;
- }
- }
- }
+ /* Otherwise, pRec must be smaller than sample $i and larger than
+ ** sample ($i-1). */
+ assert( i==pIdx->nSample
+ || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec, 0)>0
+ || pParse->db->mallocFailed );
+ assert( i==0
+ || sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec, 0)<0
+ || pParse->db->mallocFailed );
}
+#endif /* ifdef SQLITE_DEBUG */
/* At this point, aSample[i] is the first sample that is greater than
** or equal to pVal. Or if i==pIdx->nSample, then all samples are less
- ** than pVal. If aSample[i]==pVal, then isEq==1.
+ ** than pVal. If aSample[i]==pVal, then res==0.
*/
- if( isEq ){
- assert( i<pIdx->nSample );
- aStat[0] = aSample[i].nLt;
- aStat[1] = aSample[i].nEq;
+ if( res==0 ){
+ aStat[0] = aSample[i].anLt[iCol];
+ aStat[1] = aSample[i].anEq[iCol];
}else{
tRowcnt iLower, iUpper, iGap;
if( i==0 ){
iLower = 0;
- iUpper = aSample[0].nLt;
+ iUpper = aSample[0].anLt[iCol];
}else{
- iUpper = i>=pIdx->nSample ? n : aSample[i].nLt;
- iLower = aSample[i-1].nEq + aSample[i-1].nLt;
+ i64 nRow0 = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
+ iUpper = i>=pIdx->nSample ? nRow0 : aSample[i].anLt[iCol];
+ iLower = aSample[i-1].anEq[iCol] + aSample[i-1].anLt[iCol];
}
- aStat[1] = pIdx->avgEq;
+ aStat[1] = pIdx->aAvgEq[iCol];
if( iLower>=iUpper ){
iGap = 0;
}else{
@@ -2606,44 +1972,140 @@ static int whereKeyStats(
}
aStat[0] = iLower + iGap;
}
- return SQLITE_OK;
}
-#endif /* SQLITE_ENABLE_STAT3 */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
-** If expression pExpr represents a literal value, set *pp to point to
-** an sqlite3_value structure containing the same value, with affinity
-** aff applied to it, before returning. It is the responsibility of the
-** caller to eventually release this structure by passing it to
-** sqlite3ValueFree().
-**
-** If the current parse is a recompile (sqlite3Reprepare()) and pExpr
-** is an SQL variable that currently has a non-NULL value bound to it,
-** create an sqlite3_value structure containing this value, again with
-** affinity aff applied to it, instead.
-**
-** If neither of the above apply, set *pp to NULL.
-**
-** If an error occurs, return an error code. Otherwise, SQLITE_OK.
+** If it is not NULL, pTerm is a term that provides an upper or lower
+** bound on a range scan. Without considering pTerm, it is estimated
+** that the scan will visit nNew rows. This function returns the number
+** estimated to be visited after taking pTerm into account.
+**
+** If the user explicitly specified a likelihood() value for this term,
+** then the return value is the likelihood multiplied by the number of
+** input rows. Otherwise, this function assumes that an "IS NOT NULL" term
+** has a likelihood of 0.50, and any other term a likelihood of 0.25.
+*/
+static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
+ LogEst nRet = nNew;
+ if( pTerm ){
+ if( pTerm->truthProb<=0 ){
+ nRet += pTerm->truthProb;
+ }else if( (pTerm->wtFlags & TERM_VNULL)==0 ){
+ nRet -= 20; assert( 20==sqlite3LogEst(4) );
+ }
+ }
+ return nRet;
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** This function is called to estimate the number of rows visited by a
+** range-scan on a skip-scan index. For example:
+**
+** CREATE INDEX i1 ON t1(a, b, c);
+** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?;
+**
+** Value pLoop->nOut is currently set to the estimated number of rows
+** visited for scanning (a=? AND b=?). This function reduces that estimate
+** by some factor to account for the (c BETWEEN ? AND ?) expression based
+** on the stat4 data for the index. this scan will be peformed multiple
+** times (once for each (a,b) combination that matches a=?) is dealt with
+** by the caller.
+**
+** It does this by scanning through all stat4 samples, comparing values
+** extracted from pLower and pUpper with the corresponding column in each
+** sample. If L and U are the number of samples found to be less than or
+** equal to the values extracted from pLower and pUpper respectively, and
+** N is the total number of samples, the pLoop->nOut value is adjusted
+** as follows:
+**
+** nOut = nOut * ( min(U - L, 1) / N )
+**
+** If pLower is NULL, or a value cannot be extracted from the term, L is
+** set to zero. If pUpper is NULL, or a value cannot be extracted from it,
+** U is set to N.
+**
+** Normally, this function sets *pbDone to 1 before returning. However,
+** if no value can be extracted from either pLower or pUpper (and so the
+** estimate of the number of rows delivered remains unchanged), *pbDone
+** is left as is.
+**
+** If an error occurs, an SQLite error code is returned. Otherwise,
+** SQLITE_OK.
*/
-#ifdef SQLITE_ENABLE_STAT3
-static int valueFromExpr(
- Parse *pParse,
- Expr *pExpr,
- u8 aff,
- sqlite3_value **pp
+static int whereRangeSkipScanEst(
+ Parse *pParse, /* Parsing & code generating context */
+ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
+ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
+ WhereLoop *pLoop, /* Update the .nOut value of this loop */
+ int *pbDone /* Set to true if at least one expr. value extracted */
){
- if( pExpr->op==TK_VARIABLE
- || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
- ){
- int iVar = pExpr->iColumn;
- sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
- *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
- return SQLITE_OK;
+ Index *p = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
+ sqlite3 *db = pParse->db;
+ int nLower = -1;
+ int nUpper = p->nSample+1;
+ int rc = SQLITE_OK;
+ int iCol = p->aiColumn[nEq];
+ u8 aff = iCol>=0 ? p->pTable->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+ CollSeq *pColl;
+
+ sqlite3_value *p1 = 0; /* Value extracted from pLower */
+ sqlite3_value *p2 = 0; /* Value extracted from pUpper */
+ sqlite3_value *pVal = 0; /* Value extracted from record */
+
+ pColl = sqlite3LocateCollSeq(pParse, p->azColl[nEq]);
+ if( pLower ){
+ rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight, aff, &p1);
+ nLower = 0;
+ }
+ if( pUpper && rc==SQLITE_OK ){
+ rc = sqlite3Stat4ValueFromExpr(pParse, pUpper->pExpr->pRight, aff, &p2);
+ nUpper = p2 ? 0 : p->nSample;
+ }
+
+ if( p1 || p2 ){
+ int i;
+ int nDiff;
+ for(i=0; rc==SQLITE_OK && i<p->nSample; i++){
+ rc = sqlite3Stat4Column(db, p->aSample[i].p, p->aSample[i].n, nEq, &pVal);
+ if( rc==SQLITE_OK && p1 ){
+ int res = sqlite3MemCompare(p1, pVal, pColl);
+ if( res>=0 ) nLower++;
+ }
+ if( rc==SQLITE_OK && p2 ){
+ int res = sqlite3MemCompare(p2, pVal, pColl);
+ if( res>=0 ) nUpper++;
+ }
+ }
+ nDiff = (nUpper - nLower);
+ if( nDiff<=0 ) nDiff = 1;
+
+ /* If there is both an upper and lower bound specified, and the
+ ** comparisons indicate that they are close together, use the fallback
+ ** method (assume that the scan visits 1/64 of the rows) for estimating
+ ** the number of rows visited. Otherwise, estimate the number of rows
+ ** using the method described in the header comment for this function. */
+ if( nDiff!=1 || pUpper==0 || pLower==0 ){
+ int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff));
+ pLoop->nOut -= nAdjust;
+ *pbDone = 1;
+ WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
+ nLower, nUpper, nAdjust*-1, pLoop->nOut));
+ }
+
+ }else{
+ assert( *pbDone==0 );
}
- return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
+
+ sqlite3ValueFree(p1);
+ sqlite3ValueFree(p2);
+ sqlite3ValueFree(pVal);
+
+ return rc;
}
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -2660,97 +2122,167 @@ static int valueFromExpr(
** If either of the upper or lower bound is not present, then NULL is passed in
** place of the corresponding WhereTerm.
**
-** The nEq parameter is passed the index of the index column subject to the
-** range constraint. Or, equivalently, the number of equality constraints
-** optimized by the proposed index scan. For example, assuming index p is
-** on t1(a, b), and the SQL query is:
+** The value in (pBuilder->pNew->u.btree.nEq) is the index of the index
+** column subject to the range constraint. Or, equivalently, the number of
+** equality constraints optimized by the proposed index scan. For example,
+** assuming index p is on t1(a, b), and the SQL query is:
**
** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ...
**
-** then nEq should be passed the value 1 (as the range restricted column,
-** b, is the second left-most column of the index). Or, if the query is:
+** then nEq is set to 1 (as the range restricted column, b, is the second
+** left-most column of the index). Or, if the query is:
**
** ... FROM t1 WHERE a > ? AND a < ? ...
**
-** then nEq should be passed 0.
-**
-** The returned value is an integer divisor to reduce the estimated
-** search space. A return value of 1 means that range constraints are
-** no help at all. A return value of 2 means range constraints are
-** expected to reduce the search space by half. And so forth...
-**
-** In the absence of sqlite_stat3 ANALYZE data, each range inequality
-** reduces the search space by a factor of 4. Hence a single constraint (x>?)
-** results in a return of 4 and a range constraint (x>? AND x<?) results
-** in a return of 16.
+** then nEq is set to 0.
+**
+** When this function is called, *pnOut is set to the sqlite3LogEst() of the
+** number of rows that the index scan is expected to visit without
+** considering the range constraints. If nEq is 0, this is the number of
+** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
+** to account for the range contraints pLower and pUpper.
+**
+** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be
+** used, a single range inequality reduces the search space by a factor of 4.
+** and a pair of constraints (x>? AND x<?) reduces the expected number of
+** rows visited by a factor of 64.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index containing the range-compared column; "x" */
- int nEq, /* index into p->aCol[] of the range-compared column */
+ WhereLoopBuilder *pBuilder,
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
- double *pRangeDiv /* OUT: Reduce search space by this divisor */
+ WhereLoop *pLoop /* Modify the .nOut and maybe .rRun fields */
){
int rc = SQLITE_OK;
+ int nOut = pLoop->nOut;
+ LogEst nNew;
-#ifdef SQLITE_ENABLE_STAT3
-
- if( nEq==0 && p->nSample ){
- sqlite3_value *pRangeVal;
- tRowcnt iLower = 0;
- tRowcnt iUpper = p->aiRowEst[0];
- tRowcnt a[2];
- u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
-
- if( pLower ){
- Expr *pExpr = pLower->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
- assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
- if( rc==SQLITE_OK
- && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
- ){
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ Index *p = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
+
+ if( p->nSample>0
+ && nEq<p->nSampleCol
+ && OptimizationEnabled(pParse->db, SQLITE_Stat3)
+ ){
+ if( nEq==pBuilder->nRecValid ){
+ UnpackedRecord *pRec = pBuilder->pRec;
+ tRowcnt a[2];
+ u8 aff;
+
+ /* Variable iLower will be set to the estimate of the number of rows in
+ ** the index that are less than the lower bound of the range query. The
+ ** lower bound being the concatenation of $P and $L, where $P is the
+ ** key-prefix formed by the nEq values matched against the nEq left-most
+ ** columns of the index, and $L is the value in pLower.
+ **
+ ** Or, if pLower is NULL or $L cannot be extracted from it (because it
+ ** is not a simple variable or literal value), the lower bound of the
+ ** range is $P. Due to a quirk in the way whereKeyStats() works, even
+ ** if $L is available, whereKeyStats() is called for both ($P) and
+ ** ($P:$L) and the larger of the two returned values used.
+ **
+ ** Similarly, iUpper is to be set to the estimate of the number of rows
+ ** less than the upper bound of the range query. Where the upper bound
+ ** is either ($P) or ($P:$U). Again, even if $U is available, both values
+ ** of iUpper are requested of whereKeyStats() and the smaller used.
+ */
+ tRowcnt iLower;
+ tRowcnt iUpper;
+
+ if( nEq==p->nKeyCol ){
+ aff = SQLITE_AFF_INTEGER;
+ }else{
+ aff = p->pTable->aCol[p->aiColumn[nEq]].affinity;
+ }
+ /* Determine iLower and iUpper using ($P) only. */
+ if( nEq==0 ){
+ iLower = 0;
+ iUpper = sqlite3LogEstToInt(p->aiRowLogEst[0]);
+ }else{
+ /* Note: this call could be optimized away - since the same values must
+ ** have been requested when testing key $P in whereEqualScanEst(). */
+ whereKeyStats(pParse, p, pRec, 0, a);
iLower = a[0];
- if( (pLower->eOperator & WO_GT)!=0 ) iLower += a[1];
+ iUpper = a[0] + a[1];
}
- sqlite3ValueFree(pRangeVal);
- }
- if( rc==SQLITE_OK && pUpper ){
- Expr *pExpr = pUpper->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
- assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
- if( rc==SQLITE_OK
- && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
- ){
- iUpper = a[0];
- if( (pUpper->eOperator & WO_LE)!=0 ) iUpper += a[1];
+
+ /* If possible, improve on the iLower estimate using ($P:$L). */
+ if( pLower ){
+ int bOk; /* True if value is extracted from pExpr */
+ Expr *pExpr = pLower->pExpr->pRight;
+ assert( (pLower->eOperator & (WO_GT|WO_GE))!=0 );
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
+ if( rc==SQLITE_OK && bOk ){
+ tRowcnt iNew;
+ whereKeyStats(pParse, p, pRec, 0, a);
+ iNew = a[0] + ((pLower->eOperator & WO_GT) ? a[1] : 0);
+ if( iNew>iLower ) iLower = iNew;
+ nOut--;
+ }
}
- sqlite3ValueFree(pRangeVal);
- }
- if( rc==SQLITE_OK ){
- if( iUpper<=iLower ){
- *pRangeDiv = (double)p->aiRowEst[0];
- }else{
- *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower);
+
+ /* If possible, improve on the iUpper estimate using ($P:$U). */
+ if( pUpper ){
+ int bOk; /* True if value is extracted from pExpr */
+ Expr *pExpr = pUpper->pExpr->pRight;
+ assert( (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq, &bOk);
+ if( rc==SQLITE_OK && bOk ){
+ tRowcnt iNew;
+ whereKeyStats(pParse, p, pRec, 1, a);
+ iNew = a[0] + ((pUpper->eOperator & WO_LE) ? a[1] : 0);
+ if( iNew<iUpper ) iUpper = iNew;
+ nOut--;
+ }
+ }
+
+ pBuilder->pRec = pRec;
+ if( rc==SQLITE_OK ){
+ if( iUpper>iLower ){
+ nNew = sqlite3LogEst(iUpper - iLower);
+ }else{
+ nNew = 10; assert( 10==sqlite3LogEst(2) );
+ }
+ if( nNew<nOut ){
+ nOut = nNew;
+ }
+ pLoop->nOut = (LogEst)nOut;
+ WHERETRACE(0x10, ("range scan regions: %u..%u est=%d\n",
+ (u32)iLower, (u32)iUpper, nOut));
+ return SQLITE_OK;
}
- WHERETRACE(("range scan regions: %u..%u div=%g\n",
- (u32)iLower, (u32)iUpper, *pRangeDiv));
- return SQLITE_OK;
+ }else{
+ int bDone = 0;
+ rc = whereRangeSkipScanEst(pParse, pLower, pUpper, pLoop, &bDone);
+ if( bDone ) return rc;
}
}
#else
UNUSED_PARAMETER(pParse);
- UNUSED_PARAMETER(p);
- UNUSED_PARAMETER(nEq);
+ UNUSED_PARAMETER(pBuilder);
#endif
assert( pLower || pUpper );
- *pRangeDiv = (double)1;
- if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4;
- if( pUpper ) *pRangeDiv *= (double)4;
+ assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 );
+ nNew = whereRangeAdjust(pLower, nOut);
+ nNew = whereRangeAdjust(pUpper, nNew);
+
+ /* TUNING: If there is both an upper and lower limit, assume the range is
+ ** reduced by an additional 75%. This means that, by default, an open-ended
+ ** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
+ ** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to
+ ** match 1/64 of the index. */
+ if( pLower && pUpper ) nNew -= 20;
+
+ nOut -= (pLower!=0) + (pUpper!=0);
+ if( nNew<10 ) nNew = 10;
+ if( nNew<nOut ) nOut = nNew;
+ pLoop->nOut = (LogEst)nOut;
return rc;
}
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
@@ -2770,37 +2302,53 @@ static int whereRangeScanEst(
*/
static int whereEqualScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */
- double *pnRow /* Write the revised row estimate here */
+ tRowcnt *pnRow /* Write the revised row estimate here */
){
- sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ int nEq = pBuilder->pNew->u.btree.nEq;
+ UnpackedRecord *pRec = pBuilder->pRec;
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
tRowcnt a[2]; /* Statistics */
+ int bOk;
+ assert( nEq>=1 );
+ assert( nEq<=p->nColumn );
assert( p->aSample!=0 );
assert( p->nSample>0 );
- aff = p->pTable->aCol[p->aiColumn[0]].affinity;
- if( pExpr ){
- rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
- if( rc ) goto whereEqualScanEst_cancel;
- }else{
- pRhs = sqlite3ValueNew(pParse->db);
+ assert( pBuilder->nRecValid<nEq );
+
+ /* If values are not available for all fields of the index to the left
+ ** of this one, no estimate can be made. Return SQLITE_NOTFOUND. */
+ if( pBuilder->nRecValid<(nEq-1) ){
+ return SQLITE_NOTFOUND;
}
- if( pRhs==0 ) return SQLITE_NOTFOUND;
- rc = whereKeyStats(pParse, p, pRhs, 0, a);
- if( rc==SQLITE_OK ){
- WHERETRACE(("equality scan regions: %d\n", (int)a[1]));
- *pnRow = a[1];
+
+ /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
+ ** below would return the same value. */
+ if( nEq>=p->nColumn ){
+ *pnRow = 1;
+ return SQLITE_OK;
}
-whereEqualScanEst_cancel:
- sqlite3ValueFree(pRhs);
+
+ aff = p->pTable->aCol[p->aiColumn[nEq-1]].affinity;
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, aff, nEq-1, &bOk);
+ pBuilder->pRec = pRec;
+ if( rc!=SQLITE_OK ) return rc;
+ if( bOk==0 ) return SQLITE_NOTFOUND;
+ pBuilder->nRecValid = nEq;
+
+ whereKeyStats(pParse, p, pRec, 0, a);
+ WHERETRACE(0x10,("equality scan regions: %d\n", (int)a[1]));
+ *pnRow = a[1];
+
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT3) */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
-#ifdef SQLITE_ENABLE_STAT3
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -2819,902 +2367,35 @@ whereEqualScanEst_cancel:
*/
static int whereInScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
- double *pnRow /* Write the revised row estimate here */
+ tRowcnt *pnRow /* Write the revised row estimate here */
){
- int rc = SQLITE_OK; /* Subfunction return code */
- double nEst; /* Number of rows for a single term */
- double nRowEst = (double)0; /* New estimate of the number of rows */
- int i; /* Loop counter */
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]);
+ int nRecValid = pBuilder->nRecValid;
+ int rc = SQLITE_OK; /* Subfunction return code */
+ tRowcnt nEst; /* Number of rows for a single term */
+ tRowcnt nRowEst = 0; /* New estimate of the number of rows */
+ int i; /* Loop counter */
assert( p->aSample!=0 );
for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
- nEst = p->aiRowEst[0];
- rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
+ nEst = nRow0;
+ rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
nRowEst += nEst;
+ pBuilder->nRecValid = nRecValid;
}
+
if( rc==SQLITE_OK ){
- if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
+ if( nRowEst > nRow0 ) nRowEst = nRow0;
*pnRow = nRowEst;
- WHERETRACE(("IN row estimate: est=%g\n", nRowEst));
+ WHERETRACE(0x10,("IN row estimate: est=%g\n", nRowEst));
}
+ assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT3) */
-
-/*
-** Check to see if column iCol of the table with cursor iTab will appear
-** in sorted order according to the current query plan.
-**
-** Return values:
-**
-** 0 iCol is not ordered
-** 1 iCol has only a single value
-** 2 iCol is in ASC order
-** 3 iCol is in DESC order
-*/
-static int isOrderedColumn(
- WhereBestIdx *p,
- int iTab,
- int iCol
-){
- int i, j;
- WhereLevel *pLevel = &p->aLevel[p->i-1];
- Index *pIdx;
- u8 sortOrder;
- for(i=p->i-1; i>=0; i--, pLevel--){
- if( pLevel->iTabCur!=iTab ) continue;
- if( (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- return 1;
- }
- assert( (pLevel->plan.wsFlags & WHERE_ORDERED)!=0 );
- if( (pIdx = pLevel->plan.u.pIdx)!=0 ){
- if( iCol<0 ){
- sortOrder = 0;
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }else{
- int n = pIdx->nColumn;
- for(j=0; j<n; j++){
- if( iCol==pIdx->aiColumn[j] ) break;
- }
- if( j>=n ) return 0;
- sortOrder = pIdx->aSortOrder[j];
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }
- }else{
- if( iCol!=(-1) ) return 0;
- sortOrder = 0;
- testcase( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 );
- }
- if( (pLevel->plan.wsFlags & WHERE_REVERSE)!=0 ){
- assert( sortOrder==0 || sortOrder==1 );
- testcase( sortOrder==1 );
- sortOrder = 1 - sortOrder;
- }
- return sortOrder+2;
- }
- return 0;
-}
-
-/*
-** This routine decides if pIdx can be used to satisfy the ORDER BY
-** clause, either in whole or in part. The return value is the
-** cumulative number of terms in the ORDER BY clause that are satisfied
-** by the index pIdx and other indices in outer loops.
-**
-** The table being queried has a cursor number of "base". pIdx is the
-** index that is postulated for use to access the table.
-**
-** The *pbRev value is set to 0 order 1 depending on whether or not
-** pIdx should be run in the forward order or in reverse order.
-*/
-static int isSortingIndex(
- WhereBestIdx *p, /* Best index search context */
- Index *pIdx, /* The index we are testing */
- int base, /* Cursor number for the table to be sorted */
- int *pbRev, /* Set to 1 for reverse-order scan of pIdx */
- int *pbObUnique /* ORDER BY column values will different in every row */
-){
- int i; /* Number of pIdx terms used */
- int j; /* Number of ORDER BY terms satisfied */
- int sortOrder = 2; /* 0: forward. 1: backward. 2: unknown */
- int nTerm; /* Number of ORDER BY terms */
- struct ExprList_item *pOBItem;/* A term of the ORDER BY clause */
- Table *pTab = pIdx->pTable; /* Table that owns index pIdx */
- ExprList *pOrderBy; /* The ORDER BY clause */
- Parse *pParse = p->pParse; /* Parser context */
- sqlite3 *db = pParse->db; /* Database connection */
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
- int seenRowid = 0; /* True if an ORDER BY rowid term is seen */
- int uniqueNotNull; /* pIdx is UNIQUE with all terms are NOT NULL */
- int outerObUnique; /* Outer loops generate different values in
- ** every row for the ORDER BY columns */
-
- if( p->i==0 ){
- nPriorSat = 0;
- outerObUnique = 1;
- }else{
- u32 wsFlags = p->aLevel[p->i-1].plan.wsFlags;
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
- if( (wsFlags & WHERE_ORDERED)==0 ){
- /* This loop cannot be ordered unless the next outer loop is
- ** also ordered */
- return nPriorSat;
- }
- if( OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ){
- /* Only look at the outer-most loop if the OrderByIdxJoin
- ** optimization is disabled */
- return nPriorSat;
- }
- testcase( wsFlags & WHERE_OB_UNIQUE );
- testcase( wsFlags & WHERE_ALL_UNIQUE );
- outerObUnique = (wsFlags & (WHERE_OB_UNIQUE|WHERE_ALL_UNIQUE))!=0;
- }
- pOrderBy = p->pOrderBy;
- assert( pOrderBy!=0 );
- if( pIdx->bUnordered ){
- /* Hash indices (indicated by the "unordered" tag on sqlite_stat1) cannot
- ** be used for sorting */
- return nPriorSat;
- }
- nTerm = pOrderBy->nExpr;
- uniqueNotNull = pIdx->onError!=OE_None;
- assert( nTerm>0 );
-
- /* Argument pIdx must either point to a 'real' named index structure,
- ** or an index structure allocated on the stack by bestBtreeIndex() to
- ** represent the rowid index that is part of every table. */
- assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
-
- /* Match terms of the ORDER BY clause against columns of
- ** the index.
- **
- ** Note that indices have pIdx->nColumn regular columns plus
- ** one additional column containing the rowid. The rowid column
- ** of the index is also allowed to match against the ORDER BY
- ** clause.
- */
- j = nPriorSat;
- for(i=0,pOBItem=&pOrderBy->a[j]; j<nTerm && i<=pIdx->nColumn; i++){
- Expr *pOBExpr; /* The expression of the ORDER BY pOBItem */
- CollSeq *pColl; /* The collating sequence of pOBExpr */
- int termSortOrder; /* Sort order for this term */
- int iColumn; /* The i-th column of the index. -1 for rowid */
- int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
- int isEq; /* Subject to an == or IS NULL constraint */
- int isMatch; /* ORDER BY term matches the index term */
- const char *zColl; /* Name of collating sequence for i-th index term */
- WhereTerm *pConstraint; /* A constraint in the WHERE clause */
-
- /* If the next term of the ORDER BY clause refers to anything other than
- ** a column in the "base" table, then this index will not be of any
- ** further use in handling the ORDER BY. */
- pOBExpr = sqlite3ExprSkipCollate(pOBItem->pExpr);
- if( pOBExpr->op!=TK_COLUMN || pOBExpr->iTable!=base ){
- break;
- }
-
- /* Find column number and collating sequence for the next entry
- ** in the index */
- if( pIdx->zName && i<pIdx->nColumn ){
- iColumn = pIdx->aiColumn[i];
- if( iColumn==pIdx->pTable->iPKey ){
- iColumn = -1;
- }
- iSortOrder = pIdx->aSortOrder[i];
- zColl = pIdx->azColl[i];
- assert( zColl!=0 );
- }else{
- iColumn = -1;
- iSortOrder = 0;
- zColl = 0;
- }
-
- /* Check to see if the column number and collating sequence of the
- ** index match the column number and collating sequence of the ORDER BY
- ** clause entry. Set isMatch to 1 if they both match. */
- if( pOBExpr->iColumn==iColumn ){
- if( zColl ){
- pColl = sqlite3ExprCollSeq(pParse, pOBItem->pExpr);
- if( !pColl ) pColl = db->pDfltColl;
- isMatch = sqlite3StrICmp(pColl->zName, zColl)==0;
- }else{
- isMatch = 1;
- }
- }else{
- isMatch = 0;
- }
-
- /* termSortOrder is 0 or 1 for whether or not the access loop should
- ** run forward or backwards (respectively) in order to satisfy this
- ** term of the ORDER BY clause. */
- assert( pOBItem->sortOrder==0 || pOBItem->sortOrder==1 );
- assert( iSortOrder==0 || iSortOrder==1 );
- termSortOrder = iSortOrder ^ pOBItem->sortOrder;
-
- /* If X is the column in the index and ORDER BY clause, check to see
- ** if there are any X= or X IS NULL constraints in the WHERE clause. */
- pConstraint = findTerm(p->pWC, base, iColumn, p->notReady,
- WO_EQ|WO_ISNULL|WO_IN, pIdx);
- if( pConstraint==0 ){
- isEq = 0;
- }else if( (pConstraint->eOperator & WO_IN)!=0 ){
- isEq = 0;
- }else if( (pConstraint->eOperator & WO_ISNULL)!=0 ){
- uniqueNotNull = 0;
- isEq = 1; /* "X IS NULL" means X has only a single value */
- }else if( pConstraint->prereqRight==0 ){
- isEq = 1; /* Constraint "X=constant" means X has only a single value */
- }else{
- Expr *pRight = pConstraint->pExpr->pRight;
- if( pRight->op==TK_COLUMN ){
- WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)",
- pRight->iTable, pRight->iColumn));
- isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn);
- WHERETRACE((" -> isEq=%d\n", isEq));
-
- /* If the constraint is of the form X=Y where Y is an ordered value
- ** in an outer loop, then make sure the sort order of Y matches the
- ** sort order required for X. */
- if( isMatch && isEq>=2 && isEq!=pOBItem->sortOrder+2 ){
- testcase( isEq==2 );
- testcase( isEq==3 );
- break;
- }
- }else{
- isEq = 0; /* "X=expr" places no ordering constraints on X */
- }
- }
- if( !isMatch ){
- if( isEq==0 ){
- break;
- }else{
- continue;
- }
- }else if( isEq!=1 ){
- if( sortOrder==2 ){
- sortOrder = termSortOrder;
- }else if( termSortOrder!=sortOrder ){
- break;
- }
- }
- j++;
- pOBItem++;
- if( iColumn<0 ){
- seenRowid = 1;
- break;
- }else if( pTab->aCol[iColumn].notNull==0 && isEq!=1 ){
- testcase( isEq==0 );
- testcase( isEq==2 );
- testcase( isEq==3 );
- uniqueNotNull = 0;
- }
- }
- if( seenRowid ){
- uniqueNotNull = 1;
- }else if( uniqueNotNull==0 || i<pIdx->nColumn ){
- uniqueNotNull = 0;
- }
-
- /* If we have not found at least one ORDER BY term that matches the
- ** index, then show no progress. */
- if( pOBItem==&pOrderBy->a[nPriorSat] ) return nPriorSat;
-
- /* Either the outer queries must generate rows where there are no two
- ** rows with the same values in all ORDER BY columns, or else this
- ** loop must generate just a single row of output. Example: Suppose
- ** the outer loops generate A=1 and A=1, and this loop generates B=3
- ** and B=4. Then without the following test, ORDER BY A,B would
- ** generate the wrong order output: 1,3 1,4 1,3 1,4
- */
- if( outerObUnique==0 && uniqueNotNull==0 ) return nPriorSat;
- *pbObUnique = uniqueNotNull;
-
- /* Return the necessary scan order back to the caller */
- *pbRev = sortOrder & 1;
-
- /* If there was an "ORDER BY rowid" term that matched, or it is only
- ** possible for a single row from this table to match, then skip over
- ** any additional ORDER BY terms dealing with this table.
- */
- if( uniqueNotNull ){
- /* Advance j over additional ORDER BY terms associated with base */
- WhereMaskSet *pMS = p->pWC->pMaskSet;
- Bitmask m = ~getMask(pMS, base);
- while( j<nTerm && (exprTableUsage(pMS, pOrderBy->a[j].pExpr)&m)==0 ){
- j++;
- }
- }
- return j;
-}
-
-/*
-** Find the best query plan for accessing a particular table. Write the
-** best query plan and its cost into the p->cost.
-**
-** The lowest cost plan wins. The cost is an estimate of the amount of
-** CPU and disk I/O needed to process the requested result.
-** Factors that influence cost include:
-**
-** * The estimated number of rows that will be retrieved. (The
-** fewer the better.)
-**
-** * Whether or not sorting must occur.
-**
-** * Whether or not there must be separate lookups in the
-** index and in the main table.
-**
-** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
-** the SQL statement, then this function only considers plans using the
-** named index. If no such plan is found, then the returned cost is
-** SQLITE_BIG_DBL. If a plan is found that uses the named index,
-** then the cost is calculated in the usual way.
-**
-** If a NOT INDEXED clause was attached to the table
-** in the SELECT statement, then no indexes are considered. However, the
-** selected plan may still take advantage of the built-in rowid primary key
-** index.
-*/
-static void bestBtreeIndex(WhereBestIdx *p){
- Parse *pParse = p->pParse; /* The parsing context */
- WhereClause *pWC = p->pWC; /* The WHERE clause */
- struct SrcList_item *pSrc = p->pSrc; /* The FROM clause term to search */
- int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
- Index *pProbe; /* An index we are evaluating */
- Index *pIdx; /* Copy of pProbe, or zero for IPK index */
- int eqTermMask; /* Current mask of valid equality operators */
- int idxEqTermMask; /* Index mask of valid equality operators */
- Index sPk; /* A fake index object for the primary key */
- tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
- int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
- int wsFlagMask; /* Allowed flags in p->cost.plan.wsFlag */
- int nPriorSat; /* ORDER BY terms satisfied by outer loops */
- int nOrderBy; /* Number of ORDER BY terms */
- char bSortInit; /* Initializer for bSort in inner loop */
- char bDistInit; /* Initializer for bDist in inner loop */
-
-
- /* Initialize the cost to a worst-case value */
- memset(&p->cost, 0, sizeof(p->cost));
- p->cost.rCost = SQLITE_BIG_DBL;
-
- /* If the pSrc table is the right table of a LEFT JOIN then we may not
- ** use an index to satisfy IS NULL constraints on that table. This is
- ** because columns might end up being NULL if the table does not match -
- ** a circumstance which the index cannot help us discover. Ticket #2177.
- */
- if( pSrc->jointype & JT_LEFT ){
- idxEqTermMask = WO_EQ|WO_IN;
- }else{
- idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL;
- }
-
- if( pSrc->pIndex ){
- /* An INDEXED BY clause specifies a particular index to use */
- pIdx = pProbe = pSrc->pIndex;
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
- }else{
- /* There is no INDEXED BY clause. Create a fake Index object in local
- ** variable sPk to represent the rowid primary key index. Make this
- ** fake index the first in a chain of Index objects with all of the real
- ** indices to follow */
- Index *pFirst; /* First of real indices on the table */
- memset(&sPk, 0, sizeof(Index));
- sPk.nColumn = 1;
- sPk.aiColumn = &aiColumnPk;
- sPk.aiRowEst = aiRowEstPk;
- sPk.onError = OE_Replace;
- sPk.pTable = pSrc->pTab;
- aiRowEstPk[0] = pSrc->pTab->nRowEst;
- aiRowEstPk[1] = 1;
- pFirst = pSrc->pTab->pIndex;
- if( pSrc->notIndexed==0 ){
- /* The real indices of the table are only considered if the
- ** NOT INDEXED qualifier is omitted from the FROM clause */
- sPk.pNext = pFirst;
- }
- pProbe = &sPk;
- wsFlagMask = ~(
- WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
- );
- eqTermMask = WO_EQ|WO_IN;
- pIdx = 0;
- }
-
- nOrderBy = p->pOrderBy ? p->pOrderBy->nExpr : 0;
- if( p->i ){
- nPriorSat = p->aLevel[p->i-1].plan.nOBSat;
- bSortInit = nPriorSat<nOrderBy;
- bDistInit = 0;
- }else{
- nPriorSat = 0;
- bSortInit = nOrderBy>0;
- bDistInit = p->pDistinct!=0;
- }
-
- /* Loop over all indices looking for the best one to use
- */
- for(; pProbe; pIdx=pProbe=pProbe->pNext){
- const tRowcnt * const aiRowEst = pProbe->aiRowEst;
- WhereCost pc; /* Cost of using pProbe */
- double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
-
- /* The following variables are populated based on the properties of
- ** index being evaluated. They are then used to determine the expected
- ** cost and number of rows returned.
- **
- ** pc.plan.nEq:
- ** Number of equality terms that can be implemented using the index.
- ** In other words, the number of initial fields in the index that
- ** are used in == or IN or NOT NULL constraints of the WHERE clause.
- **
- ** nInMul:
- ** The "in-multiplier". This is an estimate of how many seek operations
- ** SQLite must perform on the index in question. For example, if the
- ** WHERE clause is:
- **
- ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6)
- **
- ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is
- ** set to 9. Given the same schema and either of the following WHERE
- ** clauses:
- **
- ** WHERE a = 1
- ** WHERE a >= 2
- **
- ** nInMul is set to 1.
- **
- ** If there exists a WHERE term of the form "x IN (SELECT ...)", then
- ** the sub-select is assumed to return 25 rows for the purposes of
- ** determining nInMul.
- **
- ** bInEst:
- ** Set to true if there was at least one "x IN (SELECT ...)" term used
- ** in determining the value of nInMul. Note that the RHS of the
- ** IN operator must be a SELECT, not a value list, for this variable
- ** to be true.
- **
- ** rangeDiv:
- ** An estimate of a divisor by which to reduce the search space due
- ** to inequality constraints. In the absence of sqlite_stat3 ANALYZE
- ** data, a single inequality reduces the search space to 1/4rd its
- ** original size (rangeDiv==4). Two inequalities reduce the search
- ** space to 1/16th of its original size (rangeDiv==16).
- **
- ** bSort:
- ** Boolean. True if there is an ORDER BY clause that will require an
- ** external sort (i.e. scanning the index being evaluated will not
- ** correctly order records).
- **
- ** bDist:
- ** Boolean. True if there is a DISTINCT clause that will require an
- ** external btree.
- **
- ** bLookup:
- ** Boolean. True if a table lookup is required for each index entry
- ** visited. In other words, true if this is not a covering index.
- ** This is always false for the rowid primary key index of a table.
- ** For other indexes, it is true unless all the columns of the table
- ** used by the SELECT statement are present in the index (such an
- ** index is sometimes described as a covering index).
- ** For example, given the index on (a, b), the second of the following
- ** two queries requires table b-tree lookups in order to find the value
- ** of column c, but the first does not because columns a and b are
- ** both available in the index.
- **
- ** SELECT a, b FROM tbl WHERE a = 1;
- ** SELECT a, b, c FROM tbl WHERE a = 1;
- */
- int bInEst = 0; /* True if "x IN (SELECT...)" seen */
- int nInMul = 1; /* Number of distinct equalities to lookup */
- double rangeDiv = (double)1; /* Estimated reduction in search space */
- int nBound = 0; /* Number of range constraints seen */
- char bSort = bSortInit; /* True if external sort required */
- char bDist = bDistInit; /* True if index cannot help with DISTINCT */
- char bLookup = 0; /* True if not a covering index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-#ifdef SQLITE_ENABLE_STAT3
- WhereTerm *pFirstTerm = 0; /* First term matching the index */
-#endif
-
- WHERETRACE((
- " %s(%s):\n",
- pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk")
- ));
- memset(&pc, 0, sizeof(pc));
- pc.plan.nOBSat = nPriorSat;
-
- /* Determine the values of pc.plan.nEq and nInMul */
- for(pc.plan.nEq=0; pc.plan.nEq<pProbe->nColumn; pc.plan.nEq++){
- int j = pProbe->aiColumn[pc.plan.nEq];
- pTerm = findTerm(pWC, iCur, j, p->notReady, eqTermMask, pIdx);
- if( pTerm==0 ) break;
- pc.plan.wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
- testcase( pTerm->pWC!=pWC );
- if( pTerm->eOperator & WO_IN ){
- Expr *pExpr = pTerm->pExpr;
- pc.plan.wsFlags |= WHERE_COLUMN_IN;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
- nInMul *= 25;
- bInEst = 1;
- }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
- /* "x IN (value, value, ...)" */
- nInMul *= pExpr->x.pList->nExpr;
- }
- }else if( pTerm->eOperator & WO_ISNULL ){
- pc.plan.wsFlags |= WHERE_COLUMN_NULL;
- }
-#ifdef SQLITE_ENABLE_STAT3
- if( pc.plan.nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
-#endif
- pc.used |= pTerm->prereqRight;
- }
-
- /* If the index being considered is UNIQUE, and there is an equality
- ** constraint for all columns in the index, then this search will find
- ** at most a single row. In this case set the WHERE_UNIQUE flag to
- ** indicate this to the caller.
- **
- ** Otherwise, if the search may find more than one row, test to see if
- ** there is a range constraint on indexed column (pc.plan.nEq+1) that
- ** can be optimized using the index.
- */
- if( pc.plan.nEq==pProbe->nColumn && pProbe->onError!=OE_None ){
- testcase( pc.plan.wsFlags & WHERE_COLUMN_IN );
- testcase( pc.plan.wsFlags & WHERE_COLUMN_NULL );
- if( (pc.plan.wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
- pc.plan.wsFlags |= WHERE_UNIQUE;
- if( p->i==0 || (p->aLevel[p->i-1].plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- pc.plan.wsFlags |= WHERE_ALL_UNIQUE;
- }
- }
- }else if( pProbe->bUnordered==0 ){
- int j;
- j = (pc.plan.nEq==pProbe->nColumn ? -1 : pProbe->aiColumn[pc.plan.nEq]);
- if( findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
- WhereTerm *pTop, *pBtm;
- pTop = findTerm(pWC, iCur, j, p->notReady, WO_LT|WO_LE, pIdx);
- pBtm = findTerm(pWC, iCur, j, p->notReady, WO_GT|WO_GE, pIdx);
- whereRangeScanEst(pParse, pProbe, pc.plan.nEq, pBtm, pTop, &rangeDiv);
- if( pTop ){
- nBound = 1;
- pc.plan.wsFlags |= WHERE_TOP_LIMIT;
- pc.used |= pTop->prereqRight;
- testcase( pTop->pWC!=pWC );
- }
- if( pBtm ){
- nBound++;
- pc.plan.wsFlags |= WHERE_BTM_LIMIT;
- pc.used |= pBtm->prereqRight;
- testcase( pBtm->pWC!=pWC );
- }
- pc.plan.wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
- }
- }
-
- /* If there is an ORDER BY clause and the index being considered will
- ** naturally scan rows in the required order, set the appropriate flags
- ** in pc.plan.wsFlags. Otherwise, if there is an ORDER BY clause but
- ** the index will scan rows in a different order, set the bSort
- ** variable. */
- if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
- int bRev = 2;
- int bObUnique = 0;
- WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat));
- pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique);
- WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n",
- bRev, bObUnique, pc.plan.nOBSat));
- if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
- pc.plan.wsFlags |= WHERE_ORDERED;
- if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE;
- }
- if( nOrderBy==pc.plan.nOBSat ){
- bSort = 0;
- pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE;
- }
- if( bRev & 1 ) pc.plan.wsFlags |= WHERE_REVERSE;
- }
-
- /* If there is a DISTINCT qualifier and this index will scan rows in
- ** order of the DISTINCT expressions, clear bDist and set the appropriate
- ** flags in pc.plan.wsFlags. */
- if( bDist
- && isDistinctIndex(pParse, pWC, pProbe, iCur, p->pDistinct, pc.plan.nEq)
- && (pc.plan.wsFlags & WHERE_COLUMN_IN)==0
- ){
- bDist = 0;
- pc.plan.wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
- }
-
- /* If currently calculating the cost of using an index (not the IPK
- ** index), determine if all required column data may be obtained without
- ** using the main table (i.e. if the index is a covering
- ** index for this query). If it is, set the WHERE_IDX_ONLY flag in
- ** pc.plan.wsFlags. Otherwise, set the bLookup variable to true. */
- if( pIdx ){
- Bitmask m = pSrc->colUsed;
- int j;
- for(j=0; j<pIdx->nColumn; j++){
- int x = pIdx->aiColumn[j];
- if( x<BMS-1 ){
- m &= ~(((Bitmask)1)<<x);
- }
- }
- if( m==0 ){
- pc.plan.wsFlags |= WHERE_IDX_ONLY;
- }else{
- bLookup = 1;
- }
- }
-
- /*
- ** Estimate the number of rows of output. For an "x IN (SELECT...)"
- ** constraint, do not let the estimate exceed half the rows in the table.
- */
- pc.plan.nRow = (double)(aiRowEst[pc.plan.nEq] * nInMul);
- if( bInEst && pc.plan.nRow*2>aiRowEst[0] ){
- pc.plan.nRow = aiRowEst[0]/2;
- nInMul = (int)(pc.plan.nRow / aiRowEst[pc.plan.nEq]);
- }
-
-#ifdef SQLITE_ENABLE_STAT3
- /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
- ** and we do not think that values of x are unique and if histogram
- ** data is available for column x, then it might be possible
- ** to get a better estimate on the number of rows based on
- ** VALUE and how common that value is according to the histogram.
- */
- if( pc.plan.nRow>(double)1 && pc.plan.nEq==1
- && pFirstTerm!=0 && aiRowEst[1]>1 ){
- assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
- if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
- testcase( pFirstTerm->eOperator & WO_EQ );
- testcase( pFirstTerm->eOperator & WO_EQUIV );
- testcase( pFirstTerm->eOperator & WO_ISNULL );
- whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight,
- &pc.plan.nRow);
- }else if( bInEst==0 ){
- assert( pFirstTerm->eOperator & WO_IN );
- whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList,
- &pc.plan.nRow);
- }
- }
-#endif /* SQLITE_ENABLE_STAT3 */
-
- /* Adjust the number of output rows and downward to reflect rows
- ** that are excluded by range constraints.
- */
- pc.plan.nRow = pc.plan.nRow/rangeDiv;
- if( pc.plan.nRow<1 ) pc.plan.nRow = 1;
-
- /* Experiments run on real SQLite databases show that the time needed
- ** to do a binary search to locate a row in a table or index is roughly
- ** log10(N) times the time to move from one row to the next row within
- ** a table or index. The actual times can vary, with the size of
- ** records being an important factor. Both moves and searches are
- ** slower with larger records, presumably because fewer records fit
- ** on one page and hence more pages have to be fetched.
- **
- ** The ANALYZE command and the sqlite_stat1 and sqlite_stat3 tables do
- ** not give us data on the relative sizes of table and index records.
- ** So this computation assumes table records are about twice as big
- ** as index records
- */
- if( (pc.plan.wsFlags&~(WHERE_REVERSE|WHERE_ORDERED|WHERE_OB_UNIQUE))
- ==WHERE_IDX_ONLY
- && (pWC->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
- && sqlite3GlobalConfig.bUseCis
- && OptimizationEnabled(pParse->db, SQLITE_CoverIdxScan)
- ){
- /* This index is not useful for indexing, but it is a covering index.
- ** A full-scan of the index might be a little faster than a full-scan
- ** of the table, so give this case a cost slightly less than a table
- ** scan. */
- pc.rCost = aiRowEst[0]*3 + pProbe->nColumn;
- pc.plan.wsFlags |= WHERE_COVER_SCAN|WHERE_COLUMN_RANGE;
- }else if( (pc.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- /* The cost of a full table scan is a number of move operations equal
- ** to the number of rows in the table.
- **
- ** We add an additional 4x penalty to full table scans. This causes
- ** the cost function to err on the side of choosing an index over
- ** choosing a full scan. This 4x full-scan penalty is an arguable
- ** decision and one which we expect to revisit in the future. But
- ** it seems to be working well enough at the moment.
- */
- pc.rCost = aiRowEst[0]*4;
- pc.plan.wsFlags &= ~WHERE_IDX_ONLY;
- if( pIdx ){
- pc.plan.wsFlags &= ~WHERE_ORDERED;
- pc.plan.nOBSat = nPriorSat;
- }
- }else{
- log10N = estLog(aiRowEst[0]);
- pc.rCost = pc.plan.nRow;
- if( pIdx ){
- if( bLookup ){
- /* For an index lookup followed by a table lookup:
- ** nInMul index searches to find the start of each index range
- ** + nRow steps through the index
- ** + nRow table searches to lookup the table entry using the rowid
- */
- pc.rCost += (nInMul + pc.plan.nRow)*log10N;
- }else{
- /* For a covering index:
- ** nInMul index searches to find the initial entry
- ** + nRow steps through the index
- */
- pc.rCost += nInMul*log10N;
- }
- }else{
- /* For a rowid primary key lookup:
- ** nInMult table searches to find the initial entry for each range
- ** + nRow steps through the table
- */
- pc.rCost += nInMul*log10N;
- }
- }
-
- /* Add in the estimated cost of sorting the result. Actual experimental
- ** measurements of sorting performance in SQLite show that sorting time
- ** adds C*N*log10(N) to the cost, where N is the number of rows to be
- ** sorted and C is a factor between 1.95 and 4.3. We will split the
- ** difference and select C of 3.0.
- */
- if( bSort ){
- double m = estLog(pc.plan.nRow*(nOrderBy - pc.plan.nOBSat)/nOrderBy);
- m *= (double)(pc.plan.nOBSat ? 2 : 3);
- pc.rCost += pc.plan.nRow*m;
- }
- if( bDist ){
- pc.rCost += pc.plan.nRow*estLog(pc.plan.nRow)*3;
- }
-
- /**** Cost of using this index has now been computed ****/
-
- /* If there are additional constraints on this table that cannot
- ** be used with the current index, but which might lower the number
- ** of output rows, adjust the nRow value accordingly. This only
- ** matters if the current index is the least costly, so do not bother
- ** with this step if we already know this index will not be chosen.
- ** Also, never reduce the output row count below 2 using this step.
- **
- ** It is critical that the notValid mask be used here instead of
- ** the notReady mask. When computing an "optimal" index, the notReady
- ** mask will only have one bit set - the bit for the current table.
- ** The notValid mask, on the other hand, always has all bits set for
- ** tables that are not in outer loops. If notReady is used here instead
- ** of notValid, then a optimal index that depends on inner joins loops
- ** might be selected even when there exists an optimal index that has
- ** no such dependency.
- */
- if( pc.plan.nRow>2 && pc.rCost<=p->cost.rCost ){
- int k; /* Loop counter */
- int nSkipEq = pc.plan.nEq; /* Number of == constraints to skip */
- int nSkipRange = nBound; /* Number of < constraints to skip */
- Bitmask thisTab; /* Bitmap for pSrc */
-
- thisTab = getMask(pWC->pMaskSet, iCur);
- for(pTerm=pWC->a, k=pWC->nTerm; pc.plan.nRow>2 && k; k--, pTerm++){
- if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
- if( (pTerm->prereqAll & p->notValid)!=thisTab ) continue;
- if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
- if( nSkipEq ){
- /* Ignore the first pc.plan.nEq equality matches since the index
- ** has already accounted for these */
- nSkipEq--;
- }else{
- /* Assume each additional equality match reduces the result
- ** set size by a factor of 10 */
- pc.plan.nRow /= 10;
- }
- }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
- if( nSkipRange ){
- /* Ignore the first nSkipRange range constraints since the index
- ** has already accounted for these */
- nSkipRange--;
- }else{
- /* Assume each additional range constraint reduces the result
- ** set size by a factor of 3. Indexed range constraints reduce
- ** the search space by a larger factor: 4. We make indexed range
- ** more selective intentionally because of the subjective
- ** observation that indexed range constraints really are more
- ** selective in practice, on average. */
- pc.plan.nRow /= 3;
- }
- }else if( (pTerm->eOperator & WO_NOOP)==0 ){
- /* Any other expression lowers the output row count by half */
- pc.plan.nRow /= 2;
- }
- }
- if( pc.plan.nRow<2 ) pc.plan.nRow = 2;
- }
-
-
- WHERETRACE((
- " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
- " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
- " used=0x%llx nOBSat=%d\n",
- pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
- p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used,
- pc.plan.nOBSat
- ));
-
- /* If this index is the best we have seen so far, then record this
- ** index and its cost in the p->cost structure.
- */
- if( (!pIdx || pc.plan.wsFlags) && compareCost(&pc, &p->cost) ){
- p->cost = pc;
- p->cost.plan.wsFlags &= wsFlagMask;
- p->cost.plan.u.pIdx = pIdx;
- }
-
- /* If there was an INDEXED BY clause, then only that one index is
- ** considered. */
- if( pSrc->pIndex ) break;
-
- /* Reset masks for the next index in the loop */
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
- }
-
- /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag
- ** is set, then reverse the order that the index will be scanned
- ** in. This is used for application testing, to help find cases
- ** where application behavior depends on the (undefined) order that
- ** SQLite outputs rows in in the absence of an ORDER BY clause. */
- if( !p->pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
- p->cost.plan.wsFlags |= WHERE_REVERSE;
- }
-
- assert( p->pOrderBy || (p->cost.plan.wsFlags&WHERE_ORDERED)==0 );
- assert( p->cost.plan.u.pIdx==0 || (p->cost.plan.wsFlags&WHERE_ROWID_EQ)==0 );
- assert( pSrc->pIndex==0
- || p->cost.plan.u.pIdx==0
- || p->cost.plan.u.pIdx==pSrc->pIndex
- );
-
- WHERETRACE((" best index is %s cost=%.1f\n",
- p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
- p->cost.rCost));
-
- bestOrClauseIndex(p);
- bestAutomaticIndex(p);
- p->cost.plan.wsFlags |= eqTermMask;
-}
-
-/*
-** Find the query plan for accessing table pSrc->pTab. Write the
-** best query plan and its cost into the WhereCost object supplied
-** as the last parameter. This function may calculate the cost of
-** both real and virtual table scans.
-**
-** This function does not take ORDER BY or DISTINCT into account. Nor
-** does it remember the virtual table query plan. All it does is compute
-** the cost while determining if an OR optimization is applicable. The
-** details will be reconsidered later if the optimization is found to be
-** applicable.
-*/
-static void bestIndex(WhereBestIdx *p){
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(p->pSrc->pTab) ){
- sqlite3_index_info *pIdxInfo = 0;
- p->ppIdxInfo = &pIdxInfo;
- bestVirtualIndex(p);
- assert( pIdxInfo!=0 || p->pParse->db->mallocFailed );
- if( pIdxInfo && pIdxInfo->needToFreeIdxStr ){
- sqlite3_free(pIdxInfo->idxStr);
- }
- sqlite3DbFree(p->pParse->db, pIdxInfo);
- }else
-#endif
- {
- bestBtreeIndex(p);
- }
-}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** Disable a term in the WHERE clause. Except, do not disable the term
@@ -3731,9 +2412,6 @@ static void bestIndex(WhereBestIdx *p){
** in the ON clause. The term is disabled in (3) because it is not part
** of a LEFT OUTER JOIN. In (1), the term is not disabled.
**
-** IMPLEMENTATION-OF: R-24597-58655 No tests are done for terms that are
-** completely satisfied by indices.
-**
** Disabling a term causes that term to not be tested in the inner loop
** of the join. Disabling is an optimization. When terms are satisfied
** by indices, we disable them to prevent redundant tests in the inner
@@ -3746,6 +2424,7 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
if( pTerm
&& (pTerm->wtFlags & TERM_CODED)==0
&& (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pLevel->notReady & pTerm->prereqAll)==0
){
pTerm->wtFlags |= TERM_CODED;
if( pTerm->iParent>=0 ){
@@ -3813,6 +2492,7 @@ static int codeEqualityTerm(
WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
WhereLevel *pLevel, /* The level of the FROM clause we are working on */
int iEq, /* Index of the equality term within this level */
+ int bRev, /* True for reverse-order IN operations */
int iTarget /* Attempt to leave results in this register */
){
Expr *pX = pTerm->pExpr;
@@ -3830,27 +2510,29 @@ static int codeEqualityTerm(
int eType;
int iTab;
struct InLoop *pIn;
- u8 bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
+ WhereLoop *pLoop = pLevel->pWLoop;
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0
- && pLevel->plan.u.pIdx->aSortOrder[iEq]
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
+ && pLoop->u.btree.pIndex!=0
+ && pLoop->u.btree.pIndex->aSortOrder[iEq]
){
testcase( iEq==0 );
- testcase( iEq==pLevel->plan.u.pIdx->nColumn-1 );
- testcase( iEq>0 && iEq+1<pLevel->plan.u.pIdx->nColumn );
testcase( bRev );
bRev = !bRev;
}
assert( pX->op==TK_IN );
iReg = iTarget;
- eType = sqlite3FindInIndex(pParse, pX, 0);
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0);
if( eType==IN_INDEX_INDEX_DESC ){
testcase( bRev );
bRev = !bRev;
}
iTab = pX->iTable;
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
- assert( pLevel->plan.wsFlags & WHERE_IN_ABLE );
+ VdbeCoverageIf(v, bRev);
+ VdbeCoverageIf(v, !bRev);
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+ pLoop->wsFlags |= WHERE_IN_ABLE;
if( pLevel->u.in.nIn==0 ){
pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
}
@@ -3867,8 +2549,8 @@ static int codeEqualityTerm(
}else{
pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
}
- pIn->eEndLoopOp = bRev ? OP_Prev : OP_Next;
- sqlite3VdbeAddOp1(v, OP_IsNull, iReg);
+ pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
+ sqlite3VdbeAddOp1(v, OP_IsNull, iReg); VdbeCoverage(v);
}else{
pLevel->u.in.nIn = 0;
}
@@ -3880,7 +2562,7 @@ static int codeEqualityTerm(
/*
** Generate code that will evaluate all == and IN constraints for an
-** index.
+** index scan.
**
** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
@@ -3895,9 +2577,15 @@ static int codeEqualityTerm(
** The only thing it does is allocate the pLevel->iMem memory cell and
** compute the affinity string.
**
-** This routine always allocates at least one memory cell and returns
-** the index of that memory cell. The code that
-** calls this routine will use that memory cell to store the termination
+** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints
+** are == or IN and are covered by the nEq. nExtraReg is 1 if there is
+** an inequality constraint (such as the "c>=5 AND c<10" in the example) that
+** occurs after the nEq quality constraints.
+**
+** This routine allocates a range of nEq+nExtraReg memory cells and returns
+** the index of the first memory cell in that range. The code that
+** calls this routine will use that memory range to store keys for
+** start and termination conditions of the loop.
** key value of the loop. If one or more IN operators appear, then
** this routine allocates an additional nEq memory cells for internal
** use.
@@ -3920,29 +2608,33 @@ static int codeEqualityTerm(
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 */
+ int bRev, /* Reverse the order of IN operators */
int nExtraReg, /* Number of extra registers to allocate */
char **pzAff /* OUT: Set to point to affinity string */
){
- int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */
+ u16 nEq; /* The number of == or IN constraints to code */
+ u16 nSkip; /* Number of left-most columns to skip */
Vdbe *v = pParse->pVdbe; /* The vm under construction */
Index *pIdx; /* The index being used for this loop */
- int iCur = pLevel->iTabCur; /* The cursor of the table */
WhereTerm *pTerm; /* A single constraint term */
+ WhereLoop *pLoop; /* The WhereLoop object */
int j; /* Loop counter */
int regBase; /* Base register */
int nReg; /* Number of registers to allocate */
char *zAff; /* Affinity string to return */
/* This module is only called on query plans that use an index. */
- assert( pLevel->plan.wsFlags & WHERE_INDEXED );
- pIdx = pLevel->plan.u.pIdx;
+ pLoop = pLevel->pWLoop;
+ assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ nEq = pLoop->u.btree.nEq;
+ nSkip = pLoop->u.btree.nSkip;
+ pIdx = pLoop->u.btree.pIndex;
+ assert( pIdx!=0 );
/* Figure out how many memory cells we will need then allocate them.
*/
regBase = pParse->nMem + 1;
- nReg = pLevel->plan.nEq + nExtraReg;
+ nReg = pLoop->u.btree.nEq + nExtraReg;
pParse->nMem += nReg;
zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx));
@@ -3950,19 +2642,37 @@ static int codeAllEqualityTerms(
pParse->db->mallocFailed = 1;
}
+ if( nSkip ){
+ int iIdxCur = pLevel->iIdxCur;
+ sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
+ j = sqlite3VdbeAddOp0(v, OP_Goto);
+ pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT),
+ iIdxCur, 0, regBase, nSkip);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ sqlite3VdbeJumpHere(v, j);
+ for(j=0; j<nSkip; j++){
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
+ assert( pIdx->aiColumn[j]>=0 );
+ VdbeComment((v, "%s", pIdx->pTable->aCol[pIdx->aiColumn[j]].zName));
+ }
+ }
+
/* Evaluate the equality constraints
*/
- assert( pIdx->nColumn>=nEq );
- for(j=0; j<nEq; j++){
+ assert( zAff==0 || (int)strlen(zAff)>=nEq );
+ for(j=nSkip; j<nEq; j++){
int r1;
- int k = pIdx->aiColumn[j];
- pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
- if( pTerm==0 ) break;
- /* The following true for indices with redundant columns.
+ pTerm = pLoop->aLTerm[j];
+ assert( pTerm!=0 );
+ /* The following testcase is true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, regBase+j);
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
if( r1!=regBase+j ){
if( nReg==1 ){
sqlite3ReleaseTempReg(pParse, regBase);
@@ -3975,7 +2685,10 @@ static int codeAllEqualityTerms(
testcase( pTerm->eOperator & WO_IN );
if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
Expr *pRight = pTerm->pExpr->pRight;
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+j, pLevel->addrBrk);
+ if( sqlite3ExprCanBeNull(pRight) ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
+ VdbeCoverage(v);
+ }
if( zAff ){
if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){
zAff[j] = SQLITE_AFF_NONE;
@@ -4006,7 +2719,7 @@ static void explainAppendTerm(
const char *zOp /* Name of the operator */
){
if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3StrAccumAppend(pStr, zColumn, -1);
+ sqlite3StrAccumAppendAll(pStr, zColumn);
sqlite3StrAccumAppend(pStr, zOp, 1);
sqlite3StrAccumAppend(pStr, "?", 1);
}
@@ -4030,32 +2743,40 @@ static void explainAppendTerm(
** It is the responsibility of the caller to free the buffer when it is
** no longer required.
*/
-static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
- WherePlan *pPlan = &pLevel->plan;
- Index *pIndex = pPlan->u.pIdx;
- int nEq = pPlan->nEq;
+static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
+ Index *pIndex = pLoop->u.btree.pIndex;
+ u16 nEq = pLoop->u.btree.nEq;
+ u16 nSkip = pLoop->u.btree.nSkip;
int i, j;
Column *aCol = pTab->aCol;
- int *aiColumn = pIndex->aiColumn;
+ i16 *aiColumn = pIndex->aiColumn;
StrAccum txt;
- if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
+ if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
return 0;
}
sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
txt.db = db;
sqlite3StrAccumAppend(&txt, " (", 2);
for(i=0; i<nEq; i++){
- explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "=");
+ char *z = aiColumn[i] < 0 ? "rowid" : aCol[aiColumn[i]].zName;
+ if( i>=nSkip ){
+ explainAppendTerm(&txt, i, z, "=");
+ }else{
+ if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5);
+ sqlite3StrAccumAppend(&txt, "ANY(", 4);
+ sqlite3StrAccumAppendAll(&txt, z);
+ sqlite3StrAccumAppend(&txt, ")", 1);
+ }
}
j = i;
- if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
- char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
+ if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
+ char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i++, z, ">");
}
- if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
- char *z = (j==pIndex->nColumn ) ? "rowid" : aCol[aiColumn[j]].zName;
+ if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
+ char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i, z, "<");
}
sqlite3StrAccumAppend(&txt, ")", 1);
@@ -4076,21 +2797,26 @@ static void explainOneScan(
int iFrom, /* Value for "from" column of output */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
- if( pParse->explain==2 ){
- u32 flags = pLevel->plan.wsFlags;
+#ifndef SQLITE_DEBUG
+ if( pParse->explain==2 )
+#endif
+ {
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
char *zMsg; /* Text to add to EQP output */
- sqlite3_int64 nRow; /* Expected number of rows visited by scan */
int iId = pParse->iSelectId; /* Select id (left-most output column) */
int isSearch; /* True for a SEARCH. False for SCAN. */
+ WhereLoop *pLoop; /* The controlling WhereLoop object */
+ u32 flags; /* Flags that describe this loop */
+ pLoop = pLevel->pWLoop;
+ flags = pLoop->wsFlags;
if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
- isSearch = (pLevel->plan.nEq>0)
- || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
- || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
+ isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
+ || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
+ || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
if( pItem->pSelect ){
@@ -4102,43 +2828,44 @@ static void explainOneScan(
if( pItem->zAlias ){
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
}
- if( (flags & WHERE_INDEXED)!=0 ){
- char *zWhere = explainIndexRange(db, pLevel, pItem->pTab);
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
- ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
- ((flags & WHERE_IDX_ONLY)?"COVERING ":""),
- ((flags & WHERE_TEMP_INDEX)?"":" "),
- ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName),
- zWhere
- );
+ if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0
+ && ALWAYS(pLoop->u.btree.pIndex!=0)
+ ){
+ const char *zFmt;
+ Index *pIdx = pLoop->u.btree.pIndex;
+ char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
+ assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
+ if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
+ zFmt = zWhere ? "%s USING PRIMARY KEY%.0s%s" : "%s%.0s%s";
+ }else if( flags & WHERE_AUTO_INDEX ){
+ zFmt = "%s USING AUTOMATIC COVERING INDEX%.0s%s";
+ }else if( flags & WHERE_IDX_ONLY ){
+ zFmt = "%s USING COVERING INDEX %s%s";
+ }else{
+ zFmt = "%s USING INDEX %s%s";
+ }
+ zMsg = sqlite3MAppendf(db, zMsg, zFmt, zMsg, pIdx->zName, zWhere);
sqlite3DbFree(db, zWhere);
- }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
+ }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
- if( flags&WHERE_ROWID_EQ ){
+ if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
}else if( flags&WHERE_BTM_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
- }else if( flags&WHERE_TOP_LIMIT ){
+ }else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
}
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
- pVtabIdx->idxNum, pVtabIdx->idxStr);
+ pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
- if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){
- testcase( wctrlFlags & WHERE_ORDERBY_MIN );
- nRow = 1;
- }else{
- nRow = (sqlite3_int64)pLevel->plan.nRow;
- }
- zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow);
+ zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg);
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
}
}
@@ -4154,7 +2881,6 @@ static void explainOneScan(
static Bitmask codeOneLoopStart(
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
- u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
Bitmask notReady /* Which tables are currently available */
){
int j, k; /* Loop counters */
@@ -4163,27 +2889,31 @@ static Bitmask codeOneLoopStart(
int omitTable; /* True if we use the index only */
int bRev; /* True if we need to scan in reverse order */
WhereLevel *pLevel; /* The where level to be coded */
+ WhereLoop *pLoop; /* The WhereLoop object being coded */
WhereClause *pWC; /* Decomposition of the entire WHERE clause */
WhereTerm *pTerm; /* A WHERE clause term */
Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* Database connection */
Vdbe *v; /* The prepared stmt under constructions */
struct SrcList_item *pTabItem; /* FROM clause term being coded */
int addrBrk; /* Jump here to break out of the loop */
int addrCont; /* Jump here to continue with next cycle */
int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
int iReleaseReg = 0; /* Temp register to free before returning */
- Bitmask newNotReady; /* Return value */
pParse = pWInfo->pParse;
v = pParse->pVdbe;
- pWC = pWInfo->pWC;
+ pWC = &pWInfo->sWC;
+ db = pParse->db;
pLevel = &pWInfo->a[iLevel];
+ pLoop = pLevel->pWLoop;
pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
iCur = pTabItem->iCursor;
- bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
- omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0
- && (wctrlFlags & WHERE_FORCE_TABLE)==0;
- VdbeNoopComment((v, "Begin Join Loop %d", iLevel));
+ pLevel->notReady = notReady & ~getMask(&pWInfo->sMaskSet, iCur);
+ bRev = (pWInfo->revMask>>iLevel)&1;
+ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
+ && (pWInfo->wctrlFlags & WHERE_FORCE_TABLE)==0;
+ VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
/* Create labels for the "break" and "continue" instructions
** for the current loop. Jump to addrBrk to break out of a loop.
@@ -4211,87 +2941,85 @@ static Bitmask codeOneLoopStart(
/* Special case of a FROM clause subquery implemented as a co-routine */
if( pTabItem->viaCoroutine ){
int regYield = pTabItem->regReturn;
- sqlite3VdbeAddOp2(v, OP_Integer, pTabItem->addrFillSub-1, regYield);
- pLevel->p2 = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
- VdbeComment((v, "next row of co-routine %s", pTabItem->pTab->zName));
- sqlite3VdbeAddOp2(v, OP_If, regYield+1, addrBrk);
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
+ VdbeCoverage(v);
+ VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
pLevel->op = OP_Goto;
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
- /* Case 0: The table is a virtual-table. Use the VFilter and VNext
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ /* Case 1: The table is a virtual-table. Use the VFilter and VNext
** to access the data.
*/
int iReg; /* P3 Value for OP_VFilter */
int addrNotFound;
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
- int nConstraint = pVtabIdx->nConstraint;
- struct sqlite3_index_constraint_usage *aUsage =
- pVtabIdx->aConstraintUsage;
- const struct sqlite3_index_constraint *aConstraint =
- pVtabIdx->aConstraint;
+ int nConstraint = pLoop->nLTerm;
sqlite3ExprCachePush(pParse);
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
addrNotFound = pLevel->addrBrk;
- for(j=1; j<=nConstraint; j++){
- for(k=0; k<nConstraint; k++){
- if( aUsage[k].argvIndex==j ){
- int iTarget = iReg+j+1;
- pTerm = &pWC->a[aConstraint[k].iTermOffset];
- if( pTerm->eOperator & WO_IN ){
- codeEqualityTerm(pParse, pTerm, pLevel, k, iTarget);
- addrNotFound = pLevel->addrNxt;
- }else{
- sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
- }
- break;
- }
+ for(j=0; j<nConstraint; j++){
+ int iTarget = iReg+j+2;
+ pTerm = pLoop->aLTerm[j];
+ if( pTerm==0 ) continue;
+ if( pTerm->eOperator & WO_IN ){
+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
+ addrNotFound = pLevel->addrNxt;
+ }else{
+ sqlite3ExprCode(pParse, pTerm->pExpr->pRight, iTarget);
}
- if( k==nConstraint ) break;
}
- sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
- sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
- sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg, pVtabIdx->idxStr,
- pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
- pVtabIdx->needToFreeIdxStr = 0;
- for(j=0; j<nConstraint; j++){
- if( aUsage[j].omit ){
- int iTerm = aConstraint[j].iTermOffset;
- disableTerm(pLevel, &pWC->a[iTerm]);
+ sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
+ sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
+ pLoop->u.vtab.idxStr,
+ pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
+ VdbeCoverage(v);
+ pLoop->u.vtab.needFree = 0;
+ for(j=0; j<nConstraint && j<16; j++){
+ if( (pLoop->u.vtab.omitMask>>j)&1 ){
+ disableTerm(pLevel, pLoop->aLTerm[j]);
}
}
pLevel->op = OP_VNext;
pLevel->p1 = iCur;
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
}else
#endif /* SQLITE_OMIT_VIRTUALTABLE */
- if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){
- /* Case 1: We can directly reference a single row using an
+ if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0
+ ){
+ /* Case 2: We can directly reference a single row using an
** equality comparison against the ROWID field. Or
** we reference multiple rows using a "rowid IN (...)"
** construct.
*/
- iReleaseReg = sqlite3GetTempReg(pParse);
- pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
+ assert( pLoop->u.btree.nEq==1 );
+ pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->pExpr!=0 );
assert( omitTable==0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, iReleaseReg);
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ iReleaseReg = ++pParse->nMem;
+ iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
+ if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
addrNxt = pLevel->addrNxt;
- sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
+ sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
+ VdbeCoverage(v);
sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
VdbeComment((v, "pk"));
pLevel->op = OP_Noop;
- }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){
- /* Case 2: We have an inequality comparison against the ROWID field.
+ }else if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
+ ){
+ /* Case 3: We have an inequality comparison against the ROWID field.
*/
int testOp = OP_Noop;
int start;
@@ -4299,8 +3027,11 @@ static Bitmask codeOneLoopStart(
WhereTerm *pStart, *pEnd;
assert( omitTable==0 );
- pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0);
- pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0);
+ j = 0;
+ pStart = pEnd = 0;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++];
+ assert( pStart!=0 || pEnd!=0 );
if( bRev ){
pTerm = pStart;
pStart = pEnd;
@@ -4314,34 +3045,42 @@ static Bitmask codeOneLoopStart(
** seek opcodes. It depends on a particular ordering of TK_xx
*/
const u8 aMoveOp[] = {
- /* TK_GT */ OP_SeekGt,
- /* TK_LE */ OP_SeekLe,
- /* TK_LT */ OP_SeekLt,
- /* TK_GE */ OP_SeekGe
+ /* TK_GT */ OP_SeekGT,
+ /* TK_LE */ OP_SeekLE,
+ /* TK_LT */ OP_SeekLT,
+ /* TK_GE */ OP_SeekGE
};
assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
- testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ assert( (pStart->wtFlags & TERM_VNULL)==0 );
+ testcase( pStart->wtFlags & TERM_VIRTUAL );
pX = pStart->pExpr;
assert( pX!=0 );
- assert( pStart->leftCursor==iCur );
+ testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
VdbeComment((v, "pk"));
+ VdbeCoverageIf(v, pX->op==TK_GT);
+ VdbeCoverageIf(v, pX->op==TK_LE);
+ VdbeCoverageIf(v, pX->op==TK_LT);
+ VdbeCoverageIf(v, pX->op==TK_GE);
sqlite3ExprCacheAffinityChange(pParse, r1, 1);
sqlite3ReleaseTempReg(pParse, rTemp);
disableTerm(pLevel, pStart);
}else{
sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
}
if( pEnd ){
Expr *pX;
pX = pEnd->pExpr;
assert( pX!=0 );
- assert( pEnd->leftCursor==iCur );
- testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ assert( (pEnd->wtFlags & TERM_VNULL)==0 );
+ testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
+ testcase( pEnd->wtFlags & TERM_VIRTUAL );
memEndValue = ++pParse->nMem;
sqlite3ExprCode(pParse, pX->pRight, memEndValue);
if( pX->op==TK_LT || pX->op==TK_GT ){
@@ -4355,20 +3094,20 @@ static Bitmask codeOneLoopStart(
pLevel->op = bRev ? OP_Prev : OP_Next;
pLevel->p1 = iCur;
pLevel->p2 = start;
- if( pStart==0 && pEnd==0 ){
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
- }else{
- assert( pLevel->p5==0 );
- }
+ assert( pLevel->p5==0 );
if( testOp!=OP_Noop ){
- iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
+ iRowidReg = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
+ VdbeCoverageIf(v, testOp==OP_Le);
+ VdbeCoverageIf(v, testOp==OP_Lt);
+ VdbeCoverageIf(v, testOp==OP_Ge);
+ VdbeCoverageIf(v, testOp==OP_Gt);
sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
}
- }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
- /* Case 3: A scan using an index.
+ }else if( pLoop->wsFlags & WHERE_INDEXED ){
+ /* Case 4: A scan using an index.
**
** The WHERE clause may contain zero or more equality
** terms ("==" or "IN" operators) that refer to the N
@@ -4404,20 +3143,19 @@ static Bitmask codeOneLoopStart(
0,
OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
OP_Last, /* 3: (!start_constraints && startEq && bRev) */
- OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
- OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
- OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
- OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
+ OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */
+ OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */
+ OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */
+ OP_SeekLE /* 7: (start_constraints && startEq && bRev) */
};
static const u8 aEndOp[] = {
- OP_Noop, /* 0: (!end_constraints) */
- OP_IdxGE, /* 1: (end_constraints && !bRev) */
- OP_IdxLT /* 2: (end_constraints && bRev) */
+ OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */
+ OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */
+ OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */
+ OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
};
- int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
- int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
+ u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
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 <= */
@@ -4429,11 +3167,13 @@ static Bitmask codeOneLoopStart(
int nExtraReg = 0; /* Number of extra registers needed */
int op; /* Instruction opcode */
char *zStartAff; /* Affinity for start of range constraint */
- char *zEndAff; /* Affinity for end of range constraint */
+ char cEndAff = 0; /* Affinity for end of range constraint */
+ u8 bSeekPastNull = 0; /* True to seek past initial nulls */
+ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
- pIdx = pLevel->plan.u.pIdx;
+ pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
- k = (nEq==pIdx->nColumn ? -1 : pIdx->aiColumn[nEq]);
+ assert( nEq>=pLoop->u.btree.nSkip );
/* If this loop satisfies a sort order (pOrderBy) request that
** was passed to this function to implement a "SELECT min(x) ..."
@@ -4443,52 +3183,62 @@ static Bitmask codeOneLoopStart(
** the first one after the nEq equality constraints in the index,
** this requires some special handling.
*/
- if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && (pLevel->plan.wsFlags&WHERE_ORDERED)
- && (pIdx->nColumn>nEq)
+ assert( pWInfo->pOrderBy==0
+ || pWInfo->pOrderBy->nExpr==1
+ || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
+ if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
+ && pWInfo->nOBSat>0
+ && (pIdx->nKeyCol>nEq)
){
- /* assert( pOrderBy->nExpr==1 ); */
- /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
- isMinQuery = 1;
+ assert( pLoop->u.btree.nSkip==0 );
+ bSeekPastNull = 1;
nExtraReg = 1;
}
/* Find any inequality constraint terms for the start and end
** of the range.
*/
- if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){
- pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx);
+ j = nEq;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
+ pRangeStart = pLoop->aLTerm[j++];
nExtraReg = 1;
}
- if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){
- pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
+ pRangeEnd = pLoop->aLTerm[j++];
nExtraReg = 1;
+ if( pRangeStart==0
+ && (j = pIdx->aiColumn[nEq])>=0
+ && pIdx->pTable->aCol[j].notNull==0
+ ){
+ bSeekPastNull = 1;
+ }
}
+ assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
/* Generate code to evaluate all constraint terms using == or IN
** and store the values of those terms in an array of registers
** starting at regBase.
*/
- regBase = codeAllEqualityTerms(
- pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff
- );
- zEndAff = sqlite3DbStrDup(pParse->db, zStartAff);
+ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
+ assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
+ if( zStartAff ) cEndAff = zStartAff[nEq];
addrNxt = pLevel->addrNxt;
/* 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).
*/
- if( (nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
- || (bRev && pIdx->nColumn==nEq)
+ if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
+ || (bRev && pIdx->nKeyCol==nEq)
){
SWAP(WhereTerm *, pRangeEnd, pRangeStart);
+ SWAP(u8, bSeekPastNull, bStopAtNull);
}
- testcase( pRangeStart && pRangeStart->eOperator & WO_LE );
- testcase( pRangeStart && pRangeStart->eOperator & WO_GE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 );
startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
start_constraints = pRangeStart || nEq>0;
@@ -4498,8 +3248,11 @@ static Bitmask codeOneLoopStart(
if( pRangeStart ){
Expr *pRight = pRangeStart->pExpr->pRight;
sqlite3ExprCode(pParse, pRight, regBase+nEq);
- if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
+ if( (pRangeStart->wtFlags & TERM_VNULL)==0
+ && sqlite3ExprCanBeNull(pRight)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
}
if( zStartAff ){
if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){
@@ -4513,23 +3266,24 @@ static Bitmask codeOneLoopStart(
}
}
nConstraint++;
- testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- }else if( isMinQuery ){
+ testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
+ }else if( bSeekPastNull ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
nConstraint++;
startEq = 0;
start_constraints = 1;
}
- codeApplyAffinity(pParse, regBase, nConstraint, zStartAff);
+ codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 );
- testcase( op==OP_Rewind );
- testcase( op==OP_Last );
- testcase( op==OP_SeekGt );
- testcase( op==OP_SeekGe );
- testcase( op==OP_SeekLe );
- testcase( op==OP_SeekLt );
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
/* Load the value for the inequality constraint at the end of the
** range (if any).
@@ -4539,67 +3293,64 @@ static Bitmask codeOneLoopStart(
Expr *pRight = pRangeEnd->pExpr->pRight;
sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
sqlite3ExprCode(pParse, pRight, regBase+nEq);
- if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
+ if( (pRangeEnd->wtFlags & TERM_VNULL)==0
+ && sqlite3ExprCanBeNull(pRight)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
+ }
+ if( sqlite3CompareAffinity(pRight, cEndAff)!=SQLITE_AFF_NONE
+ && !sqlite3ExprNeedsNoAffinityChange(pRight, cEndAff)
+ ){
+ codeApplyAffinity(pParse, regBase+nEq, 1, &cEndAff);
}
- if( zEndAff ){
- if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){
- /* Since the comparison is to be performed with no conversions
- ** applied to the operands, set the affinity to apply to pRight to
- ** SQLITE_AFF_NONE. */
- zEndAff[nEq] = SQLITE_AFF_NONE;
- }
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){
- zEndAff[nEq] = SQLITE_AFF_NONE;
- }
- }
- codeApplyAffinity(pParse, regBase, nEq+1, zEndAff);
nConstraint++;
- testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
+ testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
+ }else if( bStopAtNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ nConstraint++;
}
- sqlite3DbFree(pParse->db, zStartAff);
- sqlite3DbFree(pParse->db, zEndAff);
+ sqlite3DbFree(db, zStartAff);
/* 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 );
- if( op!=OP_Noop ){
+ if( nConstraint ){
+ op = aEndOp[bRev*2 + endEq];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
- sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0);
- }
-
- /* 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.
- */
- r1 = sqlite3GetTempReg(pParse);
- testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT );
- testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT );
- if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
- sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
- sqlite3ReleaseTempReg(pParse, r1);
/* Seek the table cursor, if required */
disableTerm(pLevel, pRangeStart);
disableTerm(pLevel, pRangeEnd);
- if( !omitTable ){
- iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
+ if( omitTable ){
+ /* pIdx is a covering index. No need to access the main table. */
+ }else if( HasRowid(pIdx->pTable) ){
+ iRowidReg = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
+ }else if( iCur!=iIdxCur ){
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
+ }
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
+ iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
}
/* Record the instruction used to terminate the loop. Disable
** WHERE clause terms made redundant by the index range scan.
*/
- if( pLevel->plan.wsFlags & WHERE_UNIQUE ){
+ if( pLoop->wsFlags & WHERE_ONEROW ){
pLevel->op = OP_Noop;
}else if( bRev ){
pLevel->op = OP_Prev;
@@ -4607,7 +3358,8 @@ static Bitmask codeOneLoopStart(
pLevel->op = OP_Next;
}
pLevel->p1 = iIdxCur;
- if( pLevel->plan.wsFlags & WHERE_COVER_SCAN ){
+ pLevel->p3 = (pLoop->wsFlags&WHERE_UNQ_WANTED)!=0 ? 1:0;
+ if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
}else{
assert( pLevel->p5==0 );
@@ -4615,8 +3367,8 @@ static Bitmask codeOneLoopStart(
}else
#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
- /* Case 4: Two or more separately indexed terms connected by OR
+ if( pLoop->wsFlags & WHERE_MULTI_OR ){
+ /* Case 5: Two or more separately indexed terms connected by OR
**
** Example:
**
@@ -4654,6 +3406,10 @@ static Bitmask codeOneLoopStart(
**
** B: <after the loop>
**
+ ** Added 2014-05-26: If the table is a WITHOUT ROWID table, then
+ ** use an ephermeral index instead of a RowSet to record the primary
+ ** keys of the rows we have already seen.
+ **
*/
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
SrcList *pOrTab; /* Shortened table list or OR-clause generation */
@@ -4667,9 +3423,11 @@ static Bitmask codeOneLoopStart(
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
int ii; /* Loop counter */
+ u16 wctrlFlags; /* Flags for sub-WHERE clause */
Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
+ Table *pTab = pTabItem->pTab;
- pTerm = pLevel->plan.u.pTerm;
+ pTerm = pLoop->aLTerm[0];
assert( pTerm!=0 );
assert( pTerm->eOperator & WO_OR );
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
@@ -4685,10 +3443,10 @@ static Bitmask codeOneLoopStart(
int nNotReady; /* The number of notReady tables */
struct SrcList_item *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(pParse->db,
+ pOrTab = sqlite3StackAllocRaw(db,
sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
if( pOrTab==0 ) return notReady;
- pOrTab->nAlloc = (i16)(nNotReady + 1);
+ pOrTab->nAlloc = (u8)(nNotReady + 1);
pOrTab->nSrc = pOrTab->nAlloc;
memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
origSrc = pWInfo->pTabList->a;
@@ -4700,7 +3458,8 @@ static Bitmask codeOneLoopStart(
}
/* Initialize the rowset register to contain NULL. An SQL NULL is
- ** equivalent to an empty rowset.
+ ** equivalent to an empty rowset. Or, create an ephermeral index
+ ** capable of holding primary keys in the case of a WITHOUT ROWID.
**
** Also initialize regReturn to contain the address of the instruction
** immediately following the OP_Return at the bottom of the loop. This
@@ -4710,10 +3469,17 @@ static Bitmask codeOneLoopStart(
** fall through to the next instruction, just as an OP_Next does if
** called on an uninitialized cursor.
*/
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- regRowset = ++pParse->nMem;
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ if( HasRowid(pTab) ){
+ regRowset = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ regRowset = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
regRowid = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
}
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
@@ -4735,46 +3501,102 @@ static Bitmask codeOneLoopStart(
int iTerm;
for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
Expr *pExpr = pWC->a[iTerm].pExpr;
+ if( &pWC->a[iTerm] == pTerm ) continue;
if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
- if( pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_ORINFO) ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
+ testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
+ if( pWC->a[iTerm].wtFlags & (TERM_ORINFO|TERM_VIRTUAL) ) continue;
if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
- pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
- pAndExpr = sqlite3ExprAnd(pParse->db, pAndExpr, pExpr);
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
}
if( pAndExpr ){
pAndExpr = sqlite3PExpr(pParse, TK_AND, 0, pAndExpr, 0);
}
}
+ /* Run a separate WHERE clause for each term of the OR clause. After
+ ** eliminating duplicates from other WHERE clauses, the action for each
+ ** sub-WHERE clause is to to invoke the main loop body as a subroutine.
+ */
+ wctrlFlags = WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
+ WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY;
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
- WhereInfo *pSubWInfo; /* Info for single OR-term scan */
- Expr *pOrExpr = pOrTerm->pExpr;
+ WhereInfo *pSubWInfo; /* Info for single OR-term scan */
+ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
+ int j1 = 0; /* Address of jump operation */
if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
pAndExpr->pLeft = pOrExpr;
pOrExpr = pAndExpr;
}
/* Loop through table entries that match term pOrTerm. */
pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
- WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
- WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY, iCovCur);
- assert( pSubWInfo || pParse->nErr || pParse->db->mallocFailed );
+ wctrlFlags, iCovCur);
+ assert( pSubWInfo || pParse->nErr || db->mallocFailed );
if( pSubWInfo ){
- WhereLevel *pLvl;
+ WhereLoop *pSubLoop;
explainOneScan(
pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
);
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
+ /* This is the sub-WHERE clause body. First skip over
+ ** duplicate rows from prior sub-WHERE clauses, and record the
+ ** rowid (or PRIMARY KEY) for the current row so that the same
+ ** row will be skipped in subsequent sub-WHERE clauses.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
int r;
- r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
- regRowid, 0);
- sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
- sqlite3VdbeCurrentAddr(v)+2, r, iSet);
+ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
+ if( HasRowid(pTab) ){
+ r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
+ j1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0, r,iSet);
+ VdbeCoverage(v);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ int nPk = pPk->nKeyCol;
+ int iPk;
+
+ /* Read the PK into an array of temp registers. */
+ r = sqlite3GetTempRange(pParse, nPk);
+ for(iPk=0; iPk<nPk; iPk++){
+ int iCol = pPk->aiColumn[iPk];
+ sqlite3ExprCodeGetColumn(pParse, pTab, iCol, iCur, r+iPk, 0);
+ }
+
+ /* Check if the temp table already contains this key. If so,
+ ** the row has already been included in the result set and
+ ** can be ignored (by jumping past the Gosub below). Otherwise,
+ ** insert the key into the temp table and proceed with processing
+ ** the row.
+ **
+ ** Use some of the same optimizations as OP_RowSetTest: If iSet
+ ** is zero, assume that the key cannot already be present in
+ ** the temp table. And if iSet is -1, assume that there is no
+ ** need to insert the key into the temp table, as it will never
+ ** be tested for. */
+ if( iSet ){
+ j1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
+ VdbeCoverage(v);
+ }
+ if( iSet>=0 ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
+ if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ }
+
+ /* Release the array of temp registers */
+ sqlite3ReleaseTempRange(pParse, r, nPk);
+ }
}
+
+ /* Invoke the main loop body as a subroutine */
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
+ /* Jump here (skipping the main loop body subroutine) if the
+ ** current sub-WHERE row is a duplicate from prior sub-WHEREs. */
+ if( j1 ) sqlite3VdbeJumpHere(v, j1);
+
/* The pSubWInfo->untestedTerms flag means that this OR term
** contained one or more AND term from a notReady table. The
** terms from the notReady table could not be tested and will
@@ -4794,13 +3616,15 @@ static Bitmask codeOneLoopStart(
** pCov to NULL to indicate that no candidate covering index will
** be available.
*/
- pLvl = &pSubWInfo->a[0];
- if( (pLvl->plan.wsFlags & WHERE_INDEXED)!=0
- && (pLvl->plan.wsFlags & WHERE_TEMP_INDEX)==0
- && (ii==0 || pLvl->plan.u.pIdx==pCov)
+ pSubLoop = pSubWInfo->a[0].pWLoop;
+ assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
+ if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
+ && (ii==0 || pSubLoop->u.btree.pIndex==pCov)
+ && (HasRowid(pTab) || !IsPrimaryKeyIndex(pSubLoop->u.btree.pIndex))
){
- assert( pLvl->iIdxCur==iCovCur );
- pCov = pLvl->plan.u.pIdx;
+ assert( pSubWInfo->a[0].iIdxCur==iCovCur );
+ pCov = pSubLoop->u.btree.pIndex;
+ wctrlFlags |= WHERE_REOPEN_IDX;
}else{
pCov = 0;
}
@@ -4814,45 +3638,47 @@ static Bitmask codeOneLoopStart(
if( pCov ) pLevel->iIdxCur = iCovCur;
if( pAndExpr ){
pAndExpr->pLeft = 0;
- sqlite3ExprDelete(pParse->db, pAndExpr);
+ sqlite3ExprDelete(db, pAndExpr);
}
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
- if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab);
+ if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
if( !untestedTerms ) disableTerm(pLevel, pTerm);
}else
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
{
- /* Case 5: There is no usable index. We must do a complete
+ /* Case 6: There is no usable index. We must do a complete
** scan of the entire table.
*/
static const u8 aStep[] = { OP_Next, OP_Prev };
static const u8 aStart[] = { OP_Rewind, OP_Last };
assert( bRev==0 || bRev==1 );
- assert( omitTable==0 );
- pLevel->op = aStep[bRev];
- pLevel->p1 = iCur;
- pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ if( pTabItem->isRecursive ){
+ /* Tables marked isRecursive have only a single row that is stored in
+ ** a pseudo-cursor. No need to Rewind or Next such cursors. */
+ pLevel->op = OP_Noop;
+ }else{
+ pLevel->op = aStep[bRev];
+ pLevel->p1 = iCur;
+ pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ }
}
- newNotReady = notReady & ~getMask(pWC->pMaskSet, iCur);
/* Insert code to test every subexpression that can be completely
** computed using the current set of tables.
- **
- ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through
- ** the use of indices become tests that are evaluated against each row of
- ** the relevant input tables.
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
Expr *pE;
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & newNotReady)!=0 ){
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
testcase( pWInfo->untestedTerms==0
&& (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
pWInfo->untestedTerms = 1;
@@ -4876,22 +3702,28 @@ static Bitmask codeOneLoopStart(
** the implied "t1.a=123" constraint.
*/
for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
- Expr *pE;
+ Expr *pE, *pEAlt;
WhereTerm *pAlt;
- Expr sEq;
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue;
if( pTerm->leftCursor!=iCur ) continue;
+ if( pLevel->iLeftJoin ) continue;
pE = pTerm->pExpr;
assert( !ExprHasProperty(pE, EP_FromJoin) );
- assert( (pTerm->prereqRight & newNotReady)!=0 );
+ assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0);
if( pAlt==0 ) continue;
if( pAlt->wtFlags & (TERM_CODED) ) continue;
- VdbeNoopComment((v, "begin transitive constraint"));
- sEq = *pAlt->pExpr;
- sEq.pLeft = pE->pLeft;
- sqlite3ExprIfFalse(pParse, &sEq, addrCont, SQLITE_JUMPIFNULL);
+ testcase( pAlt->eOperator & WO_EQ );
+ testcase( pAlt->eOperator & WO_IN );
+ VdbeModuleComment((v, "begin transitive constraint"));
+ pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
+ if( pEAlt ){
+ *pEAlt = *pAlt->pExpr;
+ pEAlt->pLeft = pE->pLeft;
+ sqlite3ExprIfFalse(pParse, pEAlt, addrCont, SQLITE_JUMPIFNULL);
+ sqlite3StackFree(db, pEAlt);
+ }
}
/* For a LEFT OUTER JOIN, generate code that will record the fact that
@@ -4903,10 +3735,10 @@ static Bitmask codeOneLoopStart(
VdbeComment((v, "record LEFT JOIN hit"));
sqlite3ExprCacheClear(pParse);
for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & newNotReady)!=0 ){
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
assert( pWInfo->untestedTerms );
continue;
}
@@ -4915,52 +3747,2125 @@ static Bitmask codeOneLoopStart(
pTerm->wtFlags |= TERM_CODED;
}
}
- sqlite3ReleaseTempReg(pParse, iReleaseReg);
- return newNotReady;
+ return pLevel->notReady;
+}
+
+#if defined(WHERETRACE_ENABLED) && defined(SQLITE_ENABLE_TREE_EXPLAIN)
+/*
+** Generate "Explanation" text for a WhereTerm.
+*/
+static void whereExplainTerm(Vdbe *v, WhereTerm *pTerm){
+ char zType[4];
+ memcpy(zType, "...", 4);
+ if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
+ if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
+ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
+ sqlite3ExplainPrintf(v, "%s ", zType);
+ sqlite3ExplainExpr(v, pTerm->pExpr);
+}
+#endif /* WHERETRACE_ENABLED && SQLITE_ENABLE_TREE_EXPLAIN */
+
+
+#ifdef WHERETRACE_ENABLED
+/*
+** Print a WhereLoop object for debugging purposes
+*/
+static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+7)/8;
+ struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ const char *zName;
+ if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
+ if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
+ int i = sqlite3Strlen30(zName) - 1;
+ while( zName[i]!='_' ) i--;
+ zName += i;
+ }
+ sqlite3DebugPrintf(".%-16s %2d", zName, p->u.btree.nEq);
+ }else{
+ sqlite3DebugPrintf("%20s","");
+ }
+ }else{
+ char *z;
+ if( p->u.vtab.idxStr ){
+ z = sqlite3_mprintf("(%d,\"%s\",%x)",
+ p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask);
+ }else{
+ z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask);
+ }
+ sqlite3DebugPrintf(" %-19s", z);
+ sqlite3_free(z);
+ }
+ sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
+#ifdef SQLITE_ENABLE_TREE_EXPLAIN
+ /* If the 0x100 bit of wheretracing is set, then show all of the constraint
+ ** expressions in the WhereLoop.aLTerm[] array.
+ */
+ if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){ /* WHERETRACE 0x100 */
+ int i;
+ Vdbe *v = pWInfo->pParse->pVdbe;
+ sqlite3ExplainBegin(v);
+ for(i=0; i<p->nLTerm; i++){
+ WhereTerm *pTerm = p->aLTerm[i];
+ if( pTerm==0 ) continue;
+ sqlite3ExplainPrintf(v, " (%d) #%-2d ", i+1, (int)(pTerm-pWC->a));
+ sqlite3ExplainPush(v);
+ whereExplainTerm(v, pTerm);
+ sqlite3ExplainPop(v);
+ sqlite3ExplainNL(v);
+ }
+ sqlite3ExplainFinish(v);
+ sqlite3DebugPrintf("%s", sqlite3VdbeExplanation(v));
+ }
+#endif
+}
+#endif
+
+/*
+** Convert bulk memory into a valid WhereLoop that can be passed
+** to whereLoopClear harmlessly.
+*/
+static void whereLoopInit(WhereLoop *p){
+ p->aLTerm = p->aLTermSpace;
+ p->nLTerm = 0;
+ p->nLSlot = ArraySize(p->aLTermSpace);
+ p->wsFlags = 0;
}
-#if defined(SQLITE_TEST)
/*
-** The following variable holds a text description of query plan generated
-** by the most recent call to sqlite3WhereBegin(). Each call to WhereBegin
-** overwrites the previous. This information is used for testing and
-** analysis only.
+** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact.
*/
-char sqlite3_query_plan[BMS*2*40]; /* Text of the join */
-static int nQPlan = 0; /* Next free slow in _query_plan[] */
+static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
+ if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){
+ sqlite3_free(p->u.vtab.idxStr);
+ p->u.vtab.needFree = 0;
+ p->u.vtab.idxStr = 0;
+ }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
+ sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
+ sqlite3KeyInfoUnref(p->u.btree.pIndex->pKeyInfo);
+ sqlite3DbFree(db, p->u.btree.pIndex);
+ p->u.btree.pIndex = 0;
+ }
+ }
+}
-#endif /* SQLITE_TEST */
+/*
+** Deallocate internal memory used by a WhereLoop object
+*/
+static void whereLoopClear(sqlite3 *db, WhereLoop *p){
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ whereLoopClearUnion(db, p);
+ whereLoopInit(p);
+}
+/*
+** Increase the memory allocation for pLoop->aLTerm[] to be at least n.
+*/
+static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
+ WhereTerm **paNew;
+ if( p->nLSlot>=n ) return SQLITE_OK;
+ n = (n+7)&~7;
+ paNew = sqlite3DbMallocRaw(db, sizeof(p->aLTerm[0])*n);
+ if( paNew==0 ) return SQLITE_NOMEM;
+ memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot);
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ p->aLTerm = paNew;
+ p->nLSlot = n;
+ return SQLITE_OK;
+}
+
+/*
+** Transfer content from the second pLoop into the first.
+*/
+static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
+ whereLoopClearUnion(db, pTo);
+ if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
+ memset(&pTo->u, 0, sizeof(pTo->u));
+ return SQLITE_NOMEM;
+ }
+ memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
+ memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0]));
+ if( pFrom->wsFlags & WHERE_VIRTUALTABLE ){
+ pFrom->u.vtab.needFree = 0;
+ }else if( (pFrom->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ pFrom->u.btree.pIndex = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Delete a WhereLoop object
+*/
+static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
+ whereLoopClear(db, p);
+ sqlite3DbFree(db, p);
+}
/*
** Free a WhereInfo structure
*/
static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
if( ALWAYS(pWInfo) ){
- int i;
- for(i=0; i<pWInfo->nLevel; i++){
- sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
- if( pInfo ){
- /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */
- if( pInfo->needToFreeIdxStr ){
- sqlite3_free(pInfo->idxStr);
+ whereClauseClear(&pWInfo->sWC);
+ while( pWInfo->pLoops ){
+ WhereLoop *p = pWInfo->pLoops;
+ pWInfo->pLoops = p->pNextLoop;
+ whereLoopDelete(db, p);
+ }
+ sqlite3DbFree(db, pWInfo);
+ }
+}
+
+/*
+** Return TRUE if both of the following are true:
+**
+** (1) X has the same or lower cost that Y
+** (2) X is a proper subset of Y
+**
+** By "proper subset" we mean that X uses fewer WHERE clause terms
+** than Y and that every WHERE clause term used by X is also used
+** by Y.
+**
+** If X is a proper subset of Y then Y is a better choice and ought
+** to have a lower cost. This routine returns TRUE when that cost
+** relationship is inverted and needs to be adjusted.
+*/
+static int whereLoopCheaperProperSubset(
+ const WhereLoop *pX, /* First WhereLoop to compare */
+ const WhereLoop *pY /* Compare against this WhereLoop */
+){
+ int i, j;
+ if( pX->nLTerm >= pY->nLTerm ) return 0; /* X is not a subset of Y */
+ if( pX->rRun >= pY->rRun ){
+ if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */
+ if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */
+ }
+ for(i=pX->nLTerm-1; i>=0; i--){
+ for(j=pY->nLTerm-1; j>=0; j--){
+ if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
+ }
+ if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ }
+ return 1; /* All conditions meet */
+}
+
+/*
+** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so
+** that:
+**
+** (1) pTemplate costs less than any other WhereLoops that are a proper
+** subset of pTemplate
+**
+** (2) pTemplate costs more than any other WhereLoops for which pTemplate
+** is a proper subset.
+**
+** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
+** WHERE clause terms than Y and that every WHERE clause term used by X is
+** also used by Y.
+**
+** This adjustment is omitted for SKIPSCAN loops. In a SKIPSCAN loop, the
+** WhereLoop.nLTerm field is not an accurate measure of the number of WHERE
+** clause terms covered, since some of the first nLTerm entries in aLTerm[]
+** will be NULL (because they are skipped). That makes it more difficult
+** to compare the loops. We could add extra code to do the comparison, and
+** perhaps we will someday. But SKIPSCAN is sufficiently uncommon, and this
+** adjustment is sufficient minor, that it is very difficult to construct
+** a test case where the extra code would improve the query plan. Better
+** to avoid the added complexity and just omit cost adjustments to SKIPSCAN
+** loops.
+*/
+static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
+ if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
+ if( (pTemplate->wsFlags & WHERE_SKIPSCAN)!=0 ) return;
+ for(; p; p=p->pNextLoop){
+ if( p->iTab!=pTemplate->iTab ) continue;
+ if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
+ if( (p->wsFlags & WHERE_SKIPSCAN)!=0 ) continue;
+ if( whereLoopCheaperProperSubset(p, pTemplate) ){
+ /* Adjust pTemplate cost downward so that it is cheaper than its
+ ** subset p */
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut - 1;
+ }else if( whereLoopCheaperProperSubset(pTemplate, p) ){
+ /* Adjust pTemplate cost upward so that it is costlier than p since
+ ** pTemplate is a proper subset of p */
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut + 1;
+ }
+ }
+}
+
+/*
+** Search the list of WhereLoops in *ppPrev looking for one that can be
+** supplanted by pTemplate.
+**
+** Return NULL if the WhereLoop list contains an entry that can supplant
+** pTemplate, in other words if pTemplate does not belong on the list.
+**
+** If pX is a WhereLoop that pTemplate can supplant, then return the
+** link that points to pX.
+**
+** If pTemplate cannot supplant any existing element of the list but needs
+** to be added to the list, then return a pointer to the tail of the list.
+*/
+static WhereLoop **whereLoopFindLesser(
+ WhereLoop **ppPrev,
+ const WhereLoop *pTemplate
+){
+ WhereLoop *p;
+ for(p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev){
+ if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){
+ /* If either the iTab or iSortIdx values for two WhereLoop are different
+ ** then those WhereLoops need to be considered separately. Neither is
+ ** a candidate to replace the other. */
+ continue;
+ }
+ /* In the current implementation, the rSetup value is either zero
+ ** or the cost of building an automatic index (NlogN) and the NlogN
+ ** is the same for compatible WhereLoops. */
+ assert( p->rSetup==0 || pTemplate->rSetup==0
+ || p->rSetup==pTemplate->rSetup );
+
+ /* whereLoopAddBtree() always generates and inserts the automatic index
+ ** case first. Hence compatible candidate WhereLoops never have a larger
+ ** rSetup. Call this SETUP-INVARIANT */
+ assert( p->rSetup>=pTemplate->rSetup );
+
+ /* Any loop using an appliation-defined index (or PRIMARY KEY or
+ ** UNIQUE constraint) with one or more == constraints is better
+ ** than an automatic index. */
+ if( (p->wsFlags & WHERE_AUTO_INDEX)!=0
+ && (pTemplate->wsFlags & WHERE_INDEXED)!=0
+ && (pTemplate->wsFlags & WHERE_COLUMN_EQ)!=0
+ && (p->prereq & pTemplate->prereq)==pTemplate->prereq
+ ){
+ break;
+ }
+
+ /* If existing WhereLoop p is better than pTemplate, pTemplate can be
+ ** discarded. WhereLoop p is better if:
+ ** (1) p has no more dependencies than pTemplate, and
+ ** (2) p has an equal or lower cost than pTemplate
+ */
+ if( (p->prereq & pTemplate->prereq)==p->prereq /* (1) */
+ && p->rSetup<=pTemplate->rSetup /* (2a) */
+ && p->rRun<=pTemplate->rRun /* (2b) */
+ && p->nOut<=pTemplate->nOut /* (2c) */
+ ){
+ return 0; /* Discard pTemplate */
+ }
+
+ /* If pTemplate is always better than p, then cause p to be overwritten
+ ** with pTemplate. pTemplate is better than p if:
+ ** (1) pTemplate has no more dependences than p, and
+ ** (2) pTemplate has an equal or lower cost than p.
+ */
+ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */
+ && p->rRun>=pTemplate->rRun /* (2a) */
+ && p->nOut>=pTemplate->nOut /* (2b) */
+ ){
+ assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */
+ break; /* Cause p to be overwritten by pTemplate */
+ }
+ }
+ return ppPrev;
+}
+
+/*
+** Insert or replace a WhereLoop entry using the template supplied.
+**
+** An existing WhereLoop entry might be overwritten if the new template
+** is better and has fewer dependencies. Or the template will be ignored
+** and no insert will occur if an existing WhereLoop is faster and has
+** fewer dependencies than the template. Otherwise a new WhereLoop is
+** added based on the template.
+**
+** If pBuilder->pOrSet is not NULL then we care about only the
+** prerequisites and rRun and nOut costs of the N best loops. That
+** information is gathered in the pBuilder->pOrSet object. This special
+** processing mode is used only for OR clause processing.
+**
+** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we
+** still might overwrite similar loops with the new template if the
+** new template is better. Loops may be overwritten if the following
+** conditions are met:
+**
+** (1) They have the same iTab.
+** (2) They have the same iSortIdx.
+** (3) The template has same or fewer dependencies than the current loop
+** (4) The template has the same or lower cost than the current loop
+*/
+static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
+ WhereLoop **ppPrev, *p;
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ sqlite3 *db = pWInfo->pParse->db;
+
+ /* If pBuilder->pOrSet is defined, then only keep track of the costs
+ ** and prereqs.
+ */
+ if( pBuilder->pOrSet!=0 ){
+#if WHERETRACE_ENABLED
+ u16 n = pBuilder->pOrSet->n;
+ int x =
+#endif
+ whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun,
+ pTemplate->nOut);
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ return SQLITE_OK;
+ }
+
+ /* Look for an existing WhereLoop to replace with pTemplate
+ */
+ whereLoopAdjustCost(pWInfo->pLoops, pTemplate);
+ ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate);
+
+ if( ppPrev==0 ){
+ /* There already exists a WhereLoop on the list that is better
+ ** than pTemplate, so just ignore pTemplate */
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-noop: ");
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ return SQLITE_OK;
+ }else{
+ p = *ppPrev;
+ }
+
+ /* If we reach this point it means that either p[] should be overwritten
+ ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
+ ** WhereLoop and insert it.
+ */
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ if( p!=0 ){
+ sqlite3DebugPrintf("ins-del: ");
+ whereLoopPrint(p, pBuilder->pWC);
+ }
+ sqlite3DebugPrintf("ins-new: ");
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ if( p==0 ){
+ /* Allocate a new WhereLoop to add to the end of the list */
+ *ppPrev = p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
+ if( p==0 ) return SQLITE_NOMEM;
+ whereLoopInit(p);
+ p->pNextLoop = 0;
+ }else{
+ /* We will be overwriting WhereLoop p[]. But before we do, first
+ ** go through the rest of the list and delete any other entries besides
+ ** p[] that are also supplated by pTemplate */
+ WhereLoop **ppTail = &p->pNextLoop;
+ WhereLoop *pToDel;
+ while( *ppTail ){
+ ppTail = whereLoopFindLesser(ppTail, pTemplate);
+ if( ppTail==0 ) break;
+ pToDel = *ppTail;
+ if( pToDel==0 ) break;
+ *ppTail = pToDel->pNextLoop;
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-del: ");
+ whereLoopPrint(pToDel, pBuilder->pWC);
+ }
+#endif
+ whereLoopDelete(db, pToDel);
+ }
+ }
+ whereLoopXfer(db, p, pTemplate);
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ Index *pIndex = p->u.btree.pIndex;
+ if( pIndex && pIndex->tnum==0 ){
+ p->u.btree.pIndex = 0;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Adjust the WhereLoop.nOut value downward to account for terms of the
+** WHERE clause that reference the loop but which are not used by an
+** index.
+**
+** In the current implementation, the first extra WHERE clause term reduces
+** the number of output rows by a factor of 10 and each additional term
+** reduces the number of output rows by sqrt(2).
+*/
+static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){
+ WhereTerm *pTerm, *pX;
+ Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
+ int i, j;
+
+ if( !OptimizationEnabled(pWC->pWInfo->pParse->db, SQLITE_AdjustOutEst) ){
+ return;
+ }
+ for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
+ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
+ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
+ if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
+ for(j=pLoop->nLTerm-1; j>=0; j--){
+ pX = pLoop->aLTerm[j];
+ if( pX==0 ) continue;
+ if( pX==pTerm ) break;
+ if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
+ }
+ if( j<0 ){
+ pLoop->nOut += (pTerm->truthProb<=0 ? pTerm->truthProb : -1);
+ }
+ }
+}
+
+/*
+** Adjust the cost C by the costMult facter T. This only occurs if
+** compiled with -DSQLITE_ENABLE_COSTMULT
+*/
+#ifdef SQLITE_ENABLE_COSTMULT
+# define ApplyCostMultiplier(C,T) C += T
+#else
+# define ApplyCostMultiplier(C,T)
+#endif
+
+/*
+** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
+** index pIndex. Try to match one more.
+**
+** When this function is called, pBuilder->pNew->nOut contains the
+** number of rows expected to be visited by filtering using the nEq
+** terms only. If it is modified, this value is restored before this
+** function returns.
+**
+** If pProbe->tnum==0, that means pIndex is a fake index used for the
+** INTEGER PRIMARY KEY.
+*/
+static int whereLoopAddBtreeIndex(
+ WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
+ struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ Index *pProbe, /* An index on pSrc */
+ LogEst nInMul /* log(Number of iterations due to IN) */
+){
+ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
+ sqlite3 *db = pParse->db; /* Database connection malloc context */
+ WhereLoop *pNew; /* Template WhereLoop under construction */
+ WhereTerm *pTerm; /* A WhereTerm under consideration */
+ int opMask; /* Valid operators for constraints */
+ WhereScan scan; /* Iterator for WHERE terms */
+ Bitmask saved_prereq; /* Original value of pNew->prereq */
+ u16 saved_nLTerm; /* Original value of pNew->nLTerm */
+ u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u16 saved_nSkip; /* Original value of pNew->u.btree.nSkip */
+ u32 saved_wsFlags; /* Original value of pNew->wsFlags */
+ LogEst saved_nOut; /* Original value of pNew->nOut */
+ int iCol; /* Index of the column in the table */
+ int rc = SQLITE_OK; /* Return code */
+ LogEst rLogSize; /* Logarithm of table size */
+ WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
+
+ pNew = pBuilder->pNew;
+ if( db->mallocFailed ) return SQLITE_NOMEM;
+
+ assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
+ if( pNew->wsFlags & WHERE_BTM_LIMIT ){
+ opMask = WO_LT|WO_LE;
+ }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
+ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
+ }else{
+ opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
+ }
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+
+ assert( pNew->u.btree.nEq<pProbe->nColumn );
+ iCol = pProbe->aiColumn[pNew->u.btree.nEq];
+
+ pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, iCol,
+ opMask, pProbe);
+ saved_nEq = pNew->u.btree.nEq;
+ saved_nSkip = pNew->u.btree.nSkip;
+ saved_nLTerm = pNew->nLTerm;
+ saved_wsFlags = pNew->wsFlags;
+ saved_prereq = pNew->prereq;
+ saved_nOut = pNew->nOut;
+ pNew->rSetup = 0;
+ rLogSize = estLog(pProbe->aiRowLogEst[0]);
+
+ /* Consider using a skip-scan if there are no WHERE clause constraints
+ ** available for the left-most terms of the index, and if the average
+ ** number of repeats in the left-most terms is at least 18.
+ **
+ ** The magic number 18 is selected on the basis that scanning 17 rows
+ ** is almost always quicker than an index seek (even though if the index
+ ** contains fewer than 2^17 rows we assume otherwise in other parts of
+ ** the code). And, even if it is not, it should not be too much slower.
+ ** On the other hand, the extra seeks could end up being significantly
+ ** more expensive. */
+ assert( 42==sqlite3LogEst(18) );
+ if( pTerm==0
+ && saved_nEq==saved_nSkip
+ && saved_nEq+1<pProbe->nKeyCol
+ && pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
+ && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
+ ){
+ LogEst nIter;
+ pNew->u.btree.nEq++;
+ pNew->u.btree.nSkip++;
+ pNew->aLTerm[pNew->nLTerm++] = 0;
+ pNew->wsFlags |= WHERE_SKIPSCAN;
+ nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
+ pNew->nOut -= nIter;
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
+ pNew->nOut = saved_nOut;
+ }
+ for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
+ u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */
+ LogEst rCostIdx;
+ LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
+ int nIn = 0;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int nRecValid = pBuilder->nRecValid;
+#endif
+ if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
+ && (iCol<0 || pSrc->pTab->aCol[iCol].notNull)
+ ){
+ continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
+ }
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+
+ pNew->wsFlags = saved_wsFlags;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->nLTerm = saved_nLTerm;
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTerm;
+ pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
+
+ assert( nInMul==0
+ || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
+ || (pNew->wsFlags & WHERE_COLUMN_IN)!=0
+ || (pNew->wsFlags & WHERE_SKIPSCAN)!=0
+ );
+
+ if( eOp & WO_IN ){
+ Expr *pExpr = pTerm->pExpr;
+ pNew->wsFlags |= WHERE_COLUMN_IN;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
+ nIn = 46; assert( 46==sqlite3LogEst(25) );
+ }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
+ /* "x IN (value, value, ...)" */
+ nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
+ }
+ assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
+ ** changes "x IN (?)" into "x=?". */
+
+ }else if( eOp & (WO_EQ) ){
+ pNew->wsFlags |= WHERE_COLUMN_EQ;
+ if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
+ if( iCol>=0 && !IsUniqueIndex(pProbe) ){
+ pNew->wsFlags |= WHERE_UNQ_WANTED;
+ }else{
+ pNew->wsFlags |= WHERE_ONEROW;
}
- sqlite3DbFree(db, pInfo);
}
- if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
- Index *pIdx = pWInfo->a[i].plan.u.pIdx;
- if( pIdx ){
- sqlite3DbFree(db, pIdx->zColAff);
- sqlite3DbFree(db, pIdx);
+ }else if( eOp & WO_ISNULL ){
+ pNew->wsFlags |= WHERE_COLUMN_NULL;
+ }else if( eOp & (WO_GT|WO_GE) ){
+ testcase( eOp & WO_GT );
+ testcase( eOp & WO_GE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pBtm = pTerm;
+ pTop = 0;
+ }else{
+ assert( eOp & (WO_LT|WO_LE) );
+ testcase( eOp & WO_LT );
+ testcase( eOp & WO_LE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aLTerm[pNew->nLTerm-2] : 0;
+ }
+
+ /* At this point pNew->nOut is set to the number of rows expected to
+ ** be visited by the index scan before considering term pTerm, or the
+ ** values of nIn and nInMul. In other words, assuming that all
+ ** "x IN(...)" terms are replaced with "x = ?". This block updates
+ ** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
+ assert( pNew->nOut==saved_nOut );
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ /* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
+ ** data, using some other estimate. */
+ whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
+ }else{
+ int nEq = ++pNew->u.btree.nEq;
+ assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) );
+
+ assert( pNew->nOut==saved_nOut );
+ if( pTerm->truthProb<=0 && iCol>=0 ){
+ assert( (eOp & WO_IN) || nIn==0 );
+ testcase( eOp & WO_IN );
+ pNew->nOut += pTerm->truthProb;
+ pNew->nOut -= nIn;
+ }else{
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ tRowcnt nOut = 0;
+ if( nInMul==0
+ && pProbe->nSample
+ && pNew->u.btree.nEq<=pProbe->nSampleCol
+ && OptimizationEnabled(db, SQLITE_Stat3)
+ && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
+ ){
+ Expr *pExpr = pTerm->pExpr;
+ if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){
+ testcase( eOp & WO_EQ );
+ testcase( eOp & WO_ISNULL );
+ rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
+ }else{
+ rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
+ }
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */
+ if( nOut ){
+ pNew->nOut = sqlite3LogEst(nOut);
+ if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
+ pNew->nOut -= nIn;
+ }
+ }
+ if( nOut==0 )
+#endif
+ {
+ pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]);
+ if( eOp & WO_ISNULL ){
+ /* TUNING: If there is no likelihood() value, assume that a
+ ** "col IS NULL" expression matches twice as many rows
+ ** as (col=?). */
+ pNew->nOut += 10;
+ }
}
}
}
- whereClauseClear(pWInfo->pWC);
- sqlite3DbFree(db, pWInfo);
+
+ /* Set rCostIdx to the cost of visiting selected rows in index. Add
+ ** it to pNew->rRun, which is currently set to the cost of the index
+ ** seek only. Then, if this is a non-covering index, add the cost of
+ ** visiting the rows in the main table. */
+ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
+ }
+ ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult);
+
+ nOutUnadjusted = pNew->nOut;
+ pNew->rRun += nInMul + nIn;
+ pNew->nOut += nInMul + nIn;
+ whereLoopOutputAdjust(pBuilder->pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ pNew->nOut = saved_nOut;
+ }else{
+ pNew->nOut = nOutUnadjusted;
+ }
+
+ if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
+ && pNew->u.btree.nEq<pProbe->nColumn
+ ){
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
+ }
+ pNew->nOut = saved_nOut;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ pBuilder->nRecValid = nRecValid;
+#endif
+ }
+ pNew->prereq = saved_prereq;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nSkip = saved_nSkip;
+ pNew->wsFlags = saved_wsFlags;
+ pNew->nOut = saved_nOut;
+ pNew->nLTerm = saved_nLTerm;
+ return rc;
+}
+
+/*
+** Return True if it is possible that pIndex might be useful in
+** implementing the ORDER BY clause in pBuilder.
+**
+** Return False if pBuilder does not contain an ORDER BY clause or
+** if there is no way for pIndex to be useful in implementing that
+** ORDER BY clause.
+*/
+static int indexMightHelpWithOrderBy(
+ WhereLoopBuilder *pBuilder,
+ Index *pIndex,
+ int iCursor
+){
+ ExprList *pOB;
+ int ii, jj;
+
+ if( pIndex->bUnordered ) return 0;
+ if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
+ for(ii=0; ii<pOB->nExpr; ii++){
+ Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ if( pExpr->op!=TK_COLUMN ) return 0;
+ if( pExpr->iTable==iCursor ){
+ for(jj=0; jj<pIndex->nKeyCol; jj++){
+ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Return a bitmask where 1s indicate that the corresponding column of
+** the table is used by an index. Only the first 63 columns are considered.
+*/
+static Bitmask columnsInIndex(Index *pIdx){
+ Bitmask m = 0;
+ int j;
+ for(j=pIdx->nColumn-1; j>=0; j--){
+ int x = pIdx->aiColumn[j];
+ if( x>=0 ){
+ testcase( x==BMS-1 );
+ testcase( x==BMS-2 );
+ if( x<BMS-1 ) m |= MASKBIT(x);
+ }
+ }
+ return m;
+}
+
+/* Check to see if a partial index with pPartIndexWhere can be used
+** in the current query. Return true if it can be and false if not.
+*/
+static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
+ int i;
+ WhereTerm *pTerm;
+ for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ if( sqlite3ExprImpliesExpr(pTerm->pExpr, pWhere, iTab) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Add all WhereLoop objects for a single table of the join where the table
+** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
+** a b-tree table, not a virtual table.
+**
+** The costs (WhereLoop.rRun) of the b-tree loops added by this function
+** are calculated as follows:
+**
+** For a full scan, assuming the table (or index) contains nRow rows:
+**
+** cost = nRow * 3.0 // full-table scan
+** cost = nRow * K // scan of covering index
+** cost = nRow * (K+3.0) // scan of non-covering index
+**
+** where K is a value between 1.1 and 3.0 set based on the relative
+** estimated average size of the index and table records.
+**
+** For an index scan, where nVisit is the number of index rows visited
+** by the scan, and nSeek is the number of seek operations required on
+** the index b-tree:
+**
+** cost = nSeek * (log(nRow) + K * nVisit) // covering index
+** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index
+**
+** Normally, nSeek is 1. nSeek values greater than 1 come about if the
+** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
+** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans.
+**
+** The estimated values (nRow, nVisit, nSeek) often contain a large amount
+** of uncertainty. For this reason, scoring is designed to pick plans that
+** "do the least harm" if the estimates are inaccurate. For example, a
+** log(nRow) factor is omitted from a non-covering index scan in order to
+** bias the scoring in favor of using an index, since the worst-case
+** performance of using an index is far better than the worst-case performance
+** of a full table scan.
+*/
+static int whereLoopAddBtree(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mExtra /* Extra prerequesites for using this table */
+){
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Index *pProbe; /* An index we are evaluating */
+ Index sPk; /* A fake index object for the primary key */
+ LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
+ i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
+ SrcList *pTabList; /* The FROM clause */
+ struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ WhereLoop *pNew; /* Template WhereLoop object */
+ int rc = SQLITE_OK; /* Return code */
+ int iSortIdx = 1; /* Index number */
+ int b; /* A boolean value */
+ LogEst rSize; /* number of rows in the table */
+ LogEst rLogSize; /* Logarithm of the number of rows in the table */
+ WhereClause *pWC; /* The parsed WHERE clause */
+ Table *pTab; /* Table being queried */
+
+ pNew = pBuilder->pNew;
+ pWInfo = pBuilder->pWInfo;
+ pTabList = pWInfo->pTabList;
+ pSrc = pTabList->a + pNew->iTab;
+ pTab = pSrc->pTab;
+ pWC = pBuilder->pWC;
+ assert( !IsVirtual(pSrc->pTab) );
+
+ if( pSrc->pIndex ){
+ /* An INDEXED BY clause specifies a particular index to use */
+ pProbe = pSrc->pIndex;
+ }else if( !HasRowid(pTab) ){
+ pProbe = pTab->pIndex;
+ }else{
+ /* There is no INDEXED BY clause. Create a fake Index object in local
+ ** variable sPk to represent the rowid primary key index. Make this
+ ** fake index the first in a chain of Index objects with all of the real
+ ** indices to follow */
+ Index *pFirst; /* First of real indices on the table */
+ memset(&sPk, 0, sizeof(Index));
+ sPk.nKeyCol = 1;
+ sPk.nColumn = 1;
+ sPk.aiColumn = &aiColumnPk;
+ sPk.aiRowLogEst = aiRowEstPk;
+ sPk.onError = OE_Replace;
+ sPk.pTable = pTab;
+ sPk.szIdxRow = pTab->szTabRow;
+ aiRowEstPk[0] = pTab->nRowLogEst;
+ aiRowEstPk[1] = 0;
+ pFirst = pSrc->pTab->pIndex;
+ if( pSrc->notIndexed==0 ){
+ /* The real indices of the table are only considered if the
+ ** NOT INDEXED qualifier is omitted from the FROM clause */
+ sPk.pNext = pFirst;
+ }
+ pProbe = &sPk;
+ }
+ rSize = pTab->nRowLogEst;
+ rLogSize = estLog(rSize);
+
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ /* Automatic indexes */
+ if( !pBuilder->pOrSet
+ && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
+ && pSrc->pIndex==0
+ && !pSrc->viaCoroutine
+ && !pSrc->notIndexed
+ && HasRowid(pTab)
+ && !pSrc->isCorrelated
+ && !pSrc->isRecursive
+ ){
+ /* Generate auto-index WhereLoops */
+ WhereTerm *pTerm;
+ WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
+ for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+ if( termCanDriveIndex(pTerm, pSrc, 0) ){
+ pNew->u.btree.nEq = 1;
+ pNew->u.btree.nSkip = 0;
+ pNew->u.btree.pIndex = 0;
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ /* TUNING: One-time cost for computing the automatic index is
+ ** approximately 7*N*log2(N) where N is the number of rows in
+ ** the table being indexed. */
+ pNew->rSetup = rLogSize + rSize + 28; assert( 28==sqlite3LogEst(7) );
+ ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
+ /* TUNING: Each index lookup yields 20 rows in the table. This
+ ** is more than the usual guess of 10 rows, since we have no way
+ ** of knowning how selective the index will ultimately be. It would
+ ** not be unreasonable to make this value much larger. */
+ pNew->nOut = 43; assert( 43==sqlite3LogEst(20) );
+ pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut);
+ pNew->wsFlags = WHERE_AUTO_INDEX;
+ pNew->prereq = mExtra | pTerm->prereqRight;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
+ }
+ }
+#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
+
+ /* Loop over all indices
+ */
+ for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
+ if( pProbe->pPartIdxWhere!=0
+ && !whereUsablePartialIndex(pNew->iTab, pWC, pProbe->pPartIdxWhere) ){
+ continue; /* Partial index inappropriate for this query */
+ }
+ rSize = pProbe->aiRowLogEst[0];
+ pNew->u.btree.nEq = 0;
+ pNew->u.btree.nSkip = 0;
+ pNew->nLTerm = 0;
+ pNew->iSortIdx = 0;
+ pNew->rSetup = 0;
+ pNew->prereq = mExtra;
+ pNew->nOut = rSize;
+ pNew->u.btree.pIndex = pProbe;
+ b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
+ /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */
+ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 );
+ if( pProbe->tnum<=0 ){
+ /* Integer primary key index */
+ pNew->wsFlags = WHERE_IPK;
+
+ /* Full table scan */
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ /* TUNING: Cost of full table scan is (N*3.0). */
+ pNew->rRun = rSize + 16;
+ ApplyCostMultiplier(pNew->rRun, pTab->costMult);
+ whereLoopOutputAdjust(pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }else{
+ Bitmask m;
+ if( pProbe->isCovering ){
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
+ m = 0;
+ }else{
+ m = pSrc->colUsed & ~columnsInIndex(pProbe);
+ pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+ }
+
+ /* Full scan via index */
+ if( b
+ || !HasRowid(pTab)
+ || ( m==0
+ && pProbe->bUnordered==0
+ && (pProbe->szIdxRow<pTab->szTabRow)
+ && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
+ && sqlite3GlobalConfig.bUseCis
+ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan)
+ )
+ ){
+ pNew->iSortIdx = b ? iSortIdx : 0;
+
+ /* The cost of visiting the index rows is N*K, where K is
+ ** between 1.1 and 3.0, depending on the relative sizes of the
+ ** index and table rows. If this is a non-covering index scan,
+ ** also add the cost of visiting table rows (N*3.0). */
+ pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
+ if( m!=0 ){
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, rSize+16);
+ }
+ ApplyCostMultiplier(pNew->rRun, pTab->costMult);
+ whereLoopOutputAdjust(pWC, pNew);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }
+ }
+
+ rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3Stat4ProbeFree(pBuilder->pRec);
+ pBuilder->nRecValid = 0;
+ pBuilder->pRec = 0;
+#endif
+
+ /* If there was an INDEXED BY clause, then only that one index is
+ ** considered. */
+ if( pSrc->pIndex ) break;
+ }
+ return rc;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Add all WhereLoop objects for a table of the join identified by
+** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
+*/
+static int whereLoopAddVirtual(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mExtra
+){
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Parse *pParse; /* The parsing context */
+ WhereClause *pWC; /* The WHERE clause */
+ struct SrcList_item *pSrc; /* The FROM clause term to search */
+ Table *pTab;
+ sqlite3 *db;
+ sqlite3_index_info *pIdxInfo;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage;
+ WhereTerm *pTerm;
+ int i, j;
+ int iTerm, mxTerm;
+ int nConstraint;
+ int seenIn = 0; /* True if an IN operator is seen */
+ int seenVar = 0; /* True if a non-constant constraint is seen */
+ int iPhase; /* 0: const w/o IN, 1: const, 2: no IN, 2: IN */
+ WhereLoop *pNew;
+ int rc = SQLITE_OK;
+
+ pWInfo = pBuilder->pWInfo;
+ pParse = pWInfo->pParse;
+ db = pParse->db;
+ pWC = pBuilder->pWC;
+ pNew = pBuilder->pNew;
+ pSrc = &pWInfo->pTabList->a[pNew->iTab];
+ pTab = pSrc->pTab;
+ assert( IsVirtual(pTab) );
+ pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pBuilder->pOrderBy);
+ if( pIdxInfo==0 ) return SQLITE_NOMEM;
+ pNew->prereq = 0;
+ pNew->rSetup = 0;
+ pNew->wsFlags = WHERE_VIRTUALTABLE;
+ pNew->nLTerm = 0;
+ pNew->u.vtab.needFree = 0;
+ pUsage = pIdxInfo->aConstraintUsage;
+ nConstraint = pIdxInfo->nConstraint;
+ if( whereLoopResize(db, pNew, nConstraint) ){
+ sqlite3DbFree(db, pIdxInfo);
+ return SQLITE_NOMEM;
+ }
+
+ for(iPhase=0; iPhase<=3; iPhase++){
+ if( !seenIn && (iPhase&1)!=0 ){
+ iPhase++;
+ if( iPhase>3 ) break;
+ }
+ if( !seenVar && iPhase>1 ) break;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
+ j = pIdxCons->iTermOffset;
+ pTerm = &pWC->a[j];
+ switch( iPhase ){
+ case 0: /* Constants without IN operator */
+ pIdxCons->usable = 0;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ seenIn = 1;
+ }
+ if( pTerm->prereqRight!=0 ){
+ seenVar = 1;
+ }else if( (pTerm->eOperator & WO_IN)==0 ){
+ pIdxCons->usable = 1;
+ }
+ break;
+ case 1: /* Constants with IN operators */
+ assert( seenIn );
+ pIdxCons->usable = (pTerm->prereqRight==0);
+ break;
+ case 2: /* Variables without IN */
+ assert( seenVar );
+ pIdxCons->usable = (pTerm->eOperator & WO_IN)==0;
+ break;
+ default: /* Variables with IN */
+ assert( seenVar && seenIn );
+ pIdxCons->usable = 1;
+ break;
+ }
+ }
+ memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
+ pIdxInfo->estimatedRows = 25;
+ rc = vtabBestIndex(pParse, pTab, pIdxInfo);
+ if( rc ) goto whereLoopAddVtab_exit;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ pNew->prereq = mExtra;
+ mxTerm = -1;
+ assert( pNew->nLSlot>=nConstraint );
+ for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ j = pIdxCons->iTermOffset;
+ if( iTerm>=nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aLTerm[iTerm]!=0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse, "%s.xBestIndex() malfunction", pTab->zName);
+ goto whereLoopAddVtab_exit;
+ }
+ testcase( iTerm==nConstraint-1 );
+ testcase( j==0 );
+ testcase( j==pWC->nTerm-1 );
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ assert( iTerm<pNew->nLSlot );
+ pNew->aLTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ testcase( iTerm==15 );
+ testcase( iTerm==16 );
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ if( pUsage[i].omit==0 ){
+ /* Do not attempt to use an IN constraint if the virtual table
+ ** says that the equivalent EQ constraint cannot be safely omitted.
+ ** If we do attempt to use such a constraint, some rows might be
+ ** repeated in the output. */
+ break;
+ }
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
+ }
+ }
+ }
+ if( i>=nConstraint ){
+ pNew->nLTerm = mxTerm+1;
+ assert( pNew->nLTerm<=pNew->nLSlot );
+ pNew->u.vtab.idxNum = pIdxInfo->idxNum;
+ pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pNew->u.vtab.idxStr = pIdxInfo->idxStr;
+ pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
+ pIdxInfo->nOrderBy : 0);
+ pNew->rSetup = 0;
+ pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
+ pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
+ whereLoopInsert(pBuilder, pNew);
+ if( pNew->u.vtab.needFree ){
+ sqlite3_free(pNew->u.vtab.idxStr);
+ pNew->u.vtab.needFree = 0;
+ }
+ }
+ }
+
+whereLoopAddVtab_exit:
+ if( pIdxInfo->needToFreeIdxStr ) sqlite3_free(pIdxInfo->idxStr);
+ sqlite3DbFree(db, pIdxInfo);
+ return rc;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/*
+** Add WhereLoop entries to handle OR terms. This works for either
+** btrees or virtual tables.
+*/
+static int whereLoopAddOr(WhereLoopBuilder *pBuilder, Bitmask mExtra){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ WhereClause *pWC;
+ WhereLoop *pNew;
+ WhereTerm *pTerm, *pWCEnd;
+ int rc = SQLITE_OK;
+ int iCur;
+ WhereClause tempWC;
+ WhereLoopBuilder sSubBuild;
+ WhereOrSet sSum, sCur;
+ struct SrcList_item *pItem;
+
+ pWC = pBuilder->pWC;
+ if( pWInfo->wctrlFlags & WHERE_AND_ONLY ) return SQLITE_OK;
+ pWCEnd = pWC->a + pWC->nTerm;
+ pNew = pBuilder->pNew;
+ memset(&sSum, 0, sizeof(sSum));
+ pItem = pWInfo->pTabList->a + pNew->iTab;
+ iCur = pItem->iCursor;
+
+ for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
+ if( (pTerm->eOperator & WO_OR)!=0
+ && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
+ ){
+ WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
+ WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
+ WhereTerm *pOrTerm;
+ int once = 1;
+ int i, j;
+
+ sSubBuild = *pBuilder;
+ sSubBuild.pOrderBy = 0;
+ sSubBuild.pOrSet = &sCur;
+
+ for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
+ if( (pOrTerm->eOperator & WO_AND)!=0 ){
+ sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc;
+ }else if( pOrTerm->leftCursor==iCur ){
+ tempWC.pWInfo = pWC->pWInfo;
+ tempWC.pOuter = pWC;
+ tempWC.op = TK_AND;
+ tempWC.nTerm = 1;
+ tempWC.a = pOrTerm;
+ sSubBuild.pWC = &tempWC;
+ }else{
+ continue;
+ }
+ sCur.n = 0;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pItem->pTab) ){
+ rc = whereLoopAddVirtual(&sSubBuild, mExtra);
+ }else
+#endif
+ {
+ rc = whereLoopAddBtree(&sSubBuild, mExtra);
+ }
+ assert( rc==SQLITE_OK || sCur.n==0 );
+ if( sCur.n==0 ){
+ sSum.n = 0;
+ break;
+ }else if( once ){
+ whereOrMove(&sSum, &sCur);
+ once = 0;
+ }else{
+ WhereOrSet sPrev;
+ whereOrMove(&sPrev, &sSum);
+ sSum.n = 0;
+ for(i=0; i<sPrev.n; i++){
+ for(j=0; j<sCur.n; j++){
+ whereOrInsert(&sSum, sPrev.a[i].prereq | sCur.a[j].prereq,
+ sqlite3LogEstAdd(sPrev.a[i].rRun, sCur.a[j].rRun),
+ sqlite3LogEstAdd(sPrev.a[i].nOut, sCur.a[j].nOut));
+ }
+ }
+ }
+ }
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ pNew->wsFlags = WHERE_MULTI_OR;
+ pNew->rSetup = 0;
+ pNew->iSortIdx = 0;
+ memset(&pNew->u, 0, sizeof(pNew->u));
+ for(i=0; rc==SQLITE_OK && i<sSum.n; i++){
+ /* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs
+ ** of all sub-scans required by the OR-scan. However, due to rounding
+ ** errors, it may be that the cost of the OR-scan is equal to its
+ ** most expensive sub-scan. Add the smallest possible penalty
+ ** (equivalent to multiplying the cost by 1.07) to ensure that
+ ** this does not happen. Otherwise, for WHERE clauses such as the
+ ** following where there is an index on "y":
+ **
+ ** WHERE likelihood(x=?, 0.99) OR y=?
+ **
+ ** the planner may elect to "OR" together a full-table scan and an
+ ** index lookup. And other similarly odd results. */
+ pNew->rRun = sSum.a[i].rRun + 1;
+ pNew->nOut = sSum.a[i].nOut;
+ pNew->prereq = sSum.a[i].prereq;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Add all WhereLoop objects for all tables
+*/
+static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ Bitmask mExtra = 0;
+ Bitmask mPrior = 0;
+ int iTab;
+ SrcList *pTabList = pWInfo->pTabList;
+ struct SrcList_item *pItem;
+ sqlite3 *db = pWInfo->pParse->db;
+ int nTabList = pWInfo->nLevel;
+ int rc = SQLITE_OK;
+ u8 priorJoinType = 0;
+ WhereLoop *pNew;
+
+ /* Loop over the tables in the join, from left to right */
+ pNew = pBuilder->pNew;
+ whereLoopInit(pNew);
+ for(iTab=0, pItem=pTabList->a; iTab<nTabList; iTab++, pItem++){
+ pNew->iTab = iTab;
+ pNew->maskSelf = getMask(&pWInfo->sMaskSet, pItem->iCursor);
+ if( ((pItem->jointype|priorJoinType) & (JT_LEFT|JT_CROSS))!=0 ){
+ mExtra = mPrior;
+ }
+ priorJoinType = pItem->jointype;
+ if( IsVirtual(pItem->pTab) ){
+ rc = whereLoopAddVirtual(pBuilder, mExtra);
+ }else{
+ rc = whereLoopAddBtree(pBuilder, mExtra);
+ }
+ if( rc==SQLITE_OK ){
+ rc = whereLoopAddOr(pBuilder, mExtra);
+ }
+ mPrior |= pNew->maskSelf;
+ if( rc || db->mallocFailed ) break;
+ }
+ whereLoopClear(db, pNew);
+ return rc;
+}
+
+/*
+** Examine a WherePath (with the addition of the extra WhereLoop of the 5th
+** parameters) to see if it outputs rows in the requested ORDER BY
+** (or GROUP BY) without requiring a separate sort operation. Return N:
+**
+** N>0: N terms of the ORDER BY clause are satisfied
+** N==0: No terms of the ORDER BY clause are satisfied
+** N<0: Unknown yet how many terms of ORDER BY might be satisfied.
+**
+** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
+** strict. With GROUP BY and DISTINCT the only requirement is that
+** equivalent rows appear immediately adjacent to one another. GROUP BY
+** and DISTINCT do not require rows to appear in any particular order as long
+** as equivelent rows are grouped together. Thus for GROUP BY and DISTINCT
+** the pOrderBy terms can be matched in any order. With ORDER BY, the
+** pOrderBy terms must be matched in strict left-to-right order.
+*/
+static i8 wherePathSatisfiesOrderBy(
+ WhereInfo *pWInfo, /* The WHERE clause */
+ ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
+ WherePath *pPath, /* The WherePath to check */
+ u16 wctrlFlags, /* Might contain WHERE_GROUPBY or WHERE_DISTINCTBY */
+ u16 nLoop, /* Number of entries in pPath->aLoop[] */
+ WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
+ Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */
+){
+ u8 revSet; /* True if rev is known */
+ u8 rev; /* Composite sort order */
+ u8 revIdx; /* Index sort order */
+ u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
+ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
+ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
+ u16 nKeyCol; /* Number of key columns in pIndex */
+ u16 nColumn; /* Total number of ordered columns in the index */
+ u16 nOrderBy; /* Number terms in the ORDER BY clause */
+ int iLoop; /* Index of WhereLoop in pPath being processed */
+ int i, j; /* Loop counters */
+ int iCur; /* Cursor number for current WhereLoop */
+ int iColumn; /* A column number within table iCur */
+ WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */
+ WhereTerm *pTerm; /* A single term of the WHERE clause */
+ Expr *pOBExpr; /* An expression from the ORDER BY clause */
+ CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */
+ Index *pIndex; /* The index associated with pLoop */
+ sqlite3 *db = pWInfo->pParse->db; /* Database connection */
+ Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */
+ Bitmask obDone; /* Mask of all ORDER BY terms */
+ Bitmask orderDistinctMask; /* Mask of all well-ordered loops */
+ Bitmask ready; /* Mask of inner loops */
+
+ /*
+ ** We say the WhereLoop is "one-row" if it generates no more than one
+ ** row of output. A WhereLoop is one-row if all of the following are true:
+ ** (a) All index columns match with WHERE_COLUMN_EQ.
+ ** (b) The index is unique
+ ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row.
+ ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags.
+ **
+ ** We say the WhereLoop is "order-distinct" if the set of columns from
+ ** that WhereLoop that are in the ORDER BY clause are different for every
+ ** row of the WhereLoop. Every one-row WhereLoop is automatically
+ ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause
+ ** is not order-distinct. To be order-distinct is not quite the same as being
+ ** UNIQUE since a UNIQUE column or index can have multiple rows that
+ ** are NULL and NULL values are equivalent for the purpose of order-distinct.
+ ** To be order-distinct, the columns must be UNIQUE and NOT NULL.
+ **
+ ** The rowid for a table is always UNIQUE and NOT NULL so whenever the
+ ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is
+ ** automatically order-distinct.
+ */
+
+ assert( pOrderBy!=0 );
+ if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
+
+ nOrderBy = pOrderBy->nExpr;
+ testcase( nOrderBy==BMS-1 );
+ if( nOrderBy>BMS-1 ) return 0; /* Cannot optimize overly large ORDER BYs */
+ isOrderDistinct = 1;
+ obDone = MASKBIT(nOrderBy)-1;
+ orderDistinctMask = 0;
+ ready = 0;
+ for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
+ if( iLoop>0 ) ready |= pLoop->maskSelf;
+ pLoop = iLoop<nLoop ? pPath->aLoop[iLoop] : pLast;
+ if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
+ if( pLoop->u.vtab.isOrdered ) obSat = obDone;
+ break;
+ }
+ iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
+
+ /* Mark off any ORDER BY term X that is a column in the table of
+ ** the current loop for which there is term in the WHERE
+ ** clause of the form X IS NULL or X=? that reference only outer
+ ** loops.
+ */
+ for(i=0; i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
+ ~ready, WO_EQ|WO_ISNULL, 0);
+ if( pTerm==0 ) continue;
+ if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){
+ const char *z1, *z2;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z1 = pColl->zName;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pTerm->pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z2 = pColl->zName;
+ if( sqlite3StrICmp(z1, z2)!=0 ) continue;
+ }
+ obSat |= MASKBIT(i);
+ }
+
+ if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){
+ if( pLoop->wsFlags & WHERE_IPK ){
+ pIndex = 0;
+ nKeyCol = 0;
+ nColumn = 1;
+ }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){
+ return 0;
+ }else{
+ nKeyCol = pIndex->nKeyCol;
+ nColumn = pIndex->nColumn;
+ assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
+ assert( pIndex->aiColumn[nColumn-1]==(-1) || !HasRowid(pIndex->pTable));
+ isOrderDistinct = IsUniqueIndex(pIndex);
+ }
+
+ /* Loop through all columns of the index and deal with the ones
+ ** that are not constrained by == or IN.
+ */
+ rev = revSet = 0;
+ distinctColumns = 0;
+ for(j=0; j<nColumn; j++){
+ u8 bOnce; /* True to run the ORDER BY search loop */
+
+ /* Skip over == and IS NULL terms */
+ if( j<pLoop->u.btree.nEq
+ && pLoop->u.btree.nSkip==0
+ && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
+ ){
+ if( i & WO_ISNULL ){
+ testcase( isOrderDistinct );
+ isOrderDistinct = 0;
+ }
+ continue;
+ }
+
+ /* Get the column number in the table (iColumn) and sort order
+ ** (revIdx) for the j-th column of the index.
+ */
+ if( pIndex ){
+ iColumn = pIndex->aiColumn[j];
+ revIdx = pIndex->aSortOrder[j];
+ if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
+ }else{
+ iColumn = -1;
+ revIdx = 0;
+ }
+
+ /* An unconstrained column that might be NULL means that this
+ ** WhereLoop is not well-ordered
+ */
+ if( isOrderDistinct
+ && iColumn>=0
+ && j>=pLoop->u.btree.nEq
+ && pIndex->pTable->aCol[iColumn].notNull==0
+ ){
+ isOrderDistinct = 0;
+ }
+
+ /* Find the ORDER BY term that corresponds to the j-th column
+ ** of the index and mark that ORDER BY term off
+ */
+ bOnce = 1;
+ isMatch = 0;
+ for(i=0; bOnce && i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ testcase( wctrlFlags & WHERE_GROUPBY );
+ testcase( wctrlFlags & WHERE_DISTINCTBY );
+ if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ if( pOBExpr->iColumn!=iColumn ) continue;
+ if( iColumn>=0 ){
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
+ }
+ isMatch = 1;
+ break;
+ }
+ if( isMatch && (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){
+ /* Make sure the sort order is compatible in an ORDER BY clause.
+ ** Sort order is irrelevant for a GROUP BY clause. */
+ if( revSet ){
+ if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
+ }else{
+ rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ if( rev ) *pRevMask |= MASKBIT(iLoop);
+ revSet = 1;
+ }
+ }
+ if( isMatch ){
+ if( iColumn<0 ){
+ testcase( distinctColumns==0 );
+ distinctColumns = 1;
+ }
+ obSat |= MASKBIT(i);
+ }else{
+ /* No match found */
+ if( j==0 || j<nKeyCol ){
+ testcase( isOrderDistinct!=0 );
+ isOrderDistinct = 0;
+ }
+ break;
+ }
+ } /* end Loop over all index columns */
+ if( distinctColumns ){
+ testcase( isOrderDistinct==0 );
+ isOrderDistinct = 1;
+ }
+ } /* end-if not one-row */
+
+ /* Mark off any other ORDER BY terms that reference pLoop */
+ if( isOrderDistinct ){
+ orderDistinctMask |= pLoop->maskSelf;
+ for(i=0; i<nOrderBy; i++){
+ Expr *p;
+ Bitmask mTerm;
+ if( MASKBIT(i) & obSat ) continue;
+ p = pOrderBy->a[i].pExpr;
+ mTerm = exprTableUsage(&pWInfo->sMaskSet,p);
+ if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
+ if( (mTerm&~orderDistinctMask)==0 ){
+ obSat |= MASKBIT(i);
+ }
+ }
+ }
+ } /* End the loop over all WhereLoops from outer-most down to inner-most */
+ if( obSat==obDone ) return (i8)nOrderBy;
+ if( !isOrderDistinct ){
+ for(i=nOrderBy-1; i>0; i--){
+ Bitmask m = MASKBIT(i) - 1;
+ if( (obSat&m)==m ) return i;
+ }
+ return 0;
}
+ return -1;
+}
+
+
+/*
+** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(),
+** the planner assumes that the specified pOrderBy list is actually a GROUP
+** BY clause - and so any order that groups rows as required satisfies the
+** request.
+**
+** Normally, in this case it is not possible for the caller to determine
+** whether or not the rows are really being delivered in sorted order, or
+** just in some other order that provides the required grouping. However,
+** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then
+** this function may be called on the returned WhereInfo object. It returns
+** true if the rows really will be sorted in the specified order, or false
+** otherwise.
+**
+** For example, assuming:
+**
+** CREATE INDEX i1 ON t1(x, Y);
+**
+** then
+**
+** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1
+** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0
+*/
+int sqlite3WhereIsSorted(WhereInfo *pWInfo){
+ assert( pWInfo->wctrlFlags & WHERE_GROUPBY );
+ assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP );
+ return pWInfo->sorted;
}
+#ifdef WHERETRACE_ENABLED
+/* For debugging use only: */
+static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
+ static char zName[65];
+ int i;
+ for(i=0; i<nLoop; i++){ zName[i] = pPath->aLoop[i]->cId; }
+ if( pLast ) zName[i++] = pLast->cId;
+ zName[i] = 0;
+ return zName;
+}
+#endif
+
+/*
+** Return the cost of sorting nRow rows, assuming that the keys have
+** nOrderby columns and that the first nSorted columns are already in
+** order.
+*/
+static LogEst whereSortingCost(
+ WhereInfo *pWInfo,
+ LogEst nRow,
+ int nOrderBy,
+ int nSorted
+){
+ /* TUNING: Estimated cost of a full external sort, where N is
+ ** the number of rows to sort is:
+ **
+ ** cost = (3.0 * N * log(N)).
+ **
+ ** Or, if the order-by clause has X terms but only the last Y
+ ** terms are out of order, then block-sorting will reduce the
+ ** sorting cost to:
+ **
+ ** cost = (3.0 * N * log(N)) * (Y/X)
+ **
+ ** The (Y/X) term is implemented using stack variable rScale
+ ** below. */
+ LogEst rScale, rSortCost;
+ assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
+ rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
+ rSortCost = nRow + estLog(nRow) + rScale + 16;
+
+ /* TUNING: The cost of implementing DISTINCT using a B-TREE is
+ ** similar but with a larger constant of proportionality.
+ ** Multiply by an additional factor of 3.0. */
+ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
+ rSortCost += 16;
+ }
+
+ return rSortCost;
+}
+
+/*
+** Given the list of WhereLoop objects at pWInfo->pLoops, this routine
+** attempts to find the lowest cost path that visits each WhereLoop
+** once. This path is then loaded into the pWInfo->a[].pWLoop fields.
+**
+** Assume that the total number of output rows that will need to be sorted
+** will be nRowEst (in the 10*log2 representation). Or, ignore sorting
+** costs if nRowEst==0.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation
+** error occurs.
+*/
+static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
+ int mxChoice; /* Maximum number of simultaneous paths tracked */
+ int nLoop; /* Number of terms in the join */
+ Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* The database connection */
+ int iLoop; /* Loop counter over the terms of the join */
+ int ii, jj; /* Loop counters */
+ int mxI = 0; /* Index of next entry to replace */
+ int nOrderBy; /* Number of ORDER BY clause terms */
+ LogEst mxCost = 0; /* Maximum cost of a set of paths */
+ LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */
+ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
+ WherePath *aFrom; /* All nFrom paths at the previous level */
+ WherePath *aTo; /* The nTo best paths at the current level */
+ WherePath *pFrom; /* An element of aFrom[] that we are working on */
+ WherePath *pTo; /* An element of aTo[] that we are working on */
+ WhereLoop *pWLoop; /* One of the WhereLoop objects */
+ WhereLoop **pX; /* Used to divy up the pSpace memory */
+ LogEst *aSortCost = 0; /* Sorting and partial sorting costs */
+ char *pSpace; /* Temporary memory used by this routine */
+ int nSpace; /* Bytes of space allocated at pSpace */
+
+ pParse = pWInfo->pParse;
+ db = pParse->db;
+ nLoop = pWInfo->nLevel;
+ /* TUNING: For simple queries, only the best path is tracked.
+ ** For 2-way joins, the 5 best paths are followed.
+ ** For joins of 3 or more tables, track the 10 best paths */
+ mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10);
+ assert( nLoop<=pWInfo->pTabList->nSrc );
+ WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst));
+
+ /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this
+ ** case the purpose of this call is to estimate the number of rows returned
+ ** by the overall query. Once this estimate has been obtained, the caller
+ ** will invoke this function a second time, passing the estimate as the
+ ** nRowEst parameter. */
+ if( pWInfo->pOrderBy==0 || nRowEst==0 ){
+ nOrderBy = 0;
+ }else{
+ nOrderBy = pWInfo->pOrderBy->nExpr;
+ }
+
+ /* Allocate and initialize space for aTo, aFrom and aSortCost[] */
+ nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
+ nSpace += sizeof(LogEst) * nOrderBy;
+ pSpace = sqlite3DbMallocRaw(db, nSpace);
+ if( pSpace==0 ) return SQLITE_NOMEM;
+ aTo = (WherePath*)pSpace;
+ aFrom = aTo+mxChoice;
+ memset(aFrom, 0, sizeof(aFrom[0]));
+ pX = (WhereLoop**)(aFrom+mxChoice);
+ for(ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop){
+ pFrom->aLoop = pX;
+ }
+ if( nOrderBy ){
+ /* If there is an ORDER BY clause and it is not being ignored, set up
+ ** space for the aSortCost[] array. Each element of the aSortCost array
+ ** is either zero - meaning it has not yet been initialized - or the
+ ** cost of sorting nRowEst rows of data where the first X terms of
+ ** the ORDER BY clause are already in order, where X is the array
+ ** index. */
+ aSortCost = (LogEst*)pX;
+ memset(aSortCost, 0, sizeof(LogEst) * nOrderBy);
+ }
+ assert( aSortCost==0 || &pSpace[nSpace]==(char*)&aSortCost[nOrderBy] );
+ assert( aSortCost!=0 || &pSpace[nSpace]==(char*)pX );
+
+ /* Seed the search with a single WherePath containing zero WhereLoops.
+ **
+ ** TUNING: Do not let the number of iterations go above 25. If the cost
+ ** of computing an automatic index is not paid back within the first 25
+ ** rows, then do not use the automatic index. */
+ aFrom[0].nRow = MIN(pParse->nQueryLoop, 46); assert( 46==sqlite3LogEst(25) );
+ nFrom = 1;
+ assert( aFrom[0].isOrdered==0 );
+ if( nOrderBy ){
+ /* If nLoop is zero, then there are no FROM terms in the query. Since
+ ** in this case the query may return a maximum of one row, the results
+ ** are already in the requested order. Set isOrdered to nOrderBy to
+ ** indicate this. Or, if nLoop is greater than zero, set isOrdered to
+ ** -1, indicating that the result set may or may not be ordered,
+ ** depending on the loops added to the current plan. */
+ aFrom[0].isOrdered = nLoop>0 ? -1 : nOrderBy;
+ }
+
+ /* Compute successively longer WherePaths using the previous generation
+ ** of WherePaths as the basis for the next. Keep track of the mxChoice
+ ** best paths at each generation */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ nTo = 0;
+ for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
+ LogEst nOut; /* Rows visited by (pFrom+pWLoop) */
+ LogEst rCost; /* Cost of path (pFrom+pWLoop) */
+ LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */
+ i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */
+ Bitmask maskNew; /* Mask of src visited by (..) */
+ Bitmask revMask = 0; /* Mask of rev-order loops for (..) */
+
+ if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
+ if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
+ /* At this point, pWLoop is a candidate to be the next loop.
+ ** Compute its cost */
+ rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
+ rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted);
+ nOut = pFrom->nRow + pWLoop->nOut;
+ maskNew = pFrom->maskLoop | pWLoop->maskSelf;
+ if( isOrdered<0 ){
+ isOrdered = wherePathSatisfiesOrderBy(pWInfo,
+ pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
+ iLoop, pWLoop, &revMask);
+ }else{
+ revMask = pFrom->revLoop;
+ }
+ if( isOrdered>=0 && isOrdered<nOrderBy ){
+ if( aSortCost[isOrdered]==0 ){
+ aSortCost[isOrdered] = whereSortingCost(
+ pWInfo, nRowEst, nOrderBy, isOrdered
+ );
+ }
+ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]);
+
+ WHERETRACE(0x002,
+ ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
+ aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy,
+ rUnsorted, rCost));
+ }else{
+ rCost = rUnsorted;
+ }
+
+ /* Check to see if pWLoop should be added to the set of
+ ** mxChoice best-so-far paths.
+ **
+ ** First look for an existing path among best-so-far paths
+ ** that covers the same set of loops and has the same isOrdered
+ ** setting as the current path candidate.
+ **
+ ** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
+ ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
+ ** of legal values for isOrdered, -1..64.
+ */
+ for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
+ if( pTo->maskLoop==maskNew
+ && ((pTo->isOrdered^isOrdered)&0x80)==0
+ ){
+ testcase( jj==nTo-1 );
+ break;
+ }
+ }
+ if( jj>=nTo ){
+ /* None of the existing best-so-far paths match the candidate. */
+ if( nTo>=mxChoice
+ && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted))
+ ){
+ /* The current candidate is no better than any of the mxChoice
+ ** paths currently in the best-so-far buffer. So discard
+ ** this candidate as not viable. */
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ }
+#endif
+ continue;
+ }
+ /* If we reach this points it means that the new candidate path
+ ** needs to be added to the set of best-so-far paths. */
+ if( nTo<mxChoice ){
+ /* Increase the size of the aTo set by one */
+ jj = nTo++;
+ }else{
+ /* New path replaces the prior worst to keep count below mxChoice */
+ jj = mxI;
+ }
+ pTo = &aTo[jj];
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ }
+#endif
+ }else{
+ /* Control reaches here if best-so-far path pTo=aTo[jj] covers the
+ ** same set of loops and has the sam isOrdered setting as the
+ ** candidate path. Check to see if the candidate should replace
+ ** pTo or if the candidate should be skipped */
+ if( pTo->rCost<rCost || (pTo->rCost==rCost && pTo->nRow<=nOut) ){
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Skip %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ sqlite3DebugPrintf(" vs %s cost=%-3d,%d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ }
+#endif
+ /* Discard the candidate path from further consideration */
+ testcase( pTo->rCost==rCost );
+ continue;
+ }
+ testcase( pTo->rCost==rCost+1 );
+ /* Control reaches here if the candidate path is better than the
+ ** pTo path. Replace pTo with the candidate. */
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Update %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ sqlite3DebugPrintf(" was %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ }
+#endif
+ }
+ /* pWLoop is a winner. Add it to the set of best so far */
+ pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
+ pTo->revLoop = revMask;
+ pTo->nRow = nOut;
+ pTo->rCost = rCost;
+ pTo->rUnsorted = rUnsorted;
+ pTo->isOrdered = isOrdered;
+ memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
+ pTo->aLoop[iLoop] = pWLoop;
+ if( nTo>=mxChoice ){
+ mxI = 0;
+ mxCost = aTo[0].rCost;
+ mxUnsorted = aTo[0].nRow;
+ for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
+ if( pTo->rCost>mxCost
+ || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted)
+ ){
+ mxCost = pTo->rCost;
+ mxUnsorted = pTo->rUnsorted;
+ mxI = jj;
+ }
+ }
+ }
+ }
+ }
+
+#ifdef WHERETRACE_ENABLED /* >=2 */
+ if( sqlite3WhereTrace>=2 ){
+ sqlite3DebugPrintf("---- after round %d ----\n", iLoop);
+ for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){
+ sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?');
+ if( pTo->isOrdered>0 ){
+ sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop);
+ }else{
+ sqlite3DebugPrintf("\n");
+ }
+ }
+ }
+#endif
+
+ /* Swap the roles of aFrom and aTo for the next generation */
+ pFrom = aTo;
+ aTo = aFrom;
+ aFrom = pFrom;
+ nFrom = nTo;
+ }
+
+ if( nFrom==0 ){
+ sqlite3ErrorMsg(pParse, "no query solution");
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_ERROR;
+ }
+
+ /* Find the lowest cost path. pFrom will be left pointing to that path */
+ pFrom = aFrom;
+ for(ii=1; ii<nFrom; ii++){
+ if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
+ }
+ assert( pWInfo->nLevel==nLoop );
+ /* Load the lowest cost path into pWInfo */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ WhereLevel *pLevel = pWInfo->a + iLoop;
+ pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
+ pLevel->iFrom = pWLoop->iTab;
+ pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor;
+ }
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0
+ && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0
+ && pWInfo->eDistinct==WHERE_DISTINCT_NOOP
+ && nRowEst
+ ){
+ Bitmask notUsed;
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom,
+ WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed);
+ if( rc==pWInfo->pResultSet->nExpr ){
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ }
+ if( pWInfo->pOrderBy ){
+ if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
+ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ }else{
+ pWInfo->nOBSat = pFrom->isOrdered;
+ if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0;
+ pWInfo->revMask = pFrom->revLoop;
+ }
+ if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
+ && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr
+ ){
+ Bitmask notUsed = 0;
+ int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
+ pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed
+ );
+ assert( pWInfo->sorted==0 );
+ pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr);
+ }
+ }
+
+
+ pWInfo->nRowOut = pFrom->nRow;
+
+ /* Free temporary memory and return success */
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_OK;
+}
+
+/*
+** Most queries use only a single table (they are not joins) and have
+** simple == constraints against indexed fields. This routine attempts
+** to plan those simple cases using much less ceremony than the
+** general-purpose query planner, and thereby yield faster sqlite3_prepare()
+** times for the common case.
+**
+** Return non-zero on success, if this query can be handled by this
+** no-frills query planner. Return zero if this query needs the
+** general-purpose query planner.
+*/
+static int whereShortCut(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo;
+ struct SrcList_item *pItem;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ WhereLoop *pLoop;
+ int iCur;
+ int j;
+ Table *pTab;
+ Index *pIdx;
+
+ pWInfo = pBuilder->pWInfo;
+ if( pWInfo->wctrlFlags & WHERE_FORCE_TABLE ) return 0;
+ assert( pWInfo->pTabList->nSrc>=1 );
+ pItem = pWInfo->pTabList->a;
+ pTab = pItem->pTab;
+ if( IsVirtual(pTab) ) return 0;
+ if( pItem->zIndex ) return 0;
+ iCur = pItem->iCursor;
+ pWC = &pWInfo->sWC;
+ pLoop = pBuilder->pNew;
+ pLoop->wsFlags = 0;
+ pLoop->u.btree.nSkip = 0;
+ pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0);
+ if( pTerm ){
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
+ pLoop->aLTerm[0] = pTerm;
+ pLoop->nLTerm = 1;
+ pLoop->u.btree.nEq = 1;
+ /* TUNING: Cost of a rowid lookup is 10 */
+ pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
+ }else{
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ assert( pLoop->aLTermSpace==pLoop->aLTerm );
+ assert( ArraySize(pLoop->aLTermSpace)==4 );
+ if( !IsUniqueIndex(pIdx)
+ || pIdx->pPartIdxWhere!=0
+ || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
+ ) continue;
+ for(j=0; j<pIdx->nKeyCol; j++){
+ pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx);
+ if( pTerm==0 ) break;
+ pLoop->aLTerm[j] = pTerm;
+ }
+ if( j!=pIdx->nKeyCol ) continue;
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
+ if( pIdx->isCovering || (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
+ pLoop->wsFlags |= WHERE_IDX_ONLY;
+ }
+ pLoop->nLTerm = j;
+ pLoop->u.btree.nEq = j;
+ pLoop->u.btree.pIndex = pIdx;
+ /* TUNING: Cost of a unique index lookup is 15 */
+ pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */
+ break;
+ }
+ }
+ if( pLoop->wsFlags ){
+ pLoop->nOut = (LogEst)1;
+ pWInfo->a[0].pWLoop = pLoop;
+ pLoop->maskSelf = getMask(&pWInfo->sMaskSet, iCur);
+ pWInfo->a[0].iTabCur = iCur;
+ pWInfo->nRowOut = 1;
+ if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
+ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
+#ifdef SQLITE_DEBUG
+ pLoop->cId = '0';
+#endif
+ return 1;
+ }
+ return 0;
+}
/*
** Generate the beginning of the loop used for WHERE clause processing.
@@ -5037,25 +5942,25 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
**
** ORDER BY CLAUSE PROCESSING
**
-** pOrderBy is a pointer to the ORDER BY clause of a SELECT statement,
+** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause
+** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement
** if there is one. If there is no ORDER BY clause or if this routine
** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
**
-** If an index can be used so that the natural output order of the table
-** scan is correct for the ORDER BY clause, then that index is used and
-** the returned WhereInfo.nOBSat field is set to pOrderBy->nExpr. This
-** is an optimization that prevents an unnecessary sort of the result set
-** if an index appropriate for the ORDER BY clause already exists.
-**
-** If the where clause loops cannot be arranged to provide the correct
-** output order, then WhereInfo.nOBSat is 0.
+** The iIdxCur parameter is the cursor number of an index. If
+** WHERE_ONETABLE_ONLY is set, iIdxCur is the cursor number of an index
+** to use for OR clause processing. The WHERE clause should use this
+** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is
+** the first cursor in an array of cursors for all indices. iIdxCur should
+** be used to compute the appropriate cursor depending on which index is
+** used.
*/
WhereInfo *sqlite3WhereBegin(
Parse *pParse, /* The parser context */
- SrcList *pTabList, /* A list of all tables to be scanned */
+ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
Expr *pWhere, /* The WHERE clause */
- ExprList *pOrderBy, /* An ORDER BY clause, or NULL */
- ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */
+ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
+ ExprList *pResultSet, /* Result set of the query */
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
){
@@ -5064,18 +5969,29 @@ WhereInfo *sqlite3WhereBegin(
WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */
- WhereBestIdx sWBI; /* Best index search context */
+ WhereLoopBuilder sWLB; /* The WhereLoop builder */
WhereMaskSet *pMaskSet; /* The expression mask set */
WhereLevel *pLevel; /* A single level in pWInfo->a[] */
- int iFrom; /* First unused FROM clause element */
- int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */
+ WhereLoop *pLoop; /* Pointer to a single WhereLoop object */
int ii; /* Loop counter */
sqlite3 *db; /* Database connection */
+ int rc; /* Return code */
/* Variable initialization */
- memset(&sWBI, 0, sizeof(sWBI));
- sWBI.pParse = pParse;
+ db = pParse->db;
+ memset(&sWLB, 0, sizeof(sWLB));
+
+ /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
+ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
+ if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+ sWLB.pOrderBy = pOrderBy;
+
+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
+ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -5100,46 +6016,57 @@ WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- db = pParse->db;
nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
- pWInfo = sqlite3DbMallocZero(db,
- nByteWInfo +
- sizeof(WhereClause) +
- sizeof(WhereMaskSet)
- );
+ pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
pWInfo = 0;
goto whereBeginError;
}
+ pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
- pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
- pWInfo->pWC = sWBI.pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
+ pWInfo->pOrderBy = pOrderBy;
+ pWInfo->pResultSet = pResultSet;
+ pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
- pMaskSet = (WhereMaskSet*)&sWBI.pWC[1];
- sWBI.aLevel = pWInfo->a;
-
- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
- if( OptimizationDisabled(db, SQLITE_DistinctOpt) ) pDistinct = 0;
+ pMaskSet = &pWInfo->sMaskSet;
+ sWLB.pWInfo = pWInfo;
+ sWLB.pWC = &pWInfo->sWC;
+ sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo);
+ assert( EIGHT_BYTE_ALIGNMENT(sWLB.pNew) );
+ whereLoopInit(sWLB.pNew);
+#ifdef SQLITE_DEBUG
+ sWLB.pNew->cId = '*';
+#endif
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
- whereClauseInit(sWBI.pWC, pParse, pMaskSet, wctrlFlags);
- sqlite3ExprCodeConstants(pParse, pWhere);
- whereSplit(sWBI.pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
+ whereClauseInit(&pWInfo->sWC, pWInfo);
+ whereSplit(&pWInfo->sWC, pWhere, TK_AND);
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
*/
- if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
- sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
- pWhere = 0;
+ for(ii=0; ii<sWLB.pWC->nTerm; ii++){
+ if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){
+ sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak,
+ SQLITE_JUMPIFNULL);
+ sWLB.pWC->a[ii].wtFlags |= TERM_CODED;
+ }
+ }
+
+ /* Special case: No FROM clause
+ */
+ if( nTabList==0 ){
+ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr;
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
}
/* Assign a bit from the bitmask to every term in the FROM clause.
@@ -5177,306 +6104,151 @@ WhereInfo *sqlite3WhereBegin(
** want to analyze these virtual terms, so start analyzing at the end
** and work forward so that the added virtual terms are never processed.
*/
- exprAnalyzeAll(pTabList, sWBI.pWC);
+ exprAnalyzeAll(pTabList, &pWInfo->sWC);
if( db->mallocFailed ){
goto whereBeginError;
}
- /* Check if the DISTINCT qualifier, if there is one, is redundant.
- ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to
- ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT.
- */
- if( pDistinct && isDistinctRedundant(pParse, pTabList, sWBI.pWC, pDistinct) ){
- pDistinct = 0;
- pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pResultSet) ){
+ /* The DISTINCT marking is pointless. Ignore it. */
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }else if( pOrderBy==0 ){
+ /* Try to ORDER BY the result set to make distinct processing easier */
+ pWInfo->wctrlFlags |= WHERE_DISTINCTBY;
+ pWInfo->pOrderBy = pResultSet;
+ }
}
- /* Chose the best index to use for each table in the FROM clause.
- **
- ** This loop fills in the following fields:
- **
- ** pWInfo->a[].pIdx The index to use for this level of the loop.
- ** pWInfo->a[].wsFlags WHERE_xxx flags associated with pIdx
- ** pWInfo->a[].nEq The number of == and IN constraints
- ** pWInfo->a[].iFrom Which term of the FROM clause is being coded
- ** pWInfo->a[].iTabCur The VDBE cursor for the database table
- ** pWInfo->a[].iIdxCur The VDBE cursor for the index
- ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term
- **
- ** This loop also figures out the nesting order of tables in the FROM
- ** clause.
- */
- sWBI.notValid = ~(Bitmask)0;
- sWBI.pOrderBy = pOrderBy;
- sWBI.n = nTabList;
- sWBI.pDistinct = pDistinct;
- andFlags = ~0;
- WHERETRACE(("*** Optimizer Start ***\n"));
- for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){
- WhereCost bestPlan; /* Most efficient plan seen so far */
- Index *pIdx; /* Index for FROM table at pTabItem */
- int j; /* For looping over FROM tables */
- int bestJ = -1; /* The value of j */
- Bitmask m; /* Bitmask value for j or bestJ */
- int isOptimal; /* Iterator for optimal/non-optimal search */
- int ckOptimal; /* Do the optimal scan check */
- int nUnconstrained; /* Number tables without INDEXED BY */
- Bitmask notIndexed; /* Mask of tables that cannot use an index */
-
- memset(&bestPlan, 0, sizeof(bestPlan));
- bestPlan.rCost = SQLITE_BIG_DBL;
- WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i));
-
- /* Loop through the remaining entries in the FROM clause to find the
- ** next nested loop. The loop tests all FROM clause entries
- ** either once or twice.
- **
- ** The first test is always performed if there are two or more entries
- ** remaining and never performed if there is only one FROM clause entry
- ** to choose from. The first test looks for an "optimal" scan. In
- ** this context an optimal scan is one that uses the same strategy
- ** for the given FROM clause entry as would be selected if the entry
- ** were used as the innermost nested loop. In other words, a table
- ** is chosen such that the cost of running that table cannot be reduced
- ** by waiting for other tables to run first. This "optimal" test works
- ** by first assuming that the FROM clause is on the inner loop and finding
- ** its query plan, then checking to see if that query plan uses any
- ** other FROM clause terms that are sWBI.notValid. If no notValid terms
- ** are used then the "optimal" query plan works.
- **
- ** Note that the WhereCost.nRow parameter for an optimal scan might
- ** not be as small as it would be if the table really were the innermost
- ** join. The nRow value can be reduced by WHERE clause constraints
- ** that do not use indices. But this nRow reduction only happens if the
- ** table really is the innermost join.
- **
- ** The second loop iteration is only performed if no optimal scan
- ** strategies were found by the first iteration. This second iteration
- ** is used to search for the lowest cost scan overall.
- **
- ** Without the optimal scan step (the first iteration) a suboptimal
- ** plan might be chosen for queries like this:
- **
- ** CREATE TABLE t1(a, b);
- ** CREATE TABLE t2(c, d);
- ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
- **
- ** The best strategy is to iterate through table t1 first. However it
- ** is not possible to determine this with a simple greedy algorithm.
- ** Since the cost of a linear scan through table t2 is the same
- ** as the cost of a linear scan through table t1, a simple greedy
- ** algorithm may choose to use t2 for the outer loop, which is a much
- ** costlier approach.
- */
- nUnconstrained = 0;
- notIndexed = 0;
-
- /* The optimal scan check only occurs if there are two or more tables
- ** available to be reordered */
- if( iFrom==nTabList-1 ){
- ckOptimal = 0; /* Common case of just one table in the FROM clause */
- }else{
- ckOptimal = -1;
- for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
- m = getMask(pMaskSet, sWBI.pSrc->iCursor);
- if( (m & sWBI.notValid)==0 ){
- if( j==iFrom ) iFrom++;
- continue;
- }
- if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ) break;
- if( ++ckOptimal ) break;
- if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
+ /* Construct the WhereLoop objects */
+ WHERETRACE(0xffff,("*** Optimizer Start ***\n"));
+ /* Display all terms of the WHERE clause */
+#if defined(WHERETRACE_ENABLED) && defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ if( sqlite3WhereTrace & 0x100 ){
+ int i;
+ Vdbe *v = pParse->pVdbe;
+ sqlite3ExplainBegin(v);
+ for(i=0; i<sWLB.pWC->nTerm; i++){
+ sqlite3ExplainPrintf(v, "#%-2d ", i);
+ sqlite3ExplainPush(v);
+ whereExplainTerm(v, &sWLB.pWC->a[i]);
+ sqlite3ExplainPop(v);
+ sqlite3ExplainNL(v);
+ }
+ sqlite3ExplainFinish(v);
+ sqlite3DebugPrintf("%s", sqlite3VdbeExplanation(v));
+ }
+#endif
+ if( nTabList!=1 || whereShortCut(&sWLB)==0 ){
+ rc = whereLoopAddAll(&sWLB);
+ if( rc ) goto whereBeginError;
+
+ /* Display all of the WhereLoop objects if wheretrace is enabled */
+#ifdef WHERETRACE_ENABLED /* !=0 */
+ if( sqlite3WhereTrace ){
+ WhereLoop *p;
+ int i;
+ static char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
+ for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
+ p->cId = zLabel[i%sizeof(zLabel)];
+ whereLoopPrint(p, sWLB.pWC);
}
}
- assert( ckOptimal==0 || ckOptimal==1 );
-
- for(isOptimal=ckOptimal; isOptimal>=0 && bestJ<0; isOptimal--){
- for(j=iFrom, sWBI.pSrc=&pTabList->a[j]; j<nTabList; j++, sWBI.pSrc++){
- if( j>iFrom && (sWBI.pSrc->jointype & (JT_LEFT|JT_CROSS))!=0 ){
- /* This break and one like it in the ckOptimal computation loop
- ** above prevent table reordering across LEFT and CROSS JOINs.
- ** The LEFT JOIN case is necessary for correctness. The prohibition
- ** against reordering across a CROSS JOIN is an SQLite feature that
- ** allows the developer to control table reordering */
- break;
- }
- m = getMask(pMaskSet, sWBI.pSrc->iCursor);
- if( (m & sWBI.notValid)==0 ){
- assert( j>iFrom );
- continue;
- }
- sWBI.notReady = (isOptimal ? m : sWBI.notValid);
- if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
-
- WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
- j, sWBI.pSrc->pTab->zName, isOptimal));
- assert( sWBI.pSrc->pTab );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(sWBI.pSrc->pTab) ){
- sWBI.ppIdxInfo = &pWInfo->a[j].pIdxInfo;
- bestVirtualIndex(&sWBI);
- }else
#endif
- {
- bestBtreeIndex(&sWBI);
- }
- assert( isOptimal || (sWBI.cost.used&sWBI.notValid)==0 );
-
- /* If an INDEXED BY clause is present, then the plan must use that
- ** index if it uses any index at all */
- assert( sWBI.pSrc->pIndex==0
- || (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
- || sWBI.cost.plan.u.pIdx==sWBI.pSrc->pIndex );
-
- if( isOptimal && (sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- notIndexed |= m;
- }
- if( isOptimal ){
- pWInfo->a[j].rOptCost = sWBI.cost.rCost;
- }else if( ckOptimal ){
- /* If two or more tables have nearly the same outer loop cost, but
- ** very different inner loop (optimal) cost, we want to choose
- ** for the outer loop that table which benefits the least from
- ** being in the inner loop. The following code scales the
- ** outer loop cost estimate to accomplish that. */
- WHERETRACE((" scaling cost from %.1f to %.1f\n",
- sWBI.cost.rCost,
- sWBI.cost.rCost/pWInfo->a[j].rOptCost));
- sWBI.cost.rCost /= pWInfo->a[j].rOptCost;
- }
-
- /* Conditions under which this table becomes the best so far:
- **
- ** (1) The table must not depend on other tables that have not
- ** yet run. (In other words, it must not depend on tables
- ** in inner loops.)
- **
- ** (2) (This rule was removed on 2012-11-09. The scaling of the
- ** cost using the optimal scan cost made this rule obsolete.)
- **
- ** (3) All tables have an INDEXED BY clause or this table lacks an
- ** INDEXED BY clause or this table uses the specific
- ** index specified by its INDEXED BY clause. This rule ensures
- ** that a best-so-far is always selected even if an impossible
- ** combination of INDEXED BY clauses are given. The error
- ** will be detected and relayed back to the application later.
- ** The NEVER() comes about because rule (2) above prevents
- ** An indexable full-table-scan from reaching rule (3).
- **
- ** (4) The plan cost must be lower than prior plans, where "cost"
- ** is defined by the compareCost() function above.
- */
- if( (sWBI.cost.used&sWBI.notValid)==0 /* (1) */
- && (nUnconstrained==0 || sWBI.pSrc->pIndex==0 /* (3) */
- || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
- && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
- ){
- WHERETRACE((" === table %d (%s) is best so far\n"
- " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
- j, sWBI.pSrc->pTab->zName,
- sWBI.cost.rCost, sWBI.cost.plan.nRow,
- sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));
- bestPlan = sWBI.cost;
- bestJ = j;
- }
-
- /* In a join like "w JOIN x LEFT JOIN y JOIN z" make sure that
- ** table y (and not table z) is always the next inner loop inside
- ** of table x. */
- if( (sWBI.pSrc->jointype & JT_LEFT)!=0 ) break;
- }
+
+ wherePathSolver(pWInfo, 0);
+ if( db->mallocFailed ) goto whereBeginError;
+ if( pWInfo->pOrderBy ){
+ wherePathSolver(pWInfo, pWInfo->nRowOut+1);
+ if( db->mallocFailed ) goto whereBeginError;
}
- assert( bestJ>=0 );
- assert( sWBI.notValid & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
- assert( bestJ==iFrom || (pTabList->a[iFrom].jointype & JT_LEFT)==0 );
- testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
- testcase( bestJ>iFrom && bestJ<nTabList-1
- && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
- WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
- " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
- bestJ, pTabList->a[bestJ].pTab->zName,
- pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
- bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));
- if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
- assert( pWInfo->eDistinct==0 );
- pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
+ pWInfo->revMask = (Bitmask)(-1);
+ }
+ if( pParse->nErr || NEVER(db->mallocFailed) ){
+ goto whereBeginError;
+ }
+#ifdef WHERETRACE_ENABLED /* !=0 */
+ if( sqlite3WhereTrace ){
+ int ii;
+ sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
+ if( pWInfo->nOBSat>0 ){
+ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask);
}
- andFlags &= bestPlan.plan.wsFlags;
- pLevel->plan = bestPlan.plan;
- pLevel->iTabCur = pTabList->a[bestJ].iCursor;
- testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
- testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
- if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
- if( (wctrlFlags & WHERE_ONETABLE_ONLY)
- && (bestPlan.plan.wsFlags & WHERE_TEMP_INDEX)==0
- ){
- pLevel->iIdxCur = iIdxCur;
- }else{
- pLevel->iIdxCur = pParse->nTab++;
+ switch( pWInfo->eDistinct ){
+ case WHERE_DISTINCT_UNIQUE: {
+ sqlite3DebugPrintf(" DISTINCT=unique");
+ break;
+ }
+ case WHERE_DISTINCT_ORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=ordered");
+ break;
+ }
+ case WHERE_DISTINCT_UNORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=unordered");
+ break;
}
- }else{
- pLevel->iIdxCur = -1;
}
- sWBI.notValid &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor);
- pLevel->iFrom = (u8)bestJ;
- if( bestPlan.plan.nRow>=(double)1 ){
- pParse->nQueryLoop *= bestPlan.plan.nRow;
+ sqlite3DebugPrintf("\n");
+ for(ii=0; ii<pWInfo->nLevel; ii++){
+ whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC);
}
-
- /* Check that if the table scanned by this loop iteration had an
- ** INDEXED BY clause attached to it, that the named index is being
- ** used for the scan. If not, then query compilation has failed.
- ** Return an error.
- */
- pIdx = pTabList->a[bestJ].pIndex;
- if( pIdx ){
- if( (bestPlan.plan.wsFlags & WHERE_INDEXED)==0 ){
- sqlite3ErrorMsg(pParse, "cannot use index: %s", pIdx->zName);
- goto whereBeginError;
- }else{
- /* If an INDEXED BY clause is used, the bestIndex() function is
- ** guaranteed to find the index specified in the INDEXED BY clause
- ** if it find an index at all. */
- assert( bestPlan.plan.u.pIdx==pIdx );
+ }
+#endif
+ /* Attempt to omit tables from the join that do not effect the result */
+ if( pWInfo->nLevel>=2
+ && pResultSet!=0
+ && OptimizationEnabled(db, SQLITE_OmitNoopJoin)
+ ){
+ Bitmask tabUsed = exprListTableUsage(pMaskSet, pResultSet);
+ if( sWLB.pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, sWLB.pOrderBy);
+ while( pWInfo->nLevel>=2 ){
+ WhereTerm *pTerm, *pEnd;
+ pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
+ if( (pWInfo->pTabList->a[pLoop->iTab].jointype & JT_LEFT)==0 ) break;
+ if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
+ && (pLoop->wsFlags & WHERE_ONEROW)==0
+ ){
+ break;
}
+ if( (tabUsed & pLoop->maskSelf)!=0 ) break;
+ pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
+ for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ ){
+ break;
+ }
+ }
+ if( pTerm<pEnd ) break;
+ WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
+ pWInfo->nLevel--;
+ nTabList--;
}
}
- WHERETRACE(("*** Optimizer Finished ***\n"));
- if( pParse->nErr || db->mallocFailed ){
- goto whereBeginError;
- }
- if( nTabList ){
- pLevel--;
- pWInfo->nOBSat = pLevel->plan.nOBSat;
- }else{
- pWInfo->nOBSat = 0;
- }
-
- /* If the total query only selects a single row, then the ORDER BY
- ** clause is irrelevant.
- */
- if( (andFlags & WHERE_UNIQUE)!=0 && pOrderBy ){
- assert( nTabList==0 || (pLevel->plan.wsFlags & WHERE_ALL_UNIQUE)!=0 );
- pWInfo->nOBSat = pOrderBy->nExpr;
- }
+ WHERETRACE(0xffff,("*** Optimizer Finished ***\n"));
+ pWInfo->pParse->nQueryLoop += pWInfo->nRowOut;
/* 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 one-pass algorithm only works if the WHERE clause constrains
** the statement to update a single row.
*/
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
- if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){
+ if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
+ && (pWInfo->a[0].pWLoop->wsFlags & WHERE_ONEROW)!=0 ){
pWInfo->okOnePass = 1;
- pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY;
+ if( HasRowid(pTabList->a[0].pTab) ){
+ pWInfo->a[0].pWLoop->wsFlags &= ~WHERE_IDX_ONLY;
+ }
}
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
- sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
notReady = ~(Bitmask)0;
- pWInfo->nRowOut = (double)1;
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
@@ -5484,13 +6256,13 @@ WhereInfo *sqlite3WhereBegin(
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
- pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ pLoop = pLevel->pWLoop;
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
/* Do nothing */
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
int iCur = pTabItem->iCursor;
sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB);
@@ -5498,13 +6270,18 @@ WhereInfo *sqlite3WhereBegin(
/* noop */
}else
#endif
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
+ if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
&& (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
- int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
+ int op = OP_OpenRead;
+ if( pWInfo->okOnePass ){
+ op = OP_OpenWrite;
+ pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
+ };
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
- testcase( pTab->nCol==BMS-1 );
- testcase( pTab->nCol==BMS );
- if( !pWInfo->okOnePass && pTab->nCol<BMS ){
+ assert( pTabItem->iCursor==pLevel->iTabCur );
+ testcase( !pWInfo->okOnePass && pTab->nCol==BMS-1 );
+ testcase( !pWInfo->okOnePass && pTab->nCol==BMS );
+ if( !pWInfo->okOnePass && pTab->nCol<BMS && HasRowid(pTab) ){
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
@@ -5515,23 +6292,46 @@ WhereInfo *sqlite3WhereBegin(
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){
- constructAutomaticIndex(pParse, sWBI.pWC, pTabItem, notReady, pLevel);
- }else
-#endif
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
- Index *pIx = pLevel->plan.u.pIdx;
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
- int iIndexCur = pLevel->iIdxCur;
+ if( pLoop->wsFlags & WHERE_INDEXED ){
+ Index *pIx = pLoop->u.btree.pIndex;
+ int iIndexCur;
+ int op = OP_OpenRead;
+ /* iIdxCur is always set if to a positive value if ONEPASS is possible */
+ assert( iIdxCur!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
+ && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0
+ ){
+ /* This is one term of an OR-optimization using the PRIMARY KEY of a
+ ** WITHOUT ROWID table. No need for a separate index */
+ iIndexCur = pLevel->iTabCur;
+ op = 0;
+ }else if( pWInfo->okOnePass ){
+ Index *pJ = pTabItem->pTab->pIndex;
+ iIndexCur = iIdxCur;
+ assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
+ while( ALWAYS(pJ) && pJ!=pIx ){
+ iIndexCur++;
+ pJ = pJ->pNext;
+ }
+ op = OP_OpenWrite;
+ pWInfo->aiCurOnePass[1] = iIndexCur;
+ }else if( iIdxCur && (wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ){
+ iIndexCur = iIdxCur;
+ if( wctrlFlags & WHERE_REOPEN_IDX ) op = OP_ReopenIdx;
+ }else{
+ iIndexCur = pParse->nTab++;
+ }
+ pLevel->iIdxCur = iIndexCur;
assert( pIx->pSchema==pTab->pSchema );
assert( iIndexCur>=0 );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIndexCur, pIx->tnum, iDb,
- (char*)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIx->zName));
+ if( op ){
+ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIx);
+ VdbeComment((v, "%s", pIx->zName));
+ }
}
- sqlite3CodeVerifySchema(pParse, iDb);
- notReady &= ~getMask(sWBI.pWC->pMaskSet, pTabItem->iCursor);
+ if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
+ notReady &= ~getMask(&pWInfo->sMaskSet, pTabItem->iCursor);
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@@ -5543,67 +6343,21 @@ WhereInfo *sqlite3WhereBegin(
notReady = ~(Bitmask)0;
for(ii=0; ii<nTabList; ii++){
pLevel = &pWInfo->a[ii];
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ constructAutomaticIndex(pParse, &pWInfo->sWC,
+ &pTabList->a[pLevel->iFrom], notReady, pLevel);
+ if( db->mallocFailed ) goto whereBeginError;
+ }
+#endif
explainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags);
- notReady = codeOneLoopStart(pWInfo, ii, wctrlFlags, notReady);
+ pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
+ notReady = codeOneLoopStart(pWInfo, ii, notReady);
pWInfo->iContinue = pLevel->addrCont;
}
-#ifdef SQLITE_TEST /* For testing and debugging use only */
- /* Record in the query plan information about the current table
- ** and the index used to access it (if any). If the table itself
- ** is not used, its name is just '{}'. If no index is used
- ** the index is listed as "{}". If the primary key is used the
- ** index name is '*'.
- */
- for(ii=0; ii<nTabList; ii++){
- char *z;
- int n;
- int w;
- struct SrcList_item *pTabItem;
-
- pLevel = &pWInfo->a[ii];
- w = pLevel->plan.wsFlags;
- pTabItem = &pTabList->a[pLevel->iFrom];
- z = pTabItem->zAlias;
- if( z==0 ) z = pTabItem->pTab->zName;
- n = sqlite3Strlen30(z);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){
- if( (w & WHERE_IDX_ONLY)!=0 && (w & WHERE_COVER_SCAN)==0 ){
- memcpy(&sqlite3_query_plan[nQPlan], "{}", 2);
- nQPlan += 2;
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], z, n);
- nQPlan += n;
- }
- sqlite3_query_plan[nQPlan++] = ' ';
- }
- testcase( w & WHERE_ROWID_EQ );
- testcase( w & WHERE_ROWID_RANGE );
- if( w & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
- memcpy(&sqlite3_query_plan[nQPlan], "* ", 2);
- nQPlan += 2;
- }else if( (w & WHERE_INDEXED)!=0 && (w & WHERE_COVER_SCAN)==0 ){
- n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){
- memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n);
- nQPlan += n;
- sqlite3_query_plan[nQPlan++] = ' ';
- }
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], "{} ", 3);
- nQPlan += 3;
- }
- }
- while( nQPlan>0 && sqlite3_query_plan[nQPlan-1]==' ' ){
- sqlite3_query_plan[--nQPlan] = 0;
- }
- sqlite3_query_plan[nQPlan] = 0;
- nQPlan = 0;
-#endif /* SQLITE_TEST // Testing and debugging use only */
-
- /* Record the continuation address in the WhereInfo structure. Then
- ** clean up and return.
- */
+ /* Done. */
+ VdbeModuleComment((v, "Begin WHERE-core"));
return pWInfo;
/* Jump here if malloc fails */
@@ -5624,40 +6378,56 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
Vdbe *v = pParse->pVdbe;
int i;
WhereLevel *pLevel;
+ WhereLoop *pLoop;
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
/* Generate loop termination code.
*/
+ VdbeModuleComment((v, "End WHERE-core"));
sqlite3ExprCacheClear(pParse);
for(i=pWInfo->nLevel-1; i>=0; i--){
+ int addr;
pLevel = &pWInfo->a[i];
+ pLoop = pLevel->pWLoop;
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
if( pLevel->op!=OP_Noop ){
- sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2);
+ sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
sqlite3VdbeChangeP5(v, pLevel->p5);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, pLevel->op==OP_Next);
+ VdbeCoverageIf(v, pLevel->op==OP_Prev);
+ VdbeCoverageIf(v, pLevel->op==OP_VNext);
}
- if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
+ if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
struct InLoop *pIn;
int j;
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
}
sqlite3DbFree(db, pLevel->u.in.aInLoop);
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
+ if( pLevel->addrSkip ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrSkip);
+ VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));
+ sqlite3VdbeJumpHere(v, pLevel->addrSkip);
+ sqlite3VdbeJumpHere(v, pLevel->addrSkip-2);
+ }
if( pLevel->iLeftJoin ){
- int addr;
- addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin);
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 );
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ || (pLoop->wsFlags & WHERE_INDEXED)!=0 );
+ if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
}
- if( pLevel->iIdxCur>=0 ){
+ if( pLoop->wsFlags & WHERE_INDEXED ){
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
if( pLevel->op==OP_Return ){
@@ -5667,6 +6437,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
}
sqlite3VdbeJumpHere(v, addr);
}
+ VdbeModuleComment((v, "End WHERE-loop%d: %s", i,
+ pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
}
/* The "break" point is here, just past the end of the outer loop.
@@ -5674,33 +6446,65 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
- /* Close all of the cursors that were opened by sqlite3WhereBegin.
- */
- assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
+ assert( pWInfo->nLevel<=pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
+ int k, last;
+ VdbeOp *pOp;
Index *pIdx = 0;
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
+ pLoop = pLevel->pWLoop;
+
+ /* For a co-routine, change all OP_Column references to the table of
+ ** the co-routine into OP_SCopy of result contained in a register.
+ ** OP_Rowid becomes OP_Null.
+ */
+ if( pTabItem->viaCoroutine && !db->mallocFailed ){
+ last = sqlite3VdbeCurrentAddr(v);
+ k = pLevel->addrBody;
+ pOp = sqlite3VdbeGetOp(v, k);
+ for(; k<last; k++, pOp++){
+ if( pOp->p1!=pLevel->iTabCur ) continue;
+ if( pOp->opcode==OP_Column ){
+ pOp->opcode = OP_Copy;
+ pOp->p1 = pOp->p2 + pTabItem->regResult;
+ pOp->p2 = pOp->p3;
+ pOp->p3 = 0;
+ }else if( pOp->opcode==OP_Rowid ){
+ pOp->opcode = OP_Null;
+ pOp->p1 = 0;
+ pOp->p3 = 0;
+ }
+ }
+ continue;
+ }
+
+ /* Close all of the cursors that were opened by sqlite3WhereBegin.
+ ** Except, do not close cursors that will be reused by the OR optimization
+ ** (WHERE_OMIT_OPEN_CLOSE). And do not close the OP_OpenWrite cursors
+ ** created for the ONEPASS optimization.
+ */
if( (pTab->tabFlags & TF_Ephemeral)==0
&& pTab->pSelect==0
&& (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
){
- int ws = pLevel->plan.wsFlags;
+ int ws = pLoop->wsFlags;
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
- if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){
+ if( (ws & WHERE_INDEXED)!=0
+ && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0
+ && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1]
+ ){
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
}
- /* 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.
+ /* If this scan uses an index, make VDBE code substitutions to read data
+ ** from the index instead of from the table where possible. In some cases
+ ** this optimization prevents the table from ever being read, which can
+ ** yield a significant performance boost.
**
** Calls to the code generator in between sqlite3WhereBegin and
** sqlite3WhereEnd will have created code that references the table
@@ -5708,29 +6512,30 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( pLevel->plan.wsFlags & WHERE_INDEXED ){
- pIdx = pLevel->plan.u.pIdx;
- }else if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
+ if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){
+ pIdx = pLoop->u.btree.pIndex;
+ }else if( pLoop->wsFlags & WHERE_MULTI_OR ){
pIdx = pLevel->u.pCovidx;
}
- if( pIdx && !db->mallocFailed){
- int k, j, last;
- VdbeOp *pOp;
-
- pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
+ if( pIdx && !db->mallocFailed ){
last = sqlite3VdbeCurrentAddr(v);
- for(k=pWInfo->iTop; k<last; k++, pOp++){
+ k = pLevel->addrBody;
+ pOp = sqlite3VdbeGetOp(v, k);
+ for(; k<last; k++, pOp++){
if( pOp->p1!=pLevel->iTabCur ) continue;
if( pOp->opcode==OP_Column ){
- for(j=0; j<pIdx->nColumn; j++){
- if( pOp->p2==pIdx->aiColumn[j] ){
- pOp->p2 = j;
- pOp->p1 = pLevel->iIdxCur;
- break;
- }
+ int x = pOp->p2;
+ assert( pIdx->pTable==pTab );
+ if( !HasRowid(pTab) ){
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ x = pPk->aiColumn[x];
+ }
+ x = sqlite3ColumnOfIndex(pIdx, x);
+ if( x>=0 ){
+ pOp->p2 = x;
+ pOp->p1 = pLevel->iIdxCur;
}
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || j<pIdx->nColumn );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 );
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
diff --git a/src/whereInt.h b/src/whereInt.h
new file mode 100644
index 0000000..81f4a03
--- /dev/null
+++ b/src/whereInt.h
@@ -0,0 +1,461 @@
+/*
+** 2013-11-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 structure and macro definitions for the query
+** planner logic in "where.c". These definitions are broken out into
+** a separate source file for easier editing.
+*/
+
+/*
+** Trace output macros
+*/
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+/***/ int sqlite3WhereTrace = 0;
+#endif
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
+# define WHERETRACE_ENABLED 1
+#else
+# define WHERETRACE(K,X)
+#endif
+
+/* Forward references
+*/
+typedef struct WhereClause WhereClause;
+typedef struct WhereMaskSet WhereMaskSet;
+typedef struct WhereOrInfo WhereOrInfo;
+typedef struct WhereAndInfo WhereAndInfo;
+typedef struct WhereLevel WhereLevel;
+typedef struct WhereLoop WhereLoop;
+typedef struct WherePath WherePath;
+typedef struct WhereTerm WhereTerm;
+typedef struct WhereLoopBuilder WhereLoopBuilder;
+typedef struct WhereScan WhereScan;
+typedef struct WhereOrCost WhereOrCost;
+typedef struct WhereOrSet WhereOrSet;
+
+/*
+** This object contains information needed to implement a single nested
+** loop in WHERE clause.
+**
+** Contrast this object with WhereLoop. This object describes the
+** implementation of the loop. WhereLoop describes the algorithm.
+** This object contains a pointer to the WhereLoop algorithm as one of
+** its elements.
+**
+** The WhereInfo object contains a single instance of this object for
+** each term in the FROM clause (which is to say, for each of the
+** nested loops as implemented). The order of WhereLevel objects determines
+** the loop nested order, with WhereInfo.a[0] being the outer loop and
+** WhereInfo.a[WhereInfo.nLevel-1] being the inner loop.
+*/
+struct WhereLevel {
+ int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
+ int iTabCur; /* The VDBE cursor used to access the table */
+ int iIdxCur; /* The VDBE cursor used to access pIdx */
+ int addrBrk; /* Jump here to break out of the loop */
+ int addrNxt; /* Jump here to start the next IN combination */
+ int addrSkip; /* Jump here for next iteration of skip-scan */
+ int addrCont; /* Jump here to continue with the next loop cycle */
+ int addrFirst; /* First instruction of interior of the loop */
+ int addrBody; /* Beginning of the body of this loop */
+ u8 iFrom; /* Which entry in the FROM clause */
+ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
+ int p1, p2; /* Operands of the opcode used to ends the loop */
+ union { /* Information that depends on pWLoop->wsFlags */
+ struct {
+ int nIn; /* Number of entries in aInLoop[] */
+ struct InLoop {
+ int iCur; /* The VDBE cursor used by this IN operator */
+ int addrInTop; /* Top of the IN loop */
+ u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
+ } *aInLoop; /* Information about each nested IN operator */
+ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
+ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
+ } u;
+ struct WhereLoop *pWLoop; /* The selected WhereLoop object */
+ Bitmask notReady; /* FROM entries not usable at this level */
+};
+
+/*
+** Each instance of this object represents an algorithm for evaluating one
+** term of a join. Every term of the FROM clause will have at least
+** one corresponding WhereLoop object (unless INDEXED BY constraints
+** prevent a query solution - which is an error) and many terms of the
+** FROM clause will have multiple WhereLoop objects, each describing a
+** potential way of implementing that FROM-clause term, together with
+** dependencies and cost estimates for using the chosen algorithm.
+**
+** Query planning consists of building up a collection of these WhereLoop
+** objects, then computing a particular sequence of WhereLoop objects, with
+** one WhereLoop object per FROM clause term, that satisfy all dependencies
+** and that minimize the overall cost.
+*/
+struct WhereLoop {
+ Bitmask prereq; /* Bitmask of other loops that must run first */
+ Bitmask maskSelf; /* Bitmask identifying table iTab */
+#ifdef SQLITE_DEBUG
+ char cId; /* Symbolic ID of this loop for debugging use */
+#endif
+ u8 iTab; /* Position in FROM clause of table for this loop */
+ u8 iSortIdx; /* Sorting index number. 0==None */
+ LogEst rSetup; /* One-time setup cost (ex: create transient index) */
+ LogEst rRun; /* Cost of running each loop */
+ LogEst nOut; /* Estimated number of output rows */
+ union {
+ struct { /* Information for internal btree tables */
+ u16 nEq; /* Number of equality constraints */
+ u16 nSkip; /* Number of initial index columns to skip */
+ Index *pIndex; /* Index used, or NULL */
+ } btree;
+ struct { /* Information for virtual tables */
+ int idxNum; /* Index number */
+ u8 needFree; /* True if sqlite3_free(idxStr) is needed */
+ i8 isOrdered; /* True if satisfies ORDER BY */
+ u16 omitMask; /* Terms that may be omitted */
+ char *idxStr; /* Index identifier string */
+ } vtab;
+ } u;
+ u32 wsFlags; /* WHERE_* flags describing the plan */
+ u16 nLTerm; /* Number of entries in aLTerm[] */
+ /**** whereLoopXfer() copies fields above ***********************/
+# define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot)
+ u16 nLSlot; /* Number of slots allocated for aLTerm[] */
+ WhereTerm **aLTerm; /* WhereTerms used */
+ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
+ WhereTerm *aLTermSpace[4]; /* Initial aLTerm[] space */
+};
+
+/* This object holds the prerequisites and the cost of running a
+** subquery on one operand of an OR operator in the WHERE clause.
+** See WhereOrSet for additional information
+*/
+struct WhereOrCost {
+ Bitmask prereq; /* Prerequisites */
+ LogEst rRun; /* Cost of running this subquery */
+ LogEst nOut; /* Number of outputs for this subquery */
+};
+
+/* The WhereOrSet object holds a set of possible WhereOrCosts that
+** correspond to the subquery(s) of OR-clause processing. Only the
+** best N_OR_COST elements are retained.
+*/
+#define N_OR_COST 3
+struct WhereOrSet {
+ u16 n; /* Number of valid a[] entries */
+ WhereOrCost a[N_OR_COST]; /* Set of best costs */
+};
+
+
+/* Forward declaration of methods */
+static int whereLoopResize(sqlite3*, WhereLoop*, int);
+
+/*
+** Each instance of this object holds a sequence of WhereLoop objects
+** that implement some or all of a query plan.
+**
+** Think of each WhereLoop object as a node in a graph with arcs
+** showing dependencies and costs for travelling between nodes. (That is
+** not a completely accurate description because WhereLoop costs are a
+** vector, not a scalar, and because dependencies are many-to-one, not
+** one-to-one as are graph nodes. But it is a useful visualization aid.)
+** Then a WherePath object is a path through the graph that visits some
+** or all of the WhereLoop objects once.
+**
+** The "solver" works by creating the N best WherePath objects of length
+** 1. Then using those as a basis to compute the N best WherePath objects
+** of length 2. And so forth until the length of WherePaths equals the
+** number of nodes in the FROM clause. The best (lowest cost) WherePath
+** at the end is the choosen query plan.
+*/
+struct WherePath {
+ Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */
+ Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
+ LogEst nRow; /* Estimated number of rows generated by this path */
+ LogEst rCost; /* Total cost of this path */
+ LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */
+ i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */
+ WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
+};
+
+/*
+** The query generator uses an array of instances of this structure to
+** help it analyze the subexpressions of the WHERE clause. Each WHERE
+** clause subexpression is separated from the others by AND operators,
+** usually, or sometimes subexpressions separated by OR.
+**
+** All WhereTerms are collected into a single WhereClause structure.
+** The following identity holds:
+**
+** WhereTerm.pWC->a[WhereTerm.idx] == WhereTerm
+**
+** When a term is of the form:
+**
+** X <op> <expr>
+**
+** where X is a column name and <op> is one of certain operators,
+** then WhereTerm.leftCursor and WhereTerm.u.leftColumn record the
+** cursor number and column number for X. WhereTerm.eOperator records
+** the <op> using a bitmask encoding defined by WO_xxx below. The
+** use of a bitmask encoding for the operator allows us to search
+** quickly for terms that match any of several different operators.
+**
+** A WhereTerm might also be two or more subterms connected by OR:
+**
+** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR ....
+**
+** In this second case, wtFlag has the TERM_ORINFO bit set and eOperator==WO_OR
+** and the WhereTerm.u.pOrInfo field points to auxiliary information that
+** is collected about the OR clause.
+**
+** If a term in the WHERE clause does not match either of the two previous
+** categories, then eOperator==0. The WhereTerm.pExpr field is still set
+** to the original subexpression content and wtFlags is set up appropriately
+** but no other fields in the WhereTerm object are meaningful.
+**
+** When eOperator!=0, prereqRight and prereqAll record sets of cursor numbers,
+** but they do so indirectly. A single WhereMaskSet structure translates
+** cursor number into bits and the translated bit is stored in the prereq
+** fields. The translation is used in order to maximize the number of
+** bits that will fit in a Bitmask. The VDBE cursor numbers might be
+** spread out over the non-negative integers. For example, the cursor
+** numbers might be 3, 8, 9, 10, 20, 23, 41, and 45. The WhereMaskSet
+** translates these sparse cursor numbers into consecutive integers
+** beginning with 0 in order to make the best possible use of the available
+** bits in the Bitmask. So, in the example above, the cursor numbers
+** would be mapped into integers 0 through 7.
+**
+** The number of terms in a join is limited by the number of bits
+** in prereqRight and prereqAll. The default is 64 bits, hence SQLite
+** is only able to process joins with 64 or fewer tables.
+*/
+struct WhereTerm {
+ Expr *pExpr; /* Pointer to the subexpression that is this term */
+ int iParent; /* Disable pWC->a[iParent] when this term disabled */
+ int leftCursor; /* Cursor number of X in "X <op> <expr>" */
+ union {
+ int leftColumn; /* Column number of X in "X <op> <expr>" */
+ WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
+ WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
+ } u;
+ LogEst truthProb; /* Probability of truth for this expression */
+ u16 eOperator; /* A WO_xx value describing <op> */
+ u8 wtFlags; /* TERM_xxx bit flags. See below */
+ u8 nChild; /* Number of children that must disable us */
+ WhereClause *pWC; /* The clause this term is part of */
+ Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
+ Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
+};
+
+/*
+** Allowed values of WhereTerm.wtFlags
+*/
+#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 */
+#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
+#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
+#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
+#else
+# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
+#endif
+
+/*
+** An instance of the WhereScan object is used as an iterator for locating
+** terms in the WHERE clause that are useful to the query planner.
+*/
+struct WhereScan {
+ WhereClause *pOrigWC; /* Original, innermost WhereClause */
+ WhereClause *pWC; /* WhereClause currently being scanned */
+ char *zCollName; /* Required collating sequence, if not NULL */
+ char idxaff; /* Must match this affinity, if zCollName!=NULL */
+ unsigned char nEquiv; /* Number of entries in aEquiv[] */
+ unsigned char iEquiv; /* Next unused slot in aEquiv[] */
+ u32 opMask; /* Acceptable operators */
+ int k; /* Resume scanning at this->pWC->a[this->k] */
+ int aEquiv[22]; /* Cursor,Column pairs for equivalence classes */
+};
+
+/*
+** An instance of the following structure holds all information about a
+** WHERE clause. Mostly this is a container for one or more WhereTerms.
+**
+** Explanation of pOuter: For a WHERE clause of the form
+**
+** a AND ((b AND c) OR (d AND e)) AND f
+**
+** There are separate WhereClause objects for the whole clause and for
+** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
+** subclauses points to the WhereClause object for the whole clause.
+*/
+struct WhereClause {
+ WhereInfo *pWInfo; /* WHERE clause processing context */
+ WhereClause *pOuter; /* Outer conjunction */
+ u8 op; /* Split operator. TK_AND or TK_OR */
+ int nTerm; /* Number of terms */
+ int nSlot; /* Number of entries in a[] */
+ WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
+#if defined(SQLITE_SMALL_STACK)
+ WhereTerm aStatic[1]; /* Initial static space for a[] */
+#else
+ WhereTerm aStatic[8]; /* Initial static space for a[] */
+#endif
+};
+
+/*
+** A WhereTerm with eOperator==WO_OR has its u.pOrInfo pointer set to
+** a dynamically allocated instance of the following structure.
+*/
+struct WhereOrInfo {
+ WhereClause wc; /* Decomposition into subterms */
+ Bitmask indexable; /* Bitmask of all indexable tables in the clause */
+};
+
+/*
+** A WhereTerm with eOperator==WO_AND has its u.pAndInfo pointer set to
+** a dynamically allocated instance of the following structure.
+*/
+struct WhereAndInfo {
+ WhereClause wc; /* The subexpression broken out */
+};
+
+/*
+** An instance of the following structure keeps track of a mapping
+** between VDBE cursor numbers and bits of the bitmasks in WhereTerm.
+**
+** The VDBE cursor numbers are small integers contained in
+** SrcList_item.iCursor and Expr.iTable fields. For any given WHERE
+** clause, the cursor numbers might not begin with 0 and they might
+** contain gaps in the numbering sequence. But we want to make maximum
+** use of the bits in our bitmasks. This structure provides a mapping
+** from the sparse cursor numbers into consecutive integers beginning
+** with 0.
+**
+** If WhereMaskSet.ix[A]==B it means that The A-th bit of a Bitmask
+** corresponds VDBE cursor number B. The A-th bit of a bitmask is 1<<A.
+**
+** For example, if the WHERE clause expression used these VDBE
+** cursors: 4, 5, 8, 29, 57, 73. Then the WhereMaskSet structure
+** would map those cursor numbers into bits 0 through 5.
+**
+** Note that the mapping is not necessarily ordered. In the example
+** above, the mapping might go like this: 4->3, 5->1, 8->2, 29->0,
+** 57->5, 73->4. Or one of 719 other combinations might be used. It
+** does not really matter. What is important is that sparse cursor
+** numbers all get mapped into bit numbers that begin with 0 and contain
+** no gaps.
+*/
+struct WhereMaskSet {
+ int n; /* Number of assigned cursor values */
+ int ix[BMS]; /* Cursor assigned to each bit */
+};
+
+/*
+** This object is a convenience wrapper holding all information needed
+** to construct WhereLoop objects for a particular query.
+*/
+struct WhereLoopBuilder {
+ WhereInfo *pWInfo; /* Information about this WHERE */
+ WhereClause *pWC; /* WHERE clause terms */
+ ExprList *pOrderBy; /* ORDER BY clause */
+ WhereLoop *pNew; /* Template WhereLoop */
+ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ UnpackedRecord *pRec; /* Probe for stat4 (if required) */
+ int nRecValid; /* Number of valid fields currently in pRec */
+#endif
+};
+
+/*
+** 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
+** this structure is returned by the first half and passed
+** into the second half to give some continuity.
+**
+** An instance of this object holds the complete state of the query
+** planner.
+*/
+struct WhereInfo {
+ Parse *pParse; /* Parsing and code generating context */
+ SrcList *pTabList; /* List of tables in the join */
+ ExprList *pOrderBy; /* The ORDER BY clause or NULL */
+ ExprList *pResultSet; /* Result set. DISTINCT operates on these */
+ WhereLoop *pLoops; /* List of all WhereLoop objects */
+ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
+ LogEst nRowOut; /* Estimated number of output rows */
+ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
+ i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
+ u8 sorted; /* True if really sorted (not just grouped) */
+ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
+ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
+ u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
+ u8 nLevel; /* Number of nested loop */
+ int iTop; /* The very beginning of the WHERE loop */
+ int iContinue; /* Jump here to continue with next record */
+ int iBreak; /* Jump here to break out of the loop */
+ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
+ int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
+ WhereClause sWC; /* Decomposition of the WHERE clause */
+ WhereLevel a[1]; /* Information about each nest loop in WHERE */
+};
+
+/*
+** Bitmasks for the operators on WhereTerm objects. These are all
+** operators that are of interest to the query planner. An
+** OR-ed combination of these values can be used when searching for
+** particular WhereTerms within a WhereClause.
+*/
+#define WO_IN 0x001
+#define WO_EQ 0x002
+#define WO_LT (WO_EQ<<(TK_LT-TK_EQ))
+#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
+#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
+#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
+#define WO_MATCH 0x040
+#define WO_ISNULL 0x080
+#define WO_OR 0x100 /* Two or more OR-connected terms */
+#define WO_AND 0x200 /* Two or more AND-connected terms */
+#define WO_EQUIV 0x400 /* Of the form A==B, both columns */
+#define WO_NOOP 0x800 /* This term does not restrict search space */
+
+#define WO_ALL 0xfff /* Mask of all possible WO_* values */
+#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
+
+/*
+** These are definitions of bits in the WhereLoop.wsFlags field.
+** The particular combination of bits in each WhereLoop help to
+** determine the algorithm that WhereLoop represents.
+*/
+#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR */
+#define WHERE_COLUMN_RANGE 0x00000002 /* x<EXPR and/or x>EXPR */
+#define WHERE_COLUMN_IN 0x00000004 /* x IN (...) */
+#define WHERE_COLUMN_NULL 0x00000008 /* x IS NULL */
+#define WHERE_CONSTRAINT 0x0000000f /* Any of the WHERE_COLUMN_xxx values */
+#define WHERE_TOP_LIMIT 0x00000010 /* x<EXPR or x<=EXPR constraint */
+#define WHERE_BTM_LIMIT 0x00000020 /* x>EXPR or x>=EXPR constraint */
+#define WHERE_BOTH_LIMIT 0x00000030 /* Both x>EXPR and x<EXPR */
+#define WHERE_IDX_ONLY 0x00000040 /* Use index only - omit table */
+#define WHERE_IPK 0x00000100 /* x is the INTEGER PRIMARY KEY */
+#define WHERE_INDEXED 0x00000200 /* WhereLoop.u.btree.pIndex is valid */
+#define WHERE_VIRTUALTABLE 0x00000400 /* WhereLoop.u.vtab is valid */
+#define WHERE_IN_ABLE 0x00000800 /* Able to support an IN operator */
+#define WHERE_ONEROW 0x00001000 /* Selects no more than one row */
+#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */
+#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
+#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
+#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
diff --git a/test/all.test b/test/all.test
index 54302d0..f6e722f 100644
--- a/test/all.test
+++ b/test/all.test
@@ -39,6 +39,7 @@ run_test_suite pcache50
run_test_suite pcache90
run_test_suite pcache100
run_test_suite prepare
+run_test_suite mmap
if {$::tcl_platform(platform)=="unix"} {
ifcapable !default_autovacuum {
@@ -47,5 +48,3 @@ if {$::tcl_platform(platform)=="unix"} {
}
finish_test
-
-
diff --git a/test/alter.test b/test/alter.test
index aca71c4..ddf1698 100644
--- a/test/alter.test
+++ b/test/alter.test
@@ -847,7 +847,8 @@ do_test alter-14.2 {
set system_table_list {1 sqlite_master}
catchsql ANALYZE
ifcapable analyze { lappend system_table_list 2 sqlite_stat1 }
-ifcapable stat3 { lappend system_table_list 4 sqlite_stat3 }
+ifcapable stat3 { lappend system_table_list 3 sqlite_stat3 }
+ifcapable stat4 { lappend system_table_list 4 sqlite_stat4 }
foreach {tn tbl} $system_table_list {
do_test alter-15.$tn.1 {
@@ -859,5 +860,57 @@ foreach {tn tbl} $system_table_list {
} [list 1 "table $tbl may not be altered"]
}
+#------------------------------------------------------------------------
+# Verify that ALTER TABLE works on tables with the WITHOUT rowid option.
+#
+do_execsql_test alter-16.1 {
+ CREATE TABLE t16a(a TEXT, b REAL, c INT, PRIMARY KEY(a,b)) WITHOUT rowid;
+ INSERT INTO t16a VALUES('abc',1.25,99);
+ ALTER TABLE t16a ADD COLUMN d TEXT DEFAULT 'xyzzy';
+ INSERT INTO t16a VALUES('cba',5.5,98,'fizzle');
+ SELECT * FROM t16a ORDER BY a;
+} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
+do_execsql_test alter-16.2 {
+ ALTER TABLE t16a RENAME TO t16a_rn;
+ SELECT * FROM t16a_rn ORDER BY a;
+} {abc 1.25 99 xyzzy cba 5.5 98 fizzle}
+
+#-------------------------------------------------------------------------
+# Verify that NULL values into the internal-use-only sqlite_rename_*()
+# functions do not cause problems.
+#
+do_execsql_test alter-17.1 {
+ SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)','abc');
+} {{CREATE TABLE "abc"(a,b,c)}}
+do_execsql_test alter-17.2 {
+ SELECT sqlite_rename_table('CREATE TABLE xyz(a,b,c)',NULL);
+} {{CREATE TABLE "(NULL)"(a,b,c)}}
+do_execsql_test alter-17.3 {
+ SELECT sqlite_rename_table(NULL,'abc');
+} {{}}
+do_execsql_test alter-17.4 {
+ SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN','abc');
+} {{CREATE TRIGGER r1 ON "abc" WHEN}}
+do_execsql_test alter-17.5 {
+ SELECT sqlite_rename_trigger('CREATE TRIGGER r1 ON xyz WHEN',NULL);
+} {{CREATE TRIGGER r1 ON "(NULL)" WHEN}}
+do_execsql_test alter-17.6 {
+ SELECT sqlite_rename_trigger(NULL,'abc');
+} {{}}
+do_execsql_test alter-17.7 {
+ SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
+ 'xyzzy','lmnop');
+} {{CREATE TABLE t1(a REFERENCES "lmnop")}}
+do_execsql_test alter-17.8 {
+ SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
+ 'xyzzy',NULL);
+} {{CREATE TABLE t1(a REFERENCES "(NULL)")}}
+do_execsql_test alter-17.9 {
+ SELECT sqlite_rename_parent('CREATE TABLE t1(a REFERENCES "xyzzy")',
+ NULL, 'lmnop');
+} {{}}
+do_execsql_test alter-17.10 {
+ SELECT sqlite_rename_parent(NULL,'abc','xyz');
+} {{}}
finish_test
diff --git a/test/alter4.test b/test/alter4.test
index cda4553..eaad37e 100644
--- a/test/alter4.test
+++ b/test/alter4.test
@@ -143,6 +143,11 @@ do_test alter4-2.6 {
alter table t1 add column d DEFAULT CURRENT_TIME;
}
} {1 {Cannot add a column with non-constant default}}
+do_test alter4-2.7 {
+ catchsql {
+ alter table t1 add column d default (-+1);
+ }
+} {1 {Cannot add a column with non-constant default}}
do_test alter4-2.99 {
execsql {
DROP TABLE t1;
@@ -329,4 +334,25 @@ do_test alter4-8.2 {
}
} [list $::sql]
+
+# Test that a default value equal to -1 multipied by the smallest possible
+# 64-bit integer is correctly converted to a real.
+do_execsql_test alter4-9.1 {
+ CREATE TABLE t5(
+ a INTEGER DEFAULT -9223372036854775808,
+ b INTEGER DEFAULT (-(-9223372036854775808))
+ );
+ INSERT INTO t5 DEFAULT VALUES;
+}
+
+do_execsql_test alter4-9.2 { SELECT typeof(a), a, typeof(b), b FROM t5; } {
+ integer -9223372036854775808
+ real 9.22337203685478e+18
+}
+
+do_execsql_test alter4-9.3 {
+ ALTER TABLE t5 ADD COLUMN c INTEGER DEFAULT (-(-9223372036854775808));
+ SELECT typeof(c), c FROM t5;
+} {real 9.22337203685478e+18}
+
finish_test
diff --git a/test/amatch1.test b/test/amatch1.test
new file mode 100644
index 0000000..cc0f77a
--- /dev/null
+++ b/test/amatch1.test
@@ -0,0 +1,117 @@
+# 2013-09-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 "approximate_match" virtual
+# table that is in the "amatch.c" extension.
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# If SQLITE_ENABLE_FTS4 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Create the fts_kjv_genesis procedure which fills and FTS3/4 table with
+# the complete text of the Book of Genesis.
+#
+source $testdir/genesis.tcl
+
+
+
+do_test amatch1-1.0 {
+ db eval {
+ CREATE VIRTUAL TABLE t1 USING fts4(words); --, tokenize porter);
+ }
+ fts_kjv_genesis
+ db eval {
+ INSERT INTO t1(t1) VALUES('optimize');
+ CREATE VIRTUAL TABLE temp.t1aux USING fts4aux(main, t1);
+ SELECT term FROM t1aux WHERE col=0 ORDER BY 1 LIMIT 5
+ }
+} {a abated abel abelmizraim abidah}
+do_test amatch1-1.1 {
+ db eval {
+ SELECT term FROM t1aux WHERE term>'b' AND col=0 ORDER BY 1 LIMIT 5
+ }
+} {baalhanan babel back backward bad}
+do_test amatch1-1.2 {
+ db eval {
+ SELECT term FROM t1aux WHERE term>'b' AND col=0 LIMIT 5
+ }
+} {baalhanan babel back backward bad}
+
+# Load the amatch extension
+load_static_extension db amatch
+
+do_execsql_test amatch1-2.0 {
+ CREATE TABLE costs(iLang, cFrom, cTo, Cost);
+ INSERT INTO costs VALUES(0, '', '?', 100);
+ INSERT INTO costs VALUES(0, '?', '', 100);
+ INSERT INTO costs VALUES(0, '?', '?', 150);
+ CREATE TABLE vocab(w TEXT UNIQUE);
+ INSERT OR IGNORE INTO vocab SELECT term FROM t1aux;
+ CREATE VIRTUAL TABLE t2 USING approximate_match(
+ vocabulary_table=t1aux,
+ vocabulary_word=term,
+ edit_distances=costs
+ );
+ CREATE VIRTUAL TABLE t3 USING approximate_match(
+ vocabulary_table=vocab,
+ vocabulary_word=w,
+ edit_distances=costs
+ );
+ CREATE VIRTUAL TABLE t4 USING approximate_match(
+ vocabulary_table=vtemp,
+ vocabulary_word=w,
+ edit_distances=costs
+ );
+} {}
+puts "Query against fts4aux: [time {
+ do_execsql_test amatch1-2.1 {
+ SELECT word, distance FROM t2
+ WHERE word MATCH 'josxph' AND distance<300;
+ } {joseph 150}} 1]"
+puts "Query against ordinary table: [time {
+ do_execsql_test amatch1-2.2 {
+ SELECT word, distance FROM t3
+ WHERE word MATCH 'josxph' AND distance<300;
+ } {joseph 150}} 1]"
+puts "Temp table initialized from fts4aux: [time {
+ do_execsql_test amatch1-2.3a {
+ CREATE TEMP TABLE vtemp(w TEXT UNIQUE);
+ INSERT OR IGNORE INTO vtemp SELECT term FROM t1aux;
+ } {}} 1]"
+puts "Query against temp table: [time {
+ do_execsql_test amatch1-2.3b {
+ SELECT word, distance FROM t4
+ WHERE word MATCH 'josxph' AND distance<300;
+ } {joseph 150}} 1]"
+do_execsql_test amatch1-2.11 {
+ SELECT word, distance FROM t2
+ WHERE word MATCH 'joxxph' AND distance<=300;
+} {joseph 300}
+do_execsql_test amatch1-2.12 {
+ SELECT word, distance FROM t3
+ WHERE word MATCH 'joxxph' AND distance<=300;
+} {joseph 300}
+do_execsql_test amatch1-2.21 {
+ SELECT word, distance FROM t2
+ WHERE word MATCH 'joxxph' AND distance<300;
+} {}
+do_execsql_test amatch1-2.22 {
+ SELECT word, distance FROM t3
+ WHERE word MATCH 'joxxph' AND distance<300;
+} {}
+
+finish_test
diff --git a/test/analyze.test b/test/analyze.test
index 362702a..c445084 100644
--- a/test/analyze.test
+++ b/test/analyze.test
@@ -288,7 +288,7 @@ do_test analyze-4.3 {
} {}
# Verify that DROP TABLE and DROP INDEX remove entries from the
-# sqlite_stat1 and sqlite_stat3 tables.
+# sqlite_stat1, sqlite_stat3 and sqlite_stat4 tables.
#
do_test analyze-5.0 {
execsql {
@@ -306,12 +306,13 @@ do_test analyze-5.0 {
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
-ifcapable stat3 {
+ifcapable stat4||stat3 {
+ ifcapable stat4 {set stat sqlite_stat4} else {set stat sqlite_stat3}
do_test analyze-5.1 {
- execsql {
- SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
- SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
- }
+ execsql "
+ SELECT DISTINCT idx FROM $stat ORDER BY 1;
+ SELECT DISTINCT tbl FROM $stat ORDER BY 1;
+ "
} {t3i1 t3i2 t3i3 t4i1 t4i2 t3 t4}
}
do_test analyze-5.2 {
@@ -321,12 +322,12 @@ do_test analyze-5.2 {
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
-ifcapable stat3 {
+ifcapable stat4||stat3 {
do_test analyze-5.3 {
- execsql {
- SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
- SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
- }
+ execsql "
+ SELECT DISTINCT idx FROM $stat ORDER BY 1;
+ SELECT DISTINCT tbl FROM $stat ORDER BY 1;
+ "
} {t3i1 t3i3 t4i1 t4i2 t3 t4}
}
do_test analyze-5.4 {
@@ -336,12 +337,12 @@ do_test analyze-5.4 {
SELECT DISTINCT tbl FROM sqlite_stat1 ORDER BY 1;
}
} {t4i1 t4i2 t4}
-ifcapable stat3 {
+ifcapable stat4||stat3 {
do_test analyze-5.5 {
- execsql {
- SELECT DISTINCT idx FROM sqlite_stat3 ORDER BY 1;
- SELECT DISTINCT tbl FROM sqlite_stat3 ORDER BY 1;
- }
+ execsql "
+ SELECT DISTINCT idx FROM $stat ORDER BY 1;
+ SELECT DISTINCT tbl FROM $stat ORDER BY 1;
+ "
} {t4i1 t4i2 t4}
}
@@ -360,5 +361,4 @@ do_test analyze-99.1 {
}
} {1 {malformed database schema (sqlite_stat1) - near "nonsense": syntax error}}
-
finish_test
diff --git a/test/analyze3.test b/test/analyze3.test
index f705bc6..e7416d5 100644
--- a/test/analyze3.test
+++ b/test/analyze3.test
@@ -17,7 +17,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-ifcapable !stat3 {
+ifcapable !stat4&&!stat3 {
finish_test
return
}
@@ -43,6 +43,8 @@ ifcapable !stat3 {
# analyze3-5.*: Check that the query plans of applicable statements are
# invalidated if the values of SQL parameter are modified
# using the clear_bindings() or transfer_bindings() APIs.
+#
+# analyze3-6.*: Test that the problem fixed by commit [127a5b776d] is fixed.
#
proc getvar {varname} { uplevel #0 set $varname }
@@ -93,14 +95,29 @@ do_test analyze3-1.1.1 {
COMMIT;
ANALYZE;
}
-} {}
+ ifcapable stat4 {
+ execsql { SELECT count(*)>0 FROM sqlite_stat4; }
+ } else {
+ execsql { SELECT count(*)>0 FROM sqlite_stat3; }
+ }
+} {1}
+
+do_execsql_test analyze3-1.1.x {
+ SELECT count(*) FROM t1 WHERE x>200 AND x<300;
+ SELECT count(*) FROM t1 WHERE x>0 AND x<1100;
+} {99 1000}
+
+# The first of the following two SELECT statements visits 99 rows. So
+# it is better to use the index. But the second visits every row in
+# the table (1000 in total) so it is better to do a full-table scan.
+#
do_eqp_test analyze3-1.1.2 {
SELECT sum(y) FROM t1 WHERE x>200 AND x<300
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~179 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
do_eqp_test analyze3-1.1.3 {
SELECT sum(y) FROM t1 WHERE x>0 AND x<1100
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?) (~959 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_test analyze3-1.1.4 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
@@ -117,17 +134,17 @@ do_test analyze3-1.1.6 {
} {199 0 14850}
do_test analyze3-1.1.7 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>0 AND x<1100 }
-} {2000 0 499500}
+} {999 999 499500}
do_test analyze3-1.1.8 {
set l [string range "0" 0 end]
set u [string range "1100" 0 end]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
-} {2000 0 499500}
+} {999 999 499500}
do_test analyze3-1.1.9 {
set l [expr int(0)]
set u [expr int(1100)]
sf_execsql { SELECT sum(y) FROM t1 WHERE x>$l AND x<$u }
-} {2000 0 499500}
+} {999 999 499500}
# The following tests are similar to the block above. The difference is
@@ -144,12 +161,17 @@ do_test analyze3-1.2.1 {
ANALYZE;
}
} {}
+do_execsql_test analyze3-2.1.x {
+ SELECT count(*) FROM t2 WHERE x>1 AND x<2;
+ SELECT count(*) FROM t2 WHERE x>0 AND x<99;
+} {200 990}
do_eqp_test analyze3-1.2.2 {
SELECT sum(y) FROM t2 WHERE x>1 AND x<2
-} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~196 rows)}}
+} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?)}}
do_eqp_test analyze3-1.2.3 {
SELECT sum(y) FROM t2 WHERE x>0 AND x<99
-} {0 0 0 {SEARCH TABLE t2 USING INDEX i2 (x>? AND x<?) (~968 rows)}}
+} {0 0 0 {SCAN TABLE t2}}
+
do_test analyze3-1.2.4 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
} {161 0 4760}
@@ -165,17 +187,17 @@ do_test analyze3-1.2.6 {
} {161 0 integer integer 4760}
do_test analyze3-1.2.7 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>0 AND x<99 }
-} {1981 0 490555}
+} {999 999 490555}
do_test analyze3-1.2.8 {
set l [string range "0" 0 end]
set u [string range "99" 0 end]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
-} {1981 0 text text 490555}
+} {999 999 text text 490555}
do_test analyze3-1.2.9 {
set l [expr int(0)]
set u [expr int(99)]
sf_execsql {SELECT typeof($l), typeof($u), sum(y) FROM t2 WHERE x>$l AND x<$u}
-} {1981 0 integer integer 490555}
+} {999 999 integer integer 490555}
# Same tests a third time. This time, column x has INTEGER affinity and
# is not the leftmost column of the table. This triggered a bug causing
@@ -191,12 +213,16 @@ do_test analyze3-1.3.1 {
ANALYZE;
}
} {}
+do_execsql_test analyze3-1.3.x {
+ SELECT count(*) FROM t3 WHERE x>200 AND x<300;
+ SELECT count(*) FROM t3 WHERE x>0 AND x<1100
+} {99 1000}
do_eqp_test analyze3-1.3.2 {
SELECT sum(y) FROM t3 WHERE x>200 AND x<300
-} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~156 rows)}}
+} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?)}}
do_eqp_test analyze3-1.3.3 {
SELECT sum(y) FROM t3 WHERE x>0 AND x<1100
-} {0 0 0 {SEARCH TABLE t3 USING INDEX i3 (x>? AND x<?) (~989 rows)}}
+} {0 0 0 {SCAN TABLE t3}}
do_test analyze3-1.3.4 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
@@ -213,17 +239,17 @@ do_test analyze3-1.3.6 {
} {199 0 14850}
do_test analyze3-1.3.7 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>0 AND x<1100 }
-} {2000 0 499500}
+} {999 999 499500}
do_test analyze3-1.3.8 {
set l [string range "0" 0 end]
set u [string range "1100" 0 end]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
-} {2000 0 499500}
+} {999 999 499500}
do_test analyze3-1.3.9 {
set l [expr int(0)]
set u [expr int(1100)]
sf_execsql { SELECT sum(y) FROM t3 WHERE x>$l AND x<$u }
-} {2000 0 499500}
+} {999 999 499500}
#-------------------------------------------------------------------------
# Test that the values of bound SQL variables may be used for the LIKE
@@ -248,10 +274,10 @@ do_test analyze3-2.1 {
} {}
do_eqp_test analyze3-2.2 {
SELECT count(a) FROM t1 WHERE b LIKE 'a%'
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?) (~31250 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b>? AND b<?)}}
do_eqp_test analyze3-2.3 {
SELECT count(a) FROM t1 WHERE b LIKE '%a'
-} {0 0 0 {SCAN TABLE t1 (~500000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_test analyze3-2.4 {
sf_execsql { SELECT count(*) FROM t1 WHERE b LIKE 'a%' }
@@ -310,7 +336,6 @@ do_test analyze3-3.1 {
execsql COMMIT
execsql ANALYZE
} {}
-
do_test analyze3-3.2.1 {
set S [sqlite3_prepare_v2 db "SELECT * FROM t1 WHERE b>?" -1 dummy]
sqlite3_expired $S
@@ -330,7 +355,7 @@ do_test analyze3-3.2.5 {
do_test analyze3-3.2.6 {
sqlite3_bind_text $S 1 "abc" 3
sqlite3_expired $S
-} {0}
+} {1}
do_test analyze3-3.2.7 {
sqlite3_finalize $S
} {SQLITE_OK}
@@ -612,4 +637,29 @@ do_test analyze3-5.1.3 {
sqlite3_finalize $S1
} {SQLITE_OK}
+#-------------------------------------------------------------------------
+
+do_test analyze3-6.1 {
+ execsql { DROP TABLE IF EXISTS t1 }
+ execsql BEGIN
+ execsql { CREATE TABLE t1(a, b, c) }
+ for {set i 0} {$i < 1000} {incr i} {
+ execsql "INSERT INTO t1 VALUES([expr $i/100], 'x', [expr $i/10])"
+ }
+ execsql {
+ CREATE INDEX i1 ON t1(a, b);
+ CREATE INDEX i2 ON t1(c);
+ }
+ execsql COMMIT
+ execsql ANALYZE
+} {}
+
+do_eqp_test analyze3-6-3 {
+ SELECT * FROM t1 WHERE a = 5 AND c = 13;
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}}
+
+do_eqp_test analyze3-6-2 {
+ SELECT * FROM t1 WHERE a = 5 AND b > 'w' AND c = 13;
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}}
+
finish_test
diff --git a/test/analyze4.test b/test/analyze4.test
index 1fed564..974ed89 100644
--- a/test/analyze4.test
+++ b/test/analyze4.test
@@ -38,7 +38,7 @@ do_test analyze4-1.0 {
# Should choose the t1a index since it is more specific than t1b.
db eval {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=5 AND b IS NULL}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
# Verify that the t1b index shows that it does not narrow down the
# search any at all.
diff --git a/test/analyze5.test b/test/analyze5.test
index 1041d70..ac175c0 100644
--- a/test/analyze5.test
+++ b/test/analyze5.test
@@ -10,14 +10,14 @@
#***********************************************************************
#
# This file implements tests for SQLite library. The focus of the tests
-# in this file is the use of the sqlite_stat3 histogram data on tables
+# in this file is the use of the sqlite_stat4 histogram data on tables
# with many repeated values and only a few distinct values.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-ifcapable !stat3 {
+ifcapable !stat4&&!stat3 {
finish_test
return
}
@@ -28,6 +28,17 @@ proc eqp {sql {db db}} {
uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db
}
+proc alpha {blob} {
+ set ret ""
+ foreach c [split $blob {}] {
+ if {[string is alpha $c]} {append ret $c}
+ }
+ return $ret
+}
+db func alpha alpha
+
+db func lindex lindex
+
unset -nocomplain i t u v w x y z
do_test analyze5-1.0 {
db eval {CREATE TABLE t1(t,u,v TEXT COLLATE nocase,w,x,y,z)}
@@ -55,17 +66,40 @@ do_test analyze5-1.0 {
CREATE INDEX t1y ON t1(y); -- integers 0 and very few 1s
CREATE INDEX t1z ON t1(z); -- integers 0, 1, 2, and 3
ANALYZE;
- SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
+ }
+ ifcapable stat4 {
+ db eval {
+ SELECT DISTINCT lindex(test_decode(sample),0)
+ FROM sqlite_stat4 WHERE idx='t1u' ORDER BY nlt;
+ }
+ } else {
+ db eval {
+ SELECT sample FROM sqlite_stat3 WHERE idx='t1u' ORDER BY nlt;
+ }
}
} {alpha bravo charlie delta}
do_test analyze5-1.1 {
- db eval {SELECT DISTINCT lower(sample) FROM sqlite_stat3 WHERE idx='t1v'
- ORDER BY 1}
+ ifcapable stat4 {
+ db eval {
+ SELECT DISTINCT lower(lindex(test_decode(sample), 0))
+ FROM sqlite_stat4 WHERE idx='t1v' ORDER BY 1
+ }
+ } else {
+ db eval {
+ SELECT lower(sample) FROM sqlite_stat3 WHERE idx='t1v' ORDER BY 1
+ }
+ }
} {alpha bravo charlie delta}
-do_test analyze5-1.2 {
- db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
-} {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
+ifcapable stat4 {
+ do_test analyze5-1.2 {
+ db eval {SELECT idx, count(*) FROM sqlite_stat4 GROUP BY 1 ORDER BY 1}
+ } {t1t 8 t1u 8 t1v 8 t1w 8 t1x 8 t1y 9 t1z 8}
+} else {
+ do_test analyze5-1.2 {
+ db eval {SELECT idx, count(*) FROM sqlite_stat3 GROUP BY 1 ORDER BY 1}
+ } {t1t 4 t1u 4 t1v 4 t1w 4 t1x 4 t1y 2 t1z 4}
+}
# Verify that range queries generate the correct row count estimates
#
@@ -156,13 +190,14 @@ foreach {testid where index rows} {
} {
# Verify that the expected index is used with the expected row count
- do_test analyze5-1.${testid}a {
- set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
- set idx {}
- regexp {INDEX (t1.) } $x all idx
- regexp {~([0-9]+) rows} $x all nrow
- list $idx $nrow
- } [list $index $rows]
+ # No longer valid due to an EXPLAIN QUERY PLAN output format change
+ # do_test analyze5-1.${testid}a {
+ # set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
+ # set idx {}
+ # regexp {INDEX (t1.) } $x all idx
+ # regexp {~([0-9]+) rows} $x all nrow
+ # list $idx $nrow
+ # } [list $index $rows]
# Verify that the same result is achieved regardless of whether or not
# the index is used
@@ -202,15 +237,14 @@ foreach {testid where index rows} {
} {
# Verify that the expected index is used with the expected row count
-if {$testid==50299} {breakpoint; set sqlite_where_trace 1}
- do_test analyze5-1.${testid}a {
- set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
- set idx {}
- regexp {INDEX (t1.) } $x all idx
- regexp {~([0-9]+) rows} $x all nrow
- list $idx $nrow
- } [list $index $rows]
-if {$testid==50299} exit
+ # No longer valid due to an EXPLAIN QUERY PLAN format change
+ # do_test analyze5-1.${testid}a {
+ # set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3]
+ # set idx {}
+ # regexp {INDEX (t1.) } $x all idx
+ # regexp {~([0-9]+) rows} $x all nrow
+ # list $idx $nrow
+ # } [list $index $rows]
# Verify that the same result is achieved regardless of whether or not
# the index is used
diff --git a/test/analyze6.test b/test/analyze6.test
index eaa9d73..31ace8e 100644
--- a/test/analyze6.test
+++ b/test/analyze6.test
@@ -17,7 +17,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-ifcapable !stat3 {
+ifcapable !stat4&&!stat3 {
finish_test
return
}
@@ -30,14 +30,14 @@ proc eqp {sql {db db}} {
do_test analyze6-1.0 {
db eval {
- CREATE TABLE cat(x INT);
+ CREATE TABLE cat(x INT, yz TEXT);
CREATE UNIQUE INDEX catx ON cat(x);
/* Give cat 16 unique integers */
- INSERT INTO cat VALUES(1);
- INSERT INTO cat VALUES(2);
- INSERT INTO cat SELECT x+2 FROM cat;
- INSERT INTO cat SELECT x+4 FROM cat;
- INSERT INTO cat SELECT x+8 FROM cat;
+ INSERT INTO cat(x) VALUES(1);
+ INSERT INTO cat(x) VALUES(2);
+ INSERT INTO cat(x) SELECT x+2 FROM cat;
+ INSERT INTO cat(x) SELECT x+4 FROM cat;
+ INSERT INTO cat(x) SELECT x+8 FROM cat;
CREATE TABLE ev(y INT);
CREATE INDEX evy ON ev(y);
@@ -61,14 +61,14 @@ do_test analyze6-1.0 {
#
do_test analyze6-1.1 {
eqp {SELECT count(*) FROM ev, cat WHERE x=y}
-} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}}
+} {0 0 1 {SCAN TABLE cat USING COVERING INDEX catx} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}}
# The same plan is chosen regardless of the order of the tables in the
# FROM clause.
#
do_test analyze6-1.2 {
eqp {SELECT count(*) FROM cat, ev WHERE x=y}
-} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}}
+} {0 0 0 {SCAN TABLE cat USING COVERING INDEX catx} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}}
# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30
@@ -82,26 +82,26 @@ do_test analyze6-2.1 {
ANALYZE;
}
eqp {SELECT * FROM t201 WHERE z=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}}
do_test analyze6-2.2 {
eqp {SELECT * FROM t201 WHERE y=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}}
do_test analyze6-2.3 {
eqp {SELECT * FROM t201 WHERE x=5}
-} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}}
do_test analyze6-2.4 {
execsql {
- INSERT INTO t201 VALUES(1,2,3);
+ INSERT INTO t201 VALUES(1,2,3),(2,3,4),(3,4,5);
ANALYZE t201;
}
eqp {SELECT * FROM t201 WHERE z=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}}
do_test analyze6-2.5 {
eqp {SELECT * FROM t201 WHERE y=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}}
do_test analyze6-2.6 {
eqp {SELECT * FROM t201 WHERE x=5}
-} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}}
do_test analyze6-2.7 {
execsql {
INSERT INTO t201 VALUES(4,5,7);
@@ -111,12 +111,12 @@ do_test analyze6-2.7 {
ANALYZE t201;
}
eqp {SELECT * FROM t201 WHERE z=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}}
do_test analyze6-2.8 {
eqp {SELECT * FROM t201 WHERE y=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INDEX sqlite_autoindex_t201_1 (y=?)}}
do_test analyze6-2.9 {
eqp {SELECT * FROM t201 WHERE x=5}
-} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)}}
finish_test
diff --git a/test/analyze7.test b/test/analyze7.test
index 46ec39e..7666454 100644
--- a/test/analyze7.test
+++ b/test/analyze7.test
@@ -37,13 +37,13 @@ do_test analyze7-1.0 {
WHERE value BETWEEN 1 AND 256;
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;
}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test analyze7-1.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test analyze7-1.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
# Run an analyze on one of the three indices. Verify that this
# effects the row-count estimate on the one query that uses that
@@ -53,20 +53,20 @@ do_test analyze7-2.0 {
execsql {ANALYZE t1a;}
db cache flush
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test analyze7-2.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test analyze7-2.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
# Verify that since the query planner now things that t1a is more
# selective than t1b, it prefers to use t1a.
#
do_test analyze7-2.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
# Run an analysis on another of the three indices. Verify that this
# new analysis works and does not disrupt the previous analysis.
@@ -75,39 +75,40 @@ do_test analyze7-3.0 {
execsql {ANALYZE t1cd;}
db cache flush;
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test analyze7-3.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b=123;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test analyze7-3.2.1 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=?;}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
-ifcapable stat3 {
- # If ENABLE_STAT3 is defined, SQLite comes up with a different estimated
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
+ifcapable stat4||stat3 {
+ # If ENABLE_STAT4 is defined, SQLite comes up with a different estimated
# row count for (c=2) than it does for (c=?).
do_test analyze7-3.2.2 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~57 rows)}}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
} else {
- # If ENABLE_STAT3 is not defined, the expected row count for (c=2) is the
+ # If ENABLE_STAT4 is not defined, the expected row count for (c=2) is the
# same as that for (c=?).
do_test analyze7-3.2.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;}
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~86 rows)}}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?)}}
}
do_test analyze7-3.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
-ifcapable {!stat3} {
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
+
+ifcapable {!stat4 && !stat3} {
do_test analyze7-3.4 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test analyze7-3.5 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
}
do_test analyze7-3.6 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND d=123 AND b=123}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?)}}
finish_test
diff --git a/test/analyze8.test b/test/analyze8.test
index f3e2710..4384c39 100644
--- a/test/analyze8.test
+++ b/test/analyze8.test
@@ -16,7 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-ifcapable !stat3 {
+ifcapable !stat4&&!stat3 {
finish_test
return
}
@@ -61,43 +61,55 @@ do_test 1.0 {
#
do_test 1.1 {
eqp {SELECT * FROM t1 WHERE a=100 AND b=55}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test 1.2 {
eqp {SELECT * FROM t1 WHERE a=99 AND b=55}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test 1.3 {
eqp {SELECT * FROM t1 WHERE a=101 AND b=55}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test 1.4 {
eqp {SELECT * FROM t1 WHERE a=100 AND b=56}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
do_test 1.5 {
eqp {SELECT * FROM t1 WHERE a=99 AND b=56}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test 1.6 {
eqp {SELECT * FROM t1 WHERE a=101 AND b=56}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test 2.1 {
eqp {SELECT * FROM t1 WHERE a=100 AND b BETWEEN 50 AND 54}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
# There are many more values of c between 0 and 100000 than there are
# between 800000 and 900000. So t1c is more selective for the latter
# range.
+#
+# Test 3.2 is a little unstable. It depends on the planner estimating
+# that (b BETWEEN 50 AND 54) will match more rows than (c BETWEEN
+# 800000 AND 900000). Which is a pretty close call (50 vs. 32), so
+# the planner could get it wrong with an unlucky set of samples. This
+# case happens to work, but others ("b BETWEEN 40 AND 44" for example)
+# will fail.
#
+do_execsql_test 3.0 {
+ SELECT count(*) FROM t1 WHERE b BETWEEN 50 AND 54;
+ SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 100000;
+ SELECT count(*) FROM t1 WHERE c BETWEEN 800000 AND 900000;
+} {50 376 32}
do_test 3.1 {
eqp {SELECT * FROM t1 WHERE b BETWEEN 50 AND 54 AND c BETWEEN 0 AND 100000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?) (~6 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
do_test 3.2 {
eqp {SELECT * FROM t1
WHERE b BETWEEN 50 AND 54 AND c BETWEEN 800000 AND 900000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~4 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
do_test 3.3 {
eqp {SELECT * FROM t1 WHERE a=100 AND c BETWEEN 0 AND 100000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~63 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?)}}
do_test 3.4 {
eqp {SELECT * FROM t1
WHERE a=100 AND c BETWEEN 800000 AND 900000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
finish_test
diff --git a/test/analyze9.test b/test/analyze9.test
new file mode 100644
index 0000000..0d72658
--- /dev/null
+++ b/test/analyze9.test
@@ -0,0 +1,1091 @@
+# 2013 August 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 contains automated tests used to verify that the sqlite_stat4
+# functionality is working.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix analyze9
+
+ifcapable !stat4 {
+ finish_test
+ return
+}
+
+proc s {blob} {
+ set ret ""
+ binary scan $blob c* bytes
+ foreach b $bytes {
+ set t [binary format c $b]
+ if {[string is print $t]} {
+ append ret $t
+ } else {
+ append ret .
+ }
+ }
+ return $ret
+}
+db function s s
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a TEXT, b TEXT);
+ INSERT INTO t1 VALUES('(0)', '(0)');
+ INSERT INTO t1 VALUES('(1)', '(1)');
+ INSERT INTO t1 VALUES('(2)', '(2)');
+ INSERT INTO t1 VALUES('(3)', '(3)');
+ INSERT INTO t1 VALUES('(4)', '(4)');
+ CREATE INDEX i1 ON t1(a, b);
+} {}
+
+
+do_execsql_test 1.1 {
+ ANALYZE;
+} {}
+
+do_execsql_test 1.2 {
+ SELECT tbl,idx,nEq,nLt,nDLt,test_decode(sample) FROM sqlite_stat4;
+} {
+ t1 i1 {1 1 1} {0 0 0} {0 0 0} {(0) (0) 1}
+ t1 i1 {1 1 1} {1 1 1} {1 1 1} {(1) (1) 2}
+ t1 i1 {1 1 1} {2 2 2} {2 2 2} {(2) (2) 3}
+ t1 i1 {1 1 1} {3 3 3} {3 3 3} {(3) (3) 4}
+ t1 i1 {1 1 1} {4 4 4} {4 4 4} {(4) (4) 5}
+}
+
+if {[permutation] != "utf16"} {
+ do_execsql_test 1.3 {
+ SELECT tbl,idx,nEq,nLt,nDLt,s(sample) FROM sqlite_stat4;
+ } {
+ t1 i1 {1 1 1} {0 0 0} {0 0 0} ....(0)(0)
+ t1 i1 {1 1 1} {1 1 1} {1 1 1} ....(1)(1).
+ t1 i1 {1 1 1} {2 2 2} {2 2 2} ....(2)(2).
+ t1 i1 {1 1 1} {3 3 3} {3 3 3} ....(3)(3).
+ t1 i1 {1 1 1} {4 4 4} {4 4 4} ....(4)(4).
+ }
+}
+
+
+#-------------------------------------------------------------------------
+# This is really just to test SQL user function "test_decode".
+#
+reset_db
+do_execsql_test 2.1 {
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1 VALUES('some text', 14, NULL);
+ INSERT INTO t1 VALUES(22.0, NULL, x'656667');
+ CREATE INDEX i1 ON t1(a, b, c);
+ ANALYZE;
+ SELECT test_decode(sample) FROM sqlite_stat4;
+} {
+ {22.0 NULL x'656667' 2}
+ {{some text} 14 NULL 1}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 3.1 {
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i2 ON t2(a, b);
+ BEGIN;
+}
+
+do_test 3.2 {
+ for {set i 0} {$i < 1000} {incr i} {
+ set a [expr $i / 10]
+ set b [expr int(rand() * 15.0)]
+ execsql { INSERT INTO t2 VALUES($a, $b) }
+ }
+ execsql COMMIT
+} {}
+
+db func lindex lindex
+
+# Each value of "a" occurs exactly 10 times in the table.
+#
+do_execsql_test 3.3.1 {
+ SELECT count(*) FROM t2 GROUP BY a;
+} [lrange [string repeat "10 " 100] 0 99]
+
+# The first element in the "nEq" list of all samples should therefore be 10.
+#
+do_execsql_test 3.3.2 {
+ ANALYZE;
+ SELECT lindex(nEq, 0) FROM sqlite_stat4;
+} [lrange [string repeat "10 " 100] 0 23]
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 3.4 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 'one-a');
+ INSERT INTO t1 VALUES(11, 1, 'one-b');
+ INSERT INTO t1 VALUES(21, 1, 'one-c');
+ INSERT INTO t1 VALUES(31, 1, 'one-d');
+ INSERT INTO t1 VALUES(41, 1, 'one-e');
+ INSERT INTO t1 VALUES(51, 1, 'one-f');
+ INSERT INTO t1 VALUES(61, 1, 'one-g');
+ INSERT INTO t1 VALUES(71, 1, 'one-h');
+ INSERT INTO t1 VALUES(81, 1, 'one-i');
+ INSERT INTO t1 VALUES(91, 1, 'one-j');
+ INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1;
+ INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ CREATE INDEX t1b ON t1(b);
+ ANALYZE;
+ SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60;
+} {three-d three-e three-f}
+
+
+#-------------------------------------------------------------------------
+# These tests verify that the sample selection for stat4 appears to be
+# working as designed.
+#
+
+reset_db
+db func lindex lindex
+db func lrange lrange
+
+do_execsql_test 4.0 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a, b, c);
+ CREATE INDEX i1 ON t1(c, b, a);
+}
+
+
+proc insert_filler_rows_n {iStart args} {
+ set A(-ncopy) 1
+ set A(-nval) 1
+
+ foreach {k v} $args {
+ if {[info exists A($k)]==0} { error "no such option: $k" }
+ set A($k) $v
+ }
+ if {[llength $args] % 2} {
+ error "option requires an argument: [lindex $args end]"
+ }
+
+ for {set i 0} {$i < $A(-nval)} {incr i} {
+ set iVal [expr $iStart+$i]
+ for {set j 0} {$j < $A(-ncopy)} {incr j} {
+ execsql { INSERT INTO t1 VALUES($iVal, $iVal, $iVal) }
+ }
+ }
+}
+
+do_test 4.1 {
+ execsql { BEGIN }
+ insert_filler_rows_n 0 -ncopy 10 -nval 19
+ insert_filler_rows_n 20 -ncopy 1 -nval 100
+
+ execsql {
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'a');
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'b');
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'c');
+
+ INSERT INTO t1(c, b, a) VALUES(200, 2, 'e');
+ INSERT INTO t1(c, b, a) VALUES(200, 2, 'f');
+
+ INSERT INTO t1(c, b, a) VALUES(201, 3, 'g');
+ INSERT INTO t1(c, b, a) VALUES(201, 4, 'h');
+
+ ANALYZE;
+ SELECT count(*) FROM sqlite_stat4;
+ SELECT count(*) FROM t1;
+ }
+} {24 297}
+
+do_execsql_test 4.2 {
+ SELECT
+ neq,
+ lrange(nlt, 0, 2),
+ lrange(ndlt, 0, 2),
+ lrange(test_decode(sample), 0, 2)
+ FROM sqlite_stat4
+ ORDER BY rowid LIMIT 16;
+} {
+ {10 10 10 1} {0 0 0} {0 0 0} {0 0 0}
+ {10 10 10 1} {10 10 10} {1 1 1} {1 1 1}
+ {10 10 10 1} {20 20 20} {2 2 2} {2 2 2}
+ {10 10 10 1} {30 30 30} {3 3 3} {3 3 3}
+ {10 10 10 1} {40 40 40} {4 4 4} {4 4 4}
+ {10 10 10 1} {50 50 50} {5 5 5} {5 5 5}
+ {10 10 10 1} {60 60 60} {6 6 6} {6 6 6}
+ {10 10 10 1} {70 70 70} {7 7 7} {7 7 7}
+ {10 10 10 1} {80 80 80} {8 8 8} {8 8 8}
+ {10 10 10 1} {90 90 90} {9 9 9} {9 9 9}
+ {10 10 10 1} {100 100 100} {10 10 10} {10 10 10}
+ {10 10 10 1} {110 110 110} {11 11 11} {11 11 11}
+ {10 10 10 1} {120 120 120} {12 12 12} {12 12 12}
+ {10 10 10 1} {130 130 130} {13 13 13} {13 13 13}
+ {10 10 10 1} {140 140 140} {14 14 14} {14 14 14}
+ {10 10 10 1} {150 150 150} {15 15 15} {15 15 15}
+}
+
+do_execsql_test 4.3 {
+ SELECT
+ neq,
+ lrange(nlt, 0, 2),
+ lrange(ndlt, 0, 2),
+ lrange(test_decode(sample), 0, 1)
+ FROM sqlite_stat4
+ ORDER BY rowid DESC LIMIT 2;
+} {
+ {2 1 1 1} {295 296 296} {120 122 125} {201 4}
+ {5 3 1 1} {290 290 290} {119 119 119} {200 1}
+}
+
+do_execsql_test 4.4 { SELECT count(DISTINCT c) FROM t1 WHERE c<201 } 120
+do_execsql_test 4.5 { SELECT count(DISTINCT c) FROM t1 WHERE c<200 } 119
+
+# Check that the perioidic samples are present.
+do_execsql_test 4.6 {
+ SELECT count(*) FROM sqlite_stat4
+ WHERE lindex(test_decode(sample), 3) IN
+ ('34', '68', '102', '136', '170', '204', '238', '272')
+} {8}
+
+reset_db
+do_test 4.7 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(o,t INTEGER PRIMARY KEY);
+ CREATE INDEX i1 ON t1(o);
+ }
+ for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} {
+ execsql { INSERT INTO t1 VALUES('x', $i) }
+ }
+ execsql {
+ COMMIT;
+ ANALYZE;
+ SELECT count(*) FROM sqlite_stat4;
+ }
+} {8}
+do_execsql_test 4.8 {
+ SELECT test_decode(sample) FROM sqlite_stat4;
+} {
+ {x 211} {x 423} {x 635} {x 847}
+ {x 1590} {x 3710} {x 5830} {x 7950}
+}
+
+
+#-------------------------------------------------------------------------
+# The following would cause a crash at one point.
+#
+reset_db
+do_execsql_test 5.1 {
+ PRAGMA encoding = 'utf-16';
+ CREATE TABLE t0(v);
+ ANALYZE;
+}
+
+#-------------------------------------------------------------------------
+# This was also crashing (corrupt sqlite_stat4 table).
+#
+reset_db
+do_execsql_test 6.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+ INSERT INTO t1 VALUES(1, 1);
+ INSERT INTO t1 VALUES(2, 2);
+ INSERT INTO t1 VALUES(3, 3);
+ INSERT INTO t1 VALUES(4, 4);
+ INSERT INTO t1 VALUES(5, 5);
+ ANALYZE;
+ PRAGMA writable_schema = 1;
+ CREATE TEMP TABLE x1 AS
+ SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat4
+ ORDER BY (rowid%5), rowid;
+ DELETE FROM sqlite_stat4;
+ INSERT INTO sqlite_stat4 SELECT * FROM x1;
+ PRAGMA writable_schema = 0;
+ ANALYZE sqlite_master;
+}
+do_execsql_test 6.2 {
+ SELECT * FROM t1 WHERE a = 'abc';
+}
+
+#-------------------------------------------------------------------------
+# The following tests experiment with adding corrupted records to the
+# 'sample' column of the sqlite_stat4 table.
+#
+reset_db
+sqlite3_db_config_lookaside db 0 0 0
+
+database_may_be_corrupt
+do_execsql_test 7.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ INSERT INTO t1 VALUES(1, 1);
+ INSERT INTO t1 VALUES(2, 2);
+ INSERT INTO t1 VALUES(3, 3);
+ INSERT INTO t1 VALUES(4, 4);
+ INSERT INTO t1 VALUES(5, 5);
+ ANALYZE;
+ UPDATE sqlite_stat4 SET sample = X'' WHERE rowid = 1;
+ ANALYZE sqlite_master;
+}
+
+do_execsql_test 7.2 {
+ UPDATE sqlite_stat4 SET sample = X'FFFF';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 1;
+} {1 1}
+
+do_execsql_test 7.3 {
+ ANALYZE;
+ UPDATE sqlite_stat4 SET neq = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 1;
+} {1 1}
+
+do_execsql_test 7.4 {
+ ANALYZE;
+ UPDATE sqlite_stat4 SET ndlt = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 3;
+} {3 3}
+
+do_execsql_test 7.5 {
+ ANALYZE;
+ UPDATE sqlite_stat4 SET nlt = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 5;
+} {5 5}
+
+database_never_corrupt
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 8.1 {
+ CREATE TABLE t1(x TEXT);
+ CREATE INDEX i1 ON t1(x);
+ INSERT INTO t1 VALUES('1');
+ INSERT INTO t1 VALUES('2');
+ INSERT INTO t1 VALUES('3');
+ INSERT INTO t1 VALUES('4');
+ ANALYZE;
+}
+do_execsql_test 8.2 {
+ SELECT * FROM t1 WHERE x = 3;
+} {3}
+
+#-------------------------------------------------------------------------
+# Check that the bug fixed by [91733bc485] really is fixed.
+#
+reset_db
+do_execsql_test 9.1 {
+ CREATE TABLE t1(a, b, c, d, e);
+ CREATE INDEX i1 ON t1(a, b, c, d);
+ CREATE INDEX i2 ON t1(e);
+}
+do_test 9.2 {
+ execsql BEGIN;
+ for {set i 0} {$i < 100} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
+ }
+ for {set i 0} {$i < 20} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', 101, $i)"
+ }
+ for {set i 102} {$i < 200} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
+ }
+ execsql COMMIT
+ execsql ANALYZE
+} {}
+
+do_eqp_test 9.3.1 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=101 AND e=5;
+} {/t1 USING INDEX i2/}
+do_eqp_test 9.3.2 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=99 AND e=5;
+} {/t1 USING INDEX i1/}
+
+set value_d [expr 101]
+do_eqp_test 9.4.1 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
+} {/t1 USING INDEX i2/}
+set value_d [expr 99]
+do_eqp_test 9.4.2 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
+} {/t1 USING INDEX i1/}
+
+#-------------------------------------------------------------------------
+# Check that the planner takes stat4 data into account when considering
+# "IS NULL" and "IS NOT NULL" constraints.
+#
+do_execsql_test 10.1.1 {
+ DROP TABLE IF EXISTS t3;
+ CREATE TABLE t3(a, b);
+ CREATE INDEX t3a ON t3(a);
+ CREATE INDEX t3b ON t3(b);
+}
+do_test 10.1.2 {
+ for {set i 1} {$i < 100} {incr i} {
+ if {$i>90} { set a $i } else { set a NULL }
+ set b [expr $i % 5]
+ execsql "INSERT INTO t3 VALUES($a, $b)"
+ }
+ execsql ANALYZE
+} {}
+do_eqp_test 10.1.3 {
+ SELECT * FROM t3 WHERE a IS NULL AND b = 2
+} {/t3 USING INDEX t3b/}
+do_eqp_test 10.1.4 {
+ SELECT * FROM t3 WHERE a IS NOT NULL AND b = 2
+} {/t3 USING INDEX t3a/}
+
+do_execsql_test 10.2.1 {
+ DROP TABLE IF EXISTS t3;
+ CREATE TABLE t3(x, a, b);
+ CREATE INDEX t3a ON t3(x, a);
+ CREATE INDEX t3b ON t3(x, b);
+}
+do_test 10.2.2 {
+ for {set i 1} {$i < 100} {incr i} {
+ if {$i>90} { set a $i } else { set a NULL }
+ set b [expr $i % 5]
+ execsql "INSERT INTO t3 VALUES('xyz', $a, $b)"
+ }
+ execsql ANALYZE
+} {}
+do_eqp_test 10.2.3 {
+ SELECT * FROM t3 WHERE x = 'xyz' AND a IS NULL AND b = 2
+} {/t3 USING INDEX t3b/}
+do_eqp_test 10.2.4 {
+ SELECT * FROM t3 WHERE x = 'xyz' AND a IS NOT NULL AND b = 2
+} {/t3 USING INDEX t3a/}
+
+#-------------------------------------------------------------------------
+# Check that stat4 data is used correctly with non-default collation
+# sequences.
+#
+foreach {tn schema} {
+ 1 {
+ CREATE TABLE t4(a COLLATE nocase, b);
+ CREATE INDEX t4a ON t4(a);
+ CREATE INDEX t4b ON t4(b);
+ }
+ 2 {
+ CREATE TABLE t4(a, b);
+ CREATE INDEX t4a ON t4(a COLLATE nocase);
+ CREATE INDEX t4b ON t4(b);
+ }
+} {
+ drop_all_tables
+ do_test 11.$tn.1 { execsql $schema } {}
+
+ do_test 11.$tn.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ if { ($i % 10)==0 } { set a ABC } else { set a DEF }
+ set b [expr $i % 5]
+ execsql { INSERT INTO t4 VALUES($a, $b) }
+ }
+ execsql ANALYZE
+ } {}
+
+ do_eqp_test 11.$tn.3 {
+ SELECT * FROM t4 WHERE a = 'def' AND b = 3;
+ } {/t4 USING INDEX t4b/}
+
+ if {$tn==1} {
+ set sql "SELECT * FROM t4 WHERE a = 'abc' AND b = 3;"
+ do_eqp_test 11.$tn.4 $sql {/t4 USING INDEX t4a/}
+ } else {
+
+ set sql "SELECT * FROM t4 WHERE a = 'abc' COLLATE nocase AND b = 3;"
+ do_eqp_test 11.$tn.5 $sql {/t4 USING INDEX t4a/}
+
+ set sql "SELECT * FROM t4 WHERE a COLLATE nocase = 'abc' AND b = 3;"
+ do_eqp_test 11.$tn.6 $sql {/t4 USING INDEX t4a/}
+ }
+}
+
+foreach {tn schema} {
+ 1 {
+ CREATE TABLE t4(x, a COLLATE nocase, b);
+ CREATE INDEX t4a ON t4(x, a);
+ CREATE INDEX t4b ON t4(x, b);
+ }
+ 2 {
+ CREATE TABLE t4(x, a, b);
+ CREATE INDEX t4a ON t4(x, a COLLATE nocase);
+ CREATE INDEX t4b ON t4(x, b);
+ }
+} {
+ drop_all_tables
+ do_test 12.$tn.1 { execsql $schema } {}
+
+ do_test 12.$tn.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ if { ($i % 10)==0 } { set a ABC } else { set a DEF }
+ set b [expr $i % 5]
+ execsql { INSERT INTO t4 VALUES(X'abcdef', $a, $b) }
+ }
+ execsql ANALYZE
+ } {}
+
+ do_eqp_test 12.$tn.3 {
+ SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'def' AND b = 3;
+ } {/t4 USING INDEX t4b/}
+
+ if {$tn==1} {
+ set sql "SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' AND b = 3;"
+ do_eqp_test 12.$tn.4 $sql {/t4 USING INDEX t4a/}
+ } else {
+ set sql {
+ SELECT * FROM t4 WHERE x=X'abcdef' AND a = 'abc' COLLATE nocase AND b = 3
+ }
+ do_eqp_test 12.$tn.5 $sql {/t4 USING INDEX t4a/}
+ set sql {
+ SELECT * FROM t4 WHERE x=X'abcdef' AND a COLLATE nocase = 'abc' AND b = 3
+ }
+ do_eqp_test 12.$tn.6 $sql {/t4 USING INDEX t4a/}
+ }
+}
+
+#-------------------------------------------------------------------------
+# Check that affinities are taken into account when using stat4 data to
+# estimate the number of rows scanned by a rowid constraint.
+#
+drop_all_tables
+do_test 13.1 {
+ execsql {
+ CREATE TABLE t1(a, b, c, d);
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b, c);
+ }
+ for {set i 0} {$i<100} {incr i} {
+ if {$i %2} {set a abc} else {set a def}
+ execsql { INSERT INTO t1(rowid, a, b, c) VALUES($i, $a, $i, $i) }
+ }
+ execsql ANALYZE
+} {}
+do_eqp_test 13.2.1 {
+ SELECT * FROM t1 WHERE a='abc' AND rowid<15 AND b<12
+} {/SEARCH TABLE t1 USING INDEX i1/}
+do_eqp_test 13.2.2 {
+ SELECT * FROM t1 WHERE a='abc' AND rowid<'15' AND b<12
+} {/SEARCH TABLE t1 USING INDEX i1/}
+do_eqp_test 13.3.1 {
+ SELECT * FROM t1 WHERE a='abc' AND rowid<100 AND b<12
+} {/SEARCH TABLE t1 USING INDEX i2/}
+do_eqp_test 13.3.2 {
+ SELECT * FROM t1 WHERE a='abc' AND rowid<'100' AND b<12
+} {/SEARCH TABLE t1 USING INDEX i2/}
+
+#-------------------------------------------------------------------------
+# Check also that affinities are taken into account when using stat4 data
+# to estimate the number of rows scanned by any other constraint on a
+# column other than the leftmost.
+#
+drop_all_tables
+do_test 14.1 {
+ execsql { CREATE TABLE t1(a, b INTEGER, c) }
+ for {set i 0} {$i<100} {incr i} {
+ set c [expr $i % 3]
+ execsql { INSERT INTO t1 VALUES('ott', $i, $c) }
+ }
+ execsql {
+ CREATE INDEX i1 ON t1(a, b);
+ CREATE INDEX i2 ON t1(c);
+ ANALYZE;
+ }
+} {}
+do_eqp_test 13.2.1 {
+ SELECT * FROM t1 WHERE a='ott' AND b<10 AND c=1
+} {/SEARCH TABLE t1 USING INDEX i1/}
+do_eqp_test 13.2.2 {
+ SELECT * FROM t1 WHERE a='ott' AND b<'10' AND c=1
+} {/SEARCH TABLE t1 USING INDEX i1/}
+
+#-------------------------------------------------------------------------
+# By default, 16 non-periodic samples are collected for the stat4 table.
+# The following tests attempt to verify that the most common keys are
+# being collected.
+#
+proc check_stat4 {tn} {
+ db eval ANALYZE
+ db eval {SELECT a, b, c, d FROM t1} {
+ incr k($a)
+ incr k([list $a $b])
+ incr k([list $a $b $c])
+ if { [info exists k([list $a $b $c $d])]==0 } { incr nRow }
+ incr k([list $a $b $c $d])
+ }
+
+ set L [list]
+ foreach key [array names k] {
+ lappend L [list $k($key) $key]
+ }
+
+ set nSample $nRow
+ if {$nSample>16} {set nSample 16}
+
+ set nThreshold [lindex [lsort -decr -integer -index 0 $L] [expr $nSample-1] 0]
+ foreach key [array names k] {
+ if {$k($key)>$nThreshold} {
+ set expect($key) 1
+ }
+ if {$k($key)==$nThreshold} {
+ set possible($key) 1
+ }
+ }
+
+
+ set nPossible [expr $nSample - [llength [array names expect]]]
+
+ #puts "EXPECT: [array names expect]"
+ #puts "POSSIBLE($nPossible/[array size possible]): [array names possible]"
+ #puts "HAVE: [db eval {SELECT test_decode(sample) FROM sqlite_stat4 WHERE idx='i1'}]"
+
+ db eval {SELECT test_decode(sample) AS s FROM sqlite_stat4 WHERE idx='i1'} {
+ set seen 0
+ for {set i 0} {$i<4} {incr i} {
+ unset -nocomplain expect([lrange $s 0 $i])
+ if {[info exists possible([lrange $s 0 $i])]} {
+ set seen 1
+ unset -nocomplain possible([lrange $s 0 $i])
+ }
+ }
+ if {$seen} {incr nPossible -1}
+ }
+ if {$nPossible<0} {set nPossible 0}
+
+ set res [list [llength [array names expect]] $nPossible]
+ uplevel [list do_test $tn [list set {} $res] {0 0}]
+}
+
+drop_all_tables
+do_test 14.1.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX i1 ON t1(a,b,c,d);
+ }
+ for {set i 0} {$i < 160} {incr i} {
+ execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) }
+ if {($i % 10)==0} { execsql { INSERT INTO t1 VALUES($i,$i,$i,$i) } }
+ }
+} {}
+check_stat4 14.1.2
+
+do_test 14.2.1 {
+ execsql { DELETE FROM t1 }
+ for {set i 0} {$i < 1600} {incr i} {
+ execsql { INSERT INTO t1 VALUES($i/10,$i/17,$i/27,$i/37) }
+ }
+} {}
+check_stat4 14.2.2
+
+do_test 14.3.1 {
+ for {set i 0} {$i < 10} {incr i} {
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ execsql { INSERT INTO t1 VALUES($i*50,$i*50,$i*50,$i*50) }
+ }
+} {}
+check_stat4 14.3.2
+
+do_test 14.4.1 {
+ execsql {DELETE FROM t1}
+ for {set i 1} {$i < 160} {incr i} {
+ set b [expr $i % 10]
+ if {$b==0 || $b==2} {set b 1}
+ execsql { INSERT INTO t1 VALUES($i/10,$b,$i,$i) }
+ }
+} {}
+check_stat4 14.4.2
+db func lrange lrange
+db func lindex lindex
+do_execsql_test 14.4.3 {
+ SELECT lrange(test_decode(sample), 0, 1) AS s FROM sqlite_stat4
+ WHERE lindex(s, 1)=='1' ORDER BY rowid
+} {
+ {0 1} {1 1} {2 1} {3 1}
+ {4 1} {5 1} {6 1} {7 1}
+ {8 1} {9 1} {10 1} {11 1}
+ {12 1} {13 1} {14 1} {15 1}
+}
+
+#-------------------------------------------------------------------------
+# Test that nothing untoward happens if the stat4 table contains entries
+# for indexes that do not exist. Or NULL values in the idx column.
+# Or NULL values in any of the other columns.
+#
+drop_all_tables
+do_execsql_test 15.1 {
+ CREATE TABLE x1(a, b, UNIQUE(a, b));
+ INSERT INTO x1 VALUES(1, 2);
+ INSERT INTO x1 VALUES(3, 4);
+ INSERT INTO x1 VALUES(5, 6);
+ ANALYZE;
+ INSERT INTO sqlite_stat4 VALUES(NULL, NULL, NULL, NULL, NULL, NULL);
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.2 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.3 {
+ INSERT INTO sqlite_stat4 VALUES(42, 42, 42, 42, 42, 42);
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.4 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.5 {
+ UPDATE sqlite_stat1 SET stat = NULL;
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.6 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.7 {
+ ANALYZE;
+ UPDATE sqlite_stat1 SET tbl = 'no such tbl';
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.8 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.9 {
+ ANALYZE;
+ UPDATE sqlite_stat4 SET neq = NULL, nlt=NULL, ndlt=NULL;
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.10 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+# This is just for coverage....
+do_execsql_test 15.11 {
+ ANALYZE;
+ UPDATE sqlite_stat1 SET stat = stat || ' unordered';
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.12 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+#-------------------------------------------------------------------------
+# Test that allocations used for sqlite_stat4 samples are included in
+# the quantity returned by SQLITE_DBSTATUS_SCHEMA_USED.
+#
+set one [string repeat x 1000]
+set two [string repeat x 2000]
+do_test 16.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, UNIQUE(a));
+ INSERT INTO t1 VALUES($one);
+ ANALYZE;
+ }
+ set nByte [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
+
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, UNIQUE(a));
+ INSERT INTO t1 VALUES($two);
+ ANALYZE;
+ }
+ set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
+ puts -nonewline " (nByte=$nByte nByte2=$nByte2)"
+
+ expr {$nByte2 > $nByte+900 && $nByte2 < $nByte+1100}
+} {1}
+
+#-------------------------------------------------------------------------
+# Test that stat4 data may be used with partial indexes.
+#
+do_test 17.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, b, c, d);
+ CREATE INDEX i1 ON t1(a, b) WHERE d IS NOT NULL;
+ INSERT INTO t1 VALUES(-1, -1, -1, NULL);
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ }
+
+ for {set i 0} {$i < 32} {incr i} {
+ if {$i<8} {set b 0} else { set b $i }
+ execsql { INSERT INTO t1 VALUES($i%2, $b, $i/2, 'abc') }
+ }
+ execsql {ANALYZE main.t1}
+} {}
+
+do_catchsql_test 17.1.2 {
+ ANALYZE temp.t1;
+} {1 {no such table: temp.t1}}
+
+do_eqp_test 17.2 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10;
+} {/USING INDEX i1/}
+do_eqp_test 17.3 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10;
+} {/USING INDEX i1/}
+
+do_execsql_test 17.4 {
+ CREATE INDEX i2 ON t1(c, d);
+ ANALYZE main.i2;
+}
+do_eqp_test 17.5 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=10 AND c=10;
+} {/USING INDEX i1/}
+do_eqp_test 17.6 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10;
+} {/USING INDEX i2/}
+
+#-------------------------------------------------------------------------
+#
+do_test 18.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ }
+ for {set i 0} {$i < 9} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ }
+ }
+ execsql ANALYZE
+ execsql { SELECT count(*) FROM sqlite_stat4 }
+} {9}
+
+#-------------------------------------------------------------------------
+# For coverage.
+#
+ifcapable view {
+ do_test 19.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(x, y);
+ CREATE INDEX i1 ON t1(x, y);
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ ANALYZE;
+ }
+ } {}
+}
+ifcapable auth {
+ proc authproc {op args} {
+ if {$op == "SQLITE_ANALYZE"} { return "SQLITE_DENY" }
+ return "SQLITE_OK"
+ }
+ do_test 19.2 {
+ reset_db
+ db auth authproc
+ execsql {
+ CREATE TABLE t1(x, y);
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ }
+ catchsql ANALYZE
+ } {1 {not authorized}}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+proc r {args} { expr rand() }
+db func r r
+db func lrange lrange
+do_test 20.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX i1 ON t1(a,b,c,d);
+ }
+ for {set i 0} {$i < 16} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, r(), r(), r());
+ INSERT INTO t1 VALUES($i, $i, r(), r());
+ INSERT INTO t1 VALUES($i, $i, $i, r());
+ INSERT INTO t1 VALUES($i, $i, $i, $i);
+ INSERT INTO t1 VALUES($i, $i, $i, $i);
+ INSERT INTO t1 VALUES($i, $i, $i, r());
+ INSERT INTO t1 VALUES($i, $i, r(), r());
+ INSERT INTO t1 VALUES($i, r(), r(), r());
+ }
+ }
+} {}
+do_execsql_test 20.2 { ANALYZE }
+for {set i 0} {$i<16} {incr i} {
+ set val "$i $i $i $i"
+ do_execsql_test 20.3.$i {
+ SELECT count(*) FROM sqlite_stat4
+ WHERE lrange(test_decode(sample), 0, 3)=$val
+ } {1}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+
+do_execsql_test 21.0 {
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i2 ON t2(a);
+}
+
+do_test 21.1 {
+ for {set i 1} {$i < 100} {incr i} {
+ execsql {
+ INSERT INTO t2 VALUES(CASE WHEN $i < 80 THEN 'one' ELSE 'two' END, $i)
+ }
+ }
+ execsql ANALYZE
+} {}
+
+# Condition (a='one') matches 80% of the table. (rowid<10) reduces this to
+# 10%, but (rowid<50) only reduces it to 50%. So in the first case below
+# the index is used. In the second, it is not.
+#
+do_eqp_test 21.2 {
+ SELECT * FROM t2 WHERE a='one' AND rowid < 10
+} {/*USING INDEX i2 (a=? AND rowid<?)*/}
+do_eqp_test 21.3 {
+ SELECT * FROM t2 WHERE a='one' AND rowid < 50
+} {/*USING INTEGER PRIMARY KEY*/}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 22.0 {
+ CREATE TABLE t3(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
+}
+do_execsql_test 22.1 {
+ WITH r(x) AS (
+ SELECT 1
+ UNION ALL
+ SELECT x+1 FROM r WHERE x<=100
+ )
+
+ INSERT INTO t3 SELECT
+ CASE WHEN (x>45 AND x<96) THEN 'B' ELSE 'A' END, /* Column "a" */
+ x, /* Column "b" */
+ CASE WHEN (x<51) THEN 'one' ELSE 'two' END, /* Column "c" */
+ x /* Column "d" */
+ FROM r;
+
+ CREATE INDEX i3 ON t3(c);
+ CREATE INDEX i4 ON t3(d);
+ ANALYZE;
+}
+
+# Expression (c='one' AND a='B') matches 5 table rows. But (c='one' AND a=A')
+# matches 45. Expression (d<?) matches 20. Neither index is a covering index.
+#
+# Therefore, with stat4 data, SQLite prefers (c='one' AND a='B') over (d<20),
+# and (d<20) over (c='one' AND a='A').
+foreach {tn where res} {
+ 1 "c='one' AND a='B' AND d < 20" {/*INDEX i3 (c=? AND a=?)*/}
+ 2 "c='one' AND a='A' AND d < 20" {/*INDEX i4 (d<?)*/}
+} {
+ do_eqp_test 22.2.$tn "SELECT * FROM t3 WHERE $where" $res
+}
+
+proc int_to_char {i} {
+ set ret ""
+ set char [list a b c d e f g h i j]
+ foreach {div} {1000 100 10 1} {
+ append ret [lindex $char [expr ($i / $div) % 10]]
+ }
+ set ret
+}
+db func int_to_char int_to_char
+
+do_execsql_test 23.0 {
+ CREATE TABLE t4(
+ a COLLATE nocase, b, c,
+ d, e, f,
+ PRIMARY KEY(c, b, a)
+ ) WITHOUT ROWID;
+ CREATE INDEX i41 ON t4(e);
+ CREATE INDEX i42 ON t4(f);
+
+ WITH data(a, b, c, d, e, f) AS (
+ SELECT int_to_char(0), 'xyz', 'zyx', '*', 0, 0
+ UNION ALL
+ SELECT
+ int_to_char(f+1), b, c, d, (e+1) % 2, f+1
+ FROM data WHERE f<1024
+ )
+ INSERT INTO t4 SELECT a, b, c, d, e, f FROM data;
+ ANALYZE;
+} {}
+
+do_eqp_test 23.1 {
+ SELECT * FROM t4 WHERE
+ (e=1 AND b='xyz' AND c='zyx' AND a<'AEA') AND f<300
+} {
+ 0 0 0 {SEARCH TABLE t4 USING INDEX i41 (e=? AND c=? AND b=? AND a<?)}
+}
+do_eqp_test 23.2 {
+ SELECT * FROM t4 WHERE
+ (e=1 AND b='xyz' AND c='zyx' AND a<'JJJ') AND f<300
+} {
+ 0 0 0 {SEARCH TABLE t4 USING INDEX i42 (f<?)}
+}
+
+do_execsql_test 24.0 {
+ CREATE TABLE t5(c, d, b, e, a, PRIMARY KEY(a, b, c)) WITHOUT ROWID;
+ WITH data(a, b, c, d, e) AS (
+ SELECT 'z', 'y', 0, 0, 0
+ UNION ALL
+ SELECT
+ a, CASE WHEN b='y' THEN 'n' ELSE 'y' END, c+1, e/250, e+1
+ FROM data
+ WHERE e<1000
+ )
+ INSERT INTO t5(a, b, c, d, e) SELECT * FROM data;
+ CREATE INDEX t5d ON t5(d);
+ CREATE INDEX t5e ON t5(e);
+ ANALYZE;
+}
+
+foreach {tn where eqp} {
+ 1 "d=0 AND a='z' AND b='n' AND e<200" {/*t5d (d=? AND a=? AND b=?)*/}
+ 2 "d=0 AND a='z' AND b='n' AND e<100" {/*t5e (e<?)*/}
+
+ 3 "d=0 AND e<300" {/*t5d (d=?)*/}
+ 4 "d=0 AND e<200" {/*t5e (e<?)*/}
+} {
+ do_eqp_test 24.$tn "SeLeCt * FROM t5 WHERE $where" $eqp
+}
+
+finish_test
diff --git a/test/analyzeA.test b/test/analyzeA.test
new file mode 100644
index 0000000..d9ca2c0
--- /dev/null
+++ b/test/analyzeA.test
@@ -0,0 +1,167 @@
+# 2013 August 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 contains automated tests used to verify that the current build
+# (which must be either ENABLE_STAT3 or ENABLE_STAT4) works with both stat3
+# and stat4 data.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix analyzeA
+
+ifcapable !stat4&&!stat3 {
+ finish_test
+ return
+}
+
+# Populate the stat3 table according to the current contents of the db
+#
+proc populate_stat3 {{bDropTable 1}} {
+ # Open a second connection on database "test.db" and run ANALYZE. If this
+ # is an ENABLE_STAT3 build, this is all that is required to create and
+ # populate the sqlite_stat3 table.
+ #
+ sqlite3 db2 test.db
+ execsql { ANALYZE }
+
+ # Now, if this is an ENABLE_STAT4 build, create and populate the
+ # sqlite_stat3 table based on the stat4 data gathered by the ANALYZE
+ # above. Then drop the sqlite_stat4 table.
+ #
+ ifcapable stat4 {
+ db2 func lindex lindex
+ execsql {
+ PRAGMA writable_schema = on;
+ CREATE TABLE sqlite_stat3(tbl,idx,neq,nlt,ndlt,sample);
+ INSERT INTO sqlite_stat3
+ SELECT DISTINCT tbl, idx,
+ lindex(neq,0), lindex(nlt,0), lindex(ndlt,0), test_extract(sample, 0)
+ FROM sqlite_stat4;
+ } db2
+ if {$bDropTable} { execsql {DROP TABLE sqlite_stat4} db2 }
+ execsql { PRAGMA writable_schema = off }
+ }
+
+ # Modify the database schema cookie to ensure that the other connection
+ # reloads the schema.
+ #
+ execsql {
+ CREATE TABLE obscure_tbl_nm(x);
+ DROP TABLE obscure_tbl_nm;
+ } db2
+ db2 close
+}
+
+# Populate the stat4 table according to the current contents of the db
+#
+proc populate_stat4 {{bDropTable 1}} {
+ sqlite3 db2 test.db
+ execsql { ANALYZE }
+
+ ifcapable stat3 {
+ execsql {
+ PRAGMA writable_schema = on;
+ CREATE TABLE sqlite_stat4(tbl,idx,neq,nlt,ndlt,sample);
+ INSERT INTO sqlite_stat4
+ SELECT tbl, idx, neq, nlt, ndlt, sqlite_record(sample)
+ FROM sqlite_stat3;
+ } db2
+ if {$bDropTable} { execsql {DROP TABLE sqlite_stat3} db2 }
+ execsql { PRAGMA writable_schema = off }
+ }
+
+ # Modify the database schema cookie to ensure that the other connection
+ # reloads the schema.
+ #
+ execsql {
+ CREATE TABLE obscure_tbl_nm(x);
+ DROP TABLE obscure_tbl_nm;
+ } db2
+ db2 close
+}
+
+# Populate the stat4 table according to the current contents of the db.
+# Leave deceptive data in the stat3 table. This data should be ignored
+# in favour of that from the stat4 table.
+#
+proc populate_both {} {
+ ifcapable stat4 { populate_stat3 0 }
+ ifcapable stat3 { populate_stat4 0 }
+
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA writable_schema = on;
+ UPDATE sqlite_stat3 SET idx =
+ CASE idx WHEN 't1b' THEN 't1c' ELSE 't1b'
+ END;
+ PRAGMA writable_schema = off;
+ CREATE TABLE obscure_tbl_nm(x);
+ DROP TABLE obscure_tbl_nm;
+ } db2
+ db2 close
+}
+
+foreach {tn analyze_cmd} {
+ 1 populate_stat4
+ 2 populate_stat3
+ 3 populate_both
+} {
+ reset_db
+ do_test 1.$tn.1 {
+ execsql { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c) }
+ for {set i 0} {$i < 100} {incr i} {
+ set c [expr int(pow(1.1,$i)/100)]
+ set b [expr 125 - int(pow(1.1,99-$i))/100]
+ execsql {INSERT INTO t1 VALUES($i, $b, $c)}
+ }
+ } {}
+
+ execsql { CREATE INDEX t1b ON t1(b) }
+ execsql { CREATE INDEX t1c ON t1(c) }
+ $analyze_cmd
+
+ do_execsql_test 1.$tn.2.1 { SELECT count(*) FROM t1 WHERE b=31 } 1
+ do_execsql_test 1.$tn.2.2 { SELECT count(*) FROM t1 WHERE c=0 } 49
+ do_execsql_test 1.$tn.2.3 { SELECT count(*) FROM t1 WHERE b=125 } 49
+ do_execsql_test 1.$tn.2.4 { SELECT count(*) FROM t1 WHERE c=16 } 1
+
+ do_eqp_test 1.$tn.2.5 {
+ SELECT * FROM t1 WHERE b = 31 AND c = 0;
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}}
+ do_eqp_test 1.$tn.2.6 {
+ SELECT * FROM t1 WHERE b = 125 AND c = 16;
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)}}
+
+ do_execsql_test 1.$tn.3.1 {
+ SELECT count(*) FROM t1 WHERE b BETWEEN 0 AND 50
+ } {6}
+ do_execsql_test 1.$tn.3.2 {
+ SELECT count(*) FROM t1 WHERE c BETWEEN 0 AND 50
+ } {90}
+ do_execsql_test 1.$tn.3.3 {
+ SELECT count(*) FROM t1 WHERE b BETWEEN 75 AND 125
+ } {90}
+ do_execsql_test 1.$tn.3.4 {
+ SELECT count(*) FROM t1 WHERE c BETWEEN 75 AND 125
+ } {6}
+
+ do_eqp_test 1.$tn.3.5 {
+ SELECT * FROM t1 WHERE b BETWEEN 0 AND 50 AND c BETWEEN 0 AND 50
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
+
+ do_eqp_test 1.$tn.3.6 {
+ SELECT * FROM t1 WHERE b BETWEEN 75 AND 125 AND c BETWEEN 75 AND 125
+ } {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
+}
+
+finish_test
+
diff --git a/test/analyzeB.test b/test/analyzeB.test
new file mode 100644
index 0000000..2a78c18
--- /dev/null
+++ b/test/analyzeB.test
@@ -0,0 +1,683 @@
+# 2013 August 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 contains automated tests used to verify that the sqlite_stat3
+# functionality is working. The tests in this file are based on a subset
+# of the sqlite_stat4 tests in analyze9.test.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix analyzeB
+
+ifcapable !stat3 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a TEXT, b TEXT);
+ INSERT INTO t1 VALUES('(0)', '(0)');
+ INSERT INTO t1 VALUES('(1)', '(1)');
+ INSERT INTO t1 VALUES('(2)', '(2)');
+ INSERT INTO t1 VALUES('(3)', '(3)');
+ INSERT INTO t1 VALUES('(4)', '(4)');
+ CREATE INDEX i1 ON t1(a, b);
+} {}
+
+
+do_execsql_test 1.1 {
+ ANALYZE;
+} {}
+
+do_execsql_test 1.2 {
+ SELECT tbl,idx,nEq,nLt,nDLt,quote(sample) FROM sqlite_stat3;
+} {
+ t1 i1 1 0 0 '(0)'
+ t1 i1 1 1 1 '(1)'
+ t1 i1 1 2 2 '(2)'
+ t1 i1 1 3 3 '(3)'
+ t1 i1 1 4 4 '(4)'
+}
+
+if {[permutation] != "utf16"} {
+ do_execsql_test 1.3 {
+ SELECT tbl,idx,nEq,nLt,nDLt,quote(sample) FROM sqlite_stat3;
+ } {
+ t1 i1 1 0 0 '(0)'
+ t1 i1 1 1 1 '(1)'
+ t1 i1 1 2 2 '(2)'
+ t1 i1 1 3 3 '(3)'
+ t1 i1 1 4 4 '(4)'
+ }
+}
+
+
+#-------------------------------------------------------------------------
+# This is really just to test SQL user function "test_decode".
+#
+reset_db
+do_execsql_test 2.1 {
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO t1(a) VALUES('some text');
+ INSERT INTO t1(a) VALUES(14);
+ INSERT INTO t1(a) VALUES(NULL);
+ INSERT INTO t1(a) VALUES(22.0);
+ INSERT INTO t1(a) VALUES(x'656667');
+ CREATE INDEX i1 ON t1(a, b, c);
+ ANALYZE;
+ SELECT quote(sample) FROM sqlite_stat3;
+} {
+ NULL 14 22.0 {'some text'} X'656667'
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 3.1 {
+ CREATE TABLE t2(a, b);
+ CREATE INDEX i2 ON t2(a, b);
+ BEGIN;
+}
+
+do_test 3.2 {
+ for {set i 0} {$i < 1000} {incr i} {
+ set a [expr $i / 10]
+ set b [expr int(rand() * 15.0)]
+ execsql { INSERT INTO t2 VALUES($a, $b) }
+ }
+ execsql COMMIT
+} {}
+
+db func lindex lindex
+
+# Each value of "a" occurs exactly 10 times in the table.
+#
+do_execsql_test 3.3.1 {
+ SELECT count(*) FROM t2 GROUP BY a;
+} [lrange [string repeat "10 " 100] 0 99]
+
+# The first element in the "nEq" list of all samples should therefore be 10.
+#
+do_execsql_test 3.3.2 {
+ ANALYZE;
+ SELECT nEq FROM sqlite_stat3;
+} [lrange [string repeat "10 " 100] 0 23]
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 3.4 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t1 VALUES(1, 1, 'one-a');
+ INSERT INTO t1 VALUES(11, 1, 'one-b');
+ INSERT INTO t1 VALUES(21, 1, 'one-c');
+ INSERT INTO t1 VALUES(31, 1, 'one-d');
+ INSERT INTO t1 VALUES(41, 1, 'one-e');
+ INSERT INTO t1 VALUES(51, 1, 'one-f');
+ INSERT INTO t1 VALUES(61, 1, 'one-g');
+ INSERT INTO t1 VALUES(71, 1, 'one-h');
+ INSERT INTO t1 VALUES(81, 1, 'one-i');
+ INSERT INTO t1 VALUES(91, 1, 'one-j');
+ INSERT INTO t1 SELECT a+1,2,'two' || substr(c,4) FROM t1;
+ INSERT INTO t1 SELECT a+2,3,'three'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+3,4,'four'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+4,5,'five'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ INSERT INTO t1 SELECT a+5,6,'six'||substr(c,4) FROM t1 WHERE c GLOB 'one-*';
+ CREATE INDEX t1b ON t1(b);
+ ANALYZE;
+ SELECT c FROM t1 WHERE b=3 AND a BETWEEN 30 AND 60;
+} {three-d three-e three-f}
+
+
+#-------------------------------------------------------------------------
+# These tests verify that the sample selection for stat3 appears to be
+# working as designed.
+#
+
+reset_db
+db func lindex lindex
+db func lrange lrange
+
+do_execsql_test 4.0 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a, b, c);
+ CREATE INDEX i1 ON t1(c, b, a);
+}
+
+
+proc insert_filler_rows_n {iStart args} {
+ set A(-ncopy) 1
+ set A(-nval) 1
+
+ foreach {k v} $args {
+ if {[info exists A($k)]==0} { error "no such option: $k" }
+ set A($k) $v
+ }
+ if {[llength $args] % 2} {
+ error "option requires an argument: [lindex $args end]"
+ }
+
+ for {set i 0} {$i < $A(-nval)} {incr i} {
+ set iVal [expr $iStart+$i]
+ for {set j 0} {$j < $A(-ncopy)} {incr j} {
+ execsql { INSERT INTO t1 VALUES($iVal, $iVal, $iVal) }
+ }
+ }
+}
+
+do_test 4.1 {
+ execsql { BEGIN }
+ insert_filler_rows_n 0 -ncopy 10 -nval 19
+ insert_filler_rows_n 20 -ncopy 1 -nval 100
+
+ execsql {
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'a');
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'b');
+ INSERT INTO t1(c, b, a) VALUES(200, 1, 'c');
+
+ INSERT INTO t1(c, b, a) VALUES(200, 2, 'e');
+ INSERT INTO t1(c, b, a) VALUES(200, 2, 'f');
+
+ INSERT INTO t1(c, b, a) VALUES(201, 3, 'g');
+ INSERT INTO t1(c, b, a) VALUES(201, 4, 'h');
+
+ ANALYZE;
+ SELECT count(*) FROM sqlite_stat3;
+ SELECT count(*) FROM t1;
+ }
+} {24 297}
+
+do_execsql_test 4.2 {
+ SELECT neq, nlt, ndlt, sample FROM sqlite_stat3 ORDER BY rowid LIMIT 16;
+} {
+ 10 0 0 0
+ 10 10 1 1
+ 10 20 2 2
+ 10 30 3 3
+ 10 40 4 4
+ 10 50 5 5
+ 10 60 6 6
+ 10 70 7 7
+ 10 80 8 8
+ 10 90 9 9
+ 10 100 10 10
+ 10 110 11 11
+ 10 120 12 12
+ 10 130 13 13
+ 10 140 14 14
+ 10 150 15 15
+}
+
+do_execsql_test 4.3 {
+ SELECT neq, nlt, ndlt, sample FROM sqlite_stat3
+ ORDER BY rowid DESC LIMIT 2;
+} {
+ 2 295 120 201
+ 5 290 119 200
+}
+
+do_execsql_test 4.4 { SELECT count(DISTINCT c) FROM t1 WHERE c<201 } 120
+do_execsql_test 4.5 { SELECT count(DISTINCT c) FROM t1 WHERE c<200 } 119
+
+reset_db
+do_test 4.7 {
+ execsql {
+ BEGIN;
+ CREATE TABLE t1(o,t INTEGER PRIMARY KEY);
+ CREATE INDEX i1 ON t1(o);
+ }
+ for {set i 0} {$i<10000} {incr i [expr (($i<1000)?1:10)]} {
+ execsql { INSERT INTO t1 VALUES('x', $i) }
+ }
+ execsql {
+ COMMIT;
+ ANALYZE;
+ SELECT count(*) FROM sqlite_stat3;
+ }
+} {1}
+do_execsql_test 4.8 {
+ SELECT sample FROM sqlite_stat3;
+} {x}
+
+
+#-------------------------------------------------------------------------
+# The following would cause a crash at one point.
+#
+reset_db
+do_execsql_test 5.1 {
+ PRAGMA encoding = 'utf-16';
+ CREATE TABLE t0(v);
+ ANALYZE;
+}
+
+#-------------------------------------------------------------------------
+# This was also crashing (corrupt sqlite_stat3 table).
+#
+reset_db
+do_execsql_test 6.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+ INSERT INTO t1 VALUES(1, 1);
+ INSERT INTO t1 VALUES(2, 2);
+ INSERT INTO t1 VALUES(3, 3);
+ INSERT INTO t1 VALUES(4, 4);
+ INSERT INTO t1 VALUES(5, 5);
+ ANALYZE;
+ PRAGMA writable_schema = 1;
+ CREATE TEMP TABLE x1 AS
+ SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat3
+ ORDER BY (rowid%5), rowid;
+ DELETE FROM sqlite_stat3;
+ INSERT INTO sqlite_stat3 SELECT * FROM x1;
+ PRAGMA writable_schema = 0;
+ ANALYZE sqlite_master;
+}
+do_execsql_test 6.2 {
+ SELECT * FROM t1 WHERE a = 'abc';
+}
+
+#-------------------------------------------------------------------------
+# The following tests experiment with adding corrupted records to the
+# 'sample' column of the sqlite_stat3 table.
+#
+reset_db
+sqlite3_db_config_lookaside db 0 0 0
+
+do_execsql_test 7.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ INSERT INTO t1 VALUES(1, 1);
+ INSERT INTO t1 VALUES(2, 2);
+ INSERT INTO t1 VALUES(3, 3);
+ INSERT INTO t1 VALUES(4, 4);
+ INSERT INTO t1 VALUES(5, 5);
+ ANALYZE;
+ UPDATE sqlite_stat3 SET sample = X'' WHERE rowid = 1;
+ ANALYZE sqlite_master;
+}
+
+do_execsql_test 7.2 {
+ UPDATE sqlite_stat3 SET sample = X'FFFF';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 1;
+} {1 1}
+
+do_execsql_test 7.3 {
+ ANALYZE;
+ UPDATE sqlite_stat3 SET neq = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 1;
+} {1 1}
+
+do_execsql_test 7.4 {
+ ANALYZE;
+ UPDATE sqlite_stat3 SET ndlt = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 3;
+} {3 3}
+
+do_execsql_test 7.5 {
+ ANALYZE;
+ UPDATE sqlite_stat3 SET nlt = '0 0 0';
+ ANALYZE sqlite_master;
+ SELECT * FROM t1 WHERE a = 5;
+} {5 5}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 8.1 {
+ CREATE TABLE t1(x TEXT);
+ CREATE INDEX i1 ON t1(x);
+ INSERT INTO t1 VALUES('1');
+ INSERT INTO t1 VALUES('2');
+ INSERT INTO t1 VALUES('3');
+ INSERT INTO t1 VALUES('4');
+ ANALYZE;
+}
+do_execsql_test 8.2 {
+ SELECT * FROM t1 WHERE x = 3;
+} {3}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 9.1 {
+ CREATE TABLE t1(a, b, c, d, e);
+ CREATE INDEX i1 ON t1(a, b, c, d);
+ CREATE INDEX i2 ON t1(e);
+}
+do_test 9.2 {
+ execsql BEGIN;
+ for {set i 0} {$i < 100} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
+ }
+ for {set i 0} {$i < 20} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', 101, $i)"
+ }
+ for {set i 102} {$i < 200} {incr i} {
+ execsql "INSERT INTO t1 VALUES('x', 'y', 'z', $i, [expr $i/2])"
+ }
+ execsql COMMIT
+ execsql ANALYZE
+} {}
+
+do_eqp_test 9.3.1 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=101 AND e=5;
+} {/t1 USING INDEX i1/}
+do_eqp_test 9.3.2 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=99 AND e=5;
+} {/t1 USING INDEX i1/}
+
+set value_d [expr 101]
+do_eqp_test 9.4.1 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
+} {/t1 USING INDEX i1/}
+set value_d [expr 99]
+do_eqp_test 9.4.2 {
+ SELECT * FROM t1 WHERE a='x' AND b='y' AND c='z' AND d=$value_d AND e=5
+} {/t1 USING INDEX i1/}
+
+#-------------------------------------------------------------------------
+# Check that the planner takes stat3 data into account when considering
+# "IS NULL" and "IS NOT NULL" constraints.
+#
+do_execsql_test 10.1.1 {
+ DROP TABLE IF EXISTS t3;
+ CREATE TABLE t3(a, b);
+ CREATE INDEX t3a ON t3(a);
+ CREATE INDEX t3b ON t3(b);
+}
+do_test 10.1.2 {
+ for {set i 1} {$i < 100} {incr i} {
+ if {$i>90} { set a $i } else { set a NULL }
+ set b [expr $i % 5]
+ execsql "INSERT INTO t3 VALUES($a, $b)"
+ }
+ execsql ANALYZE
+} {}
+do_eqp_test 10.1.3 {
+ SELECT * FROM t3 WHERE a IS NULL AND b = 2
+} {/t3 USING INDEX t3b/}
+do_eqp_test 10.1.4 {
+ SELECT * FROM t3 WHERE a IS NOT NULL AND b = 2
+} {/t3 USING INDEX t3a/}
+
+#-------------------------------------------------------------------------
+# Check that stat3 data is used correctly with non-default collation
+# sequences.
+#
+foreach {tn schema} {
+ 1 {
+ CREATE TABLE t4(a COLLATE nocase, b);
+ CREATE INDEX t4a ON t4(a);
+ CREATE INDEX t4b ON t4(b);
+ }
+ 2 {
+ CREATE TABLE t4(a, b);
+ CREATE INDEX t4a ON t4(a COLLATE nocase);
+ CREATE INDEX t4b ON t4(b);
+ }
+} {
+ drop_all_tables
+ do_test 11.$tn.1 { execsql $schema } {}
+
+ do_test 11.$tn.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ if { ($i % 10)==0 } { set a ABC } else { set a DEF }
+ set b [expr $i % 5]
+ execsql { INSERT INTO t4 VALUES($a, $b) }
+ }
+ execsql ANALYZE
+ } {}
+
+ do_eqp_test 11.$tn.3 {
+ SELECT * FROM t4 WHERE a = 'def' AND b = 3;
+ } {/t4 USING INDEX t4b/}
+
+ if {$tn==1} {
+ set sql "SELECT * FROM t4 WHERE a = 'abc' AND b = 3;"
+ do_eqp_test 11.$tn.4 $sql {/t4 USING INDEX t4a/}
+ } else {
+
+ set sql "SELECT * FROM t4 WHERE a = 'abc' COLLATE nocase AND b = 3;"
+ do_eqp_test 11.$tn.5 $sql {/t4 USING INDEX t4a/}
+
+ set sql "SELECT * FROM t4 WHERE a COLLATE nocase = 'abc' AND b = 3;"
+ do_eqp_test 11.$tn.6 $sql {/t4 USING INDEX t4a/}
+ }
+}
+
+#-------------------------------------------------------------------------
+# Test that nothing untoward happens if the stat3 table contains entries
+# for indexes that do not exist. Or NULL values in the idx column.
+# Or NULL values in any of the other columns.
+#
+drop_all_tables
+do_execsql_test 15.1 {
+ CREATE TABLE x1(a, b, UNIQUE(a, b));
+ INSERT INTO x1 VALUES(1, 2);
+ INSERT INTO x1 VALUES(3, 4);
+ INSERT INTO x1 VALUES(5, 6);
+ ANALYZE;
+ INSERT INTO sqlite_stat3 VALUES(NULL, NULL, NULL, NULL, NULL, NULL);
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.2 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.3 {
+ INSERT INTO sqlite_stat3 VALUES(42, 42, 42, 42, 42, 42);
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.4 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.5 {
+ UPDATE sqlite_stat1 SET stat = NULL;
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.6 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.7 {
+ ANALYZE;
+ UPDATE sqlite_stat1 SET tbl = 'no such tbl';
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.8 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+do_execsql_test 15.9 {
+ ANALYZE;
+ UPDATE sqlite_stat3 SET neq = NULL, nlt=NULL, ndlt=NULL;
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.10 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+# This is just for coverage....
+do_execsql_test 15.11 {
+ ANALYZE;
+ UPDATE sqlite_stat1 SET stat = stat || ' unordered';
+}
+db close
+sqlite3 db test.db
+do_execsql_test 15.12 { SELECT * FROM x1 } {1 2 3 4 5 6}
+
+#-------------------------------------------------------------------------
+# Test that allocations used for sqlite_stat3 samples are included in
+# the quantity returned by SQLITE_DBSTATUS_SCHEMA_USED.
+#
+set one [string repeat x 1000]
+set two [string repeat x 2000]
+do_test 16.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, UNIQUE(a));
+ INSERT INTO t1 VALUES($one);
+ ANALYZE;
+ }
+ set nByte [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
+
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, UNIQUE(a));
+ INSERT INTO t1 VALUES($two);
+ ANALYZE;
+ }
+ set nByte2 [lindex [sqlite3_db_status db SCHEMA_USED 0] 1]
+
+ expr {$nByte2 > $nByte+950 && $nByte2 < $nByte+1050}
+} {1}
+
+#-------------------------------------------------------------------------
+# Test that stat3 data may be used with partial indexes.
+#
+do_test 17.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, b, c, d);
+ CREATE INDEX i1 ON t1(a, b) WHERE d IS NOT NULL;
+ INSERT INTO t1 VALUES(-1, -1, -1, NULL);
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ INSERT INTO t1 SELECT 2*a,2*b,2*c,d FROM t1;
+ }
+
+ for {set i 0} {$i < 32} {incr i} {
+ execsql { INSERT INTO t1 VALUES($i%2, $b, $i/2, 'abc') }
+ }
+ execsql {ANALYZE main.t1}
+} {}
+
+do_catchsql_test 17.1.2 {
+ ANALYZE temp.t1;
+} {1 {no such table: temp.t1}}
+
+do_eqp_test 17.2 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0;
+} {/USING INDEX i1/}
+do_eqp_test 17.3 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0;
+} {/USING INDEX i1/}
+
+do_execsql_test 17.4 {
+ CREATE INDEX i2 ON t1(c) WHERE d IS NOT NULL;
+ ANALYZE main.i2;
+}
+do_eqp_test 17.5 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0;
+} {/USING INDEX i1/}
+do_eqp_test 17.6 {
+ SELECT * FROM t1 WHERE d IS NOT NULL AND a=0 AND b=0 AND c=10;
+} {/USING INDEX i2/}
+
+#-------------------------------------------------------------------------
+#
+do_test 18.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ }
+ for {set i 0} {$i < 9} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ INSERT INTO t1 VALUES($i, 0);
+ }
+ }
+ execsql ANALYZE
+ execsql { SELECT count(*) FROM sqlite_stat3 }
+} {9}
+
+#-------------------------------------------------------------------------
+# For coverage.
+#
+ifcapable view {
+ do_test 19.1 {
+ reset_db
+ execsql {
+ CREATE TABLE t1(x, y);
+ CREATE INDEX i1 ON t1(x, y);
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ ANALYZE;
+ }
+ } {}
+}
+ifcapable auth {
+ proc authproc {op args} {
+ if {$op == "SQLITE_ANALYZE"} { return "SQLITE_DENY" }
+ return "SQLITE_OK"
+ }
+ do_test 19.2 {
+ reset_db
+ db auth authproc
+ execsql {
+ CREATE TABLE t1(x, y);
+ CREATE VIEW v1 AS SELECT * FROM t1;
+ }
+ catchsql ANALYZE
+ } {1 {not authorized}}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+proc r {args} { expr rand() }
+db func r r
+db func lrange lrange
+do_test 20.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX i1 ON t1(a,b,c,d);
+ }
+ for {set i 0} {$i < 16} {incr i} {
+ execsql {
+ INSERT INTO t1 VALUES($i, r(), r(), r());
+ INSERT INTO t1 VALUES($i, $i, r(), r());
+ INSERT INTO t1 VALUES($i, $i, $i, r());
+ INSERT INTO t1 VALUES($i, $i, $i, $i);
+ INSERT INTO t1 VALUES($i, $i, $i, $i);
+ INSERT INTO t1 VALUES($i, $i, $i, r());
+ INSERT INTO t1 VALUES($i, $i, r(), r());
+ INSERT INTO t1 VALUES($i, r(), r(), r());
+ }
+ }
+} {}
+do_execsql_test 20.2 { ANALYZE }
+for {set i 0} {$i<16} {incr i} {
+ set val $i
+ do_execsql_test 20.3.$i {
+ SELECT count(*) FROM sqlite_stat3 WHERE sample=$val
+ } {1}
+}
+
+finish_test
+
diff --git a/test/analyzeC.test b/test/analyzeC.test
new file mode 100644
index 0000000..02faa9c
--- /dev/null
+++ b/test/analyzeC.test
@@ -0,0 +1,167 @@
+# 2014-07-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 automated tests used to verify that the text terms
+# at the end of sqlite_stat1.stat are processed correctly.
+#
+# (1) "unordered" means that the index cannot be used for ORDER BY
+# or for range queries
+#
+# (2) "sz=NNN" sets the relative size of the index entries
+#
+# (3) All other fields are silently ignored
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix analyzeC
+
+# Baseline case. Range queries work OK. Indexes can be used for
+# ORDER BY.
+#
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1(a,b,c)
+ VALUES(1,2,3),(7,8,9),(4,5,6),(10,11,12),(4,8,12),(1,11,111);
+ CREATE INDEX t1a ON t1(a);
+ CREATE INDEX t1b ON t1(b);
+ ANALYZE;
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1(tbl,idx,stat)
+ VALUES('t1','t1a','12345 2'),('t1','t1b','12345 4');
+ ANALYZE sqlite_master;
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {4 5 6 # 7 8 9 # 4 8 12 #}
+do_execsql_test 1.1 {
+ EXPLAIN QUERY PLAN
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {/.* USING INDEX t1a .a>. AND a<...*/}
+do_execsql_test 1.2 {
+ SELECT c FROM t1 ORDER BY a;
+} {3 111 6 12 9 12}
+do_execsql_test 1.3 {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {/.*SCAN TABLE t1 USING INDEX t1a.*/}
+do_execsql_test 1.3x {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {~/.*B-TREE FOR ORDER BY.*/}
+
+# Now mark the t1a index as "unordered". Range queries and ORDER BY no
+# longer use the index, but equality queries do.
+#
+do_execsql_test 2.0 {
+ UPDATE sqlite_stat1 SET stat='12345 2 unordered' WHERE idx='t1a';
+ ANALYZE sqlite_master;
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {4 5 6 # 7 8 9 # 4 8 12 #}
+do_execsql_test 2.1 {
+ EXPLAIN QUERY PLAN
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {~/.*USING INDEX.*/}
+do_execsql_test 2.2 {
+ SELECT c FROM t1 ORDER BY a;
+} {3 111 6 12 9 12}
+do_execsql_test 2.3 {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {~/.*USING INDEX.*/}
+do_execsql_test 2.3x {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {/.*B-TREE FOR ORDER BY.*/}
+
+# Ignore extraneous text parameters in the sqlite_stat1.stat field.
+#
+do_execsql_test 3.0 {
+ UPDATE sqlite_stat1 SET stat='12345 2 whatever=5 unordered xyzzy=11'
+ WHERE idx='t1a';
+ ANALYZE sqlite_master;
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {4 5 6 # 7 8 9 # 4 8 12 #}
+do_execsql_test 3.1 {
+ EXPLAIN QUERY PLAN
+ SELECT *, '#' FROM t1 WHERE a BETWEEN 3 AND 8 ORDER BY c;
+} {~/.*USING INDEX.*/}
+do_execsql_test 3.2 {
+ SELECT c FROM t1 ORDER BY a;
+} {3 111 6 12 9 12}
+do_execsql_test 3.3 {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {~/.*USING INDEX.*/}
+do_execsql_test 3.3x {
+ EXPLAIN QUERY PLAN
+ SELECT c FROM t1 ORDER BY a;
+} {/.*B-TREE FOR ORDER BY.*/}
+
+# The sz=NNN parameter determines which index to scan
+#
+do_execsql_test 4.0 {
+ DROP INDEX t1a;
+ CREATE INDEX t1ab ON t1(a,b);
+ CREATE INDEX t1ca ON t1(c,a);
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1(tbl,idx,stat)
+ VALUES('t1','t1ab','12345 3 2 sz=10'),('t1','t1ca','12345 3 2 sz=20');
+ ANALYZE sqlite_master;
+ SELECT count(a) FROM t1;
+} {6}
+do_execsql_test 4.1 {
+ EXPLAIN QUERY PLAN
+ SELECT count(a) FROM t1;
+} {/.*INDEX t1ab.*/}
+do_execsql_test 4.2 {
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1(tbl,idx,stat)
+ VALUES('t1','t1ab','12345 3 2 sz=20'),('t1','t1ca','12345 3 2 sz=10');
+ ANALYZE sqlite_master;
+ SELECT count(a) FROM t1;
+} {6}
+do_execsql_test 4.3 {
+ EXPLAIN QUERY PLAN
+ SELECT count(a) FROM t1;
+} {/.*INDEX t1ca.*/}
+
+
+# The sz=NNN parameter works even if there is other extraneous text
+# in the sqlite_stat1.stat column.
+#
+do_execsql_test 5.0 {
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1(tbl,idx,stat)
+ VALUES('t1','t1ab','12345 3 2 x=5 sz=10 y=10'),
+ ('t1','t1ca','12345 3 2 whatever sz=20 junk');
+ ANALYZE sqlite_master;
+ SELECT count(a) FROM t1;
+} {6}
+do_execsql_test 5.1 {
+ EXPLAIN QUERY PLAN
+ SELECT count(a) FROM t1;
+} {/.*INDEX t1ab.*/}
+do_execsql_test 5.2 {
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1(tbl,idx,stat)
+ VALUES('t1','t1ca','12345 3 2 x=5 sz=10 y=10'),
+ ('t1','t1ab','12345 3 2 whatever sz=20 junk');
+ ANALYZE sqlite_master;
+ SELECT count(a) FROM t1;
+} {6}
+do_execsql_test 5.3 {
+ EXPLAIN QUERY PLAN
+ SELECT count(a) FROM t1;
+} {/.*INDEX t1ca.*/}
+
+
+
+
+finish_test
diff --git a/test/async5.test b/test/async5.test
index aa484fc..abac11f 100644
--- a/test/async5.test
+++ b/test/async5.test
@@ -66,4 +66,3 @@ sqlite3async_control halt never
sqlite3async_shutdown
set sqlite3async_trace 0
finish_test
-
diff --git a/test/atof1.test b/test/atof1.test
index 76eb427..5c04d02 100644
--- a/test/atof1.test
+++ b/test/atof1.test
@@ -15,7 +15,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-if {![info exists __GNUC__]} {
+if {![info exists __GNUC__] || [regexp arm $tcl_platform(machine)]} {
finish_test
return
}
diff --git a/test/attach2.test b/test/attach2.test
index db23072..f870568 100644
--- a/test/attach2.test
+++ b/test/attach2.test
@@ -377,6 +377,9 @@ do_test attach2-6.2 {
}
} {1 {cannot ATTACH database within transaction}}
+# EVIDENCE-OF: R-59740-55581 This statement will fail if SQLite is in
+# the middle of a transaction.
+#
do_test attach2-6.3 {
catchsql {
DETACH aux;
diff --git a/test/attach3.test b/test/attach3.test
index f861425..1ac10d9 100644
--- a/test/attach3.test
+++ b/test/attach3.test
@@ -322,7 +322,6 @@ do_test attach3-12.9 {
db_list
} {main temp {}}
do_test attach3-12.10 {
-breakpoint
execsql {
DETACH ?
}
diff --git a/test/auth.test b/test/auth.test
index fd402b1..43e53ef 100644
--- a/test/auth.test
+++ b/test/auth.test
@@ -2080,6 +2080,42 @@ ifcapable {altertable} {
execsql {DROP TABLE t5}
} ;# ifcapable altertable
+ifcapable {cte} {
+ do_test auth-1.310 {
+ proc auth {code arg1 arg2 arg3 arg4} {
+ if {$code=="SQLITE_RECURSIVE"} {
+ return SQLITE_DENY
+ }
+ return SQLITE_OK
+ }
+ db eval {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES(1,2),(3,4),(5,6);
+ }
+ } {}
+ do_catchsql_test auth-1.311 {
+ WITH
+ auth1311(x,y) AS (SELECT a+b, b-a FROM t1)
+ SELECT * FROM auth1311 ORDER BY x;
+ } {0 {3 1 7 1 11 1}}
+ do_catchsql_test auth-1.312 {
+ WITH RECURSIVE
+ auth1312(x,y) AS (SELECT a+b, b-a FROM t1)
+ SELECT x, y FROM auth1312 ORDER BY x;
+ } {0 {3 1 7 1 11 1}}
+ do_catchsql_test auth-1.313 {
+ WITH RECURSIVE
+ auth1313(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM auth1313 WHERE x<5)
+ SELECT * FROM t1;
+ } {0 {1 2 3 4 5 6}}
+ do_catchsql_test auth-1.314 {
+ WITH RECURSIVE
+ auth1314(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM auth1314 WHERE x<5)
+ SELECT * FROM t1 LEFT JOIN auth1314;
+ } {1 {not authorized}}
+} ;# ifcapable cte
+
do_test auth-2.1 {
proc auth {code arg1 arg2 arg3 arg4} {
if {$code=="SQLITE_READ" && $arg1=="t3" && $arg2=="x"} {
@@ -2325,10 +2361,14 @@ ifcapable compound&&subquery {
}
}
}
- ifcapable stat3 {
- set stat3 "sqlite_stat3 "
+ ifcapable stat4 {
+ set stat4 "sqlite_stat4 "
} else {
- set stat3 ""
+ ifcapable stat3 {
+ set stat4 "sqlite_stat3 "
+ } else {
+ set stat4 ""
+ }
}
do_test auth-5.2 {
execsql {
@@ -2337,7 +2377,7 @@ ifcapable compound&&subquery {
WHERE type='table'
ORDER BY name
}
- } "sqlite_stat1 ${stat3}t1 t2 t3 t4"
+ } "sqlite_stat1 ${stat4}t1 t2 t3 t4"
}
# Ticket #3944
diff --git a/test/auth2.test b/test/auth2.test
index 9343fd6..65e0591 100644
--- a/test/auth2.test
+++ b/test/auth2.test
@@ -102,6 +102,7 @@ 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 {}
+SQLITE_READ sqlite_master type main {}
SQLITE_READ sqlite_master ROWID main {}
}
do_test auth2-2.2 {
@@ -122,6 +123,7 @@ 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 {}
+SQLITE_READ sqlite_master type main {}
SQLITE_READ sqlite_master ROWID main {}
}
do_test auth2-2.3 {
diff --git a/test/autoinc.test b/test/autoinc.test
index 98f6919..2396006 100644
--- a/test/autoinc.test
+++ b/test/autoinc.test
@@ -216,7 +216,7 @@ do_test autoinc-2.27 {
} {t1 1238}
do_test autoinc-2.28 {
execsql {
- UPDATE sqlite_sequence SET seq='12345678901234567890'
+ UPDATE sqlite_sequence SET seq='-12345678901234567890'
WHERE name='t1';
INSERT INTO t1 VALUES(NULL,6);
SELECT * FROM t1;
diff --git a/test/autoindex1.test b/test/autoindex1.test
index 54ff82a..6cb0ab1 100644
--- a/test/autoindex1.test
+++ b/test/autoindex1.test
@@ -23,6 +23,14 @@ ifcapable {!autoindex} {
return
}
+# Setup for logging
+db close
+sqlite3_shutdown
+test_sqlite3_log [list lappend ::log]
+set ::log [list]
+sqlite3 db test.db
+
+
# With automatic index turned off, we do a full scan of the T2 table
do_test autoindex1-100 {
db eval {
@@ -60,6 +68,15 @@ do_test autoindex1-111 {
do_test autoindex1-112 {
db status autoindex
} {7}
+do_test autoindex1-113 {
+ set ::log
+} {SQLITE_WARNING_AUTOINDEX {automatic index on t2(c)}}
+
+db close
+sqlite3_shutdown
+test_sqlite3_log
+sqlite3_initialize
+sqlite3 db test.db
# The same test as above, but this time the T2 query is a subquery rather
# than a join.
@@ -78,6 +95,11 @@ do_test autoindex1-202 {
do_test autoindex1-210 {
db eval {
PRAGMA automatic_index=ON;
+ ANALYZE;
+ UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t1';
+ -- Table t2 actually contains 8 rows.
+ UPDATE sqlite_stat1 SET stat='16' WHERE tbl='t2';
+ ANALYZE sqlite_master;
SELECT b, (SELECT d FROM t2 WHERE c=a) FROM t1;
}
} {11 911 22 922 33 933 44 944 55 955 66 966 77 977 88 988}
@@ -91,9 +113,15 @@ do_test autoindex1-212 {
# Modify the second table of the join while the join is in progress
#
+do_execsql_test autoindex1-299 {
+ UPDATE sqlite_stat1 SET stat='10000' WHERE tbl='t2';
+ ANALYZE sqlite_master;
+ EXPLAIN QUERY PLAN
+ SELECT b, d FROM t1 CROSS JOIN t2 ON (c=a);
+} {/AUTOMATIC COVERING INDEX/}
do_test autoindex1-300 {
set r {}
- db eval {SELECT b, d FROM t1 JOIN t2 ON (c=a)} {
+ db eval {SELECT b, d FROM t1 CROSS JOIN t2 ON (c=a)} {
lappend r $b $d
db eval {UPDATE t2 SET d=d+1}
}
@@ -143,22 +171,25 @@ do_test autoindex1-401 {
do_execsql_test autoindex1-500 {
CREATE TABLE t501(a INTEGER PRIMARY KEY, b);
CREATE TABLE t502(x INTEGER PRIMARY KEY, y);
+ INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t501',null,'1000000');
+ INSERT INTO sqlite_stat1(tbl,idx,stat) VALUES('t502',null,'1000');
+ ANALYZE sqlite_master;
EXPLAIN QUERY PLAN
SELECT b FROM t501
WHERE t501.a IN (SELECT x FROM t502 WHERE y=?);
} {
- 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)}
+ 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {EXECUTE LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t502 (~100000 rows)}
+ 1 0 0 {SCAN TABLE t502}
}
do_execsql_test autoindex1-501 {
EXPLAIN QUERY PLAN
SELECT b FROM t501
WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
} {
- 0 0 0 {SCAN TABLE t501 (~500000 rows)}
+ 0 0 0 {SCAN TABLE t501}
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?) (~7 rows)}
+ 1 0 0 {SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?)}
}
do_execsql_test autoindex1-502 {
EXPLAIN QUERY PLAN
@@ -166,9 +197,9 @@ do_execsql_test autoindex1-502 {
WHERE t501.a=123
AND t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
} {
- 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t502 (~100000 rows)}
+ 1 0 0 {SCAN TABLE t502}
}
@@ -240,12 +271,12 @@ do_execsql_test autoindex1-600 {
WHERE y.sheep_no IS NULL
ORDER BY x.registering_flock;
} {
- 1 0 0 {SCAN TABLE sheep AS s (~1000000 rows)}
- 1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?) (~2 rows)}
+ 1 0 0 {SCAN TABLE sheep AS s}
+ 1 1 1 {SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?)}
1 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2}
- 2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?) (~1 rows)}
- 0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index (~1000000 rows)}
- 0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?) (~8 rows)}
+ 2 0 0 {SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?)}
+ 0 0 0 {SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index}
+ 0 1 1 {SEARCH SUBQUERY 1 AS y USING AUTOMATIC COVERING INDEX (sheep_no=?)}
}
@@ -253,7 +284,7 @@ do_execsql_test autoindex1-700 {
CREATE TABLE t5(a, b, c);
EXPLAIN QUERY PLAN SELECT a FROM t5 WHERE b=10 ORDER BY c;
} {
- 0 0 0 {SCAN TABLE t5 (~100000 rows)}
+ 0 0 0 {SCAN TABLE t5}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
diff --git a/test/autoindex2.test b/test/autoindex2.test
new file mode 100644
index 0000000..f4da416
--- /dev/null
+++ b/test/autoindex2.test
@@ -0,0 +1,271 @@
+# 2014-06-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 automatic index creation logic.
+#
+# This file contains a single real-world test case that was giving
+# suboptimal performance because of over-use of automatic indexes.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+do_execsql_test autoindex2-100 {
+ CREATE TABLE t1(
+ t1_id largeint,
+ did char(9),
+ ptime largeint,
+ exbyte char(4),
+ pe_id int,
+ field_id int,
+ mass float,
+ param10 float,
+ param11 float,
+ exmass float,
+ deviation float,
+ trange float,
+ vstatus int,
+ commit_status int,
+ formula char(329),
+ tier int DEFAULT 2,
+ ssid int DEFAULT 0,
+ last_operation largeint DEFAULT 0,
+ admin_uuid int DEFAULT 0,
+ previous_value float,
+ job_id largeint,
+ last_t1 largeint DEFAULT 0,
+ data_t1 int,
+ previous_date largeint DEFAULT 0,
+ flg8 int DEFAULT 1,
+ failed_fields char(100)
+ );
+ CREATE INDEX t1x0 on t1 (t1_id);
+ CREATE INDEX t1x1 on t1 (ptime, vstatus);
+ CREATE INDEX t1x2 on t1 (did, ssid, ptime, vstatus, exbyte, t1_id);
+ CREATE INDEX t1x3 on t1 (job_id);
+
+ CREATE TABLE t2(
+ did char(9),
+ client_did char(30),
+ description char(49),
+ uid int,
+ tzid int,
+ privilege int,
+ param2 int,
+ type char(30),
+ subtype char(32),
+ dparam1 char(7) DEFAULT '',
+ param5 char(3) DEFAULT '',
+ notional float DEFAULT 0.000000,
+ create_time largeint,
+ sample_time largeint DEFAULT 0,
+ param6 largeint,
+ frequency int,
+ expiration largeint,
+ uw_status int,
+ next_sample largeint,
+ last_sample largeint,
+ reserve1 char(29) DEFAULT '',
+ reserve2 char(29) DEFAULT '',
+ reserve3 char(29) DEFAULT '',
+ bxcdr char(19) DEFAULT 'XY',
+ ssid int DEFAULT 1,
+ last_t1_id largeint,
+ reserve4 char(29) DEFAULT '',
+ reserve5 char(29) DEFAULT '',
+ param12 int DEFAULT 0,
+ long_did char(100) DEFAULT '',
+ gr_code int DEFAULT 0,
+ drx char(100) DEFAULT '',
+ parent_id char(9) DEFAULT '',
+ param13 int DEFAULT 0,
+ position float DEFAULT 1.000000,
+ client_did3 char(100) DEFAULT '',
+ client_did4 char(100) DEFAULT '',
+ dlib_id char(9) DEFAULT ''
+ );
+ CREATE INDEX t2x0 on t2 (did);
+ CREATE INDEX t2x1 on t2 (client_did);
+ CREATE INDEX t2x2 on t2 (long_did);
+ CREATE INDEX t2x3 on t2 (uid);
+ CREATE INDEX t2x4 on t2 (param2);
+ CREATE INDEX t2x5 on t2 (type);
+ CREATE INDEX t2x6 on t2 (subtype);
+ CREATE INDEX t2x7 on t2 (last_sample);
+ CREATE INDEX t2x8 on t2 (param6);
+ CREATE INDEX t2x9 on t2 (frequency);
+ CREATE INDEX t2x10 on t2 (privilege);
+ CREATE INDEX t2x11 on t2 (sample_time);
+ CREATE INDEX t2x12 on t2 (notional);
+ CREATE INDEX t2x13 on t2 (tzid);
+ CREATE INDEX t2x14 on t2 (gr_code);
+ CREATE INDEX t2x15 on t2 (parent_id);
+
+ CREATE TABLE t3(
+ uid int,
+ param3 int,
+ uuid int,
+ acc_id int,
+ cust_num int,
+ numerix_id int,
+ pfy char(29),
+ param4 char(29),
+ param15 int DEFAULT 0,
+ flg7 int DEFAULT 0,
+ param21 int DEFAULT 0,
+ bxcdr char(2) DEFAULT 'PC',
+ c31 int DEFAULT 0,
+ c33 int DEFAULT 0,
+ c35 int DEFAULT 0,
+ c37 int,
+ mgr_uuid int,
+ back_up_uuid int,
+ priv_mars int DEFAULT 0,
+ is_qc int DEFAULT 0,
+ c41 int DEFAULT 0,
+ deleted int DEFAULT 0,
+ c47 int DEFAULT 1
+ );
+ CREATE INDEX t3x0 on t3 (uid);
+ CREATE INDEX t3x1 on t3 (param3);
+ CREATE INDEX t3x2 on t3 (uuid);
+ CREATE INDEX t3x3 on t3 (acc_id);
+ CREATE INDEX t3x4 on t3 (param4);
+ CREATE INDEX t3x5 on t3 (pfy);
+ CREATE INDEX t3x6 on t3 (is_qc);
+ SELECT count(*) FROM sqlite_master;
+} {30}
+do_execsql_test autoindex2-110 {
+ ANALYZE sqlite_master;
+ INSERT INTO sqlite_stat1 VALUES('t1','t1x3','10747267 260');
+ INSERT INTO sqlite_stat1 VALUES('t1','t1x2','10747267 121 113 2 2 2 1');
+ INSERT INTO sqlite_stat1 VALUES('t1','t1x1','10747267 50 40');
+ INSERT INTO sqlite_stat1 VALUES('t1','t1x0','10747267 1');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x15','39667 253');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x14','39667 19834');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x13','39667 13223');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x12','39667 7');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x11','39667 17');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x10','39667 19834');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x9','39667 7934');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x8','39667 11');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x7','39667 5');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x6','39667 242');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x5','39667 1984');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x4','39667 4408');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x3','39667 81');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x2','39667 551');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x1','39667 2');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2x0','39667 1');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x6','569 285');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x5','569 2');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x4','569 2');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x3','569 5');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x2','569 3');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x1','569 6');
+ INSERT INTO sqlite_stat1 VALUES('t3','t3x0','569 1');
+ ANALYZE sqlite_master;
+} {}
+do_execsql_test autoindex2-120 {
+ EXPLAIN QUERY PLAN
+ SELECT
+ t1_id,
+ t1.did,
+ param2,
+ param3,
+ t1.ptime,
+ t1.trange,
+ t1.exmass,
+ t1.mass,
+ t1.vstatus,
+ type,
+ subtype,
+ t1.deviation,
+ t1.formula,
+ dparam1,
+ reserve1,
+ reserve2,
+ param4,
+ t1.last_operation,
+ t1.admin_uuid,
+ t1.previous_value,
+ t1.job_id,
+ client_did,
+ t1.last_t1,
+ t1.data_t1,
+ t1.previous_date,
+ param5,
+ param6,
+ mgr_uuid
+ FROM
+ t1,
+ t2,
+ t3
+ WHERE
+ t1.ptime > 1393520400
+ AND param3<>9001
+ AND t3.flg7 = 1
+ AND t1.did = t2.did
+ AND t2.uid = t3.uid
+ ORDER BY t1.ptime desc LIMIT 500;
+} {0 0 0 {SEARCH TABLE t1 USING INDEX t1x1 (ptime>?)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2x0 (did=?)} 0 2 2 {SEARCH TABLE t3 USING INDEX t3x0 (uid=?)}}
+#
+# ^^^--- Before being fixed, the above was using an automatic covering
+# on t3 and reordering the tables so that t3 was in the outer loop and
+# implementing the ORDER BY clause using a B-Tree.
+
+do_execsql_test autoindex2-120 {
+ EXPLAIN QUERY PLAN
+ SELECT
+ t1_id,
+ t1.did,
+ param2,
+ param3,
+ t1.ptime,
+ t1.trange,
+ t1.exmass,
+ t1.mass,
+ t1.vstatus,
+ type,
+ subtype,
+ t1.deviation,
+ t1.formula,
+ dparam1,
+ reserve1,
+ reserve2,
+ param4,
+ t1.last_operation,
+ t1.admin_uuid,
+ t1.previous_value,
+ t1.job_id,
+ client_did,
+ t1.last_t1,
+ t1.data_t1,
+ t1.previous_date,
+ param5,
+ param6,
+ mgr_uuid
+ FROM
+ t3,
+ t2,
+ t1
+ WHERE
+ t1.ptime > 1393520400
+ AND param3<>9001
+ AND t3.flg7 = 1
+ AND t1.did = t2.did
+ AND t2.uid = t3.uid
+ ORDER BY t1.ptime desc LIMIT 500;
+} {0 0 2 {SEARCH TABLE t1 USING INDEX t1x1 (ptime>?)} 0 1 1 {SEARCH TABLE t2 USING INDEX t2x0 (did=?)} 0 2 0 {SEARCH TABLE t3 USING INDEX t3x0 (uid=?)}}
+
+finish_test
diff --git a/test/autoindex3.test b/test/autoindex3.test
new file mode 100644
index 0000000..33053bb
--- /dev/null
+++ b/test/autoindex3.test
@@ -0,0 +1,58 @@
+# 2014-06-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 automatic index creation logic,
+# and specifically that an automatic index will not be created that
+# shadows a declared index.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# The t1b and t2d indexes are not very selective. It used to be that
+# the autoindex mechanism would create automatic indexes on t1(b) or
+# t2(d), make assumptions that they were reasonably selective, and use
+# them instead of t1b or t2d. But that would be cheating, because the
+# automatic index cannot be any more selective than the real index.
+#
+# This test verifies that the cheat is no longer allowed.
+#
+do_execsql_test autoindex3-100 {
+ CREATE TABLE t1(a,b,x);
+ CREATE TABLE t2(c,d,y);
+ CREATE INDEX t1b ON t1(b);
+ CREATE INDEX t2d ON t2(d);
+ ANALYZE sqlite_master;
+ INSERT INTO sqlite_stat1 VALUES('t1','t1b','10000 500');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2d','10000 500');
+ ANALYZE sqlite_master;
+ EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d=b;
+} {~/AUTO/}
+
+# Automatic indexes can still be used if existing indexes do not
+# participate in == constraints.
+#
+do_execsql_test autoindex3-110 {
+ EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d>b AND x=y;
+} {/AUTO/}
+do_execsql_test autoindex3-120 {
+ EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d<b AND x=y;
+} {/AUTO/}
+do_execsql_test autoindex3-130 {
+ EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d IS NULL AND x=y;
+} {/AUTO/}
+do_execsql_test autoindex3-140 {
+ EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE d IN (5,b) AND x=y;
+} {/AUTO/}
+
+
+finish_test
diff --git a/test/autovacuum.test b/test/autovacuum.test
index bba40e3..9ee2cd0 100644
--- a/test/autovacuum.test
+++ b/test/autovacuum.test
@@ -526,7 +526,7 @@ do_test autovacuum-4.2 {
catchsql {
CREATE UNIQUE INDEX av1_i ON av1(a);
}
-} {1 {indexed columns are not unique}}
+} {1 {UNIQUE constraint failed: av1.a}}
do_test autovacuum-4.3 {
execsql {
SELECT sum(a) FROM av1;
diff --git a/test/backcompat.test b/test/backcompat.test
index dd40fed..ea7e6a9 100644
--- a/test/backcompat.test
+++ b/test/backcompat.test
@@ -58,12 +58,24 @@ proc do_backcompat_test {rv bin1 bin2 script} {
code1 { sqlite3 db test.db }
code2 { sqlite3 db test.db }
+ foreach c {code1 code2} {
+ $c {
+ set v [split [db version] .]
+ if {[llength $v]==3} {lappend v 0}
+ set ::sqlite_libversion [format \
+ "%d%.2d%.2d%2d" [lindex $v 0] [lindex $v 1] [lindex $v 2] [lindex $v 3]
+ ]
+ }
+ }
+
uplevel $script
catch { code1 { db close } }
catch { code2 { db close } }
catch { close $::bc_chan2 }
catch { close $::bc_chan1 }
+
+
}
array set ::incompatible [list]
@@ -200,6 +212,42 @@ do_allbackcompat_test {
do_test backcompat-1.2.7 { sql1 { PRAGMA integrity_check } } {ok}
do_test backcompat-1.2.8 { sql2 { PRAGMA integrity_check } } {ok}
+
+ do_test backcompat-2.1 {
+ sql1 {
+ CREATE TABLE t2(a UNIQUE, b PRIMARY KEY, c UNIQUE);
+ INSERT INTO t2 VALUES(1,9,5);
+ INSERT INTO t2 VALUES(5,5,1);
+ INSERT INTO t2 VALUES(9,1,9);
+ SELECT * FROM t2 ORDER BY a;
+ }
+ } {1 9 5 5 5 1 9 1 9}
+ do_test backcompat-2.2 {
+ sql2 {
+ SELECT * FROM sqlite_master WHERE rootpage=-1;
+ SELECT * FROM t2 ORDER BY a;
+ }
+ } {1 9 5 5 5 1 9 1 9}
+ do_test backcompat-2.3 {
+ sql1 {
+ SELECT * FROM t2 ORDER BY b;
+ }
+ } {9 1 9 5 5 1 1 9 5}
+ do_test backcompat-2.4 {
+ sql2 {
+ SELECT * FROM t2 ORDER BY b;
+ }
+ } {9 1 9 5 5 1 1 9 5}
+ do_test backcompat-2.5 {
+ sql1 {
+ SELECT * FROM t2 ORDER BY c;
+ }
+ } {5 5 1 1 9 5 9 1 9}
+ do_test backcompat-2.6 {
+ sql2 {
+ SELECT * FROM t2 ORDER BY c;
+ }
+ } {5 5 1 1 9 5 9 1 9}
}
foreach k [lsort [array names ::incompatible]] {
puts "ERROR: Detected journal incompatibility with version $k"
@@ -345,6 +393,48 @@ ifcapable fts3 {
} {
do_test backcompat-3.7 [list sql1 $q] [sql2 $q]
}
+
+ # Now test that an incremental merge can be started by one version
+ # and finished by another. And that the integrity-check still
+ # passes.
+ do_test backcompat-3.8 {
+ sql1 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(docid, words);
+ CREATE VIRTUAL TABLE t2 USING fts3(words);
+ }
+ code1 [list source $testdir/genesis.tcl]
+ code1 { fts_kjv_genesis }
+ sql1 {
+ INSERT INTO t2 SELECT words FROM t1;
+ INSERT INTO t2 SELECT words FROM t1;
+ INSERT INTO t2 SELECT words FROM t1;
+ INSERT INTO t2 SELECT words FROM t1;
+ INSERT INTO t2 SELECT words FROM t1;
+ INSERT INTO t2 SELECT words FROM t1;
+ SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
+ }
+ } {0 {0 1 2 3 4 5}}
+
+ if {[code1 { set ::sqlite_libversion }] >=3071200
+ && [code2 { set ::sqlite_libversion }] >=3071200
+ } {
+ do_test backcompat-3.9 {
+ sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
+ sql2 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
+ sql1 { INSERT INTO t2(t2) VALUES('merge=100,4'); }
+ sql2 { INSERT INTO t2(t2) VALUES('merge=2500,4'); }
+ sql2 {
+ SELECT level, group_concat(idx, ' ') FROM t2_segdir GROUP BY level;
+ }
+ } {0 {0 1} 1 0}
+
+ do_test backcompat-3.10 {
+ sql1 { INSERT INTO t2(t2) VALUES('integrity-check') }
+ sql2 { INSERT INTO t2(t2) VALUES('integrity-check') }
+ } {}
+ }
}
}
}
diff --git a/test/backup4.test b/test/backup4.test
index a1a7735..417df80 100644
--- a/test/backup4.test
+++ b/test/backup4.test
@@ -101,4 +101,3 @@ do_test 3.3 {
do_test 3.4 { file size test.db2 } 0
finish_test
-
diff --git a/test/between.test b/test/between.test
index 4543675..df4c679 100644
--- a/test/between.test
+++ b/test/between.test
@@ -48,14 +48,24 @@ do_test between-1.0 {
# 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.
+# is done. Then it appends the names of the table and index used.
#
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]
+ set eqp [execsql "EXPLAIN QUERY PLAN $sql"]
+ # puts eqp=$eqp
+ foreach {a b c x} $eqp {
+ if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \
+ $x all as tab idx]} {
+ lappend data $tab $idx
+ } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} {
+ lappend data $tab *
+ }
+ }
+ return $data
}
do_test between-1.1.1 {
@@ -67,7 +77,7 @@ 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 {}}
+} {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
@@ -77,7 +87,7 @@ 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 {}}
+} {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
@@ -87,12 +97,12 @@ 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 {}}
+} {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 {}}
+} {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
@@ -107,7 +117,7 @@ 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 {}}
+} {4 2 25 27 sort t1 *}
finish_test
diff --git a/test/bigfile2.test b/test/bigfile2.test
index b67cb34..de3b14b 100644
--- a/test/bigfile2.test
+++ b/test/bigfile2.test
@@ -57,6 +57,6 @@ do_test 1.3 {
} $str
db close
-file delete test.db
+delete_file test.db
finish_test
diff --git a/test/boundary3.tcl b/test/boundary3.tcl
index ac3bf0a..5fc26d6 100644
--- a/test/boundary3.tcl
+++ b/test/boundary3.tcl
@@ -13,7 +13,6 @@ puts {# 2008 December 11
# This file is automatically generated from a separate TCL script.
# This file seeks to exercise integer boundary values.
#
-# $Id: boundary3.tcl,v 1.3 2009/01/02 15:45:48 shane Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -40,16 +39,16 @@ foreach x {
} {
set x [expr {wide($x)}]
set boundarynum($x) 1
- set boundarynum([expr {$x+1}]) 1
- set boundarynum([expr {-($x+1)}]) 1
- set boundarynum([expr {-($x+2)}]) 1
- set boundarynum([expr {$x+$x+1}]) 1
- set boundarynum([expr {$x+$x+2}]) 1
+ set boundarynum([expr {wide($x+1)}]) 1
+ set boundarynum([expr {wide(-($x+1))}]) 1
+ set boundarynum([expr {wide(-($x+2))}]) 1
+ set boundarynum([expr {wide($x+$x+1)}]) 1
+ set boundarynum([expr {wide($x+$x+2)}]) 1
}
set x [expr {wide(127)}]
for {set i 1} {$i<=9} {incr i} {
set boundarynum($x) 1
- set boundarynum([expr {$x+1}]) 1
+ set boundarynum([expr {wide($x+1)}]) 1
set x [expr {wide($x*128 + 127)}]
}
@@ -116,7 +115,7 @@ foreach r $nums1 {
incr a
set t1ra($r) $a
set t1ar($a) $r
- set x [format %08x%08x [expr {wide($r)>>32}] $r]
+ set x [format %016x [expr {wide($r)}]]
set t1rx($r) $x
set t1xr($x) $r
puts " INSERT INTO t1(oid,a,x) VALUES($r,$a,'$x');"
@@ -158,7 +157,7 @@ foreach r $nums3 {
set r5 $r.5
set r0 $r.0
- if {abs($r)<9.22337203685477580800e+18} {
+ if {abs($r)<0x7FFFFFFFFFFFFFFF || $r==-9223372036854775808} {
set x $t1rx($r)
set a $t1ra($r)
puts "do_test $tname-2.$i.1 \173"
diff --git a/test/btreefault.test b/test/btreefault.test
index 9b42240..61104c5 100644
--- a/test/btreefault.test
+++ b/test/btreefault.test
@@ -55,4 +55,3 @@ do_faultsim_test 1 -prep {
}
finish_test
-
diff --git a/test/capi2.test b/test/capi2.test
index 20ee340..39f50dd 100644
--- a/test/capi2.test
+++ b/test/capi2.test
@@ -237,7 +237,7 @@ do_test capi2-3.13b {db changes} {0}
do_test capi2-3.14 {
list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \
[sqlite3_extended_errcode $DB]
-} {SQLITE_CONSTRAINT {column a is not unique} SQLITE_CONSTRAINT_UNIQUE}
+} {SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.a} SQLITE_CONSTRAINT_UNIQUE}
do_test capi2-3.15 {
set VM [sqlite3_prepare $DB {CREATE TABLE t2(a NOT NULL, b)} -1 TAIL]
set TAIL
@@ -261,7 +261,7 @@ do_test capi2-3.18 {
do_test capi2-3.19 {
list [sqlite3_finalize $VM] [sqlite3_errmsg $DB] \
[sqlite3_extended_errcode $DB]
-} {SQLITE_CONSTRAINT {t2.a may not be NULL} SQLITE_CONSTRAINT_NOTNULL}
+} {SQLITE_CONSTRAINT {NOT NULL constraint failed: t2.a} SQLITE_CONSTRAINT_NOTNULL}
do_test capi2-3.20 {
execsql {
@@ -555,7 +555,7 @@ do_test capi2-6.27 {
INSERT INTO t1 VALUES(2,4,5);
SELECT * FROM t1;
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test capi2-6.28 {
list [sqlite3_step $VM1] \
[sqlite3_column_count $VM1] \
diff --git a/test/capi3.test b/test/capi3.test
index 9d7434d..cbaa5c5 100644
--- a/test/capi3.test
+++ b/test/capi3.test
@@ -16,6 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set ::testprefix capi3
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
@@ -179,16 +180,18 @@ do_test capi3-3.4 {
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]
+if {[clang_sanitize_address]==0} {
+ 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 {
@@ -661,10 +664,12 @@ do_test capi3-6.3 {
sqlite3_finalize $STMT
} {SQLITE_OK}
-do_test capi3-6.4-misuse {
- db cache flush
- sqlite3_close $DB
-} {SQLITE_OK}
+if {[clang_sanitize_address]==0} {
+ 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'
@@ -1060,11 +1065,13 @@ if {[llength [info commands sqlite3_sleep]]>0} {
}
# 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}
+#
+if {[clang_sanitize_address]==0} {
+ 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.
#
@@ -1215,6 +1222,23 @@ do_test capi3-19.1 {
sqlite3_prepare_tkt3134 db
} {}
+# Test that calling sqlite3_column_blob() on a TEXT value does not change
+# the return type of subsequent calls to sqlite3_column_type().
+#
+do_execsql_test 20.1 {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES('abcdefghij');
+}
+do_test 20.2 {
+ set stmt [sqlite3_prepare db "SELECT * FROM t4" -1 dummy]
+ sqlite3_step $stmt
+} {SQLITE_ROW}
+do_test 20.3 { sqlite3_column_type $stmt 0 } {TEXT}
+do_test 20.4 { sqlite3_column_blob $stmt 0 } {abcdefghij}
+do_test 20.5 { sqlite3_column_type $stmt 0 } {TEXT}
+do_test 20.6 { sqlite3_finalize $stmt } SQLITE_OK
+
+
# Tests of the interface when no VFS is registered.
#
if {![info exists tester_do_binarylog]} {
diff --git a/test/capi3c.test b/test/capi3c.test
index 14545c0..6388717 100644
--- a/test/capi3c.test
+++ b/test/capi3c.test
@@ -169,16 +169,18 @@ do_test capi3c-3.4 {
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]
+if {[clang_sanitize_address]==0} {
+ 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 ""
@@ -627,13 +629,17 @@ 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 {
+if {[clang_sanitize_address]==0} {
+ do_test capi3c-6.4 {
+ db cache flush
+ sqlite3_close $DB
+ } {SQLITE_OK}
+ do_test capi3c-6.99-misuse {
+ db close
+ } {}
+} else {
db close
-} {}
+}
# This procedure sets the value of the file-format in file 'test.db'
# to $newval. Also, the schema cookie is incremented.
diff --git a/test/capi3d.test b/test/capi3d.test
index 746ec20..fb8abe8 100644
--- a/test/capi3d.test
+++ b/test/capi3d.test
@@ -108,6 +108,13 @@ db eval {CREATE TABLE t1(x)}
test_is_readonly capi3d-2.3 {INSERT INTO t1 VALUES(5)} 0
test_is_readonly capi3d-2.4 {UPDATE t1 SET x=x+1 WHERE x<0} 0
test_is_readonly capi3d-2.5 {SELECT * FROM t1} 1
+ifcapable wal {
+ test_is_readonly capi3d-2.6 {PRAGMA journal_mode=WAL} 0
+ test_is_readonly capi3d-2.7 {PRAGMA wal_checkpoint} 0
+}
+test_is_readonly capi3d-2.8 {PRAGMA application_id=1234} 0
+test_is_readonly capi3d-2.9 {VACUUM} 0
+test_is_readonly capi3d-2.10 {PRAGMA integrity_check} 1
do_test capi3-2.99 {
sqlite3_stmt_readonly 0
} 1
@@ -137,4 +144,41 @@ do_test capi3d-3.99 {
sqlite3_stmt_busy 0
} {0}
+#--------------------------------------------------------------------------
+# Test the sqlite3_stmt_busy() function with ROLLBACK statements.
+#
+reset_db
+
+do_execsql_test capi3d-4.1 {
+ CREATE TABLE t4(x,y);
+ BEGIN;
+}
+
+do_test capi3d-4.2.1 {
+ breakpoint
+ set ::s1 [sqlite3_prepare_v2 db "ROLLBACK" -1 notused]
+ sqlite3_step $::s1
+} {SQLITE_DONE}
+
+do_test capi3d-4.2.2 {
+ sqlite3_stmt_busy $::s1
+} {1}
+
+do_catchsql_test capi3d-4.2.3 {
+ VACUUM
+} {1 {cannot VACUUM - SQL statements in progress}}
+
+do_test capi3d-4.2.4 {
+ sqlite3_reset $::s1
+} {SQLITE_OK}
+
+do_catchsql_test capi3d-4.2.5 {
+ VACUUM
+} {0 {}}
+
+do_test capi3d-4.2.6 {
+ sqlite3_finalize $::s1
+} {SQLITE_OK}
+
+
finish_test
diff --git a/test/capi3e.test b/test/capi3e.test
index 21304cb..3e478e7 100644
--- a/test/capi3e.test
+++ b/test/capi3e.test
@@ -20,7 +20,11 @@ source $testdir/tester.tcl
# Make sure the system encoding is utf-8. Otherwise, if the system encoding
# is other than utf-8, [file isfile $x] may not refer to the same file
# as [sqlite3 db $x].
-encoding system utf-8
+#
+# This is no longer needed here because it should be done within the test
+# fixture executable itself, via Tcl_SetSystemEncoding.
+#
+# encoding system utf-8
# Do not use a codec for tests in this file, as the database file is
# manipulated directly using tcl scripts (using the [hexio_write] command).
@@ -60,7 +64,7 @@ proc utf8 {str} {
db close
# here's the list of file names we're testing
-set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db}
+set names {t 1 t. 1. t.d 1.d t-1 1-1 t.db ä.db ë.db ö.db ü.db ÿ.db}
set i 0
foreach name $names {
diff --git a/test/check.test b/test/check.test
index 99b72ac..02b99f2 100644
--- a/test/check.test
+++ b/test/check.test
@@ -41,7 +41,7 @@ do_test check-1.3 {
catchsql {
INSERT INTO t1 VALUES(6,7);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-1.4 {
execsql {
SELECT * FROM t1;
@@ -51,7 +51,7 @@ do_test check-1.5 {
catchsql {
INSERT INTO t1 VALUES(4,3);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-1.6 {
execsql {
SELECT * FROM t1;
@@ -88,7 +88,7 @@ do_test check-1.12 {
catchsql {
UPDATE t1 SET x=7 WHERE x==2
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-1.13 {
execsql {
SELECT * FROM t1;
@@ -98,7 +98,7 @@ do_test check-1.14 {
catchsql {
UPDATE t1 SET x=5 WHERE x==2
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-1.15 {
execsql {
SELECT * FROM t1;
@@ -142,17 +142,17 @@ do_test check-2.4 {
catchsql {
INSERT INTO t2 VALUES(1.1, NULL, NULL);
}
-} {1 {constraint one failed}}
+} {1 {CHECK constraint failed: one}}
do_test check-2.5 {
catchsql {
INSERT INTO t2 VALUES(NULL, 5, NULL);
}
-} {1 {constraint two failed}}
+} {1 {CHECK constraint failed: two}}
do_test check-2.6 {
catchsql {
INSERT INTO t2 VALUES(NULL, NULL, 3.14159);
}
-} {1 {constraint three failed}}
+} {1 {CHECK constraint failed: three}}
# Undocumented behavior: The CONSTRAINT name clause can follow a constraint.
# Such a clause is ignored. But the parser must accept it for backwards
@@ -172,7 +172,7 @@ do_test check-2.11 {
catchsql {
INSERT INTO t2b VALUES('xyzzy','hi',5);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t2b}}
do_test check-2.12 {
execsql {
CREATE TABLE t2c(
@@ -188,7 +188,7 @@ do_test check-2.13 {
catchsql {
INSERT INTO t2c VALUES('xyzzy',7,8);
}
-} {1 {constraint x_two failed}}
+} {1 {CHECK constraint failed: x_two}}
do_test check-2.cleanup {
execsql {
DROP TABLE IF EXISTS t2b;
@@ -256,7 +256,7 @@ do_test check-3.9 {
catchsql {
INSERT INTO t3 VALUES(111,222,333);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t3}}
do_test check-4.1 {
execsql {
@@ -298,7 +298,7 @@ do_test check-4.6 {
catchsql {
UPDATE t4 SET x=0, y=1;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t4}}
do_test check-4.7 {
execsql {
SELECT * FROM t4;
@@ -316,7 +316,7 @@ do_test check-4.9 {
PRAGMA ignore_check_constraints=OFF;
UPDATE t4 SET x=0, y=2;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t4}}
ifcapable vacuum {
do_test check_4.10 {
catchsql {
@@ -367,7 +367,7 @@ do_test check-6.5 {
catchsql {
UPDATE OR FAIL t1 SET x=7-x, y=y+1;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-6.6 {
execsql {
SELECT * FROM t1;
@@ -379,7 +379,7 @@ do_test check-6.7 {
INSERT INTO t1 VALUES(1,30.0);
INSERT OR ROLLBACK INTO t1 VALUES(8,40.0);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-6.8 {
catchsql {
COMMIT;
@@ -398,7 +398,7 @@ do_test check-6.12 {
catchsql {
REPLACE INTO t1 VALUES(6,7);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
do_test check-6.13 {
execsql {SELECT * FROM t1}
} {3 12.0 2 20.0}
@@ -426,7 +426,8 @@ db func myfunc myfunc
do_execsql_test 7.1 { CREATE TABLE t6(a CHECK (myfunc(a))) }
do_execsql_test 7.2 { INSERT INTO t6 VALUES(9) }
-do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } {1 {constraint failed}}
+do_catchsql_test 7.3 { INSERT INTO t6 VALUES(11) } \
+ {1 {CHECK constraint failed: t6}}
do_test 7.4 {
sqlite3 db2 test.db
@@ -449,7 +450,13 @@ do_test 7.7 {
do_test 7.8 {
db2 func myfunc myfunc
catchsql { INSERT INTO t6 VALUES(12) } db2
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t6}}
+# 2013-08-02: Silently ignore database name qualifiers in CHECK constraints.
+#
+do_execsql_test 8.1 {
+ CREATE TABLE t810(a, CHECK( main.t810.a>0 ));
+ CREATE TABLE t811(b, CHECK( xyzzy.t811.b BETWEEN 5 AND 10 ));
+} {}
finish_test
diff --git a/test/close.test b/test/close.test
index 355a886..d5d6391 100644
--- a/test/close.test
+++ b/test/close.test
@@ -76,4 +76,3 @@ do_test 1.4.4 {
} {SQLITE_OK}
finish_test
-
diff --git a/test/closure01.test b/test/closure01.test
index 5dac87a..213ef4e 100644
--- a/test/closure01.test
+++ b/test/closure01.test
@@ -15,51 +15,68 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix closure01
-ifcapable !vtab { finish_test ; return }
+ifcapable !vtab||!cte { finish_test ; return }
load_static_extension db closure
do_execsql_test 1.0 {
BEGIN;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y INTEGER);
+ WITH RECURSIVE
+ cnt(i) AS (VALUES(1) UNION ALL SELECT i+1 FROM cnt LIMIT 131072)
+ INSERT INTO t1(x, y) SELECT i, nullif(i,1)/2 FROM cnt;
CREATE INDEX t1y ON t1(y);
- INSERT INTO t1(x) VALUES(1),(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;
- INSERT INTO t1(x) SELECT x+512 FROM t1;
- INSERT INTO t1(x) SELECT x+1024 FROM t1;
- INSERT INTO t1(x) SELECT x+2048 FROM t1;
- INSERT INTO t1(x) SELECT x+4096 FROM t1;
- INSERT INTO t1(x) SELECT x+8192 FROM t1;
- INSERT INTO t1(x) SELECT x+16384 FROM t1;
- INSERT INTO t1(x) SELECT x+32768 FROM t1;
- INSERT INTO t1(x) SELECT x+65536 FROM t1;
- UPDATE t1 SET y=x/2 WHERE x>1;
COMMIT;
CREATE VIRTUAL TABLE cx
USING transitive_closure(tablename=t1, idcolumn=x, parentcolumn=y);
} {}
# The entire table
-do_execsql_test 1.1 {
+do_timed_execsql_test 1.1 {
SELECT count(*), depth FROM cx WHERE root=1 GROUP BY depth ORDER BY 1;
} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/}
+do_timed_execsql_test 1.1-cte {
+ WITH RECURSIVE
+ below(id,depth) AS (
+ VALUES(1,0)
+ UNION ALL
+ SELECT t1.x, below.depth+1
+ FROM t1 JOIN below on t1.y=below.id
+ )
+ SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1;
+} {/1 0 1 17 2 1 4 2 8 3 16 4 .* 65536 16/}
# descendents of 32768
-do_execsql_test 1.2 {
+do_timed_execsql_test 1.2 {
SELECT * FROM cx WHERE root=32768 ORDER BY id;
} {32768 0 65536 1 65537 1 131072 2}
+do_timed_execsql_test 1.2-cte {
+ WITH RECURSIVE
+ below(id,depth) AS (
+ VALUES(32768,0)
+ UNION ALL
+ SELECT t1.x, below.depth+1
+ FROM t1 JOIN below on t1.y=below.id
+ WHERE below.depth<2
+ )
+ SELECT id, depth FROM below ORDER BY id;
+} {32768 0 65536 1 65537 1 131072 2}
# descendents of 16384
-do_execsql_test 1.3 {
+do_timed_execsql_test 1.3 {
SELECT * FROM cx WHERE root=16384 AND depth<=2 ORDER BY id;
} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2}
+do_timed_execsql_test 1.3-cte {
+ WITH RECURSIVE
+ below(id,depth) AS (
+ VALUES(16384,0)
+ UNION ALL
+ SELECT t1.x, below.depth+1
+ FROM t1 JOIN below on t1.y=below.id
+ WHERE below.depth<2
+ )
+ SELECT id, depth FROM below ORDER BY id;
+} {16384 0 32768 1 32769 1 65536 2 65537 2 65538 2 65539 2}
# children of 16384
do_execsql_test 1.4 {
@@ -70,19 +87,41 @@ do_execsql_test 1.4 {
} {32768 1 {} t1 x y 32769 1 {} t1 x y}
# great-grandparent of 16384
-do_execsql_test 1.5 {
+do_timed_execsql_test 1.5 {
SELECT id, depth, root, tablename, idcolumn, parentcolumn FROM cx
WHERE root=16384
AND depth=3
AND idcolumn='Y'
AND parentcolumn='X';
} {2048 3 {} t1 Y X}
+do_timed_execsql_test 1.5-cte {
+ WITH RECURSIVE
+ above(id,depth) AS (
+ VALUES(16384,0)
+ UNION ALL
+ SELECT t1.y, above.depth+1
+ FROM t1 JOIN above ON t1.x=above.id
+ WHERE above.depth<3
+ )
+ SELECT id FROM above WHERE depth=3;
+} {2048}
# depth<5
-do_execsql_test 1.6 {
+do_timed_execsql_test 1.6 {
SELECT count(*), depth FROM cx WHERE root=1 AND depth<5
GROUP BY depth ORDER BY 1;
} {1 0 2 1 4 2 8 3 16 4}
+do_timed_execsql_test 1.6-cte {
+ WITH RECURSIVE
+ below(id,depth) AS (
+ VALUES(1,0)
+ UNION ALL
+ SELECT t1.x, below.depth+1
+ FROM t1 JOIN below ON t1.y=below.id
+ WHERE below.depth<4
+ )
+ SELECT count(*), depth FROM below GROUP BY depth ORDER BY 1;
+} {1 0 2 1 4 2 8 3 16 4}
# depth<=5
do_execsql_test 1.7 {
@@ -103,9 +142,20 @@ do_execsql_test 1.9 {
} {8 3 16 4 32 5}
# depth==5 with min() and max()
-do_execsql_test 1.10 {
+do_timed_execsql_test 1.10 {
SELECT count(*), min(id), max(id) FROM cx WHERE root=1 AND depth=5;
} {32 32 63}
+do_timed_execsql_test 1.10-cte {
+ WITH RECURSIVE
+ below(id,depth) AS (
+ VALUES(1,0)
+ UNION ALL
+ SELECT t1.x, below.depth+1
+ FROM t1 JOIN below ON t1.y=below.id
+ WHERE below.depth<5
+ )
+ SELECT count(*), min(id), max(id) FROM below WHERE depth=5;
+} {32 32 63}
# Create a much smaller table t2 with only 32 elements
db eval {
diff --git a/test/collate1.test b/test/collate1.test
index b493ee8..2085415 100644
--- a/test/collate1.test
+++ b/test/collate1.test
@@ -75,7 +75,6 @@ do_test collate1-1.1 {
}
} {{} 0x119 0x2D}
do_test collate1-1.2 {
-breakpoint
execsql {
SELECT c2 FROM collate1t1 ORDER BY 1 COLLATE hex;
}
@@ -305,4 +304,33 @@ do_test collate1-4.5 {
}
} {}
+# A problem reported on the mailing list: A CREATE TABLE statement
+# is allowed to have two or more COLLATE clauses on the same column.
+# That probably ought to be an error, but we allow it for backwards
+# compatibility. Just make sure it works and doesn't leak memory.
+#
+do_test collate1-5.1 {
+ execsql {
+ CREATE TABLE c5(
+ id INTEGER PRIMARY KEY,
+ a TEXT COLLATE binary COLLATE nocase COLLATE rtrim,
+ b TEXT COLLATE nocase COLLATE binary,
+ c TEXT COLLATE rtrim COLLATE binary COLLATE rtrim COLLATE nocase
+ );
+ INSERT INTO c5 VALUES(1, 'abc','abc','abc');
+ INSERT INTO c5 VALUES(2, 'abc ','ABC','ABC');
+ SELECT id FROM c5 WHERE a='abc' ORDER BY id;
+ }
+} {1 2}
+do_test collate1-5.2 {
+ execsql {
+ SELECT id FROM c5 WHERE b='abc' ORDER BY id;
+ }
+} {1}
+do_test collate1-5.3 {
+ execsql {
+ SELECT id FROM c5 WHERE c='abc' ORDER BY id;
+ }
+} {1 2}
+
finish_test
diff --git a/test/collate2.test b/test/collate2.test
index bf61923..d5aadb4 100644
--- a/test/collate2.test
+++ b/test/collate2.test
@@ -17,6 +17,8 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set ::testprefix collate2
+
#
# Tests are organised as follows:
#
@@ -636,13 +638,15 @@ do_test collate2-4.2 {
do_test collate2-4.3 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t3
- WHERE collate2t1.b = collate2t3.b||'';
+ WHERE collate2t1.b = collate2t3.b||''
+ ORDER BY +collate2t1.a DESC;
}
} {aa aA Aa AA}
do_test collate2-4.4 {
execsql {
SELECT collate2t1.a FROM collate2t1, collate2t3
- WHERE collate2t3.b||'' = collate2t1.b;
+ WHERE collate2t3.b||'' = collate2t1.b
+ ORDER BY +collate2t1.a DESC;
}
} {aa aA Aa AA}
@@ -691,4 +695,30 @@ do_test collate2-5.5 {
}
} {aa aa}
+do_execsql_test 6.1 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES('b');
+ INSERT INTO t1 VALUES('B');
+}
+do_execsql_test 6.2 {
+ SELECT * FROM t1 WHERE x COLLATE nocase BETWEEN 'a' AND 'c';
+} {b B}
+do_execsql_test 6.3 {
+ SELECT * FROM t1 WHERE x BETWEEN 'a' COLLATE nocase AND 'c' COLLATE nocase;
+} {b B}
+do_execsql_test 6.4 {
+ SELECT * FROM t1
+ WHERE x COLLATE nocase BETWEEN 'a' COLLATE nocase AND 'c' COLLATE nocase;
+} {b B}
+do_execsql_test 6.5 {
+ SELECT * FROM t1 WHERE +x COLLATE nocase BETWEEN 'a' AND 'c';
+} {b B}
+do_execsql_test 6.6 {
+ SELECT * FROM t1 WHERE +x BETWEEN 'a' COLLATE nocase AND 'c' COLLATE nocase;
+} {b B}
+do_execsql_test 6.7 {
+ SELECT * FROM t1
+ WHERE +x COLLATE nocase BETWEEN 'a' COLLATE nocase AND 'c' COLLATE nocase;
+} {b B}
+
finish_test
diff --git a/test/collate4.test b/test/collate4.test
index 5ae1e7b..2ddf53d 100644
--- a/test/collate4.test
+++ b/test/collate4.test
@@ -473,7 +473,7 @@ do_test collate4-3.1 {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.2 {
execsql {
SELECT * FROM collate4t1;
@@ -483,13 +483,13 @@ do_test collate4-3.3 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.4 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.5 {
execsql {
DROP TABLE collate4t1;
@@ -501,7 +501,7 @@ do_test collate4-3.6 {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.7 {
execsql {
SELECT * FROM collate4t1;
@@ -511,13 +511,13 @@ do_test collate4-3.8 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.9 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.10 {
execsql {
DROP TABLE collate4t1;
@@ -530,7 +530,7 @@ do_test collate4-3.11 {
INSERT INTO collate4t1 VALUES('abc');
INSERT INTO collate4t1 VALUES('ABC');
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.12 {
execsql {
SELECT * FROM collate4t1;
@@ -540,13 +540,13 @@ do_test collate4-3.13 {
catchsql {
INSERT INTO collate4t1 SELECT upper(a) FROM collate4t1;
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.14 {
catchsql {
INSERT INTO collate4t1 VALUES(1);
UPDATE collate4t1 SET a = 'abc';
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: collate4t1.a}}
do_test collate4-3.15 {
execsql {
diff --git a/test/conflict.test b/test/conflict.test
index 6576959..af5668e 100644
--- a/test/conflict.test
+++ b/test/conflict.test
@@ -242,7 +242,7 @@ foreach {i conf1 cmd t0 t1 t2} {
15 {} {INSERT OR ABORT} 1 {} 1
16 {} {INSERT OR ROLLBACK} 1 {} {}
} {
- if {$t0} {set t1 {t1.c may not be NULL}}
+ if {$t0} {set t1 {NOT NULL constraint failed: t1.c}}
do_test conflict-5.$i {
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
set r0 [catch {execsql [subst {
@@ -306,7 +306,7 @@ foreach {i conf1 cmd t0 t1 t2 t3 t4} {
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 {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
set t3 0
} else {
@@ -493,13 +493,13 @@ do_test conflict-9.5 {
INSERT INTO t2 VALUES(3,1,3,3,3);
SELECT * FROM t2;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t2.b}}
do_test conflict-9.6 {
catchsql {
UPDATE t2 SET b=b+1 WHERE b=1;
SELECT * FROM t2;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t2.b}}
do_test conflict-9.7 {
catchsql {
BEGIN;
@@ -507,7 +507,7 @@ do_test conflict-9.7 {
INSERT INTO t2 VALUES(3,1,3,3,3);
SELECT * FROM t2;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t2.b}}
do_test conflict-9.8 {
execsql {COMMIT}
execsql {SELECT * FROM t3}
@@ -519,7 +519,7 @@ do_test conflict-9.9 {
UPDATE t2 SET b=b+1 WHERE b=1;
SELECT * FROM t2;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t2.b}}
do_test conflict-9.10 {
execsql {COMMIT}
execsql {SELECT * FROM t3}
@@ -529,13 +529,13 @@ do_test conflict-9.11 {
INSERT INTO t2 VALUES(3,3,3,1,3);
SELECT * FROM t2;
}
-} {1 {column d is not unique}}
+} {1 {UNIQUE constraint failed: t2.d}}
do_test conflict-9.12 {
catchsql {
UPDATE t2 SET d=d+1 WHERE d=1;
SELECT * FROM t2;
}
-} {1 {column d is not unique}}
+} {1 {UNIQUE constraint failed: t2.d}}
do_test conflict-9.13 {
catchsql {
BEGIN;
@@ -543,7 +543,7 @@ do_test conflict-9.13 {
INSERT INTO t2 VALUES(3,3,3,1,3);
SELECT * FROM t2;
}
-} {1 {column d is not unique}}
+} {1 {UNIQUE constraint failed: t2.d}}
do_test conflict-9.14 {
execsql {COMMIT}
execsql {SELECT * FROM t3}
@@ -555,7 +555,7 @@ do_test conflict-9.15 {
UPDATE t2 SET d=d+1 WHERE d=1;
SELECT * FROM t2;
}
-} {1 {column d is not unique}}
+} {1 {UNIQUE constraint failed: t2.d}}
do_test conflict-9.16 {
execsql {COMMIT}
execsql {SELECT * FROM t3}
@@ -565,13 +565,13 @@ do_test conflict-9.17 {
INSERT INTO t2 VALUES(3,3,3,3,1);
SELECT * FROM t2;
}
-} {1 {column e is not unique}}
+} {1 {UNIQUE constraint failed: t2.e}}
do_test conflict-9.18 {
catchsql {
UPDATE t2 SET e=e+1 WHERE e=1;
SELECT * FROM t2;
}
-} {1 {column e is not unique}}
+} {1 {UNIQUE constraint failed: t2.e}}
do_test conflict-9.19 {
catchsql {
BEGIN;
@@ -579,7 +579,7 @@ do_test conflict-9.19 {
INSERT INTO t2 VALUES(3,3,3,3,1);
SELECT * FROM t2;
}
-} {1 {column e is not unique}}
+} {1 {UNIQUE constraint failed: t2.e}}
verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE
do_test conflict-9.20 {
catch {execsql {COMMIT}}
@@ -592,7 +592,7 @@ do_test conflict-9.21 {
UPDATE t2 SET e=e+1 WHERE e=1;
SELECT * FROM t2;
}
-} {1 {column e is not unique}}
+} {1 {UNIQUE constraint failed: t2.e}}
verify_ex_errcode conflict-9.21b SQLITE_CONSTRAINT_UNIQUE
do_test conflict-9.22 {
catch {execsql {COMMIT}}
@@ -782,7 +782,7 @@ do_test conflict-12.3 {
catchsql {
UPDATE t5 SET a=a+1 WHERE a=1;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t5.a}}
verify_ex_errcode conflict-12.3b SQLITE_CONSTRAINT_PRIMARYKEY
do_test conflict-12.4 {
execsql {
@@ -790,6 +790,14 @@ do_test conflict-12.4 {
SELECT * FROM t5;
}
} {2 one}
+do_test conflict-12.5 {
+ catchsql {
+ CREATE TABLE t5b(x);
+ INSERT INTO t5b(rowid, x) VALUES(1,10),(2,11);
+ UPDATE t5b SET rowid=rowid+1 WHERE x=10;
+ }
+} {1 {UNIQUE constraint failed: t5b.rowid}}
+verify_ex_errcode conflict-12.5b SQLITE_CONSTRAINT_ROWID
# Ticket [c38baa3d969eab7946dc50ba9d9b4f0057a19437]
@@ -804,7 +812,7 @@ do_test conflict-13.1 {
catchsql {
REPLACE INTO t13 VALUES(2);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t13}}
verify_ex_errcode conflict-13.1b SQLITE_CONSTRAINT_CHECK
do_test conflict-13.2 {
execsql {
diff --git a/test/conflict2.test b/test/conflict2.test
new file mode 100644
index 0000000..8419f1a
--- /dev/null
+++ b/test/conflict2.test
@@ -0,0 +1,855 @@
+# 2013-11-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.
+#
+# This file implements tests for the conflict resolution extension
+# in WITHOUT ROWID tables
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !conflict {
+ finish_test
+ return
+}
+
+# Create tables for the first group of tests.
+#
+do_test conflict2-1.0 {
+ execsql {
+ CREATE TABLE t1(a, b, c, PRIMARY KEY(a,b)) WITHOUT rowid;
+ 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 conflict2-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 conflict2-2.0 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(a,b)) WITHOUT rowid;
+ 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 conflict2-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 conflict2-3.0 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ CREATE TABLE t1(a, b, c INTEGER, PRIMARY KEY(c), UNIQUE(a,b)) WITHOUT rowid;
+ 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 conflict2-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 conflict2-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 conflict2-4.$i {
+ if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
+ set r0 [catch {execsql [subst {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,PRIMARY KEY(a,b) $conf1) WITHOUT rowid;
+ 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 conflict2-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 {NOT NULL constraint failed: t1.c}}
+ do_test conflict2-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 conflict2-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 1
+ 9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0 0
+ 10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0 1
+ 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 1
+ 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
+} {
+
+ # When using in-memory journals, no temporary files are required for
+ # statement journals.
+ if {[permutation] == "inmemory_journal"} { set t4 0 }
+
+ if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
+ if {[info exists TEMP_STORE] && $TEMP_STORE==3} {
+ set t3 0
+ } else {
+ set t3 [expr {$t3+$t4}]
+ }
+ do_test conflict2-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, PRIMARY KEY(a) $conf1) WITHOUT rowid;
+ 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 conflict2-7.1 {
+ execsql {
+ DROP TABLE t1;
+ DROP TABLE t2;
+ DROP TABLE t3;
+ CREATE TABLE t1(a PRIMARY KEY, b) without rowid;
+ }
+ 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 conflict2-7.2 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE OR IGNORE t1 SET a=1000;
+ }
+} {1}
+do_test conflict2-7.2.1 {
+ db changes
+} {1}
+do_test conflict2-7.3 {
+ execsql {
+ SELECT b FROM t1 WHERE a=1000;
+ }
+} {2}
+do_test conflict2-7.4 {
+ execsql {
+ SELECT count(*) FROM t1;
+ }
+} {50}
+do_test conflict2-7.5 {
+ execsql {
+ PRAGMA count_changes=on;
+ UPDATE OR REPLACE t1 SET a=1001;
+ }
+} {50}
+do_test conflict2-7.5.1 {
+ db changes
+} {50}
+do_test conflict2-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 conflict2-7.7.1 {
+ db changes
+} {50}
+
+# Make sure the row count is right for rows that are ignored on
+# an insert.
+#
+do_test conflict2-8.1 {
+ execsql {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(1,2);
+ }
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(2,3);
+ }
+} {1}
+do_test conflict2-8.1.1 {
+ db changes
+} {1}
+do_test conflict2-8.2 {
+ execsql {
+ INSERT OR IGNORE INTO t1 VALUES(2,4);
+ }
+} {0}
+do_test conflict2-8.2.1 {
+ db changes
+} {0}
+do_test conflict2-8.3 {
+ execsql {
+ INSERT OR REPLACE INTO t1 VALUES(2,4);
+ }
+} {1}
+do_test conflict2-8.3.1 {
+ db changes
+} {1}
+do_test conflict2-8.4 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT * FROM t1;
+ }
+} {0}
+do_test conflict2-8.4.1 {
+ db changes
+} {0}
+do_test conflict2-8.5 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT a+2,b+2 FROM t1;
+ }
+} {2}
+do_test conflict2-8.5.1 {
+ db changes
+} {2}
+do_test conflict2-8.6 {
+ execsql {
+ INSERT OR IGNORE INTO t1 SELECT a+3,b+3 FROM t1;
+ }
+} {3}
+do_test conflict2-8.6.1 {
+ db changes
+} {3}
+
+integrity_check conflict2-8.99
+
+do_test conflict2-9.1 {
+ execsql {
+ PRAGMA count_changes=0;
+ CREATE TABLE t2(
+ a INTEGER PRIMARY KEY 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
+ ) WITHOUT rowid;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 VALUES(1);
+ SELECT * FROM t3;
+ }
+} {1}
+do_test conflict2-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 conflict2-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 conflict2-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 conflict2-9.5 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,1,3,3,3);
+ }
+} {1 {UNIQUE constraint failed: t2.b}}
+do_test conflict2-9.5b {
+ db eval {SELECT * FROM t2;}
+} {1 1 1 1 1 2 2 2 2 2}
+do_test conflict2-9.6 {
+ catchsql {
+ UPDATE t2 SET b=b+1 WHERE b=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.b}}
+do_test conflict2-9.6b {
+ db eval {SELECT * FROM t2;}
+} {1 1 1 1 1 2 2 2 2 2}
+do_test conflict2-9.7 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,1,3,3,3);
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.b}}
+do_test conflict2-9.8 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {2}
+do_test conflict2-9.9 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET b=b+1 WHERE b=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.b}}
+do_test conflict2-9.10 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {3}
+do_test conflict2-9.11 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,3,3,1,3);
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.d}}
+do_test conflict2-9.12 {
+ catchsql {
+ UPDATE t2 SET d=d+1 WHERE d=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.d}}
+do_test conflict2-9.13 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,3,3,1,3);
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.d}}
+do_test conflict2-9.14 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {4}
+do_test conflict2-9.15 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET d=d+1 WHERE d=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.d}}
+do_test conflict2-9.16 {
+ execsql {COMMIT}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict2-9.17 {
+ catchsql {
+ INSERT INTO t2 VALUES(3,3,3,3,1);
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.e}}
+do_test conflict2-9.18 {
+ catchsql {
+ UPDATE t2 SET e=e+1 WHERE e=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.e}}
+do_test conflict2-9.19 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ INSERT INTO t2 VALUES(3,3,3,3,1);
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.e}}
+verify_ex_errcode conflict2-9.21b SQLITE_CONSTRAINT_UNIQUE
+do_test conflict2-9.20 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict2-9.21 {
+ catchsql {
+ BEGIN;
+ UPDATE t3 SET x=x+1;
+ UPDATE t2 SET e=e+1 WHERE e=1;
+ SELECT * FROM t2;
+ }
+} {1 {UNIQUE constraint failed: t2.e}}
+verify_ex_errcode conflict2-9.21b SQLITE_CONSTRAINT_UNIQUE
+do_test conflict2-9.22 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {5}
+do_test conflict2-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 conflict2-9.24 {
+ catchsql {
+ UPDATE t2 SET c=c-1 WHERE c=2;
+ SELECT * FROM t2;
+ }
+} {0 {2 2 1 2 2}}
+do_test conflict2-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 conflict2-9.26 {
+ catch {execsql {COMMIT}}
+ execsql {SELECT * FROM t3}
+} {6}
+
+do_test conflict2-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 conflict2-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 conflict2-11.1 {
+ execsql {
+ -- Create a database object (pages 2, 3 of the file)
+ BEGIN;
+ CREATE TABLE abc(a PRIMARY KEY, b, c) WITHOUT rowid;
+ 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 conflict2-11.2
+
+# Repeat test conflict2-11.1 but this time commit.
+#
+do_test conflict2-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 conflict2-11.1 but this time commit.
+#
+do_test conflict2-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 conflict2-11.6
+
+# Make sure UPDATE OR REPLACE works on tables that have only
+# an INTEGER PRIMARY KEY.
+#
+do_test conflict2-12.1 {
+ execsql {
+ CREATE TABLE t5(a INTEGER PRIMARY KEY, b text) WITHOUT rowid;
+ INSERT INTO t5 VALUES(1,'one');
+ INSERT INTO t5 VALUES(2,'two');
+ SELECT * FROM t5
+ }
+} {1 one 2 two}
+do_test conflict2-12.2 {
+ execsql {
+ UPDATE OR IGNORE t5 SET a=a+1 WHERE a=1;
+ SELECT * FROM t5;
+ }
+} {1 one 2 two}
+do_test conflict2-12.3 {
+ catchsql {
+ UPDATE t5 SET a=a+1 WHERE a=1;
+ }
+} {1 {UNIQUE constraint failed: t5.a}}
+verify_ex_errcode conflict2-12.3b SQLITE_CONSTRAINT_PRIMARYKEY
+do_test conflict2-12.4 {
+ execsql {
+ UPDATE OR REPLACE t5 SET a=a+1 WHERE a=1;
+ SELECT * FROM t5;
+ }
+} {2 one}
+
+
+# Ticket [c38baa3d969eab7946dc50ba9d9b4f0057a19437]
+# REPLACE works like ABORT on a CHECK constraint.
+#
+do_test conflict2-13.1 {
+ execsql {
+ CREATE TABLE t13(a PRIMARY KEY CHECK(a!=2)) WITHOUT rowid;
+ BEGIN;
+ REPLACE INTO t13 VALUES(1);
+ }
+ catchsql {
+ REPLACE INTO t13 VALUES(2);
+ }
+} {1 {CHECK constraint failed: t13}}
+verify_ex_errcode conflict2-13.1b SQLITE_CONSTRAINT_CHECK
+do_test conflict2-13.2 {
+ execsql {
+ REPLACE INTO t13 VALUES(3);
+ COMMIT;
+ SELECT * FROM t13;
+ }
+} {1 3}
+
+# Test for an unreleased bug in the REPLACE conflict resolution
+# discovered on 2013-11-09.
+#
+do_execsql_test conflict2-14.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(
+ x TEXT PRIMARY KEY NOT NULL,
+ y TEXT NOT NULL,
+ z INTEGER
+ );
+ INSERT INTO t1 VALUES('alpha','beta',1);
+ CREATE UNIQUE INDEX t1xy ON t1(x,y);
+ REPLACE INTO t1(x,y,z) VALUES('alpha','gamma',1);
+ PRAGMA integrity_check;
+ SELECT x,y FROM t1 INDEXED BY t1xy;
+ SELECT x,y,z FROM t1 NOT INDEXED;
+} {ok alpha gamma alpha gamma 1}
+do_execsql_test conflict2-14.2 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(
+ x TEXT PRIMARY KEY NOT NULL,
+ y TEXT NOT NULL,
+ z INTEGER
+ ) WITHOUT ROWID;
+ INSERT INTO t1 VALUES('alpha','beta',1);
+ CREATE UNIQUE INDEX t1xy ON t1(x,y);
+ REPLACE INTO t1(x,y,z) VALUES('alpha','gamma',1);
+ PRAGMA integrity_check;
+ SELECT x,y FROM t1 INDEXED BY t1xy;
+ SELECT x,y,z FROM t1 NOT INDEXED;
+} {ok alpha gamma alpha gamma 1}
+
+
+
+finish_test
diff --git a/test/conflict3.test b/test/conflict3.test
new file mode 100644
index 0000000..b51a55e
--- /dev/null
+++ b/test/conflict3.test
@@ -0,0 +1,356 @@
+# 2013-11-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 for the conflict resolution extension
+# to SQLite.
+#
+# This file focuses on making sure that combinations of REPLACE,
+# IGNORE, and FAIL conflict resolution play well together.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !conflict {
+ finish_test
+ return
+}
+
+do_execsql_test conflict-1.1 {
+ CREATE TABLE t1(
+ a INTEGER PRIMARY KEY ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-1.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-1.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-1.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Replete the tests above, but this time on a table non-INTEGER primary key.
+#
+do_execsql_test conflict-2.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a INT PRIMARY KEY ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-2.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-2.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-2.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Replete again on a WITHOUT ROWID table.
+#
+do_execsql_test conflict-3.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a INT PRIMARY KEY ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ ) WITHOUT ROWID;
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-3.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-3.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-3.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-4.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ b UNIQUE ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL,
+ a INT PRIMARY KEY ON CONFLICT REPLACE
+ ) WITHOUT ROWID;
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-4.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-4.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-4.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-5.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ b UNIQUE ON CONFLICT IGNORE,
+ a INT PRIMARY KEY ON CONFLICT REPLACE,
+ c UNIQUE ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-5.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-5.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-5.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-6.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ c UNIQUE ON CONFLICT FAIL,
+ a INT PRIMARY KEY ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-6.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-6.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-6.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-7.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a UNIQUE ON CONFLICT REPLACE,
+ b INTEGER PRIMARY KEY ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-7.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-7.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-7.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-8.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a UNIQUE ON CONFLICT REPLACE,
+ b INT PRIMARY KEY ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-8.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-8.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-8.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-9.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a UNIQUE ON CONFLICT REPLACE,
+ b INT PRIMARY KEY ON CONFLICT IGNORE,
+ c UNIQUE ON CONFLICT FAIL
+ ) WITHOUT ROWID;
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-9.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-9.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-9.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-10.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a UNIQUE ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE,
+ c INTEGER PRIMARY KEY ON CONFLICT FAIL
+ );
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-10.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-10.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-10.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-11.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(
+ a UNIQUE ON CONFLICT REPLACE,
+ b UNIQUE ON CONFLICT IGNORE,
+ c PRIMARY KEY ON CONFLICT FAIL
+ ) WITHOUT ROWID;
+ INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B. The insert should be ignored.
+#
+do_execsql_test conflict-11.2 {
+ INSERT INTO t1(a,b,c) VALUES(3,2,5);
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C. The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-11.3 {
+ catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-11.4 {
+ SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+
+finish_test
diff --git a/test/contrib01.test b/test/contrib01.test
new file mode 100644
index 0000000..43ea47c
--- /dev/null
+++ b/test/contrib01.test
@@ -0,0 +1,90 @@
+# 2013-06-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 contains test cases that were contributed on the sqlite-users
+# mailing list on 2013-06-05 by Mi Chen at mi.chen@echostar.com.
+#
+# At the time it was contributed, this test failed on trunk, but
+# worked on the NGQP.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Build some test data
+#
+do_test contrib01-1.0 {
+ db eval {
+ CREATE TABLE T1 (B INTEGER NOT NULL,
+ C INTEGER NOT NULL,
+ D INTEGER NOT NULL,
+ E INTEGER NOT NULL,
+ F INTEGER NOT NULL,
+ G INTEGER NOT NULL,
+ H INTEGER NOT NULL,
+ PRIMARY KEY (B, C, D));
+
+ CREATE TABLE T2 (A INTEGER NOT NULL,
+ B INTEGER NOT NULL,
+ C INTEGER NOT NULL,
+ PRIMARY KEY (A, B, C));
+
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15527);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15560);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15561);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15563);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15564);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15566);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15567);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15569);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15612);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15613);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15638);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15681);
+ INSERT INTO T2(A, B, C) VALUES(702118,16183,15682);
+
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15527,6,0,5,5,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15560,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15561,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15563,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15564,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15566,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15567,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15569,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15612,6,0,5,5,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15613,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15638,6,0,5,2,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15681,6,0,5,5,0);
+ INSERT INTO T1(B, C, D, E, F, G, H) VALUES(16183,15682,6,0,5,2,0);
+ }
+} {}
+do_test contrib01-1.1 {
+ db eval {
+ SELECT T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H, MAX(T1.C), '^'
+ FROM T1, T2
+ WHERE T1.B = T2.B
+ AND T1.C = T2.C
+ GROUP BY T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H
+ ORDER BY +max(t1.c);
+ }
+} {702118 16183 6 0 5 5 0 15681 ^ 702118 16183 6 0 5 2 0 15682 ^}
+do_test contrib01-1.2 {
+ db eval {
+ SELECT T2.A, T2.B, T1.D, T1.E, T1.F, T1.G, T1.H, MAX(T1.C), '^'
+ FROM T1, T2
+ WHERE T1.B = T2.B
+ AND T1.C = T2.C
+ GROUP BY T2.A, T2.B, T1.F, T1.D, T1.E, T1.G, T1.H
+ ORDER BY +max(t1.c);
+ }
+} {702118 16183 6 0 5 5 0 15681 ^ 702118 16183 6 0 5 2 0 15682 ^}
+
+finish_test
diff --git a/test/corrupt.test b/test/corrupt.test
index 09f3c5b..3e49a9f 100644
--- a/test/corrupt.test
+++ b/test/corrupt.test
@@ -25,6 +25,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# Construct a large database for testing.
#
do_test corrupt-1.1 {
diff --git a/test/corrupt2.test b/test/corrupt2.test
index 744a76e..805a614 100644
--- a/test/corrupt2.test
+++ b/test/corrupt2.test
@@ -23,6 +23,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
set presql ""
catch { set presql "$::G(perm:presql);" }
unset -nocomplain ::G(perm:presql)
diff --git a/test/corrupt3.test b/test/corrupt3.test
index a382722..436a466 100644
--- a/test/corrupt3.test
+++ b/test/corrupt3.test
@@ -23,6 +23,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas||direct_read {
diff --git a/test/corrupt4.test b/test/corrupt4.test
index 1906113..24db60f 100644
--- a/test/corrupt4.test
+++ b/test/corrupt4.test
@@ -23,6 +23,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas {
diff --git a/test/corrupt5.test b/test/corrupt5.test
index dca06e5..3f50996 100644
--- a/test/corrupt5.test
+++ b/test/corrupt5.test
@@ -19,6 +19,10 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas {
diff --git a/test/corrupt6.test b/test/corrupt6.test
index c0dcedf..7d90c4a 100644
--- a/test/corrupt6.test
+++ b/test/corrupt6.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas {
diff --git a/test/corrupt7.test b/test/corrupt7.test
index ad56656..db92cf1 100644
--- a/test/corrupt7.test
+++ b/test/corrupt7.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas {
diff --git a/test/corrupt8.test b/test/corrupt8.test
index 012beb5..d7bceba 100644
--- a/test/corrupt8.test
+++ b/test/corrupt8.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas||!autovacuum {
diff --git a/test/corrupt9.test b/test/corrupt9.test
index f199452..bb37758 100644
--- a/test/corrupt9.test
+++ b/test/corrupt9.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# We must have the page_size pragma for these tests to work.
#
ifcapable !pager_pragmas {
diff --git a/test/corruptA.test b/test/corruptA.test
index 8b76d3a..bb9912b 100644
--- a/test/corruptA.test
+++ b/test/corruptA.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# Create a database to work with.
#
diff --git a/test/corruptB.test b/test/corruptB.test
index 0ff2d6e..c51cb57 100644
--- a/test/corruptB.test
+++ b/test/corruptB.test
@@ -30,6 +30,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
do_test corruptB-1.1 {
execsql {
diff --git a/test/corruptC.test b/test/corruptC.test
index 34e81a1..adf6f44 100644
--- a/test/corruptC.test
+++ b/test/corruptC.test
@@ -27,6 +27,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# Construct a compact, dense database for testing.
#
do_test corruptC-1.1 {
@@ -202,6 +206,10 @@ do_test corruptC-2.8 {
} {1 {database disk image is malformed}}
# corruption (seed 170434)
+#
+# UPDATE: Prior to 3.8.2, this used to return SQLITE_CORRUPT. It no longer
+# does. That is Ok, the point of these tests is to verify that no buffer
+# overruns or overreads can be caused by corrupt databases.
do_test corruptC-2.9 {
db close
forcecopy test.bu test.db
@@ -211,7 +219,7 @@ do_test corruptC-2.9 {
sqlite3 db test.db
catchsql {BEGIN; DELETE FROM t1 WHERE x>13; ROLLBACK;}
-} {1 {database disk image is malformed}}
+} {0 {}}
# corruption (seed 186504)
do_test corruptC-2.10 {
diff --git a/test/corruptD.test b/test/corruptD.test
index 2423cd4..6347458 100644
--- a/test/corruptD.test
+++ b/test/corruptD.test
@@ -19,6 +19,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
#--------------------------------------------------------------------------
# OVERVIEW
#
diff --git a/test/corruptE.test b/test/corruptE.test
index 48292ab..4d5b5db 100644
--- a/test/corruptE.test
+++ b/test/corruptE.test
@@ -24,6 +24,10 @@ source $testdir/tester.tcl
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
# Do not run the tests in this file if ENABLE_OVERSIZE_CELL_CHECK is on.
#
ifcapable oversize_cell_check {
diff --git a/test/corruptF.test b/test/corruptF.test
index 33eef39..8c4fd84 100644
--- a/test/corruptF.test
+++ b/test/corruptF.test
@@ -19,6 +19,10 @@ set testprefix corruptF
#
do_not_use_codec
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
proc str {i} { format %08d $i }
# Create a 6 page database containing a single table - t1. Table t1
@@ -147,4 +151,3 @@ for {set i 127} {$i >= 0} {incr i -1} {
}
finish_test
-
diff --git a/test/corruptG.test b/test/corruptG.test
new file mode 100644
index 0000000..af920ed
--- /dev/null
+++ b/test/corruptG.test
@@ -0,0 +1,76 @@
+# 2013-08-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptG
+
+# Do not use a codec for tests in this file, as the database file is
+# manipulated directly using tcl scripts (using the [hexio_write] command).
+#
+do_not_use_codec
+
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
+
+# Create a simple database with a single entry. Then corrupt the
+# header-size varint on the index payload so that it maps into a
+# negative number. Try to use the database.
+#
+
+do_execsql_test 1.1 {
+ PRAGMA page_size=512;
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1(rowid,a,b,c) VALUES(52,'abc','xyz','123');
+ CREATE INDEX t1abc ON t1(a,b,c);
+}
+
+set idxroot [db one {SELECT rootpage FROM sqlite_master WHERE name = 't1abc'}]
+
+# Corrupt the file
+db close
+hexio_write test.db [expr {$idxroot*512 - 15}] 888080807f
+sqlite3 db test.db
+
+# Try to use the file.
+do_test 1.2 {
+ catchsql {
+ SELECT c FROM t1 WHERE a>'abc';
+ }
+} {1 {database disk image is malformed}}
+do_test 1.3 {
+ catchsql {
+ PRAGMA integrity_check
+ }
+} {1 {database disk image is malformed}}
+do_test 1.4 {
+ catchsql {
+ SELECT c FROM t1 ORDER BY a;
+ }
+} {1 {database disk image is malformed}}
+
+# Corrupt the same file in a slightly different way. Make the record header
+# sane, but corrupt one of the serial_type value to indicate a huge payload
+# such that the payload begins in allocated space but overflows the buffer.
+#
+db close
+hexio_write test.db [expr {$idxroot*512-15}] 0513ff7f01
+sqlite3 db test.db
+
+do_test 2.1 {
+ catchsql {
+ SELECT rowid FROM t1 WHERE a='abc' and b='xyz123456789XYZ';
+ }
+} {1 {database disk image is malformed}}
+
+finish_test
diff --git a/test/corruptH.test b/test/corruptH.test
new file mode 100644
index 0000000..ee2bb1e
--- /dev/null
+++ b/test/corruptH.test
@@ -0,0 +1,178 @@
+# 2014-01-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptH
+
+# Do not use a codec for tests in this file, as the database file is
+# manipulated directly using tcl scripts (using the [hexio_write] command).
+#
+do_not_use_codec
+database_may_be_corrupt
+
+# The corruption migrations tested by the code in this file are not detected
+# mmap mode.
+#
+# The reason is that in mmap mode, the different queries may use different
+# PgHdr objects for the same page (same data, but different PgHdr container
+# objects). And so the corruption is not detected.
+#
+if {[permutation]=="mmap"} {
+ finish_test
+ return
+}
+
+# Initialize the database.
+#
+do_execsql_test 1.1 {
+ PRAGMA page_size=1024;
+
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+
+ CREATE TABLE t2(x);
+ INSERT INTO t2 VALUES(randomblob(200));
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+ INSERT INTO t2 SELECT randomblob(200) FROM t2;
+} {}
+
+# Corrupt the file so that the root page of t1 is also linked into t2 as
+# a leaf page.
+#
+do_test 1.2 {
+ db eval { SELECT name, rootpage FROM sqlite_master } {
+ set r($name) $rootpage
+ }
+ db close
+ hexio_write test.db [expr {($r(t2)-1)*1024 + 11}] [format %.2X $r(t1)]
+ sqlite3 db test.db
+} {}
+
+do_test 1.3 {
+breakpoint
+ db eval { PRAGMA secure_delete=1 }
+ list [catch {
+ db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+ db eval { DELETE FROM t2 }
+ }
+ } msg] $msg
+} {1 {database disk image is malformed}}
+
+#-------------------------------------------------------------------------
+reset_db
+
+# Initialize the database.
+#
+do_execsql_test 2.1 {
+ PRAGMA auto_vacuum=0;
+ PRAGMA page_size=1024;
+
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+
+ CREATE TABLE t3(x);
+
+ CREATE TABLE t2(x PRIMARY KEY) WITHOUT ROWID;
+ INSERT INTO t2 VALUES(randomblob(100));
+
+ DROP TABLE t3;
+} {}
+
+do_test 2.2 {
+ db eval { SELECT name, rootpage FROM sqlite_master } {
+ set r($name) $rootpage
+ }
+ db close
+ set fl [hexio_get_int [hexio_read test.db 32 4]]
+
+ hexio_write test.db [expr {($fl-1) * 1024 + 0}] 00000000
+ hexio_write test.db [expr {($fl-1) * 1024 + 4}] 00000001
+ hexio_write test.db [expr {($fl-1) * 1024 + 8}] [format %.8X $r(t1)]
+ hexio_write test.db 36 00000002
+
+ sqlite3 db test.db
+} {}
+
+
+# The trick here is that the root page of the tree scanned by the outer
+# query is also currently on the free-list. So while the first seek on
+# the table (for a==1) works, by the time the second is attempted The
+# "INSERT INTO t2..." statements have recycled the root page of t1 and
+# used it as an index leaf. Normally, BtreeMovetoUnpacked() detects
+# that the PgHdr object associated with said root page does not match
+# the cursor (as it is now marked with PgHdr.intKey==0) and returns
+# SQLITE_CORRUPT.
+#
+set res23 {1 {database disk image is malformed}}
+do_test 2.3 {
+ list [catch {
+ set res [list]
+ db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+ db eval {
+ INSERT INTO t2 SELECT randomblob(100) FROM t2;
+ INSERT INTO t2 SELECT randomblob(100) FROM t2;
+ INSERT INTO t2 SELECT randomblob(100) FROM t2;
+ INSERT INTO t2 SELECT randomblob(100) FROM t2;
+ INSERT INTO t2 SELECT randomblob(100) FROM t2;
+ }
+ lappend res $b
+ }
+ set res
+ } msg] $msg
+} $res23
+
+#-------------------------------------------------------------------------
+reset_db
+
+# Initialize the database.
+#
+do_execsql_test 3.1 {
+ PRAGMA page_size=1024;
+
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+
+ CREATE TABLE t2(c INTEGER PRAGMA KEY, d);
+ INSERT INTO t2 VALUES(1, randomblob(1100));
+} {}
+
+do_test 3.2 {
+ db eval { SELECT name, rootpage FROM sqlite_master } {
+ set r($name) $rootpage
+ }
+ db close
+
+ hexio_write test.db [expr {($r(t2)-1) * 1024 + 1020}] 00000002
+
+ sqlite3 db test.db
+} {}
+
+do_test 3.3 {
+ list [catch {
+ db eval { SELECT * FROM t1 WHERE a IN (1, 2) } {
+ db eval {
+ DELETE FROM t2 WHERE c=1;
+ }
+ }
+ } msg] $msg
+} {1 {database disk image is malformed}}
+
+finish_test
+
diff --git a/test/corruptI.test b/test/corruptI.test
new file mode 100644
index 0000000..41200c5
--- /dev/null
+++ b/test/corruptI.test
@@ -0,0 +1,105 @@
+# 2014-01-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix corruptI
+
+if {[permutation]=="mmap"} {
+ finish_test
+ return
+}
+
+# Do not use a codec for tests in this file, as the database file is
+# manipulated directly using tcl scripts (using the [hexio_write] command).
+#
+do_not_use_codec
+database_may_be_corrupt
+
+# Initialize the database.
+#
+do_execsql_test 1.1 {
+ PRAGMA page_size=1024;
+ PRAGMA auto_vacuum=0;
+ CREATE TABLE t1(a);
+ CREATE INDEX i1 ON t1(a);
+ INSERT INTO t1 VALUES('abcdefghijklmnop');
+} {}
+db close
+
+do_test 1.2 {
+ set offset [hexio_get_int [hexio_read test.db [expr 2*1024 + 8] 2]]
+ set off [expr 2*1024 + $offset + 1]
+ hexio_write test.db $off 7f06
+ sqlite3 db test.db
+ catchsql { SELECT * FROM t1 WHERE a = 10 }
+} {0 {}}
+
+do_test 1.3 {
+ db close
+ set offset [hexio_get_int [hexio_read test.db [expr 2*1024 + 8] 2]]
+ set off [expr 2*1024 + $offset + 1]
+ hexio_write test.db $off FFFF7f02
+ sqlite3 db test.db
+ catchsql { SELECT * FROM t1 WHERE a = 10 }
+} {1 {database disk image is malformed}}
+
+do_test 2.0 {
+ execsql {
+ CREATE TABLE r(x);
+ INSERT INTO r VALUES('ABCDEFGHIJK');
+ CREATE INDEX r1 ON r(x);
+ }
+ set pg [db one {SELECT rootpage FROM sqlite_master WHERE name = 'r1'}]
+} {5}
+
+do_test 2.1 {
+ db close
+ set offset [hexio_get_int [hexio_read test.db [expr (5-1)*1024 + 8] 2]]
+ set off [expr (5-1)*1024 + $offset + 1]
+ hexio_write test.db $off FFFF0004
+ sqlite3 db test.db
+ catchsql { SELECT * FROM r WHERE x >= 10.0 }
+} {1 {database disk image is malformed}}
+
+do_test 2.2 {
+ catchsql { SELECT * FROM r WHERE x >= 10 }
+} {1 {database disk image is malformed}}
+
+reset_db
+
+do_execsql_test 3.1 {
+ PRAGMA page_size = 512;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ WITH s(a, b) AS (
+ SELECT 2, 'abcdefghij'
+ UNION ALL
+ SELECT a+2, b FROM s WHERe a < 40
+ )
+ INSERT INTO t1 SELECT * FROM s;
+} {}
+
+do_test 3.2 {
+ hexio_write test.db [expr 512+3] 0054
+ db close
+ sqlite3 db test.db
+ execsql { INSERT INTO t1 VALUES(5, 'klmnopqrst') }
+ execsql { INSERT INTO t1 VALUES(7, 'klmnopqrst') }
+} {}
+
+db close
+sqlite3 db test.db
+do_catchsql_test 3.2 {
+ INSERT INTO t1 VALUES(9, 'klmnopqrst');
+} {1 {database disk image is malformed}}
+
+finish_test
diff --git a/test/cost.test b/test/cost.test
new file mode 100644
index 0000000..92fff9c
--- /dev/null
+++ b/test/cost.test
@@ -0,0 +1,292 @@
+# 2014-04-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix cost
+
+
+do_execsql_test 1.1 {
+ CREATE TABLE t3(id INTEGER PRIMARY KEY, b NOT NULL);
+ CREATE TABLE t4(c, d, e);
+ CREATE UNIQUE INDEX i3 ON t3(b);
+ CREATE UNIQUE INDEX i4 ON t4(c, d);
+}
+do_eqp_test 1.2 {
+ SELECT e FROM t3, t4 WHERE b=c ORDER BY b, d;
+} {
+ 0 0 0 {SCAN TABLE t3 USING COVERING INDEX i3}
+ 0 1 1 {SEARCH TABLE t4 USING INDEX i4 (c=?)}
+}
+
+
+do_execsql_test 2.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a);
+}
+
+# It is better to use an index for ORDER BY than sort externally, even
+# if the index is a non-covering index.
+do_eqp_test 2.2 {
+ SELECT * FROM t1 ORDER BY a;
+} {
+ 0 0 0 {SCAN TABLE t1 USING INDEX i1}
+}
+
+do_execsql_test 3.1 {
+ CREATE TABLE t5(a INTEGER PRIMARY KEY,b,c,d,e,f,g);
+ CREATE INDEX t5b ON t5(b);
+ CREATE INDEX t5c ON t5(c);
+ CREATE INDEX t5d ON t5(d);
+ CREATE INDEX t5e ON t5(e);
+ CREATE INDEX t5f ON t5(f);
+ CREATE INDEX t5g ON t5(g);
+}
+
+do_eqp_test 3.2 {
+ SELECT a FROM t5
+ WHERE b IS NULL OR c IS NULL OR d IS NULL
+ ORDER BY a;
+} {
+ 0 0 0 {SEARCH TABLE t5 USING INDEX t5b (b=?)}
+ 0 0 0 {SEARCH TABLE t5 USING INDEX t5c (c=?)}
+ 0 0 0 {SEARCH TABLE t5 USING INDEX t5d (d=?)}
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+}
+
+#-------------------------------------------------------------------------
+# If there is no likelihood() or stat3 data, SQLite assumes that a closed
+# range scan (e.g. one constrained by "col BETWEEN ? AND ?" constraint)
+# visits 1/64 of the rows in a table.
+#
+# Note: 1/63 =~ 0.016
+# Note: 1/65 =~ 0.015
+#
+reset_db
+do_execsql_test 4.1 {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a);
+ CREATE INDEX i2 ON t1(b);
+}
+do_eqp_test 4.2 {
+ SELECT * FROM t1 WHERE likelihood(a=?, 0.014) AND b BETWEEN ? AND ?;
+} {
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
+}
+do_eqp_test 4.3 {
+ SELECT * FROM t1 WHERE likelihood(a=?, 0.016) AND b BETWEEN ? AND ?;
+} {
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b>? AND b<?)}
+}
+
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 5.1 {
+ CREATE TABLE t2(x, y);
+ CREATE INDEX t2i1 ON t2(x);
+}
+
+do_eqp_test 5.2 {
+ SELECT * FROM t2 ORDER BY x, y;
+} {
+ 0 0 0 {SCAN TABLE t2 USING INDEX t2i1}
+ 0 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
+}
+
+do_eqp_test 5.3 {
+ SELECT * FROM t2 WHERE x BETWEEN ? AND ? ORDER BY rowid;
+} {
+ 0 0 0 {SEARCH TABLE t2 USING INDEX t2i1 (x>? AND x<?)}
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+}
+
+# where7.test, where8.test:
+#
+do_execsql_test 6.1 {
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c);
+ CREATE INDEX t3i1 ON t3(b);
+ CREATE INDEX t3i2 ON t3(c);
+}
+
+do_eqp_test 6.2 {
+ SELECT a FROM t3 WHERE (b BETWEEN 2 AND 4) OR c=100 ORDER BY a
+} {
+ 0 0 0 {SEARCH TABLE t3 USING INDEX t3i1 (b>? AND b<?)}
+ 0 0 0 {SEARCH TABLE t3 USING INDEX t3i2 (c=?)}
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 7.1 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY,b,c,d,e,f,g);
+ 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 t1f ON t1(f);
+ CREATE INDEX t1g ON t1(g);
+}
+
+do_eqp_test 7.2 {
+ SELECT a FROM t1
+ WHERE (b>=950 AND b<=1010) OR (b IS NULL AND c NOT NULL)
+ ORDER BY a
+} {
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+}
+
+do_eqp_test 7.3 {
+ SELECT rowid FROM t1
+ WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL)
+ OR (b NOT NULL AND c IS NULL AND d NOT NULL)
+ OR (b NOT NULL AND c NOT NULL AND d IS NULL)
+} {
+ 0 0 0 {SCAN TABLE t1}
+}
+
+do_eqp_test 7.4 {
+ SELECT rowid FROM t1 WHERE (+b IS NULL AND c NOT NULL) OR c IS NULL
+} {
+ 0 0 0 {SCAN TABLE t1}
+}
+
+#-------------------------------------------------------------------------
+#
+reset_db
+do_execsql_test 8.1 {
+ CREATE TABLE composer(
+ cid INTEGER PRIMARY KEY,
+ cname TEXT
+ );
+ CREATE TABLE album(
+ aid INTEGER PRIMARY KEY,
+ aname TEXT
+ );
+ CREATE TABLE track(
+ tid INTEGER PRIMARY KEY,
+ cid INTEGER REFERENCES composer,
+ aid INTEGER REFERENCES album,
+ title TEXT
+ );
+ CREATE INDEX track_i1 ON track(cid);
+ CREATE INDEX track_i2 ON track(aid);
+}
+
+do_eqp_test 8.2 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE cname LIKE '%bach%'
+ AND unlikely(composer.cid=track.cid)
+ AND unlikely(album.aid=track.aid);
+} {
+ 0 0 2 {SCAN TABLE track}
+ 0 1 0 {SEARCH TABLE album USING INTEGER PRIMARY KEY (rowid=?)}
+ 0 2 1 {SEARCH TABLE composer USING INTEGER PRIMARY KEY (rowid=?)}
+ 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+}
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 9.1 {
+ CREATE TABLE t1(
+ a,b,c,d,e, f,g,h,i,j,
+ k,l,m,n,o, p,q,r,s,t
+ );
+ CREATE INDEX i1 ON t1(k,l,m,n,o,p,q,r,s,t);
+}
+do_test 9.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ execsql { INSERT INTO t1 DEFAULT VALUES }
+ }
+ execsql {
+ ANALYZE;
+ CREATE INDEX i2 ON t1(a,b,c,d,e,f,g,h,i,j);
+ }
+} {}
+
+set L [list a=? b=? c=? d=? e=? f=? g=? h=? i=? j=?]
+foreach {tn nTerm nRow} {
+ 1 1 10
+ 2 2 9
+ 3 3 8
+ 4 4 7
+ 5 5 6
+ 6 6 5
+ 7 7 5
+ 8 8 5
+ 9 9 5
+ 10 10 5
+} {
+ set w [join [lrange $L 0 [expr $nTerm-1]] " AND "]
+ set p1 [expr ($nRow-1) / 100.0]
+ set p2 [expr ($nRow+1) / 100.0]
+
+ set sql1 "SELECT * FROM t1 WHERE likelihood(k=?, $p1) AND $w"
+ set sql2 "SELECT * FROM t1 WHERE likelihood(k=?, $p2) AND $w"
+
+ do_eqp_test 9.3.$tn.1 $sql1 {/INDEX i1/}
+ do_eqp_test 9.3.$tn.2 $sql2 {/INDEX i2/}
+}
+
+
+#-------------------------------------------------------------------------
+#
+
+ifcapable stat4 {
+ do_execsql_test 10.1 {
+ CREATE TABLE t6(a, b, c);
+ CREATE INDEX t6i1 ON t6(a, b);
+ CREATE INDEX t6i2 ON t6(c);
+ }
+
+ do_test 10.2 {
+ for {set i 0} {$i < 16} {incr i} {
+ execsql { INSERT INTO t6 VALUES($i%4, 'xyz', $i%8) }
+ }
+ execsql ANALYZE
+ } {}
+
+ do_eqp_test 10.3 {
+ SELECT rowid FROM t6 WHERE a=0 AND c=0
+ } {
+ 0 0 0 {SEARCH TABLE t6 USING INDEX t6i2 (c=?)}
+ }
+
+ do_eqp_test 10.4 {
+ SELECT rowid FROM t6 WHERE a=0 AND b='xyz' AND c=0
+ } {
+ 0 0 0 {SEARCH TABLE t6 USING INDEX t6i2 (c=?)}
+ }
+
+ do_eqp_test 10.5 {
+ SELECT rowid FROM t6 WHERE likelihood(a=0, 0.1) AND c=0
+ } {
+ 0 0 0 {SEARCH TABLE t6 USING INDEX t6i1 (a=?)}
+ }
+
+ do_eqp_test 10.6 {
+ SELECT rowid FROM t6 WHERE likelihood(a=0, 0.1) AND b='xyz' AND c=0
+ } {
+ 0 0 0 {SEARCH TABLE t6 USING INDEX t6i1 (a=? AND b=?)}
+ }
+}
+
+finish_test
+
+
+
diff --git a/test/count.test b/test/count.test
index fbdd13b..3461e49 100644
--- a/test/count.test
+++ b/test/count.test
@@ -1,4 +1,4 @@
-# 2009 February 24
+# 2009-02-24
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
@@ -11,7 +11,6 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing "SELECT count(*)" statements.
#
-# $Id: count.test,v 1.6 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -24,8 +23,6 @@ source $testdir/tester.tcl
# tables and indexes. Test both when they contain 0 entries,
# when all entries are on the root page, and when the b-tree
# forms a structure 2 and 3 levels deep.
-#
-# count-2.*: Test that
#
#
@@ -188,5 +185,10 @@ do_test count-4.3 {
}
} {1}
+do_execsql_test count-5.1 {
+ CREATE TABLE t5(a TEXT PRIMARY KEY, b VARCHAR(50)) WITHOUT ROWID;
+ INSERT INTO t5 VALUES('bison','jazz');
+ SELECT count(*) FROM t5;
+} {1}
finish_test
diff --git a/test/crash8.test b/test/crash8.test
index 8bc2586..930834a 100644
--- a/test/crash8.test
+++ b/test/crash8.test
@@ -341,62 +341,68 @@ ifcapable pragma {
} {jkl}
}
-for {set i 1} {$i < 10} {incr i} {
- catch { db close }
- forcedelete test.db test.db-journal
- sqlite3 db test.db
- do_test crash8-5.$i.1 {
- execsql {
- CREATE TABLE t1(x PRIMARY KEY);
- INSERT INTO t1 VALUES(randomblob(900));
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
- }
- crashsql -file test.db -delay [expr ($::i%2) + 1] {
- PRAGMA cache_size = 10;
- BEGIN;
- UPDATE t1 SET x = randomblob(900);
- ROLLBACK;
- INSERT INTO t1 VALUES(randomblob(900));
- }
- execsql { PRAGMA integrity_check }
- } {ok}
+#
+# Since the following tests (crash8-5.*) rely upon being able
+# to copy a file while open, they will not work on Windows.
+#
+if {$::tcl_platform(platform)=="unix"} {
+ for {set i 1} {$i < 10} {incr i} {
+ catch { db close }
+ forcedelete test.db test.db-journal
+ sqlite3 db test.db
+ do_test crash8-5.$i.1 {
+ execsql {
+ CREATE TABLE t1(x PRIMARY KEY);
+ INSERT INTO t1 VALUES(randomblob(900));
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
+ }
+ crashsql -file test.db -delay [expr ($::i%2) + 1] {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ UPDATE t1 SET x = randomblob(900);
+ ROLLBACK;
+ INSERT INTO t1 VALUES(randomblob(900));
+ }
+ execsql { PRAGMA integrity_check }
+ } {ok}
- catch { db close }
- forcedelete test.db test.db-journal
- sqlite3 db test.db
- do_test crash8-5.$i.2 {
- execsql {
- PRAGMA cache_size = 10;
- CREATE TABLE t1(x PRIMARY KEY);
- INSERT INTO t1 VALUES(randomblob(900));
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1;
- INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
- BEGIN;
- UPDATE t1 SET x = randomblob(900);
- }
- forcedelete testX.db testX.db-journal testX.db-wal
- forcecopy test.db testX.db
- forcecopy test.db-journal testX.db-journal
- db close
-
- crashsql -file test.db -delay [expr ($::i%2) + 1] {
- SELECT * FROM sqlite_master;
- INSERT INTO t1 VALUES(randomblob(900));
- }
-
- sqlite3 db2 testX.db
- execsql { PRAGMA integrity_check } db2
- } {ok}
+ catch { db close }
+ forcedelete test.db test.db-journal
+ sqlite3 db test.db
+ do_test crash8-5.$i.2 {
+ execsql {
+ PRAGMA cache_size = 10;
+ CREATE TABLE t1(x PRIMARY KEY);
+ INSERT INTO t1 VALUES(randomblob(900));
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1;
+ INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 rows */
+ BEGIN;
+ UPDATE t1 SET x = randomblob(900);
+ }
+ forcedelete testX.db testX.db-journal testX.db-wal
+ forcecopy test.db testX.db
+ forcecopy test.db-journal testX.db-journal
+ db close
+
+ crashsql -file test.db -delay [expr ($::i%2) + 1] {
+ SELECT * FROM sqlite_master;
+ INSERT INTO t1 VALUES(randomblob(900));
+ }
+
+ sqlite3 db2 testX.db
+ execsql { PRAGMA integrity_check } db2
+ } {ok}
+ }
+ catch {db2 close}
}
-catch {db2 close}
finish_test
diff --git a/test/crypto.test b/test/crypto.test
index 8a58fe0..629be5d 100644
--- a/test/crypto.test
+++ b/test/crypto.test
@@ -4,8 +4,6 @@
# http://zetetic.net
#
# Copyright (c) 2009, ZETETIC LLC
-# All rights reserved.
-#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
@@ -44,6 +42,7 @@ file delete -force test4.db
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set old_pending_byte [sqlite3_test_control_pending_byte 0x40000000]
# If the library is not compiled with has_codec support then
# skip all tests in this file.
@@ -88,6 +87,21 @@ proc if_built_with_commoncrypto {name cmd expected} {
}
}
+proc cmpFilesChunked {file1 file2 {chunksize 16384}} {
+ set f1 [open $file1]; fconfigure $f1 -translation binary
+ set f2 [open $file2]; fconfigure $f2 -translation binary
+ while {1} {
+ set d1 [read $f1 $chunksize]
+ set d2 [read $f2 $chunksize]
+ set diff [string compare $d1 $d2]
+ if {$diff != 0 || [eof $f1] || [eof $f2]} {
+ close $f1; close $f2
+ return $diff
+ }
+ }
+ return 0
+}
+
# The database is initially empty.
# set an hex key create some basic data
# create table and insert operations should work
@@ -408,8 +422,74 @@ db close
file delete -force test.db
# attach an encrypted database
+# without specifying key, verify it fails
+# even if the source passwords are the same
+# because the kdf salts are different
+setup test.db "'testkey'"
+do_test attach-database-with-default-key {
+ sqlite_orig db2 test2.db
+ execsql {
+ PRAGMA key = 'testkey';
+ PRAGMA cipher_add_random = "x'deadbaad'";
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES ('test1', 'test2');
+ } db2
+
+ catchsql {
+ ATTACH 'test.db' AS db;
+ } db2
+
+} {1 {file is encrypted or is not a database}}
+db2 close
+file delete -force test.db
+file delete -force test2.db
+
+# attach an encrypted database
+# without specifying key, verify it attaches
+# correctly when PRAGMA cipher_store_pass = 1
+# is set.
+do_test attach-database-with-default-key-using-cipher-store-pass {
+
+ sqlite_orig db1 test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1(a,b) VALUES('foo', 'bar');
+ } db1
+ db1 close
+
+ sqlite_orig db2 test2.db
+ execsql {
+ PRAGMA key = 'testkey';
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2 VALUES ('test1', 'test2');
+ } db2
+ db2 close
+
+ sqlite_orig db1 test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ PRAGMA cipher_store_pass = 1;
+ ATTACH DATABASE 'test2.db' as db2;
+ SELECT sqlcipher_export('db2');
+ DETACH DATABASE db2;
+ } db1
+ db1 close
+
+ sqlite_orig db2 test2.db
+ execsql {
+ PRAGMA key = 'testkey';
+ SELECT * FROM t1;
+ } db2
+
+} {foo bar}
+db2 close
+file delete -force test.db
+file delete -force test2.db
+
+# attach an encrypted database
# where both database have the same
-# key
+# key explicitly
setup test.db "'testkey'"
do_test attach-database-with-same-key {
sqlite_orig db2 test2.db
@@ -422,7 +502,7 @@ do_test attach-database-with-same-key {
execsql {
SELECT count(*) FROM t2;
- ATTACH 'test.db' AS db;
+ ATTACH 'test.db' AS db KEY 'testkey';
SELECT count(*) FROM db.t1;
} db2
@@ -607,7 +687,7 @@ file delete -force test.db
# create an unencrypted database, attach a new encrypted volume
# copy data between, verify the encypted database is good afterwards
-do_test unencryped-attach {
+do_test unencrypted-attach {
sqlite_orig db test.db
execsql {
@@ -641,7 +721,7 @@ file delete -force test2.db
# create an unencrypted database, attach a new encrypted volume
# using a raw key copy data between, verify the encypted
# database is good afterwards
-do_test unencryped-attach-raw-key {
+do_test unencrypted-attach-raw-key {
sqlite_orig db test.db
execsql {
@@ -672,9 +752,45 @@ db2 close
file delete -force test.db
file delete -force test2.db
+# create an encrypted database, attach an default-key encrypted volume
+# copy data between, verify the second database
+do_test encrypted-attach-default-key {
+ sqlite_orig db test.db
+
+ execsql {
+ PRAGMA key='testkey';
+ CREATE TABLE t1(a,b);
+ BEGIN;
+ }
+
+ for {set i 1} {$i<=1000} {incr i} {
+ set r [expr {int(rand()*500000)}]
+ execsql "INSERT INTO t1 VALUES($i,$r);"
+ }
+
+ execsql {
+ COMMIT;
+ ATTACH DATABASE 'test2.db' AS test;
+ CREATE TABLE test.t1(a,b);
+ INSERT INTO test.t1 SELECT * FROM t1;
+ DETACH DATABASE test;
+ }
+
+ sqlite_orig db2 test2.db
+
+ execsql {
+ PRAGMA key='testkey';
+ SELECT count(*) FROM t1;
+ } db2
+} {1000}
+db close
+db2 close
+file delete -force test.db
+file delete -force test2.db
+
# create an encrypted database, attach an unencrypted volume
# copy data between, verify the unencypted database is good afterwards
-do_test encryped-attach-unencrypted {
+do_test encrypted-attach-unencrypted {
sqlite_orig db test.db
execsql {
@@ -683,7 +799,7 @@ do_test encryped-attach-unencrypted {
sqlite_orig db2 test2.db
execsql {
- PRAGMA key='testkey';
+ PRAGMA key = 'testkey';
CREATE TABLE t1(a,b);
BEGIN;
} db2
@@ -711,7 +827,7 @@ file delete -force test2.db
# create an unencrypted database, attach an unencrypted volume
# copy data between, verify the unencypted database is good afterwards
-do_test unencryped-attach-unencrypted {
+do_test unencrypted-attach-unencrypted {
sqlite_orig db test.db
execsql {
@@ -875,16 +991,18 @@ file delete -force test.db
# open a 1.1.8 database using the new code, HMAC disabled
do_test open-1.1.8-database {
- sqlite_orig db sqlcipher-1.1.8-testkey.db
+ file copy -force sqlcipher-1.1.8-testkey.db test.db
+ sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
- PRAGMA cipher_use_hmac = OFF;
+ PRAGMA cipher_use_hmac = off;
+ PRAGMA kdf_iter = 4000;
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {4 1 1 one one 1 2 one two}
+} {75709 1 1 one one 1 2 one two 1 2}
db close
-
+file delete -force test.db
# open a 1.1.8 database without hmac, then copy the data
do_test attach-and-copy-1.1.8 {
@@ -893,6 +1011,7 @@ do_test attach-and-copy-1.1.8 {
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_use_hmac = OFF;
+ PRAGMA kdf_iter = 4000;
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey-hmac';
CREATE TABLE db2.t1(a,b);
INSERT INTO db2.t1 SELECT * FROM main.t1;
@@ -904,9 +1023,9 @@ do_test attach-and-copy-1.1.8 {
execsql {
PRAGMA key = 'testkey-hmac';
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {4 1 1 one one 1 2 one two}
+} {75709 1 1 one one 1 2 one two 1 2}
db close
file delete -force test.db
@@ -1350,22 +1469,24 @@ do_test cipher-options-before-keys {
db close
file delete -force test.db
-# open a 1.1.8 database (no HMAC), then
+# open a 1.1.8 database (no HMAC, 4K iter), then
# try to open another 1.1.8 database. The
# attached database should have the same hmac
# setting as the original
-do_test default-use-hmac-attach {
+do_test default-hmac-kdf-attach {
file copy -force sqlcipher-1.1.8-testkey.db test.db
sqlite_orig db test.db
execsql {
PRAGMA cipher_default_use_hmac = OFF;
+ PRAGMA cipher_default_kdf_iter = 4000;
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
- ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
+ ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
SELECT count(*) from db2.t1;
PRAGMA cipher_default_use_hmac = ON;
+ PRAGMA cipher_default_kdf_iter = 64000;
}
-} {4 4}
+} {75709 75709}
db close
file delete -force test.db
@@ -1378,18 +1499,18 @@ do_test attach-1.1.8-database-from-2.0-fails {
catchsql {
PRAGMA key = 'testkey';
CREATE table t1(a,b);
- ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
+ ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
}
} {1 {file is encrypted or is not a database}}
db close
file delete -force test.db
-# open a 2.0 database (with HMAC), then
+# open a 2.0 database (with HMAC, 4k iter), then
# set the default hmac setting to OFF.
# try to a 1.1.8 database. this should
# succeed now that hmac is off by default
# before the attach
-do_test change-default-use-hmac-attach {
+do_test change-default-hmac-kdf-attach {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
@@ -1402,11 +1523,13 @@ do_test change-default-use-hmac-attach {
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
PRAGMA cipher_default_use_hmac = OFF;
- ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2;
+ PRAGMA cipher_default_kdf_iter = 4000;
+ ATTACH 'sqlcipher-1.1.8-testkey.db' AS db2 KEY 'testkey';
SELECT count(*) from db2.t1;
PRAGMA cipher_default_use_hmac = ON;
+ PRAGMA cipher_default_kdf_iter = 64000;
}
-} {1 4}
+} {1 75709}
db close
file delete -force test.db
@@ -1418,7 +1541,7 @@ do_test verify-pragma-cipher-version {
execsql {
PRAGMA cipher_version;
}
-} {2.2.1}
+} {3.2.0}
db close
file delete -force test.db
@@ -1580,16 +1703,29 @@ do_test multipage-schema-autovacuum-shortread-wal {
db close
file delete -force test.db
+# open a 3.0 database with little endian hmac page numbers (default)
+# verify it can be opened
+do_test open-3.0-le-database {
+ sqlite_orig db sqlcipher-3.0-testkey.db
+ execsql {
+ PRAGMA key = 'testkey';
+ SELECT count(*) FROM t1;
+ SELECT distinct * FROM t1;
+ }
+} {78536 1 1 one one 1 2 one two}
+db close
+
# open a 2.0 database with little endian hmac page numbers (default)
# verify it can be opened
do_test open-2.0-le-database {
sqlite_orig db sqlcipher-2.0-le-testkey.db
execsql {
PRAGMA key = 'testkey';
+ PRAGMA kdf_iter = 4000;
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {4 1 1 one one 1 2 one two}
+} {78536 1 1 one one 1 2 one two}
db close
# open a 2.0 database with big-endian hmac page numbers
@@ -1599,10 +1735,11 @@ do_test open-2.0-be-database {
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_hmac_pgno = be;
+ PRAGMA kdf_iter = 4000;
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {4 1 1 one one 1 2 one two}
+} {78536 1 1 one one 1 2 one two}
db close
# open a 2.0 database with big-endian hmac page numbers
@@ -1615,6 +1752,7 @@ do_test be-to-le-migration {
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_hmac_pgno = be;
+ PRAGMA kdf_iter = 4000;
ATTACH DATABASE 'test.db' AS db2 KEY 'testkey';
CREATE TABLE db2.t1(a,b);
INSERT INTO db2.t1 SELECT * FROM main.t1;
@@ -1626,9 +1764,9 @@ do_test be-to-le-migration {
execsql {
PRAGMA key = 'testkey';
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {4 1 1 one one 1 2 one two}
+} {78536 1 1 one one 1 2 one two}
db close
file delete -force test.db
@@ -1684,6 +1822,31 @@ do_test verify-pragma-cipher-default-use-hmac-off {
db close
file delete -force test.db
+# verify the pragma default_cipher_kdf_iter
+# is set to 64000 by default
+do_test verify-pragma-cipher-default-kdf-iter-default {
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA cipher_default_kdf_iter;
+ }
+} {64000}
+db close
+file delete -force test.db
+
+
+# verify the pragma default_cipher_kdf_ter
+# reports changes
+do_test verify-pragma-cipher-default-use-hmac-off {
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA cipher_default_kdf_iter = 1000;
+ PRAGMA cipher_default_kdf_iter;
+ PRAGMA cipher_default_kdf_iter = 64000;
+ }
+} {1000}
+db close
+file delete -force test.db
+
# verify the pragma kdf_iter
# reports the default value
do_test verify-pragma-kdf-iter-reports-default {
@@ -1692,7 +1855,7 @@ do_test verify-pragma-kdf-iter-reports-default {
PRAGMA key = 'test';
PRAGMA kdf_iter;
}
-} {4000}
+} {64000}
db close
file delete -force test.db
@@ -1843,12 +2006,13 @@ do_test open-2.0-beta-database {
sqlite_orig db sqlcipher-2.0-beta-testkey.db
execsql {
PRAGMA key = 'testkey';
+ PRAGMA kdf_iter = 4000;
PRAGMA fast_kdf_iter = 4000;
PRAGMA cipher_hmac_salt_mask = "x'00'";
SELECT count(*) FROM t1;
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
-} {2 test-0-0 test-0-1 test-1-0 test-1-1}
+} {38768 test-0-0 test-0-1 test-1-0 test-1-1}
db close
# open a 2.0 beta database
@@ -1861,6 +2025,7 @@ do_test 2.0-beta-to-2.0-migration {
execsql {
PRAGMA key = 'testkey';
PRAGMA cipher_hmac_salt_mask = "x'00'";
+ PRAGMA kdf_iter = 4000;
PRAGMA fast_kdf_iter = 4000;
SELECT count(*) FROM sqlite_master;
@@ -1876,7 +2041,7 @@ do_test 2.0-beta-to-2.0-migration {
sqlite_orig db test.db
execsql {
PRAGMA key = 'testkey';
- SELECT * FROM t1;
+ SELECT distinct * FROM t1;
}
} {test-0-0 test-0-1 test-1-0 test-1-1}
db close
@@ -1902,4 +2067,194 @@ if_built_with_commoncrypto verify-default-cipher {
db close
file delete -force test.db
+do_test migrate-1.1.8-database-to-3x-format {
+ file copy -force sqlcipher-1.1.8-testkey.db test.db
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ PRAGMA cipher_migrate;
+ }
+ db close
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ SELECT count(*) FROM sqlite_master;
+ }
+} {1}
+db close
+file delete -force test.db
+
+do_test migrate-2-0-le-database-to-3x-format {
+ file copy -force sqlcipher-2.0-le-testkey.db test.db
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ PRAGMA cipher_migrate;
+ }
+ db close
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = 'testkey';
+ SELECT count(*) FROM sqlite_master;
+ }
+} {1}
+db close
+file delete -force test.db
+
+do_test key-database-by-name {
+ sqlite_orig db test.db
+ execsql {
+ attach database 'new.db' as new;
+ pragma new.key = 'foo';
+ create table new.t1(a,b);
+ insert into new.t1(a,b) values('foo', 'bar');
+ detach database new;
+ }
+ db close
+
+ sqlite_orig db new.db
+ execsql {
+ pragma key = 'foo';
+ select * from t1;
+ }
+} {foo bar}
+db close
+file delete -force test.db
+file delete -force new.db
+
+do_test key-multiple-databases-with-different-keys-using-pragma {
+ sqlite_orig db test.db
+ execsql {
+ pragma key = 'foobar';
+ create table t1(a,b);
+ insert into t1(a,b) values('baz','qux');
+ attach database 'new.db' as new;
+ pragma new.key = 'foo';
+ create table new.t1(a,b);
+ insert into new.t1(a,b) values('foo', 'bar');
+ detach database new;
+ }
+ db close
+
+ sqlite_orig db new.db
+ execsql {
+ pragma key = 'foo';
+ attach database 'test.db' as test key 'foobar';
+ select * from t1;
+ select * from test.t1;
+ }
+} {foo bar baz qux}
+db close
+file delete -force test.db
+file delete -force new.db
+
+do_test rekey-database-by-name {
+ sqlite_orig db test.db
+ execsql {
+ attach database 'new.db' as new;
+ pragma new.key = 'foo';
+ create table new.t1(a,b);
+ insert into new.t1(a,b) values('foo', 'bar');
+ pragma new.rekey = 'bar';
+ detach database new;
+ }
+ db close
+
+ sqlite_orig db new.db
+ execsql {
+ pragma key = 'bar';
+ select * from t1;
+ }
+} {foo bar}
+db close
+file delete -force test.db
+file delete -force new.db
+
+# Requires SQLCipher to be built with -DSQLCIPHER_TEST
+if_built_with_libtomcrypt verify-random-data-alters-file-content {
+ file delete -force test.db
+ file delete -force test2.db
+ file delete -force test3.db
+ set rc {}
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ create table t1(a,b);
+ }
+ db close
+ sqlite_orig db test2.db
+ execsql {
+ PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ create table t1(a,b);
+ }
+ db close
+ sqlite_orig db test3.db
+ execsql {
+ PRAGMA key="x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA cipher_add_random = "x'deadbaad'";
+ create table t1(a,b);
+ }
+ db close
+ lappend rc [cmpFilesChunked test.db test2.db]
+ lappend rc [cmpFilesChunked test2.db test3.db]
+} {0 1}
+file delete -force test.db
+file delete -force test2.db
+file delete -force test3.db
+
+do_test can-migrate-with-keys-longer-than-64-characters {
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
+ PRAGMA kdf_iter = 4000;
+ PRAGMA user_version = 5;
+ }
+ db close
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
+ PRAGMA cipher_migrate;
+ }
+ db close
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "012345678901234567890123456789012345678901234567890123456789012345";
+ PRAGMA user_version;
+ }
+} {5}
+db close
+file delete -force test.db
+
+do_test can-migrate-with-raw-hex-key {
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA kdf_iter = 4000;
+ PRAGMA cipher_use_hmac = off;
+ PRAGMA user_version = 5;
+ }
+ db close
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA cipher_migrate;
+ }
+
+ sqlite_orig db test.db
+ execsql {
+ PRAGMA key = "x'2DD29CA851E7B56E4697B0E1F08507293D761A05CE4D1B628663F411A8086D99'";
+ PRAGMA user_version;
+ }
+
+} {5}
+db close
+file delete -force test.db
+
+sqlite3_test_control_pending_byte $old_pending_byte
finish_test
diff --git a/test/date.test b/test/date.test
index a30402c..b1d1c67 100644
--- a/test/date.test
+++ b/test/date.test
@@ -528,4 +528,27 @@ if {0==[sqlite3 -has-codec]} {
} {1}
}
}
+
+# Verify that multiple calls to date functions with 'now' return the
+# same answer.
+#
+# EVIDENCE-OF: R-34818-13664 The 'now' argument to date and time
+# functions always returns exactly the same value for multiple
+# invocations within the same sqlite3_step() call.
+#
+proc sleeper {} {after 100}
+do_test date-15.1 {
+ db func sleeper sleeper
+ db eval {
+ SELECT c - a FROM (SELECT julianday('now') AS a,
+ sleeper(), julianday('now') AS c);
+ }
+} {0.0}
+do_test date-15.2 {
+ db eval {
+ SELECT a==b FROM (SELECT current_timestamp AS a,
+ sleeper(), current_timestamp AS b);
+ }
+} {1}
+
finish_test
diff --git a/test/dbstatus.test b/test/dbstatus.test
index 9793df3..368c6b2 100644
--- a/test/dbstatus.test
+++ b/test/dbstatus.test
@@ -61,7 +61,7 @@ proc lookaside {db} {
}
}
-ifcapable stat3 {
+ifcapable stat4||stat3 {
set STAT3 1
} else {
set STAT3 0
@@ -134,7 +134,7 @@ foreach ::lookaside_buffer_size {0 64 120} {
CREATE TABLE t2(c, d);
CREATE VIEW v1 AS SELECT * FROM t1 UNION SELECT * FROM t2;
}
- 6y {
+ 6k {
CREATE TABLE t1(a, b);
CREATE INDEX i1 ON t1(a);
CREATE INDEX i2 ON t1(a,b);
@@ -204,6 +204,11 @@ foreach ::lookaside_buffer_size {0 64 120} {
set nSchema4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1]
set nFree [expr {$nAlloc1-$nAlloc2}]
+ # Tests for which the test name ends in an "k" report slightly less
+ # memory than is actually freed when all schema items are finalized.
+ # This is because memory allocated by KeyInfo objects is no longer
+ # counted as "schema memory".
+ #
# Tests for which the test name ends in an "x" report slightly less
# memory than is actually freed when all schema items are finalized.
# This is because memory allocated by virtual table implementations
@@ -214,13 +219,14 @@ foreach ::lookaside_buffer_size {0 64 120} {
# much greater than just that reported by DBSTATUS_SCHEMA_USED in this
# case.
#
- # Some of the memory used for sqlite_stat3 is unaccounted for by
+ # Some of the memory used for sqlite_stat4 is unaccounted for by
# dbstatus.
#
# Finally, on osx the estimate of memory used by the schema may be
# slightly low.
#
- if {[string match *x $tn] || $AUTOVACUUM
+ if {[string match *k $tn]
+ || [string match *x $tn] || $AUTOVACUUM
|| ([string match *y $tn] && $STAT3)
|| ($::tcl_platform(os) == "Darwin")
} {
@@ -246,7 +252,7 @@ foreach ::lookaside_buffer_size {0 64 120} {
# lookaside memory allocated by SQLite, and the memory allocated
# for the prepared statements according to sqlite3_db_status().
#
- # 3. Finalize all prepared statements Measure the total memory
+ # 3. Finalize all prepared statements. Measure the total memory
# and the prepared statement memory again.
#
# 4. Repeat step 2.
diff --git a/test/default.test b/test/default.test
index 95a4ee0..d6b6f97 100644
--- a/test/default.test
+++ b/test/default.test
@@ -64,4 +64,39 @@ ifcapable pragma {
} {0 c {} 0 'abc' 0}
}
+do_execsql_test default-3.1 {
+ CREATE TABLE t3(
+ a INTEGER PRIMARY KEY AUTOINCREMENT,
+ b INT DEFAULT 12345 UNIQUE NOT NULL CHECK( b>=0 AND b<99999 ),
+ c VARCHAR(123,456) DEFAULT 'hello' NOT NULL ON CONFLICT REPLACE,
+ d REAL,
+ e FLOATING POINT(5,10) DEFAULT 4.36,
+ f NATIONAL CHARACTER(15) COLLATE RTRIM,
+ g LONG INTEGER DEFAULT( 3600*12 )
+ );
+ INSERT INTO t3 VALUES(null, 5, 'row1', '5.25', 'xyz', 321, '432');
+ SELECT a, typeof(a), b, typeof(b), c, typeof(c),
+ d, typeof(d), e, typeof(e), f, typeof(f),
+ g, typeof(g) FROM t3;
+} {1 integer 5 integer row1 text 5.25 real xyz text 321 text 432 integer}
+do_execsql_test default-3.2 {
+ DELETE FROM t3;
+ INSERT INTO t3 DEFAULT VALUES;
+ SELECT * FROM t3;
+} {2 12345 hello {} 4.36 {} 43200}
+do_execsql_test default-3.3 {
+ CREATE TABLE t300(
+ a INT DEFAULT 2147483647,
+ b INT DEFAULT 2147483648,
+ c INT DEFAULT +9223372036854775807,
+ d INT DEFAULT -2147483647,
+ e INT DEFAULT -2147483648,
+ f INT DEFAULT -9223372036854775808,
+ g INT DEFAULT (-(-9223372036854775808)),
+ h INT DEFAULT (-(-9223372036854775807))
+ );
+ INSERT INTO t300 DEFAULT VALUES;
+ SELECT * FROM t300;
+} {2147483647 2147483648 9223372036854775807 -2147483647 -2147483648 -9223372036854775808 9.22337203685478e+18 9223372036854775807}
+
finish_test
diff --git a/test/descidx1.test b/test/descidx1.test
index c7fab34..a223664 100644
--- a/test/descidx1.test
+++ b/test/descidx1.test
@@ -197,12 +197,12 @@ ifcapable bloblit {
} {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;
+ SELECT d FROM t2 WHERE a>=2 ORDER BY a;
}
} {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;
+ SELECT d FROM t2 WHERE a>2 ORDER BY a;
}
} {3.0 4.0 5.0 6.0}
do_test descidx1-4.5 {
diff --git a/test/distinct.test b/test/distinct.test
index fcbe4e6..78c2c1d 100644
--- a/test/distinct.test
+++ b/test/distinct.test
@@ -162,10 +162,10 @@ do_execsql_test 2.0 {
foreach {tn sql temptables res} {
1 "a, b FROM t1" {} {A B a b}
2 "b, a FROM t1" {} {B A b a}
- 3 "a, b, c FROM t1" {hash} {a b c A B C}
+ 3 "a, b, c FROM t1" {hash} {A B C a b c}
4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c}
5 "b FROM t1 WHERE a = 'a'" {} {b}
- 6 "b FROM t1" {hash} {b B}
+ 6 "b FROM t1 ORDER BY +b COLLATE binary" {btree hash} {B b}
7 "a FROM t1" {} {A a}
8 "b COLLATE nocase FROM t1" {} {b}
9 "b COLLATE nocase FROM t1 ORDER BY b COLLATE nocase" {} {b}
@@ -197,4 +197,29 @@ do_test 3.1 {
}]
} {0}
+#-------------------------------------------------------------------------
+# Ticket [fccbde530a6583bf2748400919f1603d5425995c] (2014-01-08)
+# The logic that computes DISTINCT sometimes thinks that a zeroblob()
+# and a blob of all zeros are different when they should be the same.
+#
+do_execsql_test 4.1 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(a INTEGER);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(1);
+ CREATE TABLE t2(x);
+ INSERT INTO t2
+ SELECT DISTINCT
+ CASE a WHEN 1 THEN x'0000000000'
+ WHEN 2 THEN zeroblob(5)
+ ELSE 'xyzzy' END
+ FROM t1;
+ SELECT quote(x) FROM t2 ORDER BY 1;
+} {'xyzzy' X'0000000000'}
+
finish_test
diff --git a/test/e_createtable.test b/test/e_createtable.test
index 351a0f7..08f606f 100644
--- a/test/e_createtable.test
+++ b/test/e_createtable.test
@@ -58,8 +58,6 @@ proc table_list {} {
}
-# EVIDENCE-OF: R-47266-09114 -- syntax diagram type-name
-#
do_createtable_tests 0.1.1 -repair {
drop_all_tables
} {
@@ -79,7 +77,7 @@ do_createtable_tests 0.1.2 -error {
}
-# EVIDENCE-OF: R-60689-48779 -- syntax diagram column-constraint
+# syntax diagram column-constraint
#
do_createtable_tests 0.2.1 -repair {
drop_all_tables
@@ -126,7 +124,7 @@ do_createtable_tests 0.2.1 -repair {
} {}
}
-# EVIDENCE-OF: R-58169-51804 -- syntax diagram table-constraint
+# -- syntax diagram table-constraint
#
do_createtable_tests 0.3.1 -repair {
drop_all_tables
@@ -145,7 +143,7 @@ do_createtable_tests 0.3.1 -repair {
4.1 "CREATE TABLE t1(c1, c2, FOREIGN KEY(c1) REFERENCES t2)" {}
}
-# EVIDENCE-OF: R-44826-22243 -- syntax diagram column-def
+# -- syntax diagram column-def
#
do_createtable_tests 0.4.1 -repair {
drop_all_tables
@@ -160,7 +158,7 @@ do_createtable_tests 0.4.1 -repair {
} {}
}
-# EVIDENCE-OF: R-45698-45677 -- syntax diagram create-table-stmt
+# -- syntax diagram create-table-stmt
#
do_createtable_tests 0.5.1 -repair {
drop_all_tables
@@ -185,7 +183,6 @@ do_createtable_tests 0.5.1 -repair {
15 "CREATE TABLE t1 AS SELECT count(*), max(b), min(a) FROM t2" {}
}
-# EVIDENCE-OF: R-24369-11919 -- syntax diagram foreign-key-clause
#
# 1: Explicit parent-key columns.
# 2: Implicit child-key columns.
@@ -887,9 +884,10 @@ do_execsql_test e_createtable-3.3.1 {
);
} {}
-# EVIDENCE-OF: R-10288-43169 For the purposes of the DEFAULT clause, an
+# EVIDENCE-OF: R-36381-62919 For the purposes of the DEFAULT clause, an
# expression is considered constant provided that it does not contain
-# any sub-queries or string constants enclosed in double quotes.
+# any sub-queries, column or table references, or string literals
+# enclosed in double-quotes instead of single-quotes.
#
do_createtable_tests 3.4.1 -error {
default value of column [x] is not constant
@@ -1106,8 +1104,8 @@ do_catchsql_test e_createtable-3.11.5 {
# EVIDENCE-OF: R-52382-54248 Each table in SQLite may have at most one
# PRIMARY KEY.
#
-# EVIDENCE-OF: R-18080-47271 If there is more than one PRIMARY KEY
-# clause in a single CREATE TABLE statement, it is an error.
+# EVIDENCE-OF: R-31826-01813 An error is raised if more than one PRIMARY
+# KEY clause appears in a CREATE TABLE statement.
#
# To test the two above, show that zero primary keys is Ok, one primary
# key is Ok, and two or more primary keys is an error.
@@ -1130,6 +1128,17 @@ do_createtable_tests 4.1.2 -error {
6 "CREATE TABLE t5(a INTEGER PRIMARY KEY, b, c, PRIMARY KEY(a))" {}
}
+# EVIDENCE-OF: R-54755-39291 The PRIMARY KEY is optional for ordinary
+# tables but is required for WITHOUT ROWID tables.
+#
+do_catchsql_test 4.1.3 {
+ CREATE TABLE t6(a, b); --ok
+} {0 {}}
+do_catchsql_test 4.1.4 {
+ CREATE TABLE t7(a, b) WITHOUT ROWID; --Error, no PRIMARY KEY
+} {1 {PRIMARY KEY missing on table t7}}
+
+
proc table_pk {tbl} {
set pk [list]
db eval "pragma table_info($tbl)" a {
@@ -1163,12 +1172,12 @@ do_createtable_tests 4.2 -repair {
2.3 "CREATE TABLE t5(a, b INTEGER PRIMARY KEY, c)" {b}
}
-# EVIDENCE-OF: R-33986-09410 Each row in a table with a primary key must
-# feature a unique combination of values in its primary key columns.
+# EVIDENCE-OF: R-59124-61339 Each row in a table with a primary key must
+# have a unique combination of values in its primary key columns.
#
-# EVIDENCE-OF: R-39102-06737 If an INSERT or UPDATE statement attempts
-# to modify the table content so that two or more rows feature identical
-# primary key values, it is a constraint violation.
+# EVIDENCE-OF: R-06471-16287 If an INSERT or UPDATE statement attempts
+# to modify the table content so that two or more rows have identical
+# primary key values, that is a constraint violation.
#
drop_all_tables
do_execsql_test 4.3.0 {
@@ -1185,13 +1194,14 @@ do_execsql_test 4.3.0 {
INSERT INTO t2 VALUES(X'ABCDEF', 'three');
} {}
-do_createtable_tests 4.3.1 -error { %s not unique } {
+do_createtable_tests 4.3.1 -error {UNIQUE constraint failed: t1.x} {
1 "INSERT INTO t1 VALUES(0, 0)" {"column x is"}
2 "INSERT INTO t1 VALUES(45.5, 'abc')" {"column x is"}
3 "INSERT INTO t1 VALUES(0.0, 'abc')" {"column x is"}
4 "INSERT INTO t1 VALUES('brambles', 'abc')" {"column x is"}
5 "INSERT INTO t1 VALUES(X'ABCDEF', 'abc')" {"column x is"}
-
+}
+do_createtable_tests 4.3.1 -error {UNIQUE constraint failed: t2.x, t2.y} {
6 "INSERT INTO t2 VALUES(0, 'zero')" {"columns x, y are"}
7 "INSERT INTO t2 VALUES(45.5, 'one')" {"columns x, y are"}
8 "INSERT INTO t2 VALUES(0.0, 'zero')" {"columns x, y are"}
@@ -1211,13 +1221,14 @@ do_createtable_tests 4.3.2 {
9 "INSERT INTO t2 VALUES('brambles', 'abc')" {}
10 "INSERT INTO t2 VALUES(X'ABCDEF', 'abc')" {}
}
-do_createtable_tests 4.3.3 -error { %s not unique } {
+do_createtable_tests 4.3.3 -error {UNIQUE constraint failed: t1.x} {
1 "UPDATE t1 SET x=0 WHERE y='two'" {"column x is"}
2 "UPDATE t1 SET x='brambles' WHERE y='three'" {"column x is"}
3 "UPDATE t1 SET x=45.5 WHERE y='zero'" {"column x is"}
4 "UPDATE t1 SET x=X'ABCDEF' WHERE y='one'" {"column x is"}
5 "UPDATE t1 SET x=0.0 WHERE y='three'" {"column x is"}
-
+}
+do_createtable_tests 4.3.3 -error {UNIQUE constraint failed: t2.x, t2.y} {
6 "UPDATE t2 SET x=0, y='zero' WHERE y='two'" {"columns x, y are"}
7 "UPDATE t2 SET x='brambles', y='two' WHERE y='three'"
{"columns x, y are"}
@@ -1253,8 +1264,9 @@ do_createtable_tests 4.4 {
14 "INSERT INTO t2 VALUES(NULL, NULL)" {}
}
-# EVIDENCE-OF: R-61866-38053 Unless the column is an INTEGER PRIMARY KEY
-# SQLite allows NULL values in a PRIMARY KEY column.
+# EVIDENCE-OF: R-35113-43214 Unless the column is an INTEGER PRIMARY KEY
+# or the table is a WITHOUT ROWID table or the column is declared NOT
+# NULL, SQLite allows NULL values in a PRIMARY KEY column.
#
# If the column is an integer primary key, attempting to insert a NULL
# into the column triggers the auto-increment behavior. Attempting
@@ -1276,6 +1288,14 @@ do_catchsql_test 4.5.3 {
INSERT INTO t3 VALUES(2, 5, 3);
UPDATE t3 SET u = NULL WHERE s = 2;
} {1 {datatype mismatch}}
+do_catchsql_test 4.5.4 {
+ CREATE TABLE t4(s, u INT PRIMARY KEY, v) WITHOUT ROWID;
+ INSERT INTO t4 VALUES(1, NULL, 2);
+} {1 {NOT NULL constraint failed: t4.u}}
+do_catchsql_test 4.5.5 {
+ CREATE TABLE t5(s, u INT PRIMARY KEY NOT NULL, v);
+ INSERT INTO t5 VALUES(1, NULL, 2);
+} {1 {NOT NULL constraint failed: t5.u}}
# EVIDENCE-OF: R-00227-21080 A UNIQUE constraint is similar to a PRIMARY
# KEY constraint, except that a single table may have any number of
@@ -1289,14 +1309,12 @@ do_createtable_tests 4.6 {
4 "CREATE TABLE t4(a, b, c, UNIQUE(a, b, c))" {}
}
-# EVIDENCE-OF: R-55240-58877 For each UNIQUE constraint on the table,
-# each row must feature a unique combination of values in the columns
+# EVIDENCE-OF: R-30981-64168 For each UNIQUE constraint on the table,
+# each row must contain a unique combination of values in the columns
# identified by the UNIQUE constraint.
#
-# EVIDENCE-OF: R-47733-51480 If an INSERT or UPDATE statement attempts
-# to modify the table content so that two or more rows feature identical
-# values in a set of columns that are subject to a UNIQUE constraint, it
-# is a constraint violation.
+# EVIDENCE-OF: R-59124-61339 Each row in a table with a primary key must
+# have a unique combination of values in its primary key columns.
#
do_execsql_test 4.7.0 {
INSERT INTO t1 VALUES(1, 2);
@@ -1308,29 +1326,29 @@ do_execsql_test 4.7.0 {
INSERT INTO t4 VALUES('xyx', 2, 1);
INSERT INTO t4 VALUES('uvw', 1, 1);
}
-do_createtable_tests 4.7.1 -error { %s not unique } {
- 1 "INSERT INTO t1 VALUES(1, 'one')" {{column a is}}
- 2 "INSERT INTO t1 VALUES(4.3, 'two')" {{column a is}}
- 3 "INSERT INTO t1 VALUES('reveal', 'three')" {{column a is}}
- 4 "INSERT INTO t1 VALUES(X'123456', 'four')" {{column a is}}
+do_createtable_tests 4.7.1 -error {UNIQUE constraint failed: %s} {
+ 1 "INSERT INTO t1 VALUES(1, 'one')" {{t1.a}}
+ 2 "INSERT INTO t1 VALUES(4.3, 'two')" {{t1.a}}
+ 3 "INSERT INTO t1 VALUES('reveal', 'three')" {{t1.a}}
+ 4 "INSERT INTO t1 VALUES(X'123456', 'four')" {{t1.a}}
- 5 "UPDATE t1 SET a = 1 WHERE rowid=2" {{column a is}}
- 6 "UPDATE t1 SET a = 4.3 WHERE rowid=3" {{column a is}}
- 7 "UPDATE t1 SET a = 'reveal' WHERE rowid=4" {{column a is}}
- 8 "UPDATE t1 SET a = X'123456' WHERE rowid=1" {{column a is}}
+ 5 "UPDATE t1 SET a = 1 WHERE rowid=2" {{t1.a}}
+ 6 "UPDATE t1 SET a = 4.3 WHERE rowid=3" {{t1.a}}
+ 7 "UPDATE t1 SET a = 'reveal' WHERE rowid=4" {{t1.a}}
+ 8 "UPDATE t1 SET a = X'123456' WHERE rowid=1" {{t1.a}}
- 9 "INSERT INTO t4 VALUES('xyx', 1, 1)" {{columns a, b, c are}}
- 10 "INSERT INTO t4 VALUES('xyx', 2, 1)" {{columns a, b, c are}}
- 11 "INSERT INTO t4 VALUES('uvw', 1, 1)" {{columns a, b, c are}}
+ 9 "INSERT INTO t4 VALUES('xyx', 1, 1)" {{t4.a, t4.b, t4.c}}
+ 10 "INSERT INTO t4 VALUES('xyx', 2, 1)" {{t4.a, t4.b, t4.c}}
+ 11 "INSERT INTO t4 VALUES('uvw', 1, 1)" {{t4.a, t4.b, t4.c}}
- 12 "UPDATE t4 SET a='xyx' WHERE rowid=3" {{columns a, b, c are}}
- 13 "UPDATE t4 SET b=1 WHERE rowid=2" {{columns a, b, c are}}
- 14 "UPDATE t4 SET a=0, b=0, c=0" {{columns a, b, c are}}
+ 12 "UPDATE t4 SET a='xyx' WHERE rowid=3" {{t4.a, t4.b, t4.c}}
+ 13 "UPDATE t4 SET b=1 WHERE rowid=2" {{t4.a, t4.b, t4.c}}
+ 14 "UPDATE t4 SET a=0, b=0, c=0" {{t4.a, t4.b, t4.c}}
}
-# EVIDENCE-OF: R-21289-11559 As with PRIMARY KEY constraints, for the
-# purposes of UNIQUE constraints NULL values are considered distinct
-# from all other values (including other NULLs).
+# EVIDENCE-OF: R-00404-17670 For the purposes of UNIQUE constraints,
+# NULL values are considered distinct from all other values, including
+# other NULLs.
#
do_createtable_tests 4.8 {
1 "INSERT INTO t1 VALUES(NULL, NULL)" {}
@@ -1345,10 +1363,9 @@ do_createtable_tests 4.8 {
9 "UPDATE t4 SET c = NULL" {}
}
-# EVIDENCE-OF: R-26983-26377 INTEGER PRIMARY KEY columns aside, both
-# UNIQUE and PRIMARY KEY constraints are implemented by creating an
-# index in the database (in the same way as a "CREATE UNIQUE INDEX"
-# statement would).
+# EVIDENCE-OF: R-55820-29984 In most cases, UNIQUE and PRIMARY KEY
+# constraints are implemented by creating a unique index in the
+# database.
do_createtable_tests 4.9 -repair drop_all_tables -query {
SELECT count(*) FROM sqlite_master WHERE type='index'
} {
@@ -1359,7 +1376,7 @@ do_createtable_tests 4.9 -repair drop_all_tables -query {
5 "CREATE TABLE t1(a PRIMARY KEY, b, c, UNIQUE(c, b))" 2
}
-# EVIDENCE-OF: R-02252-33116 Such an index is used like any other index
+# Obsolete: R-02252-33116 Such an index is used like any other index
# in the database to optimize queries.
#
do_execsql_test 4.10.0 {
@@ -1368,13 +1385,13 @@ do_execsql_test 4.10.0 {
}
do_createtable_tests 4.10 {
1 "EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 5"
- {0 0 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (b=?) (~1 rows)}}
+ {0 0 0 {SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (b=?)}}
2 "EXPLAIN QUERY PLAN SELECT * FROM t2 ORDER BY b, c"
- {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1 (~1000000 rows)}}
+ {0 0 0 {SCAN TABLE t2 USING INDEX sqlite_autoindex_t2_1}}
3 "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b=10 AND c>10"
- {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?) (~2 rows)}}
+ {0 0 0 {SEARCH TABLE t2 USING INDEX sqlite_autoindex_t2_1 (b=? AND c>?)}}
}
# EVIDENCE-OF: R-45493-35653 A CHECK constraint may be attached to a
@@ -1407,21 +1424,21 @@ do_execsql_test 4.11 {
INSERT INTO t2 SELECT * FROM x2;
}
-do_createtable_tests 4.11 -error {constraint failed} {
- 1a "INSERT INTO x1 VALUES('one', 0)" {}
- 1b "INSERT INTO t1 VALUES('one', -4.0)" {}
+do_createtable_tests 4.11 -error {CHECK constraint failed: %s} {
+ 1a "INSERT INTO x1 VALUES('one', 0)" {x1}
+ 1b "INSERT INTO t1 VALUES('one', -4.0)" {t1}
- 2a "INSERT INTO x2 VALUES('abc', 1)" {}
- 2b "INSERT INTO t2 VALUES('abc', 1)" {}
+ 2a "INSERT INTO x2 VALUES('abc', 1)" {x2}
+ 2b "INSERT INTO t2 VALUES('abc', 1)" {t2}
- 3a "INSERT INTO x2 VALUES(0, 'abc')" {}
- 3b "INSERT INTO t2 VALUES(0, 'abc')" {}
+ 3a "INSERT INTO x2 VALUES(0, 'abc')" {x2}
+ 3b "INSERT INTO t2 VALUES(0, 'abc')" {t2}
- 4a "UPDATE t1 SET b=-1 WHERE rowid=1" {}
- 4b "UPDATE x1 SET b=-1 WHERE rowid=1" {}
+ 4a "UPDATE t1 SET b=-1 WHERE rowid=1" {t1}
+ 4b "UPDATE x1 SET b=-1 WHERE rowid=1" {x1}
- 4a "UPDATE x2 SET a='' WHERE rowid=1" {}
- 4b "UPDATE t2 SET a='' WHERE rowid=1" {}
+ 4a "UPDATE x2 SET a='' WHERE rowid=1" {x2}
+ 4b "UPDATE t2 SET a='' WHERE rowid=1" {t2}
}
# EVIDENCE-OF: R-34109-39108 If the CHECK expression evaluates to NULL,
@@ -1472,9 +1489,7 @@ do_execsql_test 4.14.0 {
INSERT INTO t3 VALUES('x', 'y', 'z');
INSERT INTO t3 VALUES(1, 2, 3);
}
-do_createtable_tests 4.14 -error {
- %s may not be NULL
-} {
+do_createtable_tests 4.14 -error {NOT NULL constraint failed: %s} {
1 "INSERT INTO t1 VALUES(NULL, 'a')" {t1.a}
2 "INSERT INTO t2 VALUES(NULL, 'b')" {t2.a}
3 "INSERT INTO t3 VALUES('c', 'd', NULL)" {t3.c}
@@ -1540,12 +1555,12 @@ do_execsql_test 4.15.0 {
}
foreach {tn tbl res ac data} {
- 1 t1_ab {1 {column a is not unique}} 0 {1 one 2 two 3 three}
- 2 t1_ro {1 {column a is not unique}} 1 {1 one 2 two}
- 3 t1_fa {1 {column a is not unique}} 0 {1 one 2 two 3 three 4 string}
+ 1 t1_ab {1 {UNIQUE constraint failed: t1_ab.a}} 0 {1 one 2 two 3 three}
+ 2 t1_ro {1 {UNIQUE constraint failed: t1_ro.a}} 1 {1 one 2 two}
+ 3 t1_fa {1 {UNIQUE constraint failed: t1_fa.a}} 0 {1 one 2 two 3 three 4 string}
4 t1_ig {0 {}} 0 {1 one 2 two 3 three 4 string 6 string}
5 t1_re {0 {}} 0 {1 one 2 two 4 string 3 string 6 string}
- 6 t1_xx {1 {column a is not unique}} 0 {1 one 2 two 3 three}
+ 6 t1_xx {1 {UNIQUE constraint failed: t1_xx.a}} 0 {1 one 2 two 3 three}
} {
catchsql COMMIT
do_execsql_test 4.15.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
@@ -1558,12 +1573,12 @@ foreach {tn tbl res ac data} {
do_execsql_test 4.15.$tn.4 "SELECT * FROM $tbl" $data
}
foreach {tn tbl res ac data} {
- 1 t2_ab {1 {t2_ab.b may not be NULL}} 0 {1 one 2 two 3 three}
- 2 t2_ro {1 {t2_ro.b may not be NULL}} 1 {1 one 2 two}
- 3 t2_fa {1 {t2_fa.b may not be NULL}} 0 {1 one 2 two 3 three 4 xx}
+ 1 t2_ab {1 {NOT NULL constraint failed: t2_ab.b}} 0 {1 one 2 two 3 three}
+ 2 t2_ro {1 {NOT NULL constraint failed: t2_ro.b}} 1 {1 one 2 two}
+ 3 t2_fa {1 {NOT NULL constraint failed: t2_fa.b}} 0 {1 one 2 two 3 three 4 xx}
4 t2_ig {0 {}} 0 {1 one 2 two 3 three 4 xx 6 xx}
- 5 t2_re {1 {t2_re.b may not be NULL}} 0 {1 one 2 two 3 three}
- 6 t2_xx {1 {t2_xx.b may not be NULL}} 0 {1 one 2 two 3 three}
+ 5 t2_re {1 {NOT NULL constraint failed: t2_re.b}} 0 {1 one 2 two 3 three}
+ 6 t2_xx {1 {NOT NULL constraint failed: t2_xx.b}} 0 {1 one 2 two 3 three}
} {
catchsql COMMIT
do_execsql_test 4.16.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
@@ -1576,12 +1591,16 @@ foreach {tn tbl res ac data} {
do_execsql_test 4.16.$tn.4 "SELECT * FROM $tbl" $data
}
foreach {tn tbl res ac data} {
- 1 t3_ab {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three}
- 2 t3_ro {1 {columns a, b are not unique}} 1 {1 one 2 two}
- 3 t3_fa {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three 4 three}
+ 1 t3_ab {1 {UNIQUE constraint failed: t3_ab.a, t3_ab.b}}
+ 0 {1 one 2 two 3 three}
+ 2 t3_ro {1 {UNIQUE constraint failed: t3_ro.a, t3_ro.b}}
+ 1 {1 one 2 two}
+ 3 t3_fa {1 {UNIQUE constraint failed: t3_fa.a, t3_fa.b}}
+ 0 {1 one 2 two 3 three 4 three}
4 t3_ig {0 {}} 0 {1 one 2 two 3 three 4 three 6 three}
5 t3_re {0 {}} 0 {1 one 2 two 4 three 3 three 6 three}
- 6 t3_xx {1 {columns a, b are not unique}} 0 {1 one 2 two 3 three}
+ 6 t3_xx {1 {UNIQUE constraint failed: t3_xx.a, t3_xx.b}}
+ 0 {1 one 2 two 3 three}
} {
catchsql COMMIT
do_execsql_test 4.17.$tn.1 "BEGIN; INSERT INTO $tbl VALUES(3, 'three')"
@@ -1612,7 +1631,7 @@ do_execsql_test 4.18.1 {
do_execsql_test 4.18.2 { BEGIN; INSERT INTO t4 VALUES(5, 6) }
do_catchsql_test 4.18.3 {
INSERT INTO t4 SELECT a+4, b+4 FROM t4
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t4}}
do_test e_createtable-4.18.4 { sqlite3_get_autocommit db } 0
do_execsql_test 4.18.5 { SELECT * FROM t4 } {1 2 3 4 5 6}
@@ -1625,7 +1644,7 @@ do_execsql_test 4.19.0 {
do_catchsql_test 4.19.1 { INSERT INTO t5 VALUES(NULL, 'not null') } {0 {}}
do_execsql_test 4.19.2 { SELECT * FROM t5 } {}
do_catchsql_test 4.19.3 { INSERT INTO t5 VALUES('not null', NULL) } \
- {1 {t5.b may not be NULL}}
+ {1 {NOT NULL constraint failed: t5.b}}
do_execsql_test 4.19.4 { SELECT * FROM t5 } {}
#------------------------------------------------------------------------
@@ -1636,6 +1655,10 @@ do_execsql_test 4.19.4 { SELECT * FROM t5 } {}
# of the special case-independent names "rowid", "oid", or "_rowid_" in
# place of a column name.
#
+# EVIDENCE-OF: R-06726-07466 A column name can be any of the names
+# defined in the CREATE TABLE statement or one of the following special
+# identifiers: "ROWID", "OID", or "_ROWID_".
+#
drop_all_tables
do_execsql_test 5.1.0 {
CREATE TABLE t1(x, y);
@@ -1660,6 +1683,10 @@ do_createtable_tests 5.1 {
# explicitly declared column and cannot be used to retrieve the integer
# rowid value.
#
+# EVIDENCE-OF: R-44615-33286 The special identifiers only refer to the
+# row key if the CREATE TABLE statement does not define a real column
+# with the same name.
+#
do_execsql_test 5.2.0 {
CREATE TABLE t2(oid, b);
CREATE TABLE t3(a, _rowid_);
@@ -1694,10 +1721,10 @@ proc is_integer_primary_key {tbl col} {
}]] 0
}
-# EVIDENCE-OF: R-53738-31673 With one exception, if a table has a
-# primary key that consists of a single column, and the declared type of
-# that column is "INTEGER" in any mixture of upper and lower case, then
-# the column becomes an alias for the rowid.
+# EVIDENCE-OF: R-47901-33947 With one exception noted below, if a rowid
+# table has a primary key that consists of a single column and the
+# declared type of that column is "INTEGER" in any mixture of upper and
+# lower case, then the column becomes an alias for the rowid.
#
# EVIDENCE-OF: R-45951-08347 if the declaration of a column with
# declared type "INTEGER" includes an "PRIMARY KEY DESC" clause, it does
@@ -1750,16 +1777,16 @@ do_execsql_test 5.4.3 {
do_catchsql_test 5.4.4.1 {
INSERT INTO t6 VALUES(2)
-} {1 {column pk is not unique}}
+} {1 {UNIQUE constraint failed: t6.pk}}
do_catchsql_test 5.4.4.2 {
INSERT INTO t7 VALUES(2)
-} {1 {column pk is not unique}}
+} {1 {UNIQUE constraint failed: t7.pk}}
do_catchsql_test 5.4.4.3 {
INSERT INTO t8 VALUES(2)
-} {1 {column pk is not unique}}
+} {1 {UNIQUE constraint failed: t8.pk}}
do_catchsql_test 5.4.4.4 {
INSERT INTO t9 VALUES(2)
-} {1 {column pk is not unique}}
+} {1 {UNIQUE constraint failed: t9.pk}}
# EVIDENCE-OF: R-56094-57830 the following three table declarations all
# cause the column "x" to be an alias for the rowid (an integer primary
diff --git a/test/e_delete.test b/test/e_delete.test
index 31bb324..b857cf1 100644
--- a/test/e_delete.test
+++ b/test/e_delete.test
@@ -29,9 +29,8 @@ do_execsql_test e_delete-0.0 {
CREATE INDEX i1 ON t1(a);
} {}
-# EVIDENCE-OF: R-62077-19799 -- syntax diagram delete-stmt
-#
-# EVIDENCE-OF: R-60796-31013 -- syntax diagram qualified-table-name
+# -- syntax diagram delete-stmt
+# -- syntax diagram qualified-table-name
#
do_delete_tests e_delete-0.1 {
1 "DELETE FROM t1" {}
@@ -292,7 +291,7 @@ do_delete_tests e_delete-2.5 -error { near "%s": syntax error } {
# of the DELETE statement is extended by the addition of optional ORDER
# BY and LIMIT clauses:
#
-# EVIDENCE-OF: R-52694-53361 -- syntax diagram delete-stmt-limited
+# -- syntax diagram delete-stmt-limited
#
do_delete_tests e_delete-3.1 {
1 "DELETE FROM t1 LIMIT 5" {}
diff --git a/test/e_droptrigger.test b/test/e_droptrigger.test
index fe96104..84dfe72 100644
--- a/test/e_droptrigger.test
+++ b/test/e_droptrigger.test
@@ -69,7 +69,7 @@ proc droptrigger_reopen_db {{event INSERT}} {
}
-# EVIDENCE-OF: R-27975-10951 -- syntax diagram drop-trigger-stmt
+# -- syntax diagram drop-trigger-stmt
#
do_droptrigger_tests 1.1 -repair {
droptrigger_reopen_db
diff --git a/test/e_dropview.test b/test/e_dropview.test
index 4a4b9c3..143dce2 100644
--- a/test/e_dropview.test
+++ b/test/e_dropview.test
@@ -70,7 +70,7 @@ proc do_dropview_tests {nm args} {
uplevel do_select_tests $nm $args
}
-# EVIDENCE-OF: R-53136-36436 -- syntax diagram drop-view-stmt
+# -- syntax diagram drop-view-stmt
#
# All paths in the syntax diagram for DROP VIEW are tested by tests 1.*.
#
diff --git a/test/e_expr.test b/test/e_expr.test
index 74d0c40..271635f 100644
--- a/test/e_expr.test
+++ b/test/e_expr.test
@@ -366,9 +366,9 @@ db collate reverse reverse_collate
# EVIDENCE-OF: R-59577-33471 The COLLATE operator is a unary postfix
# operator that assigns a collating sequence to an expression.
#
-# EVIDENCE-OF: R-23441-22541 The COLLATE operator has a higher
-# precedence (binds more tightly) than any prefix unary operator or any
-# binary operator.
+# EVIDENCE-OF: R-36231-30731 The COLLATE operator has a higher
+# precedence (binds more tightly) than any binary operator and any unary
+# prefix operator except "~".
#
do_execsql_test e_expr-9.1 { SELECT 'abcd' < 'bbbb' COLLATE reverse } 0
do_execsql_test e_expr-9.2 { SELECT ('abcd' < 'bbbb') COLLATE reverse } 1
@@ -450,7 +450,7 @@ do_execsql_test e_expr-10.3.4 { SELECT typeof('isn''t') } {text}
# containing hexadecimal data and preceded by a single "x" or "X"
# character.
#
-# EVIDENCE-OF: R-39344-59787 For example: X'53514C697465'
+# EVIDENCE-OF: R-19836-11244 Example: X'53514C697465'
#
do_execsql_test e_expr-10.4.1 { SELECT typeof(X'0123456789ABCDEF') } blob
do_execsql_test e_expr-10.4.2 { SELECT typeof(x'0123456789ABCDEF') } blob
@@ -631,7 +631,7 @@ do_test e_expr-11.7.1 { sqlite3_finalize $stmt } SQLITE_OK
#-------------------------------------------------------------------------
# "Test" the syntax diagrams in lang_expr.html.
#
-# EVIDENCE-OF: R-02989-21050 -- syntax diagram signed-number
+# -- syntax diagram signed-number
#
do_execsql_test e_expr-12.1.1 { SELECT 0, +0, -0 } {0 0 0}
do_execsql_test e_expr-12.1.2 { SELECT 1, +1, -1 } {1 1 -1}
@@ -646,7 +646,7 @@ do_execsql_test e_expr-12.1.6 {
SELECT 0.0001, +0.0001, -0.0001
} {0.0001 0.0001 -0.0001}
-# EVIDENCE-OF: R-43188-60852 -- syntax diagram literal-value
+# -- syntax diagram literal-value
#
set sqlite_current_time 1
do_execsql_test e_expr-12.2.1 {SELECT 123} {123}
@@ -659,7 +659,7 @@ do_execsql_test e_expr-12.2.7 {SELECT CURRENT_DATE} {1970-01-01}
do_execsql_test e_expr-12.2.8 {SELECT CURRENT_TIMESTAMP} {{1970-01-01 00:00:01}}
set sqlite_current_time 0
-# EVIDENCE-OF: R-50544-32159 -- syntax diagram expr
+# -- syntax diagram expr
#
forcedelete test.db2
execsql {
@@ -816,7 +816,7 @@ foreach {tn expr} {
}
}
-# EVIDENCE-OF: R-39820-63916 -- syntax diagram raise-function
+# -- syntax diagram raise-function
#
foreach {tn raiseexpr} {
1 "RAISE(IGNORE)"
@@ -1081,9 +1081,9 @@ ifcapable !icu {
# EVIDENCE-OF: R-33693-50180 The REGEXP operator is a special syntax for
# the regexp() user function.
#
-# EVIDENCE-OF: R-57289-13578 If a application-defined SQL function named
-# "regexp" is added at run-time, that function will be called in order
-# to implement the REGEXP operator.
+# EVIDENCE-OF: R-65524-61849 If an application-defined SQL function
+# named "regexp" is added at run-time, then the "X REGEXP Y" operator
+# will be implemented as a call to "regexp(Y,X)".
#
proc regexpfunc {args} {
eval lappend ::regexpargs $args
@@ -1294,7 +1294,7 @@ proc rev {str} {
set ret
}
proc reverse {lhs rhs} {
- string compare [rev $lhs] [ref $rhs]
+ string compare [rev $lhs] [rev $rhs]
}
db collate reverse reverse
do_execsql_test e_expr-23.1.1 {
@@ -1317,7 +1317,7 @@ do_execsql_test e_expr-23.1.4 {
} {B}
do_execsql_test e_expr-23.1.5 {
SELECT CASE b WHEN a THEN 'A' ELSE 'B' END FROM t1
-} {A}
+} {B}
do_execsql_test e_expr-23.1.6 {
SELECT CASE 55 WHEN '55' THEN 'A' ELSE 'B' END
} {B}
@@ -1407,10 +1407,12 @@ do_test e_expr-26.1.6 { set ::evalcount } {5}
#-------------------------------------------------------------------------
# Test statements related to CAST expressions.
#
-# EVIDENCE-OF: R-65079-31758 Application of a CAST expression is
-# different to application of a column affinity, as with a CAST
-# expression the storage class conversion is forced even if it is lossy
-# and irrreversible.
+# EVIDENCE-OF: R-20854-17109 A CAST conversion is similar to the
+# conversion that takes place when a column affinity is applied to a
+# value except that with the CAST operator the conversion always takes
+# place even if the conversion lossy and irreversible, whereas column
+# affinity only changes the data type of a value if the change is
+# lossless and reversible.
#
do_execsql_test e_expr-27.1.1 {
CREATE TABLE t3(a TEXT, b REAL, c INTEGER);
@@ -1594,26 +1596,36 @@ do_expr_test e_expr-30.4.1 { CAST('' AS INTEGER) } integer 0
do_expr_test e_expr-30.4.2 { CAST('not a number' AS INTEGER) } integer 0
do_expr_test e_expr-30.4.3 { CAST('XXI' AS INTEGER) } integer 0
-# EVIDENCE-OF: R-00741-38776 A cast of a REAL value into an INTEGER will
-# truncate the fractional part of the REAL.
+# EVIDENCE-OF: R-08980-53124 The CAST operator understands decimal
+# integers only &mdash; conversion of hexadecimal integers stops at
+# the "x" in the "0x" prefix of the hexadecimal integer string and thus
+# result of the CAST is always zero.
+do_expr_test e_expr-30.5.1 { CAST('0x1234' AS INTEGER) } integer 0
+do_expr_test e_expr-30.5.2 { CAST('0X1234' AS INTEGER) } integer 0
+
+# EVIDENCE-OF: R-02752-50091 A cast of a REAL value into an INTEGER
+# results in the integer between the REAL value and zero that is closest
+# to the REAL value.
#
do_expr_test e_expr-31.1.1 { CAST(3.14159 AS INTEGER) } integer 3
do_expr_test e_expr-31.1.2 { CAST(1.99999 AS INTEGER) } integer 1
do_expr_test e_expr-31.1.3 { CAST(-1.99999 AS INTEGER) } integer -1
do_expr_test e_expr-31.1.4 { CAST(-0.99999 AS INTEGER) } integer 0
-# EVIDENCE-OF: R-49503-28105 If a REAL is too large to be represented as
-# an INTEGER then the result of the cast is the largest negative
-# integer: -9223372036854775808.
+# EVIDENCE-OF: R-51517-40824 If a REAL is greater than the greatest
+# possible signed integer (+9223372036854775807) then the result is the
+# greatest possible signed integer and if the REAL is less than the
+# least possible signed integer (-9223372036854775808) then the result
+# is the least possible signed integer.
#
-do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer -9223372036854775808
+do_expr_test e_expr-31.2.1 { CAST(2e+50 AS INT) } integer 9223372036854775807
do_expr_test e_expr-31.2.2 { CAST(-2e+50 AS INT) } integer -9223372036854775808
do_expr_test e_expr-31.2.3 {
CAST(-9223372036854775809.0 AS INT)
} integer -9223372036854775808
do_expr_test e_expr-31.2.4 {
CAST(9223372036854775809.0 AS INT)
-} integer -9223372036854775808
+} integer 9223372036854775807
# EVIDENCE-OF: R-09295-61337 Casting a TEXT or BLOB value into NUMERIC
@@ -1847,5 +1859,43 @@ foreach {tn expr} {
do_expr_test e_expr-36.4.$tn $expr null {}
}
+# EVIDENCE-OF: R-62477-06476 For example, the values NULL, 0.0, 0,
+# 'english' and '0' are all considered to be false.
+#
+do_execsql_test e_expr-37.1 {
+ SELECT CASE WHEN NULL THEN 'true' ELSE 'false' END;
+} {false}
+do_execsql_test e_expr-37.2 {
+ SELECT CASE WHEN 0.0 THEN 'true' ELSE 'false' END;
+} {false}
+do_execsql_test e_expr-37.3 {
+ SELECT CASE WHEN 0 THEN 'true' ELSE 'false' END;
+} {false}
+do_execsql_test e_expr-37.4 {
+ SELECT CASE WHEN 'engligh' THEN 'true' ELSE 'false' END;
+} {false}
+do_execsql_test e_expr-37.5 {
+ SELECT CASE WHEN '0' THEN 'true' ELSE 'false' END;
+} {false}
+
+# EVIDENCE-OF: R-55532-10108 Values 1, 1.0, 0.1, -0.1 and '1english' are
+# considered to be true.
+#
+do_execsql_test e_expr-37.6 {
+ SELECT CASE WHEN 1 THEN 'true' ELSE 'false' END;
+} {true}
+do_execsql_test e_expr-37.7 {
+ SELECT CASE WHEN 1.0 THEN 'true' ELSE 'false' END;
+} {true}
+do_execsql_test e_expr-37.8 {
+ SELECT CASE WHEN 0.1 THEN 'true' ELSE 'false' END;
+} {true}
+do_execsql_test e_expr-37.9 {
+ SELECT CASE WHEN -0.1 THEN 'true' ELSE 'false' END;
+} {true}
+do_execsql_test e_expr-37.10 {
+ SELECT CASE WHEN '1english' THEN 'true' ELSE 'false' END;
+} {true}
+
finish_test
diff --git a/test/e_fkey.test b/test/e_fkey.test
index 001ba6c..0975650 100644
--- a/test/e_fkey.test
+++ b/test/e_fkey.test
@@ -135,9 +135,9 @@ reset_db
#
# This also tests that foreign key constraints are disabled by default.
#
-# EVIDENCE-OF: R-59578-04990 Foreign key constraints are disabled by
+# EVIDENCE-OF: R-44261-39702 Foreign key constraints are disabled by
# default (for backwards compatibility), so must be enabled separately
-# for each database connection separately.
+# for each database connection.
#
drop_all_tables
do_test e_fkey-4.1 {
@@ -163,9 +163,10 @@ do_test e_fkey-4.2 {
} {world}
#-------------------------------------------------------------------------
-# EVIDENCE-OF: R-15278-54456 The application can can also use a PRAGMA
+# EVIDENCE-OF: R-08013-37737 The application can also use a PRAGMA
# foreign_keys statement to determine if foreign keys are currently
# enabled.
+
#
# This also tests the example code in section 2 of foreignkeys.in.
#
@@ -211,7 +212,7 @@ do_test e_fkey-6.1 {
catchsql {
DELETE FROM t1
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-6.2 {
execsql { PRAGMA foreign_keys }
} {1}
@@ -265,11 +266,11 @@ do_test e_fkey-7.1 {
#
do_test e_fkey-8.1 {
catchsql { INSERT INTO track VALUES(1, 'track 1', 1) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-8.2 {
execsql { INSERT INTO artist VALUES(2, 'artist 1') }
catchsql { INSERT INTO track VALUES(1, 'track 1', 1) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-8.2 {
execsql { INSERT INTO track VALUES(1, 'track 1', 2) }
} {}
@@ -283,7 +284,7 @@ do_test e_fkey-8.2 {
#
do_test e_fkey-9.1 {
catchsql { DELETE FROM artist WHERE artistid = 2 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-9.2 {
execsql {
DELETE FROM track WHERE trackartist = 2;
@@ -311,14 +312,14 @@ do_test e_fkey-10.2 {
do_test e_fkey-10.3 {
# Setting the trackid to a non-NULL value fails, of course.
catchsql { UPDATE track SET trackartist = 5 WHERE trackid = 1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-10.4 {
execsql {
INSERT INTO artist VALUES(5, 'artist 5');
UPDATE track SET trackartist = 5 WHERE trackid = 1;
}
catchsql { DELETE FROM artist WHERE artistid = 5}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-10.5 {
execsql {
UPDATE track SET trackartist = NULL WHERE trackid = 1;
@@ -344,8 +345,8 @@ proc test_r52486_21352 {tn sql} {
set res [catchsql $sql]
set results {
{0 {}}
- {1 {PRIMARY KEY must be unique}}
- {1 {foreign key constraint failed}}
+ {1 {UNIQUE constraint failed: artist.artistid}}
+ {1 {FOREIGN KEY constraint failed}}
}
if {[lsearch $results $res]<0} {
error $res
@@ -409,7 +410,7 @@ do_test e_fkey-12.1 {
} {}
do_test e_fkey-12.2 {
catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) }
-} {1 {track.trackartist may not be NULL}}
+} {1 {NOT NULL constraint failed: track.trackartist}}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-16127-35442
@@ -438,7 +439,7 @@ do_test e_fkey-13.1 {
} {}
do_test e_fkey-13.2 {
catchsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', 3) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-13.3 {
execsql { INSERT INTO track VALUES(14, 'Mr. Bojangles', NULL) }
} {}
@@ -446,7 +447,7 @@ do_test e_fkey-13.4 {
catchsql {
UPDATE track SET trackartist = 3 WHERE trackname = 'Mr. Bojangles';
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-13.5 {
execsql {
INSERT INTO artist VALUES(3, 'Sammy Davis Jr.');
@@ -464,7 +465,7 @@ do_test e_fkey-14.1 {
catchsql {
DELETE FROM artist WHERE artistname = 'Frank Sinatra';
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-14.2 {
execsql {
DELETE FROM track WHERE trackname = 'My Way';
@@ -475,7 +476,7 @@ do_test e_fkey-14.3 {
catchsql {
UPDATE artist SET artistid=4 WHERE artistname = 'Dean Martin';
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-14.4 {
execsql {
DELETE FROM track WHERE trackname IN('That''s Amore', 'Christmas Blues');
@@ -513,7 +514,7 @@ do_test e_fkey-15.1 {
proc test_efkey_45 {tn isError sql} {
do_test e_fkey-15.$tn.1 "
catchsql {$sql}
- " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
+ " [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
do_test e_fkey-15.$tn.2 {
execsql {
@@ -557,10 +558,10 @@ do_test e_fkey-16.2 {
} {}
do_test e_fkey-16.3 {
catchsql { UPDATE t2 SET b = 'two' WHERE rowid = 1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-16.4 {
catchsql { DELETE FROM t1 WHERE rowid = 1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# Specifically, test that when comparing child and parent key values the
@@ -592,7 +593,7 @@ do_test e_fkey-17.3 {
} {integer integer text}
do_test e_fkey-17.4 {
catchsql { DELETE FROM t1 WHERE rowid = 2 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
###########################################################################
### SECTION 3: Required and Suggested Database Indexes
@@ -896,7 +897,7 @@ do_test e_fkey-23.1 {
proc test_efkey_60 {tn isError sql} {
do_test e_fkey-23.$tn "
catchsql {$sql}
- " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
+ " [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
}
test_efkey_60 2 1 "INSERT INTO c1 VALUES(239, 231)"
@@ -933,7 +934,7 @@ do_test e_fkey-24.1 {
proc test_efkey_61 {tn isError sql} {
do_test e_fkey-24.$tn "
catchsql {$sql}
- " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
+ " [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
}
foreach {tn c} [list 2 c1 3 c2 4 c3] {
test_efkey_61 $tn.1 1 "INSERT INTO $c VALUES(1, 2)"
@@ -974,15 +975,15 @@ do_execsql_test e_fkey-25.2 {
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?;
} {
- 0 0 0 {SCAN TABLE artist (~1000000 rows)}
- 0 0 0 {SCAN TABLE track (~100000 rows)}
+ 0 0 0 {SCAN TABLE artist}
+ 0 0 0 {SCAN TABLE track}
}
do_execsql_test e_fkey-25.3 {
PRAGMA foreign_keys = ON;
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
} {
- 0 0 0 {SCAN TABLE artist (~1000000 rows)}
- 0 0 0 {SCAN TABLE track (~100000 rows)}
+ 0 0 0 {SCAN TABLE artist}
+ 0 0 0 {SCAN TABLE track}
}
do_test e_fkey-25.4 {
execsql {
@@ -998,7 +999,7 @@ do_test e_fkey-25.5 {
concat \
[execsql { SELECT rowid FROM track WHERE trackartist = 5 }] \
[catchsql { DELETE FROM artist WHERE artistid = 5 }]
-} {1 1 {foreign key constraint failed}}
+} {1 1 {FOREIGN KEY constraint failed}}
do_test e_fkey-25.6 {
concat \
@@ -1010,7 +1011,7 @@ do_test e_fkey-25.7 {
concat \
[execsql { SELECT rowid FROM track WHERE trackartist = 6 }] \
[catchsql { DELETE FROM artist WHERE artistid = 6 }]
-} {2 1 {foreign key constraint failed}}
+} {2 1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# EVIDENCE-OF: R-47936-10044 Or, more generally:
@@ -1099,15 +1100,15 @@ do_test e_fkey-27.2 {
do_execsql_test e_fkey-27.3 {
EXPLAIN QUERY PLAN UPDATE artist SET artistid = ?, artistname = ?
} {
- 0 0 0 {SCAN TABLE artist (~1000000 rows)}
- 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
- 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
+ 0 0 0 {SCAN TABLE artist}
+ 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
+ 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
}
do_execsql_test e_fkey-27.4 {
EXPLAIN QUERY PLAN DELETE FROM artist
} {
- 0 0 0 {SCAN TABLE artist (~1000000 rows)}
- 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?) (~10 rows)}
+ 0 0 0 {SCAN TABLE artist}
+ 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
}
@@ -1199,7 +1200,7 @@ do_test e_fkey-29.3 {
catchsql {
INSERT INTO song VALUES(2, 'Elvis Presley', 'Elvis Is Back!', 'Fever');
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
@@ -1240,7 +1241,7 @@ do_test e_fkey-31.1 {
do_test e_fkey-31.2 {
# Execute a statement that violates the immediate FK constraint.
catchsql { INSERT INTO prince VALUES(1, 2) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-31.3 {
# This time, use a trigger to fix the constraint violation before the
@@ -1265,7 +1266,7 @@ do_test e_fkey-31.4 {
DROP TRIGGER kt;
}
catchsql { INSERT INTO prince VALUES(3, 4) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-31.5 {
execsql {
COMMIT;
@@ -1296,7 +1297,7 @@ do_test e_fkey-31.5 {
proc test_efkey_34 {tn isError sql} {
do_test e_fkey-32.$tn "
catchsql {$sql}
- " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
+ " [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
}
drop_all_tables
@@ -1327,7 +1328,7 @@ drop_all_tables
proc test_efkey_35 {tn isError sql} {
do_test e_fkey-33.$tn "
catchsql {$sql}
- " [lindex {{0 {}} {1 {foreign key constraint failed}}} $isError]
+ " [lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError]
}
do_test e_fkey-33.1 {
execsql {
@@ -1417,7 +1418,7 @@ do_test e_fkey-34.1 {
proc test_efkey_29 {tn sql isError} {
do_test e_fkey-34.$tn "catchsql {$sql}" [
- lindex {{0 {}} {1 {foreign key constraint failed}}} $isError
+ lindex {{0 {}} {1 {FOREIGN KEY constraint failed}}} $isError
]
}
test_efkey_29 2 "BEGIN" 0
@@ -1491,7 +1492,7 @@ do_test e_fkey-35.2 {
INSERT INTO track VALUES(1, 'White Christmas', 5);
}
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-35.3 {
execsql {
INSERT INTO artist VALUES(5, 'Bing Crosby');
@@ -1528,7 +1529,7 @@ do_test e_fkey-36.2 {
} {}
do_test e_fkey-36.3 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-36.4 {
execsql {
UPDATE t1 SET a = 5 WHERE a = 4;
@@ -1558,7 +1559,7 @@ do_test e_fkey-37.1 {
} {}
do_test e_fkey-37.2 {
catchsql {RELEASE one}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-37.3 {
execsql {
UPDATE t1 SET a = 7 WHERE a = 6;
@@ -1575,7 +1576,7 @@ do_test e_fkey-37.4 {
} {}
do_test e_fkey-37.5 {
catchsql {RELEASE one}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-37.6 {
execsql {ROLLBACK TO one ; RELEASE one}
} {}
@@ -1606,7 +1607,7 @@ do_test e_fkey-38.2 {
} {1 1 2 2 3 3 4 4 5 6}
do_test e_fkey-38.3 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-38.4 {
execsql {
ROLLBACK TO one;
@@ -1627,11 +1628,11 @@ do_test e_fkey-38.5 {
} {}
do_test e_fkey-38.6 {
catchsql {RELEASE a}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-38.7 {
execsql {ROLLBACK TO c}
catchsql {RELEASE a}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-38.8 {
execsql {
ROLLBACK TO b;
@@ -1782,7 +1783,7 @@ do_test e_fkey-41.2 {
} {j k l m}
do_test e_fkey-41.3 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-41.4 {
execsql ROLLBACK
} {}
@@ -1820,10 +1821,10 @@ do_test e_fkey-41.2 {
} {}
do_test e_fkey-41.3 {
catchsql { DELETE FROM parent WHERE p1 = 'a' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-41.4 {
catchsql { UPDATE parent SET p2 = 'e' WHERE p1 = 'c' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# Test that RESTRICT is slightly different from NO ACTION for IMMEDIATE
@@ -1857,7 +1858,7 @@ do_test e_fkey-42.1 {
} {}
do_test e_fkey-42.2 {
catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-42.3 {
execsql {
UPDATE parent SET x = 'key two' WHERE x = 'key2';
@@ -1885,7 +1886,7 @@ do_test e_fkey-42.4 {
} {}
do_test e_fkey-42.5 {
catchsql { DELETE FROM parent WHERE x = 'key1' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-42.6 {
execsql {
DELETE FROM parent WHERE x = 'key2';
@@ -1908,7 +1909,7 @@ do_test e_fkey-42.7 {
} {}
do_test e_fkey-42.8 {
catchsql { REPLACE INTO parent VALUES('key1') }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-42.9 {
execsql {
REPLACE INTO parent VALUES('key2');
@@ -1944,13 +1945,13 @@ do_test e_fkey-43.1 {
} {}
do_test e_fkey-43.2 {
catchsql { UPDATE parent SET x = 'key one' WHERE x = 'key1' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-43.3 {
execsql { UPDATE parent SET x = 'key two' WHERE x = 'key2' }
} {}
do_test e_fkey-43.4 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-43.5 {
execsql {
UPDATE child2 SET c = 'key two';
@@ -1978,13 +1979,13 @@ do_test e_fkey-43.6 {
} {}
do_test e_fkey-43.7 {
catchsql { DELETE FROM parent WHERE x = 'key1' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-43.8 {
execsql { DELETE FROM parent WHERE x = 'key2' }
} {}
do_test e_fkey-43.9 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-43.10 {
execsql {
UPDATE child2 SET c = NULL;
@@ -2240,7 +2241,7 @@ do_test e_fkey-49.3 {
} {ONE two three}
do_test e_fkey-49.4 {
catchsql { UPDATE parent SET a = '' WHERE a = 'oNe' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
@@ -2275,7 +2276,7 @@ do_test e_fkey-50.1 {
} {}
do_test e_fkey-50.2 {
catchsql { DELETE FROM artist WHERE artistname = 'Sammy Davis Jr.' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-50.3 {
execsql {
INSERT INTO artist VALUES(0, 'Unknown Artist');
@@ -2639,7 +2640,7 @@ do_test e_fkey-58.1 {
}
execsql { INSERT INTO c5 VALUES('a', 'b') }
catchsql { DROP TABLE p }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-58.2 {
execsql { SELECT * FROM p }
} {a b}
@@ -2648,7 +2649,7 @@ do_test e_fkey-58.3 {
BEGIN;
DROP TABLE p;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-58.4 {
execsql {
SELECT * FROM p;
@@ -2682,11 +2683,11 @@ do_test e_fkey-59.2 {
} {}
do_test e_fkey-59.3 {
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-59.4 {
execsql { CREATE TABLE p(a, b, PRIMARY KEY(a, b)) }
catchsql COMMIT
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-59.5 {
execsql { INSERT INTO p VALUES('a', 'b') }
execsql COMMIT
@@ -2849,7 +2850,7 @@ foreach zMatch [list SIMPLE PARTIAL FULL Simple parTIAL FuLL ] {
# Check that the FK is enforced properly if there are no NULL values
# in the child key columns.
catchsql { INSERT INTO c VALUES('a', 2, 4) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
}
#-------------------------------------------------------------------------
@@ -2879,13 +2880,13 @@ do_test e_fkey-62.3 {
} {}
do_test e_fkey-62.4 {
catchsql { INSERT INTO ci VALUES('x', 'y') }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-62.5 {
catchsql { INSERT INTO cd VALUES('x', 'y') }
} {0 {}}
do_test e_fkey-62.6 {
catchsql { COMMIT }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test e_fkey-62.7 {
execsql {
DELETE FROM cd;
@@ -2946,45 +2947,52 @@ proc test_on_update_recursion {limit} {
"
}
-do_test e_fkey-63.1.1 {
- test_on_delete_recursion $SQLITE_MAX_TRIGGER_DEPTH
-} {0 0}
-do_test e_fkey-63.1.2 {
- test_on_delete_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1]
-} {1 {too many levels of trigger recursion}}
-do_test e_fkey-63.1.3 {
- sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5
- test_on_delete_recursion 5
-} {0 0}
-do_test e_fkey-63.1.4 {
- test_on_delete_recursion 6
-} {1 {too many levels of trigger recursion}}
-do_test e_fkey-63.1.5 {
- sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000
-} {5}
-do_test e_fkey-63.2.1 {
- test_on_update_recursion $SQLITE_MAX_TRIGGER_DEPTH
-} {0 0}
-do_test e_fkey-63.2.2 {
- test_on_update_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1]
-} {1 {too many levels of trigger recursion}}
-do_test e_fkey-63.2.3 {
- sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5
- test_on_update_recursion 5
-} {0 0}
-do_test e_fkey-63.2.4 {
- test_on_update_recursion 6
-} {1 {too many levels of trigger recursion}}
-do_test e_fkey-63.2.5 {
- sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000
-} {5}
+# If the current build was created using clang with the -fsanitize=address
+# switch, then the library uses considerably more stack space than usual.
+# So much more, that some of the following tests cause stack overflows
+# if they are run under this configuration.
+#
+if {[clang_sanitize_address]==0} {
+ do_test e_fkey-63.1.1 {
+ test_on_delete_recursion $SQLITE_MAX_TRIGGER_DEPTH
+ } {0 0}
+ do_test e_fkey-63.1.2 {
+ test_on_delete_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1]
+ } {1 {too many levels of trigger recursion}}
+ do_test e_fkey-63.1.3 {
+ sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5
+ test_on_delete_recursion 5
+ } {0 0}
+ do_test e_fkey-63.1.4 {
+ test_on_delete_recursion 6
+ } {1 {too many levels of trigger recursion}}
+ do_test e_fkey-63.1.5 {
+ sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000
+ } {5}
+ do_test e_fkey-63.2.1 {
+ test_on_update_recursion $SQLITE_MAX_TRIGGER_DEPTH
+ } {0 0}
+ do_test e_fkey-63.2.2 {
+ test_on_update_recursion [expr $SQLITE_MAX_TRIGGER_DEPTH+1]
+ } {1 {too many levels of trigger recursion}}
+ do_test e_fkey-63.2.3 {
+ sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 5
+ test_on_update_recursion 5
+ } {0 0}
+ do_test e_fkey-63.2.4 {
+ test_on_update_recursion 6
+ } {1 {too many levels of trigger recursion}}
+ do_test e_fkey-63.2.5 {
+ sqlite3_limit db SQLITE_LIMIT_TRIGGER_DEPTH 1000000
+ } {5}
+}
#-------------------------------------------------------------------------
# The setting of the recursive_triggers pragma does not affect foreign
# key actions.
#
-# EVIDENCE-OF: R-51769-32730 The PRAGMA recursive_triggers setting does
-# not not affect the operation of foreign key actions.
+# EVIDENCE-OF: R-44355-00270 The PRAGMA recursive_triggers setting does
+# not affect the operation of foreign key actions.
#
foreach recursive_triggers_setting [list 0 1 ON OFF] {
drop_all_tables
diff --git a/test/e_insert.test b/test/e_insert.test
index 951ae24..0ea4b76 100644
--- a/test/e_insert.test
+++ b/test/e_insert.test
@@ -50,7 +50,7 @@ proc do_insert_tests {args} {
uplevel do_select_tests $args
}
-# EVIDENCE-OF: R-21350-31508 -- syntax diagram insert-stmt
+# -- syntax diagram insert-stmt
#
do_insert_tests e_insert-0 {
1 "INSERT INTO a1 DEFAULT VALUES" {}
@@ -174,9 +174,9 @@ do_insert_tests e_insert-1.2 -error {
4 "INSERT INTO a2 VALUES(1,2,3,4,5)" {a2 3 5}
}
-# EVIDENCE-OF: R-34231-22576 In this case the result of evaluating the
-# left-most expression in each term of the VALUES list is inserted into
-# the left-most column of the each new row, and forth for each
+# EVIDENCE-OF: R-29730-42609 In this case the result of evaluating the
+# left-most expression from each term of the VALUES list is inserted
+# into the left-most column of each new row, and so forth for each
# subsequent expression.
#
delete_all_data
@@ -191,8 +191,8 @@ do_insert_tests e_insert-1.3 {
3b "SELECT * FROM a2 WHERE oid=last_insert_rowid()" {2 x y}
}
-# EVIDENCE-OF: R-44710-64652 If a column-list is specified, then the
-# number of values in each term of the VALUS list must match the number
+# EVIDENCE-OF: R-09234-17933 If a column-list is specified, then the
+# number of values in each term of the VALUE list must match the number
# of specified columns.
#
do_insert_tests e_insert-1.4 -error {
@@ -371,22 +371,22 @@ do_execsql_test e_insert-4.1.0 {
INSERT INTO a4 VALUES(3, 'a');
} {}
foreach {tn sql error ac data } {
- 1.1 "INSERT INTO a4 VALUES(2,'b')" {column c is not unique} 1 {1 a 2 a 3 a}
+ 1.1 "INSERT INTO a4 VALUES(2,'b')" {UNIQUE constraint failed: a4.c} 1 {1 a 2 a 3 a}
1.2 "INSERT OR REPLACE INTO a4 VALUES(2, 'b')" {} 1 {1 a 3 a 2 b}
1.3 "INSERT OR IGNORE INTO a4 VALUES(3, 'c')" {} 1 {1 a 3 a 2 b}
1.4 "BEGIN" {} 0 {1 a 3 a 2 b}
- 1.5 "INSERT INTO a4 VALUES(1, 'd')" {column c is not unique} 0 {1 a 3 a 2 b}
+ 1.5 "INSERT INTO a4 VALUES(1, 'd')" {UNIQUE constraint failed: a4.c} 0 {1 a 3 a 2 b}
1.6 "INSERT OR ABORT INTO a4 VALUES(1, 'd')"
- {column c is not unique} 0 {1 a 3 a 2 b}
+ {UNIQUE constraint failed: a4.c} 0 {1 a 3 a 2 b}
1.7 "INSERT OR ROLLBACK INTO a4 VALUES(1, 'd')"
- {column c is not unique} 1 {1 a 3 a 2 b}
+ {UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b}
1.8 "INSERT INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
- {column c is not unique} 1 {1 a 3 a 2 b}
+ {UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b}
1.9 "INSERT OR FAIL INTO a4 SELECT 4, 'e' UNION ALL SELECT 3, 'e'"
- {column c is not unique} 1 {1 a 3 a 2 b 4 e}
+ {UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b 4 e}
2.1 "INSERT INTO a4 VALUES(2,'f')"
- {column c is not unique} 1 {1 a 3 a 2 b 4 e}
+ {UNIQUE constraint failed: a4.c} 1 {1 a 3 a 2 b 4 e}
2.2 "REPLACE INTO a4 VALUES(2, 'f')" {} 1 {1 a 3 a 4 e 2 f}
} {
do_catchsql_test e_insert-4.1.$tn.1 $sql [list [expr {$error!=""}] $error]
diff --git a/test/e_reindex.test b/test/e_reindex.test
index b39f37e..4b86787 100644
--- a/test/e_reindex.test
+++ b/test/e_reindex.test
@@ -26,7 +26,7 @@ do_execsql_test e_reindex-0.0 {
CREATE INDEX i2 ON t1(b, a);
} {}
-# EVIDENCE-OF: R-51477-38549 -- syntax diagram reindex-stmt
+# -- syntax diagram reindex-stmt
#
do_reindex_tests e_reindex-0.1 {
1 "REINDEX" {}
@@ -67,10 +67,10 @@ sqlite3 db test.db
do_execsql_test e_reindex-1.3 {
PRAGMA integrity_check;
} [list \
- {rowid 4 missing from index i2} \
- {rowid 4 missing from index i1} \
- {rowid 5 missing from index i2} \
- {rowid 5 missing from index i1} \
+ {row 3 missing from index i2} \
+ {row 3 missing from index i1} \
+ {row 4 missing from index i2} \
+ {row 4 missing from index i1} \
{wrong # of entries in index i2} \
{wrong # of entries in index i1}
]
@@ -98,7 +98,7 @@ proc sort_by_length {lhs rhs} {
array set V {one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight 8}
proc sort_by_value {lhs rhs} {
global V
- set res [expr {$V($lhs) - $V(rhs)}]
+ set res [expr {$V($lhs) - $V($rhs)}]
if {$res!=0} {return $res}
return [string compare $lhs $rhs]
}
diff --git a/test/e_select.test b/test/e_select.test
index ea44aed..89d61b5 100644
--- a/test/e_select.test
+++ b/test/e_select.test
@@ -83,7 +83,7 @@ proc do_join_test {tn select res} {
# The following tests check that all paths on the syntax diagrams on
# the lang_select.html page may be taken.
#
-# EVIDENCE-OF: R-11353-33501 -- syntax diagram join-constraint
+# -- syntax diagram join-constraint
#
do_join_test e_select-0.1.1 {
SELECT count(*) FROM t1 %JOIN% t2 ON (t1.a=t2.a)
@@ -101,7 +101,7 @@ do_catchsql_test e_select-0.1.5 {
SELECT count(*) FROM t1, t2 USING (a) ON (t1.a=t2.a)
} {1 {near "ON": syntax error}}
-# EVIDENCE-OF: R-40919-40941 -- syntax diagram select-core
+# -- syntax diagram select-core
#
# 0: SELECT ...
# 1: SELECT DISTINCT ...
@@ -226,7 +226,7 @@ do_select_tests e_select-0.2 {
}
-# EVIDENCE-OF: R-41378-26734 -- syntax diagram result-column
+# -- syntax diagram result-column
#
do_select_tests e_select-0.3 {
1 "SELECT * FROM t1" {a one b two c three}
@@ -236,9 +236,9 @@ do_select_tests e_select-0.3 {
5 "SELECT 'x'||a||'x' AS alias FROM t1" {xax xbx xcx}
}
-# EVIDENCE-OF: R-43129-35648 -- syntax diagram join-source
+# -- syntax diagram join-source
#
-# EVIDENCE-OF: R-36683-37460 -- syntax diagram join-op
+# -- syntax diagram join-op
#
do_select_tests e_select-0.4 {
1 "SELECT t1.rowid FROM t1" {1 2 3}
@@ -263,7 +263,7 @@ do_select_tests e_select-0.4 {
16 "SELECT t1.rowid FROM t1 CROSS JOIN t3" {1 1 2 2 3 3}
}
-# EVIDENCE-OF: R-28308-37813 -- syntax diagram compound-operator
+# -- syntax diagram compound-operator
#
do_select_tests e_select-0.5 {
1 "SELECT rowid FROM t1 UNION ALL SELECT rowid+2 FROM t4" {1 2 3 3 4}
@@ -272,7 +272,7 @@ do_select_tests e_select-0.5 {
4 "SELECT rowid FROM t1 EXCEPT SELECT rowid+2 FROM t4" {1 2}
}
-# EVIDENCE-OF: R-06480-34950 -- syntax diagram ordering-term
+# -- syntax diagram ordering-term
#
do_select_tests e_select-0.6 {
1 "SELECT b||a FROM t1 ORDER BY b||a" {onea threec twob}
@@ -281,7 +281,7 @@ do_select_tests e_select-0.6 {
4 "SELECT b||a FROM t1 ORDER BY (b||a) DESC" {twob threec onea}
}
-# EVIDENCE-OF: R-23926-36668 -- syntax diagram select-stmt
+# -- syntax diagram select-stmt
#
do_select_tests e_select-0.7 {
1 "SELECT * FROM t1" {a one b two c three}
@@ -333,9 +333,9 @@ do_select_tests e_select-1.1 {
6 "SELECT count(*) WHERE 1" {1}
}
-# EVIDENCE-OF: R-48114-33255 If there is only a single table in the
-# join-source following the FROM clause, then the input data used by the
-# SELECT statement is the contents of the named table.
+# EVIDENCE-OF: R-45424-07352 If there is only a single table or subquery
+# in the FROM clause, then the input data used by the SELECT statement
+# is the contents of the named table.
#
# The results of the SELECT queries suggest that they are operating on the
# contents of the table 'xx'.
@@ -357,10 +357,10 @@ do_select_tests e_select-1.2 {
3 "SELECT sum(x), sum(y) FROM xx" {-17.89 -16.87}
}
-# EVIDENCE-OF: R-23593-12456 If there is more than one table specified
-# as part of the join-source following the FROM keyword, then the
-# contents of each named table are joined into a single dataset for the
-# simple SELECT statement to operate on.
+# EVIDENCE-OF: R-28355-09804 If there is more than one table or subquery
+# in FROM clause then the contents of all tables and/or subqueries are
+# joined into a single dataset for the simple SELECT statement to
+# operate on.
#
# There are more detailed tests for subsequent requirements that add
# more detail to this idea. We just add a single test that shows that
@@ -383,10 +383,10 @@ do_select_tests e_select-1.3 {
# of cartesian joins in the SELECT documentation is consistent with SQLite.
# In doing so, we test the following three requirements as a side-effect:
#
-# EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER
-# JOIN", "JOIN" or a comma (",") and there is no ON or USING clause,
-# then the result of the join is simply the cartesian product of the
-# left and right-hand datasets.
+# EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN",
+# "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING
+# clause, then the result of the join is simply the cartesian product of
+# the left and right-hand datasets.
#
# The tests are built on this assertion. Really, they test that the output
# of a CROSS JOIN, JOIN, INNER JOIN or "," join matches the expected result
@@ -395,8 +395,8 @@ do_select_tests e_select-1.3 {
# EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER
# JOIN", "JOIN" and "," join operators.
#
-# EVIDENCE-OF: R-07544-24155 The "CROSS JOIN" join operator produces the
-# same data as the "INNER JOIN", "JOIN" and "," operators
+# EVIDENCE-OF: R-25071-21202 The "CROSS JOIN" join operator produces the
+# same result as the "INNER JOIN", "JOIN" and "," operators
#
# All tests are run 4 times, with the only difference in each run being
# which of the 4 equivalent cartesian product join operators are used.
@@ -450,24 +450,24 @@ do_join_test e_select-1.4.1.4 {
# left-hand and right-hand datasets.
#
do_join_test e_select-1.4.2.1 {
- SELECT * FROM x2 %JOIN% x3
+ SELECT * FROM x2 %JOIN% x3 ORDER BY +c, +f
} [list -60.06 {} {} -39.24 {} encompass -1 \
- -60.06 {} {} presenting 51 reformation dignified \
- -60.06 {} {} conducting -87.24 37.56 {} \
- -60.06 {} {} coldest -96 dramatists 82.3 \
-60.06 {} {} alerting {} -93.79 {} \
+ -60.06 {} {} coldest -96 dramatists 82.3 \
+ -60.06 {} {} conducting -87.24 37.56 {} \
+ -60.06 {} {} presenting 51 reformation dignified \
-58 {} 1.21 -39.24 {} encompass -1 \
- -58 {} 1.21 presenting 51 reformation dignified \
- -58 {} 1.21 conducting -87.24 37.56 {} \
- -58 {} 1.21 coldest -96 dramatists 82.3 \
-58 {} 1.21 alerting {} -93.79 {} \
+ -58 {} 1.21 coldest -96 dramatists 82.3 \
+ -58 {} 1.21 conducting -87.24 37.56 {} \
+ -58 {} 1.21 presenting 51 reformation dignified \
]
# TODO: Come back and add a few more like the above.
-# EVIDENCE-OF: R-20659-43267 In other words, if the left-hand dataset
-# consists of Nlhs rows of Mlhs columns, and the right-hand dataset of
-# Nrhs rows of Mrhs columns, then the cartesian product is a dataset of
-# Nlhs.Nrhs rows, each containing Mlhs+Mrhs columns.
+# EVIDENCE-OF: R-18439-38548 In other words, if the left-hand dataset
+# consists of Nleft rows of Mleft columns, and the right-hand dataset of
+# Nright rows of Mright columns, then the cartesian product is a dataset
+# of Nleft&times;Nright rows, each containing Mleft+Mright columns.
#
# x1, x2 (Nlhs=3, Nrhs=2) (Mlhs=2, Mrhs=3)
do_join_test e_select-1.4.3.1 {
@@ -513,11 +513,10 @@ do_select_tests e_select-1.4.5 [list \
4 { SELECT * FROM t1 AS y INNER JOIN t1 AS x } $t1_cross_t1 \
]
-
-# EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then
-# the ON expression is evaluated for each row of the cartesian product
-# as a boolean expression. All rows for which the expression evaluates
-# to false are excluded from the dataset.
+# EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON
+# expression is evaluated for each row of the cartesian product as a
+# boolean expression. Only rows for which the expression evaluates to
+# true are included from the dataset.
#
foreach {tn select res} [list \
1 { SELECT * FROM t1 %JOIN% t2 ON (1) } $t1_cross_t2 \
@@ -540,9 +539,9 @@ foreach {tn select res} [list \
do_join_test e_select-1.3.$tn $select $res
}
-# EVIDENCE-OF: R-63358-54862 If there is a USING clause specified as
-# part of the join-constraint, then each of the column names specified
-# must exist in the datasets to both the left and right of the join-op.
+# EVIDENCE-OF: R-49933-05137 If there is a USING clause then each of the
+# column names specified must exist in the datasets to both the left and
+# right of the join-operator.
#
do_select_tests e_select-1.4 -error {
cannot join using column %s - column not present in both tables
@@ -552,10 +551,10 @@ do_select_tests e_select-1.4 -error {
3 { SELECT * FROM t3, (SELECT a AS b, b AS c FROM t1) USING (a) } "a"
}
-# EVIDENCE-OF: R-55987-04584 For each pair of namesake columns, the
+# EVIDENCE-OF: R-22776-52830 For each pair of named columns, the
# expression "lhs.X = rhs.X" is evaluated for each row of the cartesian
-# product as a boolean expression. All rows for which one or more of the
-# expressions evaluates to false are excluded from the result set.
+# product as a boolean expression. Only rows for which all such
+# expressions evaluates to true are included from the result set.
#
do_select_tests e_select-1.5 {
1 { SELECT * FROM t1, t3 USING (a) } {a one 1 b two 2}
@@ -566,8 +565,8 @@ do_select_tests e_select-1.5 {
# USING clause, the normal rules for handling affinities, collation
# sequences and NULL values in comparisons apply.
#
-# EVIDENCE-OF: R-35466-18578 The column from the dataset on the
-# left-hand side of the join operator is considered to be on the
+# EVIDENCE-OF: R-38422-04402 The column from the dataset on the
+# left-hand side of the join-operator is considered to be on the
# left-hand side of the comparison operator (=) for the purposes of
# collation sequence and affinity precedence.
#
@@ -622,10 +621,9 @@ foreach {tn select res} {
} {
do_join_test e_select-1.7.$tn $select $res
}
-
-# EVIDENCE-OF: R-41434-12448 If the join-op is a "LEFT JOIN" or "LEFT
-# OUTER JOIN", then after the ON or USING filtering clauses have been
-# applied, an extra row is added to the output for each row in the
+# EVIDENCE-OF: R-42531-52874 If the join-operator is a "LEFT JOIN" or
+# "LEFT OUTER JOIN", then after the ON or USING filtering clauses have
+# been applied, an extra row is added to the output for each row in the
# original left-hand input dataset that corresponds to no rows at all in
# the composite dataset (if any).
#
@@ -660,8 +658,8 @@ do_select_tests e_select-1.9 {
2b "SELECT * FROM t7 LEFT JOIN t8 USING (a)" {x ex 24 abc 24 y why 25 {} {}}
}
-# EVIDENCE-OF: R-01809-52134 If the NATURAL keyword is added to any of
-# the join-ops, then an implicit USING clause is added to the
+# EVIDENCE-OF: R-04932-55942 If the NATURAL keyword is in the
+# join-operator then an implicit USING clause is added to the
# join-constraints. The implicit USING clause contains each of the
# column names that appear in both the left and right-hand input
# datasets.
@@ -734,10 +732,10 @@ do_execsql_test e_select-3.0 {
INSERT INTO x2 VALUES(7, 'mistrusted', 'standardized');
} {}
-# EVIDENCE-OF: R-06999-14330 If a WHERE clause is specified, the WHERE
+# EVIDENCE-OF: R-60775-64916 If a WHERE clause is specified, the WHERE
# expression is evaluated for each row in the input data as a boolean
-# expression. All rows for which the WHERE clause expression evaluates
-# to false are excluded from the dataset before continuing.
+# expression. Only rows for which the WHERE clause expression evaluates
+# to true are included from the dataset before continuing.
#
do_execsql_test e_select-3.1.1 { SELECT k FROM x1 WHERE x } {3}
do_execsql_test e_select-3.1.2 { SELECT k FROM x1 WHERE y } {3 5 6}
@@ -815,8 +813,8 @@ do_select_tests e_select-4.1 {
}
}
-# EVIDENCE-OF: R-61869-22578 It is an error to use a "*" or "alias.*"
-# expression in any context other than than a result expression list.
+# EVIDENCE-OF: R-38023-18396 It is an error to use a "*" or "alias.*"
+# expression in any context other than a result expression list.
#
# EVIDENCE-OF: R-44324-41166 It is also an error to use a "*" or
# "alias.*" expression in a simple SELECT query that does not have a
@@ -1009,12 +1007,12 @@ do_execsql_test e_select-4.9.0 {
INSERT INTO b3 VALUES('dEF', 'dEF');
} {}
-# EVIDENCE-OF: R-57754-57109 If the SELECT statement is an aggregate
+# EVIDENCE-OF: R-07284-35990 If the SELECT statement is an aggregate
# query with a GROUP BY clause, then each of the expressions specified
# as part of the GROUP BY clause is evaluated for each row of the
# dataset. Each row is then assigned to a "group" based on the results;
# rows for which the results of evaluating the GROUP BY expressions are
-# the same are assigned to the same group.
+# the same get assigned to the same group.
#
# These tests also show that the following is not untrue:
#
@@ -1226,7 +1224,7 @@ do_select_tests e_select-5.1 {
# EVIDENCE-OF: R-08861-34280 If the simple SELECT is a SELECT ALL, then
# the entire set of result rows are returned by the SELECT.
#
-# EVIDENCE-OF: R-47911-02086 If neither ALL or DISTINCT are present,
+# EVIDENCE-OF: R-01256-01950 If neither ALL or DISTINCT are present,
# then the behavior is as if ALL were specified.
#
# EVIDENCE-OF: R-14442-41305 If the simple SELECT is a SELECT DISTINCT,
@@ -1373,8 +1371,9 @@ foreach {tn select op1 op2} {
do_catchsql_test e_select-7.2.$tn $select [list 1 $err]
}
-# EVIDENCE-OF: R-22874-32655 ORDER BY and LIMIT clauses may only occur
-# at the end of the entire compound SELECT.
+# EVIDENCE-OF: R-45440-25633 ORDER BY and LIMIT clauses may only occur
+# at the end of the entire compound SELECT, and then only if the final
+# element of the compound is not a VALUES clause.
#
foreach {tn select} {
1 "SELECT * FROM j1 UNION ALL SELECT * FROM j2,j3 ORDER BY a"
@@ -1386,6 +1385,7 @@ foreach {tn select} {
7 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 ORDER BY a"
8 "SELECT count(*) FROM j1 UNION SELECT max(e) FROM j2 ORDER BY 1"
+ 8b "VALUES('8b') UNION SELECT max(e) FROM j2 ORDER BY 1"
9 "SELECT count(*), * FROM j1 UNION SELECT *,* FROM j2 ORDER BY 1,2,3"
10 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10"
11 "SELECT * FROM j1 UNION SELECT * FROM j2,j3 LIMIT 10 OFFSET 5"
@@ -1407,6 +1407,14 @@ foreach {tn select} {
} {
do_test e_select-7.3.$tn { catch {execsql $select} msg } 0
}
+foreach {tn select} {
+ 50 "SELECT * FROM j1 ORDER BY 1 UNION ALL SELECT * FROM j2,j3"
+ 51 "SELECT * FROM j1 LIMIT 1 UNION ALL SELECT * FROM j2,j3"
+ 52 "SELECT count(*) FROM j1 UNION ALL VALUES(11) ORDER BY 1"
+ 53 "SELECT count(*) FROM j1 UNION ALL VALUES(11) LIMIT 1"
+} {
+ do_test e_select-7.3.$tn { catch {execsql $select} msg } 1
+}
# EVIDENCE-OF: R-08531-36543 A compound SELECT created using UNION ALL
# operator returns all the rows from the SELECT to the left of the UNION
diff --git a/test/e_select2.test b/test/e_select2.test
index b338d4f..8330894 100644
--- a/test/e_select2.test
+++ b/test/e_select2.test
@@ -344,16 +344,16 @@ foreach {tn indexes} {
catchsql { DROP INDEX i3 }
execsql $indexes
- # EVIDENCE-OF: R-46122-14930 If the join-op is "CROSS JOIN", "INNER
- # JOIN", "JOIN" or a comma (",") and there is no ON or USING clause,
- # then the result of the join is simply the cartesian product of the
- # left and right-hand datasets.
+ # EVIDENCE-OF: R-49872-03192 If the join-operator is "CROSS JOIN",
+ # "INNER JOIN", "JOIN" or a comma (",") and there is no ON or USING
+ # clause, then the result of the join is simply the cartesian product of
+ # the left and right-hand datasets.
#
# EVIDENCE-OF: R-46256-57243 There is no difference between the "INNER
# JOIN", "JOIN" and "," join operators.
#
- # EVIDENCE-OF: R-07544-24155 The "CROSS JOIN" join operator produces the
- # same data as the "INNER JOIN", "JOIN" and "," operators
+ # EVIDENCE-OF: R-25071-21202 The "CROSS JOIN" join operator produces the
+ # same result as the "INNER JOIN", "JOIN" and "," operators
#
test_join $tn.1.1 "t1, t2" {t1 t2}
test_join $tn.1.2 "t1 INNER JOIN t2" {t1 t2}
@@ -368,10 +368,10 @@ foreach {tn indexes} {
test_join $tn.1.11 "t2 CROSS JOIN t2 AS x" {t2 t2}
test_join $tn.1.12 "t2 JOIN t2 AS x" {t2 t2}
- # EVIDENCE-OF: R-22775-56496 If there is an ON clause specified, then
- # the ON expression is evaluated for each row of the cartesian product
- # as a boolean expression. All rows for which the expression evaluates
- # to false are excluded from the dataset.
+ # EVIDENCE-OF: R-38465-03616 If there is an ON clause then the ON
+ # expression is evaluated for each row of the cartesian product as a
+ # boolean expression. Only rows for which the expression evaluates to
+ # true are included from the dataset.
#
test_join $tn.2.1 "t1, t2 ON (t1.a=t2.a)" {t1 t2 -on {te_equals a a}}
test_join $tn.2.2 "t2, t1 ON (t1.a=t2.a)" {t2 t1 -on {te_equals a a}}
@@ -504,14 +504,14 @@ do_execsql_test e_select-2.2.0 {
INSERT INTO t5 VALUES(2, 'two');
} {}
-# EVIDENCE-OF: R-55824-40976 A sub-select specified in the join-source
-# following the FROM clause in a simple SELECT statement is handled as
-# if it was a table containing the data returned by executing the
-# sub-select statement.
+# EVIDENCE-OF: R-59237-46742 A subquery specified in the
+# table-or-subquery following the FROM clause in a simple SELECT
+# statement is handled as if it was a table containing the data returned
+# by executing the subquery statement.
#
-# EVIDENCE-OF: R-42612-06757 Each column of the sub-select dataset
-# inherits the collation sequence and affinity of the corresponding
-# expression in the sub-select statement.
+# EVIDENCE-OF: R-27438-53558 Each column of the subquery has the
+# collation sequence and affinity of the corresponding expression in the
+# subquery statement.
#
foreach {tn subselect select spec} {
1 "SELECT * FROM t2" "SELECT * FROM t1 JOIN %ss%"
diff --git a/test/e_update.test b/test/e_update.test
index 230c97f..e9c6b26 100644
--- a/test/e_update.test
+++ b/test/e_update.test
@@ -49,7 +49,7 @@ proc do_update_tests {args} {
uplevel do_select_tests $args
}
-# EVIDENCE-OF: R-62337-45828 -- syntax diagram update-stmt
+# -- syntax diagram update-stmt
#
do_update_tests e_update-0 {
1 "UPDATE t1 SET a=10" {}
@@ -278,30 +278,30 @@ do_execsql_test e_update-1.8.0 {
} {}
foreach {tn sql error ac data } {
1 "UPDATE t3 SET b='one' WHERE a=3"
- {column b is not unique} 1 {1 one 2 two 3 three 4 four}
+ {UNIQUE constraint failed: t3.b} 1 {1 one 2 two 3 three 4 four}
2 "UPDATE OR REPLACE t3 SET b='one' WHERE a=3"
{} 1 {2 two 3 one 4 four}
3 "UPDATE OR FAIL t3 SET b='three'"
- {column b is not unique} 1 {2 three 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
4 "UPDATE OR IGNORE t3 SET b='three' WHERE a=3"
{} 1 {2 three 3 one 4 four}
5 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
- {column b is not unique} 1 {2 three 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
6 "BEGIN" {} 0 {2 three 3 one 4 four}
7 "UPDATE t3 SET b='three' WHERE a=3"
- {column b is not unique} 0 {2 three 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 0 {2 three 3 one 4 four}
8 "UPDATE OR ABORT t3 SET b='three' WHERE a=3"
- {column b is not unique} 0 {2 three 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 0 {2 three 3 one 4 four}
9 "UPDATE OR FAIL t3 SET b='two'"
- {column b is not unique} 0 {2 two 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 0 {2 two 3 one 4 four}
10 "UPDATE OR IGNORE t3 SET b='four' WHERE a=3"
{} 0 {2 two 3 one 4 four}
@@ -310,7 +310,7 @@ foreach {tn sql error ac data } {
{} 0 {2 two 3 four}
12 "UPDATE OR ROLLBACK t3 SET b='four'"
- {column b is not unique} 1 {2 three 3 one 4 four}
+ {UNIQUE constraint failed: t3.b} 1 {2 three 3 one 4 four}
} {
do_catchsql_test e_update-1.8.$tn.1 $sql [list [expr {$error!=""}] $error]
do_execsql_test e_update-1.8.$tn.2 {SELECT * FROM t3} [list {*}$data]
@@ -493,7 +493,7 @@ do_update_tests e_update-2.5 -error {
# of the UPDATE statement is extended with optional ORDER BY and LIMIT
# clauses
#
-# EVIDENCE-OF: R-45169-39597 -- syntax diagram update-stmt-limited
+# -- syntax diagram update-stmt-limited
#
do_update_tests e_update-3.0 {
1 "UPDATE t1 SET a=b LIMIT 5" {}
diff --git a/test/e_uri.test b/test/e_uri.test
index ac34ed4..a8865ca 100644
--- a/test/e_uri.test
+++ b/test/e_uri.test
@@ -359,7 +359,7 @@ foreach {tn uri error} "
# EVIDENCE-OF: R-49793-28525 Setting the cache parameter to "private" is
# equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
#
-# EVIDENCE-OF: R-19510-48080 If sqlite3_open_v2() is used and the
+# EVIDENCE-OF: R-31773-41793 If sqlite3_open_v2() is used and the
# "cache" parameter is present in a URI filename, its value overrides
# any behavior requested by setting SQLITE_OPEN_PRIVATECACHE or
# SQLITE_OPEN_SHAREDCACHE flag.
diff --git a/test/e_vacuum.test b/test/e_vacuum.test
index bad12d3..99b31aa 100644
--- a/test/e_vacuum.test
+++ b/test/e_vacuum.test
@@ -65,7 +65,7 @@ proc fragment_count {name} {
}
-# EVIDENCE-OF: R-45173-45977 -- syntax diagram vacuum-stmt
+# -- syntax diagram vacuum-stmt
#
do_execsql_test e_vacuum-0.1 { VACUUM } {}
diff --git a/test/eqp.test b/test/eqp.test
index 454f2af..046088c 100644
--- a/test/eqp.test
+++ b/test/eqp.test
@@ -33,47 +33,47 @@ set testprefix eqp
proc det {args} { uplevel do_eqp_test $args }
do_execsql_test 1.1 {
- CREATE TABLE t1(a, b);
+ CREATE TABLE t1(a INT, b INT, ex TEXT);
CREATE INDEX i1 ON t1(a);
CREATE INDEX i2 ON t1(b);
- CREATE TABLE t2(a, b);
- CREATE TABLE t3(a, b);
+ CREATE TABLE t2(a INT, b INT, ex TEXT);
+ CREATE TABLE t3(a INT, b INT, ex TEXT);
}
do_eqp_test 1.2 {
SELECT * FROM t2, t1 WHERE t1.a=1 OR t1.b=2;
} {
- 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
- 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}
- 0 1 0 {SCAN TABLE t2 (~1000000 rows)}
+ 0 0 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
+ 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
+ 0 1 0 {SCAN TABLE t2}
}
do_eqp_test 1.3 {
SELECT * FROM t2 CROSS JOIN t1 WHERE t1.a=1 OR t1.b=2;
} {
- 0 0 0 {SCAN TABLE t2 (~1000000 rows)}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}
+ 0 0 0 {SCAN TABLE t2}
+ 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
+ 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
}
do_eqp_test 1.3 {
SELECT a FROM t1 ORDER BY a
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
}
do_eqp_test 1.4 {
SELECT a FROM t1 ORDER BY +a
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
do_eqp_test 1.5 {
SELECT a FROM t1 WHERE a=4
} {
- 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}
}
do_eqp_test 1.6 {
SELECT DISTINCT count(*) FROM t3 GROUP BY a;
} {
- 0 0 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t3}
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
}
@@ -81,40 +81,40 @@ do_eqp_test 1.6 {
do_eqp_test 1.7 {
SELECT * FROM t3 JOIN (SELECT 1)
} {
- 0 0 1 {SCAN SUBQUERY 1 (~1 rows)}
- 0 1 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 1 {SCAN SUBQUERY 1}
+ 0 1 0 {SCAN TABLE t3}
}
do_eqp_test 1.8 {
SELECT * FROM t3 JOIN (SELECT 1 UNION SELECT 2)
} {
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
- 0 0 1 {SCAN SUBQUERY 1 (~2 rows)}
- 0 1 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 1 {SCAN SUBQUERY 1}
+ 0 1 0 {SCAN TABLE t3}
}
do_eqp_test 1.9 {
SELECT * FROM t3 JOIN (SELECT 1 EXCEPT SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3 (~1000000 rows)}
+ 3 0 0 {SCAN TABLE t3}
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)}
- 0 0 1 {SCAN SUBQUERY 1 (~17 rows)}
- 0 1 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 1 {SCAN SUBQUERY 1}
+ 0 1 0 {SCAN TABLE t3}
}
do_eqp_test 1.10 {
SELECT * FROM t3 JOIN (SELECT 1 INTERSECT SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3 (~1000000 rows)}
+ 3 0 0 {SCAN TABLE t3}
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)}
- 0 0 1 {SCAN SUBQUERY 1 (~1 rows)}
- 0 1 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 1 {SCAN SUBQUERY 1}
+ 0 1 0 {SCAN TABLE t3}
}
do_eqp_test 1.11 {
SELECT * FROM t3 JOIN (SELECT 1 UNION ALL SELECT a FROM t3 LIMIT 17)
} {
- 3 0 0 {SCAN TABLE t3 (~1000000 rows)}
+ 3 0 0 {SCAN TABLE t3}
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)}
- 0 0 1 {SCAN SUBQUERY 1 (~17 rows)}
- 0 1 0 {SCAN TABLE t3 (~1000000 rows)}
+ 0 0 1 {SCAN SUBQUERY 1}
+ 0 1 0 {SCAN TABLE t3}
}
#-------------------------------------------------------------------------
@@ -122,55 +122,55 @@ do_eqp_test 1.11 {
#
drop_all_tables
do_execsql_test 2.1 {
- CREATE TABLE t1(x, y);
+ CREATE TABLE t1(x INT, y INT, ex TEXT);
- CREATE TABLE t2(x, y);
+ CREATE TABLE t2(x INT, y INT, ex TEXT);
CREATE INDEX t2i1 ON t2(x);
}
det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
det 2.2.2 "SELECT DISTINCT min(x), max(x) FROM t2 GROUP BY x ORDER BY 1" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
det 2.2.3 "SELECT DISTINCT * FROM t1" {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
}
det 2.2.4 "SELECT DISTINCT * FROM t1, t2" {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 0 1 1 {SCAN TABLE t2 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE t2}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
}
det 2.2.5 "SELECT DISTINCT * FROM t1, t2 ORDER BY t1.x" {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 0 1 1 {SCAN TABLE t2 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SCAN TABLE t2}
0 0 0 {USE TEMP B-TREE FOR DISTINCT}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
det 2.2.6 "SELECT DISTINCT t2.x FROM t1, t2 ORDER BY t2.x" {
- 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
- 0 1 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 1 {SCAN TABLE t2 USING COVERING INDEX t2i1}
+ 0 1 0 {SCAN TABLE t1}
}
det 2.3.1 "SELECT max(x) FROM t2" {
- 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)}
+ 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1}
}
det 2.3.2 "SELECT min(x) FROM t2" {
- 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1 (~1 rows)}
+ 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1}
}
det 2.3.3 "SELECT min(x), max(x) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
}
det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" {
- 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)}
}
@@ -181,39 +181,39 @@ det 2.4.1 "SELECT * FROM t1 WHERE rowid=?" {
do_eqp_test 3.1.1 {
SELECT (SELECT x FROM t1 AS sub) FROM t1;
} {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1 AS sub}
}
do_eqp_test 3.1.2 {
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub);
} {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1 AS sub}
}
do_eqp_test 3.1.3 {
SELECT * FROM t1 WHERE (SELECT x FROM t1 AS sub ORDER BY y);
} {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1 AS sub}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
do_eqp_test 3.1.4 {
SELECT * FROM t1 WHERE (SELECT x FROM t2 ORDER BY x);
} {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
}
det 3.2.1 {
SELECT * FROM (SELECT * FROM t1 ORDER BY x LIMIT 10) ORDER BY y LIMIT 5
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {SCAN SUBQUERY 1 (~10 rows)}
+ 0 0 0 {SCAN SUBQUERY 1}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
det 3.2.2 {
@@ -222,34 +222,34 @@ det 3.2.2 {
(SELECT * FROM t2 ORDER BY x LIMIT 10) AS x2
ORDER BY x2.y LIMIT 5
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)}
- 0 0 0 {SCAN SUBQUERY 1 AS x1 (~10 rows)}
- 0 1 1 {SCAN SUBQUERY 2 AS x2 (~10 rows)}
+ 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
+ 0 0 0 {SCAN SUBQUERY 1 AS x1}
+ 0 1 1 {SCAN SUBQUERY 2 AS x2}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
det 3.3.1 {
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2)
} {
- 0 0 0 {SCAN TABLE t1 (~100000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t2}
}
det 3.3.2 {
SELECT * FROM t1 WHERE y IN (SELECT y FROM t2 WHERE t1.x!=t2.x)
} {
- 0 0 0 {SCAN TABLE t1 (~500000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 (~500000 rows)}
+ 1 0 0 {SCAN TABLE t2}
}
det 3.3.3 {
SELECT * FROM t1 WHERE EXISTS (SELECT y FROM t2 WHERE t1.x!=t2.x)
} {
- 0 0 0 {SCAN TABLE t1 (~500000 rows)}
+ 0 0 0 {SCAN TABLE t1}
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 (~500000 rows)}
+ 1 0 0 {SCAN TABLE t2}
}
#-------------------------------------------------------------------------
@@ -258,43 +258,43 @@ det 3.3.3 {
do_eqp_test 4.1.1 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
+ 2 0 0 {SCAN TABLE t2}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.1.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t2}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.1.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t2}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
}
do_eqp_test 4.1.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t2}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
}
do_eqp_test 4.1.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t2}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
}
@@ -302,64 +302,64 @@ do_eqp_test 4.1.5 {
do_eqp_test 4.2.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 USING INDEX t2i1 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.2.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
+ 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION)}
}
do_eqp_test 4.2.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
+ 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (INTERSECT)}
}
do_eqp_test 4.2.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
- 2 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ 2 0 0 {SCAN TABLE t2 USING INDEX t2i1}
+ 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
}
do_eqp_test 4.3.1 {
SELECT x FROM t1 UNION SELECT x FROM t2
} {
- 1 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1}
+ 2 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
}
do_eqp_test 4.3.2 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1
} {
- 2 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t1}
+ 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
- 4 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 4 0 0 {SCAN TABLE t1}
0 0 0 {COMPOUND SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)}
}
do_eqp_test 4.3.3 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1
} {
- 2 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 2 0 0 {SCAN TABLE t1}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1 (~1000000 rows)}
+ 3 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION)}
- 4 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 4 0 0 {SCAN TABLE t1}
4 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 4 (UNION)}
}
@@ -370,128 +370,147 @@ do_eqp_test 4.3.3 {
#
drop_all_tables
-# EVIDENCE-OF: R-64208-08323 sqlite> EXPLAIN QUERY PLAN SELECT a, b
-# FROM t1 WHERE a=1; 0|0|0|SCAN TABLE t1 (~100000 rows)
-do_execsql_test 5.1.0 { CREATE TABLE t1(a, b) }
+# EVIDENCE-OF: R-47779-47605 sqlite> EXPLAIN QUERY PLAN SELECT a, b
+# FROM t1 WHERE a=1;
+# 0|0|0|SCAN TABLE t1
+#
+do_execsql_test 5.1.0 { CREATE TABLE t1(a INT, b INT, ex TEXT) }
det 5.1.1 "SELECT a, b FROM t1 WHERE a=1" {
- 0 0 0 {SCAN TABLE t1 (~100000 rows)}
+ 0 0 0 {SCAN TABLE t1}
}
-# EVIDENCE-OF: R-09022-44606 sqlite> CREATE INDEX i1 ON t1(a);
+# EVIDENCE-OF: R-55852-17599 sqlite> CREATE INDEX i1 ON t1(a);
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
-# 0|0|0|SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)
+# 0|0|0|SEARCH TABLE t1 USING INDEX i1
+#
do_execsql_test 5.2.0 { CREATE INDEX i1 ON t1(a) }
det 5.2.1 "SELECT a, b FROM t1 WHERE a=1" {
- 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
}
-# EVIDENCE-OF: R-62228-34103 sqlite> CREATE INDEX i2 ON t1(a, b);
+# EVIDENCE-OF: R-21179-11011 sqlite> CREATE INDEX i2 ON t1(a, b);
# sqlite> EXPLAIN QUERY PLAN SELECT a, b FROM t1 WHERE a=1;
-# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
+# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
+#
do_execsql_test 5.3.0 { CREATE INDEX i2 ON t1(a, b) }
det 5.3.1 "SELECT a, b FROM t1 WHERE a=1" {
- 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)}
}
-# EVIDENCE-OF: R-22253-05302 sqlite> EXPLAIN QUERY PLAN SELECT t1.*,
-# t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2; 0|0|0|SEARCH TABLE t1
-# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|1|SCAN TABLE t2
-# (~1000000 rows)
-do_execsql_test 5.4.0 {CREATE TABLE t2(c, d)}
-det 5.4.1 "SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2" {
- 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)}
- 0 1 1 {SCAN TABLE t2 (~1000000 rows)}
+# EVIDENCE-OF: R-09991-48941 sqlite> EXPLAIN QUERY PLAN
+# SELECT t1.*, t2.* FROM t1, t2 WHERE t1.a=1 AND t1.b>2;
+# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)
+# 0|1|1|SCAN TABLE t2
+#
+do_execsql_test 5.4.0 {CREATE TABLE t2(c INT, d INT, ex TEXT)}
+det 5.4.1 "SELECT t1.a, t2.c FROM t1, t2 WHERE t1.a=1 AND t1.b>2" {
+ 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)}
+ 0 1 1 {SCAN TABLE t2}
}
-# EVIDENCE-OF: R-21040-07025 sqlite> EXPLAIN QUERY PLAN SELECT t1.*,
-# t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2; 0|0|1|SEARCH TABLE t1
-# USING COVERING INDEX i2 (a=? AND b>?) (~3 rows) 0|1|0|SCAN TABLE t2
-# (~1000000 rows)
-det 5.5 "SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2" {
- 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?) (~2 rows)}
- 0 1 0 {SCAN TABLE t2 (~1000000 rows)}
+# EVIDENCE-OF: R-33626-61085 sqlite> EXPLAIN QUERY PLAN
+# SELECT t1.*, t2.* FROM t2, t1 WHERE t1.a=1 AND t1.b>2;
+# 0|0|1|SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)
+# 0|1|0|SCAN TABLE t2
+#
+det 5.5 "SELECT t1.a, t2.c FROM t2, t1 WHERE t1.a=1 AND t1.b>2" {
+ 0 0 1 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=? AND b>?)}
+ 0 1 0 {SCAN TABLE t2}
}
-# EVIDENCE-OF: R-39007-61103 sqlite> CREATE INDEX i3 ON t1(b);
+# EVIDENCE-OF: R-04002-25654 sqlite> CREATE INDEX i3 ON t1(b);
# sqlite> EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=1 OR b=2;
-# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
-# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)
+# 0|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
+# 0|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?)
+#
do_execsql_test 5.5.0 {CREATE INDEX i3 ON t1(b)}
-det 5.6.1 "SELECT * FROM t1 WHERE a=1 OR b=2" {
- 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
- 0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)}
+det 5.6.1 "SELECT a, b FROM t1 WHERE a=1 OR b=2" {
+ 0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)}
}
-# EVIDENCE-OF: R-33025-54904 sqlite> EXPLAIN QUERY PLAN SELECT c, d
-# FROM t2 ORDER BY c; 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|USE TEMP
-# B-TREE FOR ORDER BY
+# EVIDENCE-OF: R-24577-38891 sqlite> EXPLAIN QUERY PLAN
+# SELECT c, d FROM t2 ORDER BY c;
+# 0|0|0|SCAN TABLE t2
+# 0|0|0|USE TEMP B-TREE FOR ORDER BY
+#
det 5.7 "SELECT c, d FROM t2 ORDER BY c" {
- 0 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
-# EVIDENCE-OF: R-38854-22809 sqlite> CREATE INDEX i4 ON t2(c);
+# EVIDENCE-OF: R-58157-12355 sqlite> CREATE INDEX i4 ON t2(c);
# sqlite> EXPLAIN QUERY PLAN SELECT c, d FROM t2 ORDER BY c;
-# 0|0|0|SCAN TABLE t2 USING INDEX i4 (~1000000 rows)
+# 0|0|0|SCAN TABLE t2 USING INDEX i4
+#
do_execsql_test 5.8.0 {CREATE INDEX i4 ON t2(c)}
det 5.8.1 "SELECT c, d FROM t2 ORDER BY c" {
- 0 0 0 {SCAN TABLE t2 USING INDEX i4 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING INDEX i4}
}
-# EVIDENCE-OF: R-29884-43993 sqlite> EXPLAIN QUERY PLAN SELECT
+# EVIDENCE-OF: R-13931-10421 sqlite> EXPLAIN QUERY PLAN SELECT
# (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2;
-# 0|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|EXECUTE SCALAR SUBQUERY 1
-# 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)
-# 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2 2|0|0|SEARCH TABLE t1 USING
-# INDEX i3 (b=?) (~10 rows)
+# 0|0|0|SCAN TABLE t2
+# 0|0|0|EXECUTE SCALAR SUBQUERY 1
+# 1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
+# 0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2
+# 2|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?)
+#
det 5.9 {
SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2
} {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i4}
0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?) (~10 rows)}
+ 1 0 0 {SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)}
0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 2}
- 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?) (~10 rows)}
+ 2 0 0 {SEARCH TABLE t1 USING INDEX i3 (b=?)}
}
-# EVIDENCE-OF: R-17911-16445 sqlite> EXPLAIN QUERY PLAN SELECT
-# count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
-# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows) 0|0|0|SCAN
-# SUBQUERY 1 (~1000000 rows) 0|0|0|USE TEMP B-TREE FOR GROUP BY
+# EVIDENCE-OF: R-50892-45943 sqlite> EXPLAIN QUERY PLAN
+# SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
+# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
+# 0|0|0|SCAN SUBQUERY 1
+# 0|0|0|USE TEMP B-TREE FOR GROUP BY
+#
det 5.10 {
SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x
} {
- 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)}
- 0 0 0 {SCAN SUBQUERY 1 (~100 rows)}
+ 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2}
+ 0 0 0 {SCAN SUBQUERY 1}
0 0 0 {USE TEMP B-TREE FOR GROUP BY}
}
-# EVIDENCE-OF: R-18544-33103 sqlite> EXPLAIN QUERY PLAN SELECT * FROM
-# (SELECT * FROM t2 WHERE c=1), t1; 0|0|0|SEARCH TABLE t2 USING INDEX i4
-# (c=?) (~10 rows) 0|1|1|SCAN TABLE t1 (~1000000 rows)
-det 5.11 "SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1" {
- 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?) (~10 rows)}
- 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)}
+# EVIDENCE-OF: R-46219-33846 sqlite> EXPLAIN QUERY PLAN
+# SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1;
+# 0|0|0|SEARCH TABLE t2 USING INDEX i4 (c=?)
+# 0|1|1|SCAN TABLE t1
+#
+det 5.11 "SELECT a, b FROM (SELECT * FROM t2 WHERE c=1), t1" {
+ 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?)}
+ 0 1 1 {SCAN TABLE t1 USING COVERING INDEX i2}
}
-# EVIDENCE-OF: R-40701-42164 sqlite> EXPLAIN QUERY PLAN SELECT a FROM
-# t1 UNION SELECT c FROM t2; 1|0|0|SCAN TABLE t1 (~1000000 rows)
-# 2|0|0|SCAN TABLE t2 (~1000000 rows) 0|0|0|COMPOUND SUBQUERIES 1 AND 2
-# USING TEMP B-TREE (UNION)
-det 5.12 "SELECT a FROM t1 UNION SELECT c FROM t2" {
- 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
- 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4 (~1000000 rows)}
+# EVIDENCE-OF: R-37879-39987 sqlite> EXPLAIN QUERY PLAN
+# SELECT a FROM t1 UNION SELECT c FROM t2;
+# 1|0|0|SCAN TABLE t1
+# 2|0|0|SCAN TABLE t2
+# 0|0|0|COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)
+#
+det 5.12 "SELECT a,b FROM t1 UNION SELECT c, 99 FROM t2" {
+ 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2}
+ 2 0 0 {SCAN TABLE t2 USING COVERING INDEX i4}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
}
-# EVIDENCE-OF: R-61538-24748 sqlite> EXPLAIN QUERY PLAN SELECT a FROM
-# t1 EXCEPT SELECT d FROM t2 ORDER BY 1; 1|0|0|SCAN TABLE t1 USING
-# COVERING INDEX i2 (~1000000 rows) 2|0|0|SCAN TABLE t2 (~1000000 rows)
-# 2|0|0|USE TEMP B-TREE FOR ORDER BY 0|0|0|COMPOUND SUBQUERIES 1 AND 2
-# (EXCEPT)
+# EVIDENCE-OF: R-44864-63011 sqlite> EXPLAIN QUERY PLAN
+# SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1;
+# 1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
+# 2|0|0|SCAN TABLE t2 2|0|0|USE TEMP B-TREE FOR ORDER BY
+# 0|0|0|COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)
+#
det 5.13 "SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1" {
- 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)}
- 2 0 0 {SCAN TABLE t2 (~1000000 rows)}
+ 1 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
+ 2 0 0 {SCAN TABLE t2}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)}
}
@@ -529,10 +548,10 @@ proc do_peqp_test {tn sql res} {
}
do_peqp_test 6.1 {
- SELECT a FROM t1 EXCEPT SELECT d FROM t2 ORDER BY 1
+ SELECT a, b FROM t1 EXCEPT SELECT d, 99 FROM t2 ORDER BY 1
} [string trimleft {
-1 0 0 SCAN TABLE t1 USING COVERING INDEX i2 (~1000000 rows)
-2 0 0 SCAN TABLE t2 (~1000000 rows)
+1 0 0 SCAN TABLE t1 USING COVERING INDEX i2
+2 0 0 SCAN TABLE t2
2 0 0 USE TEMP B-TREE FOR ORDER BY
0 0 0 COMPOUND SUBQUERIES 1 AND 2 (EXCEPT)
}]
@@ -544,26 +563,26 @@ do_peqp_test 6.1 {
drop_all_tables
do_execsql_test 7.0 {
- CREATE TABLE t1(a, b);
- CREATE TABLE t2(a, b);
+ CREATE TABLE t1(a INT, b INT, ex CHAR(100));
+ CREATE TABLE t2(a INT, b INT, ex CHAR(100));
CREATE INDEX i1 ON t2(a);
}
det 7.1 "SELECT count(*) FROM t1" {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t1}
}
det 7.2 "SELECT count(*) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1}
}
do_execsql_test 7.3 {
- INSERT INTO t1 VALUES(1, 2);
- INSERT INTO t1 VALUES(3, 4);
+ INSERT INTO t1(a,b) VALUES(1, 2);
+ INSERT INTO t1(a,b) VALUES(3, 4);
- INSERT INTO t2 VALUES(1, 2);
- INSERT INTO t2 VALUES(3, 4);
- INSERT INTO t2 VALUES(5, 6);
+ INSERT INTO t2(a,b) VALUES(1, 2);
+ INSERT INTO t2(a,b) VALUES(3, 4);
+ INSERT INTO t2(a,b) VALUES(5, 6);
ANALYZE;
}
@@ -572,12 +591,56 @@ db close
sqlite3 db test.db
det 7.4 "SELECT count(*) FROM t1" {
- 0 0 0 {SCAN TABLE t1 (~2 rows)}
+ 0 0 0 {SCAN TABLE t1}
}
det 7.5 "SELECT count(*) FROM t2" {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1(~3 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i1}
+}
+
+#-------------------------------------------------------------------------
+# The following tests - eqp-8.* - test that queries that use the OP_Count
+# optimization return something sensible with EQP.
+#
+drop_all_tables
+
+do_execsql_test 8.0 {
+ CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID;
+ CREATE TABLE t2(a, b, c);
+}
+
+det 8.1.1 "SELECT * FROM t2" {
+ 0 0 0 {SCAN TABLE t2}
+}
+
+det 8.1.2 "SELECT * FROM t2 WHERE rowid=?" {
+ 0 0 0 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
+det 8.1.3 "SELECT count(*) FROM t2" {
+ 0 0 0 {SCAN TABLE t2}
+}
+
+det 8.2.1 "SELECT * FROM t1" {
+ 0 0 0 {SCAN TABLE t1}
+}
+
+det 8.2.2 "SELECT * FROM t1 WHERE b=?" {
+ 0 0 0 {SEARCH TABLE t1 USING PRIMARY KEY (b=?)}
+}
+
+det 8.2.3 "SELECT * FROM t1 WHERE b=? AND c=?" {
+ 0 0 0 {SEARCH TABLE t1 USING PRIMARY KEY (b=? AND c=?)}
+}
+
+det 8.2.4 "SELECT count(*) FROM t1" {
+ 0 0 0 {SCAN TABLE t1}
+}
+
+
+
+
+
+
finish_test
diff --git a/test/errmsg.test b/test/errmsg.test
index 6b3f3b7..8df8a70 100644
--- a/test/errmsg.test
+++ b/test/errmsg.test
@@ -78,14 +78,14 @@ do_test 2.2 {
error_messages "INSERT INTO t1 VALUES('ghi', 'def')"
} [list {*}{
SQLITE_ERROR {SQL logic error or missing database}
- SQLITE_CONSTRAINT {column b is not unique}
+ SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
}]
verify_ex_errcode 2.2b SQLITE_CONSTRAINT_UNIQUE
do_test 2.3 {
error_messages_v2 "INSERT INTO t1 VALUES('ghi', 'def')"
} [list {*}{
- SQLITE_CONSTRAINT {column b is not unique}
- SQLITE_CONSTRAINT {column b is not unique}
+ SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
+ SQLITE_CONSTRAINT {UNIQUE constraint failed: t1.b}
}]
verify_ex_errcode 2.3b SQLITE_CONSTRAINT_UNIQUE
diff --git a/test/exclusive.test b/test/exclusive.test
index ffde891..c000dfe 100644
--- a/test/exclusive.test
+++ b/test/exclusive.test
@@ -506,4 +506,3 @@ do_execsql_test exclusive-6.5 {
} {exclusive}
finish_test
-
diff --git a/test/exclusive2.test b/test/exclusive2.test
index 54203e3..712363e 100644
--- a/test/exclusive2.test
+++ b/test/exclusive2.test
@@ -301,7 +301,6 @@ do_test exclusive2-3.3 {
readPagerChangeCounter test.db
} {4}
do_test exclusive2-3.4 {
-breakpoint
execsql {
INSERT INTO t1 VALUES(randstr(200, 200));
}
diff --git a/test/extension01.test b/test/extension01.test
new file mode 100644
index 0000000..97b7726
--- /dev/null
+++ b/test/extension01.test
@@ -0,0 +1,83 @@
+# 2014-06-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 tests for various small extensions.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix extension01
+
+load_static_extension db fileio
+do_test 1.0 {
+ forcedelete file1.txt
+ set out [open ./file1.txt wb]
+ puts -nonewline $out "This is a text file without a line ending"
+ close $out
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT);
+ INSERT INTO t1 VALUES(1, readfile('./file1.txt'));
+ SELECT * FROM t1;
+ }
+} {1 {This is a text file without a line ending}}
+do_test 1.1 {
+ forcedelete file2.txt
+ db nullvalue nil
+ db eval {
+ DELETE FROM t1;
+ INSERT INTO t1 VALUES(2, readfile(NULL)),(3, readfile('file2.txt'));
+ SELECT a, b, typeof(b) FROM t1;
+ }
+} {2 nil null 3 nil null}
+
+do_test 1.2 {
+ db eval {
+ SELECT writefile('./file2.txt', 'A second test line');
+ }
+} {18}
+do_test 1.3 {
+ set in [open ./file2.txt rb]
+ set x [read $in]
+ close $in
+ list $x [file size file2.txt]
+} {{A second test line} 18}
+
+do_test 1.4 {
+ db eval {
+ SELECT writefile('./file2.txt', NULL);
+ }
+} {0}
+do_test 1.5 {
+ file size ./file2.txt
+} {0}
+
+do_test 1.6 {
+ if {$::tcl_platform(platform)=="unix"} {
+ file attributes ./file2.txt -permissions r--r--r--
+ } else {
+ file attributes ./file2.txt -readonly 1
+ }
+ db eval {
+ SELECT writefile('./file2.txt', 'Another test');
+ }
+} {nil}
+do_test 1.7 {
+ if {$::tcl_platform(platform)=="unix"} {
+ file attributes ./file2.txt -permissions rw-r--r--
+ } else {
+ file attributes ./file2.txt -readonly 0
+ }
+ db eval {
+ SELECT writefile(NULL, 'Another test');
+ }
+} {nil}
+
+finish_test
diff --git a/test/fallocate.test b/test/fallocate.test
index 8a5fa32..f523c2c 100644
--- a/test/fallocate.test
+++ b/test/fallocate.test
@@ -143,4 +143,3 @@ if {!$skipwaltests} {
finish_test
-
diff --git a/test/filefmt.test b/test/filefmt.test
index bc1af18..2df1424 100644
--- a/test/filefmt.test
+++ b/test/filefmt.test
@@ -248,4 +248,3 @@ do_test filefmt-4.4 {
db2 close
finish_test
-
diff --git a/test/fkey1.test b/test/fkey1.test
index e7c00d1..90a4c44 100644
--- a/test/fkey1.test
+++ b/test/fkey1.test
@@ -117,5 +117,8 @@ do_test fkey1-3.4 {
{0 0 t5 d {} {SET DEFAULT} CASCADE NONE} \
{0 1 t5 e {} {SET DEFAULT} CASCADE NONE} \
]
+do_test fkey1-3.5 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 0 0}
finish_test
diff --git a/test/fkey2.test b/test/fkey2.test
index 3e5b27c..4c8daa0 100644
--- a/test/fkey2.test
+++ b/test/fkey2.test
@@ -104,38 +104,38 @@ set FkeySimpleSchema {
set FkeySimpleTests {
- 1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {foreign key constraint failed}}
+ 1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
1.2 "INSERT INTO t1 VALUES(1, 2)" {0 {}}
1.3 "INSERT INTO t2 VALUES(1, 3)" {0 {}}
- 1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {foreign key constraint failed}}
+ 1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
1.5 "INSERT INTO t2 VALUES(NULL, 4)" {0 {}}
- 1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {foreign key constraint failed}}
+ 1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
1.7 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
1.9 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
1.10 "UPDATE t2 SET c=NULL WHERE d=4" {0 {}}
- 1.11 "DELETE FROM t1 WHERE a=1" {1 {foreign key constraint failed}}
- 1.12 "UPDATE t1 SET a = 2" {1 {foreign key constraint failed}}
+ 1.11 "DELETE FROM t1 WHERE a=1" {1 {FOREIGN KEY constraint failed}}
+ 1.12 "UPDATE t1 SET a = 2" {1 {FOREIGN KEY constraint failed}}
1.13 "UPDATE t1 SET a = 1" {0 {}}
- 2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {foreign key constraint failed}}
+ 2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
2.2 "INSERT INTO t3 VALUES(1, 2)" {0 {}}
2.3 "INSERT INTO t4 VALUES(1, 3)" {0 {}}
- 4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {foreign key constraint failed}}
+ 4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
4.2 "INSERT INTO t7 VALUES(2, 1)" {0 {}}
4.3 "INSERT INTO t8 VALUES(1, 3)" {0 {}}
- 4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {foreign key constraint failed}}
+ 4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
4.5 "INSERT INTO t8 VALUES(NULL, 4)" {0 {}}
- 4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {foreign key constraint failed}}
+ 4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
4.7 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}}
- 4.11 "DELETE FROM t7 WHERE b=1" {1 {foreign key constraint failed}}
- 4.12 "UPDATE t7 SET b = 2" {1 {foreign key constraint failed}}
+ 4.11 "DELETE FROM t7 WHERE b=1" {1 {FOREIGN KEY constraint failed}}
+ 4.12 "UPDATE t7 SET b = 2" {1 {FOREIGN KEY constraint failed}}
4.13 "UPDATE t7 SET b = 1" {0 {}}
- 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {foreign key constraint failed}}
- 4.15 "UPDATE t7 SET b = 5" {1 {foreign key constraint failed}}
- 4.16 "UPDATE t7 SET rowid = 5" {1 {foreign key constraint failed}}
+ 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {FOREIGN KEY constraint failed}}
+ 4.15 "UPDATE t7 SET b = 5" {1 {FOREIGN KEY constraint failed}}
+ 4.16 "UPDATE t7 SET rowid = 5" {1 {FOREIGN KEY constraint failed}}
4.17 "UPDATE t7 SET a = 10" {0 {}}
5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}}
@@ -215,7 +215,7 @@ do_test fkey2-1.5.1 {
} {35.0 text}
do_test fkey2-1.5.2 {
catchsql { DELETE FROM i }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
# Same test using a regular primary key with integer affinity.
drop_all_tables
@@ -231,7 +231,7 @@ do_test fkey2-1.6.1 {
} {35.0 text 35 integer}
do_test fkey2-1.6.2 {
catchsql { DELETE FROM i }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
# Use a collation sequence on the parent key.
drop_all_tables
@@ -243,7 +243,7 @@ do_test fkey2-1.7.1 {
INSERT INTO j VALUES('sqlite');
}
catchsql { DELETE FROM i }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
# Use the parent key collation even if it is default and the child key
# has an explicit value.
@@ -255,7 +255,7 @@ do_test fkey2-1.7.2 {
INSERT INTO i VALUES('SQLite');
}
catchsql { INSERT INTO j VALUES('sqlite') }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-1.7.3 {
execsql {
INSERT INTO i VALUES('sqlite');
@@ -263,7 +263,7 @@ do_test fkey2-1.7.3 {
DELETE FROM i WHERE i = 'SQLite';
}
catchsql { DELETE FROM i WHERE i = 'sqlite' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# This section (test cases fkey2-2.*) contains tests to check that the
@@ -271,7 +271,7 @@ do_test fkey2-1.7.3 {
#
proc fkey2-2-test {tn nocommit sql {res {}}} {
if {$res eq "FKV"} {
- set expected {1 {foreign key constraint failed}}
+ set expected {1 {FOREIGN KEY constraint failed}}
} else {
set expected [list 0 $res]
}
@@ -279,7 +279,7 @@ proc fkey2-2-test {tn nocommit sql {res {}}} {
if {$nocommit} {
do_test fkey2-2.${tn}c {
catchsql COMMIT
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
}
}
@@ -375,7 +375,7 @@ fkey2-2-test 65 1 "INSERT INTO leaf VALUES('b', 2)"
fkey2-2-test 66 1 "INSERT INTO leaf VALUES('c', 1)"
do_test fkey2-2-test-67 {
catchsql "INSERT INTO node SELECT parent, 3 FROM leaf"
-} {1 {column nodeid is not unique}}
+} {1 {UNIQUE constraint failed: node.nodeid}}
fkey2-2-test 68 0 "COMMIT" FKV
fkey2-2-test 69 1 "INSERT INTO node VALUES(1, NULL)"
fkey2-2-test 70 0 "INSERT INTO node VALUES(2, NULL)"
@@ -417,14 +417,14 @@ do_test fkey2-3.1.2 {
} {}
do_test fkey2-3.1.3 {
catchsql { UPDATE ab SET a = 5 }
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: ef}}
do_test fkey2-3.1.4 {
execsql { SELECT * FROM ab }
} {1 b}
do_test fkey2-3.1.4 {
execsql BEGIN;
catchsql { UPDATE ab SET a = 5 }
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: ef}}
do_test fkey2-3.1.5 {
execsql COMMIT;
execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
@@ -433,7 +433,7 @@ do_test fkey2-3.1.5 {
do_test fkey2-3.2.1 {
execsql BEGIN;
catchsql { DELETE FROM ab }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-3.2.2 {
execsql COMMIT
execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
@@ -555,7 +555,7 @@ do_test fkey2-7.1 {
} {}
do_test fkey2-7.2 {
catchsql { INSERT INTO t2 VALUES(1, 'A'); }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-7.3 {
execsql {
INSERT INTO t1 VALUES(1, 2);
@@ -568,19 +568,19 @@ do_test fkey2-7.4 {
} {}
do_test fkey2-7.5 {
catchsql { UPDATE t2 SET c = 3 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-7.6 {
catchsql { DELETE FROM t1 WHERE a = 2 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-7.7 {
execsql { DELETE FROM t1 WHERE a = 1 }
} {}
do_test fkey2-7.8 {
catchsql { UPDATE t1 SET a = 3 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-7.9 {
catchsql { UPDATE t2 SET rowid = 3 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# Test that it is not possible to enable/disable FK support while a
@@ -645,7 +645,7 @@ do_test fkey2-9.1.4 {
} {2 two}
do_test fkey2-9.1.5 {
catchsql { DELETE FROM t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-9.2.1 {
execsql {
@@ -780,13 +780,13 @@ do_test fkey2-12.1.3 {
} {}
do_test fkey2-12.1.4 {
catchsql "UPDATE t1 SET b = 'five' WHERE b = 'two'"
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-12.1.5 {
execsql "DELETE FROM t1 WHERE b = 'two'"
} {}
do_test fkey2-12.1.6 {
catchsql "COMMIT"
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-12.1.7 {
execsql {
INSERT INTO t1 VALUES(2, 'two');
@@ -828,7 +828,7 @@ do_test fkey2-12.2.3 {
INSERT INTO t2 VALUES('b');
}
catchsql { DELETE FROM t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-12.2.4 {
execsql {
SELECT * FROM t1;
@@ -866,14 +866,14 @@ do_test fkey2-12.3.2 {
} {no possibly}
do_test fkey2-12.3.3 {
catchsql { INSERT INTO down(c39, c38) VALUES('yes', 'no') }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-12.3.4 {
execsql {
INSERT INTO up(c34, c35) VALUES('yes', 'no');
INSERT INTO down(c39, c38) VALUES('yes', 'no');
}
catchsql { DELETE FROM up WHERE c34 = 'yes' }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-12.3.5 {
execsql {
DELETE FROM up WHERE c34 = 'possibly';
@@ -901,7 +901,7 @@ foreach {tn stmt} {
} {
do_test fkey2-13.1.$tn.1 {
catchsql $stmt
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-13.1.$tn.2 {
execsql {
SELECT * FROM pp;
@@ -911,7 +911,7 @@ foreach {tn stmt} {
do_test fkey2-13.1.$tn.3 {
execsql BEGIN;
catchsql $stmt
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-13.1.$tn.4 {
execsql {
COMMIT;
@@ -1015,13 +1015,13 @@ ifcapable altertable {
]
do_test fkey2-14.2.2.3 {
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2.2.4 {
execsql { INSERT INTO t4 VALUES(1, NULL) }
} {}
do_test fkey2-14.2.2.5 {
catchsql { UPDATE t4 SET b = 5 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2.2.6 {
catchsql { UPDATE t4 SET b = 1 }
} {0 {}}
@@ -1096,13 +1096,13 @@ ifcapable altertable {
]
do_test fkey2-14.2tmp.2.3 {
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2tmp.2.4 {
execsql { INSERT INTO t4 VALUES(1, NULL) }
} {}
do_test fkey2-14.2tmp.2.5 {
catchsql { UPDATE t4 SET b = 5 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2tmp.2.6 {
catchsql { UPDATE t4 SET b = 1 }
} {0 {}}
@@ -1178,13 +1178,13 @@ ifcapable altertable {
]
do_test fkey2-14.2aux.2.3 {
catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2aux.2.4 {
execsql { INSERT INTO t4 VALUES(1, NULL) }
} {}
do_test fkey2-14.2aux.2.5 {
catchsql { UPDATE t4 SET b = 5 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-14.2aux.2.6 {
catchsql { UPDATE t4 SET b = 1 }
} {0 {}}
@@ -1210,7 +1210,7 @@ do_test fkey-2.14.3.2 {
} {}
do_test fkey-2.14.3.3 {
catchsql { DROP TABLE t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey-2.14.3.4 {
execsql {
DELETE FROM t2;
@@ -1229,7 +1229,7 @@ do_test fkey-2.14.3.5 {
} {}
do_test fkey-2.14.3.6 {
catchsql { DROP TABLE t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey-2.14.3.7 {
execsql {
DROP TABLE t2;
@@ -1387,15 +1387,15 @@ foreach {tn zSchema} {
do_test fkey2-16.1.$tn.3 {
catchsql { UPDATE self SET b = 15 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-16.1.$tn.4 {
catchsql { UPDATE self SET a = 15 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-16.1.$tn.5 {
catchsql { UPDATE self SET a = 15, b = 16 }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-16.1.$tn.6 {
catchsql { UPDATE self SET a = 17, b = 17 }
@@ -1406,7 +1406,7 @@ foreach {tn zSchema} {
} {}
do_test fkey2-16.1.$tn.8 {
catchsql { INSERT INTO self VALUES(20, 21) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
}
#-------------------------------------------------------------------------
@@ -1463,7 +1463,7 @@ do_test fkey2-17.1.6 {
INSERT INTO one VALUES(0, 0, 0);
UPDATE two SET e=e+1, f=f+1;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-17.1.7 {
execsql { SELECT * FROM one }
} {1 2 3 2 3 4 3 4 5 0 0 0}
@@ -1619,7 +1619,7 @@ ifcapable auth {
}
do_test fkey2-18.8 {
catchsql { INSERT INTO short VALUES(1, 3, 2) }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-18.9 {
execsql { INSERT INTO short VALUES(1, 3, NULL) }
} {}
@@ -1628,7 +1628,7 @@ ifcapable auth {
} {1 3 2 1 3 {}}
do_test fkey2-18.11 {
catchsql { UPDATE short SET f = 2 WHERE f IS NULL }
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
db auth {}
unset authargs
@@ -1680,7 +1680,7 @@ foreach {tn insert} {
} {
do_test fkey2-20.2.$tn.1 {
catchsql "$insert INTO cc VALUES(1, 2)"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.2.$tn.2 {
execsql { SELECT * FROM cc }
} {}
@@ -1691,7 +1691,7 @@ foreach {tn insert} {
INSERT INTO cc VALUES(1, 2);
}
catchsql "$insert INTO cc VALUES(3, 4)"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.2.$tn.4 {
execsql { COMMIT ; SELECT * FROM cc }
} {1 2}
@@ -1716,13 +1716,13 @@ foreach {tn update} {
} {}
do_test fkey2-20.3.$tn.2 {
catchsql "$update pp SET a = 1"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.3.$tn.3 {
execsql { SELECT * FROM pp }
} {2 two}
do_test fkey2-20.3.$tn.4 {
catchsql "$update cc SET d = 1"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.3.$tn.5 {
execsql { SELECT * FROM cc }
} {1 2}
@@ -1732,7 +1732,7 @@ foreach {tn update} {
INSERT INTO pp VALUES(3, 'three');
}
catchsql "$update pp SET a = 1 WHERE a = 2"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.3.$tn.7 {
execsql { COMMIT ; SELECT * FROM pp }
} {2 two 3 three}
@@ -1742,7 +1742,7 @@ foreach {tn update} {
INSERT INTO cc VALUES(2, 2);
}
catchsql "$update cc SET d = 1 WHERE c = 1"
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test fkey2-20.3.$tn.9 {
execsql { COMMIT ; SELECT * FROM cc }
} {1 2 2 2}
@@ -1768,7 +1768,7 @@ do_test fkey2-genfkey.1.1 {
} {}
do_test fkey2-genfkey.1.2 {
catchsql { INSERT INTO t2 VALUES(1, 2) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.3 {
execsql {
INSERT INTO t1 VALUES(1, 2, 3);
@@ -1780,7 +1780,7 @@ do_test fkey2-genfkey.1.4 {
} {}
do_test fkey2-genfkey.1.5 {
catchsql { UPDATE t2 SET e = 5 WHERE e IS NULL }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.6 {
execsql { UPDATE t2 SET e = 1 WHERE e IS NULL }
} {}
@@ -1789,13 +1789,13 @@ do_test fkey2-genfkey.1.7 {
} {}
do_test fkey2-genfkey.1.8 {
catchsql { UPDATE t1 SET a = 10 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.9 {
catchsql { UPDATE t1 SET a = NULL }
} {1 {datatype mismatch}}
do_test fkey2-genfkey.1.10 {
catchsql { DELETE FROM t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.11 {
execsql { UPDATE t2 SET e = NULL }
} {}
@@ -1815,7 +1815,7 @@ do_test fkey2-genfkey.1.13 {
} {}
do_test fkey2-genfkey.1.14 {
catchsql { INSERT INTO t3 VALUES(3, 1, 4) }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.15 {
execsql {
INSERT INTO t1 VALUES(1, 1, 4);
@@ -1824,16 +1824,16 @@ do_test fkey2-genfkey.1.15 {
} {}
do_test fkey2-genfkey.1.16 {
catchsql { DELETE FROM t1 }
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.17 {
catchsql { UPDATE t1 SET b = 10}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-genfkey.1.18 {
execsql { UPDATE t1 SET a = 10}
} {}
do_test fkey2-genfkey.1.19 {
catchsql { UPDATE t3 SET h = 'hello' WHERE i = 3}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
drop_all_tables
do_test fkey2-genfkey.2.1 {
@@ -1946,7 +1946,7 @@ do_test fkey2-dd08e5.1.2 {
catchsql {
DELETE FROM tdd08;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-dd08e5.1.3 {
execsql {
SELECT * FROM tdd08;
@@ -1956,17 +1956,17 @@ do_test fkey2-dd08e5.1.4 {
catchsql {
INSERT INTO tdd08_b VALUES(400,500,300);
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-dd08e5.1.5 {
catchsql {
UPDATE tdd08_b SET x=x+1;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-dd08e5.1.6 {
catchsql {
UPDATE tdd08 SET a=a+1;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
#-------------------------------------------------------------------------
# Verify that ticket ce7c133ea6cc9ccdc1a60d80441f80b6180f5eba
@@ -1987,12 +1987,12 @@ do_test fkey2-ce7c13.1.2 {
catchsql {
UPDATE tce71 set b = 201 where a = 100;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-ce7c13.1.3 {
catchsql {
UPDATE tce71 set a = 101 where a = 100;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-ce7c13.1.4 {
execsql {
CREATE TABLE tce73(a INTEGER PRIMARY KEY, b, UNIQUE(a,b));
@@ -2007,11 +2007,11 @@ do_test fkey2-ce7c13.1.5 {
catchsql {
UPDATE tce73 set b = 201 where a = 100;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey2-ce7c13.1.6 {
catchsql {
UPDATE tce73 set a = 101 where a = 100;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
finish_test
diff --git a/test/fkey3.test b/test/fkey3.test
index 6a11f88..a8d0382 100644
--- a/test/fkey3.test
+++ b/test/fkey3.test
@@ -43,13 +43,13 @@ do_test fkey3-1.2 {
catchsql {
DELETE FROM t1 WHERE x=100;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey3-1.3 {
catchsql {
DROP TABLE t1;
}
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_test fkey3-1.4 {
execsql {
@@ -95,17 +95,17 @@ do_execsql_test 3.1.1 {
} {}
do_catchsql_test 3.1.2 {
INSERT INTO t3 VALUES(NULL, 2, 5, 2);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_catchsql_test 3.1.3 {
INSERT INTO t3 VALUES(NULL, 3, 5, 2);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.2.1 {
CREATE TABLE t4(a UNIQUE, b REFERENCES t4(a));
}
do_catchsql_test 3.2.2 {
INSERT INTO t4 VALUES(NULL, 1);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.3.1 {
CREATE TABLE t5(a INTEGER PRIMARY KEY, b REFERENCES t5(a));
@@ -113,7 +113,7 @@ do_execsql_test 3.3.1 {
} {}
do_catchsql_test 3.3.2 {
INSERT INTO t5 VALUES(NULL, 3);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.4.1 {
CREATE TABLE t6(a INTEGER PRIMARY KEY, b, c, d,
@@ -127,7 +127,7 @@ do_execsql_test 3.4.4 { INSERT INTO t6 VALUES(NULL, 'a', 1, 'a'); } {}
do_execsql_test 3.4.5 { INSERT INTO t6 VALUES(5, 'a', 2, 'a'); } {}
do_catchsql_test 3.4.6 {
INSERT INTO t6 VALUES(NULL, 'a', 65, 'a');
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.4.7 {
INSERT INTO t6 VALUES(100, 'one', 100, 'one');
@@ -149,10 +149,10 @@ do_execsql_test 3.5.2 { INSERT INTO t7 VALUES('x', 1, 'x', NULL) } {}
do_execsql_test 3.5.3 { INSERT INTO t7 VALUES('x', 2, 'x', 2) } {}
do_catchsql_test 3.5.4 {
INSERT INTO t7 VALUES('x', 450, 'x', NULL);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_catchsql_test 3.5.5 {
INSERT INTO t7 VALUES('x', 450, 'x', 451);
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.6.1 {
@@ -163,7 +163,7 @@ do_execsql_test 3.6.1 {
}
do_catchsql_test 3.6.2 {
UPDATE t8 SET d = 2;
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_execsql_test 3.6.3 { UPDATE t8 SET d = 1; }
do_execsql_test 3.6.4 { UPDATE t8 SET e = 2; }
@@ -181,6 +181,6 @@ do_catchsql_test 3.6.5 {
INSERT INTO TestTable VALUES (1, 'parent', 1, null);
INSERT INTO TestTable VALUES (2, 'child', 1, 1);
UPDATE TestTable SET parent_id=1000 where id=2;
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
finish_test
diff --git a/test/fkey5.test b/test/fkey5.test
index 40a1a5e..5aa8b1d 100644
--- a/test/fkey5.test
+++ b/test/fkey5.test
@@ -12,9 +12,12 @@
#
# This file tests the PRAGMA foreign_key_check command.
#
+# EVIDENCE-OF: R-05426-18119 PRAGMA foreign_key_check; PRAGMA
+# foreign_key_check(table-name);
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix fkey5
ifcapable {!foreignkey} {
finish_test
@@ -82,6 +85,20 @@ do_test fkey5-1.4 {
}
} {}
+# EVIDENCE-OF: R-45728-08709 There are four columns in each result row.
+#
+# EVIDENCE-OF: R-55672-01620 The first column is the name of the table
+# that contains the REFERENCES clause.
+#
+# EVIDENCE-OF: R-25219-25618 The second column is the rowid of the row
+# that contains the invalid REFERENCES clause.
+#
+# EVIDENCE-OF: R-40482-20265 The third column is the name of the table
+# that is referred to.
+#
+# EVIDENCE-OF: R-62839-07969 The fourth column is the index of the
+# specific foreign key constraint that failed.
+#
do_test fkey5-2.0 {
db eval {
INSERT INTO c5 SELECT x FROM c1;
@@ -99,6 +116,9 @@ do_test fkey5-2.2 {
PRAGMA foreign_key_check(c1);
}
} {}
+do_execsql_test fkey5-2.3 {
+ PRAGMA foreign_key_list(c5);
+} {0 0 p1 x {} {NO ACTION} {NO ACTION} NONE}
do_test fkey5-3.0 {
db eval {
@@ -306,5 +326,38 @@ do_test fkey5-8.7 {
} {}
+#-------------------------------------------------------------------------
+# Tests 9.* verify that missing parent tables are handled correctly.
+#
+do_execsql_test 9.1.1 {
+ CREATE TABLE k1(x REFERENCES s1);
+ PRAGMA foreign_key_check(k1);
+} {}
+do_execsql_test 9.1.2 {
+ INSERT INTO k1 VALUES(NULL);
+ PRAGMA foreign_key_check(k1);
+} {}
+do_execsql_test 9.1.3 {
+ INSERT INTO k1 VALUES(1);
+ PRAGMA foreign_key_check(k1);
+} {k1 2 s1 0}
+
+do_execsql_test 9.2.1 {
+ CREATE TABLE k2(x, y, FOREIGN KEY(x, y) REFERENCES s1(a, b));
+ PRAGMA foreign_key_check(k2);
+} {}
+do_execsql_test 9.2 {
+ INSERT INTO k2 VALUES(NULL, 'five');
+ PRAGMA foreign_key_check(k2);
+} {}
+do_execsql_test 9.3 {
+ INSERT INTO k2 VALUES('one', NULL);
+ PRAGMA foreign_key_check(k2);
+} {}
+do_execsql_test 9.4 {
+ INSERT INTO k2 VALUES('six', 'seven');
+ PRAGMA foreign_key_check(k2);
+} {k2 3 s1 0}
+
finish_test
diff --git a/test/fkey6.test b/test/fkey6.test
new file mode 100644
index 0000000..6fc3de2
--- /dev/null
+++ b/test/fkey6.test
@@ -0,0 +1,175 @@
+# 2013-07-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 tests the PRAGMA defer_foreign_keys and
+# SQLITE_DBSTATUS_DEFERRED_FKS
+#
+# EVIDENCE-OF: R-18981-16292 When the defer_foreign_keys PRAGMA is on,
+# enforcement of all foreign key constraints is delayed until the
+# outermost transaction is committed.
+#
+# EVIDENCE-OF: R-28911-57501 The defer_foreign_keys pragma defaults to
+# OFF so that foreign key constraints are only deferred if they are
+# created as "DEFERRABLE INITIALLY DEFERRED".
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!foreignkey} {
+ finish_test
+ return
+}
+
+do_execsql_test fkey6-1.0 {
+ PRAGMA defer_foreign_keys;
+} {0}
+
+do_execsql_test fkey6-1.1 {
+ PRAGMA foreign_keys=ON;
+ CREATE TABLE t1(x INTEGER PRIMARY KEY);
+ CREATE TABLE t2(y INTEGER PRIMARY KEY,
+ z INTEGER REFERENCES t1(x) DEFERRABLE INITIALLY DEFERRED);
+ CREATE INDEX t2z ON t2(z);
+ CREATE TABLE t3(u INTEGER PRIMARY KEY, v INTEGER REFERENCES t1(x));
+ CREATE INDEX t3v ON t3(v);
+ INSERT INTO t1 VALUES(1),(2),(3),(4),(5);
+ INSERT INTO t2 VALUES(1,1),(2,2);
+ INSERT INTO t3 VALUES(3,3),(4,4);
+} {}
+do_test fkey6-1.2 {
+ catchsql {DELETE FROM t1 WHERE x=2;}
+} {1 {FOREIGN KEY constraint failed}}
+do_test fkey6-1.3 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 0 0}
+do_test fkey6-1.4 {
+ execsql {
+ BEGIN;
+ DELETE FROM t1 WHERE x=1;
+ }
+} {}
+do_test fkey6-1.5.1 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 1
+} {0 1 0}
+do_test fkey6-1.5.2 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 1 0}
+do_test fkey6-1.6 {
+ execsql {
+ ROLLBACK;
+ }
+} {}
+do_test fkey6-1.7 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 0 0}
+do_test fkey6-1.8 {
+ execsql {
+ PRAGMA defer_foreign_keys=ON;
+ BEGIN;
+ DELETE FROM t1 WHERE x=3;
+ }
+} {}
+do_test fkey6-1.9 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 1 0}
+
+# EVIDENCE-OF: R-21752-26913 The defer_foreign_keys pragma is
+# automatically switched off at each COMMIT or ROLLBACK. Hence, the
+# defer_foreign_keys pragma must be separately enabled for each
+# transaction.
+do_execsql_test fkey6-1.10.1 {
+ PRAGMA defer_foreign_keys;
+ ROLLBACK;
+ PRAGMA defer_foreign_keys;
+ BEGIN;
+ PRAGMA defer_foreign_keys=ON;
+ PRAGMA defer_foreign_keys;
+ COMMIT;
+ PRAGMA defer_foreign_keys;
+ BEGIN;
+} {1 0 1 0}
+do_test fkey6-1.10.2 {
+ catchsql {DELETE FROM t1 WHERE x=3}
+} {1 {FOREIGN KEY constraint failed}}
+db eval {ROLLBACK}
+
+do_test fkey6-1.20 {
+ execsql {
+ BEGIN;
+ DELETE FROM t1 WHERE x=1;
+ }
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 1 0}
+do_test fkey6-1.21 {
+ execsql {
+ DELETE FROM t2 WHERE y=1;
+ }
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 0 0}
+do_test fkey6-1.22 {
+ execsql {
+ COMMIT;
+ }
+} {}
+
+do_execsql_test fkey6-2.1 {
+ CREATE TABLE p1(a PRIMARY KEY);
+ INSERT INTO p1 VALUES('one'), ('two');
+ CREATE TABLE c1(x REFERENCES p1);
+ INSERT INTO c1 VALUES('two'), ('one');
+}
+
+do_execsql_test fkey6-2.2 {
+ BEGIN;
+ PRAGMA defer_foreign_keys = 1;
+ DELETE FROM p1;
+ ROLLBACK;
+ PRAGMA defer_foreign_keys;
+} {0}
+
+do_execsql_test fkey6-2.3 {
+ BEGIN;
+ PRAGMA defer_foreign_keys = 1;
+ DROP TABLE p1;
+ PRAGMA vdbe_trace = 0;
+ ROLLBACK;
+ PRAGMA defer_foreign_keys;
+} {0}
+
+do_execsql_test fkey6-2.4 {
+ BEGIN;
+ PRAGMA defer_foreign_keys = 1;
+ DELETE FROM p1;
+ DROP TABLE c1;
+ COMMIT;
+ PRAGMA defer_foreign_keys;
+} {0}
+
+do_execsql_test fkey6-2.5 {
+ DROP TABLE p1;
+ CREATE TABLE p1(a PRIMARY KEY);
+ INSERT INTO p1 VALUES('one'), ('two');
+ CREATE TABLE c1(x REFERENCES p1);
+ INSERT INTO c1 VALUES('two'), ('one');
+}
+
+do_execsql_test fkey6-2.6 {
+ BEGIN;
+ PRAGMA defer_foreign_keys = 1;
+ INSERT INTO c1 VALUES('three');
+ DROP TABLE c1;
+ COMMIT;
+ PRAGMA defer_foreign_keys;
+} {0}
+
+
+finish_test
diff --git a/test/fkey7.test b/test/fkey7.test
new file mode 100644
index 0000000..c2682ed
--- /dev/null
+++ b/test/fkey7.test
@@ -0,0 +1,54 @@
+# 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
+set testprefix fkey7
+
+ifcapable {!foreignkey} {
+ finish_test
+ return
+}
+
+do_execsql_test 1.1 {
+ PRAGMA foreign_keys = 1;
+
+ CREATE TABLE s1(a PRIMARY KEY, b);
+ CREATE TABLE par(a, b REFERENCES s1, c UNIQUE, PRIMARY KEY(a));
+
+ CREATE TABLE c1(a, b REFERENCES par);
+ CREATE TABLE c2(a, b REFERENCES par);
+ CREATE TABLE c3(a, b REFERENCES par(c));
+}
+
+proc auth {op tbl args} {
+ if {$op == "SQLITE_READ"} { set ::tbls($tbl) 1 }
+ return "SQLITE_OK"
+}
+db auth auth
+db cache size 0
+proc do_tblsread_test {tn sql tbllist} {
+ array unset ::tbls
+ uplevel [list execsql $sql]
+ uplevel [list do_test $tn {lsort [array names ::tbls]} $tbllist]
+}
+
+do_tblsread_test 1.2 { UPDATE par SET b=? WHERE a=? } {par s1}
+do_tblsread_test 1.3 { UPDATE par SET a=? WHERE b=? } {c1 c2 par}
+do_tblsread_test 1.4 { UPDATE par SET c=? WHERE b=? } {c3 par}
+do_tblsread_test 1.5 { UPDATE par SET a=?,b=?,c=? WHERE b=? } {c1 c2 c3 par s1}
+
+
+finish_test
diff --git a/test/fkey_malloc.test b/test/fkey_malloc.test
index b4b5b4e..382e6f6 100644
--- a/test/fkey_malloc.test
+++ b/test/fkey_malloc.test
@@ -69,7 +69,10 @@ proc catch_fk_error {zSql} {
if {[string match {*foreign key*} $msg]} {
return ""
}
- if {$msg eq "out of memory" || $msg eq "constraint failed"} {
+ if {$msg eq "out of memory"
+ || $msg eq "FOREIGN KEY constraint failed"
+ || $msg eq "constraint failed"
+ } {
error 1
}
error $msg
diff --git a/test/fts3aa.test b/test/fts3aa.test
index 5d79e93..6ff384a 100644
--- a/test/fts3aa.test
+++ b/test/fts3aa.test
@@ -146,7 +146,6 @@ do_test fts3aa-3.3 {
execsql {SELECT rowid FROM t1 WHERE content MATCH '-two one'}
} {1 5 9 13 17 21 25 29}
-breakpoint
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}
@@ -224,4 +223,3 @@ do_catchsql_test fts3aa-7.5 {
finish_test
-
diff --git a/test/fts3ab.test b/test/fts3ab.test
index e1f3bdc..86124f7 100644
--- a/test/fts3ab.test
+++ b/test/fts3ab.test
@@ -115,7 +115,6 @@ for {set i 1} {$i<=15} {incr i} {
db eval "INSERT INTO t4(norm,plusone,invert) VALUES([join $vset ,]);"
}
-breakpoint
do_test fts3ab-4.1 {
execsql {SELECT rowid FROM t4 WHERE t4 MATCH 'norm:one'}
} {1 3 5 7 9 11 13 15}
diff --git a/test/fts3ag.test b/test/fts3ag.test
index 18ed5ae..9b658d1 100644
--- a/test/fts3ag.test
+++ b/test/fts3ag.test
@@ -78,7 +78,6 @@ do_test fts3ag-1.10 {
# Test that docListOrMerge() correctly handles reaching the end of one
# doclist before it reaches the end of the other.
do_test fts3ag-1.11 {
-breakpoint
execsql {SELECT rowid FROM t1 WHERE t1 MATCH 'this OR also'}
} {1 2}
do_test fts3ag-1.12 {
diff --git a/test/fts3ao.test b/test/fts3ao.test
index 786667a..60f0aa7 100644
--- a/test/fts3ao.test
+++ b/test/fts3ao.test
@@ -219,5 +219,32 @@ do_execsql_test 5.2 {
SELECT count(*) FROM sqlite_master WHERE name LIKE 't8%';
} {0 6}
-finish_test
+# At one point this was causing a memory leak.
+#
+foreach {tn sql} {
+ 1 {}
+ 2 { INSERT INTO ft(ft) VALUES('merge=2,2'); }
+} {
+ reset_db
+ do_execsql_test 6.$tn.1 "
+ CREATE TABLE t1(x);
+ CREATE VIRTUAL TABLE ft USING fts3;
+ INSERT INTO ft VALUES('hello world');
+ $sql
+ "
+
+ db close
+ sqlite3 db test.db
+ do_execsql_test 6.$tn.2 { SELECT * FROM t1 } {}
+
+ do_test 6.$tn.3 {
+ sqlite3 db2 test.db
+ db2 eval { DROP TABLE t1 }
+ db2 close
+ set stmt [sqlite3_prepare db { SELECT * FROM ft } -1 dummy]
+ sqlite3_finalize $stmt
+ } {SQLITE_OK}
+ db close
+}
+finish_test
diff --git a/test/fts3atoken.test b/test/fts3atoken.test
index 9277bfb..b7722c7 100644
--- a/test/fts3atoken.test
+++ b/test/fts3atoken.test
@@ -193,5 +193,3 @@ do_test fts3token-internal {
finish_test
-
-
diff --git a/test/fts3auto.test b/test/fts3auto.test
index 20b2812..20640d2 100644
--- a/test/fts3auto.test
+++ b/test/fts3auto.test
@@ -707,4 +707,3 @@ foreach {tn create} {
set sqlite_fts3_enable_parentheses $sfep
finish_test
-
diff --git a/test/fts3aux1.test b/test/fts3aux1.test
index ef17949..d17ac85 100644
--- a/test/fts3aux1.test
+++ b/test/fts3aux1.test
@@ -105,10 +105,10 @@ db func rec rec
#
do_execsql_test 2.1.1.1 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term='braid'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:} }
do_execsql_test 2.1.1.2 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term='braid'
-} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}}
+} {0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}}
# Now show that using "term='braid'" means the virtual table returns
# only 1 row to SQLite, but "+term='braid'" means all 19 are returned.
@@ -154,24 +154,24 @@ do_execsql_test 2.1.5 { SELECT * FROM terms WHERE term=NULL } {}
do_execsql_test 2.2.1.1 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term>'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 2:} }
do_execsql_test 2.2.1.2 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term>'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} }
do_execsql_test 2.2.1.3 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term<'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 4:} }
do_execsql_test 2.2.1.4 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term<'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} }
do_execsql_test 2.2.1.5 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE term BETWEEN 'brags' AND 'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 6:} }
do_execsql_test 2.2.1.6 {
EXPLAIN QUERY PLAN SELECT * FROM terms WHERE +term BETWEEN 'brags' AND 'brain'
-} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)} }
+} { 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:} }
do_test 2.2.2.1 {
set cnt 0
@@ -335,7 +335,7 @@ foreach {tn sort orderby} {
9 1 "ORDER BY occurrences DESC"
} {
- set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}]
+ set res [list 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}]
if {$sort} { lappend res 0 0 0 {USE TEMP B-TREE FOR ORDER BY} }
set sql "SELECT * FROM terms $orderby"
@@ -410,32 +410,32 @@ proc do_plansql_test {tn sql r} {
do_plansql_test 4.2 {
SELECT y FROM x2, terms WHERE y = term AND col = '*'
} {
- 0 0 0 {SCAN TABLE x2 (~1000000 rows)}
- 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)}
+ 0 0 0 {SCAN TABLE x2}
+ 0 1 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:}
a b c d e f g h i j k l
}
do_plansql_test 4.3 {
SELECT y FROM terms, x2 WHERE y = term AND col = '*'
} {
- 0 0 1 {SCAN TABLE x2 (~1000000 rows)}
- 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1: (~0 rows)}
+ 0 0 1 {SCAN TABLE x2}
+ 0 1 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 1:}
a b c d e f g h i j k l
}
do_plansql_test 4.4 {
SELECT y FROM x3, terms WHERE y = term AND col = '*'
} {
- 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}
- 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)}
+ 0 0 1 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}
+ 0 1 0 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?)}
a b c d e f g h i j k l
}
do_plansql_test 4.5 {
SELECT y FROM terms, x3 WHERE y = term AND occurrences>1 AND col = '*'
} {
- 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0: (~0 rows)}
- 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?) (~10 rows)}
+ 0 0 0 {SCAN TABLE terms VIRTUAL TABLE INDEX 0:}
+ 0 1 1 {SEARCH TABLE x3 USING COVERING INDEX i1 (y=?)}
a k l
}
@@ -519,4 +519,3 @@ do_test 8.2 {
} {1 {SQL logic error or missing database}}
finish_test
-
diff --git a/test/fts3aux2.test b/test/fts3aux2.test
new file mode 100644
index 0000000..e108fc4
--- /dev/null
+++ b/test/fts3aux2.test
@@ -0,0 +1,144 @@
+# 2011 January 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 FTS3 module.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !fts3 { finish_test ; return }
+set ::testprefix fts3aux2
+
+do_execsql_test 1.1 {
+ CREATE VIRTUAL TABLE t1 USING fts4(a, b, languageid=l);
+ INSERT INTO t1(a, b, l) VALUES
+ ('zero zero', 'zero zero', 0),
+ ('one two', 'three four', 1),
+ ('five six', 'seven eight', 2)
+ ;
+ CREATE VIRTUAL TABLE terms USING fts4aux(t1);
+} {}
+
+do_execsql_test 1.2.1 {
+ SELECT term, documents, occurrences, languageid FROM terms WHERE col = '*';
+} {zero 1 4 0}
+
+do_execsql_test 1.2.2 {
+ SELECT * FROM terms;
+} {zero * 1 4 zero 0 1 2 zero 1 1 2}
+
+do_execsql_test 1.2.3 {
+ SELECT * FROM terms WHERE languageid='';
+} {}
+
+do_execsql_test 1.2.4 {
+ SELECT * FROM terms WHERE languageid=-1;
+} {}
+
+do_execsql_test 1.2.5 {
+ SELECT * FROM terms WHERE languageid=9223372036854775807;
+} {}
+
+do_execsql_test 1.2.6 {
+ SELECT * FROM terms WHERE languageid=-9223372036854775808;
+} {}
+
+do_execsql_test 1.2.7 {
+ SELECT * FROM terms WHERE languageid=NULL;
+} {}
+
+do_execsql_test 1.3.1 {
+ SELECT term, documents, occurrences, languageid
+ FROM terms WHERE col = '*' AND languageid=1;
+} {
+ four 1 1 1 one 1 1 1 three 1 1 1 two 1 1 1
+}
+
+do_execsql_test 1.3.2 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid=1;
+} {
+ four * 1 1 1 four 1 1 1 1
+ one * 1 1 1 one 0 1 1 1
+ three * 1 1 1 three 1 1 1 1
+ two * 1 1 1 two 0 1 1 1
+}
+
+do_execsql_test 1.3.3 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid=1 AND term='zero'
+} {
+}
+
+do_execsql_test 1.3.4 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid='1' AND term='two'
+} {
+ two * 1 1 1 two 0 1 1 1
+}
+
+do_execsql_test 1.3.5 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid='+1' AND term>'four'
+} {
+ one * 1 1 1 one 0 1 1 1
+ three * 1 1 1 three 1 1 1 1
+ two * 1 1 1 two 0 1 1 1
+}
+
+do_execsql_test 1.4.1 {
+ SELECT term, documents, occurrences, languageid
+ FROM terms WHERE col = '*' AND languageid=2;
+} {
+ eight 1 1 2 five 1 1 2 seven 1 1 2 six 1 1 2
+}
+
+do_execsql_test 1.4.2 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid=2;
+} {
+ eight * 1 1 2 eight 1 1 1 2
+ five * 1 1 2 five 0 1 1 2
+ seven * 1 1 2 seven 1 1 1 2
+ six * 1 1 2 six 0 1 1 2
+}
+
+do_execsql_test 1.4.3 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE languageid=2 AND term='five';
+} {
+ five * 1 1 2 five 0 1 1 2
+}
+
+do_execsql_test 1.4.4 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE term='five' AND languageid=2
+} {
+ five * 1 1 2 five 0 1 1 2
+}
+
+do_execsql_test 1.4.5 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE term>='seven' AND languageid=2
+} {
+ seven * 1 1 2 seven 1 1 1 2
+ six * 1 1 2 six 0 1 1 2
+}
+
+do_execsql_test 1.4.6 {
+ SELECT term, col, documents, occurrences, languageid
+ FROM terms WHERE term>='e' AND term<'seven' AND languageid=2
+} {
+ eight * 1 1 2 eight 1 1 1 2
+ five * 1 1 2 five 0 1 1 2
+}
+
+finish_test
diff --git a/test/fts3corrupt.test b/test/fts3corrupt.test
index ea4c9a9..cb50e3e 100644
--- a/test/fts3corrupt.test
+++ b/test/fts3corrupt.test
@@ -166,4 +166,3 @@ do_test 5.3.1 { sqlite3_extended_errcode db } SQLITE_CORRUPT_VTAB
finish_test
-
diff --git a/test/fts3d.test b/test/fts3d.test
index 1ae992b..5c04ead 100644
--- a/test/fts3d.test
+++ b/test/fts3d.test
@@ -213,16 +213,17 @@ do_test fts3d-4.matches {
{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}
+puts [db eval {SELECT c FROM t1 } ]
+check_terms_all fts3d-4.1 {a four is test that this 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.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.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}
@@ -239,21 +240,20 @@ 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_terms fts3d-4.4 1 0 {a four is test that this 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.2 1 0 four {[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.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.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.
-breakpoint
do_test fts3d-4.5 {
execsql {
SELECT OPTIMIZE(t1) FROM t1 LIMIT 1;
@@ -355,6 +355,17 @@ do_test fts3d-6.5 {
SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
}
} {xyz_content xyz_segdir xyz_segments}
+
+# ALTER TABLE RENAME on an FTS3 table following an incr-merge op.
+#
+do_test fts3d-6.6 {
+ execsql { INSERT INTO xyz(xyz) VALUES('merge=2,2') }
+ sqlite3 db test.db
+ execsql {
+ ALTER TABLE xyz RENAME TO ott;
+ SELECT name FROM sqlite_master WHERE name GLOB '???_*' ORDER BY 1;
+ }
+} {ott_content ott_segdir ott_segments ott_stat}
finish_test
diff --git a/test/fts3defer2.test b/test/fts3defer2.test
index 337359a..87af524 100644
--- a/test/fts3defer2.test
+++ b/test/fts3defer2.test
@@ -59,6 +59,7 @@ do_execsql_test 1.2.1 {
SELECT content FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)';
} {{a b c d e f a x y}}
+
do_execsql_test 1.2.2 {
SELECT snippet(t1, '[', ']'), offsets(t1), mit(matchinfo(t1, 'pcxnal'))
FROM t1 WHERE t1 MATCH 'f (e NEAR/2 a)';
@@ -153,4 +154,3 @@ foreach {tn sql} {
finish_test
-
diff --git a/test/fts3defer3.test b/test/fts3defer3.test
new file mode 100644
index 0000000..a795e92
--- /dev/null
+++ b/test/fts3defer3.test
@@ -0,0 +1,84 @@
+# 2010 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 contains a very simple test to show that the deferred tokens
+# optimization is doing something.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+ifcapable !fts3||!fts4_deferred {
+ finish_test
+ return
+}
+set testprefix fts3defer3
+
+set nDoclist 3204
+set nDoc 800
+
+# Set up a database that contains 800 rows. Each row contains the document
+# "b b", except for the row with docid=200, which contains "a b". Hence
+# token "b" is extremely common and token "a" is not.
+#
+do_test 1.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts4;
+ BEGIN;
+ }
+ for {set i 1} {$i <= $nDoc} {incr i} {
+ set document "b b"
+ if {$i==200} { set document "a b" }
+ execsql { INSERT INTO t1 (docid, content) VALUES($i, $document) }
+ }
+ execsql COMMIT
+} {}
+
+# Check that the db contains two doclists. A small one for "a" and a
+# larger one for "b".
+#
+do_execsql_test 1.2 {
+ SELECT blockid, length(block) FROM t1_segments;
+} [list 1 8 2 $nDoclist]
+
+# Query for 'a b'. Although this test doesn't prove so, token "b" will
+# be deferred because of the very large associated doclist.
+#
+do_execsql_test 1.3 {
+ SELECT docid, content FROM t1 WHERE t1 MATCH 'a b';
+} {200 {a b}}
+
+# Zero out the doclist for token "b" within the database file. Now the
+# only queries that use token "b" that will work are those that defer
+# it. Any query that tries to use the doclist belonging to token "b"
+# will fail.
+#
+do_test 1.4 {
+ set fd [db incrblob t1_segments block 2]
+ puts -nonewline $fd [string repeat "\00" $nDoclist]
+ close $fd
+} {}
+
+# The first two queries succeed, as they defer token "b". The last one
+# fails, as it tries to load the corrupt doclist.
+#
+do_execsql_test 1.5 {
+ SELECT docid, content FROM t1 WHERE t1 MATCH 'a b';
+} {200 {a b}}
+do_execsql_test 1.6 {
+ SELECT count(*) FROM t1 WHERE t1 MATCH 'a b';
+} {1}
+do_catchsql_test 1.7 {
+ SELECT count(*) FROM t1 WHERE t1 MATCH 'b';
+} {1 {database disk image is malformed}}
+
+
+finish_test
diff --git a/test/fts3expr.test b/test/fts3expr.test
index e7c4f65..6e23faf 100644
--- a/test/fts3expr.test
+++ b/test/fts3expr.test
@@ -28,6 +28,7 @@ set sqlite_fts3_enable_parentheses 1
proc test_fts3expr {expr} {
db one {SELECT fts3_exprtest('simple', $expr, 'a', 'b', 'c')}
}
+
do_test fts3expr-1.0 {
test_fts3expr "abcd"
} {PHRASE 3 0 abcd}
@@ -495,5 +496,22 @@ do_test fts3expr-7.1 {
} {}
+do_test fts3expr-8.0 { test_fts3expr "(blah)" } {PHRASE 3 0 blah}
+do_test fts3expr-8.1 { test_fts3expr "(blah.)" } {PHRASE 3 0 blah}
+do_test fts3expr-8.2 { test_fts3expr "(blah,)" } {PHRASE 3 0 blah}
+do_test fts3expr-8.3 { test_fts3expr "(blah!)" } {PHRASE 3 0 blah}
+do_test fts3expr-8.4 { test_fts3expr "(blah-)" } {PHRASE 3 0 blah}
+
+do_test fts3expr-8.5 { test_fts3expr "((blah.))" } {PHRASE 3 0 blah}
+do_test fts3expr-8.6 { test_fts3expr "(((blah,)))" } {PHRASE 3 0 blah}
+do_test fts3expr-8.7 { test_fts3expr "((((blah!))))" } {PHRASE 3 0 blah}
+
+do_test fts3expr-8.8 { test_fts3expr "(,(blah-),)" } {PHRASE 3 0 blah}
+
set sqlite_fts3_enable_parentheses 0
+
+do_test fts3expr-9.1 {
+ test_fts3expr "f (e NEAR/2 a)"
+} {AND {PHRASE 3 0 f} {NEAR/2 {PHRASE 3 0 e} {PHRASE 3 0 a}}}
+
finish_test
diff --git a/test/fts3expr3.test b/test/fts3expr3.test
index e3cc261..a8d7319 100644
--- a/test/fts3expr3.test
+++ b/test/fts3expr3.test
@@ -204,7 +204,3 @@ do_faultsim_test fts3expr3-fault-1 -faults oom-* -body {
set sqlite_fts3_enable_parentheses 0
finish_test
-
-
-
-
diff --git a/test/fts3expr4.test b/test/fts3expr4.test
new file mode 100644
index 0000000..9473797
--- /dev/null
+++ b/test/fts3expr4.test
@@ -0,0 +1,57 @@
+# 2014 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 testing the FTS3 module.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts3expr4
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3||!icu {
+ finish_test
+ return
+}
+
+set sqlite_fts3_enable_parentheses 1
+
+proc test_icu_fts3expr {expr} {
+ db one {SELECT fts3_exprtest('icu', $expr, 'a', 'b', 'c')}
+}
+
+proc do_icu_expr_test {tn expr res} {
+ uplevel [list do_test $tn [list test_icu_fts3expr $expr] $res]
+}
+
+#-------------------------------------------------------------------------
+#
+do_icu_expr_test 1.1 "abcd" {PHRASE 3 0 abcd}
+do_icu_expr_test 1.2 " tag " {PHRASE 3 0 tag}
+do_icu_expr_test 1.3 {"x y z"} {PHRASE 3 0 x y z}
+do_icu_expr_test 1.4 {x OR y} {OR {PHRASE 3 0 x} {PHRASE 3 0 y}}
+do_icu_expr_test 1.5 {(x OR y)} {OR {PHRASE 3 0 x} {PHRASE 3 0 y}}
+do_icu_expr_test 1.6 { "(x OR y)" } {PHRASE 3 0 ( x or y )}
+
+# In "col:word", if "col" is not the name of a column, the entire thing
+# is passed to the tokenizer.
+#
+do_icu_expr_test 1.7 {a:word} {PHRASE 0 0 word}
+do_icu_expr_test 1.8 {d:word} {PHRASE 3 0 d:word}
+
+set sqlite_fts3_enable_parentheses 0
+
+do_icu_expr_test 2.1 {
+ f (e NEAR/2 a)
+} {AND {AND {AND {PHRASE 3 0 f} {PHRASE 3 0 (}} {NEAR/2 {PHRASE 3 0 e} {PHRASE 3 0 a}}} {PHRASE 3 0 )}}
+
+finish_test
+
diff --git a/test/fts3join.test b/test/fts3join.test
new file mode 100644
index 0000000..6436363
--- /dev/null
+++ b/test/fts3join.test
@@ -0,0 +1,66 @@
+# 2014 January 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 FTS3 module.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix fts3join
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE ft1 USING fts4(x);
+ INSERT INTO ft1 VALUES('aaa aaa');
+ INSERT INTO ft1 VALUES('aaa bbb');
+ INSERT INTO ft1 VALUES('bbb aaa');
+ INSERT INTO ft1 VALUES('bbb bbb');
+
+ CREATE TABLE t1(id, y);
+ INSERT INTO t1 VALUES(1, 'aaa');
+ INSERT INTO t1 VALUES(2, 'bbb');
+}
+
+do_execsql_test 1.1 {
+ SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1;
+} {1 2 3}
+
+do_execsql_test 1.2 {
+ SELECT docid FROM ft1, t1 WHERE ft1 MATCH y AND id=1 ORDER BY docid;
+} {1 2 3}
+
+do_execsql_test 2.0 {
+ CREATE VIRTUAL TABLE ft2 USING fts4(x);
+ CREATE VIRTUAL TABLE ft3 USING fts4(y);
+
+ INSERT INTO ft2 VALUES('abc');
+ INSERT INTO ft2 VALUES('def');
+ INSERT INTO ft3 VALUES('ghi');
+ INSERT INTO ft3 VALUES('abc');
+}
+
+do_execsql_test 2.1 { SELECT * FROM ft2, ft3 WHERE x MATCH y; } {abc abc}
+do_execsql_test 2.2 { SELECT * FROM ft2, ft3 WHERE y MATCH x; } {abc abc}
+do_execsql_test 2.3 { SELECT * FROM ft3, ft2 WHERE x MATCH y; } {abc abc}
+do_execsql_test 2.4 { SELECT * FROM ft3, ft2 WHERE y MATCH x; } {abc abc}
+
+do_catchsql_test 2.5 {
+ SELECT * FROM ft3, ft2 WHERE y MATCH x AND x MATCH y;
+} {1 {unable to use function MATCH in the requested context}}
+
+finish_test
+
+
diff --git a/test/fts3malloc.test b/test/fts3malloc.test
index 7eeee7f..ed30fe2 100644
--- a/test/fts3malloc.test
+++ b/test/fts3malloc.test
@@ -62,6 +62,9 @@ do_error_test fts3_malloc-1.5 {
do_write_test fts3_malloc-1.6 sqlite_master {
CREATE VIRTUAL TABLE ft6 USING fts3(a, b, tokenize porter)
}
+do_write_test fts3_malloc-1.7 sqlite_master {
+ CREATE VIRTUAL TABLE ft7 USING fts4(a, b, notindexed=b)
+}
# Test the xConnect/xDisconnect methods:
#db eval { ATTACH 'test2.db' AS aux }
@@ -81,6 +84,7 @@ do_test fts3_malloc-2.0 {
DROP TABLE ft3;
DROP TABLE ft4;
DROP TABLE ft6;
+ DROP TABLE ft7;
}
execsql { CREATE VIRTUAL TABLE ft USING fts3(a, b) }
for {set ii 1} {$ii < 32} {incr ii} {
@@ -301,4 +305,3 @@ do_write_test fts3_malloc-5.3 ft_content {
finish_test
-
diff --git a/test/fts3matchinfo.test b/test/fts3matchinfo.test
index 3998c9a..fd475af 100644
--- a/test/fts3matchinfo.test
+++ b/test/fts3matchinfo.test
@@ -426,5 +426,11 @@ do_execsql_test 8.3 {
{0 3 2 0 3 2 1 4 3} {1 3 2 1 3 2 1 4 3} {2 3 2 2 3 2 2 4 3}
}
-finish_test
+do_execsql_test 9.1 {
+ CREATE VIRTUAL TABLE ft2 USING fts4;
+ INSERT INTO ft2 VALUES('a b c d e');
+ INSERT INTO ft2 VALUES('f a b c d');
+ SELECT snippet(ft2, '[', ']', '', -1, 1) FROM ft2 WHERE ft2 MATCH 'c';
+} {{[c]} {[c]}}
+finish_test
diff --git a/test/fts3near.test b/test/fts3near.test
index 9276fa3..2d9981e 100644
--- a/test/fts3near.test
+++ b/test/fts3near.test
@@ -165,7 +165,6 @@ do_test fts3near-3.6 {
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}}
-breakpoint
do_test fts3near-3.7 {
execsql {
SELECT offsets(t1) FROM t1 WHERE content MATCH '"two four" NEAR/0 three'}
diff --git a/test/fts3prefix2.test b/test/fts3prefix2.test
index e3da3b7..e033fb6 100644
--- a/test/fts3prefix2.test
+++ b/test/fts3prefix2.test
@@ -59,4 +59,3 @@ do_execsql_test 1.3 {
}
finish_test
-
diff --git a/test/fts3query.test b/test/fts3query.test
index c8f8686..2d12351 100644
--- a/test/fts3query.test
+++ b/test/fts3query.test
@@ -118,26 +118,26 @@ do_test fts3query-4.1 {
do_eqp_test fts3query-4.2 {
SELECT t1.number FROM t1, ft WHERE t1.number=ft.rowid ORDER BY t1.date
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
- 0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)}
+ 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
+ 0 1 1 {SCAN TABLE ft VIRTUAL TABLE INDEX 1:}
}
do_eqp_test fts3query-4.3 {
SELECT t1.number FROM ft, t1 WHERE t1.number=ft.rowid ORDER BY t1.date
} {
- 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
- 0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1: (~0 rows)}
+ 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1}
+ 0 1 0 {SCAN TABLE ft VIRTUAL TABLE INDEX 1:}
}
do_eqp_test fts3query-4.4 {
SELECT t1.number FROM t1, bt WHERE t1.number=bt.rowid ORDER BY t1.date
} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
- 0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
+ 0 1 1 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?)}
}
do_eqp_test fts3query-4.5 {
SELECT t1.number FROM bt, t1 WHERE t1.number=bt.rowid ORDER BY t1.date
} {
- 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1 (~1000000 rows)}
- 0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 1 {SCAN TABLE t1 USING COVERING INDEX i1}
+ 0 1 0 {SEARCH TABLE bt USING INTEGER PRIMARY KEY (rowid=?)}
}
@@ -210,4 +210,3 @@ do_select_tests 6.2 {
finish_test
-
diff --git a/test/fts3shared.test b/test/fts3shared.test
index 5f75bd5..0943c05 100644
--- a/test/fts3shared.test
+++ b/test/fts3shared.test
@@ -1,3 +1,4 @@
+#
# 2010 September 17
#
# May you do good and not evil.
@@ -5,6 +6,9 @@
# 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 interactions between the FTS3/4 module
+# and shared-cache mode.
#
set testdir [file dirname $argv0]
@@ -14,6 +18,7 @@ ifcapable !fts3||!shared_cache {
finish_test
return
}
+set ::testprefix fts3shared
db close
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
@@ -67,6 +72,105 @@ do_test fts3shared-1.6 { sqlite3_get_autocommit db2 } 0
db close
db2 close
+#-------------------------------------------------------------------------
+# The following tests - fts3shared-2.* - test that unless FTS is bypassed
+# and the underlying tables accessed directly, it is not possible for an
+# SQLITE_LOCKED error to be enountered when committing an FTS transaction.
+#
+# Any SQLITE_LOCKED error should be returned when the fts4 (or fts4aux)
+# table is first read/written within a transaction, not later on.
+#
+set LOCKED {1 {database table is locked}}
+forcedelete test.db
+sqlite3 dbR test.db
+sqlite3 dbW test.db
+do_test 2.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t1 USING fts4;
+ CREATE TABLE t2ext(a, b);
+ CREATE VIRTUAL TABLE t2 USING fts4(content=t2ext);
+ CREATE VIRTUAL TABLE t1aux USING fts4aux(t1);
+ CREATE VIRTUAL TABLE t2aux USING fts4aux(t2);
+
+ INSERT INTO t1 VALUES('a b c');
+ INSERT INTO t2(rowid, a, b) VALUES(1, 'd e f', 'g h i');
+ } dbW
+} {}
+
+# Test that once [dbW] has written to the FTS table, no client may read
+# from the FTS or fts4aux table.
+do_test 2.2.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES('j k l');
+ } dbW
+ execsql BEGIN dbR
+} {}
+do_test 2.2.2 { catchsql "SELECT * FROM t1 WHERE rowid=1" dbR } $LOCKED
+do_test 2.2.3 { catchsql "SELECT * FROM t1 WHERE t1 MATCH 'a'" dbR } $LOCKED
+do_test 2.2.4 { catchsql "SELECT rowid FROM t1 WHERE t1 MATCH 'a'" dbR } $LOCKED
+do_test 2.2.5 { catchsql "SELECT * FROM t1" dbR } $LOCKED
+do_test 2.2.6 { catchsql "SELECT * FROM t1aux" dbR } $LOCKED
+do_test 2.2.7 { execsql COMMIT dbW } {}
+do_test 2.2.8 { execsql COMMIT dbR } {}
+
+# Same test as 2.2.*, except with a content= table.
+#
+do_test 2.3.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO t2(rowid, a, b) VALUES(2, 'j k l', 'm n o');
+ } dbW
+ execsql BEGIN dbR
+} {}
+do_test 2.3.3 { catchsql "SELECT * FROM t2 WHERE t2 MATCH 'a'" dbR } $LOCKED
+do_test 2.3.4 { catchsql "SELECT rowid FROM t2 WHERE t2 MATCH 'a'" dbR } $LOCKED
+do_test 2.3.6 { catchsql "SELECT * FROM t2aux" dbR } $LOCKED
+do_test 2.3.7 { execsql COMMIT dbW } {}
+do_test 2.3.8 { execsql COMMIT dbR } {}
+
+# Test that once a connection has read from the FTS or fts4aux table,
+# another connection may not write to the FTS table.
+#
+foreach {tn sql} {
+ 1 "SELECT * FROM t1 WHERE rowid=1"
+ 2 "SELECT * FROM t1 WHERE t1 MATCH 'a'"
+ 3 "SELECT rowid FROM t1 WHERE t1 MATCH 'a'"
+ 4 "SELECT * FROM t1"
+ 5 "SELECT * FROM t1aux"
+} {
+
+ do_test 2.4.$tn {
+ execsql BEGIN dbR
+ execsql $::sql dbR
+ execsql BEGIN dbW
+ catchsql "INSERT INTO t1 VALUES('p q r')" dbW
+ } $LOCKED
+
+ execsql ROLLBACK dbR
+ execsql ROLLBACK dbW
+}
+
+# Same test as 2.4.*, except with a content= table.
+#
+foreach {tn sql} {
+ 2 "SELECT * FROM t2 WHERE t2 MATCH 'a'"
+ 3 "SELECT rowid FROM t2 WHERE t2 MATCH 'a'"
+ 5 "SELECT * FROM t2aux"
+} {
+
+ do_test 2.5.$tn {
+ execsql BEGIN dbR
+ execsql $::sql dbR
+ execsql BEGIN dbW
+ catchsql "INSERT INTO t2(rowid, a, b) VALUES(3, 's t u', 'v w x')" dbW
+ } $LOCKED
+
+ execsql ROLLBACK dbR
+ execsql ROLLBACK dbW
+}
+
+dbW close
+dbR close
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
-
diff --git a/test/fts3snippet.test b/test/fts3snippet.test
index b8646cd..415251d 100644
--- a/test/fts3snippet.test
+++ b/test/fts3snippet.test
@@ -16,6 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix fts3snippet
# If SQLITE_ENABLE_FTS3 is not defined, omit this file.
ifcapable !fts3 { finish_test ; return }
@@ -138,7 +139,7 @@ foreach {DO_MALLOC_TEST enc} {
# Set variable $T to the test name prefix for this iteration of the loop.
#
- set T "fts3snippet-$enc"
+ set T "fts3snippet-1.$enc"
##########################################################################
# Test the offset function.
@@ -432,10 +433,10 @@ foreach {DO_MALLOC_TEST enc} {
{2 2 1 3 3 3 6 3 0 0 0 2 3 2}
}]
- # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the
- # "query by rowid" or "linear scan" strategies, then the snippet and
- # offsets both return an empty string, and the matchinfo function
- # returns a blob value zero bytes in size.
+ # EVIDENCE-OF: R-40630-02268 If used within a SELECT that uses the
+ # "query by rowid" or "linear scan" strategies, then the snippet and
+ # offsets both return an empty string, and the matchinfo function
+ # returns a blob value zero bytes in size.
#
set r 1000000 ;# A rowid that exists in table ft
do_select_test $T.10.0 { SELECT rowid FROM ft WHERE rowid = $r } $r
@@ -459,5 +460,65 @@ foreach {DO_MALLOC_TEST enc} {
} {0 blob}
}
+#-------------------------------------------------------------------------
+# Test an interaction between the snippet() function and OR clauses.
+#
+do_execsql_test 2.1 {
+ CREATE VIRTUAL TABLE t2 USING fts4;
+ INSERT INTO t2 VALUES('one two three four five');
+ INSERT INTO t2 VALUES('two three four five one');
+ INSERT INTO t2 VALUES('three four five one two');
+ INSERT INTO t2 VALUES('four five one two three');
+ INSERT INTO t2 VALUES('five one two three four');
+}
+
+do_execsql_test 2.2 {
+ SELECT snippet(t2, '[', ']') FROM t2 WHERE t2 MATCH 'one OR (four AND six)'
+} {
+ {[one] two three [four] five}
+ {two three [four] five [one]}
+ {three [four] five [one] two}
+ {[four] five [one] two three}
+ {five [one] two three [four]}
+}
+
+do_execsql_test 2.3 {
+ SELECT snippet(t2, '[', ']') FROM t2
+ WHERE t2 MATCH 'one OR (four AND six)'
+ ORDER BY docid DESC
+} {
+ {five [one] two three [four]}
+ {[four] five [one] two three}
+ {three [four] five [one] two}
+ {two three [four] five [one]}
+ {[one] two three [four] five}
+}
+
+do_execsql_test 2.4 {
+ INSERT INTO t2 VALUES('six');
+}
+
+do_execsql_test 2.5 {
+ SELECT snippet(t2, '[', ']') FROM t2 WHERE t2 MATCH 'one OR (four AND six)'
+} {
+ {[one] two three [four] five}
+ {two three [four] five [one]}
+ {three [four] five [one] two}
+ {[four] five [one] two three}
+ {five [one] two three [four]}
+}
+
+do_execsql_test 2.6 {
+ SELECT snippet(t2, '[', ']') FROM t2
+ WHERE t2 MATCH 'one OR (four AND six)'
+ ORDER BY docid DESC
+} {
+ {five [one] two three [four]}
+ {[four] five [one] two three}
+ {three [four] five [one] two}
+ {two three [four] five [one]}
+ {[one] two three [four] five}
+}
+
set sqlite_fts3_enable_parentheses 0
finish_test
diff --git a/test/fts3sort.test b/test/fts3sort.test
index be75604..218bf3e 100644
--- a/test/fts3sort.test
+++ b/test/fts3sort.test
@@ -182,4 +182,3 @@ do_execsql_test 3.2 {
finish_test
-
diff --git a/test/fts3tok1.test b/test/fts3tok1.test
index 98e55a0..e6fbbe1 100644
--- a/test/fts3tok1.test
+++ b/test/fts3tok1.test
@@ -90,7 +90,6 @@ do_execsql_test 1.13.1 {
INSERT INTO c1(x) VALUES('a b c');
INSERT INTO c1(x) VALUES('d e f');
}
-breakpoint
do_execsql_test 1.13.2 {
SELECT * FROM c1, t1 WHERE input = x AND c1.rowid=t1.rowid;
} {
@@ -113,5 +112,3 @@ do_catchsql_test 2.1 {
finish_test
-
-
diff --git a/test/fts3tok_err.test b/test/fts3tok_err.test
index aaa7272..df0d4be 100644
--- a/test/fts3tok_err.test
+++ b/test/fts3tok_err.test
@@ -45,5 +45,3 @@ do_faultsim_test fts3tok_err-2 -faults oom* -prep {
finish_test
-
-
diff --git a/test/fts3varint.test b/test/fts3varint.test
new file mode 100644
index 0000000..ca0189d
--- /dev/null
+++ b/test/fts3varint.test
@@ -0,0 +1,118 @@
+# 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 $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts3varint
+
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+proc test_list {list} {
+ foreach n $list { fts3_test_varint $n }
+}
+
+proc do_fts3_varint_test {tn list} {
+ uplevel [list do_test $tn [list test_list $list] {}]
+}
+
+do_fts3_varint_test 1.0 {
+ 1 10 100 1000 10000 100000 1000000
+ 10000000 100000000 1000000000 10000000000
+ 100000000000 1000000000000 10000000000000
+}
+
+do_fts3_varint_test 1.1 {
+ -1 -10 -100 -1000 -10000 -100000 -1000000
+ -10000000 -100000000 -1000000000 -10000000000
+ -100000000000 -1000000000000 -10000000000000
+}
+
+do_fts3_varint_test 2.0 { 0 1 2 }
+do_fts3_varint_test 2.1 { 1 2 3 }
+do_fts3_varint_test 2.2 { 3 4 5 }
+do_fts3_varint_test 2.3 { 7 8 9 }
+do_fts3_varint_test 2.4 { 15 16 17 }
+do_fts3_varint_test 2.5 { 31 32 33 }
+do_fts3_varint_test 2.6 { 63 64 65 }
+do_fts3_varint_test 2.7 { 127 128 129 }
+do_fts3_varint_test 2.8 { 255 256 257 }
+do_fts3_varint_test 2.9 { 511 512 513 }
+do_fts3_varint_test 2.10 { 1023 1024 1025 }
+do_fts3_varint_test 2.11 { 2047 2048 2049 }
+do_fts3_varint_test 2.12 { 4095 4096 4097 }
+do_fts3_varint_test 2.13 { 8191 8192 8193 }
+do_fts3_varint_test 2.14 { 16383 16384 16385 }
+do_fts3_varint_test 2.15 { 32767 32768 32769 }
+do_fts3_varint_test 2.16 { 65535 65536 65537 }
+do_fts3_varint_test 2.17 { 131071 131072 131073 }
+do_fts3_varint_test 2.18 { 262143 262144 262145 }
+do_fts3_varint_test 2.19 { 524287 524288 524289 }
+do_fts3_varint_test 2.20 { 1048575 1048576 1048577 }
+do_fts3_varint_test 2.21 { 2097151 2097152 2097153 }
+do_fts3_varint_test 2.22 { 4194303 4194304 4194305 }
+do_fts3_varint_test 2.23 { 8388607 8388608 8388609 }
+do_fts3_varint_test 2.24 { 16777215 16777216 16777217 }
+do_fts3_varint_test 2.25 { 33554431 33554432 33554433 }
+do_fts3_varint_test 2.26 { 67108863 67108864 67108865 }
+do_fts3_varint_test 2.27 { 134217727 134217728 134217729 }
+do_fts3_varint_test 2.28 { 268435455 268435456 268435457 }
+do_fts3_varint_test 2.29 { 536870911 536870912 536870913 }
+do_fts3_varint_test 2.30 { 1073741823 1073741824 1073741825 }
+do_fts3_varint_test 2.31 { 2147483647 2147483648 2147483649 }
+do_fts3_varint_test 2.32 { 4294967295 4294967296 4294967297 }
+do_fts3_varint_test 2.33 { 8589934591 8589934592 8589934593 }
+do_fts3_varint_test 2.34 { 17179869183 17179869184 17179869185 }
+do_fts3_varint_test 2.35 { 34359738367 34359738368 34359738369 }
+do_fts3_varint_test 2.36 { 68719476735 68719476736 68719476737 }
+do_fts3_varint_test 2.37 { 137438953471 137438953472 137438953473 }
+do_fts3_varint_test 2.38 { 274877906943 274877906944 274877906945 }
+do_fts3_varint_test 2.39 { 549755813887 549755813888 549755813889 }
+do_fts3_varint_test 2.40 { 1099511627775 1099511627776 1099511627777 }
+do_fts3_varint_test 2.41 { 2199023255551 2199023255552 2199023255553 }
+do_fts3_varint_test 2.42 { 4398046511103 4398046511104 4398046511105 }
+do_fts3_varint_test 2.43 { 8796093022207 8796093022208 8796093022209 }
+do_fts3_varint_test 2.44 { 17592186044415 17592186044416 17592186044417 }
+do_fts3_varint_test 2.45 { 35184372088831 35184372088832 35184372088833 }
+do_fts3_varint_test 2.46 { 70368744177663 70368744177664 70368744177665 }
+do_fts3_varint_test 2.47 { 140737488355327 140737488355328 140737488355329 }
+do_fts3_varint_test 2.48 { 281474976710655 281474976710656 281474976710657 }
+do_fts3_varint_test 2.49 { 562949953421311 562949953421312 562949953421313 }
+do_fts3_varint_test 2.50 { 1125899906842623 1125899906842624 1125899906842625 }
+do_fts3_varint_test 2.51 { 2251799813685247 2251799813685248 2251799813685249 }
+do_fts3_varint_test 2.52 { 4503599627370495 4503599627370496 4503599627370497 }
+do_fts3_varint_test 2.53 { 9007199254740991 9007199254740992 9007199254740993 }
+do_fts3_varint_test 2.54 {
+ 18014398509481983 18014398509481984 18014398509481985 }
+do_fts3_varint_test 2.55 {
+ 36028797018963967 36028797018963968 36028797018963969 }
+do_fts3_varint_test 2.56 {
+ 72057594037927935 72057594037927936 72057594037927937 }
+do_fts3_varint_test 2.57 {
+ 144115188075855871 144115188075855872 144115188075855873 }
+do_fts3_varint_test 2.58 {
+ 288230376151711743 288230376151711744 288230376151711745 }
+do_fts3_varint_test 2.59 {
+ 576460752303423487 576460752303423488 576460752303423489 }
+do_fts3_varint_test 2.60 {
+ 1152921504606846975 1152921504606846976 1152921504606846977 }
+do_fts3_varint_test 2.61 {
+ 2305843009213693951 2305843009213693952 2305843009213693953 }
+do_fts3_varint_test 2.62 {
+ 4611686018427387903 4611686018427387904 4611686018427387905 }
+do_fts3_varint_test 2.63 {
+ 9223372036854775807 9223372036854775808 9223372036854775809 }
+
+do_fts3_varint_test 3.0 { 18446744073709551615 -18446744073709551615 }
+
+finish_test
diff --git a/test/fts4aa.test b/test/fts4aa.test
index 2e6baf8..88550c9 100644
--- a/test/fts4aa.test
+++ b/test/fts4aa.test
@@ -22,1548 +22,10 @@ ifcapable !fts3 {
return
}
-# This procedure fills an existing FTS3/FTS4 table with many entries.
-# The table needs to have a single column (other than docid) named "words".
+# Create the fts_kjv_genesis procedure which fills and FTS3/4 table with
+# the complete text of the Book of Genesis.
#
-proc fts4aa_fill_table {} {
-db eval {
-BEGIN TRANSACTION;
-INSERT INTO t1(docid,words) VALUES(1001001,'In the beginning God created the heaven and the earth.');
-INSERT INTO t1(docid,words) VALUES(1001002,'And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.');
-INSERT INTO t1(docid,words) VALUES(1001003,'And God said, Let there be light: and there was light.');
-INSERT INTO t1(docid,words) VALUES(1001004,'And God saw the light, that it was good: and God divided the light from the darkness.');
-INSERT INTO t1(docid,words) VALUES(1001005,'And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.');
-INSERT INTO t1(docid,words) VALUES(1001006,'And God said, Let there be a firmament in the midst of the waters, and let it divide the waters from the waters.');
-INSERT INTO t1(docid,words) VALUES(1001007,'And God made the firmament, and divided the waters which were under the firmament from the waters which were above the firmament: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001008,'And God called the firmament Heaven. And the evening and the morning were the second day.');
-INSERT INTO t1(docid,words) VALUES(1001009,'And God said, Let the waters under the heaven be gathered together unto one place, and let the dry land appear: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001010,'And God called the dry land Earth; and the gathering together of the waters called he Seas: and God saw that it was good.');
-INSERT INTO t1(docid,words) VALUES(1001011,'And God said, Let the earth bring forth grass, the herb yielding seed, and the fruit tree yielding fruit after his kind, whose seed is in itself, upon the earth: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001012,'And the earth brought forth grass, and herb yielding seed after his kind, and the tree yielding fruit, whose seed was in itself, after his kind: and God saw that it was good.');
-INSERT INTO t1(docid,words) VALUES(1001013,'And the evening and the morning were the third day.');
-INSERT INTO t1(docid,words) VALUES(1001014,'And God said, Let there be lights in the firmament of the heaven to divide the day from the night; and let them be for signs, and for seasons, and for days, and years:');
-INSERT INTO t1(docid,words) VALUES(1001015,'And let them be for lights in the firmament of the heaven to give light upon the earth: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001016,'And God made two great lights; the greater light to rule the day, and the lesser light to rule the night: he made the stars also.');
-INSERT INTO t1(docid,words) VALUES(1001017,'And God set them in the firmament of the heaven to give light upon the earth,');
-INSERT INTO t1(docid,words) VALUES(1001018,'And to rule over the day and over the night, and to divide the light from the darkness: and God saw that it was good.');
-INSERT INTO t1(docid,words) VALUES(1001019,'And the evening and the morning were the fourth day.');
-INSERT INTO t1(docid,words) VALUES(1001020,'And God said, Let the waters bring forth abundantly the moving creature that hath life, and fowl that may fly above the earth in the open firmament of heaven.');
-INSERT INTO t1(docid,words) VALUES(1001021,'And God created great whales, and every living creature that moveth, which the waters brought forth abundantly, after their kind, and every winged fowl after his kind: and God saw that it was good.');
-INSERT INTO t1(docid,words) VALUES(1001022,'And God blessed them, saying, Be fruitful, and multiply, and fill the waters in the seas, and let fowl multiply in the earth.');
-INSERT INTO t1(docid,words) VALUES(1001023,'And the evening and the morning were the fifth day.');
-INSERT INTO t1(docid,words) VALUES(1001024,'And God said, Let the earth bring forth the living creature after his kind, cattle, and creeping thing, and beast of the earth after his kind: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001025,'And God made the beast of the earth after his kind, and cattle after their kind, and every thing that creepeth upon the earth after his kind: and God saw that it was good.');
-INSERT INTO t1(docid,words) VALUES(1001026,'And God said, Let us make man in our image, after our likeness: and let them have dominion over the fish of the sea, and over the fowl of the air, and over the cattle, and over all the earth, and over every creeping thing that creepeth upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1001027,'So God created man in his own image, in the image of God created he him; male and female created he them.');
-INSERT INTO t1(docid,words) VALUES(1001028,'And God blessed them, and God said unto them, Be fruitful, and multiply, and replenish the earth, and subdue it: and have dominion over the fish of the sea, and over the fowl of the air, and over every living thing that moveth upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1001029,'And God said, Behold, I have given you every herb bearing seed, which is upon the face of all the earth, and every tree, in the which is the fruit of a tree yielding seed; to you it shall be for meat.');
-INSERT INTO t1(docid,words) VALUES(1001030,'And to every beast of the earth, and to every fowl of the air, and to every thing that creepeth upon the earth, wherein there is life, I have given every green herb for meat: and it was so.');
-INSERT INTO t1(docid,words) VALUES(1001031,'And God saw every thing that he had made, and, behold, it was very good. And the evening and the morning were the sixth day.');
-INSERT INTO t1(docid,words) VALUES(1002001,'Thus the heavens and the earth were finished, and all the host of them.');
-INSERT INTO t1(docid,words) VALUES(1002002,'And on the seventh day God ended his work which he had made; and he rested on the seventh day from all his work which he had made.');
-INSERT INTO t1(docid,words) VALUES(1002003,'And God blessed the seventh day, and sanctified it: because that in it he had rested from all his work which God created and made.');
-INSERT INTO t1(docid,words) VALUES(1002004,'These are the generations of the heavens and of the earth when they were created, in the day that the LORD God made the earth and the heavens,');
-INSERT INTO t1(docid,words) VALUES(1002005,'And every plant of the field before it was in the earth, and every herb of the field before it grew: for the LORD God had not caused it to rain upon the earth, and there was not a man to till the ground.');
-INSERT INTO t1(docid,words) VALUES(1002006,'But there went up a mist from the earth, and watered the whole face of the ground.');
-INSERT INTO t1(docid,words) VALUES(1002007,'And the LORD God formed man of the dust of the ground, and breathed into his nostrils the breath of life; and man became a living soul.');
-INSERT INTO t1(docid,words) VALUES(1002008,'And the LORD God planted a garden eastward in Eden; and there he put the man whom he had formed.');
-INSERT INTO t1(docid,words) VALUES(1002009,'And out of the ground made the LORD God to grow every tree that is pleasant to the sight, and good for food; the tree of life also in the midst of the garden, and the tree of knowledge of good and evil.');
-INSERT INTO t1(docid,words) VALUES(1002010,'And a river went out of Eden to water the garden; and from thence it was parted, and became into four heads.');
-INSERT INTO t1(docid,words) VALUES(1002011,'The name of the first is Pison: that is it which compasseth the whole land of Havilah, where there is gold;');
-INSERT INTO t1(docid,words) VALUES(1002012,'And the gold of that land is good: there is bdellium and the onyx stone.');
-INSERT INTO t1(docid,words) VALUES(1002013,'And the name of the second river is Gihon: the same is it that compasseth the whole land of Ethiopia.');
-INSERT INTO t1(docid,words) VALUES(1002014,'And the name of the third river is Hiddekel: that is it which goeth toward the east of Assyria. And the fourth river is Euphrates.');
-INSERT INTO t1(docid,words) VALUES(1002015,'And the LORD God took the man, and put him into the garden of Eden to dress it and to keep it.');
-INSERT INTO t1(docid,words) VALUES(1002016,'And the LORD God commanded the man, saying, Of every tree of the garden thou mayest freely eat:');
-INSERT INTO t1(docid,words) VALUES(1002017,'But of the tree of the knowledge of good and evil, thou shalt not eat of it: for in the day that thou eatest thereof thou shalt surely die.');
-INSERT INTO t1(docid,words) VALUES(1002018,'And the LORD God said, It is not good that the man should be alone; I will make him an help meet for him.');
-INSERT INTO t1(docid,words) VALUES(1002019,'And out of the ground the LORD God formed every beast of the field, and every fowl of the air; and brought them unto Adam to see what he would call them: and whatsoever Adam called every living creature, that was the name thereof.');
-INSERT INTO t1(docid,words) VALUES(1002020,'And Adam gave names to all cattle, and to the fowl of the air, and to every beast of the field; but for Adam there was not found an help meet for him.');
-INSERT INTO t1(docid,words) VALUES(1002021,'And the LORD God caused a deep sleep to fall upon Adam, and he slept: and he took one of his ribs, and closed up the flesh instead thereof;');
-INSERT INTO t1(docid,words) VALUES(1002022,'And the rib, which the LORD God had taken from man, made he a woman, and brought her unto the man.');
-INSERT INTO t1(docid,words) VALUES(1002023,'And Adam said, This is now bone of my bones, and flesh of my flesh: she shall be called Woman, because she was taken out of Man.');
-INSERT INTO t1(docid,words) VALUES(1002024,'Therefore shall a man leave his father and his mother, and shall cleave unto his wife: and they shall be one flesh.');
-INSERT INTO t1(docid,words) VALUES(1002025,'And they were both naked, the man and his wife, and were not ashamed.');
-INSERT INTO t1(docid,words) VALUES(1003001,'Now the serpent was more subtil than any beast of the field which the LORD God had made. And he said unto the woman, Yea, hath God said, Ye shall not eat of every tree of the garden?');
-INSERT INTO t1(docid,words) VALUES(1003002,'And the woman said unto the serpent, We may eat of the fruit of the trees of the garden:');
-INSERT INTO t1(docid,words) VALUES(1003003,'But of the fruit of the tree which is in the midst of the garden, God hath said, Ye shall not eat of it, neither shall ye touch it, lest ye die.');
-INSERT INTO t1(docid,words) VALUES(1003004,'And the serpent said unto the woman, Ye shall not surely die:');
-INSERT INTO t1(docid,words) VALUES(1003005,'For God doth know that in the day ye eat thereof, then your eyes shall be opened, and ye shall be as gods, knowing good and evil.');
-INSERT INTO t1(docid,words) VALUES(1003006,'And when the woman saw that the tree was good for food, and that it was pleasant to the eyes, and a tree to be desired to make one wise, she took of the fruit thereof, and did eat, and gave also unto her husband with her; and he did eat.');
-INSERT INTO t1(docid,words) VALUES(1003007,'And the eyes of them both were opened, and they knew that they were naked; and they sewed fig leaves together, and made themselves aprons.');
-INSERT INTO t1(docid,words) VALUES(1003008,'And they heard the voice of the LORD God walking in the garden in the cool of the day: and Adam and his wife hid themselves from the presence of the LORD God amongst the trees of the garden.');
-INSERT INTO t1(docid,words) VALUES(1003009,'And the LORD God called unto Adam, and said unto him, Where art thou?');
-INSERT INTO t1(docid,words) VALUES(1003010,'And he said, I heard thy voice in the garden, and I was afraid, because I was naked; and I hid myself.');
-INSERT INTO t1(docid,words) VALUES(1003011,'And he said, Who told thee that thou wast naked? Hast thou eaten of the tree, whereof I commanded thee that thou shouldest not eat?');
-INSERT INTO t1(docid,words) VALUES(1003012,'And the man said, The woman whom thou gavest to be with me, she gave me of the tree, and I did eat.');
-INSERT INTO t1(docid,words) VALUES(1003013,'And the LORD God said unto the woman, What is this that thou hast done? And the woman said, The serpent beguiled me, and I did eat.');
-INSERT INTO t1(docid,words) VALUES(1003014,'And the LORD God said unto the serpent, Because thou hast done this, thou art cursed above all cattle, and above every beast of the field; upon thy belly shalt thou go, and dust shalt thou eat all the days of thy life:');
-INSERT INTO t1(docid,words) VALUES(1003015,'And I will put enmity between thee and the woman, and between thy seed and her seed; it shall bruise thy head, and thou shalt bruise his heel.');
-INSERT INTO t1(docid,words) VALUES(1003016,'Unto the woman he said, I will greatly multiply thy sorrow and thy conception; in sorrow thou shalt bring forth children; and thy desire shall be to thy husband, and he shall rule over thee.');
-INSERT INTO t1(docid,words) VALUES(1003017,'And unto Adam he said, Because thou hast hearkened unto the voice of thy wife, and hast eaten of the tree, of which I commanded thee, saying, Thou shalt not eat of it: cursed is the ground for thy sake; in sorrow shalt thou eat of it all the days of thy life;');
-INSERT INTO t1(docid,words) VALUES(1003018,'Thorns also and thistles shall it bring forth to thee; and thou shalt eat the herb of the field;');
-INSERT INTO t1(docid,words) VALUES(1003019,'In the sweat of thy face shalt thou eat bread, till thou return unto the ground; for out of it wast thou taken: for dust thou art, and unto dust shalt thou return.');
-INSERT INTO t1(docid,words) VALUES(1003020,'And Adam called his wife''s name Eve; because she was the mother of all living.');
-INSERT INTO t1(docid,words) VALUES(1003021,'Unto Adam also and to his wife did the LORD God make coats of skins, and clothed them.');
-INSERT INTO t1(docid,words) VALUES(1003022,'And the LORD God said, Behold, the man is become as one of us, to know good and evil: and now, lest he put forth his hand, and take also of the tree of life, and eat, and live for ever:');
-INSERT INTO t1(docid,words) VALUES(1003023,'Therefore the LORD God sent him forth from the garden of Eden, to till the ground from whence he was taken.');
-INSERT INTO t1(docid,words) VALUES(1003024,'So he drove out the man; and he placed at the east of the garden of Eden Cherubims, and a flaming sword which turned every way, to keep the way of the tree of life.');
-INSERT INTO t1(docid,words) VALUES(1004001,'And Adam knew Eve his wife; and she conceived, and bare Cain, and said, I have gotten a man from the LORD.');
-INSERT INTO t1(docid,words) VALUES(1004002,'And she again bare his brother Abel. And Abel was a keeper of sheep, but Cain was a tiller of the ground.');
-INSERT INTO t1(docid,words) VALUES(1004003,'And in process of time it came to pass, that Cain brought of the fruit of the ground an offering unto the LORD.');
-INSERT INTO t1(docid,words) VALUES(1004004,'And Abel, he also brought of the firstlings of his flock and of the fat thereof. And the LORD had respect unto Abel and to his offering:');
-INSERT INTO t1(docid,words) VALUES(1004005,'But unto Cain and to his offering he had not respect. And Cain was very wroth, and his countenance fell.');
-INSERT INTO t1(docid,words) VALUES(1004006,'And the LORD said unto Cain, Why art thou wroth? and why is thy countenance fallen?');
-INSERT INTO t1(docid,words) VALUES(1004007,'If thou doest well, shalt thou not be accepted? and if thou doest not well, sin lieth at the door. And unto thee shall be his desire, and thou shalt rule over him.');
-INSERT INTO t1(docid,words) VALUES(1004008,'And Cain talked with Abel his brother: and it came to pass, when they were in the field, that Cain rose up against Abel his brother, and slew him.');
-INSERT INTO t1(docid,words) VALUES(1004009,'And the LORD said unto Cain, Where is Abel thy brother? And he said, I know not: Am I my brother''s keeper?');
-INSERT INTO t1(docid,words) VALUES(1004010,'And he said, What hast thou done? the voice of thy brother''s blood crieth unto me from the ground.');
-INSERT INTO t1(docid,words) VALUES(1004011,'And now art thou cursed from the earth, which hath opened her mouth to receive thy brother''s blood from thy hand;');
-INSERT INTO t1(docid,words) VALUES(1004012,'When thou tillest the ground, it shall not henceforth yield unto thee her strength; a fugitive and a vagabond shalt thou be in the earth.');
-INSERT INTO t1(docid,words) VALUES(1004013,'And Cain said unto the LORD, My punishment is greater than I can bear.');
-INSERT INTO t1(docid,words) VALUES(1004014,'Behold, thou hast driven me out this day from the face of the earth; and from thy face shall I be hid; and I shall be a fugitive and a vagabond in the earth; and it shall come to pass, that every one that findeth me shall slay me.');
-INSERT INTO t1(docid,words) VALUES(1004015,'And the LORD said unto him, Therefore whosoever slayeth Cain, vengeance shall be taken on him sevenfold. And the LORD set a mark upon Cain, lest any finding him should kill him.');
-INSERT INTO t1(docid,words) VALUES(1004016,'And Cain went out from the presence of the LORD, and dwelt in the land of Nod, on the east of Eden.');
-INSERT INTO t1(docid,words) VALUES(1004017,'And Cain knew his wife; and she conceived, and bare Enoch: and he builded a city, and called the name of the city, after the name of his son, Enoch.');
-INSERT INTO t1(docid,words) VALUES(1004018,'And unto Enoch was born Irad: and Irad begat Mehujael: and Mehujael begat Methusael: and Methusael begat Lamech.');
-INSERT INTO t1(docid,words) VALUES(1004019,'And Lamech took unto him two wives: the name of the one was Adah, and the name of the other Zillah.');
-INSERT INTO t1(docid,words) VALUES(1004020,'And Adah bare Jabal: he was the father of such as dwell in tents, and of such as have cattle.');
-INSERT INTO t1(docid,words) VALUES(1004021,'And his brother''s name was Jubal: he was the father of all such as handle the harp and organ.');
-INSERT INTO t1(docid,words) VALUES(1004022,'And Zillah, she also bare Tubalcain, an instructer of every artificer in brass and iron: and the sister of Tubalcain was Naamah.');
-INSERT INTO t1(docid,words) VALUES(1004023,'And Lamech said unto his wives, Adah and Zillah, Hear my voice; ye wives of Lamech, hearken unto my speech: for I have slain a man to my wounding, and a young man to my hurt.');
-INSERT INTO t1(docid,words) VALUES(1004024,'If Cain shall be avenged sevenfold, truly Lamech seventy and sevenfold.');
-INSERT INTO t1(docid,words) VALUES(1004025,'And Adam knew his wife again; and she bare a son, and called his name Seth: For God, said she, hath appointed me another seed instead of Abel, whom Cain slew.');
-INSERT INTO t1(docid,words) VALUES(1004026,'And to Seth, to him also there was born a son; and he called his name Enos: then began men to call upon the name of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1005001,'This is the book of the generations of Adam. In the day that God created man, in the likeness of God made he him;');
-INSERT INTO t1(docid,words) VALUES(1005002,'Male and female created he them; and blessed them, and called their name Adam, in the day when they were created.');
-INSERT INTO t1(docid,words) VALUES(1005003,'And Adam lived an hundred and thirty years, and begat a son in his own likeness, and after his image; and called his name Seth:');
-INSERT INTO t1(docid,words) VALUES(1005004,'And the days of Adam after he had begotten Seth were eight hundred years: and he begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005005,'And all the days that Adam lived were nine hundred and thirty years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005006,'And Seth lived an hundred and five years, and begat Enos:');
-INSERT INTO t1(docid,words) VALUES(1005007,'And Seth lived after he begat Enos eight hundred and seven years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005008,'And all the days of Seth were nine hundred and twelve years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005009,'And Enos lived ninety years, and begat Cainan:');
-INSERT INTO t1(docid,words) VALUES(1005010,'And Enos lived after he begat Cainan eight hundred and fifteen years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005011,'And all the days of Enos were nine hundred and five years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005012,'And Cainan lived seventy years and begat Mahalaleel:');
-INSERT INTO t1(docid,words) VALUES(1005013,'And Cainan lived after he begat Mahalaleel eight hundred and forty years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005014,'And all the days of Cainan were nine hundred and ten years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005015,'And Mahalaleel lived sixty and five years, and begat Jared:');
-INSERT INTO t1(docid,words) VALUES(1005016,'And Mahalaleel lived after he begat Jared eight hundred and thirty years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005017,'And all the days of Mahalaleel were eight hundred ninety and five years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005018,'And Jared lived an hundred sixty and two years, and he begat Enoch:');
-INSERT INTO t1(docid,words) VALUES(1005019,'And Jared lived after he begat Enoch eight hundred years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005020,'And all the days of Jared were nine hundred sixty and two years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005021,'And Enoch lived sixty and five years, and begat Methuselah:');
-INSERT INTO t1(docid,words) VALUES(1005022,'And Enoch walked with God after he begat Methuselah three hundred years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005023,'And all the days of Enoch were three hundred sixty and five years:');
-INSERT INTO t1(docid,words) VALUES(1005024,'And Enoch walked with God: and he was not; for God took him.');
-INSERT INTO t1(docid,words) VALUES(1005025,'And Methuselah lived an hundred eighty and seven years, and begat Lamech.');
-INSERT INTO t1(docid,words) VALUES(1005026,'And Methuselah lived after he begat Lamech seven hundred eighty and two years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005027,'And all the days of Methuselah were nine hundred sixty and nine years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005028,'And Lamech lived an hundred eighty and two years, and begat a son:');
-INSERT INTO t1(docid,words) VALUES(1005029,'And he called his name Noah, saying, This same shall comfort us concerning our work and toil of our hands, because of the ground which the LORD hath cursed.');
-INSERT INTO t1(docid,words) VALUES(1005030,'And Lamech lived after he begat Noah five hundred ninety and five years, and begat sons and daughters:');
-INSERT INTO t1(docid,words) VALUES(1005031,'And all the days of Lamech were seven hundred seventy and seven years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1005032,'And Noah was five hundred years old: and Noah begat Shem, Ham, and Japheth.');
-INSERT INTO t1(docid,words) VALUES(1006001,'And it came to pass, when men began to multiply on the face of the earth, and daughters were born unto them,');
-INSERT INTO t1(docid,words) VALUES(1006002,'That the sons of God saw the daughters of men that they were fair; and they took them wives of all which they chose.');
-INSERT INTO t1(docid,words) VALUES(1006003,'And the LORD said, My spirit shall not always strive with man, for that he also is flesh: yet his days shall be an hundred and twenty years.');
-INSERT INTO t1(docid,words) VALUES(1006004,'There were giants in the earth in those days; and also after that, when the sons of God came in unto the daughters of men, and they bare children to them, the same became mighty men which were of old, men of renown.');
-INSERT INTO t1(docid,words) VALUES(1006005,'And God saw that the wickedness of man was great in the earth, and that every imagination of the thoughts of his heart was only evil continually.');
-INSERT INTO t1(docid,words) VALUES(1006006,'And it repented the LORD that he had made man on the earth, and it grieved him at his heart.');
-INSERT INTO t1(docid,words) VALUES(1006007,'And the LORD said, I will destroy man whom I have created from the face of the earth; both man, and beast, and the creeping thing, and the fowls of the air; for it repenteth me that I have made them.');
-INSERT INTO t1(docid,words) VALUES(1006008,'But Noah found grace in the eyes of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1006009,'These are the generations of Noah: Noah was a just man and perfect in his generations, and Noah walked with God.');
-INSERT INTO t1(docid,words) VALUES(1006010,'And Noah begat three sons, Shem, Ham, and Japheth.');
-INSERT INTO t1(docid,words) VALUES(1006011,'The earth also was corrupt before God, and the earth was filled with violence.');
-INSERT INTO t1(docid,words) VALUES(1006012,'And God looked upon the earth, and, behold, it was corrupt; for all flesh had corrupted his way upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1006013,'And God said unto Noah, The end of all flesh is come before me; for the earth is filled with violence through them; and, behold, I will destroy them with the earth.');
-INSERT INTO t1(docid,words) VALUES(1006014,'Make thee an ark of gopher wood; rooms shalt thou make in the ark, and shalt pitch it within and without with pitch.');
-INSERT INTO t1(docid,words) VALUES(1006015,'And this is the fashion which thou shalt make it of: The length of the ark shall be three hundred cubits, the breadth of it fifty cubits, and the height of it thirty cubits.');
-INSERT INTO t1(docid,words) VALUES(1006016,'A window shalt thou make to the ark, and in a cubit shalt thou finish it above; and the door of the ark shalt thou set in the side thereof; with lower, second, and third stories shalt thou make it.');
-INSERT INTO t1(docid,words) VALUES(1006017,'And, behold, I, even I, do bring a flood of waters upon the earth, to destroy all flesh, wherein is the breath of life, from under heaven; and every thing that is in the earth shall die.');
-INSERT INTO t1(docid,words) VALUES(1006018,'But with thee will I establish my covenant; and thou shalt come into the ark, thou, and thy sons, and thy wife, and thy sons'' wives with thee.');
-INSERT INTO t1(docid,words) VALUES(1006019,'And of every living thing of all flesh, two of every sort shalt thou bring into the ark, to keep them alive with thee; they shall be male and female.');
-INSERT INTO t1(docid,words) VALUES(1006020,'Of fowls after their kind, and of cattle after their kind, of every creeping thing of the earth after his kind, two of every sort shall come unto thee, to keep them alive.');
-INSERT INTO t1(docid,words) VALUES(1006021,'And take thou unto thee of all food that is eaten, and thou shalt gather it to thee; and it shall be for food for thee, and for them.');
-INSERT INTO t1(docid,words) VALUES(1006022,'Thus did Noah; according to all that God commanded him, so did he.');
-INSERT INTO t1(docid,words) VALUES(1007001,'And the LORD said unto Noah, Come thou and all thy house into the ark; for thee have I seen righteous before me in this generation.');
-INSERT INTO t1(docid,words) VALUES(1007002,'Of every clean beast thou shalt take to thee by sevens, the male and his female: and of beasts that are not clean by two, the male and his female.');
-INSERT INTO t1(docid,words) VALUES(1007003,'Of fowls also of the air by sevens, the male and the female; to keep seed alive upon the face of all the earth.');
-INSERT INTO t1(docid,words) VALUES(1007004,'For yet seven days, and I will cause it to rain upon the earth forty days and forty nights; and every living substance that I have made will I destroy from off the face of the earth.');
-INSERT INTO t1(docid,words) VALUES(1007005,'And Noah did according unto all that the LORD commanded him.');
-INSERT INTO t1(docid,words) VALUES(1007006,'And Noah was six hundred years old when the flood of waters was upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1007007,'And Noah went in, and his sons, and his wife, and his sons'' wives with him, into the ark, because of the waters of the flood.');
-INSERT INTO t1(docid,words) VALUES(1007008,'Of clean beasts, and of beasts that are not clean, and of fowls, and of every thing that creepeth upon the earth,');
-INSERT INTO t1(docid,words) VALUES(1007009,'There went in two and two unto Noah into the ark, the male and the female, as God had commanded Noah.');
-INSERT INTO t1(docid,words) VALUES(1007010,'And it came to pass after seven days, that the waters of the flood were upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1007011,'In the six hundredth year of Noah''s life, in the second month, the seventeenth day of the month, the same day were all the fountains of the great deep broken up, and the windows of heaven were opened.');
-INSERT INTO t1(docid,words) VALUES(1007012,'And the rain was upon the earth forty days and forty nights.');
-INSERT INTO t1(docid,words) VALUES(1007013,'In the selfsame day entered Noah, and Shem, and Ham, and Japheth, the sons of Noah, and Noah''s wife, and the three wives of his sons with them, into the ark;');
-INSERT INTO t1(docid,words) VALUES(1007014,'They, and every beast after his kind, and all the cattle after their kind, and every creeping thing that creepeth upon the earth after his kind, and every fowl after his kind, every bird of every sort.');
-INSERT INTO t1(docid,words) VALUES(1007015,'And they went in unto Noah into the ark, two and two of all flesh, wherein is the breath of life.');
-INSERT INTO t1(docid,words) VALUES(1007016,'And they that went in, went in male and female of all flesh, as God had commanded him: and the LORD shut him in.');
-INSERT INTO t1(docid,words) VALUES(1007017,'And the flood was forty days upon the earth; and the waters increased, and bare up the ark, and it was lift up above the earth.');
-INSERT INTO t1(docid,words) VALUES(1007018,'And the waters prevailed, and were increased greatly upon the earth; and the ark went upon the face of the waters.');
-INSERT INTO t1(docid,words) VALUES(1007019,'And the waters prevailed exceedingly upon the earth; and all the high hills, that were under the whole heaven, were covered.');
-INSERT INTO t1(docid,words) VALUES(1007020,'Fifteen cubits upward did the waters prevail; and the mountains were covered.');
-INSERT INTO t1(docid,words) VALUES(1007021,'And all flesh died that moved upon the earth, both of fowl, and of cattle, and of beast, and of every creeping thing that creepeth upon the earth, and every man:');
-INSERT INTO t1(docid,words) VALUES(1007022,'All in whose nostrils was the breath of life, of all that was in the dry land, died.');
-INSERT INTO t1(docid,words) VALUES(1007023,'And every living substance was destroyed which was upon the face of the ground, both man, and cattle, and the creeping things, and the fowl of the heaven; and they were destroyed from the earth: and Noah only remained alive, and they that were with him in the ark.');
-INSERT INTO t1(docid,words) VALUES(1007024,'And the waters prevailed upon the earth an hundred and fifty days.');
-INSERT INTO t1(docid,words) VALUES(1008001,'And God remembered Noah, and every living thing, and all the cattle that was with him in the ark: and God made a wind to pass over the earth, and the waters asswaged;');
-INSERT INTO t1(docid,words) VALUES(1008002,'The fountains also of the deep and the windows of heaven were stopped, and the rain from heaven was restrained;');
-INSERT INTO t1(docid,words) VALUES(1008003,'And the waters returned from off the earth continually: and after the end of the hundred and fifty days the waters were abated.');
-INSERT INTO t1(docid,words) VALUES(1008004,'And the ark rested in the seventh month, on the seventeenth day of the month, upon the mountains of Ararat.');
-INSERT INTO t1(docid,words) VALUES(1008005,'And the waters decreased continually until the tenth month: in the tenth month, on the first day of the month, were the tops of the mountains seen.');
-INSERT INTO t1(docid,words) VALUES(1008006,'And it came to pass at the end of forty days, that Noah opened the window of the ark which he had made:');
-INSERT INTO t1(docid,words) VALUES(1008007,'And he sent forth a raven, which went forth to and fro, until the waters were dried up from off the earth.');
-INSERT INTO t1(docid,words) VALUES(1008008,'Also he sent forth a dove from him, to see if the waters were abated from off the face of the ground;');
-INSERT INTO t1(docid,words) VALUES(1008009,'But the dove found no rest for the sole of her foot, and she returned unto him into the ark, for the waters were on the face of the whole earth: then he put forth his hand, and took her, and pulled her in unto him into the ark.');
-INSERT INTO t1(docid,words) VALUES(1008010,'And he stayed yet other seven days; and again he sent forth the dove out of the ark;');
-INSERT INTO t1(docid,words) VALUES(1008011,'And the dove came in to him in the evening; and, lo, in her mouth was an olive leaf pluckt off: so Noah knew that the waters were abated from off the earth.');
-INSERT INTO t1(docid,words) VALUES(1008012,'And he stayed yet other seven days; and sent forth the dove; which returned not again unto him any more.');
-INSERT INTO t1(docid,words) VALUES(1008013,'And it came to pass in the six hundredth and first year, in the first month, the first day of the month, the waters were dried up from off the earth: and Noah removed the covering of the ark, and looked, and, behold, the face of the ground was dry.');
-INSERT INTO t1(docid,words) VALUES(1008014,'And in the second month, on the seven and twentieth day of the month, was the earth dried.');
-INSERT INTO t1(docid,words) VALUES(1008015,'And God spake unto Noah, saying,');
-INSERT INTO t1(docid,words) VALUES(1008016,'Go forth of the ark, thou, and thy wife, and thy sons, and thy sons'' wives with thee.');
-INSERT INTO t1(docid,words) VALUES(1008017,'Bring forth with thee every living thing that is with thee, of all flesh, both of fowl, and of cattle, and of every creeping thing that creepeth upon the earth; that they may breed abundantly in the earth, and be fruitful, and multiply upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1008018,'And Noah went forth, and his sons, and his wife, and his sons'' wives with him:');
-INSERT INTO t1(docid,words) VALUES(1008019,'Every beast, every creeping thing, and every fowl, and whatsoever creepeth upon the earth, after their kinds, went forth out of the ark.');
-INSERT INTO t1(docid,words) VALUES(1008020,'And Noah builded an altar unto the LORD; and took of every clean beast, and of every clean fowl, and offered burnt offerings on the altar.');
-INSERT INTO t1(docid,words) VALUES(1008021,'And the LORD smelled a sweet savour; and the LORD said in his heart, I will not again curse the ground any more for man''s sake; for the imagination of man''s heart is evil from his youth; neither will I again smite any more every thing living, as I have done.');
-INSERT INTO t1(docid,words) VALUES(1008022,'While the earth remaineth, seedtime and harvest, and cold and heat, and summer and winter, and day and night shall not cease.');
-INSERT INTO t1(docid,words) VALUES(1009001,'And God blessed Noah and his sons, and said unto them, Be fruitful, and multiply, and replenish the earth.');
-INSERT INTO t1(docid,words) VALUES(1009002,'And the fear of you and the dread of you shall be upon every beast of the earth, and upon every fowl of the air, upon all that moveth upon the earth, and upon all the fishes of the sea; into your hand are they delivered.');
-INSERT INTO t1(docid,words) VALUES(1009003,'Every moving thing that liveth shall be meat for you; even as the green herb have I given you all things.');
-INSERT INTO t1(docid,words) VALUES(1009004,'But flesh with the life thereof, which is the blood thereof, shall ye not eat.');
-INSERT INTO t1(docid,words) VALUES(1009005,'And surely your blood of your lives will I require; at the hand of every beast will I require it, and at the hand of man; at the hand of every man''s brother will I require the life of man.');
-INSERT INTO t1(docid,words) VALUES(1009006,'Whoso sheddeth man''s blood, by man shall his blood be shed: for in the image of God made he man.');
-INSERT INTO t1(docid,words) VALUES(1009007,'And you, be ye fruitful, and multiply; bring forth abundantly in the earth, and multiply therein.');
-INSERT INTO t1(docid,words) VALUES(1009008,'And God spake unto Noah, and to his sons with him, saying,');
-INSERT INTO t1(docid,words) VALUES(1009009,'And I, behold, I establish my covenant with you, and with your seed after you;');
-INSERT INTO t1(docid,words) VALUES(1009010,'And with every living creature that is with you, of the fowl, of the cattle, and of every beast of the earth with you; from all that go out of the ark, to every beast of the earth.');
-INSERT INTO t1(docid,words) VALUES(1009011,'And I will establish my covenant with you, neither shall all flesh be cut off any more by the waters of a flood; neither shall there any more be a flood to destroy the earth.');
-INSERT INTO t1(docid,words) VALUES(1009012,'And God said, This is the token of the covenant which I make between me and you and every living creature that is with you, for perpetual generations:');
-INSERT INTO t1(docid,words) VALUES(1009013,'I do set my bow in the cloud, and it shall be for a token of a covenant between me and the earth.');
-INSERT INTO t1(docid,words) VALUES(1009014,'And it shall come to pass, when I bring a cloud over the earth, that the bow shall be seen in the cloud:');
-INSERT INTO t1(docid,words) VALUES(1009015,'And I will remember my covenant, which is between me and you and every living creature of all flesh; and the waters shall no more become a flood to destroy all flesh.');
-INSERT INTO t1(docid,words) VALUES(1009016,'And the bow shall be in the cloud; and I will look upon it, that I may remember the everlasting covenant between God and every living creature of all flesh that is upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1009017,'And God said unto Noah, This is the token of the covenant, which I have established between me and all flesh that is upon the earth.');
-INSERT INTO t1(docid,words) VALUES(1009018,'And the sons of Noah, that went forth of the ark, were Shem, and Ham, and Japheth: and Ham is the father of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1009019,'These are the three sons of Noah: and of them was the whole earth overspread.');
-INSERT INTO t1(docid,words) VALUES(1009020,'And Noah began to be an husbandman, and he planted a vineyard:');
-INSERT INTO t1(docid,words) VALUES(1009021,'And he drank of the wine, and was drunken; and he was uncovered within his tent.');
-INSERT INTO t1(docid,words) VALUES(1009022,'And Ham, the father of Canaan, saw the nakedness of his father, and told his two brethren without.');
-INSERT INTO t1(docid,words) VALUES(1009023,'And Shem and Japheth took a garment, and laid it upon both their shoulders, and went backward, and covered the nakedness of their father; and their faces were backward, and they saw not their father''s nakedness.');
-INSERT INTO t1(docid,words) VALUES(1009024,'And Noah awoke from his wine, and knew what his younger son had done unto him.');
-INSERT INTO t1(docid,words) VALUES(1009025,'And he said, Cursed be Canaan; a servant of servants shall he be unto his brethren.');
-INSERT INTO t1(docid,words) VALUES(1009026,'And he said, Blessed be the LORD God of Shem; and Canaan shall be his servant.');
-INSERT INTO t1(docid,words) VALUES(1009027,'God shall enlarge Japheth, and he shall dwell in the tents of Shem; and Canaan shall be his servant.');
-INSERT INTO t1(docid,words) VALUES(1009028,'And Noah lived after the flood three hundred and fifty years.');
-INSERT INTO t1(docid,words) VALUES(1009029,'And all the days of Noah were nine hundred and fifty years: and he died.');
-INSERT INTO t1(docid,words) VALUES(1010001,'Now these are the generations of the sons of Noah, Shem, Ham, and Japheth: and unto them were sons born after the flood.');
-INSERT INTO t1(docid,words) VALUES(1010002,'The sons of Japheth; Gomer, and Magog, and Madai, and Javan, and Tubal, and Meshech, and Tiras.');
-INSERT INTO t1(docid,words) VALUES(1010003,'And the sons of Gomer; Ashkenaz, and Riphath, and Togarmah.');
-INSERT INTO t1(docid,words) VALUES(1010004,'And the sons of Javan; Elishah, and Tarshish, Kittim, and Dodanim.');
-INSERT INTO t1(docid,words) VALUES(1010005,'By these were the isles of the Gentiles divided in their lands; every one after his tongue, after their families, in their nations.');
-INSERT INTO t1(docid,words) VALUES(1010006,'And the sons of Ham; Cush, and Mizraim, and Phut, and Canaan.');
-INSERT INTO t1(docid,words) VALUES(1010007,'And the sons of Cush; Seba, and Havilah, and Sabtah, and Raamah, and Sabtechah: and the sons of Raamah; Sheba, and Dedan.');
-INSERT INTO t1(docid,words) VALUES(1010008,'And Cush begat Nimrod: he began to be a mighty one in the earth.');
-INSERT INTO t1(docid,words) VALUES(1010009,'He was a mighty hunter before the LORD: wherefore it is said, Even as Nimrod the mighty hunter before the LORD.');
-INSERT INTO t1(docid,words) VALUES(1010010,'And the beginning of his kingdom was Babel, and Erech, and Accad, and Calneh, in the land of Shinar.');
-INSERT INTO t1(docid,words) VALUES(1010011,'Out of that land went forth Asshur, and builded Nineveh, and the city Rehoboth, and Calah,');
-INSERT INTO t1(docid,words) VALUES(1010012,'And Resen between Nineveh and Calah: the same is a great city.');
-INSERT INTO t1(docid,words) VALUES(1010013,'And Mizraim begat Ludim, and Anamim, and Lehabim, and Naphtuhim,');
-INSERT INTO t1(docid,words) VALUES(1010014,'And Pathrusim, and Casluhim, (out of whom came Philistim,) and Caphtorim.');
-INSERT INTO t1(docid,words) VALUES(1010015,'And Canaan begat Sidon his first born, and Heth,');
-INSERT INTO t1(docid,words) VALUES(1010016,'And the Jebusite, and the Amorite, and the Girgasite,');
-INSERT INTO t1(docid,words) VALUES(1010017,'And the Hivite, and the Arkite, and the Sinite,');
-INSERT INTO t1(docid,words) VALUES(1010018,'And the Arvadite, and the Zemarite, and the Hamathite: and afterward were the families of the Canaanites spread abroad.');
-INSERT INTO t1(docid,words) VALUES(1010019,'And the border of the Canaanites was from Sidon, as thou comest to Gerar, unto Gaza; as thou goest, unto Sodom, and Gomorrah, and Admah, and Zeboim, even unto Lasha.');
-INSERT INTO t1(docid,words) VALUES(1010020,'These are the sons of Ham, after their families, after their tongues, in their countries, and in their nations.');
-INSERT INTO t1(docid,words) VALUES(1010021,'Unto Shem also, the father of all the children of Eber, the brother of Japheth the elder, even to him were children born.');
-INSERT INTO t1(docid,words) VALUES(1010022,'The children of Shem; Elam, and Asshur, and Arphaxad, and Lud, and Aram.');
-INSERT INTO t1(docid,words) VALUES(1010023,'And the children of Aram; Uz, and Hul, and Gether, and Mash.');
-INSERT INTO t1(docid,words) VALUES(1010024,'And Arphaxad begat Salah; and Salah begat Eber.');
-INSERT INTO t1(docid,words) VALUES(1010025,'And unto Eber were born two sons: the name of one was Peleg; for in his days was the earth divided; and his brother''s name was Joktan.');
-INSERT INTO t1(docid,words) VALUES(1010026,'And Joktan begat Almodad, and Sheleph, and Hazarmaveth, and Jerah,');
-INSERT INTO t1(docid,words) VALUES(1010027,'And Hadoram, and Uzal, and Diklah,');
-INSERT INTO t1(docid,words) VALUES(1010028,'And Obal, and Abimael, and Sheba,');
-INSERT INTO t1(docid,words) VALUES(1010029,'And Ophir, and Havilah, and Jobab: all these were the sons of Joktan.');
-INSERT INTO t1(docid,words) VALUES(1010030,'And their dwelling was from Mesha, as thou goest unto Sephar a mount of the east.');
-INSERT INTO t1(docid,words) VALUES(1010031,'These are the sons of Shem, after their families, after their tongues, in their lands, after their nations.');
-INSERT INTO t1(docid,words) VALUES(1010032,'These are the families of the sons of Noah, after their generations, in their nations: and by these were the nations divided in the earth after the flood.');
-INSERT INTO t1(docid,words) VALUES(1011001,'And the whole earth was of one language, and of one speech.');
-INSERT INTO t1(docid,words) VALUES(1011002,'And it came to pass, as they journeyed from the east, that they found a plain in the land of Shinar; and they dwelt there.');
-INSERT INTO t1(docid,words) VALUES(1011003,'And they said one to another, Go to, let us make brick, and burn them thoroughly. And they had brick for stone, and slime had they for morter.');
-INSERT INTO t1(docid,words) VALUES(1011004,'And they said, Go to, let us build us a city and a tower, whose top may reach unto heaven; and let us make us a name, lest we be scattered abroad upon the face of the whole earth.');
-INSERT INTO t1(docid,words) VALUES(1011005,'And the LORD came down to see the city and the tower, which the children of men builded.');
-INSERT INTO t1(docid,words) VALUES(1011006,'And the LORD said, Behold, the people is one, and they have all one language; and this they begin to do: and now nothing will be restrained from them, which they have imagined to do.');
-INSERT INTO t1(docid,words) VALUES(1011007,'Go to, let us go down, and there confound their language, that they may not understand one another''s speech.');
-INSERT INTO t1(docid,words) VALUES(1011008,'So the LORD scattered them abroad from thence upon the face of all the earth: and they left off to build the city.');
-INSERT INTO t1(docid,words) VALUES(1011009,'Therefore is the name of it called Babel; because the LORD did there confound the language of all the earth: and from thence did the LORD scatter them abroad upon the face of all the earth.');
-INSERT INTO t1(docid,words) VALUES(1011010,'These are the generations of Shem: Shem was an hundred years old, and begat Arphaxad two years after the flood:');
-INSERT INTO t1(docid,words) VALUES(1011011,'And Shem lived after he begat Arphaxad five hundred years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011012,'And Arphaxad lived five and thirty years, and begat Salah:');
-INSERT INTO t1(docid,words) VALUES(1011013,'And Arphaxad lived after he begat Salah four hundred and three years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011014,'And Salah lived thirty years, and begat Eber:');
-INSERT INTO t1(docid,words) VALUES(1011015,'And Salah lived after he begat Eber four hundred and three years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011016,'And Eber lived four and thirty years, and begat Peleg:');
-INSERT INTO t1(docid,words) VALUES(1011017,'And Eber lived after he begat Peleg four hundred and thirty years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011018,'And Peleg lived thirty years, and begat Reu:');
-INSERT INTO t1(docid,words) VALUES(1011019,'And Peleg lived after he begat Reu two hundred and nine years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011020,'And Reu lived two and thirty years, and begat Serug:');
-INSERT INTO t1(docid,words) VALUES(1011021,'And Reu lived after he begat Serug two hundred and seven years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011022,'And Serug lived thirty years, and begat Nahor:');
-INSERT INTO t1(docid,words) VALUES(1011023,'And Serug lived after he begat Nahor two hundred years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011024,'And Nahor lived nine and twenty years, and begat Terah:');
-INSERT INTO t1(docid,words) VALUES(1011025,'And Nahor lived after he begat Terah an hundred and nineteen years, and begat sons and daughters.');
-INSERT INTO t1(docid,words) VALUES(1011026,'And Terah lived seventy years, and begat Abram, Nahor, and Haran.');
-INSERT INTO t1(docid,words) VALUES(1011027,'Now these are the generations of Terah: Terah begat Abram, Nahor, and Haran; and Haran begat Lot.');
-INSERT INTO t1(docid,words) VALUES(1011028,'And Haran died before his father Terah in the land of his nativity, in Ur of the Chaldees.');
-INSERT INTO t1(docid,words) VALUES(1011029,'And Abram and Nahor took them wives: the name of Abram''s wife was Sarai; and the name of Nahor''s wife, Milcah, the daughter of Haran, the father of Milcah, and the father of Iscah.');
-INSERT INTO t1(docid,words) VALUES(1011030,'But Sarai was barren; she had no child.');
-INSERT INTO t1(docid,words) VALUES(1011031,'And Terah took Abram his son, and Lot the son of Haran his son''s son, and Sarai his daughter in law, his son Abram''s wife; and they went forth with them from Ur of the Chaldees, to go into the land of Canaan; and they came unto Haran, and dwelt there.');
-INSERT INTO t1(docid,words) VALUES(1011032,'And the days of Terah were two hundred and five years: and Terah died in Haran.');
-INSERT INTO t1(docid,words) VALUES(1012001,'Now the LORD had said unto Abram, Get thee out of thy country, and from thy kindred, and from thy father''s house, unto a land that I will shew thee:');
-INSERT INTO t1(docid,words) VALUES(1012002,'And I will make of thee a great nation, and I will bless thee, and make thy name great; and thou shalt be a blessing:');
-INSERT INTO t1(docid,words) VALUES(1012003,'And I will bless them that bless thee, and curse him that curseth thee: and in thee shall all families of the earth be blessed.');
-INSERT INTO t1(docid,words) VALUES(1012004,'So Abram departed, as the LORD had spoken unto him; and Lot went with him: and Abram was seventy and five years old when he departed out of Haran.');
-INSERT INTO t1(docid,words) VALUES(1012005,'And Abram took Sarai his wife, and Lot his brother''s son, and all their substance that they had gathered, and the souls that they had gotten in Haran; and they went forth to go into the land of Canaan; and into the land of Canaan they came.');
-INSERT INTO t1(docid,words) VALUES(1012006,'And Abram passed through the land unto the place of Sichem, unto the plain of Moreh. And the Canaanite was then in the land.');
-INSERT INTO t1(docid,words) VALUES(1012007,'And the LORD appeared unto Abram, and said, Unto thy seed will I give this land: and there builded he an altar unto the LORD, who appeared unto him.');
-INSERT INTO t1(docid,words) VALUES(1012008,'And he removed from thence unto a mountain on the east of Bethel, and pitched his tent, having Bethel on the west, and Hai on the east: and there he builded an altar unto the LORD, and called upon the name of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1012009,'And Abram journeyed, going on still toward the south.');
-INSERT INTO t1(docid,words) VALUES(1012010,'And there was a famine in the land: and Abram went down into Egypt to sojourn there; for the famine was grievous in the land.');
-INSERT INTO t1(docid,words) VALUES(1012011,'And it came to pass, when he was come near to enter into Egypt, that he said unto Sarai his wife, Behold now, I know that thou art a fair woman to look upon:');
-INSERT INTO t1(docid,words) VALUES(1012012,'Therefore it shall come to pass, when the Egyptians shall see thee, that they shall say, This is his wife: and they will kill me, but they will save thee alive.');
-INSERT INTO t1(docid,words) VALUES(1012013,'Say, I pray thee, thou art my sister: that it may be well with me for thy sake; and my soul shall live because of thee.');
-INSERT INTO t1(docid,words) VALUES(1012014,'And it came to pass, that, when Abram was come into Egypt, the Egyptians beheld the woman that she was very fair.');
-INSERT INTO t1(docid,words) VALUES(1012015,'The princes also of Pharaoh saw her, and commended her before Pharaoh: and the woman was taken into Pharaoh''s house.');
-INSERT INTO t1(docid,words) VALUES(1012016,'And he entreated Abram well for her sake: and he had sheep, and oxen, and he asses, and menservants, and maidservants, and she asses, and camels.');
-INSERT INTO t1(docid,words) VALUES(1012017,'And the LORD plagued Pharaoh and his house with great plagues because of Sarai Abram''s wife.');
-INSERT INTO t1(docid,words) VALUES(1012018,'And Pharaoh called Abram and said, What is this that thou hast done unto me? why didst thou not tell me that she was thy wife?');
-INSERT INTO t1(docid,words) VALUES(1012019,'Why saidst thou, She is my sister? so I might have taken her to me to wife: now therefore behold thy wife, take her, and go thy way.');
-INSERT INTO t1(docid,words) VALUES(1012020,'And Pharaoh commanded his men concerning him: and they sent him away, and his wife, and all that he had.');
-INSERT INTO t1(docid,words) VALUES(1013001,'And Abram went up out of Egypt, he, and his wife, and all that he had, and Lot with him, into the south.');
-INSERT INTO t1(docid,words) VALUES(1013002,'And Abram was very rich in cattle, in silver, and in gold.');
-INSERT INTO t1(docid,words) VALUES(1013003,'And he went on his journeys from the south even to Bethel, unto the place where his tent had been at the beginning, between Bethel and Hai;');
-INSERT INTO t1(docid,words) VALUES(1013004,'Unto the place of the altar, which he had make there at the first: and there Abram called on the name of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1013005,'And Lot also, which went with Abram, had flocks, and herds, and tents.');
-INSERT INTO t1(docid,words) VALUES(1013006,'And the land was not able to bear them, that they might dwell together: for their substance was great, so that they could not dwell together.');
-INSERT INTO t1(docid,words) VALUES(1013007,'And there was a strife between the herdmen of Abram''s cattle and the herdmen of Lot''s cattle: and the Canaanite and the Perizzite dwelled then in the land.');
-INSERT INTO t1(docid,words) VALUES(1013008,'And Abram said unto Lot, Let there be no strife, I pray thee, between me and thee, and between my herdmen and thy herdmen; for we be brethren.');
-INSERT INTO t1(docid,words) VALUES(1013009,'Is not the whole land before thee? separate thyself, I pray thee, from me: if thou wilt take the left hand, then I will go to the right; or if thou depart to the right hand, then I will go to the left.');
-INSERT INTO t1(docid,words) VALUES(1013010,'And Lot lifted up his eyes, and beheld all the plain of Jordan, that it was well watered every where, before the LORD destroyed Sodom and Gomorrah, even as the garden of the LORD, like the land of Egypt, as thou comest unto Zoar.');
-INSERT INTO t1(docid,words) VALUES(1013011,'Then Lot chose him all the plain of Jordan; and Lot journeyed east: and they separated themselves the one from the other.');
-INSERT INTO t1(docid,words) VALUES(1013012,'Abram dwelled in the land of Canaan, and Lot dwelled in the cities of the plain, and pitched his tent toward Sodom.');
-INSERT INTO t1(docid,words) VALUES(1013013,'But the men of Sodom were wicked and sinners before the LORD exceedingly.');
-INSERT INTO t1(docid,words) VALUES(1013014,'And the LORD said unto Abram, after that Lot was separated from him, Lift up now thine eyes, and look from the place where thou art northward, and southward, and eastward, and westward:');
-INSERT INTO t1(docid,words) VALUES(1013015,'For all the land which thou seest, to thee will I give it, and to thy seed for ever.');
-INSERT INTO t1(docid,words) VALUES(1013016,'And I will make thy seed as the dust of the earth: so that if a man can number the dust of the earth, then shall thy seed also be numbered.');
-INSERT INTO t1(docid,words) VALUES(1013017,'Arise, walk through the land in the length of it and in the breadth of it; for I will give it unto thee.');
-INSERT INTO t1(docid,words) VALUES(1013018,'Then Abram removed his tent, and came and dwelt in the plain of Mamre, which is in Hebron, and built there an altar unto the LORD.');
-INSERT INTO t1(docid,words) VALUES(1014001,'And it came to pass in the days of Amraphel king of Shinar, Arioch king of Ellasar, Chedorlaomer king of Elam, and Tidal king of nations;');
-INSERT INTO t1(docid,words) VALUES(1014002,'That these made war with Bera king of Sodom, and with Birsha king of Gomorrah, Shinab king of Admah, and Shemeber king of Zeboiim, and the king of Bela, which is Zoar.');
-INSERT INTO t1(docid,words) VALUES(1014003,'All these were joined together in the vale of Siddim, which is the salt sea.');
-INSERT INTO t1(docid,words) VALUES(1014004,'Twelve years they served Chedorlaomer, and in the thirteenth year they rebelled.');
-INSERT INTO t1(docid,words) VALUES(1014005,'And in the fourteenth year came Chedorlaomer, and the kings that were with him, and smote the Rephaims in Ashteroth Karnaim, and the Zuzims in Ham, and the Emins in Shaveh Kiriathaim,');
-INSERT INTO t1(docid,words) VALUES(1014006,'And the Horites in their mount Seir, unto Elparan, which is by the wilderness.');
-INSERT INTO t1(docid,words) VALUES(1014007,'And they returned, and came to Enmishpat, which is Kadesh, and smote all the country of the Amalekites, and also the Amorites, that dwelt in Hazezontamar.');
-INSERT INTO t1(docid,words) VALUES(1014008,'And there went out the king of Sodom, and the king of Gomorrah, and the king of Admah, and the king of Zeboiim, and the king of Bela (the same is Zoar;) and they joined battle with them in the vale of Siddim;');
-INSERT INTO t1(docid,words) VALUES(1014009,'With Chedorlaomer the king of Elam, and with Tidal king of nations, and Amraphel king of Shinar, and Arioch king of Ellasar; four kings with five.');
-INSERT INTO t1(docid,words) VALUES(1014010,'And the vale of Siddim was full of slimepits; and the kings of Sodom and Gomorrah fled, and fell there; and they that remained fled to the mountain.');
-INSERT INTO t1(docid,words) VALUES(1014011,'And they took all the goods of Sodom and Gomorrah, and all their victuals, and went their way.');
-INSERT INTO t1(docid,words) VALUES(1014012,'And they took Lot, Abram''s brother''s son, who dwelt in Sodom, and his goods, and departed.');
-INSERT INTO t1(docid,words) VALUES(1014013,'And there came one that had escaped, and told Abram the Hebrew; for he dwelt in the plain of Mamre the Amorite, brother of Eshcol, and brother of Aner: and these were confederate with Abram.');
-INSERT INTO t1(docid,words) VALUES(1014014,'And when Abram heard that his brother was taken captive, he armed his trained servants, born in his own house, three hundred and eighteen, and pursued them unto Dan.');
-INSERT INTO t1(docid,words) VALUES(1014015,'And he divided himself against them, he and his servants, by night, and smote them, and pursued them unto Hobah, which is on the left hand of Damascus.');
-INSERT INTO t1(docid,words) VALUES(1014016,'And he brought back all the goods, and also brought again his brother Lot, and his goods, and the women also, and the people.');
-INSERT INTO t1(docid,words) VALUES(1014017,'And the king of Sodom went out to meet him after his return from the slaughter of Chedorlaomer, and of the kings that were with him, at the valley of Shaveh, which is the king''s dale.');
-INSERT INTO t1(docid,words) VALUES(1014018,'And Melchizedek king of Salem brought forth bread and wine: and he was the priest of the most high God.');
-INSERT INTO t1(docid,words) VALUES(1014019,'And he blessed him, and said, Blessed be Abram of the most high God, possessor of heaven and earth:');
-INSERT INTO t1(docid,words) VALUES(1014020,'And blessed be the most high God, which hath delivered thine enemies into thy hand. And he gave him tithes of all.');
-INSERT INTO t1(docid,words) VALUES(1014021,'And the king of Sodom said unto Abram, Give me the persons, and take the goods to thyself.');
-INSERT INTO t1(docid,words) VALUES(1014022,'And Abram said to the king of Sodom, I have lift up mine hand unto the LORD, the most high God, the possessor of heaven and earth,');
-INSERT INTO t1(docid,words) VALUES(1014023,'That I will not take from a thread even to a shoelatchet, and that I will not take any thing that is thine, lest thou shouldest say, I have made Abram rich:');
-INSERT INTO t1(docid,words) VALUES(1014024,'Save only that which the young men have eaten, and the portion of the men which went with me, Aner, Eshcol, and Mamre; let them take their portion.');
-INSERT INTO t1(docid,words) VALUES(1015001,'After these things the word of the LORD came unto Abram in a vision, saying, Fear not, Abram: I am thy shield, and thy exceeding great reward.');
-INSERT INTO t1(docid,words) VALUES(1015002,'And Abram said, LORD God, what wilt thou give me, seeing I go childless, and the steward of my house is this Eliezer of Damascus?');
-INSERT INTO t1(docid,words) VALUES(1015003,'And Abram said, Behold, to me thou hast given no seed: and, lo, one born in my house is mine heir.');
-INSERT INTO t1(docid,words) VALUES(1015004,'And, behold, the word of the LORD came unto him, saying, This shall not be thine heir; but he that shall come forth out of thine own bowels shall be thine heir.');
-INSERT INTO t1(docid,words) VALUES(1015005,'And he brought him forth abroad, and said, Look now toward heaven, and tell the stars, if thou be able to number them: and he said unto him, So shall thy seed be.');
-INSERT INTO t1(docid,words) VALUES(1015006,'And he believed in the LORD; and he counted it to him for righteousness.');
-INSERT INTO t1(docid,words) VALUES(1015007,'And he said unto him, I am the LORD that brought thee out of Ur of the Chaldees, to give thee this land to inherit it.');
-INSERT INTO t1(docid,words) VALUES(1015008,'And he said, LORD God, whereby shall I know that I shall inherit it?');
-INSERT INTO t1(docid,words) VALUES(1015009,'And he said unto him, Take me an heifer of three years old, and a she goat of three years old, and a ram of three years old, and a turtledove, and a young pigeon.');
-INSERT INTO t1(docid,words) VALUES(1015010,'And he took unto him all these, and divided them in the midst, and laid each piece one against another: but the birds divided he not.');
-INSERT INTO t1(docid,words) VALUES(1015011,'And when the fowls came down upon the carcases, Abram drove them away.');
-INSERT INTO t1(docid,words) VALUES(1015012,'And when the sun was going down, a deep sleep fell upon Abram; and, lo, an horror of great darkness fell upon him.');
-INSERT INTO t1(docid,words) VALUES(1015013,'And he said unto Abram, Know of a surety that thy seed shall be a stranger in a land that is not their''s, and shall serve them; and they shall afflict them four hundred years;');
-INSERT INTO t1(docid,words) VALUES(1015014,'And also that nation, whom they shall serve, will I judge: and afterward shall they come out with great substance.');
-INSERT INTO t1(docid,words) VALUES(1015015,'And thou shalt go to thy fathers in peace; thou shalt be buried in a good old age.');
-INSERT INTO t1(docid,words) VALUES(1015016,'But in the fourth generation they shall come hither again: for the iniquity of the Amorites is not yet full.');
-INSERT INTO t1(docid,words) VALUES(1015017,'And it came to pass, that, when the sun went down, and it was dark, behold a smoking furnace, and a burning lamp that passed between those pieces.');
-INSERT INTO t1(docid,words) VALUES(1015018,'In the same day the LORD made a covenant with Abram, saying, Unto thy seed have I given this land, from the river of Egypt unto the great river, the river Euphrates:');
-INSERT INTO t1(docid,words) VALUES(1015019,'The Kenites, and the Kenizzites, and the Kadmonites,');
-INSERT INTO t1(docid,words) VALUES(1015020,'And the Hittites, and the Perizzites, and the Rephaims,');
-INSERT INTO t1(docid,words) VALUES(1015021,'And the Amorites, and the Canaanites, and the Girgashites, and the Jebusites.');
-INSERT INTO t1(docid,words) VALUES(1016001,'Now Sarai Abram''s wife bare him no children: and she had an handmaid, an Egyptian, whose name was Hagar.');
-INSERT INTO t1(docid,words) VALUES(1016002,'And Sarai said unto Abram, Behold now, the LORD hath restrained me from bearing: I pray thee, go in unto my maid; it may be that I may obtain children by her. And Abram hearkened to the voice of Sarai.');
-INSERT INTO t1(docid,words) VALUES(1016003,'And Sarai Abram''s wife took Hagar her maid the Egyptian, after Abram had dwelt ten years in the land of Canaan, and gave her to her husband Abram to be his wife.');
-INSERT INTO t1(docid,words) VALUES(1016004,'And he went in unto Hagar, and she conceived: and when she saw that she had conceived, her mistress was despised in her eyes.');
-INSERT INTO t1(docid,words) VALUES(1016005,'And Sarai said unto Abram, My wrong be upon thee: I have given my maid into thy bosom; and when she saw that she had conceived, I was despised in her eyes: the LORD judge between me and thee.');
-INSERT INTO t1(docid,words) VALUES(1016006,'But Abram said unto Sarai, Behold, thy maid is in thine hand; do to her as it pleaseth thee. And when Sarai dealt hardly with her, she fled from her face.');
-INSERT INTO t1(docid,words) VALUES(1016007,'And the angel of the LORD found her by a fountain of water in the wilderness, by the fountain in the way to Shur.');
-INSERT INTO t1(docid,words) VALUES(1016008,'And he said, Hagar, Sarai''s maid, whence camest thou? and whither wilt thou go? And she said, I flee from the face of my mistress Sarai.');
-INSERT INTO t1(docid,words) VALUES(1016009,'And the angel of the LORD said unto her, Return to thy mistress, and submit thyself under her hands.');
-INSERT INTO t1(docid,words) VALUES(1016010,'And the angel of the LORD said unto her, I will multiply thy seed exceedingly, that it shall not be numbered for multitude.');
-INSERT INTO t1(docid,words) VALUES(1016011,'And the angel of the LORD said unto her, Behold, thou art with child and shalt bear a son, and shalt call his name Ishmael; because the LORD hath heard thy affliction.');
-INSERT INTO t1(docid,words) VALUES(1016012,'And he will be a wild man; his hand will be against every man, and every man''s hand against him; and he shall dwell in the presence of all his brethren.');
-INSERT INTO t1(docid,words) VALUES(1016013,'And she called the name of the LORD that spake unto her, Thou God seest me: for she said, Have I also here looked after him that seeth me?');
-INSERT INTO t1(docid,words) VALUES(1016014,'Wherefore the well was called Beerlahairoi; behold, it is between Kadesh and Bered.');
-INSERT INTO t1(docid,words) VALUES(1016015,'And Hagar bare Abram a son: and Abram called his son''s name, which Hagar bare, Ishmael.');
-INSERT INTO t1(docid,words) VALUES(1016016,'And Abram was fourscore and six years old, when Hagar bare Ishmael to Abram.');
-INSERT INTO t1(docid,words) VALUES(1017001,'And when Abram was ninety years old and nine, the LORD appeared to Abram, and said unto him, I am the Almighty God; walk before me, and be thou perfect.');
-INSERT INTO t1(docid,words) VALUES(1017002,'And I will make my covenant between me and thee, and will multiply thee exceedingly.');
-INSERT INTO t1(docid,words) VALUES(1017003,'And Abram fell on his face: and God talked with him, saying,');
-INSERT INTO t1(docid,words) VALUES(1017004,'As for me, behold, my covenant is with thee, and thou shalt be a father of many nations.');
-INSERT INTO t1(docid,words) VALUES(1017005,'Neither shall thy name any more be called Abram, but thy name shall be Abraham; for a father of many nations have I made thee.');
-INSERT INTO t1(docid,words) VALUES(1017006,'And I will make thee exceeding fruitful, and I will make nations of thee, and kings shall come out of thee.');
-INSERT INTO t1(docid,words) VALUES(1017007,'And I will establish my covenant between me and thee and thy seed after thee in their generations for an everlasting covenant, to be a God unto thee, and to thy seed after thee.');
-INSERT INTO t1(docid,words) VALUES(1017008,'And I will give unto thee, and to thy seed after thee, the land wherein thou art a stranger, all the land of Canaan, for an everlasting possession; and I will be their God.');
-INSERT INTO t1(docid,words) VALUES(1017009,'And God said unto Abraham, Thou shalt keep my covenant therefore, thou, and thy seed after thee in their generations.');
-INSERT INTO t1(docid,words) VALUES(1017010,'This is my covenant, which ye shall keep, between me and you and thy seed after thee; Every man child among you shall be circumcised.');
-INSERT INTO t1(docid,words) VALUES(1017011,'And ye shall circumcise the flesh of your foreskin; and it shall be a token of the covenant betwixt me and you.');
-INSERT INTO t1(docid,words) VALUES(1017012,'And he that is eight days old shall be circumcised among you, every man child in your generations, he that is born in the house, or bought with money of any stranger, which is not of thy seed.');
-INSERT INTO t1(docid,words) VALUES(1017013,'He that is born in thy house, and he that is bought with thy money, must needs be circumcised: and my covenant shall be in your flesh for an everlasting covenant.');
-INSERT INTO t1(docid,words) VALUES(1017014,'And the uncircumcised man child whose flesh of his foreskin is not circumcised, that soul shall be cut off from his people; he hath broken my covenant.');
-INSERT INTO t1(docid,words) VALUES(1017015,'And God said unto Abraham, As for Sarai thy wife, thou shalt not call her name Sarai, but Sarah shall her name be.');
-INSERT INTO t1(docid,words) VALUES(1017016,'And I will bless her, and give thee a son also of her: yea, I will bless her, and she shall be a mother of nations; kings of people shall be of her.');
-INSERT INTO t1(docid,words) VALUES(1017017,'Then Abraham fell upon his face, and laughed, and said in his heart, Shall a child be born unto him that is an hundred years old? and shall Sarah, that is ninety years old, bear?');
-INSERT INTO t1(docid,words) VALUES(1017018,'And Abraham said unto God, O that Ishmael might live before thee!');
-INSERT INTO t1(docid,words) VALUES(1017019,'And God said, Sarah thy wife shall bear thee a son indeed; and thou shalt call his name Isaac: and I will establish my covenant with him for an everlasting covenant, and with his seed after him.');
-INSERT INTO t1(docid,words) VALUES(1017020,'And as for Ishmael, I have heard thee: Behold, I have blessed him, and will make him fruitful, and will multiply him exceedingly; twelve princes shall he beget, and I will make him a great nation.');
-INSERT INTO t1(docid,words) VALUES(1017021,'But my covenant will I establish with Isaac, which Sarah shall bear unto thee at this set time in the next year.');
-INSERT INTO t1(docid,words) VALUES(1017022,'And he left off talking with him, and God went up from Abraham.');
-INSERT INTO t1(docid,words) VALUES(1017023,'And Abraham took Ishmael his son, and all that were born in his house, and all that were bought with his money, every male among the men of Abraham''s house; and circumcised the flesh of their foreskin in the selfsame day, as God had said unto him.');
-INSERT INTO t1(docid,words) VALUES(1017024,'And Abraham was ninety years old and nine, when he was circumcised in the flesh of his foreskin.');
-INSERT INTO t1(docid,words) VALUES(1017025,'And Ishmael his son was thirteen years old, when he was circumcised in the flesh of his foreskin.');
-INSERT INTO t1(docid,words) VALUES(1017026,'In the selfsame day was Abraham circumcised, and Ishmael his son.');
-INSERT INTO t1(docid,words) VALUES(1017027,'And all the men of his house, born in the house, and bought with money of the stranger, were circumcised with him.');
-INSERT INTO t1(docid,words) VALUES(1018001,'And the LORD appeared unto him in the plains of Mamre: and he sat in the tent door in the heat of the day;');
-INSERT INTO t1(docid,words) VALUES(1018002,'And he lift up his eyes and looked, and, lo, three men stood by him: and when he saw them, he ran to meet them from the tent door, and bowed himself toward the ground,');
-INSERT INTO t1(docid,words) VALUES(1018003,'And said, My LORD, if now I have found favour in thy sight, pass not away, I pray thee, from thy servant:');
-INSERT INTO t1(docid,words) VALUES(1018004,'Let a little water, I pray you, be fetched, and wash your feet, and rest yourselves under the tree:');
-INSERT INTO t1(docid,words) VALUES(1018005,'And I will fetch a morsel of bread, and comfort ye your hearts; after that ye shall pass on: for therefore are ye come to your servant. And they said, So do, as thou hast said.');
-INSERT INTO t1(docid,words) VALUES(1018006,'And Abraham hastened into the tent unto Sarah, and said, Make ready quickly three measures of fine meal, knead it, and make cakes upon the hearth.');
-INSERT INTO t1(docid,words) VALUES(1018007,'And Abraham ran unto the herd, and fetcht a calf tender and good, and gave it unto a young man; and he hasted to dress it.');
-INSERT INTO t1(docid,words) VALUES(1018008,'And he took butter, and milk, and the calf which he had dressed, and set it before them; and he stood by them under the tree, and they did eat.');
-INSERT INTO t1(docid,words) VALUES(1018009,'And they said unto him, Where is Sarah thy wife? And he said, Behold, in the tent.');
-INSERT INTO t1(docid,words) VALUES(1018010,'And he said, I will certainly return unto thee according to the time of life; and, lo, Sarah thy wife shall have a son. And Sarah heard it in the tent door, which was behind him.');
-INSERT INTO t1(docid,words) VALUES(1018011,'Now Abraham and Sarah were old and well stricken in age; and it ceased to be with Sarah after the manner of women.');
-INSERT INTO t1(docid,words) VALUES(1018012,'Therefore Sarah laughed within herself, saying, After I am waxed old shall I have pleasure, my lord being old also?');
-INSERT INTO t1(docid,words) VALUES(1018013,'And the LORD said unto Abraham, Wherefore did Sarah laugh, saying, Shall I of a surety bear a child, which am old?');
-INSERT INTO t1(docid,words) VALUES(1018014,'Is any thing too hard for the LORD? At the time appointed I will return unto thee, according to the time of life, and Sarah shall have a son.');
-INSERT INTO t1(docid,words) VALUES(1018015,'Then Sarah denied, saying, I laughed not; for she was afraid. And he said, Nay; but thou didst laugh.');
-INSERT INTO t1(docid,words) VALUES(1018016,'And the men rose up from thence, and looked toward Sodom: and Abraham went with them to bring them on the way.');
-INSERT INTO t1(docid,words) VALUES(1018017,'And the LORD said, Shall I hide from Abraham that thing which I do;');
-INSERT INTO t1(docid,words) VALUES(1018018,'Seeing that Abraham shall surely become a great and mighty nation, and all the nations of the earth shall be blessed in him?');
-INSERT INTO t1(docid,words) VALUES(1018019,'For I know him, that he will command his children and his household after him, and they shall keep the way of the LORD, to do justice and judgment; that the LORD may bring upon Abraham that which he hath spoken of him.');
-INSERT INTO t1(docid,words) VALUES(1018020,'And the LORD said, Because the cry of Sodom and Gomorrah is great, and because their sin is very grievous;');
-INSERT INTO t1(docid,words) VALUES(1018021,'I will go down now, and see whether they have done altogether according to the cry of it, which is come unto me; and if not, I will know.');
-INSERT INTO t1(docid,words) VALUES(1018022,'And the men turned their faces from thence, and went toward Sodom: but Abraham stood yet before the LORD.');
-INSERT INTO t1(docid,words) VALUES(1018023,'And Abraham drew near, and said, Wilt thou also destroy the righteous with the wicked?');
-INSERT INTO t1(docid,words) VALUES(1018024,'Peradventure there be fifty righteous within the city: wilt thou also destroy and not spare the place for the fifty righteous that are therein?');
-INSERT INTO t1(docid,words) VALUES(1018025,'That be far from thee to do after this manner, to slay the righteous with the wicked: and that the righteous should be as the wicked, that be far from thee: Shall not the Judge of all the earth do right?');
-INSERT INTO t1(docid,words) VALUES(1018026,'And the LORD said, If I find in Sodom fifty righteous within the city, then I will spare all the place for their sakes.');
-INSERT INTO t1(docid,words) VALUES(1018027,'And Abraham answered and said, Behold now, I have taken upon me to speak unto the LORD, which am but dust and ashes:');
-INSERT INTO t1(docid,words) VALUES(1018028,'Peradventure there shall lack five of the fifty righteous: wilt thou destroy all the city for lack of five? And he said, If I find there forty and five, I will not destroy it.');
-INSERT INTO t1(docid,words) VALUES(1018029,'And he spake unto him yet again, and said, Peradventure there shall be forty found there. And he said, I will not do it for forty''s sake.');
-INSERT INTO t1(docid,words) VALUES(1018030,'And he said unto him, Oh let not the LORD be angry, and I will speak: Peradventure there shall thirty be found there. And he said, I will not do it, if I find thirty there.');
-INSERT INTO t1(docid,words) VALUES(1018031,'And he said, Behold now, I have taken upon me to speak unto the LORD: Peradventure there shall be twenty found there. And he said, I will not destroy it for twenty''s sake.');
-INSERT INTO t1(docid,words) VALUES(1018032,'And he said, Oh let not the LORD be angry, and I will speak yet but this once: Peradventure ten shall be found there. And he said, I will not destroy it for ten''s sake.');
-INSERT INTO t1(docid,words) VALUES(1018033,'And the LORD went his way, as soon as he had left communing with Abraham: and Abraham returned unto his place.');
-INSERT INTO t1(docid,words) VALUES(1019001,'And there came two angels to Sodom at even; and Lot sat in the gate of Sodom: and Lot seeing them rose up to meet them; and he bowed himself with his face toward the ground;');
-INSERT INTO t1(docid,words) VALUES(1019002,'And he said, Behold now, my lords, turn in, I pray you, into your servant''s house, and tarry all night, and wash your feet, and ye shall rise up early, and go on your ways. And they said, Nay; but we will abide in the street all night.');
-INSERT INTO t1(docid,words) VALUES(1019003,'And he pressed upon them greatly; and they turned in unto him, and entered into his house; and he made them a feast, and did bake unleavened bread, and they did eat.');
-INSERT INTO t1(docid,words) VALUES(1019004,'But before they lay down, the men of the city, even the men of Sodom, compassed the house round, both old and young, all the people from every quarter:');
-INSERT INTO t1(docid,words) VALUES(1019005,'And they called unto Lot, and said unto him, Where are the men which came in to thee this night? bring them out unto us, that we may know them.');
-INSERT INTO t1(docid,words) VALUES(1019006,'And Lot went out at the door unto them, and shut the door after him,');
-INSERT INTO t1(docid,words) VALUES(1019007,'And said, I pray you, brethren, do not so wickedly.');
-INSERT INTO t1(docid,words) VALUES(1019008,'Behold now, I have two daughters which have not known man; let me, I pray you, bring them out unto you, and do ye to them as is good in your eyes: only unto these men do nothing; for therefore came they under the shadow of my roof.');
-INSERT INTO t1(docid,words) VALUES(1019009,'And they said, Stand back. And they said again, This one fellow came in to sojourn, and he will needs be a judge: now will we deal worse with thee, than with them. And they pressed sore upon the man, even Lot, and came near to break the door.');
-INSERT INTO t1(docid,words) VALUES(1019010,'But the men put forth their hand, and pulled Lot into the house to them, and shut to the door.');
-INSERT INTO t1(docid,words) VALUES(1019011,'And they smote the men that were at the door of the house with blindness, both small and great: so that they wearied themselves to find the door.');
-INSERT INTO t1(docid,words) VALUES(1019012,'And the men said unto Lot, Hast thou here any besides? son in law, and thy sons, and thy daughters, and whatsoever thou hast in the city, bring them out of this place:');
-INSERT INTO t1(docid,words) VALUES(1019013,'For we will destroy this place, because the cry of them is waxen great before the face of the LORD; and the LORD hath sent us to destroy it.');
-INSERT INTO t1(docid,words) VALUES(1019014,'And Lot went out, and spake unto his sons in law, which married his daughters, and said, Up, get you out of this place; for the LORD will destroy this city. But he seemed as one that mocked unto his sons in law.');
-INSERT INTO t1(docid,words) VALUES(1019015,'And when the morning arose, then the angels hastened Lot, saying, Arise, take thy wife, and thy two daughters, which are here; lest thou be consumed in the iniquity of the city.');
-INSERT INTO t1(docid,words) VALUES(1019016,'And while he lingered, the men laid hold upon his hand, and upon the hand of his wife, and upon the hand of his two daughters; the LORD being merciful unto him: and they brought him forth, and set him without the city.');
-INSERT INTO t1(docid,words) VALUES(1019017,'And it came to pass, when they had brought them forth abroad, that he said, Escape for thy life; look not behind thee, neither stay thou in all the plain; escape to the mountain, lest thou be consumed.');
-INSERT INTO t1(docid,words) VALUES(1019018,'And Lot said unto them, Oh, not so, my LORD:');
-INSERT INTO t1(docid,words) VALUES(1019019,'Behold now, thy servant hath found grace in thy sight, and thou hast magnified thy mercy, which thou hast shewed unto me in saving my life; and I cannot escape to the mountain, lest some evil take me, and I die:');
-INSERT INTO t1(docid,words) VALUES(1019020,'Behold now, this city is near to flee unto, and it is a little one: Oh, let me escape thither, (is it not a little one?) and my soul shall live.');
-INSERT INTO t1(docid,words) VALUES(1019021,'And he said unto him, See, I have accepted thee concerning this thing also, that I will not overthrow this city, for the which thou hast spoken.');
-INSERT INTO t1(docid,words) VALUES(1019022,'Haste thee, escape thither; for I cannot do anything till thou be come thither. Therefore the name of the city was called Zoar.');
-INSERT INTO t1(docid,words) VALUES(1019023,'The sun was risen upon the earth when Lot entered into Zoar.');
-INSERT INTO t1(docid,words) VALUES(1019024,'Then the LORD rained upon Sodom and upon Gomorrah brimstone and fire from the LORD out of heaven;');
-INSERT INTO t1(docid,words) VALUES(1019025,'And he overthrew those cities, and all the plain, and all the inhabitants of the cities, and that which grew upon the ground.');
-INSERT INTO t1(docid,words) VALUES(1019026,'But his wife looked back from behind him, and she became a pillar of salt.');
-INSERT INTO t1(docid,words) VALUES(1019027,'And Abraham gat up early in the morning to the place where he stood before the LORD:');
-INSERT INTO t1(docid,words) VALUES(1019028,'And he looked toward Sodom and Gomorrah, and toward all the land of the plain, and beheld, and, lo, the smoke of the country went up as the smoke of a furnace.');
-INSERT INTO t1(docid,words) VALUES(1019029,'And it came to pass, when God destroyed the cities of the plain, that God remembered Abraham, and sent Lot out of the midst of the overthrow, when he overthrew the cities in the which Lot dwelt.');
-INSERT INTO t1(docid,words) VALUES(1019030,'And Lot went up out of Zoar, and dwelt in the mountain, and his two daughters with him; for he feared to dwell in Zoar: and he dwelt in a cave, he and his two daughters.');
-INSERT INTO t1(docid,words) VALUES(1019031,'And the firstborn said unto the younger, Our father is old, and there is not a man in the earth to come in unto us after the manner of all the earth:');
-INSERT INTO t1(docid,words) VALUES(1019032,'Come, let us make our father drink wine, and we will lie with him, that we may preserve seed of our father.');
-INSERT INTO t1(docid,words) VALUES(1019033,'And they made their father drink wine that night: and the firstborn went in, and lay with her father; and he perceived not when she lay down, nor when she arose.');
-INSERT INTO t1(docid,words) VALUES(1019034,'And it came to pass on the morrow, that the firstborn said unto the younger, Behold, I lay yesternight with my father: let us make him drink wine this night also; and go thou in, and lie with him, that we may preserve seed of our father.');
-INSERT INTO t1(docid,words) VALUES(1019035,'And they made their father drink wine that night also: and the younger arose, and lay with him; and he perceived not when she lay down, nor when she arose.');
-INSERT INTO t1(docid,words) VALUES(1019036,'Thus were both the daughters of Lot with child by their father.');
-INSERT INTO t1(docid,words) VALUES(1019037,'And the first born bare a son, and called his name Moab: the same is the father of the Moabites unto this day.');
-INSERT INTO t1(docid,words) VALUES(1019038,'And the younger, she also bare a son, and called his name Benammi: the same is the father of the children of Ammon unto this day.');
-INSERT INTO t1(docid,words) VALUES(1020001,'And Abraham journeyed from thence toward the south country, and dwelled between Kadesh and Shur, and sojourned in Gerar.');
-INSERT INTO t1(docid,words) VALUES(1020002,'And Abraham said of Sarah his wife, She is my sister: and Abimelech king of Gerar sent, and took Sarah.');
-INSERT INTO t1(docid,words) VALUES(1020003,'But God came to Abimelech in a dream by night, and said to him, Behold, thou art but a dead man, for the woman which thou hast taken; for she is a man''s wife.');
-INSERT INTO t1(docid,words) VALUES(1020004,'But Abimelech had not come near her: and he said, LORD, wilt thou slay also a righteous nation?');
-INSERT INTO t1(docid,words) VALUES(1020005,'Said he not unto me, She is my sister? and she, even she herself said, He is my brother: in the integrity of my heart and innocency of my hands have I done this.');
-INSERT INTO t1(docid,words) VALUES(1020006,'And God said unto him in a dream, Yea, I know that thou didst this in the integrity of thy heart; for I also withheld thee from sinning against me: therefore suffered I thee not to touch her.');
-INSERT INTO t1(docid,words) VALUES(1020007,'Now therefore restore the man his wife; for he is a prophet, and he shall pray for thee, and thou shalt live: and if thou restore her not, know thou that thou shalt surely die, thou, and all that are thine.');
-INSERT INTO t1(docid,words) VALUES(1020008,'Therefore Abimelech rose early in the morning, and called all his servants, and told all these things in their ears: and the men were sore afraid.');
-INSERT INTO t1(docid,words) VALUES(1020009,'Then Abimelech called Abraham, and said unto him, What hast thou done unto us? and what have I offended thee, that thou hast brought on me and on my kingdom a great sin? thou hast done deeds unto me that ought not to be done.');
-INSERT INTO t1(docid,words) VALUES(1020010,'And Abimelech said unto Abraham, What sawest thou, that thou hast done this thing?');
-INSERT INTO t1(docid,words) VALUES(1020011,'And Abraham said, Because I thought, Surely the fear of God is not in this place; and they will slay me for my wife''s sake.');
-INSERT INTO t1(docid,words) VALUES(1020012,'And yet indeed she is my sister; she is the daughter of my father, but not the daughter of my mother; and she became my wife.');
-INSERT INTO t1(docid,words) VALUES(1020013,'And it came to pass, when God caused me to wander from my father''s house, that I said unto her, This is thy kindness which thou shalt shew unto me; at every place whither we shall come, say of me, He is my brother.');
-INSERT INTO t1(docid,words) VALUES(1020014,'And Abimelech took sheep, and oxen, and menservants, and womenservants, and gave them unto Abraham, and restored him Sarah his wife.');
-INSERT INTO t1(docid,words) VALUES(1020015,'And Abimelech said, Behold, my land is before thee: dwell where it pleaseth thee.');
-INSERT INTO t1(docid,words) VALUES(1020016,'And unto Sarah he said, Behold, I have given thy brother a thousand pieces of silver: behold, he is to thee a covering of the eyes, unto all that are with thee, and with all other: thus she was reproved.');
-INSERT INTO t1(docid,words) VALUES(1020017,'So Abraham prayed unto God: and God healed Abimelech, and his wife, and his maidservants; and they bare children.');
-INSERT INTO t1(docid,words) VALUES(1020018,'For the LORD had fast closed up all the wombs of the house of Abimelech, because of Sarah Abraham''s wife.');
-INSERT INTO t1(docid,words) VALUES(1021001,'And the LORD visited Sarah as he had said, and the LORD did unto Sarah as he had spoken.');
-INSERT INTO t1(docid,words) VALUES(1021002,'For Sarah conceived, and bare Abraham a son in his old age, at the set time of which God had spoken to him.');
-INSERT INTO t1(docid,words) VALUES(1021003,'And Abraham called the name of his son that was born unto him, whom Sarah bare to him, Isaac.');
-INSERT INTO t1(docid,words) VALUES(1021004,'And Abraham circumcised his son Isaac being eight days old, as God had commanded him.');
-INSERT INTO t1(docid,words) VALUES(1021005,'And Abraham was an hundred years old, when his son Isaac was born unto him.');
-INSERT INTO t1(docid,words) VALUES(1021006,'And Sarah said, God hath made me to laugh, so that all that hear will laugh with me.');
-INSERT INTO t1(docid,words) VALUES(1021007,'And she said, Who would have said unto Abraham, that Sarah should have given children suck? for I have born him a son in his old age.');
-INSERT INTO t1(docid,words) VALUES(1021008,'And the child grew, and was weaned: and Abraham made a great feast the same day that Isaac was weaned.');
-INSERT INTO t1(docid,words) VALUES(1021009,'And Sarah saw the son of Hagar the Egyptian, which she had born unto Abraham, mocking.');
-INSERT INTO t1(docid,words) VALUES(1021010,'Wherefore she said unto Abraham, Cast out this bondwoman and her son: for the son of this bondwoman shall not be heir with my son, even with Isaac.');
-INSERT INTO t1(docid,words) VALUES(1021011,'And the thing was very grievous in Abraham''s sight because of his son.');
-INSERT INTO t1(docid,words) VALUES(1021012,'And God said unto Abraham, Let it not be grievous in thy sight because of the lad, and because of thy bondwoman; in all that Sarah hath said unto thee, hearken unto her voice; for in Isaac shall thy seed be called.');
-INSERT INTO t1(docid,words) VALUES(1021013,'And also of the son of the bondwoman will I make a nation, because he is thy seed.');
-INSERT INTO t1(docid,words) VALUES(1021014,'And Abraham rose up early in the morning, and took bread, and a bottle of water, and gave it unto Hagar, putting it on her shoulder, and the child, and sent her away: and she departed, and wandered in the wilderness of Beersheba.');
-INSERT INTO t1(docid,words) VALUES(1021015,'And the water was spent in the bottle, and she cast the child under one of the shrubs.');
-INSERT INTO t1(docid,words) VALUES(1021016,'And she went, and sat her down over against him a good way off, as it were a bow shot: for she said, Let me not see the death of the child. And she sat over against him, and lift up her voice, and wept.');
-INSERT INTO t1(docid,words) VALUES(1021017,'And God heard the voice of the lad; and the angel of God called to Hagar out of heaven, and said unto her, What aileth thee, Hagar? fear not; for God hath heard the voice of the lad where he is.');
-INSERT INTO t1(docid,words) VALUES(1021018,'Arise, lift up the lad, and hold him in thine hand; for I will make him a great nation.');
-INSERT INTO t1(docid,words) VALUES(1021019,'And God opened her eyes, and she saw a well of water; and she went, and filled the bottle with water, and gave the lad drink.');
-INSERT INTO t1(docid,words) VALUES(1021020,'And God was with the lad; and he grew, and dwelt in the wilderness, and became an archer.');
-INSERT INTO t1(docid,words) VALUES(1021021,'And he dwelt in the wilderness of Paran: and his mother took him a wife out of the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1021022,'And it came to pass at that time, that Abimelech and Phichol the chief captain of his host spake unto Abraham, saying, God is with thee in all that thou doest:');
-INSERT INTO t1(docid,words) VALUES(1021023,'Now therefore swear unto me here by God that thou wilt not deal falsely with me, nor with my son, nor with my son''s son: but according to the kindness that I have done unto thee, thou shalt do unto me, and to the land wherein thou hast sojourned.');
-INSERT INTO t1(docid,words) VALUES(1021024,'And Abraham said, I will swear.');
-INSERT INTO t1(docid,words) VALUES(1021025,'And Abraham reproved Abimelech because of a well of water, which Abimelech''s servants had violently taken away.');
-INSERT INTO t1(docid,words) VALUES(1021026,'And Abimelech said, I wot not who hath done this thing; neither didst thou tell me, neither yet heard I of it, but to day.');
-INSERT INTO t1(docid,words) VALUES(1021027,'And Abraham took sheep and oxen, and gave them unto Abimelech; and both of them made a covenant.');
-INSERT INTO t1(docid,words) VALUES(1021028,'And Abraham set seven ewe lambs of the flock by themselves.');
-INSERT INTO t1(docid,words) VALUES(1021029,'And Abimelech said unto Abraham, What mean these seven ewe lambs which thou hast set by themselves?');
-INSERT INTO t1(docid,words) VALUES(1021030,'And he said, For these seven ewe lambs shalt thou take of my hand, that they may be a witness unto me, that I have digged this well.');
-INSERT INTO t1(docid,words) VALUES(1021031,'Wherefore he called that place Beersheba; because there they sware both of them.');
-INSERT INTO t1(docid,words) VALUES(1021032,'Thus they made a covenant at Beersheba: then Abimelech rose up, and Phichol the chief captain of his host, and they returned into the land of the Philistines.');
-INSERT INTO t1(docid,words) VALUES(1021033,'And Abraham planted a grove in Beersheba, and called there on the name of the LORD, the everlasting God.');
-INSERT INTO t1(docid,words) VALUES(1021034,'And Abraham sojourned in the Philistines'' land many days.');
-INSERT INTO t1(docid,words) VALUES(1022001,'And it came to pass after these things, that God did tempt Abraham, and said unto him, Abraham: and he said, Behold, here I am.');
-INSERT INTO t1(docid,words) VALUES(1022002,'And he said, Take now thy son, thine only son Isaac, whom thou lovest, and get thee into the land of Moriah; and offer him there for a burnt offering upon one of the mountains which I will tell thee of.');
-INSERT INTO t1(docid,words) VALUES(1022003,'And Abraham rose up early in the morning, and saddled his ass, and took two of his young men with him, and Isaac his son, and clave the wood for the burnt offering, and rose up, and went unto the place of which God had told him.');
-INSERT INTO t1(docid,words) VALUES(1022004,'Then on the third day Abraham lifted up his eyes, and saw the place afar off.');
-INSERT INTO t1(docid,words) VALUES(1022005,'And Abraham said unto his young men, Abide ye here with the ass; and I and the lad will go yonder and worship, and come again to you.');
-INSERT INTO t1(docid,words) VALUES(1022006,'And Abraham took the wood of the burnt offering, and laid it upon Isaac his son; and he took the fire in his hand, and a knife; and they went both of them together.');
-INSERT INTO t1(docid,words) VALUES(1022007,'And Isaac spake unto Abraham his father, and said, My father: and he said, Here am I, my son. And he said, Behold the fire and the wood: but where is the lamb for a burnt offering?');
-INSERT INTO t1(docid,words) VALUES(1022008,'And Abraham said, My son, God will provide himself a lamb for a burnt offering: so they went both of them together.');
-INSERT INTO t1(docid,words) VALUES(1022009,'And they came to the place which God had told him of; and Abraham built an altar there, and laid the wood in order, and bound Isaac his son, and laid him on the altar upon the wood.');
-INSERT INTO t1(docid,words) VALUES(1022010,'And Abraham stretched forth his hand, and took the knife to slay his son.');
-INSERT INTO t1(docid,words) VALUES(1022011,'And the angel of the LORD called unto him out of heaven, and said, Abraham, Abraham: and he said, Here am I.');
-INSERT INTO t1(docid,words) VALUES(1022012,'And he said, Lay not thine hand upon the lad, neither do thou any thing unto him: for now I know that thou fearest God, seeing thou hast not withheld thy son, thine only son from me.');
-INSERT INTO t1(docid,words) VALUES(1022013,'And Abraham lifted up his eyes, and looked, and behold behind him a ram caught in a thicket by his horns: and Abraham went and took the ram, and offered him up for a burnt offering in the stead of his son.');
-INSERT INTO t1(docid,words) VALUES(1022014,'And Abraham called the name of that place Jehovahjireh: as it is said to this day, In the mount of the LORD it shall be seen.');
-INSERT INTO t1(docid,words) VALUES(1022015,'And the angel of the LORD called unto Abraham out of heaven the second time,');
-INSERT INTO t1(docid,words) VALUES(1022016,'And said, By myself have I sworn, saith the LORD, for because thou hast done this thing, and hast not withheld thy son, thine only son:');
-INSERT INTO t1(docid,words) VALUES(1022017,'That in blessing I will bless thee, and in multiplying I will multiply thy seed as the stars of the heaven, and as the sand which is upon the sea shore; and thy seed shall possess the gate of his enemies;');
-INSERT INTO t1(docid,words) VALUES(1022018,'And in thy seed shall all the nations of the earth be blessed; because thou hast obeyed my voice.');
-INSERT INTO t1(docid,words) VALUES(1022019,'So Abraham returned unto his young men, and they rose up and went together to Beersheba; and Abraham dwelt at Beersheba.');
-INSERT INTO t1(docid,words) VALUES(1022020,'And it came to pass after these things, that it was told Abraham, saying, Behold, Milcah, she hath also born children unto thy brother Nahor;');
-INSERT INTO t1(docid,words) VALUES(1022021,'Huz his firstborn, and Buz his brother, and Kemuel the father of Aram,');
-INSERT INTO t1(docid,words) VALUES(1022022,'And Chesed, and Hazo, and Pildash, and Jidlaph, and Bethuel.');
-INSERT INTO t1(docid,words) VALUES(1022023,'And Bethuel begat Rebekah: these eight Milcah did bear to Nahor, Abraham''s brother.');
-INSERT INTO t1(docid,words) VALUES(1022024,'And his concubine, whose name was Reumah, she bare also Tebah, and Gaham, and Thahash, and Maachah.');
-INSERT INTO t1(docid,words) VALUES(1023001,'And Sarah was an hundred and seven and twenty years old: these were the years of the life of Sarah.');
-INSERT INTO t1(docid,words) VALUES(1023002,'And Sarah died in Kirjatharba; the same is Hebron in the land of Canaan: and Abraham came to mourn for Sarah, and to weep for her.');
-INSERT INTO t1(docid,words) VALUES(1023003,'And Abraham stood up from before his dead, and spake unto the sons of Heth, saying,');
-INSERT INTO t1(docid,words) VALUES(1023004,'I am a stranger and a sojourner with you: give me a possession of a buryingplace with you, that I may bury my dead out of my sight.');
-INSERT INTO t1(docid,words) VALUES(1023005,'And the children of Heth answered Abraham, saying unto him,');
-INSERT INTO t1(docid,words) VALUES(1023006,'Hear us, my lord: thou art a mighty prince among us: in the choice of our sepulchres bury thy dead; none of us shall withhold from thee his sepulchre, but that thou mayest bury thy dead.');
-INSERT INTO t1(docid,words) VALUES(1023007,'And Abraham stood up, and bowed himself to the people of the land, even to the children of Heth.');
-INSERT INTO t1(docid,words) VALUES(1023008,'And he communed with them, saying, If it be your mind that I should bury my dead out of my sight; hear me, and intreat for me to Ephron the son of Zohar,');
-INSERT INTO t1(docid,words) VALUES(1023009,'That he may give me the cave of Machpelah, which he hath, which is in the end of his field; for as much money as it is worth he shall give it me for a possession of a buryingplace amongst you.');
-INSERT INTO t1(docid,words) VALUES(1023010,'And Ephron dwelt among the children of Heth: and Ephron the Hittite answered Abraham in the audience of the children of Heth, even of all that went in at the gate of his city, saying,');
-INSERT INTO t1(docid,words) VALUES(1023011,'Nay, my lord, hear me: the field give I thee, and the cave that is therein, I give it thee; in the presence of the sons of my people give I it thee: bury thy dead.');
-INSERT INTO t1(docid,words) VALUES(1023012,'And Abraham bowed down himself before the people of the land.');
-INSERT INTO t1(docid,words) VALUES(1023013,'And he spake unto Ephron in the audience of the people of the land, saying, But if thou wilt give it, I pray thee, hear me: I will give thee money for the field; take it of me, and I will bury my dead there.');
-INSERT INTO t1(docid,words) VALUES(1023014,'And Ephron answered Abraham, saying unto him,');
-INSERT INTO t1(docid,words) VALUES(1023015,'My lord, hearken unto me: the land is worth four hundred shekels of silver; what is that betwixt me and thee? bury therefore thy dead.');
-INSERT INTO t1(docid,words) VALUES(1023016,'And Abraham hearkened unto Ephron; and Abraham weighed to Ephron the silver, which he had named in the audience of the sons of Heth, four hundred shekels of silver, current money with the merchant.');
-INSERT INTO t1(docid,words) VALUES(1023017,'And the field of Ephron which was in Machpelah, which was before Mamre, the field, and the cave which was therein, and all the trees that were in the field, that were in all the borders round about, were made sure');
-INSERT INTO t1(docid,words) VALUES(1023018,'Unto Abraham for a possession in the presence of the children of Heth, before all that went in at the gate of his city.');
-INSERT INTO t1(docid,words) VALUES(1023019,'And after this, Abraham buried Sarah his wife in the cave of the field of Machpelah before Mamre: the same is Hebron in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1023020,'And the field, and the cave that is therein, were made sure unto Abraham for a possession of a buryingplace by the sons of Heth.');
-INSERT INTO t1(docid,words) VALUES(1024001,'And Abraham was old, and well stricken in age: and the LORD had blessed Abraham in all things.');
-INSERT INTO t1(docid,words) VALUES(1024002,'And Abraham said unto his eldest servant of his house, that ruled over all that he had, Put, I pray thee, thy hand under my thigh:');
-INSERT INTO t1(docid,words) VALUES(1024003,'And I will make thee swear by the LORD, the God of heaven, and the God of the earth, that thou shalt not take a wife unto my son of the daughters of the Canaanites, among whom I dwell:');
-INSERT INTO t1(docid,words) VALUES(1024004,'But thou shalt go unto my country, and to my kindred, and take a wife unto my son Isaac.');
-INSERT INTO t1(docid,words) VALUES(1024005,'And the servant said unto him, Peradventure the woman will not be willing to follow me unto this land: must I needs bring thy son again unto the land from whence thou camest?');
-INSERT INTO t1(docid,words) VALUES(1024006,'And Abraham said unto him, Beware thou that thou bring not my son thither again.');
-INSERT INTO t1(docid,words) VALUES(1024007,'The LORD God of heaven, which took me from my father''s house, and from the land of my kindred, and which spake unto me, and that sware unto me, saying, Unto thy seed will I give this land; he shall send his angel before thee, and thou shalt take a wife unto my son from thence.');
-INSERT INTO t1(docid,words) VALUES(1024008,'And if the woman will not be willing to follow thee, then thou shalt be clear from this my oath: only bring not my son thither again.');
-INSERT INTO t1(docid,words) VALUES(1024009,'And the servant put his hand under the thigh of Abraham his master, and sware to him concerning that matter.');
-INSERT INTO t1(docid,words) VALUES(1024010,'And the servant took ten camels of the camels of his master, and departed; for all the goods of his master were in his hand: and he arose, and went to Mesopotamia, unto the city of Nahor.');
-INSERT INTO t1(docid,words) VALUES(1024011,'And he made his camels to kneel down without the city by a well of water at the time of the evening, even the time that women go out to draw water.');
-INSERT INTO t1(docid,words) VALUES(1024012,'And he said O LORD God of my master Abraham, I pray thee, send me good speed this day, and shew kindness unto my master Abraham.');
-INSERT INTO t1(docid,words) VALUES(1024013,'Behold, I stand here by the well of water; and the daughters of the men of the city come out to draw water:');
-INSERT INTO t1(docid,words) VALUES(1024014,'And let it come to pass, that the damsel to whom I shall say, Let down thy pitcher, I pray thee, that I may drink; and she shall say, Drink, and I will give thy camels drink also: let the same be she that thou hast appointed for thy servant Isaac; and thereby shall I know that thou hast shewed kindness unto my master.');
-INSERT INTO t1(docid,words) VALUES(1024015,'And it came to pass, before he had done speaking, that, behold, Rebekah came out, who was born to Bethuel, son of Milcah, the wife of Nahor, Abraham''s brother, with her pitcher upon her shoulder.');
-INSERT INTO t1(docid,words) VALUES(1024016,'And the damsel was very fair to look upon, a virgin, neither had any man known her: and she went down to the well, and filled her pitcher, and came up.');
-INSERT INTO t1(docid,words) VALUES(1024017,'And the servant ran to meet her, and said, Let me, I pray thee, drink a little water of thy pitcher.');
-INSERT INTO t1(docid,words) VALUES(1024018,'And she said, Drink, my lord: and she hasted, and let down her pitcher upon her hand, and gave him drink.');
-INSERT INTO t1(docid,words) VALUES(1024019,'And when she had done giving him drink, she said, I will draw water for thy camels also, until they have done drinking.');
-INSERT INTO t1(docid,words) VALUES(1024020,'And she hasted, and emptied her pitcher into the trough, and ran again unto the well to draw water, and drew for all his camels.');
-INSERT INTO t1(docid,words) VALUES(1024021,'And the man wondering at her held his peace, to wit whether the LORD had made his journey prosperous or not.');
-INSERT INTO t1(docid,words) VALUES(1024022,'And it came to pass, as the camels had done drinking, that the man took a golden earring of half a shekel weight, and two bracelets for her hands of ten shekels weight of gold;');
-INSERT INTO t1(docid,words) VALUES(1024023,'And said, Whose daughter art thou? tell me, I pray thee: is there room in thy father''s house for us to lodge in?');
-INSERT INTO t1(docid,words) VALUES(1024024,'And she said unto him, I am the daughter of Bethuel the son of Milcah, which she bare unto Nahor.');
-INSERT INTO t1(docid,words) VALUES(1024025,'She said moreover unto him, We have both straw and provender enough, and room to lodge in.');
-INSERT INTO t1(docid,words) VALUES(1024026,'And the man bowed down his head, and worshipped the LORD.');
-INSERT INTO t1(docid,words) VALUES(1024027,'And he said, Blessed be the LORD God of my master Abraham, who hath not left destitute my master of his mercy and his truth: I being in the way, the LORD led me to the house of my master''s brethren.');
-INSERT INTO t1(docid,words) VALUES(1024028,'And the damsel ran, and told them of her mother''s house these things.');
-INSERT INTO t1(docid,words) VALUES(1024029,'And Rebekah had a brother, and his name was Laban: and Laban ran out unto the man, unto the well.');
-INSERT INTO t1(docid,words) VALUES(1024030,'And it came to pass, when he saw the earring and bracelets upon his sister''s hands, and when he heard the words of Rebekah his sister, saying, Thus spake the man unto me; that he came unto the man; and, behold, he stood by the camels at the well.');
-INSERT INTO t1(docid,words) VALUES(1024031,'And he said, Come in, thou blessed of the LORD; wherefore standest thou without? for I have prepared the house, and room for the camels.');
-INSERT INTO t1(docid,words) VALUES(1024032,'And the man came into the house: and he ungirded his camels, and gave straw and provender for the camels, and water to wash his feet, and the men''s feet that were with him.');
-INSERT INTO t1(docid,words) VALUES(1024033,'And there was set meat before him to eat: but he said, I will not eat, until I have told mine errand. And he said, Speak on.');
-INSERT INTO t1(docid,words) VALUES(1024034,'And he said, I am Abraham''s servant.');
-INSERT INTO t1(docid,words) VALUES(1024035,'And the LORD hath blessed my master greatly; and he is become great: and he hath given him flocks, and herds, and silver, and gold, and menservants, and maidservants, and camels, and asses.');
-INSERT INTO t1(docid,words) VALUES(1024036,'And Sarah my master''s wife bare a son to my master when she was old: and unto him hath he given all that he hath.');
-INSERT INTO t1(docid,words) VALUES(1024037,'And my master made me swear, saying, Thou shalt not take a wife to my son of the daughters of the Canaanites, in whose land I dwell:');
-INSERT INTO t1(docid,words) VALUES(1024038,'But thou shalt go unto my father''s house, and to my kindred, and take a wife unto my son.');
-INSERT INTO t1(docid,words) VALUES(1024039,'And I said unto my master, Peradventure the woman will not follow me.');
-INSERT INTO t1(docid,words) VALUES(1024040,'And he said unto me, The LORD, before whom I walk, will send his angel with thee, and prosper thy way; and thou shalt take a wife for my son of my kindred, and of my father''s house:');
-INSERT INTO t1(docid,words) VALUES(1024041,'Then shalt thou be clear from this my oath, when thou comest to my kindred; and if they give not thee one, thou shalt be clear from my oath.');
-INSERT INTO t1(docid,words) VALUES(1024042,'And I came this day unto the well, and said, O LORD God of my master Abraham, if now thou do prosper my way which I go:');
-INSERT INTO t1(docid,words) VALUES(1024043,'Behold, I stand by the well of water; and it shall come to pass, that when the virgin cometh forth to draw water, and I say to her, Give me, I pray thee, a little water of thy pitcher to drink;');
-INSERT INTO t1(docid,words) VALUES(1024044,'And she say to me, Both drink thou, and I will also draw for thy camels: let the same be the woman whom the LORD hath appointed out for my master''s son.');
-INSERT INTO t1(docid,words) VALUES(1024045,'And before I had done speaking in mine heart, behold, Rebekah came forth with her pitcher on her shoulder; and she went down unto the well, and drew water: and I said unto her, Let me drink, I pray thee.');
-INSERT INTO t1(docid,words) VALUES(1024046,'And she made haste, and let down her pitcher from her shoulder, and said, Drink, and I will give thy camels drink also: so I drank, and she made the camels drink also.');
-INSERT INTO t1(docid,words) VALUES(1024047,'And I asked her, and said, Whose daughter art thou? And she said, the daughter of Bethuel, Nahor''s son, whom Milcah bare unto him: and I put the earring upon her face, and the bracelets upon her hands.');
-INSERT INTO t1(docid,words) VALUES(1024048,'And I bowed down my head, and worshipped the LORD, and blessed the LORD God of my master Abraham, which had led me in the right way to take my master''s brother''s daughter unto his son.');
-INSERT INTO t1(docid,words) VALUES(1024049,'And now if ye will deal kindly and truly with my master, tell me: and if not, tell me; that I may turn to the right hand, or to the left.');
-INSERT INTO t1(docid,words) VALUES(1024050,'Then Laban and Bethuel answered and said, The thing proceedeth from the LORD: we cannot speak unto thee bad or good.');
-INSERT INTO t1(docid,words) VALUES(1024051,'Behold, Rebekah is before thee, take her, and go, and let her be thy master''s son''s wife, as the LORD hath spoken.');
-INSERT INTO t1(docid,words) VALUES(1024052,'And it came to pass, that, when Abraham''s servant heard their words, he worshipped the LORD, bowing himself to the earth.');
-INSERT INTO t1(docid,words) VALUES(1024053,'And the servant brought forth jewels of silver, and jewels of gold, and raiment, and gave them to Rebekah: he gave also to her brother and to her mother precious things.');
-INSERT INTO t1(docid,words) VALUES(1024054,'And they did eat and drink, he and the men that were with him, and tarried all night; and they rose up in the morning, and he said, Send me away unto my master.');
-INSERT INTO t1(docid,words) VALUES(1024055,'And her brother and her mother said, Let the damsel abide with us a few days, at the least ten; after that she shall go.');
-INSERT INTO t1(docid,words) VALUES(1024056,'And he said unto them, Hinder me not, seeing the LORD hath prospered my way; send me away that I may go to my master.');
-INSERT INTO t1(docid,words) VALUES(1024057,'And they said, We will call the damsel, and enquire at her mouth.');
-INSERT INTO t1(docid,words) VALUES(1024058,'And they called Rebekah, and said unto her, Wilt thou go with this man? And she said, I will go.');
-INSERT INTO t1(docid,words) VALUES(1024059,'And they sent away Rebekah their sister, and her nurse, and Abraham''s servant, and his men.');
-INSERT INTO t1(docid,words) VALUES(1024060,'And they blessed Rebekah, and said unto her, Thou art our sister, be thou the mother of thousands of millions, and let thy seed possess the gate of those which hate them.');
-INSERT INTO t1(docid,words) VALUES(1024061,'And Rebekah arose, and her damsels, and they rode upon the camels, and followed the man: and the servant took Rebekah, and went his way.');
-INSERT INTO t1(docid,words) VALUES(1024062,'And Isaac came from the way of the well Lahairoi; for he dwelt in the south country.');
-INSERT INTO t1(docid,words) VALUES(1024063,'And Isaac went out to meditate in the field at the eventide: and he lifted up his eyes, and saw, and, behold, the camels were coming.');
-INSERT INTO t1(docid,words) VALUES(1024064,'And Rebekah lifted up her eyes, and when she saw Isaac, she lighted off the camel.');
-INSERT INTO t1(docid,words) VALUES(1024065,'For she had said unto the servant, What man is this that walketh in the field to meet us? And the servant had said, It is my master: therefore she took a vail, and covered herself.');
-INSERT INTO t1(docid,words) VALUES(1024066,'And the servant told Isaac all things that he had done.');
-INSERT INTO t1(docid,words) VALUES(1024067,'And Isaac brought her into his mother Sarah''s tent, and took Rebekah, and she became his wife; and he loved her: and Isaac was comforted after his mother''s death.');
-INSERT INTO t1(docid,words) VALUES(1025001,'Then again Abraham took a wife, and her name was Keturah.');
-INSERT INTO t1(docid,words) VALUES(1025002,'And she bare him Zimran, and Jokshan, and Medan, and Midian, and Ishbak, and Shuah.');
-INSERT INTO t1(docid,words) VALUES(1025003,'And Jokshan begat Sheba, and Dedan. And the sons of Dedan were Asshurim, and Letushim, and Leummim.');
-INSERT INTO t1(docid,words) VALUES(1025004,'And the sons of Midian; Ephah, and Epher, and Hanoch, and Abidah, and Eldaah. All these were the children of Keturah.');
-INSERT INTO t1(docid,words) VALUES(1025005,'And Abraham gave all that he had unto Isaac.');
-INSERT INTO t1(docid,words) VALUES(1025006,'But unto the sons of the concubines, which Abraham had, Abraham gave gifts, and sent them away from Isaac his son, while he yet lived, eastward, unto the east country.');
-INSERT INTO t1(docid,words) VALUES(1025007,'And these are the days of the years of Abraham''s life which he lived, an hundred threescore and fifteen years.');
-INSERT INTO t1(docid,words) VALUES(1025008,'Then Abraham gave up the ghost, and died in a good old age, an old man, and full of years; and was gathered to his people.');
-INSERT INTO t1(docid,words) VALUES(1025009,'And his sons Isaac and Ishmael buried him in the cave of Machpelah, in the field of Ephron the son of Zohar the Hittite, which is before Mamre;');
-INSERT INTO t1(docid,words) VALUES(1025010,'The field which Abraham purchased of the sons of Heth: there was Abraham buried, and Sarah his wife.');
-INSERT INTO t1(docid,words) VALUES(1025011,'And it came to pass after the death of Abraham, that God blessed his son Isaac; and Isaac dwelt by the well Lahairoi.');
-INSERT INTO t1(docid,words) VALUES(1025012,'Now these are the generations of Ishmael, Abraham''s son, whom Hagar the Egyptian, Sarah''s handmaid, bare unto Abraham:');
-INSERT INTO t1(docid,words) VALUES(1025013,'And these are the names of the sons of Ishmael, by their names, according to their generations: the firstborn of Ishmael, Nebajoth; and Kedar, and Adbeel, and Mibsam,');
-INSERT INTO t1(docid,words) VALUES(1025014,'And Mishma, and Dumah, and Massa,');
-INSERT INTO t1(docid,words) VALUES(1025015,'Hadar, and Tema, Jetur, Naphish, and Kedemah:');
-INSERT INTO t1(docid,words) VALUES(1025016,'These are the sons of Ishmael, and these are their names, by their towns, and by their castles; twelve princes according to their nations.');
-INSERT INTO t1(docid,words) VALUES(1025017,'And these are the years of the life of Ishmael, an hundred and thirty and seven years: and he gave up the ghost and died; and was gathered unto his people.');
-INSERT INTO t1(docid,words) VALUES(1025018,'And they dwelt from Havilah unto Shur, that is before Egypt, as thou goest toward Assyria: and he died in the presence of all his brethren.');
-INSERT INTO t1(docid,words) VALUES(1025019,'And these are the generations of Isaac, Abraham''s son: Abraham begat Isaac:');
-INSERT INTO t1(docid,words) VALUES(1025020,'And Isaac was forty years old when he took Rebekah to wife, the daughter of Bethuel the Syrian of Padanaram, the sister to Laban the Syrian.');
-INSERT INTO t1(docid,words) VALUES(1025021,'And Isaac intreated the LORD for his wife, because she was barren: and the LORD was intreated of him, and Rebekah his wife conceived.');
-INSERT INTO t1(docid,words) VALUES(1025022,'And the children struggled together within her; and she said, If it be so, why am I thus? And she went to enquire of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1025023,'And the LORD said unto her, Two nations are in thy womb, and two manner of people shall be separated from thy bowels; and the one people shall be stronger than the other people; and the elder shall serve the younger.');
-INSERT INTO t1(docid,words) VALUES(1025024,'And when her days to be delivered were fulfilled, behold, there were twins in her womb.');
-INSERT INTO t1(docid,words) VALUES(1025025,'And the first came out red, all over like an hairy garment; and they called his name Esau.');
-INSERT INTO t1(docid,words) VALUES(1025026,'And after that came his brother out, and his hand took hold on Esau''s heel; and his name was called Jacob: and Isaac was threescore years old when she bare them.');
-INSERT INTO t1(docid,words) VALUES(1025027,'And the boys grew: and Esau was a cunning hunter, a man of the field; and Jacob was a plain man, dwelling in tents.');
-INSERT INTO t1(docid,words) VALUES(1025028,'And Isaac loved Esau, because he did eat of his venison: but Rebekah loved Jacob.');
-INSERT INTO t1(docid,words) VALUES(1025029,'And Jacob sod pottage: and Esau came from the field, and he was faint:');
-INSERT INTO t1(docid,words) VALUES(1025030,'And Esau said to Jacob, Feed me, I pray thee, with that same red pottage; for I am faint: therefore was his name called Edom.');
-INSERT INTO t1(docid,words) VALUES(1025031,'And Jacob said, Sell me this day thy birthright.');
-INSERT INTO t1(docid,words) VALUES(1025032,'And Esau said, Behold, I am at the point to die: and what profit shall this birthright do to me?');
-INSERT INTO t1(docid,words) VALUES(1025033,'And Jacob said, Swear to me this day; and he sware unto him: and he sold his birthright unto Jacob.');
-INSERT INTO t1(docid,words) VALUES(1025034,'Then Jacob gave Esau bread and pottage of lentiles; and he did eat and drink, and rose up, and went his way: thus Esau despised his birthright.');
-INSERT INTO t1(docid,words) VALUES(1026001,'And there was a famine in the land, beside the first famine that was in the days of Abraham. And Isaac went unto Abimelech king of the Philistines unto Gerar.');
-INSERT INTO t1(docid,words) VALUES(1026002,'And the LORD appeared unto him, and said, Go not down into Egypt; dwell in the land which I shall tell thee of:');
-INSERT INTO t1(docid,words) VALUES(1026003,'Sojourn in this land, and I will be with thee, and will bless thee; for unto thee, and unto thy seed, I will give all these countries, and I will perform the oath which I sware unto Abraham thy father;');
-INSERT INTO t1(docid,words) VALUES(1026004,'And I will make thy seed to multiply as the stars of heaven, and will give unto thy seed all these countries; and in thy seed shall all the nations of the earth be blessed;');
-INSERT INTO t1(docid,words) VALUES(1026005,'Because that Abraham obeyed my voice, and kept my charge, my commandments, my statutes, and my laws.');
-INSERT INTO t1(docid,words) VALUES(1026006,'And Isaac dwelt in Gerar:');
-INSERT INTO t1(docid,words) VALUES(1026007,'And the men of the place asked him of his wife; and he said, She is my sister: for he feared to say, She is my wife; lest, said he, the men of the place should kill me for Rebekah; because she was fair to look upon.');
-INSERT INTO t1(docid,words) VALUES(1026008,'And it came to pass, when he had been there a long time, that Abimelech king of the Philistines looked out at a window, and saw, and, behold, Isaac was sporting with Rebekah his wife.');
-INSERT INTO t1(docid,words) VALUES(1026009,'And Abimelech called Isaac, and said, Behold, of a surety she is thy wife; and how saidst thou, She is my sister? And Isaac said unto him, Because I said, Lest I die for her.');
-INSERT INTO t1(docid,words) VALUES(1026010,'And Abimelech said, What is this thou hast done unto us? one of the people might lightly have lien with thy wife, and thou shouldest have brought guiltiness upon us.');
-INSERT INTO t1(docid,words) VALUES(1026011,'And Abimelech charged all his people, saying, He that toucheth this man or his wife shall surely be put to death.');
-INSERT INTO t1(docid,words) VALUES(1026012,'Then Isaac sowed in that land, and received in the same year an hundredfold: and the LORD blessed him.');
-INSERT INTO t1(docid,words) VALUES(1026013,'And the man waxed great, and went forward, and grew until he became very great:');
-INSERT INTO t1(docid,words) VALUES(1026014,'For he had possession of flocks, and possession of herds, and great store of servants: and the Philistines envied him.');
-INSERT INTO t1(docid,words) VALUES(1026015,'For all the wells which his father''s servants had digged in the days of Abraham his father, the Philistines had stopped them, and filled them with earth.');
-INSERT INTO t1(docid,words) VALUES(1026016,'And Abimelech said unto Isaac, Go from us; for thou art much mightier than we.');
-INSERT INTO t1(docid,words) VALUES(1026017,'And Isaac departed thence, and pitched his tent in the valley of Gerar, and dwelt there.');
-INSERT INTO t1(docid,words) VALUES(1026018,'And Isaac digged again the wells of water, which they had digged in the days of Abraham his father; for the Philistines had stopped them after the death of Abraham: and he called their names after the names by which his father had called them.');
-INSERT INTO t1(docid,words) VALUES(1026019,'And Isaac''s servants digged in the valley, and found there a well of springing water.');
-INSERT INTO t1(docid,words) VALUES(1026020,'And the herdmen of Gerar did strive with Isaac''s herdmen, saying, The water is ours: and he called the name of the well Esek; because they strove with him.');
-INSERT INTO t1(docid,words) VALUES(1026021,'And they digged another well, and strove for that also: and he called the name of it Sitnah.');
-INSERT INTO t1(docid,words) VALUES(1026022,'And he removed from thence, and digged another well; and for that they strove not: and he called the name of it Rehoboth; and he said, For now the LORD hath made room for us, and we shall be fruitful in the land.');
-INSERT INTO t1(docid,words) VALUES(1026023,'And he went up from thence to Beersheba.');
-INSERT INTO t1(docid,words) VALUES(1026024,'And the LORD appeared unto him the same night, and said, I am the God of Abraham thy father: fear not, for I am with thee, and will bless thee, and multiply thy seed for my servant Abraham''s sake.');
-INSERT INTO t1(docid,words) VALUES(1026025,'And he builded an altar there, and called upon the name of the LORD, and pitched his tent there: and there Isaac''s servants digged a well.');
-INSERT INTO t1(docid,words) VALUES(1026026,'Then Abimelech went to him from Gerar, and Ahuzzath one of his friends, and Phichol the chief captain of his army.');
-INSERT INTO t1(docid,words) VALUES(1026027,'And Isaac said unto them, Wherefore come ye to me, seeing ye hate me, and have sent me away from you?');
-INSERT INTO t1(docid,words) VALUES(1026028,'And they said, We saw certainly that the LORD was with thee: and we said, Let there be now an oath betwixt us, even betwixt us and thee, and let us make a covenant with thee;');
-INSERT INTO t1(docid,words) VALUES(1026029,'That thou wilt do us no hurt, as we have not touched thee, and as we have done unto thee nothing but good, and have sent thee away in peace: thou art now the blessed of the LORD.');
-INSERT INTO t1(docid,words) VALUES(1026030,'And he made them a feast, and they did eat and drink.');
-INSERT INTO t1(docid,words) VALUES(1026031,'And they rose up betimes in the morning, and sware one to another: and Isaac sent them away, and they departed from him in peace.');
-INSERT INTO t1(docid,words) VALUES(1026032,'And it came to pass the same day, that Isaac''s servants came, and told him concerning the well which they had digged, and said unto him, We have found water.');
-INSERT INTO t1(docid,words) VALUES(1026033,'And he called it Shebah: therefore the name of the city is Beersheba unto this day.');
-INSERT INTO t1(docid,words) VALUES(1026034,'And Esau was forty years old when he took to wife Judith the daughter of Beeri the Hittite, and Bashemath the daughter of Elon the Hittite:');
-INSERT INTO t1(docid,words) VALUES(1026035,'Which were a grief of mind unto Isaac and to Rebekah.');
-INSERT INTO t1(docid,words) VALUES(1027001,'And it came to pass, that when Isaac was old, and his eyes were dim, so that he could not see, he called Esau his eldest son, and said unto him, My son: and he said unto him, Behold, here am I.');
-INSERT INTO t1(docid,words) VALUES(1027002,'And he said, Behold now, I am old, I know not the day of my death:');
-INSERT INTO t1(docid,words) VALUES(1027003,'Now therefore take, I pray thee, thy weapons, thy quiver and thy bow, and go out to the field, and take me some venison;');
-INSERT INTO t1(docid,words) VALUES(1027004,'And make me savoury meat, such as I love, and bring it to me, that I may eat; that my soul may bless thee before I die.');
-INSERT INTO t1(docid,words) VALUES(1027005,'And Rebekah heard when Isaac spake to Esau his son. And Esau went to the field to hunt for venison, and to bring it.');
-INSERT INTO t1(docid,words) VALUES(1027006,'And Rebekah spake unto Jacob her son, saying, Behold, I heard thy father speak unto Esau thy brother, saying,');
-INSERT INTO t1(docid,words) VALUES(1027007,'Bring me venison, and make me savoury meat, that I may eat, and bless thee before the LORD before my death.');
-INSERT INTO t1(docid,words) VALUES(1027008,'Now therefore, my son, obey my voice according to that which I command thee.');
-INSERT INTO t1(docid,words) VALUES(1027009,'Go now to the flock, and fetch me from thence two good kids of the goats; and I will make them savoury meat for thy father, such as he loveth:');
-INSERT INTO t1(docid,words) VALUES(1027010,'And thou shalt bring it to thy father, that he may eat, and that he may bless thee before his death.');
-INSERT INTO t1(docid,words) VALUES(1027011,'And Jacob said to Rebekah his mother, Behold, Esau my brother is a hairy man, and I am a smooth man:');
-INSERT INTO t1(docid,words) VALUES(1027012,'My father peradventure will feel me, and I shall seem to him as a deceiver; and I shall bring a curse upon me, and not a blessing.');
-INSERT INTO t1(docid,words) VALUES(1027013,'And his mother said unto him, Upon me be thy curse, my son: only obey my voice, and go fetch me them.');
-INSERT INTO t1(docid,words) VALUES(1027014,'And he went, and fetched, and brought them to his mother: and his mother made savoury meat, such as his father loved.');
-INSERT INTO t1(docid,words) VALUES(1027015,'And Rebekah took goodly raiment of her eldest son Esau, which were with her in the house, and put them upon Jacob her younger son:');
-INSERT INTO t1(docid,words) VALUES(1027016,'And she put the skins of the kids of the goats upon his hands, and upon the smooth of his neck:');
-INSERT INTO t1(docid,words) VALUES(1027017,'And she gave the savoury meat and the bread, which she had prepared, into the hand of her son Jacob.');
-INSERT INTO t1(docid,words) VALUES(1027018,'And he came unto his father, and said, My father: and he said, Here am I; who art thou, my son?');
-INSERT INTO t1(docid,words) VALUES(1027019,'And Jacob said unto his father, I am Esau thy first born; I have done according as thou badest me: arise, I pray thee, sit and eat of my venison, that thy soul may bless me.');
-INSERT INTO t1(docid,words) VALUES(1027020,'And Isaac said unto his son, How is it that thou hast found it so quickly, my son? And he said, Because the LORD thy God brought it to me.');
-INSERT INTO t1(docid,words) VALUES(1027021,'And Isaac said unto Jacob, Come near, I pray thee, that I may feel thee, my son, whether thou be my very son Esau or not.');
-INSERT INTO t1(docid,words) VALUES(1027022,'And Jacob went near unto Isaac his father; and he felt him, and said, The voice is Jacob''s voice, but the hands are the hands of Esau.');
-INSERT INTO t1(docid,words) VALUES(1027023,'And he discerned him not, because his hands were hairy, as his brother Esau''s hands: so he blessed him.');
-INSERT INTO t1(docid,words) VALUES(1027024,'And he said, Art thou my very son Esau? And he said, I am.');
-INSERT INTO t1(docid,words) VALUES(1027025,'And he said, Bring it near to me, and I will eat of my son''s venison, that my soul may bless thee. And he brought it near to him, and he did eat: and he brought him wine and he drank.');
-INSERT INTO t1(docid,words) VALUES(1027026,'And his father Isaac said unto him, Come near now, and kiss me, my son.');
-INSERT INTO t1(docid,words) VALUES(1027027,'And he came near, and kissed him: and he smelled the smell of his raiment, and blessed him, and said, See, the smell of my son is as the smell of a field which the LORD hath blessed:');
-INSERT INTO t1(docid,words) VALUES(1027028,'Therefore God give thee of the dew of heaven, and the fatness of the earth, and plenty of corn and wine:');
-INSERT INTO t1(docid,words) VALUES(1027029,'Let people serve thee, and nations bow down to thee: be lord over thy brethren, and let thy mother''s sons bow down to thee: cursed be every one that curseth thee, and blessed be he that blesseth thee.');
-INSERT INTO t1(docid,words) VALUES(1027030,'And it came to pass, as soon as Isaac had made an end of blessing Jacob, and Jacob was yet scarce gone out from the presence of Isaac his father, that Esau his brother came in from his hunting.');
-INSERT INTO t1(docid,words) VALUES(1027031,'And he also had made savoury meat, and brought it unto his father, and said unto his father, Let my father arise, and eat of his son''s venison, that thy soul may bless me.');
-INSERT INTO t1(docid,words) VALUES(1027032,'And Isaac his father said unto him, Who art thou? And he said, I am thy son, thy firstborn Esau.');
-INSERT INTO t1(docid,words) VALUES(1027033,'And Isaac trembled very exceedingly, and said, Who? where is he that hath taken venison, and brought it me, and I have eaten of all before thou camest, and have blessed him? yea, and he shall be blessed.');
-INSERT INTO t1(docid,words) VALUES(1027034,'And when Esau heard the words of his father, he cried with a great and exceeding bitter cry, and said unto his father, Bless me, even me also, O my father.');
-INSERT INTO t1(docid,words) VALUES(1027035,'And he said, Thy brother came with subtilty, and hath taken away thy blessing.');
-INSERT INTO t1(docid,words) VALUES(1027036,'And he said, Is not he rightly named Jacob? for he hath supplanted me these two times: he took away my birthright; and, behold, now he hath taken away my blessing. And he said, Hast thou not reserved a blessing for me?');
-INSERT INTO t1(docid,words) VALUES(1027037,'And Isaac answered and said unto Esau, Behold, I have made him thy lord, and all his brethren have I given to him for servants; and with corn and wine have I sustained him: and what shall I do now unto thee, my son?');
-INSERT INTO t1(docid,words) VALUES(1027038,'And Esau said unto his father, Hast thou but one blessing, my father? bless me, even me also, O my father. And Esau lifted up his voice, and wept.');
-INSERT INTO t1(docid,words) VALUES(1027039,'And Isaac his father answered and said unto him, Behold, thy dwelling shall be the fatness of the earth, and of the dew of heaven from above;');
-INSERT INTO t1(docid,words) VALUES(1027040,'And by thy sword shalt thou live, and shalt serve thy brother; and it shall come to pass when thou shalt have the dominion, that thou shalt break his yoke from off thy neck.');
-INSERT INTO t1(docid,words) VALUES(1027041,'And Esau hated Jacob because of the blessing wherewith his father blessed him: and Esau said in his heart, The days of mourning for my father are at hand; then will I slay my brother Jacob.');
-INSERT INTO t1(docid,words) VALUES(1027042,'And these words of Esau her elder son were told to Rebekah: and she sent and called Jacob her younger son, and said unto him, Behold, thy brother Esau, as touching thee, doth comfort himself, purposing to kill thee.');
-INSERT INTO t1(docid,words) VALUES(1027043,'Now therefore, my son, obey my voice; arise, flee thou to Laban my brother to Haran;');
-INSERT INTO t1(docid,words) VALUES(1027044,'And tarry with him a few days, until thy brother''s fury turn away;');
-INSERT INTO t1(docid,words) VALUES(1027045,'Until thy brother''s anger turn away from thee, and he forget that which thou hast done to him: then I will send, and fetch thee from thence: why should I be deprived also of you both in one day?');
-INSERT INTO t1(docid,words) VALUES(1027046,'And Rebekah said to Isaac, I am weary of my life because of the daughters of Heth: if Jacob take a wife of the daughters of Heth, such as these which are of the daughters of the land, what good shall my life do me?');
-INSERT INTO t1(docid,words) VALUES(1028001,'And Isaac called Jacob, and blessed him, and charged him, and said unto him, Thou shalt not take a wife of the daughters of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1028002,'Arise, go to Padanaram, to the house of Bethuel thy mother''s father; and take thee a wife from thence of the daughers of Laban thy mother''s brother.');
-INSERT INTO t1(docid,words) VALUES(1028003,'And God Almighty bless thee, and make thee fruitful, and multiply thee, that thou mayest be a multitude of people;');
-INSERT INTO t1(docid,words) VALUES(1028004,'And give thee the blessing of Abraham, to thee, and to thy seed with thee; that thou mayest inherit the land wherein thou art a stranger, which God gave unto Abraham.');
-INSERT INTO t1(docid,words) VALUES(1028005,'And Isaac sent away Jacob: and he went to Padanaram unto Laban, son of Bethuel the Syrian, the brother of Rebekah, Jacob''s and Esau''s mother.');
-INSERT INTO t1(docid,words) VALUES(1028006,'When Esau saw that Isaac had blessed Jacob, and sent him away to Padanaram, to take him a wife from thence; and that as he blessed him he gave him a charge, saying, Thou shalt not take a wife of the daughers of Canaan;');
-INSERT INTO t1(docid,words) VALUES(1028007,'And that Jacob obeyed his father and his mother, and was gone to Padanaram;');
-INSERT INTO t1(docid,words) VALUES(1028008,'And Esau seeing that the daughters of Canaan pleased not Isaac his father;');
-INSERT INTO t1(docid,words) VALUES(1028009,'Then went Esau unto Ishmael, and took unto the wives which he had Mahalath the daughter of Ishmael Abraham''s son, the sister of Nebajoth, to be his wife.');
-INSERT INTO t1(docid,words) VALUES(1028010,'And Jacob went out from Beersheba, and went toward Haran.');
-INSERT INTO t1(docid,words) VALUES(1028011,'And he lighted upon a certain place, and tarried there all night, because the sun was set; and he took of the stones of that place, and put them for his pillows, and lay down in that place to sleep.');
-INSERT INTO t1(docid,words) VALUES(1028012,'And he dreamed, and behold a ladder set up on the earth, and the top of it reached to heaven: and behold the angels of God ascending and descending on it.');
-INSERT INTO t1(docid,words) VALUES(1028013,'And, behold, the LORD stood above it, and said, I am the LORD God of Abraham thy father, and the God of Isaac: the land whereon thou liest, to thee will I give it, and to thy seed;');
-INSERT INTO t1(docid,words) VALUES(1028014,'And thy seed shall be as the dust of the earth, and thou shalt spread abroad to the west, and to the east, and to the north, and to the south: and in thee and in thy seed shall all the families of the earth be blessed.');
-INSERT INTO t1(docid,words) VALUES(1028015,'And, behold, I am with thee, and will keep thee in all places whither thou goest, and will bring thee again into this land; for I will not leave thee, until I have done that which I have spoken to thee of.');
-INSERT INTO t1(docid,words) VALUES(1028016,'And Jacob awaked out of his sleep, and he said, Surely the LORD is in this place; and I knew it not.');
-INSERT INTO t1(docid,words) VALUES(1028017,'And he was afraid, and said, How dreadful is this place! this is none other but the house of God, and this is the gate of heaven.');
-INSERT INTO t1(docid,words) VALUES(1028018,'And Jacob rose up early in the morning, and took the stone that he had put for his pillows, and set it up for a pillar, and poured oil upon the top of it.');
-INSERT INTO t1(docid,words) VALUES(1028019,'And he called the name of that place Bethel: but the name of that city was called Luz at the first.');
-INSERT INTO t1(docid,words) VALUES(1028020,'And Jacob vowed a vow, saying, If God will be with me, and will keep me in this way that I go, and will give me bread to eat, and raiment to put on,');
-INSERT INTO t1(docid,words) VALUES(1028021,'So that I come again to my father''s house in peace; then shall the LORD be my God:');
-INSERT INTO t1(docid,words) VALUES(1028022,'And this stone, which I have set for a pillar, shall be God''s house: and of all that thou shalt give me I will surely give the tenth unto thee.');
-INSERT INTO t1(docid,words) VALUES(1029001,'Then Jacob went on his journey, and came into the land of the people of the east.');
-INSERT INTO t1(docid,words) VALUES(1029002,'And he looked, and behold a well in the field, and, lo, there were three flocks of sheep lying by it; for out of that well they watered the flocks: and a great stone was upon the well''s mouth.');
-INSERT INTO t1(docid,words) VALUES(1029003,'And thither were all the flocks gathered: and they rolled the stone from the well''s mouth, and watered the sheep, and put the stone again upon the well''s mouth in his place.');
-INSERT INTO t1(docid,words) VALUES(1029004,'And Jacob said unto them, My brethren, whence be ye? And they said, Of Haran are we.');
-INSERT INTO t1(docid,words) VALUES(1029005,'And he said unto them, Know ye Laban the son of Nahor? And they said, We know him.');
-INSERT INTO t1(docid,words) VALUES(1029006,'And he said unto them, Is he well? And they said, He is well: and, behold, Rachel his daughter cometh with the sheep.');
-INSERT INTO t1(docid,words) VALUES(1029007,'And he said, Lo, it is yet high day, neither is it time that the cattle should be gathered together: water ye the sheep, and go and feed them.');
-INSERT INTO t1(docid,words) VALUES(1029008,'And they said, We cannot, until all the flocks be gathered together, and till they roll the stone from the well''s mouth; then we water the sheep.');
-INSERT INTO t1(docid,words) VALUES(1029009,'And while he yet spake with them, Rachel came with her father''s sheep; for she kept them.');
-INSERT INTO t1(docid,words) VALUES(1029010,'And it came to pass, when Jacob saw Rachel the daughter of Laban his mother''s brother, and the sheep of Laban his mother''s brother, that Jacob went near, and rolled the stone from the well''s mouth, and watered the flock of Laban his mother''s brother.');
-INSERT INTO t1(docid,words) VALUES(1029011,'And Jacob kissed Rachel, and lifted up his voice, and wept.');
-INSERT INTO t1(docid,words) VALUES(1029012,'And Jacob told Rachel that he was her father''s brother, and that he was Rebekah''s son: and she ran and told her father.');
-INSERT INTO t1(docid,words) VALUES(1029013,'And it came to pass, when Laban heard the tidings of Jacob his sister''s son, that he ran to meet him, and embraced him, and kissed him, and brought him to his house. And he told Laban all these things.');
-INSERT INTO t1(docid,words) VALUES(1029014,'And Laban said to him, Surely thou art my bone and my flesh. And he abode with him the space of a month.');
-INSERT INTO t1(docid,words) VALUES(1029015,'And Laban said unto Jacob, Because thou art my brother, shouldest thou therefore serve me for nought? tell me, what shall thy wages be?');
-INSERT INTO t1(docid,words) VALUES(1029016,'And Laban had two daughters: the name of the elder was Leah, and the name of the younger was Rachel.');
-INSERT INTO t1(docid,words) VALUES(1029017,'Leah was tender eyed; but Rachel was beautiful and well favoured.');
-INSERT INTO t1(docid,words) VALUES(1029018,'And Jacob loved Rachel; and said, I will serve thee seven years for Rachel thy younger daughter.');
-INSERT INTO t1(docid,words) VALUES(1029019,'And Laban said, It is better that I give her to thee, than that I should give her to another man: abide with me.');
-INSERT INTO t1(docid,words) VALUES(1029020,'And Jacob served seven years for Rachel; and they seemed unto him but a few days, for the love he had to her.');
-INSERT INTO t1(docid,words) VALUES(1029021,'And Jacob said unto Laban, Give me my wife, for my days are fulfilled, that I may go in unto her.');
-INSERT INTO t1(docid,words) VALUES(1029022,'And Laban gathered together all the men of the place, and made a feast.');
-INSERT INTO t1(docid,words) VALUES(1029023,'And it came to pass in the evening, that he took Leah his daughter, and brought her to him; and he went in unto her.');
-INSERT INTO t1(docid,words) VALUES(1029024,'And Laban gave unto his daughter Leah Zilpah his maid for an handmaid.');
-INSERT INTO t1(docid,words) VALUES(1029025,'And it came to pass, that in the morning, behold, it was Leah: and he said to Laban, What is this thou hast done unto me? did not I serve with thee for Rachel? wherefore then hast thou beguiled me?');
-INSERT INTO t1(docid,words) VALUES(1029026,'And Laban said, It must not be so done in our country, to give the younger before the firstborn.');
-INSERT INTO t1(docid,words) VALUES(1029027,'Fulfil her week, and we will give thee this also for the service which thou shalt serve with me yet seven other years.');
-INSERT INTO t1(docid,words) VALUES(1029028,'And Jacob did so, and fulfilled her week: and he gave him Rachel his daughter to wife also.');
-INSERT INTO t1(docid,words) VALUES(1029029,'And Laban gave to Rachel his daughter Bilhah his handmaid to be her maid.');
-INSERT INTO t1(docid,words) VALUES(1029030,'And he went in also unto Rachel, and he loved also Rachel more than Leah, and served with him yet seven other years.');
-INSERT INTO t1(docid,words) VALUES(1029031,'And when the LORD saw that Leah was hated, he opened her womb: but Rachel was barren.');
-INSERT INTO t1(docid,words) VALUES(1029032,'And Leah conceived, and bare a son, and she called his name Reuben: for she said, Surely the LORD hath looked upon my affliction; now therefore my husband will love me.');
-INSERT INTO t1(docid,words) VALUES(1029033,'And she conceived again, and bare a son; and said, Because the LORD hath heard I was hated, he hath therefore given me this son also: and she called his name Simeon.');
-INSERT INTO t1(docid,words) VALUES(1029034,'And she conceived again, and bare a son; and said, Now this time will my husband be joined unto me, because I have born him three sons: therefore was his name called Levi.');
-INSERT INTO t1(docid,words) VALUES(1029035,'And she conceived again, and bare a son: and she said, Now will I praise the LORD: therefore she called his name Judah; and left bearing.');
-INSERT INTO t1(docid,words) VALUES(1030001,'And when Rachel saw that she bare Jacob no children, Rachel envied her sister; and said unto Jacob, Give me children, or else I die.');
-INSERT INTO t1(docid,words) VALUES(1030002,'And Jacob''s anger was kindled against Rachel: and he said, Am I in God''s stead, who hath withheld from thee the fruit of the womb?');
-INSERT INTO t1(docid,words) VALUES(1030003,'And she said, Behold my maid Bilhah, go in unto her; and she shall bear upon my knees, that I may also have children by her.');
-INSERT INTO t1(docid,words) VALUES(1030004,'And she gave him Bilhah her handmaid to wife: and Jacob went in unto her.');
-INSERT INTO t1(docid,words) VALUES(1030005,'And Bilhah conceived, and bare Jacob a son.');
-INSERT INTO t1(docid,words) VALUES(1030006,'And Rachel said, God hath judged me, and hath also heard my voice, and hath given me a son: therefore called she his name Dan.');
-INSERT INTO t1(docid,words) VALUES(1030007,'And Bilhah Rachel''s maid conceived again, and bare Jacob a second son.');
-INSERT INTO t1(docid,words) VALUES(1030008,'And Rachel said, With great wrestlings have I wrestled with my sister, and I have prevailed: and she called his name Naphtali.');
-INSERT INTO t1(docid,words) VALUES(1030009,'When Leah saw that she had left bearing, she took Zilpah her maid, and gave her Jacob to wife.');
-INSERT INTO t1(docid,words) VALUES(1030010,'And Zilpah Leah''s maid bare Jacob a son.');
-INSERT INTO t1(docid,words) VALUES(1030011,'And Leah said, A troop cometh: and she called his name Gad.');
-INSERT INTO t1(docid,words) VALUES(1030012,'And Zilpah Leah''s maid bare Jacob a second son.');
-INSERT INTO t1(docid,words) VALUES(1030013,'And Leah said, Happy am I, for the daughters will call me blessed: and she called his name Asher.');
-INSERT INTO t1(docid,words) VALUES(1030014,'And Reuben went in the days of wheat harvest, and found mandrakes in the field, and brought them unto his mother Leah. Then Rachel said to Leah, Give me, I pray thee, of thy son''s mandrakes.');
-INSERT INTO t1(docid,words) VALUES(1030015,'And she said unto her, Is it a small matter that thou hast taken my husband? and wouldest thou take away my son''s mandrakes also? And Rachel said, Therefore he shall lie with thee to night for thy son''s mandrakes.');
-INSERT INTO t1(docid,words) VALUES(1030016,'And Jacob came out of the field in the evening, and Leah went out to meet him, and said, Thou must come in unto me; for surely I have hired thee with my son''s mandrakes. And he lay with her that night.');
-INSERT INTO t1(docid,words) VALUES(1030017,'And God hearkened unto Leah, and she conceived, and bare Jacob the fifth son.');
-INSERT INTO t1(docid,words) VALUES(1030018,'And Leah said, God hath given me my hire, because I have given my maiden to my husband: and she called his name Issachar.');
-INSERT INTO t1(docid,words) VALUES(1030019,'And Leah conceived again, and bare Jacob the sixth son.');
-INSERT INTO t1(docid,words) VALUES(1030020,'And Leah said, God hath endued me with a good dowry; now will my husband dwell with me, because I have born him six sons: and she called his name Zebulun.');
-INSERT INTO t1(docid,words) VALUES(1030021,'And afterwards she bare a daughter, and called her name Dinah.');
-INSERT INTO t1(docid,words) VALUES(1030022,'And God remembered Rachel, and God hearkened to her, and opened her womb.');
-INSERT INTO t1(docid,words) VALUES(1030023,'And she conceived, and bare a son; and said, God hath taken away my reproach:');
-INSERT INTO t1(docid,words) VALUES(1030024,'And she called his name Joseph; and said, The LORD shall add to me another son.');
-INSERT INTO t1(docid,words) VALUES(1030025,'And it came to pass, when Rachel had born Joseph, that Jacob said unto Laban, Send me away, that I may go unto mine own place, and to my country.');
-INSERT INTO t1(docid,words) VALUES(1030026,'Give me my wives and my children, for whom I have served thee, and let me go: for thou knowest my service which I have done thee.');
-INSERT INTO t1(docid,words) VALUES(1030027,'And Laban said unto him, I pray thee, if I have found favour in thine eyes, tarry: for I have learned by experience that the LORD hath blessed me for thy sake.');
-INSERT INTO t1(docid,words) VALUES(1030028,'And he said, Appoint me thy wages, and I will give it.');
-INSERT INTO t1(docid,words) VALUES(1030029,'And he said unto him, Thou knowest how I have served thee, and how thy cattle was with me.');
-INSERT INTO t1(docid,words) VALUES(1030030,'For it was little which thou hadst before I came, and it is now increased unto a multitude; and the LORD hath blessed thee since my coming: and now when shall I provide for mine own house also?');
-INSERT INTO t1(docid,words) VALUES(1030031,'And he said, What shall I give thee? And Jacob said, Thou shalt not give me any thing: if thou wilt do this thing for me, I will again feed and keep thy flock.');
-INSERT INTO t1(docid,words) VALUES(1030032,'I will pass through all thy flock to day, removing from thence all the speckled and spotted cattle, and all the brown cattle among the sheep, and the spotted and speckled among the goats: and of such shall be my hire.');
-INSERT INTO t1(docid,words) VALUES(1030033,'So shall my righteousness answer for me in time to come, when it shall come for my hire before thy face: every one that is not speckled and spotted among the goats, and brown among the sheep, that shall be counted stolen with me.');
-INSERT INTO t1(docid,words) VALUES(1030034,'And Laban said, Behold, I would it might be according to thy word.');
-INSERT INTO t1(docid,words) VALUES(1030035,'And he removed that day the he goats that were ringstraked and spotted, and all the she goats that were speckled and spotted, and every one that had some white in it, and all the brown among the sheep, and gave them into the hand of his sons.');
-INSERT INTO t1(docid,words) VALUES(1030036,'And he set three days'' journey betwixt himself and Jacob: and Jacob fed the rest of Laban''s flocks.');
-INSERT INTO t1(docid,words) VALUES(1030037,'And Jacob took him rods of green poplar, and of the hazel and chesnut tree; and pilled white strakes in them, and made the white appear which was in the rods.');
-INSERT INTO t1(docid,words) VALUES(1030038,'And he set the rods which he had pilled before the flocks in the gutters in the watering troughs when the flocks came to drink, that they should conceive when they came to drink.');
-INSERT INTO t1(docid,words) VALUES(1030039,'And the flocks conceived before the rods, and brought forth cattle ringstraked, speckled, and spotted.');
-INSERT INTO t1(docid,words) VALUES(1030040,'And Jacob did separate the lambs, and set the faces of the flocks toward the ringstraked, and all the brown in the flock of Laban; and he put his own flocks by themselves, and put them not unto Laban''s cattle.');
-INSERT INTO t1(docid,words) VALUES(1030041,'And it came to pass, whensoever the stronger cattle did conceive, that Jacob laid the rods before the eyes of the cattle in the gutters, that they might conceive among the rods.');
-INSERT INTO t1(docid,words) VALUES(1030042,'But when the cattle were feeble, he put them not in: so the feebler were Laban''s, and the stronger Jacob''s.');
-INSERT INTO t1(docid,words) VALUES(1030043,'And the man increased exceedingly, and had much cattle, and maidservants, and menservants, and camels, and asses.');
-INSERT INTO t1(docid,words) VALUES(1031001,'And he heard the words of Laban''s sons, saying, Jacob hath taken away all that was our father''s; and of that which was our father''s hath he gotten all this glory.');
-INSERT INTO t1(docid,words) VALUES(1031002,'And Jacob beheld the countenance of Laban, and, behold, it was not toward him as before.');
-INSERT INTO t1(docid,words) VALUES(1031003,'And the LORD said unto Jacob, Return unto the land of thy fathers, and to thy kindred; and I will be with thee.');
-INSERT INTO t1(docid,words) VALUES(1031004,'And Jacob sent and called Rachel and Leah to the field unto his flock,');
-INSERT INTO t1(docid,words) VALUES(1031005,'And said unto them, I see your father''s countenance, that it is not toward me as before; but the God of my father hath been with me.');
-INSERT INTO t1(docid,words) VALUES(1031006,'And ye know that with all my power I have served your father.');
-INSERT INTO t1(docid,words) VALUES(1031007,'And your father hath deceived me, and changed my wages ten times; but God suffered him not to hurt me.');
-INSERT INTO t1(docid,words) VALUES(1031008,'If he said thus, The speckled shall be thy wages; then all the cattle bare speckled: and if he said thus, The ringstraked shall be thy hire; then bare all the cattle ringstraked.');
-INSERT INTO t1(docid,words) VALUES(1031009,'Thus God hath taken away the cattle of your father, and given them to me.');
-INSERT INTO t1(docid,words) VALUES(1031010,'And it came to pass at the time that the cattle conceived, that I lifted up mine eyes, and saw in a dream, and, behold, the rams which leaped upon the cattle were ringstraked, speckled, and grisled.');
-INSERT INTO t1(docid,words) VALUES(1031011,'And the angel of God spake unto me in a dream, saying, Jacob: And I said, Here am I.');
-INSERT INTO t1(docid,words) VALUES(1031012,'And he said, Lift up now thine eyes, and see, all the rams which leap upon the cattle are ringstraked, speckled, and grisled: for I have seen all that Laban doeth unto thee.');
-INSERT INTO t1(docid,words) VALUES(1031013,'I am the God of Bethel, where thou anointedst the pillar, and where thou vowedst a vow unto me: now arise, get thee out from this land, and return unto the land of thy kindred.');
-INSERT INTO t1(docid,words) VALUES(1031014,'And Rachel and Leah answered and said unto him, Is there yet any portion or inheritance for us in our father''s house?');
-INSERT INTO t1(docid,words) VALUES(1031015,'Are we not counted of him strangers? for he hath sold us, and hath quite devoured also our money.');
-INSERT INTO t1(docid,words) VALUES(1031016,'For all the riches which God hath taken from our father, that is ours, and our children''s: now then, whatsoever God hath said unto thee, do.');
-INSERT INTO t1(docid,words) VALUES(1031017,'Then Jacob rose up, and set his sons and his wives upon camels;');
-INSERT INTO t1(docid,words) VALUES(1031018,'And he carried away all his cattle, and all his goods which he had gotten, the cattle of his getting, which he had gotten in Padanaram, for to go to Isaac his father in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1031019,'And Laban went to shear his sheep: and Rachel had stolen the images that were her father''s.');
-INSERT INTO t1(docid,words) VALUES(1031020,'And Jacob stole away unawares to Laban the Syrian, in that he told him not that he fled.');
-INSERT INTO t1(docid,words) VALUES(1031021,'So he fled with all that he had; and he rose up, and passed over the river, and set his face toward the mount Gilead.');
-INSERT INTO t1(docid,words) VALUES(1031022,'And it was told Laban on the third day that Jacob was fled.');
-INSERT INTO t1(docid,words) VALUES(1031023,'And he took his brethren with him, and pursued after him seven days'' journey; and they overtook him in the mount Gilead.');
-INSERT INTO t1(docid,words) VALUES(1031024,'And God came to Laban the Syrian in a dream by night, and said unto him, Take heed that thou speak not to Jacob either good or bad.');
-INSERT INTO t1(docid,words) VALUES(1031025,'Then Laban overtook Jacob. Now Jacob had pitched his tent in the mount: and Laban with his brethren pitched in the mount of Gilead.');
-INSERT INTO t1(docid,words) VALUES(1031026,'And Laban said to Jacob, What hast thou done, that thou hast stolen away unawares to me, and carried away my daughters, as captives taken with the sword?');
-INSERT INTO t1(docid,words) VALUES(1031027,'Wherefore didst thou flee away secretly, and steal away from me; and didst not tell me, that I might have sent thee away with mirth, and with songs, with tabret, and with harp?');
-INSERT INTO t1(docid,words) VALUES(1031028,'And hast not suffered me to kiss my sons and my daughters? thou hast now done foolishly in so doing.');
-INSERT INTO t1(docid,words) VALUES(1031029,'It is in the power of my hand to do you hurt: but the God of your father spake unto me yesternight, saying, Take thou heed that thou speak not to Jacob either good or bad.');
-INSERT INTO t1(docid,words) VALUES(1031030,'And now, though thou wouldest needs be gone, because thou sore longedst after thy father''s house, yet wherefore hast thou stolen my gods?');
-INSERT INTO t1(docid,words) VALUES(1031031,'And Jacob answered and said to Laban, Because I was afraid: for I said, Peradventure thou wouldest take by force thy daughters from me.');
-INSERT INTO t1(docid,words) VALUES(1031032,'With whomsoever thou findest thy gods, let him not live: before our brethren discern thou what is thine with me, and take it to thee. For Jacob knew not that Rachel had stolen them.');
-INSERT INTO t1(docid,words) VALUES(1031033,'And Laban went into Jacob''s tent, and into Leah''s tent, and into the two maidservants'' tents; but he found them not. Then went he out of Leah''s tent, and entered into Rachel''s tent.');
-INSERT INTO t1(docid,words) VALUES(1031034,'Now Rachel had taken the images, and put them in the camel''s furniture, and sat upon them. And Laban searched all the tent, but found them not.');
-INSERT INTO t1(docid,words) VALUES(1031035,'And she said to her father, Let it not displease my lord that I cannot rise up before thee; for the custom of women is upon me. And he searched but found not the images.');
-INSERT INTO t1(docid,words) VALUES(1031036,'And Jacob was wroth, and chode with Laban: and Jacob answered and said to Laban, What is my trespass? what is my sin, that thou hast so hotly pursued after me?');
-INSERT INTO t1(docid,words) VALUES(1031037,'Whereas thou hast searched all my stuff, what hast thou found of all thy household stuff? set it here before my brethren and thy brethren, that they may judge betwixt us both.');
-INSERT INTO t1(docid,words) VALUES(1031038,'This twenty years have I been with thee; thy ewes and thy she goats have not cast their young, and the rams of thy flock have I not eaten.');
-INSERT INTO t1(docid,words) VALUES(1031039,'That which was torn of beasts I brought not unto thee; I bare the loss of it; of my hand didst thou require it, whether stolen by day, or stolen by night.');
-INSERT INTO t1(docid,words) VALUES(1031040,'Thus I was; in the day the drought consumed me, and the frost by night; and my sleep departed from mine eyes.');
-INSERT INTO t1(docid,words) VALUES(1031041,'Thus have I been twenty years in thy house; I served thee fourteen years for thy two daughters, and six years for thy cattle: and thou hast changed my wages ten times.');
-INSERT INTO t1(docid,words) VALUES(1031042,'Except the God of my father, the God of Abraham, and the fear of Isaac, had been with me, surely thou hadst sent me away now empty. God hath seen mine affliction and the labour of my hands, and rebuked thee yesternight.');
-INSERT INTO t1(docid,words) VALUES(1031043,'And Laban answered and said unto Jacob, These daughters are my daughters, and these children are my children, and these cattle are my cattle, and all that thou seest is mine: and what can I do this day unto these my daughters, or unto their children which they have born?');
-INSERT INTO t1(docid,words) VALUES(1031044,'Now therefore come thou, let us make a covenant, I and thou; and let it be for a witness between me and thee.');
-INSERT INTO t1(docid,words) VALUES(1031045,'And Jacob took a stone, and set it up for a pillar.');
-INSERT INTO t1(docid,words) VALUES(1031046,'And Jacob said unto his brethren, Gather stones; and they took stones, and made an heap: and they did eat there upon the heap.');
-INSERT INTO t1(docid,words) VALUES(1031047,'And Laban called it Jegarsahadutha: but Jacob called it Galeed.');
-INSERT INTO t1(docid,words) VALUES(1031048,'And Laban said, This heap is a witness between me and thee this day. Therefore was the name of it called Galeed;');
-INSERT INTO t1(docid,words) VALUES(1031049,'And Mizpah; for he said, The LORD watch between me and thee, when we are absent one from another.');
-INSERT INTO t1(docid,words) VALUES(1031050,'If thou shalt afflict my daughters, or if thou shalt take other wives beside my daughters, no man is with us; see, God is witness betwixt me and thee.');
-INSERT INTO t1(docid,words) VALUES(1031051,'And Laban said to Jacob, Behold this heap, and behold this pillar, which I have cast betwixt me and thee:');
-INSERT INTO t1(docid,words) VALUES(1031052,'This heap be witness, and this pillar be witness, that I will not pass over this heap to thee, and that thou shalt not pass over this heap and this pillar unto me, for harm.');
-INSERT INTO t1(docid,words) VALUES(1031053,'The God of Abraham, and the God of Nahor, the God of their father, judge betwixt us. And Jacob sware by the fear of his father Isaac.');
-INSERT INTO t1(docid,words) VALUES(1031054,'Then Jacob offered sacrifice upon the mount, and called his brethren to eat bread: and they did eat bread, and tarried all night in the mount.');
-INSERT INTO t1(docid,words) VALUES(1031055,'And early in the morning Laban rose up, and kissed his sons and his daughters, and blessed them: and Laban departed, and returned unto his place.');
-INSERT INTO t1(docid,words) VALUES(1032001,'And Jacob went on his way, and the angels of God met him.');
-INSERT INTO t1(docid,words) VALUES(1032002,'And when Jacob saw them, he said, This is God''s host: and he called the name of that place Mahanaim.');
-INSERT INTO t1(docid,words) VALUES(1032003,'And Jacob sent messengers before him to Esau his brother unto the land of Seir, the country of Edom.');
-INSERT INTO t1(docid,words) VALUES(1032004,'And he commanded them, saying, Thus shall ye speak unto my lord Esau; Thy servant Jacob saith thus, I have sojourned with Laban, and stayed there until now:');
-INSERT INTO t1(docid,words) VALUES(1032005,'And I have oxen, and asses, flocks, and menservants, and womenservants: and I have sent to tell my lord, that I may find grace in thy sight.');
-INSERT INTO t1(docid,words) VALUES(1032006,'And the messengers returned to Jacob, saying, We came to thy brother Esau, and also he cometh to meet thee, and four hundred men with him.');
-INSERT INTO t1(docid,words) VALUES(1032007,'Then Jacob was greatly afraid and distressed: and he divided the people that was with him, and the flocks, and herds, and the camels, into two bands;');
-INSERT INTO t1(docid,words) VALUES(1032008,'And said, If Esau come to the one company, and smite it, then the other company which is left shall escape.');
-INSERT INTO t1(docid,words) VALUES(1032009,'And Jacob said, O God of my father Abraham, and God of my father Isaac, the LORD which saidst unto me, Return unto thy country, and to thy kindred, and I will deal well with thee:');
-INSERT INTO t1(docid,words) VALUES(1032010,'I am not worthy of the least of all the mercies, and of all the truth, which thou hast shewed unto thy servant; for with my staff I passed over this Jordan; and now I am become two bands.');
-INSERT INTO t1(docid,words) VALUES(1032011,'Deliver me, I pray thee, from the hand of my brother, from the hand of Esau: for I fear him, lest he will come and smite me, and the mother with the children.');
-INSERT INTO t1(docid,words) VALUES(1032012,'And thou saidst, I will surely do thee good, and make thy seed as the sand of the sea, which cannot be numbered for multitude.');
-INSERT INTO t1(docid,words) VALUES(1032013,'And he lodged there that same night; and took of that which came to his hand a present for Esau his brother;');
-INSERT INTO t1(docid,words) VALUES(1032014,'Two hundred she goats, and twenty he goats, two hundred ewes, and twenty rams,');
-INSERT INTO t1(docid,words) VALUES(1032015,'Thirty milch camels with their colts, forty kine, and ten bulls, twenty she asses, and ten foals.');
-INSERT INTO t1(docid,words) VALUES(1032016,'And he delivered them into the hand of his servants, every drove by themselves; and said unto his servants, Pass over before me, and put a space betwixt drove and drove.');
-INSERT INTO t1(docid,words) VALUES(1032017,'And he commanded the foremost, saying, When Esau my brother meeteth thee, and asketh thee, saying, Whose art thou? and whither goest thou? and whose are these before thee?');
-INSERT INTO t1(docid,words) VALUES(1032018,'Then thou shalt say, They be thy servant Jacob''s; it is a present sent unto my lord Esau: and, behold, also he is behind us.');
-INSERT INTO t1(docid,words) VALUES(1032019,'And so commanded he the second, and the third, and all that followed the droves, saying, On this manner shall ye speak unto Esau, when ye find him.');
-INSERT INTO t1(docid,words) VALUES(1032020,'And say ye moreover, Behold, thy servant Jacob is behind us. For he said, I will appease him with the present that goeth before me, and afterward I will see his face; peradventure he will accept of me.');
-INSERT INTO t1(docid,words) VALUES(1032021,'So went the present over before him: and himself lodged that night in the company.');
-INSERT INTO t1(docid,words) VALUES(1032022,'And he rose up that night, and took his two wives, and his two womenservants, and his eleven sons, and passed over the ford Jabbok.');
-INSERT INTO t1(docid,words) VALUES(1032023,'And he took them, and sent them over the brook, and sent over that he had.');
-INSERT INTO t1(docid,words) VALUES(1032024,'And Jacob was left alone; and there wrestled a man with him until the breaking of the day.');
-INSERT INTO t1(docid,words) VALUES(1032025,'And when he saw that he prevailed not against him, he touched the hollow of his thigh; and the hollow of Jacob''s thigh was out of joint, as he wrestled with him.');
-INSERT INTO t1(docid,words) VALUES(1032026,'And he said, Let me go, for the day breaketh. And he said, I will not let thee go, except thou bless me.');
-INSERT INTO t1(docid,words) VALUES(1032027,'And he said unto him, What is thy name? And he said, Jacob.');
-INSERT INTO t1(docid,words) VALUES(1032028,'And he said, Thy name shall be called no more Jacob, but Israel: for as a prince hast thou power with God and with men, and hast prevailed.');
-INSERT INTO t1(docid,words) VALUES(1032029,'And Jacob asked him, and said, Tell me, I pray thee, thy name. And he said, Wherefore is it that thou dost ask after my name? And he blessed him there.');
-INSERT INTO t1(docid,words) VALUES(1032030,'And Jacob called the name of the place Peniel: for I have seen God face to face, and my life is preserved.');
-INSERT INTO t1(docid,words) VALUES(1032031,'And as he passed over Penuel the sun rose upon him, and he halted upon his thigh.');
-INSERT INTO t1(docid,words) VALUES(1032032,'Therefore the children of Israel eat not of the sinew which shrank, which is upon the hollow of the thigh, unto this day: because he touched the hollow of Jacob''s thigh in the sinew that shrank.');
-INSERT INTO t1(docid,words) VALUES(1033001,'And Jacob lifted up his eyes, and looked, and, behold, Esau came, and with him four hundred men. And he divided the children unto Leah, and unto Rachel, and unto the two handmaids.');
-INSERT INTO t1(docid,words) VALUES(1033002,'And he put the handmaids and their children foremost, and Leah and her children after, and Rachel and Joseph hindermost.');
-INSERT INTO t1(docid,words) VALUES(1033003,'And he passed over before them, and bowed himself to the ground seven times, until he came near to his brother.');
-INSERT INTO t1(docid,words) VALUES(1033004,'And Esau ran to meet him, and embraced him, and fell on his neck, and kissed him: and they wept.');
-INSERT INTO t1(docid,words) VALUES(1033005,'And he lifted up his eyes, and saw the women and the children; and said, Who are those with thee? And he said, The children which God hath graciously given thy servant.');
-INSERT INTO t1(docid,words) VALUES(1033006,'Then the handmaidens came near, they and their children, and they bowed themselves.');
-INSERT INTO t1(docid,words) VALUES(1033007,'And Leah also with her children came near, and bowed themselves: and after came Joseph near and Rachel, and they bowed themselves.');
-INSERT INTO t1(docid,words) VALUES(1033008,'And he said, What meanest thou by all this drove which I met? And he said, These are to find grace in the sight of my lord.');
-INSERT INTO t1(docid,words) VALUES(1033009,'And Esau said, I have enough, my brother; keep that thou hast unto thyself.');
-INSERT INTO t1(docid,words) VALUES(1033010,'And Jacob said, Nay, I pray thee, if now I have found grace in thy sight, then receive my present at my hand: for therefore I have seen thy face, as though I had seen the face of God, and thou wast pleased with me.');
-INSERT INTO t1(docid,words) VALUES(1033011,'Take, I pray thee, my blessing that is brought to thee; because God hath dealt graciously with me, and because I have enough. And he urged him, and he took it.');
-INSERT INTO t1(docid,words) VALUES(1033012,'And he said, Let us take our journey, and let us go, and I will go before thee.');
-INSERT INTO t1(docid,words) VALUES(1033013,'And he said unto him, My lord knoweth that the children are tender, and the flocks and herds with young are with me: and if men should overdrive them one day, all the flock will die.');
-INSERT INTO t1(docid,words) VALUES(1033014,'Let my lord, I pray thee, pass over before his servant: and I will lead on softly, according as the cattle that goeth before me and the children be able to endure, until I come unto my lord unto Seir.');
-INSERT INTO t1(docid,words) VALUES(1033015,'And Esau said, Let me now leave with thee some of the folk that are with me. And he said, What needeth it? let me find grace in the sight of my lord.');
-INSERT INTO t1(docid,words) VALUES(1033016,'So Esau returned that day on his way unto Seir.');
-INSERT INTO t1(docid,words) VALUES(1033017,'And Jacob journeyed to Succoth, and built him an house, and made booths for his cattle: therefore the name of the place is called Succoth.');
-INSERT INTO t1(docid,words) VALUES(1033018,'And Jacob came to Shalem, a city of Shechem, which is in the land of Canaan, when he came from Padanaram; and pitched his tent before the city.');
-INSERT INTO t1(docid,words) VALUES(1033019,'And he bought a parcel of a field, where he had spread his tent, at the hand of the children of Hamor, Shechem''s father, for an hundred pieces of money.');
-INSERT INTO t1(docid,words) VALUES(1033020,'And he erected there an altar, and called it EleloheIsrael.');
-INSERT INTO t1(docid,words) VALUES(1034001,'And Dinah the daughter of Leah, which she bare unto Jacob, went out to see the daughters of the land.');
-INSERT INTO t1(docid,words) VALUES(1034002,'And when Shechem the son of Hamor the Hivite, prince of the country, saw her, he took her, and lay with her, and defiled her.');
-INSERT INTO t1(docid,words) VALUES(1034003,'And his soul clave unto Dinah the daughter of Jacob, and he loved the damsel, and spake kindly unto the damsel.');
-INSERT INTO t1(docid,words) VALUES(1034004,'And Shechem spake unto his father Hamor, saying, Get me this damsel to wife.');
-INSERT INTO t1(docid,words) VALUES(1034005,'And Jacob heard that he had defiled Dinah his daughter: now his sons were with his cattle in the field: and Jacob held his peace until they were come.');
-INSERT INTO t1(docid,words) VALUES(1034006,'And Hamor the father of Shechem went out unto Jacob to commune with him.');
-INSERT INTO t1(docid,words) VALUES(1034007,'And the sons of Jacob came out of the field when they heard it: and the men were grieved, and they were very wroth, because he had wrought folly in Israel in lying with Jacob''s daughter: which thing ought not to be done.');
-INSERT INTO t1(docid,words) VALUES(1034008,'And Hamor communed with them, saying, The soul of my son Shechem longeth for your daughter: I pray you give her him to wife.');
-INSERT INTO t1(docid,words) VALUES(1034009,'And make ye marriages with us, and give your daughters unto us, and take our daughters unto you.');
-INSERT INTO t1(docid,words) VALUES(1034010,'And ye shall dwell with us: and the land shall be before you; dwell and trade ye therein, and get you possessions therein.');
-INSERT INTO t1(docid,words) VALUES(1034011,'And Shechem said unto her father and unto her brethren, Let me find grace in your eyes, and what ye shall say unto me I will give.');
-INSERT INTO t1(docid,words) VALUES(1034012,'Ask me never so much dowry and gift, and I will give according as ye shall say unto me: but give me the damsel to wife.');
-INSERT INTO t1(docid,words) VALUES(1034013,'And the sons of Jacob answered Shechem and Hamor his father deceitfully, and said, because he had defiled Dinah their sister:');
-INSERT INTO t1(docid,words) VALUES(1034014,'And they said unto them, We cannot do this thing, to give our sister to one that is uncircumcised; for that were a reproach unto us:');
-INSERT INTO t1(docid,words) VALUES(1034015,'But in this will we consent unto you: If ye will be as we be, that every male of you be circumcised;');
-INSERT INTO t1(docid,words) VALUES(1034016,'Then will we give our daughters unto you, and we will take your daughters to us, and we will dwell with you, and we will become one people.');
-INSERT INTO t1(docid,words) VALUES(1034017,'But if ye will not hearken unto us, to be circumcised; then will we take our daughter, and we will be gone.');
-INSERT INTO t1(docid,words) VALUES(1034018,'And their words pleased Hamor, and Shechem Hamor''s son.');
-INSERT INTO t1(docid,words) VALUES(1034019,'And the young man deferred not to do the thing, because he had delight in Jacob''s daughter: and he was more honourable than all the house of his father.');
-INSERT INTO t1(docid,words) VALUES(1034020,'And Hamor and Shechem his son came unto the gate of their city, and communed with the men of their city, saying,');
-INSERT INTO t1(docid,words) VALUES(1034021,'These men are peaceable with us; therefore let them dwell in the land, and trade therein; for the land, behold, it is large enough for them; let us take their daughters to us for wives, and let us give them our daughters.');
-INSERT INTO t1(docid,words) VALUES(1034022,'Only herein will the men consent unto us for to dwell with us, to be one people, if every male among us be circumcised, as they are circumcised.');
-INSERT INTO t1(docid,words) VALUES(1034023,'Shall not their cattle and their substance and every beast of their''s be our''s? only let us consent unto them, and they will dwell with us.');
-INSERT INTO t1(docid,words) VALUES(1034024,'And unto Hamor and unto Shechem his son hearkened all that went out of the gate of his city; and every male was circumcised, all that went out of the gate of his city.');
-INSERT INTO t1(docid,words) VALUES(1034025,'And it came to pass on the third day, when they were sore, that two of the sons of Jacob, Simeon and Levi, Dinah''s brethren, took each man his sword, and came upon the city boldly, and slew all the males.');
-INSERT INTO t1(docid,words) VALUES(1034026,'And they slew Hamor and Shechem his son with the edge of the sword, and took Dinah out of Shechem''s house, and went out.');
-INSERT INTO t1(docid,words) VALUES(1034027,'The sons of Jacob came upon the slain, and spoiled the city, because they had defiled their sister.');
-INSERT INTO t1(docid,words) VALUES(1034028,'They took their sheep, and their oxen, and their asses, and that which was in the city, and that which was in the field,');
-INSERT INTO t1(docid,words) VALUES(1034029,'And all their wealth, and all their little ones, and their wives took they captive, and spoiled even all that was in the house.');
-INSERT INTO t1(docid,words) VALUES(1034030,'And Jacob said to Simeon and Levi, Ye have troubled me to make me to stink among the inhabitants of the land, among the Canaanites and the Perizzites: and I being few in number, they shall gather themselves together against me, and slay me; and I shall be destroyed, I and my house.');
-INSERT INTO t1(docid,words) VALUES(1034031,'And they said, Should he deal with our sister as with an harlot?');
-INSERT INTO t1(docid,words) VALUES(1035001,'And God said unto Jacob, Arise, go up to Bethel, and dwell there: and make there an altar unto God, that appeared unto thee when thou fleddest from the face of Esau thy brother.');
-INSERT INTO t1(docid,words) VALUES(1035002,'Then Jacob said unto his household, and to all that were with him, Put away the strange gods that are among you, and be clean, and change your garments:');
-INSERT INTO t1(docid,words) VALUES(1035003,'And let us arise, and go up to Bethel; and I will make there an altar unto God, who answered me in the day of my distress, and was with me in the way which I went.');
-INSERT INTO t1(docid,words) VALUES(1035004,'And they gave unto Jacob all the strange gods which were in their hand, and all their earrings which were in their ears; and Jacob hid them under the oak which was by Shechem.');
-INSERT INTO t1(docid,words) VALUES(1035005,'And they journeyed: and the terror of God was upon the cities that were round about them, and they did not pursue after the sons of Jacob.');
-INSERT INTO t1(docid,words) VALUES(1035006,'So Jacob came to Luz, which is in the land of Canaan, that is, Bethel, he and all the people that were with him.');
-INSERT INTO t1(docid,words) VALUES(1035007,'And he built there an altar, and called the place Elbethel: because there God appeared unto him, when he fled from the face of his brother.');
-INSERT INTO t1(docid,words) VALUES(1035008,'But Deborah Rebekah''s nurse died, and she was buried beneath Bethel under an oak: and the name of it was called Allonbachuth.');
-INSERT INTO t1(docid,words) VALUES(1035009,'And God appeared unto Jacob again, when he came out of Padanaram, and blessed him.');
-INSERT INTO t1(docid,words) VALUES(1035010,'And God said unto him, Thy name is Jacob: thy name shall not be called any more Jacob, but Israel shall be thy name: and he called his name Israel.');
-INSERT INTO t1(docid,words) VALUES(1035011,'And God said unto him, I am God Almighty: be fruitful and multiply; a nation and a company of nations shall be of thee, and kings shall come out of thy loins;');
-INSERT INTO t1(docid,words) VALUES(1035012,'And the land which I gave Abraham and Isaac, to thee I will give it, and to thy seed after thee will I give the land.');
-INSERT INTO t1(docid,words) VALUES(1035013,'And God went up from him in the place where he talked with him.');
-INSERT INTO t1(docid,words) VALUES(1035014,'And Jacob set up a pillar in the place where he talked with him, even a pillar of stone: and he poured a drink offering thereon, and he poured oil thereon.');
-INSERT INTO t1(docid,words) VALUES(1035015,'And Jacob called the name of the place where God spake with him, Bethel.');
-INSERT INTO t1(docid,words) VALUES(1035016,'And they journeyed from Bethel; and there was but a little way to come to Ephrath: and Rachel travailed, and she had hard labour.');
-INSERT INTO t1(docid,words) VALUES(1035017,'And it came to pass, when she was in hard labour, that the midwife said unto her, Fear not; thou shalt have this son also.');
-INSERT INTO t1(docid,words) VALUES(1035018,'And it came to pass, as her soul was in departing, (for she died) that she called his name Benoni: but his father called him Benjamin.');
-INSERT INTO t1(docid,words) VALUES(1035019,'And Rachel died, and was buried in the way to Ephrath, which is Bethlehem.');
-INSERT INTO t1(docid,words) VALUES(1035020,'And Jacob set a pillar upon her grave: that is the pillar of Rachel''s grave unto this day.');
-INSERT INTO t1(docid,words) VALUES(1035021,'And Israel journeyed, and spread his tent beyond the tower of Edar.');
-INSERT INTO t1(docid,words) VALUES(1035022,'And it came to pass, when Israel dwelt in that land, that Reuben went and lay with Bilhah his father''s concubine: and Israel heard it. Now the sons of Jacob were twelve:');
-INSERT INTO t1(docid,words) VALUES(1035023,'The sons of Leah; Reuben, Jacob''s firstborn, and Simeon, and Levi, and Judah, and Issachar, and Zebulun:');
-INSERT INTO t1(docid,words) VALUES(1035024,'The sons of Rachel; Joseph, and Benjamin:');
-INSERT INTO t1(docid,words) VALUES(1035025,'And the sons of Bilhah, Rachel''s handmaid; Dan, and Naphtali:');
-INSERT INTO t1(docid,words) VALUES(1035026,'And the sons of Zilpah, Leah''s handmaid: Gad, and Asher: these are the sons of Jacob, which were born to him in Padanaram.');
-INSERT INTO t1(docid,words) VALUES(1035027,'And Jacob came unto Isaac his father unto Mamre, unto the city of Arbah, which is Hebron, where Abraham and Isaac sojourned.');
-INSERT INTO t1(docid,words) VALUES(1035028,'And the days of Isaac were an hundred and fourscore years.');
-INSERT INTO t1(docid,words) VALUES(1035029,'And Isaac gave up the ghost, and died, and was gathered unto his people, being old and full of days: and his sons Esau and Jacob buried him.');
-INSERT INTO t1(docid,words) VALUES(1036001,'Now these are the generations of Esau, who is Edom.');
-INSERT INTO t1(docid,words) VALUES(1036002,'Esau took his wives of the daughters of Canaan; Adah the daughter of Elon the Hittite, and Aholibamah the daughter of Anah the daughter of Zibeon the Hivite;');
-INSERT INTO t1(docid,words) VALUES(1036003,'And Bashemath Ishmael''s daughter, sister of Nebajoth.');
-INSERT INTO t1(docid,words) VALUES(1036004,'And Adah bare to Esau Eliphaz; and Bashemath bare Reuel;');
-INSERT INTO t1(docid,words) VALUES(1036005,'And Aholibamah bare Jeush, and Jaalam, and Korah: these are the sons of Esau, which were born unto him in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1036006,'And Esau took his wives, and his sons, and his daughters, and all the persons of his house, and his cattle, and all his beasts, and all his substance, which he had got in the land of Canaan; and went into the country from the face of his brother Jacob.');
-INSERT INTO t1(docid,words) VALUES(1036007,'For their riches were more than that they might dwell together; and the land wherein they were strangers could not bear them because of their cattle.');
-INSERT INTO t1(docid,words) VALUES(1036008,'Thus dwelt Esau in mount Seir: Esau is Edom.');
-INSERT INTO t1(docid,words) VALUES(1036009,'And these are the generations of Esau the father of the Edomites in mount Seir:');
-INSERT INTO t1(docid,words) VALUES(1036010,'These are the names of Esau''s sons; Eliphaz the son of Adah the wife of Esau, Reuel the son of Bashemath the wife of Esau.');
-INSERT INTO t1(docid,words) VALUES(1036011,'And the sons of Eliphaz were Teman, Omar, Zepho, and Gatam, and Kenaz.');
-INSERT INTO t1(docid,words) VALUES(1036012,'And Timna was concubine to Eliphaz Esau''s son; and she bare to Eliphaz Amalek: these were the sons of Adah Esau''s wife.');
-INSERT INTO t1(docid,words) VALUES(1036013,'And these are the sons of Reuel; Nahath, and Zerah, Shammah, and Mizzah: these were the sons of Bashemath Esau''s wife.');
-INSERT INTO t1(docid,words) VALUES(1036014,'And these were the sons of Aholibamah, the daughter of Anah the daughter of Zibeon, Esau''s wife: and she bare to Esau Jeush, and Jaalam, and Korah.');
-INSERT INTO t1(docid,words) VALUES(1036015,'These were dukes of the sons of Esau: the sons of Eliphaz the firstborn son of Esau; duke Teman, duke Omar, duke Zepho, duke Kenaz,');
-INSERT INTO t1(docid,words) VALUES(1036016,'Duke Korah, duke Gatam, and duke Amalek: these are the dukes that came of Eliphaz in the land of Edom; these were the sons of Adah.');
-INSERT INTO t1(docid,words) VALUES(1036017,'And these are the sons of Reuel Esau''s son; duke Nahath, duke Zerah, duke Shammah, duke Mizzah: these are the dukes that came of Reuel in the land of Edom; these are the sons of Bashemath Esau''s wife.');
-INSERT INTO t1(docid,words) VALUES(1036018,'And these are the sons of Aholibamah Esau''s wife; duke Jeush, duke Jaalam, duke Korah: these were the dukes that came of Aholibamah the daughter of Anah, Esau''s wife.');
-INSERT INTO t1(docid,words) VALUES(1036019,'These are the sons of Esau, who is Edom, and these are their dukes.');
-INSERT INTO t1(docid,words) VALUES(1036020,'These are the sons of Seir the Horite, who inhabited the land; Lotan, and Shobal, and Zibeon, and Anah,');
-INSERT INTO t1(docid,words) VALUES(1036021,'And Dishon, and Ezer, and Dishan: these are the dukes of the Horites, the children of Seir in the land of Edom.');
-INSERT INTO t1(docid,words) VALUES(1036022,'And the children of Lotan were Hori and Hemam; and Lotan''s sister was Timna.');
-INSERT INTO t1(docid,words) VALUES(1036023,'And the children of Shobal were these; Alvan, and Manahath, and Ebal, Shepho, and Onam.');
-INSERT INTO t1(docid,words) VALUES(1036024,'And these are the children of Zibeon; both Ajah, and Anah: this was that Anah that found the mules in the wilderness, as he fed the asses of Zibeon his father.');
-INSERT INTO t1(docid,words) VALUES(1036025,'And the children of Anah were these; Dishon, and Aholibamah the daughter of Anah.');
-INSERT INTO t1(docid,words) VALUES(1036026,'And these are the children of Dishon; Hemdan, and Eshban, and Ithran, and Cheran.');
-INSERT INTO t1(docid,words) VALUES(1036027,'The children of Ezer are these; Bilhan, and Zaavan, and Akan.');
-INSERT INTO t1(docid,words) VALUES(1036028,'The children of Dishan are these; Uz, and Aran.');
-INSERT INTO t1(docid,words) VALUES(1036029,'These are the dukes that came of the Horites; duke Lotan, duke Shobal, duke Zibeon, duke Anah,');
-INSERT INTO t1(docid,words) VALUES(1036030,'Duke Dishon, duke Ezer, duke Dishan: these are the dukes that came of Hori, among their dukes in the land of Seir.');
-INSERT INTO t1(docid,words) VALUES(1036031,'And these are the kings that reigned in the land of Edom, before there reigned any king over the children of Israel.');
-INSERT INTO t1(docid,words) VALUES(1036032,'And Bela the son of Beor reigned in Edom: and the name of his city was Dinhabah.');
-INSERT INTO t1(docid,words) VALUES(1036033,'And Bela died, and Jobab the son of Zerah of Bozrah reigned in his stead.');
-INSERT INTO t1(docid,words) VALUES(1036034,'And Jobab died, and Husham of the land of Temani reigned in his stead.');
-INSERT INTO t1(docid,words) VALUES(1036035,'And Husham died, and Hadad the son of Bedad, who smote Midian in the field of Moab, reigned in his stead: and the name of his city was Avith.');
-INSERT INTO t1(docid,words) VALUES(1036036,'And Hadad died, and Samlah of Masrekah reigned in his stead.');
-INSERT INTO t1(docid,words) VALUES(1036037,'And Samlah died, and Saul of Rehoboth by the river reigned in his stead.');
-INSERT INTO t1(docid,words) VALUES(1036038,'And Saul died, and Baalhanan the son of Achbor reigned in his stead.');
-INSERT INTO t1(docid,words) VALUES(1036039,'And Baalhanan the son of Achbor died, and Hadar reigned in his stead: and the name of his city was Pau; and his wife''s name was Mehetabel, the daughter of Matred, the daughter of Mezahab.');
-INSERT INTO t1(docid,words) VALUES(1036040,'And these are the names of the dukes that came of Esau, according to their families, after their places, by their names; duke Timnah, duke Alvah, duke Jetheth,');
-INSERT INTO t1(docid,words) VALUES(1036041,'Duke Aholibamah, duke Elah, duke Pinon,');
-INSERT INTO t1(docid,words) VALUES(1036042,'Duke Kenaz, duke Teman, duke Mibzar,');
-INSERT INTO t1(docid,words) VALUES(1036043,'Duke Magdiel, duke Iram: these be the dukes of Edom, according to their habitations in the land of their possession: he is Esau the father of the Edomites.');
-INSERT INTO t1(docid,words) VALUES(1037001,'And Jacob dwelt in the land wherein his father was a stranger, in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1037002,'These are the generations of Jacob. Joseph, being seventeen years old, was feeding the flock with his brethren; and the lad was with the sons of Bilhah, and with the sons of Zilpah, his father''s wives: and Joseph brought unto his father their evil report.');
-INSERT INTO t1(docid,words) VALUES(1037003,'Now Israel loved Joseph more than all his children, because he was the son of his old age: and he made him a coat of many colours.');
-INSERT INTO t1(docid,words) VALUES(1037004,'And when his brethren saw that their father loved him more than all his brethren, they hated him, and could not speak peaceably unto him.');
-INSERT INTO t1(docid,words) VALUES(1037005,'And Joseph dreamed a dream, and he told it his brethren: and they hated him yet the more.');
-INSERT INTO t1(docid,words) VALUES(1037006,'And he said unto them, Hear, I pray you, this dream which I have dreamed:');
-INSERT INTO t1(docid,words) VALUES(1037007,'For, behold, we were binding sheaves in the field, and, lo, my sheaf arose, and also stood upright; and, behold, your sheaves stood round about, and made obeisance to my sheaf.');
-INSERT INTO t1(docid,words) VALUES(1037008,'And his brethren said to him, Shalt thou indeed reign over us? or shalt thou indeed have dominion over us? And they hated him yet the more for his dreams, and for his words.');
-INSERT INTO t1(docid,words) VALUES(1037009,'And he dreamed yet another dream, and told it his brethren, and said, Behold, I have dreamed a dream more; and, behold, the sun and the moon and the eleven stars made obeisance to me.');
-INSERT INTO t1(docid,words) VALUES(1037010,'And he told it to his father, and to his brethren: and his father rebuked him, and said unto him, What is this dream that thou hast dreamed? Shall I and thy mother and thy brethren indeed come to bow down ourselves to thee to the earth?');
-INSERT INTO t1(docid,words) VALUES(1037011,'And his brethren envied him; but his father observed the saying.');
-INSERT INTO t1(docid,words) VALUES(1037012,'And his brethren went to feed their father''s flock in Shechem.');
-INSERT INTO t1(docid,words) VALUES(1037013,'And Israel said unto Joseph, Do not thy brethren feed the flock in Shechem? come, and I will send thee unto them. And he said to him, Here am I.');
-INSERT INTO t1(docid,words) VALUES(1037014,'And he said to him, Go, I pray thee, see whether it be well with thy brethren, and well with the flocks; and bring me word again. So he sent him out of the vale of Hebron, and he came to Shechem.');
-INSERT INTO t1(docid,words) VALUES(1037015,'And a certain man found him, and, behold, he was wandering in the field: and the man asked him, saying, What seekest thou?');
-INSERT INTO t1(docid,words) VALUES(1037016,'And he said, I seek my brethren: tell me, I pray thee, where they feed their flocks.');
-INSERT INTO t1(docid,words) VALUES(1037017,'And the man said, They are departed hence; for I heard them say, Let us go to Dothan. And Joseph went after his brethren, and found them in Dothan.');
-INSERT INTO t1(docid,words) VALUES(1037018,'And when they saw him afar off, even before he came near unto them, they conspired against him to slay him.');
-INSERT INTO t1(docid,words) VALUES(1037019,'And they said one to another, Behold, this dreamer cometh.');
-INSERT INTO t1(docid,words) VALUES(1037020,'Come now therefore, and let us slay him, and cast him into some pit, and we will say, Some evil beast hath devoured him: and we shall see what will become of his dreams.');
-INSERT INTO t1(docid,words) VALUES(1037021,'And Reuben heard it, and he delivered him out of their hands; and said, Let us not kill him.');
-INSERT INTO t1(docid,words) VALUES(1037022,'And Reuben said unto them, Shed no blood, but cast him into this pit that is in the wilderness, and lay no hand upon him; that he might rid him out of their hands, to deliver him to his father again.');
-INSERT INTO t1(docid,words) VALUES(1037023,'And it came to pass, when Joseph was come unto his brethren, that they stript Joseph out of his coat, his coat of many colours that was on him;');
-INSERT INTO t1(docid,words) VALUES(1037024,'And they took him, and cast him into a pit: and the pit was empty, there was no water in it.');
-INSERT INTO t1(docid,words) VALUES(1037025,'And they sat down to eat bread: and they lifted up their eyes and looked, and, behold, a company of Ishmeelites came from Gilead with their camels bearing spicery and balm and myrrh, going to carry it down to Egypt.');
-INSERT INTO t1(docid,words) VALUES(1037026,'And Judah said unto his brethren, What profit is it if we slay our brother, and conceal his blood?');
-INSERT INTO t1(docid,words) VALUES(1037027,'Come, and let us sell him to the Ishmeelites, and let not our hand be upon him; for he is our brother and our flesh. And his brethren were content.');
-INSERT INTO t1(docid,words) VALUES(1037028,'Then there passed by Midianites merchantmen; and they drew and lifted up Joseph out of the pit, and sold Joseph to the Ishmeelites for twenty pieces of silver: and they brought Joseph into Egypt.');
-INSERT INTO t1(docid,words) VALUES(1037029,'And Reuben returned unto the pit; and, behold, Joseph was not in the pit; and he rent his clothes.');
-INSERT INTO t1(docid,words) VALUES(1037030,'And he returned unto his brethren, and said, The child is not; and I, whither shall I go?');
-INSERT INTO t1(docid,words) VALUES(1037031,'And they took Joseph''s coat, and killed a kid of the goats, and dipped the coat in the blood;');
-INSERT INTO t1(docid,words) VALUES(1037032,'And they sent the coat of many colours, and they brought it to their father; and said, This have we found: know now whether it be thy son''s coat or no.');
-INSERT INTO t1(docid,words) VALUES(1037033,'And he knew it, and said, It is my son''s coat; an evil beast hath devoured him; Joseph is without doubt rent in pieces.');
-INSERT INTO t1(docid,words) VALUES(1037034,'And Jacob rent his clothes, and put sackcloth upon his loins, and mourned for his son many days.');
-INSERT INTO t1(docid,words) VALUES(1037035,'And all his sons and all his daughters rose up to comfort him; but he refused to be comforted; and he said, For I will go down into the grave unto my son mourning. Thus his father wept for him.');
-INSERT INTO t1(docid,words) VALUES(1037036,'And the Midianites sold him into Egypt unto Potiphar, an officer of Pharaoh''s, and captain of the guard.');
-INSERT INTO t1(docid,words) VALUES(1038001,'And it came to pass at that time, that Judah went down from his brethren, and turned in to a certain Adullamite, whose name was Hirah.');
-INSERT INTO t1(docid,words) VALUES(1038002,'And Judah saw there a daughter of a certain Canaanite, whose name was Shuah; and he took her, and went in unto her.');
-INSERT INTO t1(docid,words) VALUES(1038003,'And she conceived, and bare a son; and he called his name Er.');
-INSERT INTO t1(docid,words) VALUES(1038004,'And she conceived again, and bare a son; and she called his name Onan.');
-INSERT INTO t1(docid,words) VALUES(1038005,'And she yet again conceived, and bare a son; and called his name Shelah: and he was at Chezib, when she bare him.');
-INSERT INTO t1(docid,words) VALUES(1038006,'And Judah took a wife for Er his firstborn, whose name was Tamar.');
-INSERT INTO t1(docid,words) VALUES(1038007,'And Er, Judah''s firstborn, was wicked in the sight of the LORD; and the LORD slew him.');
-INSERT INTO t1(docid,words) VALUES(1038008,'And Judah said unto Onan, Go in unto thy brother''s wife, and marry her, and raise up seed to thy brother.');
-INSERT INTO t1(docid,words) VALUES(1038009,'And Onan knew that the seed should not be his; and it came to pass, when he went in unto his brother''s wife, that he spilled it on the ground, lest that he should give seed to his brother.');
-INSERT INTO t1(docid,words) VALUES(1038010,'And the thing which he did displeased the LORD: wherefore he slew him also.');
-INSERT INTO t1(docid,words) VALUES(1038011,'Then said Judah to Tamar his daughter in law, Remain a widow at thy father''s house, till Shelah my son be grown: for he said, Lest peradventure he die also, as his brethren did. And Tamar went and dwelt in her father''s house.');
-INSERT INTO t1(docid,words) VALUES(1038012,'And in process of time the daughter of Shuah Judah''s wife died; and Judah was comforted, and went up unto his sheepshearers to Timnath, he and his friend Hirah the Adullamite.');
-INSERT INTO t1(docid,words) VALUES(1038013,'And it was told Tamar, saying, Behold thy father in law goeth up to Timnath to shear his sheep.');
-INSERT INTO t1(docid,words) VALUES(1038014,'And she put her widow''s garments off from her, and covered her with a vail, and wrapped herself, and sat in an open place, which is by the way to Timnath; for she saw that Shelah was grown, and she was not given unto him to wife.');
-INSERT INTO t1(docid,words) VALUES(1038015,'When Judah saw her, he thought her to be an harlot; because she had covered her face.');
-INSERT INTO t1(docid,words) VALUES(1038016,'And he turned unto her by the way, and said, Go to, I pray thee, let me come in unto thee; (for he knew not that she was his daughter in law.) And she said, What wilt thou give me, that thou mayest come in unto me?');
-INSERT INTO t1(docid,words) VALUES(1038017,'And he said, I will send thee a kid from the flock. And she said, Wilt thou give me a pledge, till thou send it?');
-INSERT INTO t1(docid,words) VALUES(1038018,'And he said, What pledge shall I give thee? And she said, Thy signet, and thy bracelets, and thy staff that is in thine hand. And he gave it her, and came in unto her, and she conceived by him.');
-INSERT INTO t1(docid,words) VALUES(1038019,'And she arose, and went away, and laid by her vail from her, and put on the garments of her widowhood.');
-INSERT INTO t1(docid,words) VALUES(1038020,'And Judah sent the kid by the hand of his friend the Adullamite, to receive his pledge from the woman''s hand: but he found her not.');
-INSERT INTO t1(docid,words) VALUES(1038021,'Then he asked the men of that place, saying, Where is the harlot, that was openly by the way side? And they said, There was no harlot in this place.');
-INSERT INTO t1(docid,words) VALUES(1038022,'And he returned to Judah, and said, I cannot find her; and also the men of the place said, that there was no harlot in this place.');
-INSERT INTO t1(docid,words) VALUES(1038023,'And Judah said, Let her take it to her, lest we be shamed: behold, I sent this kid, and thou hast not found her.');
-INSERT INTO t1(docid,words) VALUES(1038024,'And it came to pass about three months after, that it was told Judah, saying, Tamar thy daughter in law hath played the harlot; and also, behold, she is with child by whoredom. And Judah said, Bring her forth, and let her be burnt.');
-INSERT INTO t1(docid,words) VALUES(1038025,'When she was brought forth, she sent to her father in law, saying, By the man, whose these are, am I with child: and she said, Discern, I pray thee, whose are these, the signet, and bracelets, and staff.');
-INSERT INTO t1(docid,words) VALUES(1038026,'And Judah acknowledged them, and said, She hath been more righteous than I; because that I gave her not to Shelah my son. And he knew her again no more.');
-INSERT INTO t1(docid,words) VALUES(1038027,'And it came to pass in the time of her travail, that, behold, twins were in her womb.');
-INSERT INTO t1(docid,words) VALUES(1038028,'And it came to pass, when she travailed, that the one put out his hand: and the midwife took and bound upon his hand a scarlet thread, saying, This came out first.');
-INSERT INTO t1(docid,words) VALUES(1038029,'And it came to pass, as he drew back his hand, that, behold, his brother came out: and she said, How hast thou broken forth? this breach be upon thee: therefore his name was called Pharez.');
-INSERT INTO t1(docid,words) VALUES(1038030,'And afterward came out his brother, that had the scarlet thread upon his hand: and his name was called Zarah.');
-INSERT INTO t1(docid,words) VALUES(1039001,'And Joseph was brought down to Egypt; and Potiphar, an officer of Pharaoh, captain of the guard, an Egyptian, bought him of the hands of the Ishmeelites, which had brought him down thither.');
-INSERT INTO t1(docid,words) VALUES(1039002,'And the LORD was with Joseph, and he was a prosperous man; and he was in the house of his master the Egyptian.');
-INSERT INTO t1(docid,words) VALUES(1039003,'And his master saw that the LORD was with him, and that the LORD made all that he did to prosper in his hand.');
-INSERT INTO t1(docid,words) VALUES(1039004,'And Joseph found grace in his sight, and he served him: and he made him overseer over his house, and all that he had he put into his hand.');
-INSERT INTO t1(docid,words) VALUES(1039005,'And it came to pass from the time that he had made him overseer in his house, and over all that he had, that the LORD blessed the Egyptian''s house for Joseph''s sake; and the blessing of the LORD was upon all that he had in the house, and in the field.');
-INSERT INTO t1(docid,words) VALUES(1039006,'And he left all that he had in Joseph''s hand; and he knew not ought he had, save the bread which he did eat. And Joseph was a goodly person, and well favoured.');
-INSERT INTO t1(docid,words) VALUES(1039007,'And it came to pass after these things, that his master''s wife cast her eyes upon Joseph; and she said, Lie with me.');
-INSERT INTO t1(docid,words) VALUES(1039008,'But he refused, and said unto his master''s wife, Behold, my master wotteth not what is with me in the house, and he hath committed all that he hath to my hand;');
-INSERT INTO t1(docid,words) VALUES(1039009,'There is none greater in this house than I; neither hath he kept back any thing from me but thee, because thou art his wife: how then can I do this great wickedness, and sin against God?');
-INSERT INTO t1(docid,words) VALUES(1039010,'And it came to pass, as she spake to Joseph day by day, that he hearkened not unto her, to lie by her, or to be with her.');
-INSERT INTO t1(docid,words) VALUES(1039011,'And it came to pass about this time, that Joseph went into the house to do his business; and there was none of the men of the house there within.');
-INSERT INTO t1(docid,words) VALUES(1039012,'And she caught him by his garment, saying, Lie with me: and he left his garment in her hand, and fled, and got him out.');
-INSERT INTO t1(docid,words) VALUES(1039013,'And it came to pass, when she saw that he had left his garment in her hand, and was fled forth,');
-INSERT INTO t1(docid,words) VALUES(1039014,'That she called unto the men of her house, and spake unto them, saying, See, he hath brought in an Hebrew unto us to mock us; he came in unto me to lie with me, and I cried with a loud voice:');
-INSERT INTO t1(docid,words) VALUES(1039015,'And it came to pass, when he heard that I lifted up my voice and cried, that he left his garment with me, and fled, and got him out.');
-INSERT INTO t1(docid,words) VALUES(1039016,'And she laid up his garment by her, until his lord came home.');
-INSERT INTO t1(docid,words) VALUES(1039017,'And she spake unto him according to these words, saying, The Hebrew servant, which thou hast brought unto us, came in unto me to mock me:');
-INSERT INTO t1(docid,words) VALUES(1039018,'And it came to pass, as I lifted up my voice and cried, that he left his garment with me, and fled out.');
-INSERT INTO t1(docid,words) VALUES(1039019,'And it came to pass, when his master heard the words of his wife, which she spake unto him, saying, After this manner did thy servant to me; that his wrath was kindled.');
-INSERT INTO t1(docid,words) VALUES(1039020,'And Joseph''s master took him, and put him into the prison, a place where the king''s prisoners were bound: and he was there in the prison.');
-INSERT INTO t1(docid,words) VALUES(1039021,'But the LORD was with Joseph, and shewed him mercy, and gave him favour in the sight of the keeper of the prison.');
-INSERT INTO t1(docid,words) VALUES(1039022,'And the keeper of the prison committed to Joseph''s hand all the prisoners that were in the prison; and whatsoever they did there, he was the doer of it.');
-INSERT INTO t1(docid,words) VALUES(1039023,'The keeper of the prison looked not to any thing that was under his hand; because the LORD was with him, and that which he did, the LORD made it to prosper.');
-INSERT INTO t1(docid,words) VALUES(1040001,'And it came to pass after these things, that the butler of the king of Egypt and his baker had offended their lord the king of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1040002,'And Pharaoh was wroth against two of his officers, against the chief of the butlers, and against the chief of the bakers.');
-INSERT INTO t1(docid,words) VALUES(1040003,'And he put them in ward in the house of the captain of the guard, into the prison, the place where Joseph was bound.');
-INSERT INTO t1(docid,words) VALUES(1040004,'And the captain of the guard charged Joseph with them, and he served them: and they continued a season in ward.');
-INSERT INTO t1(docid,words) VALUES(1040005,'And they dreamed a dream both of them, each man his dream in one night, each man according to the interpretation of his dream, the butler and the baker of the king of Egypt, which were bound in the prison.');
-INSERT INTO t1(docid,words) VALUES(1040006,'And Joseph came in unto them in the morning, and looked upon them, and, behold, they were sad.');
-INSERT INTO t1(docid,words) VALUES(1040007,'And he asked Pharaoh''s officers that were with him in the ward of his lord''s house, saying, Wherefore look ye so sadly to day?');
-INSERT INTO t1(docid,words) VALUES(1040008,'And they said unto him, We have dreamed a dream, and there is no interpreter of it. And Joseph said unto them, Do not interpretations belong to God? tell me them, I pray you.');
-INSERT INTO t1(docid,words) VALUES(1040009,'And the chief butler told his dream to Joseph, and said to him, In my dream, behold, a vine was before me;');
-INSERT INTO t1(docid,words) VALUES(1040010,'And in the vine were three branches: and it was as though it budded, and her blossoms shot forth; and the clusters thereof brought forth ripe grapes:');
-INSERT INTO t1(docid,words) VALUES(1040011,'And Pharaoh''s cup was in my hand: and I took the grapes, and pressed them into Pharaoh''s cup, and I gave the cup into Pharaoh''s hand.');
-INSERT INTO t1(docid,words) VALUES(1040012,'And Joseph said unto him, This is the interpretation of it: The three branches are three days:');
-INSERT INTO t1(docid,words) VALUES(1040013,'Yet within three days shall Pharaoh lift up thine head, and restore thee unto thy place: and thou shalt deliver Pharaoh''s cup into his hand, after the former manner when thou wast his butler.');
-INSERT INTO t1(docid,words) VALUES(1040014,'But think on me when it shall be well with thee, and shew kindness, I pray thee, unto me, and make mention of me unto Pharaoh, and bring me out of this house:');
-INSERT INTO t1(docid,words) VALUES(1040015,'For indeed I was stolen away out of the land of the Hebrews: and here also have I done nothing that they should put me into the dungeon.');
-INSERT INTO t1(docid,words) VALUES(1040016,'When the chief baker saw that the interpretation was good, he said unto Joseph, I also was in my dream, and, behold, I had three white baskets on my head:');
-INSERT INTO t1(docid,words) VALUES(1040017,'And in the uppermost basket there was of all manner of bakemeats for Pharaoh; and the birds did eat them out of the basket upon my head.');
-INSERT INTO t1(docid,words) VALUES(1040018,'And Joseph answered and said, This is the interpretation thereof: The three baskets are three days:');
-INSERT INTO t1(docid,words) VALUES(1040019,'Yet within three days shall Pharaoh lift up thy head from off thee, and shall hang thee on a tree; and the birds shall eat thy flesh from off thee.');
-INSERT INTO t1(docid,words) VALUES(1040020,'And it came to pass the third day, which was Pharaoh''s birthday, that he made a feast unto all his servants: and he lifted up the head of the chief butler and of the chief baker among his servants.');
-INSERT INTO t1(docid,words) VALUES(1040021,'And he restored the chief butler unto his butlership again; and he gave the cup into Pharaoh''s hand:');
-INSERT INTO t1(docid,words) VALUES(1040022,'But he hanged the chief baker: as Joseph had interpreted to them.');
-INSERT INTO t1(docid,words) VALUES(1040023,'Yet did not the chief butler remember Joseph, but forgat him.');
-INSERT INTO t1(docid,words) VALUES(1041001,'And it came to pass at the end of two full years, that Pharaoh dreamed: and, behold, he stood by the river.');
-INSERT INTO t1(docid,words) VALUES(1041002,'And, behold, there came up out of the river seven well favoured kine and fatfleshed; and they fed in a meadow.');
-INSERT INTO t1(docid,words) VALUES(1041003,'And, behold, seven other kine came up after them out of the river, ill favoured and leanfleshed; and stood by the other kine upon the brink of the river.');
-INSERT INTO t1(docid,words) VALUES(1041004,'And the ill favoured and leanfleshed kine did eat up the seven well favoured and fat kine. So Pharaoh awoke.');
-INSERT INTO t1(docid,words) VALUES(1041005,'And he slept and dreamed the second time: and, behold, seven ears of corn came up upon one stalk, rank and good.');
-INSERT INTO t1(docid,words) VALUES(1041006,'And, behold, seven thin ears and blasted with the east wind sprung up after them.');
-INSERT INTO t1(docid,words) VALUES(1041007,'And the seven thin ears devoured the seven rank and full ears. And Pharaoh awoke, and, behold, it was a dream.');
-INSERT INTO t1(docid,words) VALUES(1041008,'And it came to pass in the morning that his spirit was troubled; and he sent and called for all the magicians of Egypt, and all the wise men thereof: and Pharaoh told them his dream; but there was none that could interpret them unto Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1041009,'Then spake the chief butler unto Pharaoh, saying, I do remember my faults this day:');
-INSERT INTO t1(docid,words) VALUES(1041010,'Pharaoh was wroth with his servants, and put me in ward in the captain of the guard''s house, both me and the chief baker:');
-INSERT INTO t1(docid,words) VALUES(1041011,'And we dreamed a dream in one night, I and he; we dreamed each man according to the interpretation of his dream.');
-INSERT INTO t1(docid,words) VALUES(1041012,'And there was there with us a young man, an Hebrew, servant to the captain of the guard; and we told him, and he interpreted to us our dreams; to each man according to his dream he did interpret.');
-INSERT INTO t1(docid,words) VALUES(1041013,'And it came to pass, as he interpreted to us, so it was; me he restored unto mine office, and him he hanged.');
-INSERT INTO t1(docid,words) VALUES(1041014,'Then Pharaoh sent and called Joseph, and they brought him hastily out of the dungeon: and he shaved himself, and changed his raiment, and came in unto Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1041015,'And Pharaoh said unto Joseph, I have dreamed a dream, and there is none that can interpret it: and I have heard say of thee, that thou canst understand a dream to interpret it.');
-INSERT INTO t1(docid,words) VALUES(1041016,'And Joseph answered Pharaoh, saying, It is not in me: God shall give Pharaoh an answer of peace.');
-INSERT INTO t1(docid,words) VALUES(1041017,'And Pharaoh said unto Joseph, In my dream, behold, I stood upon the bank of the river:');
-INSERT INTO t1(docid,words) VALUES(1041018,'And, behold, there came up out of the river seven kine, fatfleshed and well favoured; and they fed in a meadow:');
-INSERT INTO t1(docid,words) VALUES(1041019,'And, behold, seven other kine came up after them, poor and very ill favoured and leanfleshed, such as I never saw in all the land of Egypt for badness:');
-INSERT INTO t1(docid,words) VALUES(1041020,'And the lean and the ill favoured kine did eat up the first seven fat kine:');
-INSERT INTO t1(docid,words) VALUES(1041021,'And when they had eaten them up, it could not be known that they had eaten them; but they were still ill favoured, as at the beginning. So I awoke.');
-INSERT INTO t1(docid,words) VALUES(1041022,'And I saw in my dream, and, behold, seven ears came up in one stalk, full and good:');
-INSERT INTO t1(docid,words) VALUES(1041023,'And, behold, seven ears, withered, thin, and blasted with the east wind, sprung up after them:');
-INSERT INTO t1(docid,words) VALUES(1041024,'And the thin ears devoured the seven good ears: and I told this unto the magicians; but there was none that could declare it to me.');
-INSERT INTO t1(docid,words) VALUES(1041025,'And Joseph said unto Pharaoh, The dream of Pharaoh is one: God hath shewed Pharaoh what he is about to do.');
-INSERT INTO t1(docid,words) VALUES(1041026,'The seven good kine are seven years; and the seven good ears are seven years: the dream is one.');
-INSERT INTO t1(docid,words) VALUES(1041027,'And the seven thin and ill favoured kine that came up after them are seven years; and the seven empty ears blasted with the east wind shall be seven years of famine.');
-INSERT INTO t1(docid,words) VALUES(1041028,'This is the thing which I have spoken unto Pharaoh: What God is about to do he sheweth unto Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1041029,'Behold, there come seven years of great plenty throughout all the land of Egypt:');
-INSERT INTO t1(docid,words) VALUES(1041030,'And there shall arise after them seven years of famine; and all the plenty shall be forgotten in the land of Egypt; and the famine shall consume the land;');
-INSERT INTO t1(docid,words) VALUES(1041031,'And the plenty shall not be known in the land by reason of that famine following; for it shall be very grievous.');
-INSERT INTO t1(docid,words) VALUES(1041032,'And for that the dream was doubled unto Pharaoh twice; it is because the thing is established by God, and God will shortly bring it to pass.');
-INSERT INTO t1(docid,words) VALUES(1041033,'Now therefore let Pharaoh look out a man discreet and wise, and set him over the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041034,'Let Pharaoh do this, and let him appoint officers over the land, and take up the fifth part of the land of Egypt in the seven plenteous years.');
-INSERT INTO t1(docid,words) VALUES(1041035,'And let them gather all the food of those good years that come, and lay up corn under the hand of Pharaoh, and let them keep food in the cities.');
-INSERT INTO t1(docid,words) VALUES(1041036,'And that food shall be for store to the land against the seven years of famine, which shall be in the land of Egypt; that the land perish not through the famine.');
-INSERT INTO t1(docid,words) VALUES(1041037,'And the thing was good in the eyes of Pharaoh, and in the eyes of all his servants.');
-INSERT INTO t1(docid,words) VALUES(1041038,'And Pharaoh said unto his servants, Can we find such a one as this is, a man in whom the Spirit of God is?');
-INSERT INTO t1(docid,words) VALUES(1041039,'And Pharaoh said unto Joseph, Forasmuch as God hath shewed thee all this, there is none so discreet and wise as thou art:');
-INSERT INTO t1(docid,words) VALUES(1041040,'Thou shalt be over my house, and according unto thy word shall all my people be ruled: only in the throne will I be greater than thou.');
-INSERT INTO t1(docid,words) VALUES(1041041,'And Pharaoh said unto Joseph, See, I have set thee over all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041042,'And Pharaoh took off his ring from his hand, and put it upon Joseph''s hand, and arrayed him in vestures of fine linen, and put a gold chain about his neck;');
-INSERT INTO t1(docid,words) VALUES(1041043,'And he made him to ride in the second chariot which he had; and they cried before him, Bow the knee: and he made him ruler over all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041044,'And Pharaoh said unto Joseph, I am Pharaoh, and without thee shall no man lift up his hand or foot in all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041045,'And Pharaoh called Joseph''s name Zaphnathpaaneah; and he gave him to wife Asenath the daughter of Potipherah priest of On. And Joseph went out over all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041046,'And Joseph was thirty years old when he stood before Pharaoh king of Egypt. And Joseph went out from the presence of Pharaoh, and went throughout all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041047,'And in the seven plenteous years the earth brought forth by handfuls.');
-INSERT INTO t1(docid,words) VALUES(1041048,'And he gathered up all the food of the seven years, which were in the land of Egypt, and laid up the food in the cities: the food of the field, which was round about every city, laid he up in the same.');
-INSERT INTO t1(docid,words) VALUES(1041049,'And Joseph gathered corn as the sand of the sea, very much, until he left numbering; for it was without number.');
-INSERT INTO t1(docid,words) VALUES(1041050,'And unto Joseph were born two sons before the years of famine came, which Asenath the daughter of Potipherah priest of On bare unto him.');
-INSERT INTO t1(docid,words) VALUES(1041051,'And Joseph called the name of the firstborn Manasseh: For God, said he, hath made me forget all my toil, and all my father''s house.');
-INSERT INTO t1(docid,words) VALUES(1041052,'And the name of the second called he Ephraim: For God hath caused me to be fruitful in the land of my affliction.');
-INSERT INTO t1(docid,words) VALUES(1041053,'And the seven years of plenteousness, that was in the land of Egypt, were ended.');
-INSERT INTO t1(docid,words) VALUES(1041054,'And the seven years of dearth began to come, according as Joseph had said: and the dearth was in all lands; but in all the land of Egypt there was bread.');
-INSERT INTO t1(docid,words) VALUES(1041055,'And when all the land of Egypt was famished, the people cried to Pharaoh for bread: and Pharaoh said unto all the Egyptians, Go unto Joseph; what he saith to you, do.');
-INSERT INTO t1(docid,words) VALUES(1041056,'And the famine was over all the face of the earth: and Joseph opened all the storehouses, and sold unto the Egyptians; and the famine waxed sore in the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1041057,'And all countries came into Egypt to Joseph for to buy corn; because that the famine was so sore in all lands.');
-INSERT INTO t1(docid,words) VALUES(1042001,'Now when Jacob saw that there was corn in Egypt, Jacob said unto his sons, Why do ye look one upon another?');
-INSERT INTO t1(docid,words) VALUES(1042002,'And he said, Behold, I have heard that there is corn in Egypt: get you down thither, and buy for us from thence; that we may live, and not die.');
-INSERT INTO t1(docid,words) VALUES(1042003,'And Joseph''s ten brethren went down to buy corn in Egypt.');
-INSERT INTO t1(docid,words) VALUES(1042004,'But Benjamin, Joseph''s brother, Jacob sent not with his brethren; for he said, Lest peradventure mischief befall him.');
-INSERT INTO t1(docid,words) VALUES(1042005,'And the sons of Israel came to buy corn among those that came: for the famine was in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1042006,'And Joseph was the governor over the land, and he it was that sold to all the people of the land: and Joseph''s brethren came, and bowed down themselves before him with their faces to the earth.');
-INSERT INTO t1(docid,words) VALUES(1042007,'And Joseph saw his brethren, and he knew them, but made himself strange unto them, and spake roughly unto them; and he said unto them, Whence come ye? And they said, From the land of Canaan to buy food.');
-INSERT INTO t1(docid,words) VALUES(1042008,'And Joseph knew his brethren, but they knew not him.');
-INSERT INTO t1(docid,words) VALUES(1042009,'And Joseph remembered the dreams which he dreamed of them, and said unto them, Ye are spies; to see the nakedness of the land ye are come.');
-INSERT INTO t1(docid,words) VALUES(1042010,'And they said unto him, Nay, my lord, but to buy food are thy servants come.');
-INSERT INTO t1(docid,words) VALUES(1042011,'We are all one man''s sons; we are true men, thy servants are no spies.');
-INSERT INTO t1(docid,words) VALUES(1042012,'And he said unto them, Nay, but to see the nakedness of the land ye are come.');
-INSERT INTO t1(docid,words) VALUES(1042013,'And they said, Thy servants are twelve brethren, the sons of one man in the land of Canaan; and, behold, the youngest is this day with our father, and one is not.');
-INSERT INTO t1(docid,words) VALUES(1042014,'And Joseph said unto them, That is it that I spake unto you, saying, Ye are spies:');
-INSERT INTO t1(docid,words) VALUES(1042015,'Hereby ye shall be proved: By the life of Pharaoh ye shall not go forth hence, except your youngest brother come hither.');
-INSERT INTO t1(docid,words) VALUES(1042016,'Send one of you, and let him fetch your brother, and ye shall be kept in prison, that your words may be proved, whether there be any truth in you: or else by the life of Pharaoh surely ye are spies.');
-INSERT INTO t1(docid,words) VALUES(1042017,'And he put them all together into ward three days.');
-INSERT INTO t1(docid,words) VALUES(1042018,'And Joseph said unto them the third day, This do, and live; for I fear God:');
-INSERT INTO t1(docid,words) VALUES(1042019,'If ye be true men, let one of your brethren be bound in the house of your prison: go ye, carry corn for the famine of your houses:');
-INSERT INTO t1(docid,words) VALUES(1042020,'But bring your youngest brother unto me; so shall your words be verified, and ye shall not die. And they did so.');
-INSERT INTO t1(docid,words) VALUES(1042021,'And they said one to another, We are verily guilty concerning our brother, in that we saw the anguish of his soul, when he besought us, and we would not hear; therefore is this distress come upon us.');
-INSERT INTO t1(docid,words) VALUES(1042022,'And Reuben answered them, saying, Spake I not unto you, saying, Do not sin against the child; and ye would not hear? therefore, behold, also his blood is required.');
-INSERT INTO t1(docid,words) VALUES(1042023,'And they knew not that Joseph understood them; for he spake unto them by an interpreter.');
-INSERT INTO t1(docid,words) VALUES(1042024,'And he turned himself about from them, and wept; and returned to them again, and communed with them, and took from them Simeon, and bound him before their eyes.');
-INSERT INTO t1(docid,words) VALUES(1042025,'Then Joseph commanded to fill their sacks with corn, and to restore every man''s money into his sack, and to give them provision for the way: and thus did he unto them.');
-INSERT INTO t1(docid,words) VALUES(1042026,'And they laded their asses with the corn, and departed thence.');
-INSERT INTO t1(docid,words) VALUES(1042027,'And as one of them opened his sack to give his ass provender in the inn, he espied his money; for, behold, it was in his sack''s mouth.');
-INSERT INTO t1(docid,words) VALUES(1042028,'And he said unto his brethren, My money is restored; and, lo, it is even in my sack: and their heart failed them, and they were afraid, saying one to another, What is this that God hath done unto us?');
-INSERT INTO t1(docid,words) VALUES(1042029,'And they came unto Jacob their father unto the land of Canaan, and told him all that befell unto them; saying,');
-INSERT INTO t1(docid,words) VALUES(1042030,'The man, who is the lord of the land, spake roughly to us, and took us for spies of the country.');
-INSERT INTO t1(docid,words) VALUES(1042031,'And we said unto him, We are true men; we are no spies:');
-INSERT INTO t1(docid,words) VALUES(1042032,'We be twelve brethren, sons of our father; one is not, and the youngest is this day with our father in the land of Canaan.');
-INSERT INTO t1(docid,words) VALUES(1042033,'And the man, the lord of the country, said unto us, Hereby shall I know that ye are true men; leave one of your brethren here with me, and take food for the famine of your households, and be gone:');
-INSERT INTO t1(docid,words) VALUES(1042034,'And bring your youngest brother unto me: then shall I know that ye are no spies, but that ye are true men: so will I deliver you your brother, and ye shall traffick in the land.');
-INSERT INTO t1(docid,words) VALUES(1042035,'And it came to pass as they emptied their sacks, that, behold, every man''s bundle of money was in his sack: and when both they and their father saw the bundles of money, they were afraid.');
-INSERT INTO t1(docid,words) VALUES(1042036,'And Jacob their father said unto them, Me have ye bereaved of my children: Joseph is not, and Simeon is not, and ye will take Benjamin away: all these things are against me.');
-INSERT INTO t1(docid,words) VALUES(1042037,'And Reuben spake unto his father, saying, Slay my two sons, if I bring him not to thee: deliver him into my hand, and I will bring him to thee again.');
-INSERT INTO t1(docid,words) VALUES(1042038,'And he said, My son shall not go down with you; for his brother is dead, and he is left alone: if mischief befall him by the way in the which ye go, then shall ye bring down my gray hairs with sorrow to the grave.');
-INSERT INTO t1(docid,words) VALUES(1043001,'And the famine was sore in the land.');
-INSERT INTO t1(docid,words) VALUES(1043002,'And it came to pass, when they had eaten up the corn which they had brought out of Egypt, their father said unto them, Go again, buy us a little food.');
-INSERT INTO t1(docid,words) VALUES(1043003,'And Judah spake unto him, saying, The man did solemnly protest unto us, saying, Ye shall not see my face, except your brother be with you.');
-INSERT INTO t1(docid,words) VALUES(1043004,'If thou wilt send our brother with us, we will go down and buy thee food:');
-INSERT INTO t1(docid,words) VALUES(1043005,'But if thou wilt not send him, we will not go down: for the man said unto us, Ye shall not see my face, except your brother be with you.');
-INSERT INTO t1(docid,words) VALUES(1043006,'And Israel said, Wherefore dealt ye so ill with me, as to tell the man whether ye had yet a brother?');
-INSERT INTO t1(docid,words) VALUES(1043007,'And they said, The man asked us straitly of our state, and of our kindred, saying, Is your father yet alive? have ye another brother? and we told him according to the tenor of these words: could we certainly know that he would say, Bring your brother down?');
-INSERT INTO t1(docid,words) VALUES(1043008,'And Judah said unto Israel his father, Send the lad with me, and we will arise and go; that we may live, and not die, both we, and thou, and also our little ones.');
-INSERT INTO t1(docid,words) VALUES(1043009,'I will be surety for him; of my hand shalt thou require him: if I bring him not unto thee, and set him before thee, then let me bear the blame for ever:');
-INSERT INTO t1(docid,words) VALUES(1043010,'For except we had lingered, surely now we had returned this second time.');
-INSERT INTO t1(docid,words) VALUES(1043011,'And their father Israel said unto them, If it must be so now, do this; take of the best fruits in the land in your vessels, and carry down the man a present, a little balm, and a little honey, spices, and myrrh, nuts, and almonds:');
-INSERT INTO t1(docid,words) VALUES(1043012,'And take double money in your hand; and the money that was brought again in the mouth of your sacks, carry it again in your hand; peradventure it was an oversight:');
-INSERT INTO t1(docid,words) VALUES(1043013,'Take also your brother, and arise, go again unto the man:');
-INSERT INTO t1(docid,words) VALUES(1043014,'And God Almighty give you mercy before the man, that he may send away your other brother, and Benjamin. If I be bereaved of my children, I am bereaved.');
-INSERT INTO t1(docid,words) VALUES(1043015,'And the men took that present, and they took double money in their hand and Benjamin; and rose up, and went down to Egypt, and stood before Joseph.');
-INSERT INTO t1(docid,words) VALUES(1043016,'And when Joseph saw Benjamin with them, he said to the ruler of his house, Bring these men home, and slay, and make ready; for these men shall dine with me at noon.');
-INSERT INTO t1(docid,words) VALUES(1043017,'And the man did as Joseph bade; and the man brought the men into Joseph''s house.');
-INSERT INTO t1(docid,words) VALUES(1043018,'And the men were afraid, because they were brought into Joseph''s house; and they said, Because of the money that was returned in our sacks at the first time are we brought in; that he may seek occasion against us, and fall upon us, and take us for bondmen, and our asses.');
-INSERT INTO t1(docid,words) VALUES(1043019,'And they came near to the steward of Joseph''s house, and they communed with him at the door of the house,');
-INSERT INTO t1(docid,words) VALUES(1043020,'And said, O sir, we came indeed down at the first time to buy food:');
-INSERT INTO t1(docid,words) VALUES(1043021,'And it came to pass, when we came to the inn, that we opened our sacks, and, behold, every man''s money was in the mouth of his sack, our money in full weight: and we have brought it again in our hand.');
-INSERT INTO t1(docid,words) VALUES(1043022,'And other money have we brought down in our hands to buy food: we cannot tell who put our money in our sacks.');
-INSERT INTO t1(docid,words) VALUES(1043023,'And he said, Peace be to you, fear not: your God, and the God of your father, hath given you treasure in your sacks: I had your money. And he brought Simeon out unto them.');
-INSERT INTO t1(docid,words) VALUES(1043024,'And the man brought the men into Joseph''s house, and gave them water, and they washed their feet; and he gave their asses provender.');
-INSERT INTO t1(docid,words) VALUES(1043025,'And they made ready the present against Joseph came at noon: for they heard that they should eat bread there.');
-INSERT INTO t1(docid,words) VALUES(1043026,'And when Joseph came home, they brought him the present which was in their hand into the house, and bowed themselves to him to the earth.');
-INSERT INTO t1(docid,words) VALUES(1043027,'And he asked them of their welfare, and said, Is your father well, the old man of whom ye spake? Is he yet alive?');
-INSERT INTO t1(docid,words) VALUES(1043028,'And they answered, Thy servant our father is in good health, he is yet alive. And they bowed down their heads, and made obeisance.');
-INSERT INTO t1(docid,words) VALUES(1043029,'And he lifted up his eyes, and saw his brother Benjamin, his mother''s son, and said, Is this your younger brother, of whom ye spake unto me? And he said, God be gracious unto thee, my son.');
-INSERT INTO t1(docid,words) VALUES(1043030,'And Joseph made haste; for his bowels did yearn upon his brother: and he sought where to weep; and he entered into his chamber, and wept there.');
-INSERT INTO t1(docid,words) VALUES(1043031,'And he washed his face, and went out, and refrained himself, and said, Set on bread.');
-INSERT INTO t1(docid,words) VALUES(1043032,'And they set on for him by himself, and for them by themselves, and for the Egyptians, which did eat with him, by themselves: because the Egyptians might not eat bread with the Hebrews; for that is an abomination unto the Egyptians.');
-INSERT INTO t1(docid,words) VALUES(1043033,'And they sat before him, the firstborn according to his birthright, and the youngest according to his youth: and the men marvelled one at another.');
-INSERT INTO t1(docid,words) VALUES(1043034,'And he took and sent messes unto them from before him: but Benjamin''s mess was five times so much as any of their''s. And they drank, and were merry with him.');
-INSERT INTO t1(docid,words) VALUES(1044001,'And he commanded the steward of his house, saying, Fill the men''s sacks with food, as much as they can carry, and put every man''s money in his sack''s mouth.');
-INSERT INTO t1(docid,words) VALUES(1044002,'And put my cup, the silver cup, in the sack''s mouth of the youngest, and his corn money. And he did according to the word that Joseph had spoken.');
-INSERT INTO t1(docid,words) VALUES(1044003,'As soon as the morning was light, the men were sent away, they and their asses.');
-INSERT INTO t1(docid,words) VALUES(1044004,'And when they were gone out of the city, and not yet far off, Joseph said unto his steward, Up, follow after the men; and when thou dost overtake them, say unto them, Wherefore have ye rewarded evil for good?');
-INSERT INTO t1(docid,words) VALUES(1044005,'Is not this it in which my lord drinketh, and whereby indeed he divineth? ye have done evil in so doing.');
-INSERT INTO t1(docid,words) VALUES(1044006,'And he overtook them, and he spake unto them these same words.');
-INSERT INTO t1(docid,words) VALUES(1044007,'And they said unto him, Wherefore saith my lord these words? God forbid that thy servants should do according to this thing:');
-INSERT INTO t1(docid,words) VALUES(1044008,'Behold, the money, which we found in our sacks'' mouths, we brought again unto thee out of the land of Canaan: how then should we steal out of thy lord''s house silver or gold?');
-INSERT INTO t1(docid,words) VALUES(1044009,'With whomsoever of thy servants it be found, both let him die, and we also will be my lord''s bondmen.');
-INSERT INTO t1(docid,words) VALUES(1044010,'And he said, Now also let it be according unto your words: he with whom it is found shall be my servant; and ye shall be blameless.');
-INSERT INTO t1(docid,words) VALUES(1044011,'Then they speedily took down every man his sack to the ground, and opened every man his sack.');
-INSERT INTO t1(docid,words) VALUES(1044012,'And he searched, and began at the eldest, and left at the youngest: and the cup was found in Benjamin''s sack.');
-INSERT INTO t1(docid,words) VALUES(1044013,'Then they rent their clothes, and laded every man his ass, and returned to the city.');
-INSERT INTO t1(docid,words) VALUES(1044014,'And Judah and his brethren came to Joseph''s house; for he was yet there: and they fell before him on the ground.');
-INSERT INTO t1(docid,words) VALUES(1044015,'And Joseph said unto them, What deed is this that ye have done? wot ye not that such a man as I can certainly divine?');
-INSERT INTO t1(docid,words) VALUES(1044016,'And Judah said, What shall we say unto my lord? what shall we speak? or how shall we clear ourselves? God hath found out the iniquity of thy servants: behold, we are my lord''s servants, both we, and he also with whom the cup is found.');
-INSERT INTO t1(docid,words) VALUES(1044017,'And he said, God forbid that I should do so: but the man in whose hand the cup is found, he shall be my servant; and as for you, get you up in peace unto your father.');
-INSERT INTO t1(docid,words) VALUES(1044018,'Then Judah came near unto him, and said, Oh my lord, let thy servant, I pray thee, speak a word in my lord''s ears, and let not thine anger burn against thy servant: for thou art even as Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1044019,'My lord asked his servants, saying, Have ye a father, or a brother?');
-INSERT INTO t1(docid,words) VALUES(1044020,'And we said unto my lord, We have a father, an old man, and a child of his old age, a little one; and his brother is dead, and he alone is left of his mother, and his father loveth him.');
-INSERT INTO t1(docid,words) VALUES(1044021,'And thou saidst unto thy servants, Bring him down unto me, that I may set mine eyes upon him.');
-INSERT INTO t1(docid,words) VALUES(1044022,'And we said unto my lord, The lad cannot leave his father: for if he should leave his father, his father would die.');
-INSERT INTO t1(docid,words) VALUES(1044023,'And thou saidst unto thy servants, Except your youngest brother come down with you, ye shall see my face no more.');
-INSERT INTO t1(docid,words) VALUES(1044024,'And it came to pass when we came up unto thy servant my father, we told him the words of my lord.');
-INSERT INTO t1(docid,words) VALUES(1044025,'And our father said, Go again, and buy us a little food.');
-INSERT INTO t1(docid,words) VALUES(1044026,'And we said, We cannot go down: if our youngest brother be with us, then will we go down: for we may not see the man''s face, except our youngest brother be with us.');
-INSERT INTO t1(docid,words) VALUES(1044027,'And thy servant my father said unto us, Ye know that my wife bare me two sons:');
-INSERT INTO t1(docid,words) VALUES(1044028,'And the one went out from me, and I said, Surely he is torn in pieces; and I saw him not since:');
-INSERT INTO t1(docid,words) VALUES(1044029,'And if ye take this also from me, and mischief befall him, ye shall bring down my gray hairs with sorrow to the grave.');
-INSERT INTO t1(docid,words) VALUES(1044030,'Now therefore when I come to thy servant my father, and the lad be not with us; seeing that his life is bound up in the lad''s life;');
-INSERT INTO t1(docid,words) VALUES(1044031,'It shall come to pass, when he seeth that the lad is not with us, that he will die: and thy servants shall bring down the gray hairs of thy servant our father with sorrow to the grave.');
-INSERT INTO t1(docid,words) VALUES(1044032,'For thy servant became surety for the lad unto my father, saying, If I bring him not unto thee, then I shall bear the blame to my father for ever.');
-INSERT INTO t1(docid,words) VALUES(1044033,'Now therefore, I pray thee, let thy servant abide instead of the lad a bondman to my lord; and let the lad go up with his brethren.');
-INSERT INTO t1(docid,words) VALUES(1044034,'For how shall I go up to my father, and the lad be not with me? lest peradventure I see the evil that shall come on my father.');
-INSERT INTO t1(docid,words) VALUES(1045001,'Then Joseph could not refrain himself before all them that stood by him; and he cried, Cause every man to go out from me. And there stood no man with him, while Joseph made himself known unto his brethren.');
-INSERT INTO t1(docid,words) VALUES(1045002,'And he wept aloud: and the Egyptians and the house of Pharaoh heard.');
-INSERT INTO t1(docid,words) VALUES(1045003,'And Joseph said unto his brethren, I am Joseph; doth my father yet live? And his brethren could not answer him; for they were troubled at his presence.');
-INSERT INTO t1(docid,words) VALUES(1045004,'And Joseph said unto his brethren, Come near to me, I pray you. And they came near. And he said, I am Joseph your brother, whom ye sold into Egypt.');
-INSERT INTO t1(docid,words) VALUES(1045005,'Now therefore be not grieved, nor angry with yourselves, that ye sold me hither: for God did send me before you to preserve life.');
-INSERT INTO t1(docid,words) VALUES(1045006,'For these two years hath the famine been in the land: and yet there are five years, in the which there shall neither be earing nor harvest.');
-INSERT INTO t1(docid,words) VALUES(1045007,'And God sent me before you to preserve you a posterity in the earth, and to save your lives by a great deliverance.');
-INSERT INTO t1(docid,words) VALUES(1045008,'So now it was not you that sent me hither, but God: and he hath made me a father to Pharaoh, and lord of all his house, and a ruler throughout all the land of Egypt.');
-INSERT INTO t1(docid,words) VALUES(1045009,'Haste ye, and go up to my father, and say unto him, Thus saith thy son Joseph, God hath made me lord of all Egypt: come down unto me, tarry not:');
-INSERT INTO t1(docid,words) VALUES(1045010,'And thou shalt dwell in the land of Goshen, and thou shalt be near unto me, thou, and thy children, and thy children''s children, and thy flocks, and thy herds, and all that thou hast:');
-INSERT INTO t1(docid,words) VALUES(1045011,'And there will I nourish thee; for yet there are five years of famine; lest thou, and thy household, and all that thou hast, come to poverty.');
-INSERT INTO t1(docid,words) VALUES(1045012,'And, behold, your eyes see, and the eyes of my brother Benjamin, that it is my mouth that speaketh unto you.');
-INSERT INTO t1(docid,words) VALUES(1045013,'And ye shall tell my father of all my glory in Egypt, and of all that ye have seen; and ye shall haste and bring down my father hither.');
-INSERT INTO t1(docid,words) VALUES(1045014,'And he fell upon his brother Benjamin''s neck, and wept; and Benjamin wept upon his neck.');
-INSERT INTO t1(docid,words) VALUES(1045015,'Moreover he kissed all his brethren, and wept upon them: and after that his brethren talked with him.');
-INSERT INTO t1(docid,words) VALUES(1045016,'And the fame thereof was heard in Pharaoh''s house, saying, Joseph''s brethren are come: and it pleased Pharaoh well, and his servants.');
-INSERT INTO t1(docid,words) VALUES(1045017,'And Pharaoh said unto Joseph, Say unto thy brethren, This do ye; lade your beasts, and go, get you unto the land of Canaan;');
-INSERT INTO t1(docid,words) VALUES(1045018,'And take your father and your households, and come unto me: and I will give you the good of the land of Egypt, and ye shall eat the fat of the land.');
-INSERT INTO t1(docid,words) VALUES(1045019,'Now thou art commanded, this do ye; take you wagons out of the land of Egypt for your little ones, and for your wives, and bring your father, and come.');
-INSERT INTO t1(docid,words) VALUES(1045020,'Also regard not your stuff; for the good of all the land of Egypt is your''s.');
-INSERT INTO t1(docid,words) VALUES(1045021,'And the children of Israel did so: and Joseph gave them wagons, according to the commandment of Pharaoh, and gave them provision for the way.');
-INSERT INTO t1(docid,words) VALUES(1045022,'To all of them he gave each man changes of raiment; but to Benjamin he gave three hundred pieces of silver, and five changes of raiment.');
-INSERT INTO t1(docid,words) VALUES(1045023,'And to his father he sent after this manner; ten asses laden with the good things of Egypt, and ten she asses laden with corn and bread and meat for his father by the way.');
-INSERT INTO t1(docid,words) VALUES(1045024,'So he sent his brethren away, and they departed: and he said unto them, See that ye fall not out by the way.');
-INSERT INTO t1(docid,words) VALUES(1045025,'And they went up out of Egypt, and came into the land of Canaan unto Jacob their father,');
-INSERT INTO t1(docid,words) VALUES(1045026,'And told him, saying, Joseph is yet alive, and he is governor over all the land of Egypt. And Jacob''s heart fainted, for he believed them not.');
-INSERT INTO t1(docid,words) VALUES(1045027,'And they told him all the words of Joseph, which he had said unto them: and when he saw the wagons which Joseph had sent to carry him, the spirit of Jacob their father revived:');
-INSERT INTO t1(docid,words) VALUES(1045028,'And Israel said, It is enough; Joseph my son is yet alive: I will go and see him before I die.');
-INSERT INTO t1(docid,words) VALUES(1046001,'And Israel took his journey with all that he had, and came to Beersheba, and offered sacrifices unto the God of his father Isaac.');
-INSERT INTO t1(docid,words) VALUES(1046002,'And God spake unto Israel in the visions of the night, and said, Jacob, Jacob. And he said, Here am I.');
-INSERT INTO t1(docid,words) VALUES(1046003,'And he said, I am God, the God of thy father: fear not to go down into Egypt; for I will there make of thee a great nation:');
-INSERT INTO t1(docid,words) VALUES(1046004,'I will go down with thee into Egypt; and I will also surely bring thee up again: and Joseph shall put his hand upon thine eyes.');
-INSERT INTO t1(docid,words) VALUES(1046005,'And Jacob rose up from Beersheba: and the sons of Israel carried Jacob their father, and their little ones, and their wives, in the wagons which Pharaoh had sent to carry him.');
-INSERT INTO t1(docid,words) VALUES(1046006,'And they took their cattle, and their goods, which they had gotten in the land of Canaan, and came into Egypt, Jacob, and all his seed with him:');
-INSERT INTO t1(docid,words) VALUES(1046007,'His sons, and his sons'' sons with him, his daughters, and his sons'' daughters, and all his seed brought he with him into Egypt.');
-INSERT INTO t1(docid,words) VALUES(1046008,'And these are the names of the children of Israel, which came into Egypt, Jacob and his sons: Reuben, Jacob''s firstborn.');
-INSERT INTO t1(docid,words) VALUES(1046009,'And the sons of Reuben; Hanoch, and Phallu, and Hezron, and Carmi.');
-INSERT INTO t1(docid,words) VALUES(1046010,'And the sons of Simeon; Jemuel, and Jamin, and Ohad, and Jachin, and Zohar, and Shaul the son of a Canaanitish woman.');
-INSERT INTO t1(docid,words) VALUES(1046011,'And the sons of Levi; Gershon, Kohath, and Merari.');
-INSERT INTO t1(docid,words) VALUES(1046012,'And the sons of Judah; Er, and Onan, and Shelah, and Pharez, and Zarah: but Er and Onan died in the land of Canaan. And the sons of Pharez were Hezron and Hamul.');
-INSERT INTO t1(docid,words) VALUES(1046013,'And the sons of Issachar; Tola, and Phuvah, and Job, and Shimron.');
-INSERT INTO t1(docid,words) VALUES(1046014,'And the sons of Zebulun; Sered, and Elon, and Jahleel.');
-INSERT INTO t1(docid,words) VALUES(1046015,'These be the sons of Leah, which she bare unto Jacob in Padanaram, with his daughter Dinah: all the souls of his sons and his daughters were thirty and three.');
-INSERT INTO t1(docid,words) VALUES(1046016,'And the sons of Gad; Ziphion, and Haggi, Shuni, and Ezbon, Eri, and Arodi, and Areli.');
-INSERT INTO t1(docid,words) VALUES(1046017,'And the sons of Asher; Jimnah, and Ishuah, and Isui, and Beriah, and Serah their sister: and the sons of Beriah; Heber, and Malchiel.');
-INSERT INTO t1(docid,words) VALUES(1046018,'These are the sons of Zilpah, whom Laban gave to Leah his daughter, and these she bare unto Jacob, even sixteen souls.');
-INSERT INTO t1(docid,words) VALUES(1046019,'The sons of Rachel Jacob''s wife; Joseph, and Benjamin.');
-INSERT INTO t1(docid,words) VALUES(1046020,'And unto Joseph in the land of Egypt were born Manasseh and Ephraim, which Asenath the daughter of Potipherah priest of On bare unto him.');
-INSERT INTO t1(docid,words) VALUES(1046021,'And the sons of Benjamin were Belah, and Becher, and Ashbel, Gera, and Naaman, Ehi, and Rosh, Muppim, and Huppim, and Ard.');
-INSERT INTO t1(docid,words) VALUES(1046022,'These are the sons of Rachel, which were born to Jacob: all the souls were fourteen.');
-INSERT INTO t1(docid,words) VALUES(1046023,'And the sons of Dan; Hushim.');
-INSERT INTO t1(docid,words) VALUES(1046024,'And the sons of Naphtali; Jahzeel, and Guni, and Jezer, and Shillem.');
-INSERT INTO t1(docid,words) VALUES(1046025,'These are the sons of Bilhah, which Laban gave unto Rachel his daughter, and she bare these unto Jacob: all the souls were seven.');
-INSERT INTO t1(docid,words) VALUES(1046026,'All the souls that came with Jacob into Egypt, which came out of his loins, besides Jacob''s sons'' wives, all the souls were threescore and six;');
-INSERT INTO t1(docid,words) VALUES(1046027,'And the sons of Joseph, which were born him in Egypt, were two souls: all the souls of the house of Jacob, which came into Egypt, were threescore and ten.');
-INSERT INTO t1(docid,words) VALUES(1046028,'And he sent Judah before him unto Joseph, to direct his face unto Goshen; and they came into the land of Goshen.');
-INSERT INTO t1(docid,words) VALUES(1046029,'And Joseph made ready his chariot, and went up to meet Israel his father, to Goshen, and presented himself unto him; and he fell on his neck, and wept on his neck a good while.');
-INSERT INTO t1(docid,words) VALUES(1046030,'And Israel said unto Joseph, Now let me die, since I have seen thy face, because thou art yet alive.');
-INSERT INTO t1(docid,words) VALUES(1046031,'And Joseph said unto his brethren, and unto his father''s house, I will go up, and shew Pharaoh, and say unto him, My brethren, and my father''s house, which were in the land of Canaan, are come unto me;');
-INSERT INTO t1(docid,words) VALUES(1046032,'And the men are shepherds, for their trade hath been to feed cattle; and they have brought their flocks, and their herds, and all that they have.');
-INSERT INTO t1(docid,words) VALUES(1046033,'And it shall come to pass, when Pharaoh shall call you, and shall say, What is your occupation?');
-INSERT INTO t1(docid,words) VALUES(1046034,'That ye shall say, Thy servants'' trade hath been about cattle from our youth even until now, both we, and also our fathers: that ye may dwell in the land of Goshen; for every shepherd is an abomination unto the Egyptians.');
-INSERT INTO t1(docid,words) VALUES(1047001,'Then Joseph came and told Pharaoh, and said, My father and my brethren, and their flocks, and their herds, and all that they have, are come out of the land of Canaan; and, behold, they are in the land of Goshen.');
-INSERT INTO t1(docid,words) VALUES(1047002,'And he took some of his brethren, even five men, and presented them unto Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1047003,'And Pharaoh said unto his brethren, What is your occupation? And they said unto Pharaoh, Thy servants are shepherds, both we, and also our fathers.');
-INSERT INTO t1(docid,words) VALUES(1047004,'They said morever unto Pharaoh, For to sojourn in the land are we come; for thy servants have no pasture for their flocks; for the famine is sore in the land of Canaan: now therefore, we pray thee, let thy servants dwell in the land of Goshen.');
-INSERT INTO t1(docid,words) VALUES(1047005,'And Pharaoh spake unto Joseph, saying, Thy father and thy brethren are come unto thee:');
-INSERT INTO t1(docid,words) VALUES(1047006,'The land of Egypt is before thee; in the best of the land make thy father and brethren to dwell; in the land of Goshen let them dwell: and if thou knowest any men of activity among them, then make them rulers over my cattle.');
-INSERT INTO t1(docid,words) VALUES(1047007,'And Joseph brought in Jacob his father, and set him before Pharaoh: and Jacob blessed Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1047008,'And Pharaoh said unto Jacob, How old art thou?');
-INSERT INTO t1(docid,words) VALUES(1047009,'And Jacob said unto Pharaoh, The days of the years of my pilgrimage are an hundred and thirty years: few and evil have the days of the years of my life been, and have not attained unto the days of the years of the life of my fathers in the days of their pilgrimage.');
-INSERT INTO t1(docid,words) VALUES(1047010,'And Jacob blessed Pharaoh, and went out from before Pharaoh.');
-INSERT INTO t1(docid,words) VALUES(1047011,'And Joseph placed his father and his brethren, and gave them a possession in the land of Egypt, in the best of the land, in the land of Rameses, as Pharaoh had commanded.');
-INSERT INTO t1(docid,words) VALUES(1047012,'And Joseph nourished his father, and his brethren, and all his father''s household, with bread, according to their families.');
-INSERT INTO t1(docid,words) VALUES(1047013,'And there was no bread in all the land; for the famine was very sore, so that the land of Egypt and all the land of Canaan fainted by reason of the famine.');
-INSERT INTO t1(docid,words) VALUES(1047014,'And Joseph gathered up all the money that was found in the land of Egypt, and in the land of Canaan, for the corn which they bought: and Joseph brought the money into Pharaoh''s house.');
-INSERT INTO t1(docid,words) VALUES(1047015,'And when money failed in the land of Egypt, and in the land of Canaan, all the Egyptians came unto Joseph, and said, Give us bread: for why should we die in thy presence? for the money faileth.');
-INSERT INTO t1(docid,words) VALUES(1047016,'And Joseph said, Give your cattle; and I will give you for your cattle, if money fail.');
-INSERT INTO t1(docid,words) VALUES(1047017,'And they brought their cattle unto Joseph: and Joseph gave them bread in exchange for horses, and for the flocks, and for the cattle of the herds, and for the asses: and he fed them with bread for all their cattle for that year.');
-INSERT INTO t1(docid,words) VALUES(1047018,'When that year was ended, they came unto him the second year, and said unto him, We will not hide it from my lord, how that our money is spent; my lord also hath our herds of cattle; there is not ought left in the sight of my lord, but our bodies, and our lands:');
-INSERT INTO t1(docid,words) VALUES(1047019,'Wherefore shall we die before thine eyes, both we and our land? buy us and our land for bread, and we and our land will be servants unto Pharaoh: and give us seed, that we may live, and not die, that the land be not desolate.');
-INSERT INTO t1(docid,words) VALUES(1047020,'And Joseph bought all the land of Egypt for Pharaoh; for the Egyptians sold every man his field, because the famine prevailed over them: so the land became Pharaoh''s.');
-INSERT INTO t1(docid,words) VALUES(1047021,'And as for the people, he removed them to cities from one end of the borders of Egypt even to the other end thereof.');
-INSERT INTO t1(docid,words) VALUES(1047022,'Only the land of the priests bought he not; for the priests had a portion assigned them of Pharaoh, and did eat their portion which Pharaoh gave them: wherefore they sold not their lands.');
-INSERT INTO t1(docid,words) VALUES(1047023,'Then Joseph said unto the people, Behold, I have bought you this day and your land for Pharaoh: lo, here is seed for you, and ye shall sow the land.');
-INSERT INTO t1(docid,words) VALUES(1047024,'And it shall come to pass in the increase, that ye shall give the fifth part unto Pharaoh, and four parts shall be your own, for seed of the field, and for your food, and for them of your households, and for food for your little ones.');
-INSERT INTO t1(docid,words) VALUES(1047025,'And they said, Thou hast saved our lives: let us find grace in the sight of my lord, and we will be Pharaoh''s servants.');
-INSERT INTO t1(docid,words) VALUES(1047026,'And Joseph made it a law over the land of Egypt unto this day, that Pharaoh should have the fifth part, except the land of the priests only, which became not Pharaoh''s.');
-INSERT INTO t1(docid,words) VALUES(1047027,'And Israel dwelt in the land of Egypt, in the country of Goshen; and they had possessions therein, and grew, and multiplied exceedingly.');
-INSERT INTO t1(docid,words) VALUES(1047028,'And Jacob lived in the land of Egypt seventeen years: so the whole age of Jacob was an hundred forty and seven years.');
-INSERT INTO t1(docid,words) VALUES(1047029,'And the time drew nigh that Israel must die: and he called his son Joseph, and said unto him, If now I have found grace in thy sight, put, I pray thee, thy hand under my thigh, and deal kindly and truly with me; bury me not, I pray thee, in Egypt:');
-INSERT INTO t1(docid,words) VALUES(1047030,'But I will lie with my fathers, and thou shalt carry me out of Egypt, and bury me in their buryingplace. And he said, I will do as thou hast said.');
-INSERT INTO t1(docid,words) VALUES(1047031,'And he said, Swear unto me. And he sware unto him. And Israel bowed himself upon the bed''s head.');
-INSERT INTO t1(docid,words) VALUES(1048001,'And it came to pass after these things, that one told Joseph, Behold, thy father is sick: and he took with him his two sons, Manasseh and Ephraim.');
-INSERT INTO t1(docid,words) VALUES(1048002,'And one told Jacob, and said, Behold, thy son Joseph cometh unto thee: and Israel strengthened himself, and sat upon the bed.');
-INSERT INTO t1(docid,words) VALUES(1048003,'And Jacob said unto Joseph, God Almighty appeared unto me at Luz in the land of Canaan, and blessed me,');
-INSERT INTO t1(docid,words) VALUES(1048004,'And said unto me, Behold, I will make thee fruitful, and multiply thee, and I will make of thee a multitude of people; and will give this land to thy seed after thee for an everlasting possession.');
-INSERT INTO t1(docid,words) VALUES(1048005,'And now thy two sons, Ephraim and Manasseh, which were born unto thee in the land of Egypt before I came unto thee into Egypt, are mine; as Reuben and Simeon, they shall be mine.');
-INSERT INTO t1(docid,words) VALUES(1048006,'And thy issue, which thou begettest after them, shall be thine, and shall be called after the name of their brethren in their inheritance.');
-INSERT INTO t1(docid,words) VALUES(1048007,'And as for me, when I came from Padan, Rachel died by me in the land of Canaan in the way, when yet there was but a little way to come unto Ephrath: and I buried her there in the way of Ephrath; the same is Bethlehem.');
-INSERT INTO t1(docid,words) VALUES(1048008,'And Israel beheld Joseph''s sons, and said, Who are these?');
-INSERT INTO t1(docid,words) VALUES(1048009,'And Joseph said unto his father, They are my sons, whom God hath given me in this place. And he said, Bring them, I pray thee, unto me, and I will bless them.');
-INSERT INTO t1(docid,words) VALUES(1048010,'Now the eyes of Israel were dim for age, so that he could not see. And he brought them near unto him; and he kissed them, and embraced them.');
-INSERT INTO t1(docid,words) VALUES(1048011,'And Israel said unto Joseph, I had not thought to see thy face: and, lo, God hath shewed me also thy seed.');
-INSERT INTO t1(docid,words) VALUES(1048012,'And Joseph brought them out from between his knees, and he bowed himself with his face to the earth.');
-INSERT INTO t1(docid,words) VALUES(1048013,'And Joseph took them both, Ephraim in his right hand toward Israel''s left hand, and Manasseh in his left hand toward Israel''s right hand, and brought them near unto him.');
-INSERT INTO t1(docid,words) VALUES(1048014,'And Israel stretched out his right hand, and laid it upon Ephraim''s head, who was the younger, and his left hand upon Manasseh''s head, guiding his hands wittingly; for Manasseh was the firstborn.');
-INSERT INTO t1(docid,words) VALUES(1048015,'And he blessed Joseph, and said, God, before whom my fathers Abraham and Isaac did walk, the God which fed me all my life long unto this day,');
-INSERT INTO t1(docid,words) VALUES(1048016,'The Angel which redeemed me from all evil, bless the lads; and let my name be named on them, and the name of my fathers Abraham and Isaac; and let them grow into a multitude in the midst of the earth.');
-INSERT INTO t1(docid,words) VALUES(1048017,'And when Joseph saw that his father laid his right hand upon the head of Ephraim, it displeased him: and he held up his father''s hand, to remove it from Ephraim''s head unto Manasseh''s head.');
-INSERT INTO t1(docid,words) VALUES(1048018,'And Joseph said unto his father, Not so, my father: for this is the firstborn; put thy right hand upon his head.');
-INSERT INTO t1(docid,words) VALUES(1048019,'And his father refused, and said, I know it, my son, I know it: he also shall become a people, and he also shall be great: but truly his younger brother shall be greater than he, and his seed shall become a multitude of nations.');
-INSERT INTO t1(docid,words) VALUES(1048020,'And he blessed them that day, saying, In thee shall Israel bless, saying, God make thee as Ephraim and as Manasseh: and he set Ephraim before Manasseh.');
-INSERT INTO t1(docid,words) VALUES(1048021,'And Israel said unto Joseph, Behold, I die: but God shall be with you, and bring you again unto the land of your fathers.');
-INSERT INTO t1(docid,words) VALUES(1048022,'Moreover I have given to thee one portion above thy brethren, which I took out of the hand of the Amorite with my sword and with my bow.');
-INSERT INTO t1(docid,words) VALUES(1049001,'And Jacob called unto his sons, and said, Gather yourselves together, that I may tell you that which shall befall you in the last days.');
-INSERT INTO t1(docid,words) VALUES(1049002,'Gather yourselves together, and hear, ye sons of Jacob; and hearken unto Israel your father.');
-INSERT INTO t1(docid,words) VALUES(1049003,'Reuben, thou art my firstborn, my might, and the beginning of my strength, the excellency of dignity, and the excellency of power:');
-INSERT INTO t1(docid,words) VALUES(1049004,'Unstable as water, thou shalt not excel; because thou wentest up to thy father''s bed; then defiledst thou it: he went up to my couch.');
-INSERT INTO t1(docid,words) VALUES(1049005,'Simeon and Levi are brethren; instruments of cruelty are in their habitations.');
-INSERT INTO t1(docid,words) VALUES(1049006,'O my soul, come not thou into their secret; unto their assembly, mine honour, be not thou united: for in their anger they slew a man, and in their selfwill they digged down a wall.');
-INSERT INTO t1(docid,words) VALUES(1049007,'Cursed be their anger, for it was fierce; and their wrath, for it was cruel: I will divide them in Jacob, and scatter them in Israel.');
-INSERT INTO t1(docid,words) VALUES(1049008,'Judah, thou art he whom thy brethren shall praise: thy hand shall be in the neck of thine enemies; thy father''s children shall bow down before thee.');
-INSERT INTO t1(docid,words) VALUES(1049009,'Judah is a lion''s whelp: from the prey, my son, thou art gone up: he stooped down, he couched as a lion, and as an old lion; who shall rouse him up?');
-INSERT INTO t1(docid,words) VALUES(1049010,'The sceptre shall not depart from Judah, nor a lawgiver from between his feet, until Shiloh come; and unto him shall the gathering of the people be.');
-INSERT INTO t1(docid,words) VALUES(1049011,'Binding his foal unto the vine, and his ass''s colt unto the choice vine; he washed his garments in wine, and his clothes in the blood of grapes:');
-INSERT INTO t1(docid,words) VALUES(1049012,'His eyes shall be red with wine, and his teeth white with milk.');
-INSERT INTO t1(docid,words) VALUES(1049013,'Zebulun shall dwell at the haven of the sea; and he shall be for an haven of ships; and his border shall be unto Zidon.');
-INSERT INTO t1(docid,words) VALUES(1049014,'Issachar is a strong ass couching down between two burdens:');
-INSERT INTO t1(docid,words) VALUES(1049015,'And he saw that rest was good, and the land that it was pleasant; and bowed his shoulder to bear, and became a servant unto tribute.');
-INSERT INTO t1(docid,words) VALUES(1049016,'Dan shall judge his people, as one of the tribes of Israel.');
-INSERT INTO t1(docid,words) VALUES(1049017,'Dan shall be a serpent by the way, an adder in the path, that biteth the horse heels, so that his rider shall fall backward.');
-INSERT INTO t1(docid,words) VALUES(1049018,'I have waited for thy salvation, O LORD.');
-INSERT INTO t1(docid,words) VALUES(1049019,'Gad, a troop shall overcome him: but he shall overcome at the last.');
-INSERT INTO t1(docid,words) VALUES(1049020,'Out of Asher his bread shall be fat, and he shall yield royal dainties.');
-INSERT INTO t1(docid,words) VALUES(1049021,'Naphtali is a hind let loose: he giveth goodly words.');
-INSERT INTO t1(docid,words) VALUES(1049022,'Joseph is a fruitful bough, even a fruitful bough by a well; whose branches run over the wall:');
-INSERT INTO t1(docid,words) VALUES(1049023,'The archers have sorely grieved him, and shot at him, and hated him:');
-INSERT INTO t1(docid,words) VALUES(1049024,'But his bow abode in strength, and the arms of his hands were made strong by the hands of the mighty God of Jacob; (from thence is the shepherd, the stone of Israel:)');
-INSERT INTO t1(docid,words) VALUES(1049025,'Even by the God of thy father, who shall help thee; and by the Almighty, who shall bless thee with blessings of heaven above, blessings of the deep that lieth under, blessings of the breasts, and of the womb:');
-INSERT INTO t1(docid,words) VALUES(1049026,'The blessings of thy father have prevailed above the blessings of my progenitors unto the utmost bound of the everlasting hills: they shall be on the head of Joseph, and on the crown of the head of him that was separate from his brethren.');
-INSERT INTO t1(docid,words) VALUES(1049027,'Benjamin shall ravin as a wolf: in the morning he shall devour the prey, and at night he shall divide the spoil.');
-INSERT INTO t1(docid,words) VALUES(1049028,'All these are the twelve tribes of Israel: and this is it that their father spake unto them, and blessed them; every one according to his blessing he blessed them.');
-INSERT INTO t1(docid,words) VALUES(1049029,'And he charged them, and said unto them, I am to be gathered unto my people: bury me with my fathers in the cave that is in the field of Ephron the Hittite,');
-INSERT INTO t1(docid,words) VALUES(1049030,'In the cave that is in the field of Machpelah, which is before Mamre, in the land of Canaan, which Abraham bought with the field of Ephron the Hittite for a possession of a buryingplace.');
-INSERT INTO t1(docid,words) VALUES(1049031,'There they buried Abraham and Sarah his wife; there they buried Isaac and Rebekah his wife; and there I buried Leah.');
-INSERT INTO t1(docid,words) VALUES(1049032,'The purchase of the field and of the cave that is therein was from the children of Heth.');
-INSERT INTO t1(docid,words) VALUES(1049033,'And when Jacob had made an end of commanding his sons, he gathered up his feet into the bed, and yielded up the ghost, and was gathered unto his people.');
-INSERT INTO t1(docid,words) VALUES(1050001,'And Joseph fell upon his father''s face, and wept upon him, and kissed him.');
-INSERT INTO t1(docid,words) VALUES(1050002,'And Joseph commanded his servants the physicians to embalm his father: and the physicians embalmed Israel.');
-INSERT INTO t1(docid,words) VALUES(1050003,'And forty days were fulfilled for him; for so are fulfilled the days of those which are embalmed: and the Egyptians mourned for him threescore and ten days.');
-INSERT INTO t1(docid,words) VALUES(1050004,'And when the days of his mourning were past, Joseph spake unto the house of Pharaoh, saying, If now I have found grace in your eyes, speak, I pray you, in the ears of Pharaoh, saying,');
-INSERT INTO t1(docid,words) VALUES(1050005,'My father made me swear, saying, Lo, I die: in my grave which I have digged for me in the land of Canaan, there shalt thou bury me. Now therefore let me go up, I pray thee, and bury my father, and I will come again.');
-INSERT INTO t1(docid,words) VALUES(1050006,'And Pharaoh said, Go up, and bury thy father, according as he made thee swear.');
-INSERT INTO t1(docid,words) VALUES(1050007,'And Joseph went up to bury his father: and with him went up all the servants of Pharaoh, the elders of his house, and all the elders of the land of Egypt,');
-INSERT INTO t1(docid,words) VALUES(1050008,'And all the house of Joseph, and his brethren, and his father''s house: only their little ones, and their flocks, and their herds, they left in the land of Goshen.');
-INSERT INTO t1(docid,words) VALUES(1050009,'And there went up with him both chariots and horsemen: and it was a very great company.');
-INSERT INTO t1(docid,words) VALUES(1050010,'And they came to the threshingfloor of Atad, which is beyond Jordan, and there they mourned with a great and very sore lamentation: and he made a mourning for his father seven days.');
-INSERT INTO t1(docid,words) VALUES(1050011,'And when the inhabitants of the land, the Canaanites, saw the mourning in the floor of Atad, they said, This is a grievous mourning to the Egyptians: wherefore the name of it was called Abelmizraim, which is beyond Jordan.');
-INSERT INTO t1(docid,words) VALUES(1050012,'And his sons did unto him according as he commanded them:');
-INSERT INTO t1(docid,words) VALUES(1050013,'For his sons carried him into the land of Canaan, and buried him in the cave of the field of Machpelah, which Abraham bought with the field for a possession of a buryingplace of Ephron the Hittite, before Mamre.');
-INSERT INTO t1(docid,words) VALUES(1050014,'And Joseph returned into Egypt, he, and his brethren, and all that went up with him to bury his father, after he had buried his father.');
-INSERT INTO t1(docid,words) VALUES(1050015,'And when Joseph''s brethren saw that their father was dead, they said, Joseph will peradventure hate us, and will certainly requite us all the evil which we did unto him.');
-INSERT INTO t1(docid,words) VALUES(1050016,'And they sent a messenger unto Joseph, saying, Thy father did command before he died, saying,');
-INSERT INTO t1(docid,words) VALUES(1050017,'So shall ye say unto Joseph, Forgive, I pray thee now, the trespass of thy brethren, and their sin; for they did unto thee evil: and now, we pray thee, forgive the trespass of the servants of the God of thy father. And Joseph wept when they spake unto him.');
-INSERT INTO t1(docid,words) VALUES(1050018,'And his brethren also went and fell down before his face; and they said, Behold, we be thy servants.');
-INSERT INTO t1(docid,words) VALUES(1050019,'And Joseph said unto them, Fear not: for am I in the place of God?');
-INSERT INTO t1(docid,words) VALUES(1050020,'But as for you, ye thought evil against me; but God meant it unto good, to bring to pass, as it is this day, to save much people alive.');
-INSERT INTO t1(docid,words) VALUES(1050021,'Now therefore fear ye not: I will nourish you, and your little ones. And he comforted them, and spake kindly unto them.');
-INSERT INTO t1(docid,words) VALUES(1050022,'And Joseph dwelt in Egypt, he, and his father''s house: and Joseph lived an hundred and ten years.');
-INSERT INTO t1(docid,words) VALUES(1050023,'And Joseph saw Ephraim''s children of the third generation: the children also of Machir the son of Manasseh were brought up upon Joseph''s knees.');
-INSERT INTO t1(docid,words) VALUES(1050024,'And Joseph said unto his brethren, I die: and God will surely visit you, and bring you out of this land unto the land which he sware to Abraham, to Isaac, and to Jacob.');
-INSERT INTO t1(docid,words) VALUES(1050025,'And Joseph took an oath of the children of Israel, saying, God will surely visit you, and ye shall carry up my bones from hence.');
-INSERT INTO t1(docid,words) VALUES(1050026,'So Joseph died, being an hundred and ten years old: and they embalmed him, and he was put in a coffin in Egypt.');
-COMMIT;
-}
-}
+source $testdir/genesis.tcl
# The following is a list of queries to perform against the above
# FTS3/FTS4 database. We will be trying these queries in various
@@ -1589,7 +51,7 @@ do_test fts4aa-1.0 {
db eval {
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
}
- fts4aa_fill_table
+ fts_kjv_genesis
foreach q $::fts4aa_queries {
set r [db eval {SELECT docid FROM t1 WHERE words MATCH $q ORDER BY docid}]
set ::fts4aa_res($q) $r
@@ -1673,7 +135,7 @@ do_test fts4aa-2.0 {
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING fts3(words, tokenize porter);
}
- fts4aa_fill_table
+ fts_kjv_genesis
} {}
unset -nocomplain ii
set ii 0
@@ -1694,7 +156,7 @@ do_test fts4aa-3.0 {
PRAGMA page_size=65536;
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
}
- fts4aa_fill_table
+ fts_kjv_genesis
} {}
unset -nocomplain ii
set ii 0
@@ -1718,7 +180,7 @@ do_test fts4aa-4.0 {
DROP TABLE t1;
CREATE VIRTUAL TABLE t1 USING fts4(words, tokenize porter);
}
- fts4aa_fill_table
+ fts_kjv_genesis
} {}
unset -nocomplain ii
set ii 0
diff --git a/test/fts4check.test b/test/fts4check.test
index cc1d018..c98886c 100644
--- a/test/fts4check.test
+++ b/test/fts4check.test
@@ -152,4 +152,32 @@ foreach {tn disruption} {
do_execsql_test 3.2.3.$tn "ROLLBACK"
}
+#--------------------------------------------------------------------------
+# Test case 4.*
+#
+# Test that the integrity-check works if there are "notindexed" columns.
+#
+do_execsql_test 4.0 {
+ CREATE VIRTUAL TABLE t4 USING fts4(a, b, c, notindexed=b);
+ INSERT INTO t4 VALUES('text one', 'text two', 'text three');
+ INSERT INTO t4(t4) VALUES('integrity-check');
+}
+
+do_execsql_test 4.1 {
+ PRAGMA writable_schema = 1;
+ UPDATE sqlite_master
+ SET sql = 'CREATE VIRTUAL TABLE t4 USING fts4(a, b, c)'
+ WHERE name = 't4';
+}
+
+do_test 4.2 {
+ db close
+ sqlite3 db test.db
+ catchsql {
+ INSERT INTO t4(t4) VALUES('integrity-check');
+ }
+} {1 {database disk image is malformed}}
+reset_db
+
finish_test
+
diff --git a/test/fts4content.test b/test/fts4content.test
index 302f14e..6b2cd3c 100644
--- a/test/fts4content.test
+++ b/test/fts4content.test
@@ -623,4 +623,3 @@ do_execsql_test 10.7 {
}
finish_test
-
diff --git a/test/fts4docid.test b/test/fts4docid.test
new file mode 100644
index 0000000..2328b6e
--- /dev/null
+++ b/test/fts4docid.test
@@ -0,0 +1,116 @@
+# 2012 March 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.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/fts3_common.tcl
+set ::testprefix fts4docid
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Initialize a table with pseudo-randomly generated data.
+#
+do_execsql_test 1.0 { CREATE VIRTUAL TABLE t1 USING fts4; }
+do_test 1.1 {
+ foreach {docid content} {
+ 0 {F N K B T I K V B A} 1 {D M J E S P H E L O}
+ 2 {W U T Q T Q T L H G} 3 {D W H M B R S Z B K}
+ 4 {F Q I N P Q J L Z D} 5 {J O Q E Y A O E L B}
+ 6 {O V R A C R K C Y H} 7 {Z J H T Q Q O R A G}
+ 8 {L K J W G D Y W B M} 9 {K E Y I A Q R Q T S}
+ 10 {N P H Y Z M R T I C} 11 {E X H O I S E S Z F}
+ 12 {B Y Q T J X C L L J} 13 {Q D C U U A Q E Z U}
+ 14 {S I T C J R X S J M} 15 {M X M K E X L H Q Y}
+ 16 {O W E I C H U Y S Y} 17 {P V V E M T H C C S}
+ 18 {L Y A M I E N M X O} 19 {S Y R U L S Q Y F P}
+ 20 {U J S T T J J S V X} 21 {T E I W P O V A A P}
+ 22 {W D K H D H F G O J} 23 {T X Y P G M J U I L}
+ 24 {F V X E B C N B K W} 25 {E B A Y N N T Z I C}
+ 26 {G E E B C P U D H G} 27 {J D J K N S B Q T M}
+ 28 {Q T G M D O D Y V G} 29 {P X W I W V P W Z G}
+ } {
+ execsql { INSERT INTO t1(docid, content) VALUES($docid, $content) }
+ }
+} {}
+
+# Quick test regarding affinites and the docid/rowid column.
+do_execsql_test 2.1.1 { SELECT docid FROM t1 WHERE docid = 5 } {5}
+do_execsql_test 2.1.2 { SELECT docid FROM t1 WHERE docid = '5' } {5}
+do_execsql_test 2.1.3 { SELECT docid FROM t1 WHERE docid = +5 } {5}
+do_execsql_test 2.1.4 { SELECT docid FROM t1 WHERE docid = +'5' } {5}
+do_execsql_test 2.1.5 { SELECT docid FROM t1 WHERE docid < 5 } {0 1 2 3 4}
+do_execsql_test 2.1.6 { SELECT docid FROM t1 WHERE docid < '5' } {0 1 2 3 4}
+
+do_execsql_test 2.2.1 { SELECT rowid FROM t1 WHERE rowid = 5 } {5}
+do_execsql_test 2.2.2 { SELECT rowid FROM t1 WHERE rowid = '5' } {5}
+do_execsql_test 2.2.3 { SELECT rowid FROM t1 WHERE rowid = +5 } {5}
+do_execsql_test 2.2.4 { SELECT rowid FROM t1 WHERE rowid = +'5' } {5}
+do_execsql_test 2.2.5 { SELECT rowid FROM t1 WHERE rowid < 5 } {0 1 2 3 4}
+do_execsql_test 2.2.6 { SELECT rowid FROM t1 WHERE rowid < '5' } {0 1 2 3 4}
+
+#-------------------------------------------------------------------------
+# Now test a bunch of full-text queries featuring range constraints on
+# the docid field. Each query is run so that the range constraint:
+#
+# * is on the docid field,
+# * is on the docid field with a unary +,
+# * is on the rowid field,
+# * is on the rowid field with a unary +.
+#
+# Queries are run with both "ORDER BY docid DESC" and "ORDER BY docid ASC"
+# clauses.
+#
+foreach {tn where result} {
+ 1 {WHERE t1 MATCH 'O' AND xxx < 17} {1 5 6 7 11 16}
+ 2 {WHERE t1 MATCH 'O' AND xxx < 4123456789123456} {1 5 6 7 11 16 18 21 22 28}
+ 3 {WHERE t1 MATCH 'O' AND xxx < 1} {}
+ 4 {WHERE t1 MATCH 'O' AND xxx < -4123456789123456} {}
+
+ 5 {WHERE t1 MATCH 'O' AND xxx > 17} {18 21 22 28}
+ 6 {WHERE t1 MATCH 'O' AND xxx > 4123456789123456} {}
+ 7 {WHERE t1 MATCH 'O' AND xxx > 1} {5 6 7 11 16 18 21 22 28}
+ 8 {WHERE t1 MATCH 'O' AND xxx > -4123456789123456} {1 5 6 7 11 16 18 21 22 28}
+
+ 9 {WHERE t1 MATCH '"Q T"' AND xxx < 27} {2 9 12}
+ 10 {WHERE t1 MATCH '"Q T"' AND xxx <= 27} {2 9 12 27}
+ 11 {WHERE t1 MATCH '"Q T"' AND xxx > 27} {28}
+ 12 {WHERE t1 MATCH '"Q T"' AND xxx >= 27} {27 28}
+} {
+ foreach {tn2 ref order} {
+ 1 docid "ORDER BY docid ASC"
+ 2 +docid "ORDER BY docid ASC"
+ 3 rowid "ORDER BY docid ASC"
+ 4 +rowid "ORDER BY docid ASC"
+
+ 5 docid "ORDER BY docid DESC"
+ 6 +docid "ORDER BY docid DESC"
+ 7 rowid "ORDER BY docid DESC"
+ 8 +rowid "ORDER BY docid DESC"
+ } {
+ set w [string map "xxx $ref" $where]
+ set q "SELECT docid FROM t1 $w $order"
+
+ if {$tn2<5} {
+ set r [lsort -integer -increasing $result]
+ } else {
+ set r [lsort -integer -decreasing $result]
+ }
+
+ do_execsql_test 3.$tn.$tn2 $q $r
+ }
+}
+
+finish_test
diff --git a/test/fts4growth.test b/test/fts4growth.test
new file mode 100644
index 0000000..aa5f251
--- /dev/null
+++ b/test/fts4growth.test
@@ -0,0 +1,437 @@
+# 2014 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 implements regression tests for SQLite library. The
+# focus of this script is testing the FTS4 module.
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts4growth
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+source $testdir/genesis.tcl
+
+do_execsql_test 1.1 { CREATE VIRTUAL TABLE x1 USING fts3; }
+
+do_test 1.2 {
+ foreach L {
+ {"See here, young man," said Mulga Bill, "from Walgett to the sea,}
+ {From Conroy's Gap to Castlereagh, there's none can ride like me.}
+ {I'm good all round at everything as everybody knows,}
+ {Although I'm not the one to talk -- I hate a man that blows.}
+ } {
+ execsql { INSERT INTO x1 VALUES($L) }
+ }
+ execsql { SELECT end_block, length(root) FROM x1_segdir }
+} {{0 114} 114 {0 118} 118 {0 95} 95 {0 115} 115}
+
+do_execsql_test 1.3 {
+ INSERT INTO x1(x1) VALUES('optimize');
+ SELECT level, end_block, length(root) FROM x1_segdir;
+} {0 {0 394} 394}
+
+do_test 1.4 {
+ foreach L {
+ {But riding is my special gift, my chiefest, sole delight;}
+ {Just ask a wild duck can it swim, a wildcat can it fight.}
+ {There's nothing clothed in hair or hide, or built of flesh or steel,}
+ {There's nothing walks or jumps, or runs, on axle, hoof, or wheel,}
+ {But what I'll sit, while hide will hold and girths and straps are tight:}
+ {I'll ride this here two-wheeled concern right straight away at sight."}
+ } {
+ execsql { INSERT INTO x1 VALUES($L) }
+ }
+ execsql {
+ INSERT INTO x1(x1) VALUES('merge=4,4');
+ SELECT level, end_block, length(root) FROM x1_segdir;
+ }
+} {0 {0 110} 110 0 {0 132} 132 0 {0 129} 129 1 {128 658} 2}
+
+do_execsql_test 1.5 {
+ SELECT length(block) FROM x1_segments;
+} {658 {}}
+
+do_test 1.6 {
+ foreach L {
+ {'Twas Mulga Bill, from Eaglehawk, that sought his own abode,}
+ {That perched above Dead Man's Creek, beside the mountain road.}
+ {He turned the cycle down the hill and mounted for the fray,}
+ {But 'ere he'd gone a dozen yards it bolted clean away.}
+ {It left the track, and through the trees, just like a silver steak,}
+ {It whistled down the awful slope towards the Dead Man's Creek.}
+ {It shaved a stump by half an inch, it dodged a big white-box:}
+ {The very wallaroos in fright went scrambling up the rocks,}
+ {The wombats hiding in their caves dug deeper underground,}
+ {As Mulga Bill, as white as chalk, sat tight to every bound.}
+ {It struck a stone and gave a spring that cleared a fallen tree,}
+ {It raced beside a precipice as close as close could be;}
+ {And then as Mulga Bill let out one last despairing shriek}
+ {It made a leap of twenty feet into the Dead Man's Creek.}
+ } {
+ execsql { INSERT INTO x1 VALUES($L) }
+ }
+ execsql {
+ SELECT level, end_block, length(root) FROM x1_segdir;
+ }
+} {1 {128 658} 2 1 {130 1377} 6 0 {0 117} 117}
+
+do_execsql_test 1.7 {
+ SELECT sum(length(block)) FROM x1_segments WHERE blockid IN (129, 130);
+} {1377}
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 2.1 {
+ CREATE TABLE t1(docid, words);
+ CREATE VIRTUAL TABLE x2 USING fts4;
+}
+fts_kjv_genesis
+do_test 2.2 {
+ foreach id [db eval {SELECT docid FROM t1}] {
+ execsql {
+ INSERT INTO x2(docid, content) SELECT $id, words FROM t1 WHERE docid=$id
+ }
+ }
+ foreach id [db eval {SELECT docid FROM t1}] {
+ execsql {
+ INSERT INTO x2(docid, content) SELECT NULL, words FROM t1 WHERE docid=$id
+ }
+ if {[db one {SELECT count(*) FROM x2_segdir WHERE level<2}]==2} break
+ }
+} {}
+
+do_execsql_test 2.3 {
+ SELECT count(*) FROM x2_segdir WHERE level=2;
+ SELECT count(*) FROM x2_segdir WHERE level=3;
+} {6 0}
+
+do_execsql_test 2.4 {
+ INSERT INTO x2(x2) VALUES('merge=4,4');
+ SELECT count(*) FROM x2_segdir WHERE level=2;
+ SELECT count(*) FROM x2_segdir WHERE level=3;
+} {6 1}
+
+do_execsql_test 2.5 {
+ SELECT end_block FROM x2_segdir WHERE level=3;
+ INSERT INTO x2(x2) VALUES('merge=4,4');
+ SELECT end_block FROM x2_segdir WHERE level=3;
+ INSERT INTO x2(x2) VALUES('merge=4,4');
+ SELECT end_block FROM x2_segdir WHERE level=3;
+} {{3828 -3430} {3828 -10191} {3828 -14109}}
+
+do_execsql_test 2.6 {
+ SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE
+ blockid BETWEEN start_block AND leaves_end_block
+ AND level=3
+} {14109}
+
+do_execsql_test 2.7 {
+ INSERT INTO x2(x2) VALUES('merge=1000,4');
+ SELECT end_block FROM x2_segdir WHERE level=3;
+} {{3828 86120}}
+
+do_execsql_test 2.8 {
+ SELECT sum(length(block)) FROM x2_segdir, x2_segments WHERE
+ blockid BETWEEN start_block AND leaves_end_block
+ AND level=3
+} {86120}
+
+#--------------------------------------------------------------------------
+# Test that delete markers are removed from FTS segments when possible.
+# It is only possible to remove delete markers when the output of the
+# merge operation will become the oldest segment in the index.
+#
+# 3.1 - when the oldest segment is created by an 'optimize'.
+# 3.2 - when the oldest segment is created by an incremental merge.
+# 3.3 - by a crisis merge.
+#
+
+proc insert_doc {args} {
+ foreach iDoc $args {
+ set L [lindex {
+ {In your eagerness to engage the Trojans,}
+ {don’t any of you charge ahead of others,}
+ {trusting in your strength and horsemanship.}
+ {And don’t lag behind. That will hurt our charge.}
+ {Any man whose chariot confronts an enemy’s}
+ {should thrust with his spear at him from there.}
+ {That’s the most effective tactic, the way}
+ {men wiped out city strongholds long ago —}
+ {their chests full of that style and spirit.}
+ } [expr $iDoc%9]]
+ execsql { REPLACE INTO x3(docid, content) VALUES($iDoc, $L) }
+ }
+}
+
+proc delete_doc {args} {
+ foreach iDoc $args {
+ execsql { DELETE FROM x3 WHERE docid = $iDoc }
+ }
+}
+
+proc second {x} { lindex $x 1 }
+db func second second
+
+do_execsql_test 3.0 { CREATE VIRTUAL TABLE x3 USING fts4 }
+
+do_test 3.1.1 {
+ db transaction { insert_doc 1 2 3 4 5 6 }
+ execsql { SELECT level, idx, second(end_block) FROM x3_segdir }
+} {0 0 412}
+do_test 3.1.2 {
+ delete_doc 1 2 3 4 5 6
+ execsql { SELECT count(*) FROM x3_segdir }
+} {0}
+do_test 3.1.3 {
+ db transaction {
+ insert_doc 1 2 3 4 5 6 7 8 9
+ delete_doc 9 8 7
+ }
+ execsql { SELECT level, idx, second(end_block) FROM x3_segdir }
+} {0 0 591 0 1 65 0 2 72 0 3 76}
+do_test 3.1.4 {
+ execsql { INSERT INTO x3(x3) VALUES('optimize') }
+ execsql { SELECT level, idx, second(end_block) FROM x3_segdir }
+} {0 0 412}
+
+do_test 3.2.1 {
+ execsql { DELETE FROM x3 }
+ insert_doc 8 7 6 5 4 3 2 1
+ delete_doc 7 8
+ execsql { SELECT count(*) FROM x3_segdir }
+} {10}
+do_test 3.2.2 {
+ execsql { INSERT INTO x3(x3) VALUES('merge=500,10') }
+ execsql { SELECT level, idx, second(end_block) FROM x3_segdir }
+} {1 0 412}
+
+# This assumes the crisis merge happens when there are already 16
+# segments and one more is added.
+#
+do_test 3.3.1 {
+ execsql { DELETE FROM x3 }
+ insert_doc 1 2 3 4 5 6 7 8 9 10 11
+ delete_doc 11 10 9 8 7
+ execsql { SELECT count(*) FROM x3_segdir }
+} {16}
+
+do_test 3.3.2 {
+ insert_doc 12
+ execsql { SELECT level, idx, second(end_block) FROM x3_segdir WHERE level=1 }
+} {1 0 412}
+
+#--------------------------------------------------------------------------
+# Check a theory on a bug in fts4 - that segments with idx==0 were not
+# being incrementally merged correctly. Theory turned out to be false.
+#
+do_execsql_test 4.1 {
+ DROP TABLE IF EXISTS x4;
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(docid, words);
+ CREATE VIRTUAL TABLE x4 USING fts4(words);
+}
+do_test 4.2 {
+ fts_kjv_genesis
+ execsql { INSERT INTO x4 SELECT words FROM t1 }
+ execsql { INSERT INTO x4 SELECT words FROM t1 }
+} {}
+
+do_execsql_test 4.3 {
+ SELECT level, idx, second(end_block) FROM x4_segdir
+} {0 0 117483 0 1 118006}
+
+do_execsql_test 4.4 {
+ INSERT INTO x4(x4) VALUES('merge=10,2');
+ SELECT count(*) FROM x4_segdir;
+} {3}
+
+do_execsql_test 4.5 {
+ INSERT INTO x4(x4) VALUES('merge=10,2');
+ SELECT count(*) FROM x4_segdir;
+} {3}
+
+do_execsql_test 4.6 {
+ INSERT INTO x4(x4) VALUES('merge=1000,2');
+ SELECT count(*) FROM x4_segdir;
+} {1}
+
+
+
+#--------------------------------------------------------------------------
+# Check that segments are not promoted if the "end_block" field does not
+# contain a size.
+#
+do_execsql_test 5.1 {
+ DROP TABLE IF EXISTS x2;
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(docid, words);
+ CREATE VIRTUAL TABLE x2 USING fts4;
+}
+fts_kjv_genesis
+
+proc first {L} {lindex $L 0}
+db func first first
+
+do_test 5.2 {
+ foreach r [db eval { SELECT rowid FROM t1 }] {
+ execsql {
+ INSERT INTO x2(docid, content) SELECT docid, words FROM t1 WHERE rowid=$r
+ }
+ }
+ foreach d [db eval { SELECT docid FROM t1 LIMIT -1 OFFSET 20 }] {
+ execsql { DELETE FROM x2 WHERE docid = $d }
+ }
+
+ execsql {
+ INSERT INTO x2(x2) VALUES('optimize');
+ SELECT level, idx, end_block FROM x2_segdir
+ }
+} {2 0 {752 1926}}
+
+do_execsql_test 5.3 {
+ UPDATE x2_segdir SET end_block = CAST( first(end_block) AS INTEGER );
+ SELECT end_block, typeof(end_block) FROM x2_segdir;
+} {752 integer}
+
+do_execsql_test 5.4 {
+ INSERT INTO x2 SELECT words FROM t1 LIMIT 50;
+ SELECT level, idx, end_block FROM x2_segdir
+} {2 0 752 0 0 {758 5174}}
+
+do_execsql_test 5.5 {
+ UPDATE x2_segdir SET end_block = end_block || ' 1926' WHERE level=2;
+ INSERT INTO x2 SELECT words FROM t1 LIMIT 40;
+ SELECT level, idx, end_block FROM x2_segdir
+} {0 0 {752 1926} 0 1 {758 5174} 0 2 {763 4170}}
+
+proc t1_to_x2 {} {
+ foreach id [db eval {SELECT docid FROM t1 LIMIT 2}] {
+ execsql {
+ DELETE FROM x2 WHERE docid=$id;
+ INSERT INTO x2(docid, content) SELECT $id, words FROM t1 WHERE docid=$id;
+ }
+ }
+}
+
+#--------------------------------------------------------------------------
+# Check that segments created by auto-merge are not promoted until they
+# are completed.
+#
+
+do_execsql_test 6.1 {
+ CREATE VIRTUAL TABLE x5 USING fts4;
+ INSERT INTO x5 SELECT words FROM t1 LIMIT 100 OFFSET 0;
+ INSERT INTO x5 SELECT words FROM t1 LIMIT 100 OFFSET 25;
+ INSERT INTO x5 SELECT words FROM t1 LIMIT 100 OFFSET 50;
+ INSERT INTO x5 SELECT words FROM t1 LIMIT 100 OFFSET 75;
+ SELECT count(*) FROM x5_segdir
+} {4}
+
+do_execsql_test 6.2 {
+ INSERT INTO x5(x5) VALUES('merge=2,4');
+ SELECT level, idx, end_block FROM x5_segdir;
+} {0 0 {10 9216} 0 1 {21 9330} 0 2 {31 8850} 0 3 {40 8689} 1 0 {1320 -3117}}
+
+do_execsql_test 6.3 {
+ INSERT INTO x5 SELECT words FROM t1 LIMIT 100 OFFSET 100;
+ SELECT level, idx, end_block FROM x5_segdir;
+} {
+ 0 0 {10 9216} 0 1 {21 9330} 0 2 {31 8850}
+ 0 3 {40 8689} 1 0 {1320 -3117} 0 4 {1329 8297}
+}
+
+do_execsql_test 6.4 {
+ INSERT INTO x5(x5) VALUES('merge=200,4');
+ SELECT level, idx, end_block FROM x5_segdir;
+} {0 0 {1329 8297} 1 0 {1320 28009}}
+
+do_execsql_test 6.5 {
+ INSERT INTO x5 SELECT words FROM t1;
+ SELECT level, idx, end_block FROM x5_segdir;
+} {
+ 0 1 {1329 8297} 0 0 {1320 28009} 0 2 {1449 118006}
+}
+
+#--------------------------------------------------------------------------
+# Ensure that if part of an incremental merge is performed by an old
+# version that does not support storing segment sizes in the end_block
+# field, no size is stored in the final segment (as it would be incorrect).
+#
+do_execsql_test 7.1 {
+ CREATE VIRTUAL TABLE x6 USING fts4;
+ INSERT INTO x6 SELECT words FROM t1;
+ INSERT INTO x6 SELECT words FROM t1;
+ INSERT INTO x6 SELECT words FROM t1;
+ INSERT INTO x6 SELECT words FROM t1;
+ INSERT INTO x6 SELECT words FROM t1;
+ INSERT INTO x6 SELECT words FROM t1;
+ SELECT level, idx, end_block FROM x6_segdir;
+} {
+ 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006}
+ 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
+}
+
+do_execsql_test 7.2 {
+ INSERT INTO x6(x6) VALUES('merge=25,4');
+ SELECT level, idx, end_block FROM x6_segdir;
+} {
+ 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006}
+ 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
+ 1 0 {16014 -51226}
+}
+
+do_execsql_test 7.3 {
+ UPDATE x6_segdir SET end_block = first(end_block) WHERE level=1;
+ SELECT level, idx, end_block FROM x6_segdir;
+} {
+ 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006}
+ 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
+ 1 0 16014
+}
+
+do_execsql_test 7.4 {
+ INSERT INTO x6(x6) VALUES('merge=25,4');
+ SELECT level, idx, end_block FROM x6_segdir;
+} {
+ 0 0 {118 117483} 0 1 {238 118006} 0 2 {358 118006}
+ 0 3 {478 118006} 0 4 {598 118006} 0 5 {718 118006}
+ 1 0 16014
+}
+
+do_execsql_test 7.5 {
+ INSERT INTO x6(x6) VALUES('merge=2500,4');
+ SELECT level, idx, end_block FROM x6_segdir;
+} {
+ 0 0 {598 118006} 0 1 {718 118006} 1 0 16014
+}
+
+do_execsql_test 7.6 {
+ INSERT INTO x6(x6) VALUES('merge=2500,2');
+ SELECT level, idx, start_block, leaves_end_block, end_block FROM x6_segdir;
+} {
+ 2 0 23695 24147 {41262 633507}
+}
+
+do_execsql_test 7.7 {
+ SELECT sum(length(block)) FROM x6_segments
+ WHERE blockid BETWEEN 23695 AND 24147
+} {633507}
+
+
+
+finish_test
+
diff --git a/test/fts4growth2.test b/test/fts4growth2.test
new file mode 100644
index 0000000..af41d51
--- /dev/null
+++ b/test/fts4growth2.test
@@ -0,0 +1,93 @@
+# 2014 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 implements regression tests for SQLite library. The
+# focus of this script is testing the FTS4 module.
+#
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix fts4growth2
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+source $testdir/genesis.tcl
+
+do_execsql_test 1.0 { CREATE TABLE t1(docid, words); }
+fts_kjv_genesis
+
+proc structure {} {
+ puts [ db eval {SELECT level, count(*) FROM x1_segdir GROUP BY level} ]
+}
+
+proc tt {val} {
+ execsql {
+ DELETE FROM x1
+ WHERE docid IN (SELECT docid FROM t1 WHERE (rowid-1)%4==$val+0);
+ }
+ execsql {
+ INSERT INTO x1(docid, content)
+ SELECT docid, words FROM t1 WHERE (rowid%4)==$val+0;
+ }
+}
+
+do_execsql_test 1.1 {
+ CREATE VIRTUAL TABLE x1 USING fts4;
+ INSERT INTO x1(x1) VALUES('automerge=2');
+}
+
+do_test 1.2 {
+ for {set i 0} {$i < 40} {incr i} {
+ tt 0 ; tt 1 ; tt 2 ; tt 3
+ }
+ execsql {
+ SELECT max(level) FROM x1_segdir;
+ SELECT count(*) FROM x1_segdir WHERE level=2;
+ }
+} {2 1}
+
+do_test 1.3 {
+ for {set i 0} {$i < 40} {incr i} {
+ tt 0 ; tt 1 ; tt 2 ; tt 3
+ }
+ execsql {
+ SELECT max(level) FROM x1_segdir;
+ SELECT count(*) FROM x1_segdir WHERE level=2;
+ }
+} {2 1}
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 2.1 {
+ DELETE FROM t1 WHERE rowid>16;
+ DROP TABLE IF EXISTS x1;
+ CREATE VIRTUAL TABLE x1 USING fts4;
+}
+
+db func second second
+proc second {L} {lindex $L 1}
+
+for {set tn 0} {$tn < 40} {incr tn} {
+ do_test 2.2.$tn {
+ for {set i 0} {$i < 100} {incr i} {
+ tt 0 ; tt 1 ; tt 2 ; tt 3
+ }
+ execsql { SELECT max(level) FROM x1_segdir }
+ } {1}
+}
+
+
+finish_test
+
diff --git a/test/fts4incr.test b/test/fts4incr.test
new file mode 100644
index 0000000..17212ef
--- /dev/null
+++ b/test/fts4incr.test
@@ -0,0 +1,75 @@
+# 2012 March 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.
+#
+#*************************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/fts3_common.tcl
+set ::testprefix fts4incr
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+# Create the fts_kjv_genesis procedure which fills and FTS3/4 table
+# with the complete text of the Book of Genesis.
+#
+source $testdir/genesis.tcl
+
+do_test 1.0 {
+ execsql { CREATE VIRTUAL TABLE t1 USING fts4(words) }
+ fts_kjv_genesis
+} {}
+
+do_execsql_test 1.1 {
+ SELECT min(docid), max(docid) FROM t1;
+} {1001001 1050026}
+
+foreach {tn q res} {
+ 1 { SELECT count(*) FROM t1 WHERE t1 MATCH 'and' AND docid < 1010000} 224
+ 2 { SELECT count(*) FROM t1 WHERE t1 MATCH '"in the"' AND docid < 1010000} 47
+ 3 { SELECT count(*) FROM t1 WHERE t1 MATCH '"And God"' AND docid < 1010000} 33
+ 4 { SELECT count(*) FROM t1 WHERE t1
+ MATCH '"land of canaan"' AND docid < 1030000 } 7
+} {
+ foreach s {0 1} {
+ execsql "INSERT INTO t1(t1) VALUES('test-no-incr-doclist=$s')"
+ do_execsql_test 2.$tn.$s $q $res
+ set t($s) [lindex [time [list execsql $q] 100] 0]
+ }
+ puts "with optimization: $t(0) without: $t(1)"
+}
+
+do_test 2.1 {
+ execsql {
+ CREATE VIRTUAL TABLE t2 USING fts4(order=DESC);
+ }
+ set num [list one two three four five six seven eight nine ten]
+ execsql BEGIN
+ for {set i 0} {$i < 10000} {incr i} {
+ set x "[lindex $num [expr $i%10]] zero"
+ execsql { INSERT INTO t2(docid, content) VALUES($i, $x) }
+ }
+ execsql COMMIT
+ execsql { INSERT INTO t2(t2) VALUES('optimize') }
+} {}
+
+do_execsql_test 2.2 {
+ SELECT count(*) FROM t2 WHERE t2 MATCH '"never zero"'
+} {0}
+
+do_execsql_test 2.3 {
+ SELECT count(*) FROM t2 WHERE t2 MATCH '"two zero"'
+} {1000}
+
+finish_test
diff --git a/test/fts4merge4.test b/test/fts4merge4.test
new file mode 100644
index 0000000..038e460
--- /dev/null
+++ b/test/fts4merge4.test
@@ -0,0 +1,102 @@
+# 2013 May 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.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/fts3_common.tcl
+set ::testprefix fts4merge4
+
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
+
+do_execsql_test 1.1 { CREATE VIRTUAL TABLE t1 USING fts4 }
+
+do_test 1.2 {
+ for {set i 0} {$i < 2000} {incr i} {
+ execsql {INSERT INTO t1 VALUES('a b c d e f g h i j k l');}
+ }
+} {}
+
+do_test 1.3 {
+ execsql BEGIN
+ for {set i 0} {$i < 2000} {incr i} {
+ execsql {INSERT INTO t1 VALUES('a b c d e f g h i j k l');}
+ }
+ execsql {
+ INSERT INTO t1(t1) VALUES('merge=8,50');
+ COMMIT
+ }
+} {}
+
+reset_db
+do_execsql_test 2.0 { CREATE VIRTUAL TABLE t1 USING fts4 }
+do_test 2.1 {
+ for {set i 0} {$i < 2000} {incr i} {
+ execsql {INSERT INTO t1 VALUES('a b c d e f g h i j k l');}
+ }
+} {}
+do_execsql_test 2.2 { SELECT count(*) FROM t1_segdir; } 35
+do_execsql_test 2.3 { INSERT INTO t1(t1) VALUES('optimize') } {}
+do_execsql_test 2.4 { SELECT count(*) FROM t1_segdir; } 1
+
+#-------------------------------------------------------------------------
+# Now test that the automerge=? option appears to work.
+#
+do_execsql_test 2.1 { CREATE VIRTUAL TABLE t2 USING fts4; }
+
+set doc ""
+foreach c1 "a b c d e f g h i j" {
+ foreach c2 "a b c d e f g h i j" {
+ foreach c3 "a b c d e f g h i j" {
+ lappend doc "$c1$c2$c3"
+ }
+ }
+}
+set doc [string repeat $doc 10]
+
+foreach {tn am expected} {
+ 1 {automerge=2} {1 1 2 1 4 1 6 1}
+ 2 {automerge=4} {1 2 2 1 3 1}
+ 3 {automerge=8} {0 4 1 3 2 1}
+ 4 {automerge=1} {0 4 1 3 2 1}
+} {
+ foreach {tn2 openclose} {1 {} 2 { db close ; sqlite3 db test.db }} {
+ do_test 2.2.$tn.$tn2 {
+ execsql { DELETE FROM t2 }
+ execsql { INSERT INTO t2(t2) VALUES($am) };
+
+ eval $openclose
+
+ for {set i 0} {$i < 100} {incr i} {
+ execsql {
+ BEGIN;
+ INSERT INTO t2 VALUES($doc);
+ INSERT INTO t2 VALUES($doc);
+ INSERT INTO t2 VALUES($doc);
+ INSERT INTO t2 VALUES($doc);
+ INSERT INTO t2 VALUES($doc);
+ COMMIT;
+ }
+ }
+
+ execsql { SELECT level, count(*) FROM t2_segdir GROUP BY level }
+ } [list {*}$expected]
+ }
+}
+
+sqlite3_enable_shared_cache $::enable_shared_cache
+finish_test
diff --git a/test/fts4noti.test b/test/fts4noti.test
new file mode 100644
index 0000000..c90999b
--- /dev/null
+++ b/test/fts4noti.test
@@ -0,0 +1,233 @@
+# 2013 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 notindexed=xxx FTS4 option.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix fts4noti
+
+# If SQLITE_ENABLE_FTS3 is defined, omit this file.
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+
+#-------------------------------------------------------------------------
+# Test that typos in "notindexed=" column names are detected.
+#
+do_execsql_test 1.0 {
+ CREATE TABLE cc(a, b, c);
+}
+foreach {tn arg res} {
+ 1 "(b, c, notindexed=a)" {1 {no such column: a}}
+ 2 "(a, b, notindexed=a)" {0 {}}
+ 3 "(a, b, notindexed=a, notindexed=a)" {0 {}}
+ 4 "(notindexed=a, a, b)" {0 {}}
+ 5 "(notindexed=a, notindexed=b, notindexed=c, a, b, c, d)" {0 {}}
+ 6 "(notindexed=a, notindexed=B, notindexed=c, a, b, c, d)" {0 {}}
+ 7 "(notindexed=a, notindexed=b, notindexed=c, a, B, c, d)" {0 {}}
+ 8 "(notindexed=d, content=cc)" {1 {no such column: d}}
+ 9 "(notindexed=a, content=cc)" {0 {}}
+ 10 "(notindexed=a, notindexed=b, a)" {1 {no such column: b}}
+ 11 "(notindexed=a, notindexed=b, b)" {1 {no such column: a}}
+} {
+ do_catchsql_test 1.$tn "CREATE VIRTUAL TABLE t1 USING fts4 $arg" $res
+ if {[lindex $res 0]==0} { execsql "DROP TABLE t1" }
+}
+
+do_execsql_test 1.x { SELECT name FROM sqlite_master } {cc}
+
+
+#-------------------------------------------------------------------------
+# Test that notindexed columns are not indexed.
+#
+foreach {tn schema} {
+ 1 {
+ CREATE VIRTUAL TABLE t1 USING fts4(a, b, c, notindexed=b);
+ }
+ 2 {
+ CREATE TABLE c1(a, b, c);
+ INSERT INTO c1 VALUES('one two', 'three four', 'five six');
+ INSERT INTO c1 VALUES('three four', 'five six', 'one two');
+ CREATE VIRTUAL TABLE t1 USING fts4(content=c1, notindexed=b);
+ }
+ 3 {
+ CREATE VIRTUAL TABLE t1 USING fts4(content="", a, b, c, notindexed=b);
+ }
+} {
+ execsql $schema
+
+ do_execsql_test 2.$tn.1 {
+ INSERT INTO t1(docid,a,b,c) VALUES(1, 'one two', 'three four', 'five six');
+ INSERT INTO t1(docid,a,b,c) VALUES(2, 'three four', 'five six', 'one two');
+ }
+
+ do_execsql_test 2.$tn.2 { SELECT docid FROM t1 WHERE t1 MATCH 'one' } {1 2}
+ do_execsql_test 2.$tn.3 { SELECT docid FROM t1 WHERE t1 MATCH 'three' } {2}
+ do_execsql_test 2.$tn.4 { SELECT docid FROM t1 WHERE t1 MATCH 'five' } {1}
+
+ do_execsql_test 2.$tn.5 { INSERT INTO t1(t1) VALUES('optimize') }
+
+ do_execsql_test 2.$tn.6 { SELECT docid FROM t1 WHERE t1 MATCH 'one' } {1 2}
+ do_execsql_test 2.$tn.7 { SELECT docid FROM t1 WHERE t1 MATCH 'three' } {2}
+ do_execsql_test 2.$tn.8 { SELECT docid FROM t1 WHERE t1 MATCH 'five' } {1}
+
+ if {$tn!=3} {
+ do_execsql_test 2.$tn.9 { INSERT INTO t1(t1) VALUES('rebuild') }
+
+ do_execsql_test 2.$tn.10 { SELECT docid FROM t1 WHERE t1 MATCH 'one' } {1 2}
+ do_execsql_test 2.$tn.11 { SELECT docid FROM t1 WHERE t1 MATCH 'three' } {2}
+ do_execsql_test 2.$tn.12 { SELECT docid FROM t1 WHERE t1 MATCH 'five' } {1}
+
+ do_execsql_test 2.$tn.13 {
+ SELECT a,b,c FROM t1 WHERE docid=1
+ } {{one two} {three four} {five six}}
+ do_execsql_test 2.$tn.14 {
+ SELECT a,b,c FROM t1 WHERE docid=2
+ } {{three four} {five six} {one two}}
+ }
+
+ do_execsql_test 2.x { DROP TABLE t1 }
+}
+
+#-------------------------------------------------------------------------
+# Test that notindexed columns are not scanned for deferred tokens.
+#
+
+do_execsql_test 3.1 {
+ CREATE VIRTUAL TABLE t2 USING fts4(x, y, notindexed=x);
+}
+do_test 3.2 {
+ set v [string repeat " 1" 50000]
+ set v1 "x $v"
+ set v2 "y $v"
+ execsql {
+ INSERT INTO t2 VALUES(1, 'x y z');
+ INSERT INTO t2 VALUES(2, $v1);
+ INSERT INTO t2 VALUES(3, $v2);
+ INSERT INTO t2 VALUES(4, $v2);
+ INSERT INTO t2 VALUES(5, $v2);
+ INSERT INTO t2 VALUES(6, $v2);
+ }
+} {}
+
+do_execsql_test 3.3 { SELECT x FROM t2 WHERE t2 MATCH '2' } {}
+do_execsql_test 3.4 { SELECT x FROM t2 WHERE t2 MATCH '1' } {2 3 4 5 6}
+do_execsql_test 3.5 { SELECT x FROM t2 WHERE t2 MATCH 'x' } {1 2}
+do_execsql_test 3.6 { SELECT x FROM t2 WHERE t2 MATCH 'x 1' } {2}
+
+do_execsql_test 3.x { DROP TABLE t2 }
+
+#-------------------------------------------------------------------------
+# Test that the types of notindexed columns are not modified.
+#
+do_execsql_test 4.1 {
+ CREATE VIRTUAL TABLE t2 USING fts4(poi, addr, notindexed=poi);
+ INSERT INTO t2 VALUES(114, 'x x x');
+ INSERT INTO t2 VALUES(X'1234', 'y y y');
+ INSERT INTO t2 VALUES(NULL, 'z z z');
+ INSERT INTO t2 VALUES(113.2, 'w w w');
+ INSERT INTO t2 VALUES('poi', 'v v v');
+}
+do_execsql_test 4.2 { SELECT typeof(poi) FROM t2 WHERE t2 MATCH 'x' } {integer}
+do_execsql_test 4.3 { SELECT typeof(poi) FROM t2 WHERE t2 MATCH 'y' } {blob}
+do_execsql_test 4.4 { SELECT typeof(poi) FROM t2 WHERE t2 MATCH 'z' } {null}
+do_execsql_test 4.5 { SELECT typeof(poi) FROM t2 WHERE t2 MATCH 'w' } {real}
+do_execsql_test 4.6 { SELECT typeof(poi) FROM t2 WHERE t2 MATCH 'v' } {text}
+
+do_execsql_test 4.x { DROP TABLE t2 }
+
+#-------------------------------------------------------------------------
+# Test that multiple notindexed options on a single table work as expected.
+#
+do_execsql_test 5.1 {
+ CREATE VIRTUAL TABLE t2 USING fts4(
+ notindexed="three", one, two, three, notindexed="one",
+ );
+ INSERT INTO t2 VALUES('a', 'b', 'c');
+ INSERT INTO t2 VALUES('c', 'a', 'b');
+ INSERT INTO t2 VALUES('b', 'c', 'a');
+}
+do_execsql_test 5.2 { SELECT docid FROM t2 WHERE t2 MATCH 'a' } {2}
+do_execsql_test 5.3 { SELECT docid FROM t2 WHERE t2 MATCH 'b' } {1}
+do_execsql_test 5.4 { SELECT docid FROM t2 WHERE t2 MATCH 'c' } {3}
+
+do_execsql_test 5.x { DROP TABLE t2 }
+
+#-------------------------------------------------------------------------
+# Check that if an indexed column name is a prefix of a notindexed column
+# name, the column is still correctly tokenized. This was a problem at one
+# point.
+do_execsql_test 6.1.1 {
+ CREATE VIRTUAL TABLE t1 USING fts4(
+ poiCategory, poiCategoryId, notindexed=poiCategoryId
+ );
+ INSERT INTO t1(poiCategory, poiCategoryId) values ("Restaurant", 6021);
+}
+
+do_execsql_test 6.1.2 {
+ SELECT * FROM t1 WHERE t1 MATCH 'restaurant';
+} { Restaurant 6021 }
+do_execsql_test 6.1.3 {
+ SELECT * FROM t1 WHERE t1 MATCH 're*';
+} { Restaurant 6021 }
+do_execsql_test 6.1.4 {
+ SELECT * FROM t1 WHERE t1 MATCH '6021';
+} {}
+do_execsql_test 6.1.5 {
+ SELECT * FROM t1 WHERE t1 MATCH '60*';
+} {}
+
+do_execsql_test 6.2.1 {
+ DROP TABLE t1;
+ CREATE VIRTUAL TABLE t1 USING fts4(
+ poiCategory, poiCategoryId, notindexed=poiCategory
+ );
+ INSERT INTO t1(poiCategory, poiCategoryId) values ("Restaurant", 6021);
+}
+
+do_execsql_test 6.2.2 {
+ SELECT * FROM t1 WHERE t1 MATCH 'restaurant';
+} {}
+do_execsql_test 6.2.3 {
+ SELECT * FROM t1 WHERE t1 MATCH 're*';
+} {}
+do_execsql_test 6.2.4 {
+ SELECT * FROM t1 WHERE t1 MATCH '6021';
+} { Restaurant 6021 }
+do_execsql_test 6.2.5 {
+ SELECT * FROM t1 WHERE t1 MATCH '60*';
+} { Restaurant 6021 }
+
+do_execsql_test 6.3.1 {
+ DROP TABLE t1;
+ CREATE VIRTUAL TABLE t1 USING fts4(abc, ab, a, notindexed=abc);
+ CREATE VIRTUAL TABLE t2 USING fts4(a, ab, abc, notindexed=abc);
+
+ INSERT INTO t1 VALUES('no', 'yes', 'yep');
+ INSERT INTO t2 VALUES('yep', 'yes', 'no');
+
+ SELECT count(*) FROM t1 WHERE t1 MATCH 'no';
+ SELECT count(*) FROM t1 WHERE t1 MATCH 'yes';
+ SELECT count(*) FROM t1 WHERE t1 MATCH 'yep';
+
+ SELECT count(*) FROM t2 WHERE t2 MATCH 'no';
+ SELECT count(*) FROM t2 WHERE t2 MATCH 'yes';
+ SELECT count(*) FROM t2 WHERE t2 MATCH 'yep';
+} {0 1 1 0 1 1}
+
+finish_test
+
+
+
diff --git a/test/fts4unicode.test b/test/fts4unicode.test
index 8bd83f6..f237119 100644
--- a/test/fts4unicode.test
+++ b/test/fts4unicode.test
@@ -44,31 +44,36 @@ proc do_unicode_token_test3 {tn args} {
}
do_unicode_token_test 1.0 {a B c D} {0 a a 1 b B 2 c c 3 d D}
-do_unicode_token_test 1.1 {Ä Ö Ü} {0 ä Ä 1 ö Ö 2 ü Ü}
-do_unicode_token_test 1.2 {xÄx xÖx xÜx} {0 xäx xÄx 1 xöx xÖx 2 xüx xÜx}
+
+do_unicode_token_test 1.1 "\uC4 \uD6 \uDC" \
+ "0 \uE4 \uC4 1 \uF6 \uD6 2 \uFC \uDC"
+
+do_unicode_token_test 1.2 "x\uC4x x\uD6x x\uDCx" \
+ "0 x\uE4x x\uC4x 1 x\uF6x x\uD6x 2 x\uFCx x\uDCx"
# 0x00DF is a small "sharp s". 0x1E9E is a capital sharp s.
do_unicode_token_test 1.3 "\uDF" "0 \uDF \uDF"
-do_unicode_token_test 1.4 "\u1E9E" "0 ß \u1E9E"
-do_unicode_token_test 1.5 "\u1E9E" "0 \uDF \u1E9E"
+do_unicode_token_test 1.4 "\u1E9E" "0 \uDF \u1E9E"
-do_unicode_token_test 1.6 "The quick brown fox" {
+do_unicode_token_test 1.5 "The quick brown fox" {
0 the The 1 quick quick 2 brown brown 3 fox fox
}
-do_unicode_token_test 1.7 "The\u00bfquick\u224ebrown\u2263fox" {
+do_unicode_token_test 1.6 "The\u00bfquick\u224ebrown\u2263fox" {
0 the The 1 quick quick 2 brown brown 3 fox fox
}
-do_unicode_token_test2 1.8 {a B c D} {0 a a 1 b B 2 c c 3 d D}
-do_unicode_token_test2 1.9 {Ä Ö Ü} {0 a Ä 1 o Ö 2 u Ü}
-do_unicode_token_test2 1.10 {xÄx xÖx xÜx} {0 xax xÄx 1 xox xÖx 2 xux xÜx}
+do_unicode_token_test2 1.7 {a B c D} {0 a a 1 b B 2 c c 3 d D}
+do_unicode_token_test2 1.8 "\uC4 \uD6 \uDC" "0 a \uC4 1 o \uD6 2 u \uDC"
+
+do_unicode_token_test2 1.9 "x\uC4x x\uD6x x\uDCx" \
+ "0 xax x\uC4x 1 xox x\uD6x 2 xux x\uDCx"
# Check that diacritics are removed if remove_diacritics=1 is specified.
# And that they do not break tokens.
-do_unicode_token_test2 1.11 "xx\u0301xx" "0 xxxx xx\u301xx"
+do_unicode_token_test2 1.10 "xx\u0301xx" "0 xxxx xx\u301xx"
# Title-case mappings work
-do_unicode_token_test 1.12 "\u01c5" "0 \u01c6 \u01c5"
+do_unicode_token_test 1.11 "\u01c5" "0 \u01c6 \u01c5"
#-------------------------------------------------------------------------
#
@@ -378,11 +383,176 @@ foreach T $tokenizers {
do_isspace_test 6.$T.18 $T 12288
do_isspace_test 6.$T.19 $T {32 160 5760 6158}
- do_isspace_test 6.$T.19 $T {8192 8193 8194 8195}
- do_isspace_test 6.$T.19 $T {8196 8197 8198 8199}
- do_isspace_test 6.$T.19 $T {8200 8201 8202 8239}
- do_isspace_test 6.$T.19 $T {8287 12288}
+ do_isspace_test 6.$T.20 $T {8192 8193 8194 8195}
+ do_isspace_test 6.$T.21 $T {8196 8197 8198 8199}
+ do_isspace_test 6.$T.22 $T {8200 8201 8202 8239}
+ do_isspace_test 6.$T.23 $T {8287 12288}
+}
+
+#-------------------------------------------------------------------------
+# Test that the private use ranges are treated as alphanumeric.
+#
+foreach {tn1 c} {
+ 1 \ue000 2 \ue001 3 \uf000 4 \uf8fe 5 \uf8ff
+} {
+ foreach {tn2 config res} {
+ 1 "" "0 hello*world hello*world"
+ 2 "separators=*" "0 hello hello 1 world world"
+ } {
+ set config [string map [list * $c] $config]
+ set input [string map [list * $c] "hello*world"]
+ set output [string map [list * $c] $res]
+ do_unicode_token_test3 7.$tn1.$tn2 {*}$config $input $output
+ }
+}
+
+#-------------------------------------------------------------------------
+# Cursory test of remove_diacritics=0.
+#
+# 00C4;LATIN CAPITAL LETTER A WITH DIAERESIS
+# 00D6;LATIN CAPITAL LETTER O WITH DIAERESIS
+# 00E4;LATIN SMALL LETTER A WITH DIAERESIS
+# 00F6;LATIN SMALL LETTER O WITH DIAERESIS
+#
+do_execsql_test 8.1.1 "
+ CREATE VIRTUAL TABLE t3 USING fts4(tokenize=unicode61 'remove_diacritics=1');
+ INSERT INTO t3 VALUES('o');
+ INSERT INTO t3 VALUES('a');
+ INSERT INTO t3 VALUES('O');
+ INSERT INTO t3 VALUES('A');
+ INSERT INTO t3 VALUES('\xD6');
+ INSERT INTO t3 VALUES('\xC4');
+ INSERT INTO t3 VALUES('\xF6');
+ INSERT INTO t3 VALUES('\xE4');
+"
+do_execsql_test 8.1.2 {
+ SELECT rowid FROM t3 WHERE t3 MATCH 'o';
+} {1 3 5 7}
+do_execsql_test 8.1.3 {
+ SELECT rowid FROM t3 WHERE t3 MATCH 'a';
+} {2 4 6 8}
+do_execsql_test 8.2.1 {
+ CREATE VIRTUAL TABLE t4 USING fts4(tokenize=unicode61 "remove_diacritics=0");
+ INSERT INTO t4 SELECT * FROM t3;
+}
+do_execsql_test 8.2.2 {
+ SELECT rowid FROM t4 WHERE t4 MATCH 'o';
+} {1 3}
+do_execsql_test 8.2.3 {
+ SELECT rowid FROM t4 WHERE t4 MATCH 'a';
+} {2 4}
+
+#-------------------------------------------------------------------------
+#
+foreach {tn sql} {
+ 1 {
+ CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 [tokenchars= .]);
+ CREATE VIRTUAL TABLE t6 USING fts4(
+ tokenize=unicode61 [tokenchars=="] "tokenchars=[]");
+ CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 [separators=x\xC4]);
+ }
+ 2 {
+ CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 "tokenchars= .");
+ CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 "tokenchars=[=""]");
+ CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 "separators=x\xC4");
+ }
+ 3 {
+ CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 'tokenchars= .');
+ CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 'tokenchars=="[]');
+ CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 'separators=x\xC4');
+ }
+ 4 {
+ CREATE VIRTUAL TABLE t5 USING fts4(tokenize=unicode61 `tokenchars= .`);
+ CREATE VIRTUAL TABLE t6 USING fts4(tokenize=unicode61 `tokenchars=[="]`);
+ CREATE VIRTUAL TABLE t7 USING fts4(tokenize=unicode61 `separators=x\xC4`);
+ }
+} {
+ do_execsql_test 9.$tn.0 {
+ DROP TABLE IF EXISTS t5;
+ DROP TABLE IF EXISTS t5aux;
+ DROP TABLE IF EXISTS t6;
+ DROP TABLE IF EXISTS t6aux;
+ DROP TABLE IF EXISTS t7;
+ DROP TABLE IF EXISTS t7aux;
+ }
+ do_execsql_test 9.$tn.1 $sql
+
+ do_execsql_test 9.$tn.2 {
+ CREATE VIRTUAL TABLE t5aux USING fts4aux(t5);
+ INSERT INTO t5 VALUES('one two three/four.five.six');
+ SELECT * FROM t5aux;
+ } {
+ four.five.six * 1 1 four.five.six 0 1 1
+ {one two three} * 1 1 {one two three} 0 1 1
+ }
+
+ do_execsql_test 9.$tn.3 {
+ CREATE VIRTUAL TABLE t6aux USING fts4aux(t6);
+ INSERT INTO t6 VALUES('alpha=beta"gamma/delta[epsilon]zeta');
+ SELECT * FROM t6aux;
+ } {
+ {alpha=beta"gamma} * 1 1 {alpha=beta"gamma} 0 1 1
+ {delta[epsilon]zeta} * 1 1 {delta[epsilon]zeta} 0 1 1
+ }
+
+ do_execsql_test 9.$tn.4 {
+ CREATE VIRTUAL TABLE t7aux USING fts4aux(t7);
+ INSERT INTO t7 VALUES('alephxbeth\xC4gimel');
+ SELECT * FROM t7aux;
+ } {
+ aleph * 1 1 aleph 0 1 1
+ beth * 1 1 beth 0 1 1
+ gimel * 1 1 gimel 0 1 1
+ }
+}
+
+# Check that multiple options are handled correctly.
+#
+do_execsql_test 10.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE VIRTUAL TABLE t1 USING fts4(tokenize=unicode61
+ "tokenchars=xyz" "tokenchars=.=" "separators=.=" "separators=xy"
+ "separators=a" "separators=a" "tokenchars=a" "tokenchars=a"
+ );
+
+ INSERT INTO t1 VALUES('oneatwoxthreeyfour');
+ INSERT INTO t1 VALUES('a.single=word');
+ CREATE VIRTUAL TABLE t1aux USING fts4aux(t1);
+ SELECT * FROM t1aux;
+} {
+ .single=word * 1 1 .single=word 0 1 1
+ four * 1 1 four 0 1 1
+ one * 1 1 one 0 1 1
+ three * 1 1 three 0 1 1
+ two * 1 1 two 0 1 1
+}
+
+# Test that case folding happens after tokenization, not before.
+#
+do_execsql_test 10.2 {
+ DROP TABLE IF EXISTS t2;
+ CREATE VIRTUAL TABLE t2 USING fts4(tokenize=unicode61 "separators=aB");
+ INSERT INTO t2 VALUES('oneatwoBthree');
+ INSERT INTO t2 VALUES('onebtwoAthree');
+ CREATE VIRTUAL TABLE t2aux USING fts4aux(t2);
+ SELECT * FROM t2aux;
+} {
+ one * 1 1 one 0 1 1
+ onebtwoathree * 1 1 onebtwoathree 0 1 1
+ three * 1 1 three 0 1 1
+ two * 1 1 two 0 1 1
}
+# Test that the tokenchars and separators options work with the
+# fts3tokenize table.
+#
+do_execsql_test 11.1 {
+ CREATE VIRTUAL TABLE ft1 USING fts3tokenize(
+ "unicode61", "tokenchars=@.", "separators=1234567890"
+ );
+ SELECT token FROM ft1 WHERE input = 'berlin@street123sydney.road';
+} {
+ berlin@street sydney.road
+}
finish_test
diff --git a/test/func.test b/test/func.test
index 4ab7688..98ae8dd 100644
--- a/test/func.test
+++ b/test/func.test
@@ -14,6 +14,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix func
# Create a table to work with.
#
@@ -682,6 +683,32 @@ do_test func-13.7 {
lappend res [sqlite3_finalize $STMT]
} {{0 0} {1 0} SQLITE_OK}
+# Test that auxiliary data is discarded when a statement is reset.
+do_execsql_test 13.8.1 {
+ SELECT test_auxdata('constant') FROM t4;
+} {0 1}
+do_execsql_test 13.8.2 {
+ SELECT test_auxdata('constant') FROM t4;
+} {0 1}
+db cache flush
+do_execsql_test 13.8.3 {
+ SELECT test_auxdata('constant') FROM t4;
+} {0 1}
+set V "one"
+do_execsql_test 13.8.4 {
+ SELECT test_auxdata($V), $V FROM t4;
+} {0 one 1 one}
+set V "two"
+do_execsql_test 13.8.5 {
+ SELECT test_auxdata($V), $V FROM t4;
+} {0 two 1 two}
+db cache flush
+set V "three"
+do_execsql_test 13.8.6 {
+ SELECT test_auxdata($V), $V FROM t4;
+} {0 three 1 three}
+
+
# Make sure that a function with a very long name is rejected
do_test func-14.1 {
catch {
@@ -1167,6 +1194,18 @@ do_test func-24.12 {
WHEN 'program' THEN null ELSE t1 END) FROM tbl1
}
} {,is,free,software}
+# Tests to verify ticket http://www.sqlite.org/src/tktview/55746f9e65f8587c0
+do_test func-24.13 {
+ execsql {
+ SELECT typeof(group_concat(x)) FROM (SELECT '' AS x);
+ }
+} {text}
+do_test func-24.14 {
+ execsql {
+ SELECT typeof(group_concat(x,''))
+ FROM (SELECT '' AS x UNION ALL SELECT '');
+ }
+} {text}
# Use the test_isolation function to make sure that type conversions
@@ -1274,11 +1313,13 @@ do_test func-29.3 {
db eval {SELECT typeof(+x) FROM t29 ORDER BY id}
} {integer null real blob text}
if {[permutation] != "mmap"} {
- do_test func-29.4 {
- set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
- if {$x>100} {set x many}
- set x
- } {many}
+ ifcapable !direct_read {
+ do_test func-29.4 {
+ set x [lindex [sqlite3_db_status db CACHE_MISS 1] 1]
+ if {$x>100} {set x many}
+ set x
+ } {many}
+ }
}
do_test func-29.5 {
db close
@@ -1292,6 +1333,32 @@ do_test func-29.6 {
set x
} {1}
+# The OP_Column opcode has an optimization that avoids loading content
+# for fields with content-length=0 when the content offset is on an overflow
+# page. Make sure the optimization works.
+#
+do_execsql_test func-29.10 {
+ CREATE TABLE t29b(a,b,c,d,e,f,g,h,i);
+ INSERT INTO t29b
+ VALUES(1, hex(randomblob(2000)), null, 0, 1, '', zeroblob(0),'x',x'01');
+ SELECT typeof(c), typeof(d), typeof(e), typeof(f),
+ typeof(g), typeof(h), typeof(i) FROM t29b;
+} {null integer integer text blob text blob}
+do_execsql_test func-29.11 {
+ SELECT length(f), length(g), length(h), length(i) FROM t29b;
+} {0 0 1 1}
+do_execsql_test func-29.12 {
+ SELECT quote(f), quote(g), quote(h), quote(i) FROM t29b;
+} {'' X'' 'x' X'01'}
+
+# EVIDENCE-OF: R-29701-50711 The unicode(X) function returns the numeric
+# unicode code point corresponding to the first character of the string
+# X.
+#
+# EVIDENCE-OF: R-55469-62130 The char(X1,X2,...,XN) function returns a
+# string composed of characters having the unicode code point values of
+# integers X1 through XN, respectively.
+#
do_execsql_test func-30.1 {SELECT unicode('$');} 36
do_execsql_test func-30.2 [subst {SELECT unicode('\u00A2');}] 162
do_execsql_test func-30.3 [subst {SELECT unicode('\u20AC');}] 8364
@@ -1308,4 +1375,9 @@ for {set i 65536} {$i<=0x10ffff} {incr i 139} {
do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i
}
+# Test char().
+#
+do_execsql_test func-31.1 {
+ SELECT char(), length(char()), typeof(char())
+} {{} 0 text}
finish_test
diff --git a/test/func3.test b/test/func3.test
index d5a462f..3b1613b 100644
--- a/test/func3.test
+++ b/test/func3.test
@@ -70,4 +70,121 @@ do_test func3-4.1 {
} {1 SQLITE_MISUSE}
do_test func3-4.2 { set destroyed } 1
+# EVIDENCE-OF: R-41921-05214 The likelihood(X,Y) function returns
+# argument X unchanged.
+#
+do_execsql_test func3-5.1 {
+ SELECT likelihood(9223372036854775807, 0.5);
+} {9223372036854775807}
+do_execsql_test func3-5.2 {
+ SELECT likelihood(-9223372036854775808, 0.5);
+} {-9223372036854775808}
+do_execsql_test func3-5.3 {
+ SELECT likelihood(14.125, 0.5);
+} {14.125}
+do_execsql_test func3-5.4 {
+ SELECT likelihood(NULL, 0.5);
+} {{}}
+do_execsql_test func3-5.5 {
+ SELECT likelihood('test-string', 0.5);
+} {test-string}
+do_execsql_test func3-5.6 {
+ SELECT quote(likelihood(x'010203000405', 0.5));
+} {X'010203000405'}
+
+# EVIDENCE-OF: R-44133-61651 The value Y in likelihood(X,Y) must be a
+# floating point constant between 0.0 and 1.0, inclusive.
+#
+do_execsql_test func3-5.7 {
+ SELECT likelihood(123, 1.0), likelihood(456, 0.0);
+} {123 456}
+do_test func3-5.8 {
+ catchsql {
+ SELECT likelihood(123, 1.000001);
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+do_test func3-5.9 {
+ catchsql {
+ SELECT likelihood(123, -0.000001);
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+do_test func3-5.10 {
+ catchsql {
+ SELECT likelihood(123, 0.5+0.3);
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+
+# EVIDENCE-OF: R-28535-44631 The likelihood(X) function is a no-op that
+# the code generator optimizes away so that it consumes no CPU cycles
+# during run-time (that is, during calls to sqlite3_step()).
+#
+do_test func3-5.20 {
+ db eval {EXPLAIN SELECT likelihood(min(1.0+'2.0',4*11), 0.5)}
+} [db eval {EXPLAIN SELECT min(1.0+'2.0',4*11)}]
+
+
+# EVIDENCE-OF: R-11152-23456 The unlikely(X) function returns the
+# argument X unchanged.
+#
+do_execsql_test func3-5.30 {
+ SELECT unlikely(9223372036854775807);
+} {9223372036854775807}
+do_execsql_test func3-5.31 {
+ SELECT unlikely(-9223372036854775808);
+} {-9223372036854775808}
+do_execsql_test func3-5.32 {
+ SELECT unlikely(14.125);
+} {14.125}
+do_execsql_test func3-5.33 {
+ SELECT unlikely(NULL);
+} {{}}
+do_execsql_test func3-5.34 {
+ SELECT unlikely('test-string');
+} {test-string}
+do_execsql_test func3-5.35 {
+ SELECT quote(unlikely(x'010203000405'));
+} {X'010203000405'}
+
+# EVIDENCE-OF: R-22887-63324 The unlikely(X) function is a no-op that
+# the code generator optimizes away so that it consumes no CPU cycles at
+# run-time (that is, during calls to sqlite3_step()).
+#
+do_test func3-5.39 {
+ db eval {EXPLAIN SELECT unlikely(min(1.0+'2.0',4*11))}
+} [db eval {EXPLAIN SELECT min(1.0+'2.0',4*11)}]
+
+
+# EVIDENCE-OF: R-23735-03107 The likely(X) function returns the argument
+# X unchanged.
+#
+do_execsql_test func3-5.50 {
+ SELECT likely(9223372036854775807);
+} {9223372036854775807}
+do_execsql_test func3-5.51 {
+ SELECT likely(-9223372036854775808);
+} {-9223372036854775808}
+do_execsql_test func3-5.52 {
+ SELECT likely(14.125);
+} {14.125}
+do_execsql_test func3-5.53 {
+ SELECT likely(NULL);
+} {{}}
+do_execsql_test func3-5.54 {
+ SELECT likely('test-string');
+} {test-string}
+do_execsql_test func3-5.55 {
+ SELECT quote(likely(x'010203000405'));
+} {X'010203000405'}
+
+# EVIDENCE-OF: R-43464-09689 The likely(X) function is a no-op that the
+# code generator optimizes away so that it consumes no CPU cycles at
+# run-time (that is, during calls to sqlite3_step()).
+#
+do_test func3-5.59 {
+ db eval {EXPLAIN SELECT likely(min(1.0+'2.0',4*11))}
+} [db eval {EXPLAIN SELECT min(1.0+'2.0',4*11)}]
+
+
+
+
finish_test
diff --git a/test/func4.test b/test/func4.test
new file mode 100644
index 0000000..e94f8c3
--- /dev/null
+++ b/test/func4.test
@@ -0,0 +1,758 @@
+# 2013 March 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 testing the tointeger() and toreal() functions.
+#
+# Several of the toreal() tests are disabled on platforms where floating
+# point precision is not high enough to represent their constant integer
+# expression arguments as double precision floating point values.
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set saved_tcl_precision $tcl_precision
+set tcl_precision 0
+load_static_extension db totype
+
+set highPrecision(1) [expr \
+ {[db eval {SELECT tointeger(9223372036854775807 + 1);}] eq {{}}}]
+
+do_execsql_test func4-1.1 {
+ SELECT tointeger(NULL);
+} {{}}
+do_execsql_test func4-1.2 {
+ SELECT tointeger('');
+} {{}}
+do_execsql_test func4-1.3 {
+ SELECT tointeger(' ');
+} {{}}
+do_execsql_test func4-1.4 {
+ SELECT tointeger('1234');
+} {1234}
+do_execsql_test func4-1.5 {
+ SELECT tointeger(' 1234');
+} {{}}
+do_execsql_test func4-1.6 {
+ SELECT tointeger('bad');
+} {{}}
+do_execsql_test func4-1.7 {
+ SELECT tointeger('0xBAD');
+} {{}}
+do_execsql_test func4-1.8 {
+ SELECT tointeger('123BAD');
+} {{}}
+do_execsql_test func4-1.9 {
+ SELECT tointeger('0x123BAD');
+} {{}}
+do_execsql_test func4-1.10 {
+ SELECT tointeger('123NO');
+} {{}}
+do_execsql_test func4-1.11 {
+ SELECT tointeger('0x123NO');
+} {{}}
+do_execsql_test func4-1.12 {
+ SELECT tointeger('-0x1');
+} {{}}
+do_execsql_test func4-1.13 {
+ SELECT tointeger('-0x0');
+} {{}}
+do_execsql_test func4-1.14 {
+ SELECT tointeger('0x0');
+} {{}}
+do_execsql_test func4-1.15 {
+ SELECT tointeger('0x1');
+} {{}}
+do_execsql_test func4-1.16 {
+ SELECT tointeger(-1);
+} {-1}
+do_execsql_test func4-1.17 {
+ SELECT tointeger(-0);
+} {0}
+do_execsql_test func4-1.18 {
+ SELECT tointeger(0);
+} {0}
+do_execsql_test func4-1.19 {
+ SELECT tointeger(1);
+} {1}
+do_execsql_test func4-1.20 {
+ SELECT tointeger(-1.79769313486232e308 - 1);
+} {{}}
+do_execsql_test func4-1.21 {
+ SELECT tointeger(-1.79769313486232e308);
+} {{}}
+do_execsql_test func4-1.22 {
+ SELECT tointeger(-1.79769313486232e308 + 1);
+} {{}}
+do_execsql_test func4-1.23 {
+ SELECT tointeger(-9223372036854775808 - 1);
+} {-9223372036854775808}
+do_execsql_test func4-1.24 {
+ SELECT tointeger(-9223372036854775808);
+} {-9223372036854775808}
+do_execsql_test func4-1.25 {
+ SELECT tointeger(-9223372036854775808 + 1);
+} {-9223372036854775807}
+do_execsql_test func4-1.26 {
+ SELECT tointeger(-9223372036854775807 - 1);
+} {-9223372036854775808}
+do_execsql_test func4-1.27 {
+ SELECT tointeger(-9223372036854775807);
+} {-9223372036854775807}
+do_execsql_test func4-1.28 {
+ SELECT tointeger(-9223372036854775807 + 1);
+} {-9223372036854775806}
+do_execsql_test func4-1.29 {
+ SELECT tointeger(-2147483648 - 1);
+} {-2147483649}
+do_execsql_test func4-1.30 {
+ SELECT tointeger(-2147483648);
+} {-2147483648}
+do_execsql_test func4-1.31 {
+ SELECT tointeger(-2147483648 + 1);
+} {-2147483647}
+do_execsql_test func4-1.32 {
+ SELECT tointeger(2147483647 - 1);
+} {2147483646}
+do_execsql_test func4-1.33 {
+ SELECT tointeger(2147483647);
+} {2147483647}
+do_execsql_test func4-1.34 {
+ SELECT tointeger(2147483647 + 1);
+} {2147483648}
+do_execsql_test func4-1.35 {
+ SELECT tointeger(9223372036854775807 - 1);
+} {9223372036854775806}
+do_execsql_test func4-1.36 {
+ SELECT tointeger(9223372036854775807);
+} {9223372036854775807}
+if {$highPrecision(1)} {
+ do_execsql_test func4-1.37 {
+ SELECT tointeger(9223372036854775807 + 1);
+ } {{}}
+}
+do_execsql_test func4-1.38 {
+ SELECT tointeger(1.79769313486232e308 - 1);
+} {{}}
+do_execsql_test func4-1.39 {
+ SELECT tointeger(1.79769313486232e308);
+} {{}}
+do_execsql_test func4-1.40 {
+ SELECT tointeger(1.79769313486232e308 + 1);
+} {{}}
+do_execsql_test func4-1.41 {
+ SELECT tointeger(4503599627370496 - 1);
+} {4503599627370495}
+do_execsql_test func4-1.42 {
+ SELECT tointeger(4503599627370496);
+} {4503599627370496}
+do_execsql_test func4-1.43 {
+ SELECT tointeger(4503599627370496 + 1);
+} {4503599627370497}
+do_execsql_test func4-1.44 {
+ SELECT tointeger(9007199254740992 - 1);
+} {9007199254740991}
+do_execsql_test func4-1.45 {
+ SELECT tointeger(9007199254740992);
+} {9007199254740992}
+do_execsql_test func4-1.46 {
+ SELECT tointeger(9007199254740992 + 1);
+} {9007199254740993}
+do_execsql_test func4-1.47 {
+ SELECT tointeger(9223372036854775807 - 1);
+} {9223372036854775806}
+do_execsql_test func4-1.48 {
+ SELECT tointeger(9223372036854775807);
+} {9223372036854775807}
+if {$highPrecision(1)} {
+ do_execsql_test func4-1.49 {
+ SELECT tointeger(9223372036854775807 + 1);
+ } {{}}
+ do_execsql_test func4-1.50 {
+ SELECT tointeger(9223372036854775808 - 1);
+ } {{}}
+ do_execsql_test func4-1.51 {
+ SELECT tointeger(9223372036854775808);
+ } {{}}
+ do_execsql_test func4-1.52 {
+ SELECT tointeger(9223372036854775808 + 1);
+ } {{}}
+}
+do_execsql_test func4-1.53 {
+ SELECT tointeger(18446744073709551616 - 1);
+} {{}}
+do_execsql_test func4-1.54 {
+ SELECT tointeger(18446744073709551616);
+} {{}}
+do_execsql_test func4-1.55 {
+ SELECT tointeger(18446744073709551616 + 1);
+} {{}}
+
+ifcapable floatingpoint {
+ set highPrecision(2) [expr \
+ {[db eval {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}]
+
+ do_execsql_test func4-2.1 {
+ SELECT toreal(NULL);
+ } {{}}
+ do_execsql_test func4-2.2 {
+ SELECT toreal('');
+ } {{}}
+ do_execsql_test func4-2.3 {
+ SELECT toreal(' ');
+ } {{}}
+ do_execsql_test func4-2.4 {
+ SELECT toreal('1234');
+ } {1234.0}
+ do_execsql_test func4-2.5 {
+ SELECT toreal(' 1234');
+ } {{}}
+ do_execsql_test func4-2.6 {
+ SELECT toreal('bad');
+ } {{}}
+ do_execsql_test func4-2.7 {
+ SELECT toreal('0xBAD');
+ } {{}}
+ do_execsql_test func4-2.8 {
+ SELECT toreal('123BAD');
+ } {{}}
+ do_execsql_test func4-2.9 {
+ SELECT toreal('0x123BAD');
+ } {{}}
+ do_execsql_test func4-2.10 {
+ SELECT toreal('123NO');
+ } {{}}
+ do_execsql_test func4-2.11 {
+ SELECT toreal('0x123NO');
+ } {{}}
+ do_execsql_test func4-2.12 {
+ SELECT toreal('-0x1');
+ } {{}}
+ do_execsql_test func4-2.13 {
+ SELECT toreal('-0x0');
+ } {{}}
+ do_execsql_test func4-2.14 {
+ SELECT toreal('0x0');
+ } {{}}
+ do_execsql_test func4-2.15 {
+ SELECT toreal('0x1');
+ } {{}}
+ do_execsql_test func4-2.16 {
+ SELECT toreal(-1);
+ } {-1.0}
+ do_execsql_test func4-2.17 {
+ SELECT toreal(-0);
+ } {0.0}
+ do_execsql_test func4-2.18 {
+ SELECT toreal(0);
+ } {0.0}
+ do_execsql_test func4-2.19 {
+ SELECT toreal(1);
+ } {1.0}
+ do_execsql_test func4-2.20 {
+ SELECT toreal(-1.79769313486232e308 - 1);
+ } {-Inf}
+ do_execsql_test func4-2.21 {
+ SELECT toreal(-1.79769313486232e308);
+ } {-Inf}
+ do_execsql_test func4-2.22 {
+ SELECT toreal(-1.79769313486232e308 + 1);
+ } {-Inf}
+ do_execsql_test func4-2.23 {
+ SELECT toreal(-9223372036854775808 - 1);
+ } {-9.223372036854776e+18}
+ do_execsql_test func4-2.24 {
+ SELECT toreal(-9223372036854775808);
+ } {-9.223372036854776e+18}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-2.25 {
+ SELECT toreal(-9223372036854775808 + 1);
+ } {{}}
+ }
+ do_execsql_test func4-2.26 {
+ SELECT toreal(-9223372036854775807 - 1);
+ } {-9.223372036854776e+18}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-2.27 {
+ SELECT toreal(-9223372036854775807);
+ } {{}}
+ do_execsql_test func4-2.28 {
+ SELECT toreal(-9223372036854775807 + 1);
+ } {{}}
+ }
+ do_execsql_test func4-2.29 {
+ SELECT toreal(-2147483648 - 1);
+ } {-2147483649.0}
+ do_execsql_test func4-2.30 {
+ SELECT toreal(-2147483648);
+ } {-2147483648.0}
+ do_execsql_test func4-2.31 {
+ SELECT toreal(-2147483648 + 1);
+ } {-2147483647.0}
+ do_execsql_test func4-2.32 {
+ SELECT toreal(2147483647 - 1);
+ } {2147483646.0}
+ do_execsql_test func4-2.33 {
+ SELECT toreal(2147483647);
+ } {2147483647.0}
+ do_execsql_test func4-2.34 {
+ SELECT toreal(2147483647 + 1);
+ } {2147483648.0}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-2.35 {
+ SELECT toreal(9223372036854775807 - 1);
+ } {{}}
+ if {$highPrecision(1)} {
+ do_execsql_test func4-2.36 {
+ SELECT toreal(9223372036854775807);
+ } {{}}
+ }
+ }
+ do_execsql_test func4-2.37 {
+ SELECT toreal(9223372036854775807 + 1);
+ } {9.223372036854776e+18}
+ do_execsql_test func4-2.38 {
+ SELECT toreal(1.79769313486232e308 - 1);
+ } {Inf}
+ do_execsql_test func4-2.39 {
+ SELECT toreal(1.79769313486232e308);
+ } {Inf}
+ do_execsql_test func4-2.40 {
+ SELECT toreal(1.79769313486232e308 + 1);
+ } {Inf}
+ do_execsql_test func4-2.41 {
+ SELECT toreal(4503599627370496 - 1);
+ } {4503599627370495.0}
+ do_execsql_test func4-2.42 {
+ SELECT toreal(4503599627370496);
+ } {4503599627370496.0}
+ do_execsql_test func4-2.43 {
+ SELECT toreal(4503599627370496 + 1);
+ } {4503599627370497.0}
+ do_execsql_test func4-2.44 {
+ SELECT toreal(9007199254740992 - 1);
+ } {9007199254740991.0}
+ do_execsql_test func4-2.45 {
+ SELECT toreal(9007199254740992);
+ } {9007199254740992.0}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-2.46 {
+ SELECT toreal(9007199254740992 + 1);
+ } {{}}
+ }
+ do_execsql_test func4-2.47 {
+ SELECT toreal(9007199254740992 + 2);
+ } {9007199254740994.0}
+ do_execsql_test func4-2.48 {
+ SELECT toreal(tointeger(9223372036854775808) - 1);
+ } {{}}
+ if {$highPrecision(1)} {
+ do_execsql_test func4-2.49 {
+ SELECT toreal(tointeger(9223372036854775808));
+ } {{}}
+ do_execsql_test func4-2.50 {
+ SELECT toreal(tointeger(9223372036854775808) + 1);
+ } {{}}
+ }
+ do_execsql_test func4-2.51 {
+ SELECT toreal(tointeger(18446744073709551616) - 1);
+ } {{}}
+ do_execsql_test func4-2.52 {
+ SELECT toreal(tointeger(18446744073709551616));
+ } {{}}
+ do_execsql_test func4-2.53 {
+ SELECT toreal(tointeger(18446744073709551616) + 1);
+ } {{}}
+}
+
+ifcapable check {
+ do_execsql_test func4-3.1 {
+ CREATE TABLE t1(
+ x INTEGER CHECK(tointeger(x) IS NOT NULL)
+ );
+ } {}
+ do_test func4-3.2 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (NULL);
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.3 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (NULL);
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.4 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.5 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('bad');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.6 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('1234bad');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.7 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('1234.56bad');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.8 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (1234);
+ }
+ } {0 {}}
+ do_test func4-3.9 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (1234.56);
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.10 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('1234');
+ }
+ } {0 {}}
+ do_test func4-3.11 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('1234.56');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.12 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (ZEROBLOB(4));
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.13 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (X'');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.14 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (X'1234');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.15 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (X'12345678');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.16 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('1234.00');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ do_test func4-3.17 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (1234.00);
+ }
+ } {0 {}}
+ do_test func4-3.18 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES ('-9223372036854775809');
+ }
+ } {1 {CHECK constraint failed: t1}}
+ if {$highPrecision(1)} {
+ do_test func4-3.19 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES (9223372036854775808);
+ }
+ } {1 {CHECK constraint failed: t1}}
+ }
+ do_execsql_test func4-3.20 {
+ SELECT x FROM t1 ORDER BY x;
+ } {1234 1234 1234}
+
+ ifcapable floatingpoint {
+ do_execsql_test func4-4.1 {
+ CREATE TABLE t2(
+ x REAL CHECK(toreal(x) IS NOT NULL)
+ );
+ } {}
+ do_test func4-4.2 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (NULL);
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.3 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (NULL);
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.4 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.5 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('bad');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.6 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('1234bad');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.7 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('1234.56bad');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.8 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (1234);
+ }
+ } {0 {}}
+ do_test func4-4.9 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (1234.56);
+ }
+ } {0 {}}
+ do_test func4-4.10 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('1234');
+ }
+ } {0 {}}
+ do_test func4-4.11 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES ('1234.56');
+ }
+ } {0 {}}
+ do_test func4-4.12 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (ZEROBLOB(4));
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.13 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (X'');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.14 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (X'1234');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_test func4-4.15 {
+ catchsql {
+ INSERT INTO t2 (x) VALUES (X'12345678');
+ }
+ } {1 {CHECK constraint failed: t2}}
+ do_execsql_test func4-4.16 {
+ SELECT x FROM t2 ORDER BY x;
+ } {1234.0 1234.0 1234.56 1234.56}
+ }
+}
+
+ifcapable floatingpoint {
+ do_execsql_test func4-5.1 {
+ SELECT tointeger(toreal('1234'));
+ } {1234}
+ do_execsql_test func4-5.2 {
+ SELECT tointeger(toreal(-1));
+ } {-1}
+ do_execsql_test func4-5.3 {
+ SELECT tointeger(toreal(-0));
+ } {0}
+ do_execsql_test func4-5.4 {
+ SELECT tointeger(toreal(0));
+ } {0}
+ do_execsql_test func4-5.5 {
+ SELECT tointeger(toreal(1));
+ } {1}
+ do_execsql_test func4-5.6 {
+ SELECT tointeger(toreal(-9223372036854775808 - 1));
+ } {-9223372036854775808}
+ do_execsql_test func4-5.7 {
+ SELECT tointeger(toreal(-9223372036854775808));
+ } {-9223372036854775808}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-5.8 {
+ SELECT tointeger(toreal(-9223372036854775808 + 1));
+ } {{}}
+ }
+ do_execsql_test func4-5.9 {
+ SELECT tointeger(toreal(-2147483648 - 1));
+ } {-2147483649}
+ do_execsql_test func4-5.10 {
+ SELECT tointeger(toreal(-2147483648));
+ } {-2147483648}
+ do_execsql_test func4-5.11 {
+ SELECT tointeger(toreal(-2147483648 + 1));
+ } {-2147483647}
+ do_execsql_test func4-5.12 {
+ SELECT tointeger(toreal(2147483647 - 1));
+ } {2147483646}
+ do_execsql_test func4-5.13 {
+ SELECT tointeger(toreal(2147483647));
+ } {2147483647}
+ do_execsql_test func4-5.14 {
+ SELECT tointeger(toreal(2147483647 + 1));
+ } {2147483648}
+ do_execsql_test func4-5.15 {
+ SELECT tointeger(toreal(9223372036854775807 - 1));
+ } {{}}
+ if {$highPrecision(1)} {
+ do_execsql_test func4-5.16 {
+ SELECT tointeger(toreal(9223372036854775807));
+ } {{}}
+ do_execsql_test func4-5.17 {
+ SELECT tointeger(toreal(9223372036854775807 + 1));
+ } {{}}
+ }
+ do_execsql_test func4-5.18 {
+ SELECT tointeger(toreal(4503599627370496 - 1));
+ } {4503599627370495}
+ do_execsql_test func4-5.19 {
+ SELECT tointeger(toreal(4503599627370496));
+ } {4503599627370496}
+ do_execsql_test func4-5.20 {
+ SELECT tointeger(toreal(4503599627370496 + 1));
+ } {4503599627370497}
+ do_execsql_test func4-5.21 {
+ SELECT tointeger(toreal(9007199254740992 - 1));
+ } {9007199254740991}
+ do_execsql_test func4-5.22 {
+ SELECT tointeger(toreal(9007199254740992));
+ } {9007199254740992}
+ if {$highPrecision(2)} {
+ do_execsql_test func4-5.23 {
+ SELECT tointeger(toreal(9007199254740992 + 1));
+ } {{}}
+ }
+ do_execsql_test func4-5.24 {
+ SELECT tointeger(toreal(9007199254740992 + 2));
+ } {9007199254740994}
+ if {$highPrecision(1)} {
+ do_execsql_test func4-5.25 {
+ SELECT tointeger(toreal(9223372036854775808 - 1));
+ } {{}}
+ do_execsql_test func4-5.26 {
+ SELECT tointeger(toreal(9223372036854775808));
+ } {{}}
+ do_execsql_test func4-5.27 {
+ SELECT tointeger(toreal(9223372036854775808 + 1));
+ } {{}}
+ }
+ do_execsql_test func4-5.28 {
+ SELECT tointeger(toreal(18446744073709551616 - 1));
+ } {{}}
+ do_execsql_test func4-5.29 {
+ SELECT tointeger(toreal(18446744073709551616));
+ } {{}}
+ do_execsql_test func4-5.30 {
+ SELECT tointeger(toreal(18446744073709551616 + 1));
+ } {{}}
+}
+
+for {set i 0} {$i < 10} {incr i} {
+ if {$i == 8} continue
+ do_execsql_test func4-6.1.$i.1 [subst {
+ SELECT tointeger(x'[string repeat 01 $i]');
+ }] {{}}
+ ifcapable floatingpoint {
+ do_execsql_test func4-6.1.$i.2 [subst {
+ SELECT toreal(x'[string repeat 01 $i]');
+ }] {{}}
+ }
+}
+
+do_execsql_test func4-6.2.1 {
+ SELECT tointeger(x'0102030405060708');
+} {578437695752307201}
+do_execsql_test func4-6.2.2 {
+ SELECT tointeger(x'0807060504030201');
+} {72623859790382856}
+
+ifcapable floatingpoint {
+ do_execsql_test func4-6.3.1 {
+ SELECT toreal(x'ffefffffffffffff');
+ } {-1.7976931348623157e+308}
+ do_execsql_test func4-6.3.2 {
+ SELECT toreal(x'8010000000000000');
+ } {-2.2250738585072014e-308}
+ do_execsql_test func4-6.3.3 {
+ SELECT toreal(x'c000000000000000');
+ } {-2.0}
+ do_execsql_test func4-6.3.4 {
+ SELECT toreal(x'bff0000000000000');
+ } {-1.0}
+ do_execsql_test func4-6.3.5 {
+ SELECT toreal(x'8000000000000000');
+ } {-0.0}
+ do_execsql_test func4-6.3.6 {
+ SELECT toreal(x'0000000000000000');
+ } {0.0}
+ do_execsql_test func4-6.3.7 {
+ SELECT toreal(x'3ff0000000000000');
+ } {1.0}
+ do_execsql_test func4-6.3.8 {
+ SELECT toreal(x'4000000000000000');
+ } {2.0}
+ do_execsql_test func4-6.3.9 {
+ SELECT toreal(x'0010000000000000');
+ } {2.2250738585072014e-308}
+ do_execsql_test func4-6.3.10 {
+ SELECT toreal(x'7fefffffffffffff');
+ } {1.7976931348623157e+308}
+ do_execsql_test func4-6.3.11 {
+ SELECT toreal(x'8000000000000001');
+ } {-5e-324}
+ do_execsql_test func4-6.3.12 {
+ SELECT toreal(x'800fffffffffffff');
+ } {-2.225073858507201e-308}
+ do_execsql_test func4-6.3.13 {
+ SELECT toreal(x'0000000000000001');
+ } {5e-324}
+ do_execsql_test func4-6.3.14 {
+ SELECT toreal(x'000fffffffffffff');
+ } {2.225073858507201e-308}
+ do_execsql_test func4-6.3.15 {
+ SELECT toreal(x'fff0000000000000');
+ } {-Inf}
+ do_execsql_test func4-6.3.16 {
+ SELECT toreal(x'7ff0000000000000');
+ } {Inf}
+ do_execsql_test func4-6.3.17 {
+ SELECT toreal(x'fff8000000000000');
+ } {{}}
+ do_execsql_test func4-6.3.18 {
+ SELECT toreal(x'fff0000000000001');
+ } {{}}
+ do_execsql_test func4-6.3.19 {
+ SELECT toreal(x'fff7ffffffffffff');
+ } {{}}
+ do_execsql_test func4-6.3.20 {
+ SELECT toreal(x'7ff0000000000001');
+ } {{}}
+ do_execsql_test func4-6.3.21 {
+ SELECT toreal(x'7ff7ffffffffffff');
+ } {{}}
+ do_execsql_test func4-6.3.22 {
+ SELECT toreal(x'fff8000000000001');
+ } {{}}
+ do_execsql_test func4-6.3.23 {
+ SELECT toreal(x'ffffffffffffffff');
+ } {{}}
+ do_execsql_test func4-6.3.24 {
+ SELECT toreal(x'7ff8000000000000');
+ } {{}}
+ do_execsql_test func4-6.3.25 {
+ SELECT toreal(x'7fffffffffffffff');
+ } {{}}
+}
+
+set tcl_precision $saved_tcl_precision
+unset saved_tcl_precision
+finish_test
diff --git a/test/func5.test b/test/func5.test
new file mode 100644
index 0000000..bfd545b
--- /dev/null
+++ b/test/func5.test
@@ -0,0 +1,63 @@
+# 2013-11-21
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+#
+# Testing of function factoring and the SQLITE_DETERMINISTIC flag.
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Verify that constant string expressions that get factored into initializing
+# code are not reused between function parameters and other values in the
+# VDBE program, as the function might have changed the encoding.
+#
+do_execsql_test func5-1.1 {
+ PRAGMA encoding=UTF16le;
+ CREATE TABLE t1(x,a,b,c);
+ INSERT INTO t1 VALUES(1,'ab','cd',1);
+ INSERT INTO t1 VALUES(2,'gh','ef',5);
+ INSERT INTO t1 VALUES(3,'pqr','fuzzy',99);
+ INSERT INTO t1 VALUES(4,'abcdefg','xy',22);
+ INSERT INTO t1 VALUES(5,'shoe','mayer',2953);
+ SELECT x FROM t1 WHERE c=instr('abcdefg',b) OR a='abcdefg' ORDER BY +x;
+} {2 4}
+do_execsql_test func5-1.2 {
+ SELECT x FROM t1 WHERE a='abcdefg' OR c=instr('abcdefg',b) ORDER BY +x;
+} {2 4}
+
+# Verify that SQLITE_DETERMINISTIC functions get factored out of the
+# evaluation loop whereas non-deterministic functions do not. counter1()
+# is marked as non-deterministic and so is not factored out of the loop,
+# and it really is non-deterministic, returning a different result each
+# time. But counter2() is marked as deterministic, so it does get factored
+# out of the loop. counter2() has the same implementation as counter1(),
+# returning a different result on each invocation, but because it is
+# only invoked once outside of the loop, it appears to return the same
+# result multiple times.
+#
+do_execsql_test func5-2.1 {
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 VALUES(1,2),(3,4),(5,6),(7,8);
+ SELECT x, y FROM t2 WHERE x+5=5+x ORDER BY +x;
+} {1 2 3 4 5 6 7 8}
+sqlite3_create_function db
+do_execsql_test func5-2.2 {
+ SELECT x, y FROM t2
+ WHERE x+counter1('hello')=counter1('hello')+x
+ ORDER BY +x;
+} {}
+do_execsql_test func5-2.3 {
+ SELECT x, y FROM t2
+ WHERE x+counter2('hello')=counter2('hello')+x
+ ORDER BY +x;
+} {1 2 3 4 5 6 7 8}
+
+
+finish_test
diff --git a/test/fuzz.test b/test/fuzz.test
index e1b22ae..0deed3b 100644
--- a/test/fuzz.test
+++ b/test/fuzz.test
@@ -285,7 +285,7 @@ do_test fuzz-1.18 {
)
))
}
-} {0 -4294967298}
+} {0 {{}}}
# At one point the following INSERT statement caused an assert() to fail.
#
diff --git a/test/fuzz3.test b/test/fuzz3.test
index d0efc52..2b21404 100644
--- a/test/fuzz3.test
+++ b/test/fuzz3.test
@@ -18,6 +18,9 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+# These tests deal with corrupt database files
+#
+database_may_be_corrupt
expr srand(123)
diff --git a/test/fuzzer1.test b/test/fuzzer1.test
index 473d0e1..4ee5730 100644
--- a/test/fuzzer1.test
+++ b/test/fuzzer1.test
@@ -1728,36 +1728,41 @@ do_execsql_test 8.1 {
do_execsql_test 8.2.1 {
SELECT cFrom, cTo, word
FROM x3_rules CROSS JOIN x3
- WHERE word MATCH 'a' AND cost=distance AND ruleset=2;
+ WHERE word MATCH 'a' AND cost=distance AND ruleset=2
+ ORDER BY +cTo;
} {a x x a y y a z z}
do_execsql_test 8.2.2 {
SELECT cFrom, cTo, word
FROM x3 CROSS JOIN x3_rules
- WHERE word MATCH 'a' AND cost=distance AND ruleset=2;
+ WHERE word MATCH 'a' AND cost=distance AND ruleset=2
+ ORDER BY +cTo DESC
} {a z z a y y a x x}
do_execsql_test 8.2.3 {
SELECT cFrom, cTo, word
FROM x3_rules, x3
- WHERE word MATCH 'a' AND cost=distance AND ruleset=2;
+ WHERE word MATCH 'a' AND cost=distance AND ruleset=2
+ ORDER BY +cTo DESC;
} {a z z a y y a x x}
do_execsql_test 8.2.4 {
SELECT cFrom, cTo, word
FROM x3, x3_rules
- WHERE word MATCH 'a' AND cost=distance AND ruleset=2;
+ WHERE word MATCH 'a' AND cost=distance AND ruleset=2
+ ORDER BY +cTo DESC;
} {a z z a y y a x x}
do_execsql_test 8.2.5 {
CREATE INDEX i1 ON x3_rules(cost);
SELECT cFrom, cTo, word
FROM x3_rules, x3
- WHERE word MATCH 'a' AND cost=distance AND ruleset=2;
+ WHERE word MATCH 'a' AND cost=distance AND ruleset=2
+ ORDER BY +cTo DESC;
} {a z z a y y a x x}
do_execsql_test 8.2.5 {
- SELECT word FROM x3_rules, x3 WHERE word MATCH x3_rules.cFrom AND ruleset=2;
+ SELECT word FROM x3_rules, x3 WHERE word MATCH x3_rules.cFrom AND ruleset=2
} {a z y x a z y x a z y x}
do_execsql_test 8.2.6 {
diff --git a/test/genesis.tcl b/test/genesis.tcl
new file mode 100644
index 0000000..4ba4c8e
--- /dev/null
+++ b/test/genesis.tcl
@@ -0,0 +1,1560 @@
+# 2010 February 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 a TCL function that fills an FTS table with
+# lots of content useful for testing. This routine is broken out into
+# a separate file to facilitate its use by multiple test scripts.
+#
+
+# The fts_kjv_genesis routine is already loaded. This script is a no-op.
+if {[lsearch [info procs] fts_kjv_genesis]>=0} return
+
+# This procedure fills an existing FTS3/FTS4 table with many entries.
+# The table needs to have a single column (other than docid) named "words".
+#
+proc fts_kjv_genesis {} {
+db eval {
+BEGIN TRANSACTION;
+INSERT INTO t1(docid,words) VALUES(1001001,'In the beginning God created the heaven and the earth.');
+INSERT INTO t1(docid,words) VALUES(1001002,'And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.');
+INSERT INTO t1(docid,words) VALUES(1001003,'And God said, Let there be light: and there was light.');
+INSERT INTO t1(docid,words) VALUES(1001004,'And God saw the light, that it was good: and God divided the light from the darkness.');
+INSERT INTO t1(docid,words) VALUES(1001005,'And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.');
+INSERT INTO t1(docid,words) VALUES(1001006,'And God said, Let there be a firmament in the midst of the waters, and let it divide the waters from the waters.');
+INSERT INTO t1(docid,words) VALUES(1001007,'And God made the firmament, and divided the waters which were under the firmament from the waters which were above the firmament: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001008,'And God called the firmament Heaven. And the evening and the morning were the second day.');
+INSERT INTO t1(docid,words) VALUES(1001009,'And God said, Let the waters under the heaven be gathered together unto one place, and let the dry land appear: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001010,'And God called the dry land Earth; and the gathering together of the waters called he Seas: and God saw that it was good.');
+INSERT INTO t1(docid,words) VALUES(1001011,'And God said, Let the earth bring forth grass, the herb yielding seed, and the fruit tree yielding fruit after his kind, whose seed is in itself, upon the earth: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001012,'And the earth brought forth grass, and herb yielding seed after his kind, and the tree yielding fruit, whose seed was in itself, after his kind: and God saw that it was good.');
+INSERT INTO t1(docid,words) VALUES(1001013,'And the evening and the morning were the third day.');
+INSERT INTO t1(docid,words) VALUES(1001014,'And God said, Let there be lights in the firmament of the heaven to divide the day from the night; and let them be for signs, and for seasons, and for days, and years:');
+INSERT INTO t1(docid,words) VALUES(1001015,'And let them be for lights in the firmament of the heaven to give light upon the earth: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001016,'And God made two great lights; the greater light to rule the day, and the lesser light to rule the night: he made the stars also.');
+INSERT INTO t1(docid,words) VALUES(1001017,'And God set them in the firmament of the heaven to give light upon the earth,');
+INSERT INTO t1(docid,words) VALUES(1001018,'And to rule over the day and over the night, and to divide the light from the darkness: and God saw that it was good.');
+INSERT INTO t1(docid,words) VALUES(1001019,'And the evening and the morning were the fourth day.');
+INSERT INTO t1(docid,words) VALUES(1001020,'And God said, Let the waters bring forth abundantly the moving creature that hath life, and fowl that may fly above the earth in the open firmament of heaven.');
+INSERT INTO t1(docid,words) VALUES(1001021,'And God created great whales, and every living creature that moveth, which the waters brought forth abundantly, after their kind, and every winged fowl after his kind: and God saw that it was good.');
+INSERT INTO t1(docid,words) VALUES(1001022,'And God blessed them, saying, Be fruitful, and multiply, and fill the waters in the seas, and let fowl multiply in the earth.');
+INSERT INTO t1(docid,words) VALUES(1001023,'And the evening and the morning were the fifth day.');
+INSERT INTO t1(docid,words) VALUES(1001024,'And God said, Let the earth bring forth the living creature after his kind, cattle, and creeping thing, and beast of the earth after his kind: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001025,'And God made the beast of the earth after his kind, and cattle after their kind, and every thing that creepeth upon the earth after his kind: and God saw that it was good.');
+INSERT INTO t1(docid,words) VALUES(1001026,'And God said, Let us make man in our image, after our likeness: and let them have dominion over the fish of the sea, and over the fowl of the air, and over the cattle, and over all the earth, and over every creeping thing that creepeth upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1001027,'So God created man in his own image, in the image of God created he him; male and female created he them.');
+INSERT INTO t1(docid,words) VALUES(1001028,'And God blessed them, and God said unto them, Be fruitful, and multiply, and replenish the earth, and subdue it: and have dominion over the fish of the sea, and over the fowl of the air, and over every living thing that moveth upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1001029,'And God said, Behold, I have given you every herb bearing seed, which is upon the face of all the earth, and every tree, in the which is the fruit of a tree yielding seed; to you it shall be for meat.');
+INSERT INTO t1(docid,words) VALUES(1001030,'And to every beast of the earth, and to every fowl of the air, and to every thing that creepeth upon the earth, wherein there is life, I have given every green herb for meat: and it was so.');
+INSERT INTO t1(docid,words) VALUES(1001031,'And God saw every thing that he had made, and, behold, it was very good. And the evening and the morning were the sixth day.');
+INSERT INTO t1(docid,words) VALUES(1002001,'Thus the heavens and the earth were finished, and all the host of them.');
+INSERT INTO t1(docid,words) VALUES(1002002,'And on the seventh day God ended his work which he had made; and he rested on the seventh day from all his work which he had made.');
+INSERT INTO t1(docid,words) VALUES(1002003,'And God blessed the seventh day, and sanctified it: because that in it he had rested from all his work which God created and made.');
+INSERT INTO t1(docid,words) VALUES(1002004,'These are the generations of the heavens and of the earth when they were created, in the day that the LORD God made the earth and the heavens,');
+INSERT INTO t1(docid,words) VALUES(1002005,'And every plant of the field before it was in the earth, and every herb of the field before it grew: for the LORD God had not caused it to rain upon the earth, and there was not a man to till the ground.');
+INSERT INTO t1(docid,words) VALUES(1002006,'But there went up a mist from the earth, and watered the whole face of the ground.');
+INSERT INTO t1(docid,words) VALUES(1002007,'And the LORD God formed man of the dust of the ground, and breathed into his nostrils the breath of life; and man became a living soul.');
+INSERT INTO t1(docid,words) VALUES(1002008,'And the LORD God planted a garden eastward in Eden; and there he put the man whom he had formed.');
+INSERT INTO t1(docid,words) VALUES(1002009,'And out of the ground made the LORD God to grow every tree that is pleasant to the sight, and good for food; the tree of life also in the midst of the garden, and the tree of knowledge of good and evil.');
+INSERT INTO t1(docid,words) VALUES(1002010,'And a river went out of Eden to water the garden; and from thence it was parted, and became into four heads.');
+INSERT INTO t1(docid,words) VALUES(1002011,'The name of the first is Pison: that is it which compasseth the whole land of Havilah, where there is gold;');
+INSERT INTO t1(docid,words) VALUES(1002012,'And the gold of that land is good: there is bdellium and the onyx stone.');
+INSERT INTO t1(docid,words) VALUES(1002013,'And the name of the second river is Gihon: the same is it that compasseth the whole land of Ethiopia.');
+INSERT INTO t1(docid,words) VALUES(1002014,'And the name of the third river is Hiddekel: that is it which goeth toward the east of Assyria. And the fourth river is Euphrates.');
+INSERT INTO t1(docid,words) VALUES(1002015,'And the LORD God took the man, and put him into the garden of Eden to dress it and to keep it.');
+INSERT INTO t1(docid,words) VALUES(1002016,'And the LORD God commanded the man, saying, Of every tree of the garden thou mayest freely eat:');
+INSERT INTO t1(docid,words) VALUES(1002017,'But of the tree of the knowledge of good and evil, thou shalt not eat of it: for in the day that thou eatest thereof thou shalt surely die.');
+INSERT INTO t1(docid,words) VALUES(1002018,'And the LORD God said, It is not good that the man should be alone; I will make him an help meet for him.');
+INSERT INTO t1(docid,words) VALUES(1002019,'And out of the ground the LORD God formed every beast of the field, and every fowl of the air; and brought them unto Adam to see what he would call them: and whatsoever Adam called every living creature, that was the name thereof.');
+INSERT INTO t1(docid,words) VALUES(1002020,'And Adam gave names to all cattle, and to the fowl of the air, and to every beast of the field; but for Adam there was not found an help meet for him.');
+INSERT INTO t1(docid,words) VALUES(1002021,'And the LORD God caused a deep sleep to fall upon Adam, and he slept: and he took one of his ribs, and closed up the flesh instead thereof;');
+INSERT INTO t1(docid,words) VALUES(1002022,'And the rib, which the LORD God had taken from man, made he a woman, and brought her unto the man.');
+INSERT INTO t1(docid,words) VALUES(1002023,'And Adam said, This is now bone of my bones, and flesh of my flesh: she shall be called Woman, because she was taken out of Man.');
+INSERT INTO t1(docid,words) VALUES(1002024,'Therefore shall a man leave his father and his mother, and shall cleave unto his wife: and they shall be one flesh.');
+INSERT INTO t1(docid,words) VALUES(1002025,'And they were both naked, the man and his wife, and were not ashamed.');
+INSERT INTO t1(docid,words) VALUES(1003001,'Now the serpent was more subtil than any beast of the field which the LORD God had made. And he said unto the woman, Yea, hath God said, Ye shall not eat of every tree of the garden?');
+INSERT INTO t1(docid,words) VALUES(1003002,'And the woman said unto the serpent, We may eat of the fruit of the trees of the garden:');
+INSERT INTO t1(docid,words) VALUES(1003003,'But of the fruit of the tree which is in the midst of the garden, God hath said, Ye shall not eat of it, neither shall ye touch it, lest ye die.');
+INSERT INTO t1(docid,words) VALUES(1003004,'And the serpent said unto the woman, Ye shall not surely die:');
+INSERT INTO t1(docid,words) VALUES(1003005,'For God doth know that in the day ye eat thereof, then your eyes shall be opened, and ye shall be as gods, knowing good and evil.');
+INSERT INTO t1(docid,words) VALUES(1003006,'And when the woman saw that the tree was good for food, and that it was pleasant to the eyes, and a tree to be desired to make one wise, she took of the fruit thereof, and did eat, and gave also unto her husband with her; and he did eat.');
+INSERT INTO t1(docid,words) VALUES(1003007,'And the eyes of them both were opened, and they knew that they were naked; and they sewed fig leaves together, and made themselves aprons.');
+INSERT INTO t1(docid,words) VALUES(1003008,'And they heard the voice of the LORD God walking in the garden in the cool of the day: and Adam and his wife hid themselves from the presence of the LORD God amongst the trees of the garden.');
+INSERT INTO t1(docid,words) VALUES(1003009,'And the LORD God called unto Adam, and said unto him, Where art thou?');
+INSERT INTO t1(docid,words) VALUES(1003010,'And he said, I heard thy voice in the garden, and I was afraid, because I was naked; and I hid myself.');
+INSERT INTO t1(docid,words) VALUES(1003011,'And he said, Who told thee that thou wast naked? Hast thou eaten of the tree, whereof I commanded thee that thou shouldest not eat?');
+INSERT INTO t1(docid,words) VALUES(1003012,'And the man said, The woman whom thou gavest to be with me, she gave me of the tree, and I did eat.');
+INSERT INTO t1(docid,words) VALUES(1003013,'And the LORD God said unto the woman, What is this that thou hast done? And the woman said, The serpent beguiled me, and I did eat.');
+INSERT INTO t1(docid,words) VALUES(1003014,'And the LORD God said unto the serpent, Because thou hast done this, thou art cursed above all cattle, and above every beast of the field; upon thy belly shalt thou go, and dust shalt thou eat all the days of thy life:');
+INSERT INTO t1(docid,words) VALUES(1003015,'And I will put enmity between thee and the woman, and between thy seed and her seed; it shall bruise thy head, and thou shalt bruise his heel.');
+INSERT INTO t1(docid,words) VALUES(1003016,'Unto the woman he said, I will greatly multiply thy sorrow and thy conception; in sorrow thou shalt bring forth children; and thy desire shall be to thy husband, and he shall rule over thee.');
+INSERT INTO t1(docid,words) VALUES(1003017,'And unto Adam he said, Because thou hast hearkened unto the voice of thy wife, and hast eaten of the tree, of which I commanded thee, saying, Thou shalt not eat of it: cursed is the ground for thy sake; in sorrow shalt thou eat of it all the days of thy life;');
+INSERT INTO t1(docid,words) VALUES(1003018,'Thorns also and thistles shall it bring forth to thee; and thou shalt eat the herb of the field;');
+INSERT INTO t1(docid,words) VALUES(1003019,'In the sweat of thy face shalt thou eat bread, till thou return unto the ground; for out of it wast thou taken: for dust thou art, and unto dust shalt thou return.');
+INSERT INTO t1(docid,words) VALUES(1003020,'And Adam called his wife''s name Eve; because she was the mother of all living.');
+INSERT INTO t1(docid,words) VALUES(1003021,'Unto Adam also and to his wife did the LORD God make coats of skins, and clothed them.');
+INSERT INTO t1(docid,words) VALUES(1003022,'And the LORD God said, Behold, the man is become as one of us, to know good and evil: and now, lest he put forth his hand, and take also of the tree of life, and eat, and live for ever:');
+INSERT INTO t1(docid,words) VALUES(1003023,'Therefore the LORD God sent him forth from the garden of Eden, to till the ground from whence he was taken.');
+INSERT INTO t1(docid,words) VALUES(1003024,'So he drove out the man; and he placed at the east of the garden of Eden Cherubims, and a flaming sword which turned every way, to keep the way of the tree of life.');
+INSERT INTO t1(docid,words) VALUES(1004001,'And Adam knew Eve his wife; and she conceived, and bare Cain, and said, I have gotten a man from the LORD.');
+INSERT INTO t1(docid,words) VALUES(1004002,'And she again bare his brother Abel. And Abel was a keeper of sheep, but Cain was a tiller of the ground.');
+INSERT INTO t1(docid,words) VALUES(1004003,'And in process of time it came to pass, that Cain brought of the fruit of the ground an offering unto the LORD.');
+INSERT INTO t1(docid,words) VALUES(1004004,'And Abel, he also brought of the firstlings of his flock and of the fat thereof. And the LORD had respect unto Abel and to his offering:');
+INSERT INTO t1(docid,words) VALUES(1004005,'But unto Cain and to his offering he had not respect. And Cain was very wroth, and his countenance fell.');
+INSERT INTO t1(docid,words) VALUES(1004006,'And the LORD said unto Cain, Why art thou wroth? and why is thy countenance fallen?');
+INSERT INTO t1(docid,words) VALUES(1004007,'If thou doest well, shalt thou not be accepted? and if thou doest not well, sin lieth at the door. And unto thee shall be his desire, and thou shalt rule over him.');
+INSERT INTO t1(docid,words) VALUES(1004008,'And Cain talked with Abel his brother: and it came to pass, when they were in the field, that Cain rose up against Abel his brother, and slew him.');
+INSERT INTO t1(docid,words) VALUES(1004009,'And the LORD said unto Cain, Where is Abel thy brother? And he said, I know not: Am I my brother''s keeper?');
+INSERT INTO t1(docid,words) VALUES(1004010,'And he said, What hast thou done? the voice of thy brother''s blood crieth unto me from the ground.');
+INSERT INTO t1(docid,words) VALUES(1004011,'And now art thou cursed from the earth, which hath opened her mouth to receive thy brother''s blood from thy hand;');
+INSERT INTO t1(docid,words) VALUES(1004012,'When thou tillest the ground, it shall not henceforth yield unto thee her strength; a fugitive and a vagabond shalt thou be in the earth.');
+INSERT INTO t1(docid,words) VALUES(1004013,'And Cain said unto the LORD, My punishment is greater than I can bear.');
+INSERT INTO t1(docid,words) VALUES(1004014,'Behold, thou hast driven me out this day from the face of the earth; and from thy face shall I be hid; and I shall be a fugitive and a vagabond in the earth; and it shall come to pass, that every one that findeth me shall slay me.');
+INSERT INTO t1(docid,words) VALUES(1004015,'And the LORD said unto him, Therefore whosoever slayeth Cain, vengeance shall be taken on him sevenfold. And the LORD set a mark upon Cain, lest any finding him should kill him.');
+INSERT INTO t1(docid,words) VALUES(1004016,'And Cain went out from the presence of the LORD, and dwelt in the land of Nod, on the east of Eden.');
+INSERT INTO t1(docid,words) VALUES(1004017,'And Cain knew his wife; and she conceived, and bare Enoch: and he builded a city, and called the name of the city, after the name of his son, Enoch.');
+INSERT INTO t1(docid,words) VALUES(1004018,'And unto Enoch was born Irad: and Irad begat Mehujael: and Mehujael begat Methusael: and Methusael begat Lamech.');
+INSERT INTO t1(docid,words) VALUES(1004019,'And Lamech took unto him two wives: the name of the one was Adah, and the name of the other Zillah.');
+INSERT INTO t1(docid,words) VALUES(1004020,'And Adah bare Jabal: he was the father of such as dwell in tents, and of such as have cattle.');
+INSERT INTO t1(docid,words) VALUES(1004021,'And his brother''s name was Jubal: he was the father of all such as handle the harp and organ.');
+INSERT INTO t1(docid,words) VALUES(1004022,'And Zillah, she also bare Tubalcain, an instructer of every artificer in brass and iron: and the sister of Tubalcain was Naamah.');
+INSERT INTO t1(docid,words) VALUES(1004023,'And Lamech said unto his wives, Adah and Zillah, Hear my voice; ye wives of Lamech, hearken unto my speech: for I have slain a man to my wounding, and a young man to my hurt.');
+INSERT INTO t1(docid,words) VALUES(1004024,'If Cain shall be avenged sevenfold, truly Lamech seventy and sevenfold.');
+INSERT INTO t1(docid,words) VALUES(1004025,'And Adam knew his wife again; and she bare a son, and called his name Seth: For God, said she, hath appointed me another seed instead of Abel, whom Cain slew.');
+INSERT INTO t1(docid,words) VALUES(1004026,'And to Seth, to him also there was born a son; and he called his name Enos: then began men to call upon the name of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1005001,'This is the book of the generations of Adam. In the day that God created man, in the likeness of God made he him;');
+INSERT INTO t1(docid,words) VALUES(1005002,'Male and female created he them; and blessed them, and called their name Adam, in the day when they were created.');
+INSERT INTO t1(docid,words) VALUES(1005003,'And Adam lived an hundred and thirty years, and begat a son in his own likeness, and after his image; and called his name Seth:');
+INSERT INTO t1(docid,words) VALUES(1005004,'And the days of Adam after he had begotten Seth were eight hundred years: and he begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005005,'And all the days that Adam lived were nine hundred and thirty years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005006,'And Seth lived an hundred and five years, and begat Enos:');
+INSERT INTO t1(docid,words) VALUES(1005007,'And Seth lived after he begat Enos eight hundred and seven years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005008,'And all the days of Seth were nine hundred and twelve years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005009,'And Enos lived ninety years, and begat Cainan:');
+INSERT INTO t1(docid,words) VALUES(1005010,'And Enos lived after he begat Cainan eight hundred and fifteen years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005011,'And all the days of Enos were nine hundred and five years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005012,'And Cainan lived seventy years and begat Mahalaleel:');
+INSERT INTO t1(docid,words) VALUES(1005013,'And Cainan lived after he begat Mahalaleel eight hundred and forty years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005014,'And all the days of Cainan were nine hundred and ten years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005015,'And Mahalaleel lived sixty and five years, and begat Jared:');
+INSERT INTO t1(docid,words) VALUES(1005016,'And Mahalaleel lived after he begat Jared eight hundred and thirty years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005017,'And all the days of Mahalaleel were eight hundred ninety and five years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005018,'And Jared lived an hundred sixty and two years, and he begat Enoch:');
+INSERT INTO t1(docid,words) VALUES(1005019,'And Jared lived after he begat Enoch eight hundred years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005020,'And all the days of Jared were nine hundred sixty and two years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005021,'And Enoch lived sixty and five years, and begat Methuselah:');
+INSERT INTO t1(docid,words) VALUES(1005022,'And Enoch walked with God after he begat Methuselah three hundred years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005023,'And all the days of Enoch were three hundred sixty and five years:');
+INSERT INTO t1(docid,words) VALUES(1005024,'And Enoch walked with God: and he was not; for God took him.');
+INSERT INTO t1(docid,words) VALUES(1005025,'And Methuselah lived an hundred eighty and seven years, and begat Lamech.');
+INSERT INTO t1(docid,words) VALUES(1005026,'And Methuselah lived after he begat Lamech seven hundred eighty and two years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005027,'And all the days of Methuselah were nine hundred sixty and nine years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005028,'And Lamech lived an hundred eighty and two years, and begat a son:');
+INSERT INTO t1(docid,words) VALUES(1005029,'And he called his name Noah, saying, This same shall comfort us concerning our work and toil of our hands, because of the ground which the LORD hath cursed.');
+INSERT INTO t1(docid,words) VALUES(1005030,'And Lamech lived after he begat Noah five hundred ninety and five years, and begat sons and daughters:');
+INSERT INTO t1(docid,words) VALUES(1005031,'And all the days of Lamech were seven hundred seventy and seven years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1005032,'And Noah was five hundred years old: and Noah begat Shem, Ham, and Japheth.');
+INSERT INTO t1(docid,words) VALUES(1006001,'And it came to pass, when men began to multiply on the face of the earth, and daughters were born unto them,');
+INSERT INTO t1(docid,words) VALUES(1006002,'That the sons of God saw the daughters of men that they were fair; and they took them wives of all which they chose.');
+INSERT INTO t1(docid,words) VALUES(1006003,'And the LORD said, My spirit shall not always strive with man, for that he also is flesh: yet his days shall be an hundred and twenty years.');
+INSERT INTO t1(docid,words) VALUES(1006004,'There were giants in the earth in those days; and also after that, when the sons of God came in unto the daughters of men, and they bare children to them, the same became mighty men which were of old, men of renown.');
+INSERT INTO t1(docid,words) VALUES(1006005,'And God saw that the wickedness of man was great in the earth, and that every imagination of the thoughts of his heart was only evil continually.');
+INSERT INTO t1(docid,words) VALUES(1006006,'And it repented the LORD that he had made man on the earth, and it grieved him at his heart.');
+INSERT INTO t1(docid,words) VALUES(1006007,'And the LORD said, I will destroy man whom I have created from the face of the earth; both man, and beast, and the creeping thing, and the fowls of the air; for it repenteth me that I have made them.');
+INSERT INTO t1(docid,words) VALUES(1006008,'But Noah found grace in the eyes of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1006009,'These are the generations of Noah: Noah was a just man and perfect in his generations, and Noah walked with God.');
+INSERT INTO t1(docid,words) VALUES(1006010,'And Noah begat three sons, Shem, Ham, and Japheth.');
+INSERT INTO t1(docid,words) VALUES(1006011,'The earth also was corrupt before God, and the earth was filled with violence.');
+INSERT INTO t1(docid,words) VALUES(1006012,'And God looked upon the earth, and, behold, it was corrupt; for all flesh had corrupted his way upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1006013,'And God said unto Noah, The end of all flesh is come before me; for the earth is filled with violence through them; and, behold, I will destroy them with the earth.');
+INSERT INTO t1(docid,words) VALUES(1006014,'Make thee an ark of gopher wood; rooms shalt thou make in the ark, and shalt pitch it within and without with pitch.');
+INSERT INTO t1(docid,words) VALUES(1006015,'And this is the fashion which thou shalt make it of: The length of the ark shall be three hundred cubits, the breadth of it fifty cubits, and the height of it thirty cubits.');
+INSERT INTO t1(docid,words) VALUES(1006016,'A window shalt thou make to the ark, and in a cubit shalt thou finish it above; and the door of the ark shalt thou set in the side thereof; with lower, second, and third stories shalt thou make it.');
+INSERT INTO t1(docid,words) VALUES(1006017,'And, behold, I, even I, do bring a flood of waters upon the earth, to destroy all flesh, wherein is the breath of life, from under heaven; and every thing that is in the earth shall die.');
+INSERT INTO t1(docid,words) VALUES(1006018,'But with thee will I establish my covenant; and thou shalt come into the ark, thou, and thy sons, and thy wife, and thy sons'' wives with thee.');
+INSERT INTO t1(docid,words) VALUES(1006019,'And of every living thing of all flesh, two of every sort shalt thou bring into the ark, to keep them alive with thee; they shall be male and female.');
+INSERT INTO t1(docid,words) VALUES(1006020,'Of fowls after their kind, and of cattle after their kind, of every creeping thing of the earth after his kind, two of every sort shall come unto thee, to keep them alive.');
+INSERT INTO t1(docid,words) VALUES(1006021,'And take thou unto thee of all food that is eaten, and thou shalt gather it to thee; and it shall be for food for thee, and for them.');
+INSERT INTO t1(docid,words) VALUES(1006022,'Thus did Noah; according to all that God commanded him, so did he.');
+INSERT INTO t1(docid,words) VALUES(1007001,'And the LORD said unto Noah, Come thou and all thy house into the ark; for thee have I seen righteous before me in this generation.');
+INSERT INTO t1(docid,words) VALUES(1007002,'Of every clean beast thou shalt take to thee by sevens, the male and his female: and of beasts that are not clean by two, the male and his female.');
+INSERT INTO t1(docid,words) VALUES(1007003,'Of fowls also of the air by sevens, the male and the female; to keep seed alive upon the face of all the earth.');
+INSERT INTO t1(docid,words) VALUES(1007004,'For yet seven days, and I will cause it to rain upon the earth forty days and forty nights; and every living substance that I have made will I destroy from off the face of the earth.');
+INSERT INTO t1(docid,words) VALUES(1007005,'And Noah did according unto all that the LORD commanded him.');
+INSERT INTO t1(docid,words) VALUES(1007006,'And Noah was six hundred years old when the flood of waters was upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1007007,'And Noah went in, and his sons, and his wife, and his sons'' wives with him, into the ark, because of the waters of the flood.');
+INSERT INTO t1(docid,words) VALUES(1007008,'Of clean beasts, and of beasts that are not clean, and of fowls, and of every thing that creepeth upon the earth,');
+INSERT INTO t1(docid,words) VALUES(1007009,'There went in two and two unto Noah into the ark, the male and the female, as God had commanded Noah.');
+INSERT INTO t1(docid,words) VALUES(1007010,'And it came to pass after seven days, that the waters of the flood were upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1007011,'In the six hundredth year of Noah''s life, in the second month, the seventeenth day of the month, the same day were all the fountains of the great deep broken up, and the windows of heaven were opened.');
+INSERT INTO t1(docid,words) VALUES(1007012,'And the rain was upon the earth forty days and forty nights.');
+INSERT INTO t1(docid,words) VALUES(1007013,'In the selfsame day entered Noah, and Shem, and Ham, and Japheth, the sons of Noah, and Noah''s wife, and the three wives of his sons with them, into the ark;');
+INSERT INTO t1(docid,words) VALUES(1007014,'They, and every beast after his kind, and all the cattle after their kind, and every creeping thing that creepeth upon the earth after his kind, and every fowl after his kind, every bird of every sort.');
+INSERT INTO t1(docid,words) VALUES(1007015,'And they went in unto Noah into the ark, two and two of all flesh, wherein is the breath of life.');
+INSERT INTO t1(docid,words) VALUES(1007016,'And they that went in, went in male and female of all flesh, as God had commanded him: and the LORD shut him in.');
+INSERT INTO t1(docid,words) VALUES(1007017,'And the flood was forty days upon the earth; and the waters increased, and bare up the ark, and it was lift up above the earth.');
+INSERT INTO t1(docid,words) VALUES(1007018,'And the waters prevailed, and were increased greatly upon the earth; and the ark went upon the face of the waters.');
+INSERT INTO t1(docid,words) VALUES(1007019,'And the waters prevailed exceedingly upon the earth; and all the high hills, that were under the whole heaven, were covered.');
+INSERT INTO t1(docid,words) VALUES(1007020,'Fifteen cubits upward did the waters prevail; and the mountains were covered.');
+INSERT INTO t1(docid,words) VALUES(1007021,'And all flesh died that moved upon the earth, both of fowl, and of cattle, and of beast, and of every creeping thing that creepeth upon the earth, and every man:');
+INSERT INTO t1(docid,words) VALUES(1007022,'All in whose nostrils was the breath of life, of all that was in the dry land, died.');
+INSERT INTO t1(docid,words) VALUES(1007023,'And every living substance was destroyed which was upon the face of the ground, both man, and cattle, and the creeping things, and the fowl of the heaven; and they were destroyed from the earth: and Noah only remained alive, and they that were with him in the ark.');
+INSERT INTO t1(docid,words) VALUES(1007024,'And the waters prevailed upon the earth an hundred and fifty days.');
+INSERT INTO t1(docid,words) VALUES(1008001,'And God remembered Noah, and every living thing, and all the cattle that was with him in the ark: and God made a wind to pass over the earth, and the waters asswaged;');
+INSERT INTO t1(docid,words) VALUES(1008002,'The fountains also of the deep and the windows of heaven were stopped, and the rain from heaven was restrained;');
+INSERT INTO t1(docid,words) VALUES(1008003,'And the waters returned from off the earth continually: and after the end of the hundred and fifty days the waters were abated.');
+INSERT INTO t1(docid,words) VALUES(1008004,'And the ark rested in the seventh month, on the seventeenth day of the month, upon the mountains of Ararat.');
+INSERT INTO t1(docid,words) VALUES(1008005,'And the waters decreased continually until the tenth month: in the tenth month, on the first day of the month, were the tops of the mountains seen.');
+INSERT INTO t1(docid,words) VALUES(1008006,'And it came to pass at the end of forty days, that Noah opened the window of the ark which he had made:');
+INSERT INTO t1(docid,words) VALUES(1008007,'And he sent forth a raven, which went forth to and fro, until the waters were dried up from off the earth.');
+INSERT INTO t1(docid,words) VALUES(1008008,'Also he sent forth a dove from him, to see if the waters were abated from off the face of the ground;');
+INSERT INTO t1(docid,words) VALUES(1008009,'But the dove found no rest for the sole of her foot, and she returned unto him into the ark, for the waters were on the face of the whole earth: then he put forth his hand, and took her, and pulled her in unto him into the ark.');
+INSERT INTO t1(docid,words) VALUES(1008010,'And he stayed yet other seven days; and again he sent forth the dove out of the ark;');
+INSERT INTO t1(docid,words) VALUES(1008011,'And the dove came in to him in the evening; and, lo, in her mouth was an olive leaf pluckt off: so Noah knew that the waters were abated from off the earth.');
+INSERT INTO t1(docid,words) VALUES(1008012,'And he stayed yet other seven days; and sent forth the dove; which returned not again unto him any more.');
+INSERT INTO t1(docid,words) VALUES(1008013,'And it came to pass in the six hundredth and first year, in the first month, the first day of the month, the waters were dried up from off the earth: and Noah removed the covering of the ark, and looked, and, behold, the face of the ground was dry.');
+INSERT INTO t1(docid,words) VALUES(1008014,'And in the second month, on the seven and twentieth day of the month, was the earth dried.');
+INSERT INTO t1(docid,words) VALUES(1008015,'And God spake unto Noah, saying,');
+INSERT INTO t1(docid,words) VALUES(1008016,'Go forth of the ark, thou, and thy wife, and thy sons, and thy sons'' wives with thee.');
+INSERT INTO t1(docid,words) VALUES(1008017,'Bring forth with thee every living thing that is with thee, of all flesh, both of fowl, and of cattle, and of every creeping thing that creepeth upon the earth; that they may breed abundantly in the earth, and be fruitful, and multiply upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1008018,'And Noah went forth, and his sons, and his wife, and his sons'' wives with him:');
+INSERT INTO t1(docid,words) VALUES(1008019,'Every beast, every creeping thing, and every fowl, and whatsoever creepeth upon the earth, after their kinds, went forth out of the ark.');
+INSERT INTO t1(docid,words) VALUES(1008020,'And Noah builded an altar unto the LORD; and took of every clean beast, and of every clean fowl, and offered burnt offerings on the altar.');
+INSERT INTO t1(docid,words) VALUES(1008021,'And the LORD smelled a sweet savour; and the LORD said in his heart, I will not again curse the ground any more for man''s sake; for the imagination of man''s heart is evil from his youth; neither will I again smite any more every thing living, as I have done.');
+INSERT INTO t1(docid,words) VALUES(1008022,'While the earth remaineth, seedtime and harvest, and cold and heat, and summer and winter, and day and night shall not cease.');
+INSERT INTO t1(docid,words) VALUES(1009001,'And God blessed Noah and his sons, and said unto them, Be fruitful, and multiply, and replenish the earth.');
+INSERT INTO t1(docid,words) VALUES(1009002,'And the fear of you and the dread of you shall be upon every beast of the earth, and upon every fowl of the air, upon all that moveth upon the earth, and upon all the fishes of the sea; into your hand are they delivered.');
+INSERT INTO t1(docid,words) VALUES(1009003,'Every moving thing that liveth shall be meat for you; even as the green herb have I given you all things.');
+INSERT INTO t1(docid,words) VALUES(1009004,'But flesh with the life thereof, which is the blood thereof, shall ye not eat.');
+INSERT INTO t1(docid,words) VALUES(1009005,'And surely your blood of your lives will I require; at the hand of every beast will I require it, and at the hand of man; at the hand of every man''s brother will I require the life of man.');
+INSERT INTO t1(docid,words) VALUES(1009006,'Whoso sheddeth man''s blood, by man shall his blood be shed: for in the image of God made he man.');
+INSERT INTO t1(docid,words) VALUES(1009007,'And you, be ye fruitful, and multiply; bring forth abundantly in the earth, and multiply therein.');
+INSERT INTO t1(docid,words) VALUES(1009008,'And God spake unto Noah, and to his sons with him, saying,');
+INSERT INTO t1(docid,words) VALUES(1009009,'And I, behold, I establish my covenant with you, and with your seed after you;');
+INSERT INTO t1(docid,words) VALUES(1009010,'And with every living creature that is with you, of the fowl, of the cattle, and of every beast of the earth with you; from all that go out of the ark, to every beast of the earth.');
+INSERT INTO t1(docid,words) VALUES(1009011,'And I will establish my covenant with you, neither shall all flesh be cut off any more by the waters of a flood; neither shall there any more be a flood to destroy the earth.');
+INSERT INTO t1(docid,words) VALUES(1009012,'And God said, This is the token of the covenant which I make between me and you and every living creature that is with you, for perpetual generations:');
+INSERT INTO t1(docid,words) VALUES(1009013,'I do set my bow in the cloud, and it shall be for a token of a covenant between me and the earth.');
+INSERT INTO t1(docid,words) VALUES(1009014,'And it shall come to pass, when I bring a cloud over the earth, that the bow shall be seen in the cloud:');
+INSERT INTO t1(docid,words) VALUES(1009015,'And I will remember my covenant, which is between me and you and every living creature of all flesh; and the waters shall no more become a flood to destroy all flesh.');
+INSERT INTO t1(docid,words) VALUES(1009016,'And the bow shall be in the cloud; and I will look upon it, that I may remember the everlasting covenant between God and every living creature of all flesh that is upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1009017,'And God said unto Noah, This is the token of the covenant, which I have established between me and all flesh that is upon the earth.');
+INSERT INTO t1(docid,words) VALUES(1009018,'And the sons of Noah, that went forth of the ark, were Shem, and Ham, and Japheth: and Ham is the father of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1009019,'These are the three sons of Noah: and of them was the whole earth overspread.');
+INSERT INTO t1(docid,words) VALUES(1009020,'And Noah began to be an husbandman, and he planted a vineyard:');
+INSERT INTO t1(docid,words) VALUES(1009021,'And he drank of the wine, and was drunken; and he was uncovered within his tent.');
+INSERT INTO t1(docid,words) VALUES(1009022,'And Ham, the father of Canaan, saw the nakedness of his father, and told his two brethren without.');
+INSERT INTO t1(docid,words) VALUES(1009023,'And Shem and Japheth took a garment, and laid it upon both their shoulders, and went backward, and covered the nakedness of their father; and their faces were backward, and they saw not their father''s nakedness.');
+INSERT INTO t1(docid,words) VALUES(1009024,'And Noah awoke from his wine, and knew what his younger son had done unto him.');
+INSERT INTO t1(docid,words) VALUES(1009025,'And he said, Cursed be Canaan; a servant of servants shall he be unto his brethren.');
+INSERT INTO t1(docid,words) VALUES(1009026,'And he said, Blessed be the LORD God of Shem; and Canaan shall be his servant.');
+INSERT INTO t1(docid,words) VALUES(1009027,'God shall enlarge Japheth, and he shall dwell in the tents of Shem; and Canaan shall be his servant.');
+INSERT INTO t1(docid,words) VALUES(1009028,'And Noah lived after the flood three hundred and fifty years.');
+INSERT INTO t1(docid,words) VALUES(1009029,'And all the days of Noah were nine hundred and fifty years: and he died.');
+INSERT INTO t1(docid,words) VALUES(1010001,'Now these are the generations of the sons of Noah, Shem, Ham, and Japheth: and unto them were sons born after the flood.');
+INSERT INTO t1(docid,words) VALUES(1010002,'The sons of Japheth; Gomer, and Magog, and Madai, and Javan, and Tubal, and Meshech, and Tiras.');
+INSERT INTO t1(docid,words) VALUES(1010003,'And the sons of Gomer; Ashkenaz, and Riphath, and Togarmah.');
+INSERT INTO t1(docid,words) VALUES(1010004,'And the sons of Javan; Elishah, and Tarshish, Kittim, and Dodanim.');
+INSERT INTO t1(docid,words) VALUES(1010005,'By these were the isles of the Gentiles divided in their lands; every one after his tongue, after their families, in their nations.');
+INSERT INTO t1(docid,words) VALUES(1010006,'And the sons of Ham; Cush, and Mizraim, and Phut, and Canaan.');
+INSERT INTO t1(docid,words) VALUES(1010007,'And the sons of Cush; Seba, and Havilah, and Sabtah, and Raamah, and Sabtechah: and the sons of Raamah; Sheba, and Dedan.');
+INSERT INTO t1(docid,words) VALUES(1010008,'And Cush begat Nimrod: he began to be a mighty one in the earth.');
+INSERT INTO t1(docid,words) VALUES(1010009,'He was a mighty hunter before the LORD: wherefore it is said, Even as Nimrod the mighty hunter before the LORD.');
+INSERT INTO t1(docid,words) VALUES(1010010,'And the beginning of his kingdom was Babel, and Erech, and Accad, and Calneh, in the land of Shinar.');
+INSERT INTO t1(docid,words) VALUES(1010011,'Out of that land went forth Asshur, and builded Nineveh, and the city Rehoboth, and Calah,');
+INSERT INTO t1(docid,words) VALUES(1010012,'And Resen between Nineveh and Calah: the same is a great city.');
+INSERT INTO t1(docid,words) VALUES(1010013,'And Mizraim begat Ludim, and Anamim, and Lehabim, and Naphtuhim,');
+INSERT INTO t1(docid,words) VALUES(1010014,'And Pathrusim, and Casluhim, (out of whom came Philistim,) and Caphtorim.');
+INSERT INTO t1(docid,words) VALUES(1010015,'And Canaan begat Sidon his first born, and Heth,');
+INSERT INTO t1(docid,words) VALUES(1010016,'And the Jebusite, and the Amorite, and the Girgasite,');
+INSERT INTO t1(docid,words) VALUES(1010017,'And the Hivite, and the Arkite, and the Sinite,');
+INSERT INTO t1(docid,words) VALUES(1010018,'And the Arvadite, and the Zemarite, and the Hamathite: and afterward were the families of the Canaanites spread abroad.');
+INSERT INTO t1(docid,words) VALUES(1010019,'And the border of the Canaanites was from Sidon, as thou comest to Gerar, unto Gaza; as thou goest, unto Sodom, and Gomorrah, and Admah, and Zeboim, even unto Lasha.');
+INSERT INTO t1(docid,words) VALUES(1010020,'These are the sons of Ham, after their families, after their tongues, in their countries, and in their nations.');
+INSERT INTO t1(docid,words) VALUES(1010021,'Unto Shem also, the father of all the children of Eber, the brother of Japheth the elder, even to him were children born.');
+INSERT INTO t1(docid,words) VALUES(1010022,'The children of Shem; Elam, and Asshur, and Arphaxad, and Lud, and Aram.');
+INSERT INTO t1(docid,words) VALUES(1010023,'And the children of Aram; Uz, and Hul, and Gether, and Mash.');
+INSERT INTO t1(docid,words) VALUES(1010024,'And Arphaxad begat Salah; and Salah begat Eber.');
+INSERT INTO t1(docid,words) VALUES(1010025,'And unto Eber were born two sons: the name of one was Peleg; for in his days was the earth divided; and his brother''s name was Joktan.');
+INSERT INTO t1(docid,words) VALUES(1010026,'And Joktan begat Almodad, and Sheleph, and Hazarmaveth, and Jerah,');
+INSERT INTO t1(docid,words) VALUES(1010027,'And Hadoram, and Uzal, and Diklah,');
+INSERT INTO t1(docid,words) VALUES(1010028,'And Obal, and Abimael, and Sheba,');
+INSERT INTO t1(docid,words) VALUES(1010029,'And Ophir, and Havilah, and Jobab: all these were the sons of Joktan.');
+INSERT INTO t1(docid,words) VALUES(1010030,'And their dwelling was from Mesha, as thou goest unto Sephar a mount of the east.');
+INSERT INTO t1(docid,words) VALUES(1010031,'These are the sons of Shem, after their families, after their tongues, in their lands, after their nations.');
+INSERT INTO t1(docid,words) VALUES(1010032,'These are the families of the sons of Noah, after their generations, in their nations: and by these were the nations divided in the earth after the flood.');
+INSERT INTO t1(docid,words) VALUES(1011001,'And the whole earth was of one language, and of one speech.');
+INSERT INTO t1(docid,words) VALUES(1011002,'And it came to pass, as they journeyed from the east, that they found a plain in the land of Shinar; and they dwelt there.');
+INSERT INTO t1(docid,words) VALUES(1011003,'And they said one to another, Go to, let us make brick, and burn them thoroughly. And they had brick for stone, and slime had they for morter.');
+INSERT INTO t1(docid,words) VALUES(1011004,'And they said, Go to, let us build us a city and a tower, whose top may reach unto heaven; and let us make us a name, lest we be scattered abroad upon the face of the whole earth.');
+INSERT INTO t1(docid,words) VALUES(1011005,'And the LORD came down to see the city and the tower, which the children of men builded.');
+INSERT INTO t1(docid,words) VALUES(1011006,'And the LORD said, Behold, the people is one, and they have all one language; and this they begin to do: and now nothing will be restrained from them, which they have imagined to do.');
+INSERT INTO t1(docid,words) VALUES(1011007,'Go to, let us go down, and there confound their language, that they may not understand one another''s speech.');
+INSERT INTO t1(docid,words) VALUES(1011008,'So the LORD scattered them abroad from thence upon the face of all the earth: and they left off to build the city.');
+INSERT INTO t1(docid,words) VALUES(1011009,'Therefore is the name of it called Babel; because the LORD did there confound the language of all the earth: and from thence did the LORD scatter them abroad upon the face of all the earth.');
+INSERT INTO t1(docid,words) VALUES(1011010,'These are the generations of Shem: Shem was an hundred years old, and begat Arphaxad two years after the flood:');
+INSERT INTO t1(docid,words) VALUES(1011011,'And Shem lived after he begat Arphaxad five hundred years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011012,'And Arphaxad lived five and thirty years, and begat Salah:');
+INSERT INTO t1(docid,words) VALUES(1011013,'And Arphaxad lived after he begat Salah four hundred and three years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011014,'And Salah lived thirty years, and begat Eber:');
+INSERT INTO t1(docid,words) VALUES(1011015,'And Salah lived after he begat Eber four hundred and three years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011016,'And Eber lived four and thirty years, and begat Peleg:');
+INSERT INTO t1(docid,words) VALUES(1011017,'And Eber lived after he begat Peleg four hundred and thirty years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011018,'And Peleg lived thirty years, and begat Reu:');
+INSERT INTO t1(docid,words) VALUES(1011019,'And Peleg lived after he begat Reu two hundred and nine years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011020,'And Reu lived two and thirty years, and begat Serug:');
+INSERT INTO t1(docid,words) VALUES(1011021,'And Reu lived after he begat Serug two hundred and seven years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011022,'And Serug lived thirty years, and begat Nahor:');
+INSERT INTO t1(docid,words) VALUES(1011023,'And Serug lived after he begat Nahor two hundred years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011024,'And Nahor lived nine and twenty years, and begat Terah:');
+INSERT INTO t1(docid,words) VALUES(1011025,'And Nahor lived after he begat Terah an hundred and nineteen years, and begat sons and daughters.');
+INSERT INTO t1(docid,words) VALUES(1011026,'And Terah lived seventy years, and begat Abram, Nahor, and Haran.');
+INSERT INTO t1(docid,words) VALUES(1011027,'Now these are the generations of Terah: Terah begat Abram, Nahor, and Haran; and Haran begat Lot.');
+INSERT INTO t1(docid,words) VALUES(1011028,'And Haran died before his father Terah in the land of his nativity, in Ur of the Chaldees.');
+INSERT INTO t1(docid,words) VALUES(1011029,'And Abram and Nahor took them wives: the name of Abram''s wife was Sarai; and the name of Nahor''s wife, Milcah, the daughter of Haran, the father of Milcah, and the father of Iscah.');
+INSERT INTO t1(docid,words) VALUES(1011030,'But Sarai was barren; she had no child.');
+INSERT INTO t1(docid,words) VALUES(1011031,'And Terah took Abram his son, and Lot the son of Haran his son''s son, and Sarai his daughter in law, his son Abram''s wife; and they went forth with them from Ur of the Chaldees, to go into the land of Canaan; and they came unto Haran, and dwelt there.');
+INSERT INTO t1(docid,words) VALUES(1011032,'And the days of Terah were two hundred and five years: and Terah died in Haran.');
+INSERT INTO t1(docid,words) VALUES(1012001,'Now the LORD had said unto Abram, Get thee out of thy country, and from thy kindred, and from thy father''s house, unto a land that I will shew thee:');
+INSERT INTO t1(docid,words) VALUES(1012002,'And I will make of thee a great nation, and I will bless thee, and make thy name great; and thou shalt be a blessing:');
+INSERT INTO t1(docid,words) VALUES(1012003,'And I will bless them that bless thee, and curse him that curseth thee: and in thee shall all families of the earth be blessed.');
+INSERT INTO t1(docid,words) VALUES(1012004,'So Abram departed, as the LORD had spoken unto him; and Lot went with him: and Abram was seventy and five years old when he departed out of Haran.');
+INSERT INTO t1(docid,words) VALUES(1012005,'And Abram took Sarai his wife, and Lot his brother''s son, and all their substance that they had gathered, and the souls that they had gotten in Haran; and they went forth to go into the land of Canaan; and into the land of Canaan they came.');
+INSERT INTO t1(docid,words) VALUES(1012006,'And Abram passed through the land unto the place of Sichem, unto the plain of Moreh. And the Canaanite was then in the land.');
+INSERT INTO t1(docid,words) VALUES(1012007,'And the LORD appeared unto Abram, and said, Unto thy seed will I give this land: and there builded he an altar unto the LORD, who appeared unto him.');
+INSERT INTO t1(docid,words) VALUES(1012008,'And he removed from thence unto a mountain on the east of Bethel, and pitched his tent, having Bethel on the west, and Hai on the east: and there he builded an altar unto the LORD, and called upon the name of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1012009,'And Abram journeyed, going on still toward the south.');
+INSERT INTO t1(docid,words) VALUES(1012010,'And there was a famine in the land: and Abram went down into Egypt to sojourn there; for the famine was grievous in the land.');
+INSERT INTO t1(docid,words) VALUES(1012011,'And it came to pass, when he was come near to enter into Egypt, that he said unto Sarai his wife, Behold now, I know that thou art a fair woman to look upon:');
+INSERT INTO t1(docid,words) VALUES(1012012,'Therefore it shall come to pass, when the Egyptians shall see thee, that they shall say, This is his wife: and they will kill me, but they will save thee alive.');
+INSERT INTO t1(docid,words) VALUES(1012013,'Say, I pray thee, thou art my sister: that it may be well with me for thy sake; and my soul shall live because of thee.');
+INSERT INTO t1(docid,words) VALUES(1012014,'And it came to pass, that, when Abram was come into Egypt, the Egyptians beheld the woman that she was very fair.');
+INSERT INTO t1(docid,words) VALUES(1012015,'The princes also of Pharaoh saw her, and commended her before Pharaoh: and the woman was taken into Pharaoh''s house.');
+INSERT INTO t1(docid,words) VALUES(1012016,'And he entreated Abram well for her sake: and he had sheep, and oxen, and he asses, and menservants, and maidservants, and she asses, and camels.');
+INSERT INTO t1(docid,words) VALUES(1012017,'And the LORD plagued Pharaoh and his house with great plagues because of Sarai Abram''s wife.');
+INSERT INTO t1(docid,words) VALUES(1012018,'And Pharaoh called Abram and said, What is this that thou hast done unto me? why didst thou not tell me that she was thy wife?');
+INSERT INTO t1(docid,words) VALUES(1012019,'Why saidst thou, She is my sister? so I might have taken her to me to wife: now therefore behold thy wife, take her, and go thy way.');
+INSERT INTO t1(docid,words) VALUES(1012020,'And Pharaoh commanded his men concerning him: and they sent him away, and his wife, and all that he had.');
+INSERT INTO t1(docid,words) VALUES(1013001,'And Abram went up out of Egypt, he, and his wife, and all that he had, and Lot with him, into the south.');
+INSERT INTO t1(docid,words) VALUES(1013002,'And Abram was very rich in cattle, in silver, and in gold.');
+INSERT INTO t1(docid,words) VALUES(1013003,'And he went on his journeys from the south even to Bethel, unto the place where his tent had been at the beginning, between Bethel and Hai;');
+INSERT INTO t1(docid,words) VALUES(1013004,'Unto the place of the altar, which he had make there at the first: and there Abram called on the name of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1013005,'And Lot also, which went with Abram, had flocks, and herds, and tents.');
+INSERT INTO t1(docid,words) VALUES(1013006,'And the land was not able to bear them, that they might dwell together: for their substance was great, so that they could not dwell together.');
+INSERT INTO t1(docid,words) VALUES(1013007,'And there was a strife between the herdmen of Abram''s cattle and the herdmen of Lot''s cattle: and the Canaanite and the Perizzite dwelled then in the land.');
+INSERT INTO t1(docid,words) VALUES(1013008,'And Abram said unto Lot, Let there be no strife, I pray thee, between me and thee, and between my herdmen and thy herdmen; for we be brethren.');
+INSERT INTO t1(docid,words) VALUES(1013009,'Is not the whole land before thee? separate thyself, I pray thee, from me: if thou wilt take the left hand, then I will go to the right; or if thou depart to the right hand, then I will go to the left.');
+INSERT INTO t1(docid,words) VALUES(1013010,'And Lot lifted up his eyes, and beheld all the plain of Jordan, that it was well watered every where, before the LORD destroyed Sodom and Gomorrah, even as the garden of the LORD, like the land of Egypt, as thou comest unto Zoar.');
+INSERT INTO t1(docid,words) VALUES(1013011,'Then Lot chose him all the plain of Jordan; and Lot journeyed east: and they separated themselves the one from the other.');
+INSERT INTO t1(docid,words) VALUES(1013012,'Abram dwelled in the land of Canaan, and Lot dwelled in the cities of the plain, and pitched his tent toward Sodom.');
+INSERT INTO t1(docid,words) VALUES(1013013,'But the men of Sodom were wicked and sinners before the LORD exceedingly.');
+INSERT INTO t1(docid,words) VALUES(1013014,'And the LORD said unto Abram, after that Lot was separated from him, Lift up now thine eyes, and look from the place where thou art northward, and southward, and eastward, and westward:');
+INSERT INTO t1(docid,words) VALUES(1013015,'For all the land which thou seest, to thee will I give it, and to thy seed for ever.');
+INSERT INTO t1(docid,words) VALUES(1013016,'And I will make thy seed as the dust of the earth: so that if a man can number the dust of the earth, then shall thy seed also be numbered.');
+INSERT INTO t1(docid,words) VALUES(1013017,'Arise, walk through the land in the length of it and in the breadth of it; for I will give it unto thee.');
+INSERT INTO t1(docid,words) VALUES(1013018,'Then Abram removed his tent, and came and dwelt in the plain of Mamre, which is in Hebron, and built there an altar unto the LORD.');
+INSERT INTO t1(docid,words) VALUES(1014001,'And it came to pass in the days of Amraphel king of Shinar, Arioch king of Ellasar, Chedorlaomer king of Elam, and Tidal king of nations;');
+INSERT INTO t1(docid,words) VALUES(1014002,'That these made war with Bera king of Sodom, and with Birsha king of Gomorrah, Shinab king of Admah, and Shemeber king of Zeboiim, and the king of Bela, which is Zoar.');
+INSERT INTO t1(docid,words) VALUES(1014003,'All these were joined together in the vale of Siddim, which is the salt sea.');
+INSERT INTO t1(docid,words) VALUES(1014004,'Twelve years they served Chedorlaomer, and in the thirteenth year they rebelled.');
+INSERT INTO t1(docid,words) VALUES(1014005,'And in the fourteenth year came Chedorlaomer, and the kings that were with him, and smote the Rephaims in Ashteroth Karnaim, and the Zuzims in Ham, and the Emins in Shaveh Kiriathaim,');
+INSERT INTO t1(docid,words) VALUES(1014006,'And the Horites in their mount Seir, unto Elparan, which is by the wilderness.');
+INSERT INTO t1(docid,words) VALUES(1014007,'And they returned, and came to Enmishpat, which is Kadesh, and smote all the country of the Amalekites, and also the Amorites, that dwelt in Hazezontamar.');
+INSERT INTO t1(docid,words) VALUES(1014008,'And there went out the king of Sodom, and the king of Gomorrah, and the king of Admah, and the king of Zeboiim, and the king of Bela (the same is Zoar;) and they joined battle with them in the vale of Siddim;');
+INSERT INTO t1(docid,words) VALUES(1014009,'With Chedorlaomer the king of Elam, and with Tidal king of nations, and Amraphel king of Shinar, and Arioch king of Ellasar; four kings with five.');
+INSERT INTO t1(docid,words) VALUES(1014010,'And the vale of Siddim was full of slimepits; and the kings of Sodom and Gomorrah fled, and fell there; and they that remained fled to the mountain.');
+INSERT INTO t1(docid,words) VALUES(1014011,'And they took all the goods of Sodom and Gomorrah, and all their victuals, and went their way.');
+INSERT INTO t1(docid,words) VALUES(1014012,'And they took Lot, Abram''s brother''s son, who dwelt in Sodom, and his goods, and departed.');
+INSERT INTO t1(docid,words) VALUES(1014013,'And there came one that had escaped, and told Abram the Hebrew; for he dwelt in the plain of Mamre the Amorite, brother of Eshcol, and brother of Aner: and these were confederate with Abram.');
+INSERT INTO t1(docid,words) VALUES(1014014,'And when Abram heard that his brother was taken captive, he armed his trained servants, born in his own house, three hundred and eighteen, and pursued them unto Dan.');
+INSERT INTO t1(docid,words) VALUES(1014015,'And he divided himself against them, he and his servants, by night, and smote them, and pursued them unto Hobah, which is on the left hand of Damascus.');
+INSERT INTO t1(docid,words) VALUES(1014016,'And he brought back all the goods, and also brought again his brother Lot, and his goods, and the women also, and the people.');
+INSERT INTO t1(docid,words) VALUES(1014017,'And the king of Sodom went out to meet him after his return from the slaughter of Chedorlaomer, and of the kings that were with him, at the valley of Shaveh, which is the king''s dale.');
+INSERT INTO t1(docid,words) VALUES(1014018,'And Melchizedek king of Salem brought forth bread and wine: and he was the priest of the most high God.');
+INSERT INTO t1(docid,words) VALUES(1014019,'And he blessed him, and said, Blessed be Abram of the most high God, possessor of heaven and earth:');
+INSERT INTO t1(docid,words) VALUES(1014020,'And blessed be the most high God, which hath delivered thine enemies into thy hand. And he gave him tithes of all.');
+INSERT INTO t1(docid,words) VALUES(1014021,'And the king of Sodom said unto Abram, Give me the persons, and take the goods to thyself.');
+INSERT INTO t1(docid,words) VALUES(1014022,'And Abram said to the king of Sodom, I have lift up mine hand unto the LORD, the most high God, the possessor of heaven and earth,');
+INSERT INTO t1(docid,words) VALUES(1014023,'That I will not take from a thread even to a shoelatchet, and that I will not take any thing that is thine, lest thou shouldest say, I have made Abram rich:');
+INSERT INTO t1(docid,words) VALUES(1014024,'Save only that which the young men have eaten, and the portion of the men which went with me, Aner, Eshcol, and Mamre; let them take their portion.');
+INSERT INTO t1(docid,words) VALUES(1015001,'After these things the word of the LORD came unto Abram in a vision, saying, Fear not, Abram: I am thy shield, and thy exceeding great reward.');
+INSERT INTO t1(docid,words) VALUES(1015002,'And Abram said, LORD God, what wilt thou give me, seeing I go childless, and the steward of my house is this Eliezer of Damascus?');
+INSERT INTO t1(docid,words) VALUES(1015003,'And Abram said, Behold, to me thou hast given no seed: and, lo, one born in my house is mine heir.');
+INSERT INTO t1(docid,words) VALUES(1015004,'And, behold, the word of the LORD came unto him, saying, This shall not be thine heir; but he that shall come forth out of thine own bowels shall be thine heir.');
+INSERT INTO t1(docid,words) VALUES(1015005,'And he brought him forth abroad, and said, Look now toward heaven, and tell the stars, if thou be able to number them: and he said unto him, So shall thy seed be.');
+INSERT INTO t1(docid,words) VALUES(1015006,'And he believed in the LORD; and he counted it to him for righteousness.');
+INSERT INTO t1(docid,words) VALUES(1015007,'And he said unto him, I am the LORD that brought thee out of Ur of the Chaldees, to give thee this land to inherit it.');
+INSERT INTO t1(docid,words) VALUES(1015008,'And he said, LORD God, whereby shall I know that I shall inherit it?');
+INSERT INTO t1(docid,words) VALUES(1015009,'And he said unto him, Take me an heifer of three years old, and a she goat of three years old, and a ram of three years old, and a turtledove, and a young pigeon.');
+INSERT INTO t1(docid,words) VALUES(1015010,'And he took unto him all these, and divided them in the midst, and laid each piece one against another: but the birds divided he not.');
+INSERT INTO t1(docid,words) VALUES(1015011,'And when the fowls came down upon the carcases, Abram drove them away.');
+INSERT INTO t1(docid,words) VALUES(1015012,'And when the sun was going down, a deep sleep fell upon Abram; and, lo, an horror of great darkness fell upon him.');
+INSERT INTO t1(docid,words) VALUES(1015013,'And he said unto Abram, Know of a surety that thy seed shall be a stranger in a land that is not their''s, and shall serve them; and they shall afflict them four hundred years;');
+INSERT INTO t1(docid,words) VALUES(1015014,'And also that nation, whom they shall serve, will I judge: and afterward shall they come out with great substance.');
+INSERT INTO t1(docid,words) VALUES(1015015,'And thou shalt go to thy fathers in peace; thou shalt be buried in a good old age.');
+INSERT INTO t1(docid,words) VALUES(1015016,'But in the fourth generation they shall come hither again: for the iniquity of the Amorites is not yet full.');
+INSERT INTO t1(docid,words) VALUES(1015017,'And it came to pass, that, when the sun went down, and it was dark, behold a smoking furnace, and a burning lamp that passed between those pieces.');
+INSERT INTO t1(docid,words) VALUES(1015018,'In the same day the LORD made a covenant with Abram, saying, Unto thy seed have I given this land, from the river of Egypt unto the great river, the river Euphrates:');
+INSERT INTO t1(docid,words) VALUES(1015019,'The Kenites, and the Kenizzites, and the Kadmonites,');
+INSERT INTO t1(docid,words) VALUES(1015020,'And the Hittites, and the Perizzites, and the Rephaims,');
+INSERT INTO t1(docid,words) VALUES(1015021,'And the Amorites, and the Canaanites, and the Girgashites, and the Jebusites.');
+INSERT INTO t1(docid,words) VALUES(1016001,'Now Sarai Abram''s wife bare him no children: and she had an handmaid, an Egyptian, whose name was Hagar.');
+INSERT INTO t1(docid,words) VALUES(1016002,'And Sarai said unto Abram, Behold now, the LORD hath restrained me from bearing: I pray thee, go in unto my maid; it may be that I may obtain children by her. And Abram hearkened to the voice of Sarai.');
+INSERT INTO t1(docid,words) VALUES(1016003,'And Sarai Abram''s wife took Hagar her maid the Egyptian, after Abram had dwelt ten years in the land of Canaan, and gave her to her husband Abram to be his wife.');
+INSERT INTO t1(docid,words) VALUES(1016004,'And he went in unto Hagar, and she conceived: and when she saw that she had conceived, her mistress was despised in her eyes.');
+INSERT INTO t1(docid,words) VALUES(1016005,'And Sarai said unto Abram, My wrong be upon thee: I have given my maid into thy bosom; and when she saw that she had conceived, I was despised in her eyes: the LORD judge between me and thee.');
+INSERT INTO t1(docid,words) VALUES(1016006,'But Abram said unto Sarai, Behold, thy maid is in thine hand; do to her as it pleaseth thee. And when Sarai dealt hardly with her, she fled from her face.');
+INSERT INTO t1(docid,words) VALUES(1016007,'And the angel of the LORD found her by a fountain of water in the wilderness, by the fountain in the way to Shur.');
+INSERT INTO t1(docid,words) VALUES(1016008,'And he said, Hagar, Sarai''s maid, whence camest thou? and whither wilt thou go? And she said, I flee from the face of my mistress Sarai.');
+INSERT INTO t1(docid,words) VALUES(1016009,'And the angel of the LORD said unto her, Return to thy mistress, and submit thyself under her hands.');
+INSERT INTO t1(docid,words) VALUES(1016010,'And the angel of the LORD said unto her, I will multiply thy seed exceedingly, that it shall not be numbered for multitude.');
+INSERT INTO t1(docid,words) VALUES(1016011,'And the angel of the LORD said unto her, Behold, thou art with child and shalt bear a son, and shalt call his name Ishmael; because the LORD hath heard thy affliction.');
+INSERT INTO t1(docid,words) VALUES(1016012,'And he will be a wild man; his hand will be against every man, and every man''s hand against him; and he shall dwell in the presence of all his brethren.');
+INSERT INTO t1(docid,words) VALUES(1016013,'And she called the name of the LORD that spake unto her, Thou God seest me: for she said, Have I also here looked after him that seeth me?');
+INSERT INTO t1(docid,words) VALUES(1016014,'Wherefore the well was called Beerlahairoi; behold, it is between Kadesh and Bered.');
+INSERT INTO t1(docid,words) VALUES(1016015,'And Hagar bare Abram a son: and Abram called his son''s name, which Hagar bare, Ishmael.');
+INSERT INTO t1(docid,words) VALUES(1016016,'And Abram was fourscore and six years old, when Hagar bare Ishmael to Abram.');
+INSERT INTO t1(docid,words) VALUES(1017001,'And when Abram was ninety years old and nine, the LORD appeared to Abram, and said unto him, I am the Almighty God; walk before me, and be thou perfect.');
+INSERT INTO t1(docid,words) VALUES(1017002,'And I will make my covenant between me and thee, and will multiply thee exceedingly.');
+INSERT INTO t1(docid,words) VALUES(1017003,'And Abram fell on his face: and God talked with him, saying,');
+INSERT INTO t1(docid,words) VALUES(1017004,'As for me, behold, my covenant is with thee, and thou shalt be a father of many nations.');
+INSERT INTO t1(docid,words) VALUES(1017005,'Neither shall thy name any more be called Abram, but thy name shall be Abraham; for a father of many nations have I made thee.');
+INSERT INTO t1(docid,words) VALUES(1017006,'And I will make thee exceeding fruitful, and I will make nations of thee, and kings shall come out of thee.');
+INSERT INTO t1(docid,words) VALUES(1017007,'And I will establish my covenant between me and thee and thy seed after thee in their generations for an everlasting covenant, to be a God unto thee, and to thy seed after thee.');
+INSERT INTO t1(docid,words) VALUES(1017008,'And I will give unto thee, and to thy seed after thee, the land wherein thou art a stranger, all the land of Canaan, for an everlasting possession; and I will be their God.');
+INSERT INTO t1(docid,words) VALUES(1017009,'And God said unto Abraham, Thou shalt keep my covenant therefore, thou, and thy seed after thee in their generations.');
+INSERT INTO t1(docid,words) VALUES(1017010,'This is my covenant, which ye shall keep, between me and you and thy seed after thee; Every man child among you shall be circumcised.');
+INSERT INTO t1(docid,words) VALUES(1017011,'And ye shall circumcise the flesh of your foreskin; and it shall be a token of the covenant betwixt me and you.');
+INSERT INTO t1(docid,words) VALUES(1017012,'And he that is eight days old shall be circumcised among you, every man child in your generations, he that is born in the house, or bought with money of any stranger, which is not of thy seed.');
+INSERT INTO t1(docid,words) VALUES(1017013,'He that is born in thy house, and he that is bought with thy money, must needs be circumcised: and my covenant shall be in your flesh for an everlasting covenant.');
+INSERT INTO t1(docid,words) VALUES(1017014,'And the uncircumcised man child whose flesh of his foreskin is not circumcised, that soul shall be cut off from his people; he hath broken my covenant.');
+INSERT INTO t1(docid,words) VALUES(1017015,'And God said unto Abraham, As for Sarai thy wife, thou shalt not call her name Sarai, but Sarah shall her name be.');
+INSERT INTO t1(docid,words) VALUES(1017016,'And I will bless her, and give thee a son also of her: yea, I will bless her, and she shall be a mother of nations; kings of people shall be of her.');
+INSERT INTO t1(docid,words) VALUES(1017017,'Then Abraham fell upon his face, and laughed, and said in his heart, Shall a child be born unto him that is an hundred years old? and shall Sarah, that is ninety years old, bear?');
+INSERT INTO t1(docid,words) VALUES(1017018,'And Abraham said unto God, O that Ishmael might live before thee!');
+INSERT INTO t1(docid,words) VALUES(1017019,'And God said, Sarah thy wife shall bear thee a son indeed; and thou shalt call his name Isaac: and I will establish my covenant with him for an everlasting covenant, and with his seed after him.');
+INSERT INTO t1(docid,words) VALUES(1017020,'And as for Ishmael, I have heard thee: Behold, I have blessed him, and will make him fruitful, and will multiply him exceedingly; twelve princes shall he beget, and I will make him a great nation.');
+INSERT INTO t1(docid,words) VALUES(1017021,'But my covenant will I establish with Isaac, which Sarah shall bear unto thee at this set time in the next year.');
+INSERT INTO t1(docid,words) VALUES(1017022,'And he left off talking with him, and God went up from Abraham.');
+INSERT INTO t1(docid,words) VALUES(1017023,'And Abraham took Ishmael his son, and all that were born in his house, and all that were bought with his money, every male among the men of Abraham''s house; and circumcised the flesh of their foreskin in the selfsame day, as God had said unto him.');
+INSERT INTO t1(docid,words) VALUES(1017024,'And Abraham was ninety years old and nine, when he was circumcised in the flesh of his foreskin.');
+INSERT INTO t1(docid,words) VALUES(1017025,'And Ishmael his son was thirteen years old, when he was circumcised in the flesh of his foreskin.');
+INSERT INTO t1(docid,words) VALUES(1017026,'In the selfsame day was Abraham circumcised, and Ishmael his son.');
+INSERT INTO t1(docid,words) VALUES(1017027,'And all the men of his house, born in the house, and bought with money of the stranger, were circumcised with him.');
+INSERT INTO t1(docid,words) VALUES(1018001,'And the LORD appeared unto him in the plains of Mamre: and he sat in the tent door in the heat of the day;');
+INSERT INTO t1(docid,words) VALUES(1018002,'And he lift up his eyes and looked, and, lo, three men stood by him: and when he saw them, he ran to meet them from the tent door, and bowed himself toward the ground,');
+INSERT INTO t1(docid,words) VALUES(1018003,'And said, My LORD, if now I have found favour in thy sight, pass not away, I pray thee, from thy servant:');
+INSERT INTO t1(docid,words) VALUES(1018004,'Let a little water, I pray you, be fetched, and wash your feet, and rest yourselves under the tree:');
+INSERT INTO t1(docid,words) VALUES(1018005,'And I will fetch a morsel of bread, and comfort ye your hearts; after that ye shall pass on: for therefore are ye come to your servant. And they said, So do, as thou hast said.');
+INSERT INTO t1(docid,words) VALUES(1018006,'And Abraham hastened into the tent unto Sarah, and said, Make ready quickly three measures of fine meal, knead it, and make cakes upon the hearth.');
+INSERT INTO t1(docid,words) VALUES(1018007,'And Abraham ran unto the herd, and fetcht a calf tender and good, and gave it unto a young man; and he hasted to dress it.');
+INSERT INTO t1(docid,words) VALUES(1018008,'And he took butter, and milk, and the calf which he had dressed, and set it before them; and he stood by them under the tree, and they did eat.');
+INSERT INTO t1(docid,words) VALUES(1018009,'And they said unto him, Where is Sarah thy wife? And he said, Behold, in the tent.');
+INSERT INTO t1(docid,words) VALUES(1018010,'And he said, I will certainly return unto thee according to the time of life; and, lo, Sarah thy wife shall have a son. And Sarah heard it in the tent door, which was behind him.');
+INSERT INTO t1(docid,words) VALUES(1018011,'Now Abraham and Sarah were old and well stricken in age; and it ceased to be with Sarah after the manner of women.');
+INSERT INTO t1(docid,words) VALUES(1018012,'Therefore Sarah laughed within herself, saying, After I am waxed old shall I have pleasure, my lord being old also?');
+INSERT INTO t1(docid,words) VALUES(1018013,'And the LORD said unto Abraham, Wherefore did Sarah laugh, saying, Shall I of a surety bear a child, which am old?');
+INSERT INTO t1(docid,words) VALUES(1018014,'Is any thing too hard for the LORD? At the time appointed I will return unto thee, according to the time of life, and Sarah shall have a son.');
+INSERT INTO t1(docid,words) VALUES(1018015,'Then Sarah denied, saying, I laughed not; for she was afraid. And he said, Nay; but thou didst laugh.');
+INSERT INTO t1(docid,words) VALUES(1018016,'And the men rose up from thence, and looked toward Sodom: and Abraham went with them to bring them on the way.');
+INSERT INTO t1(docid,words) VALUES(1018017,'And the LORD said, Shall I hide from Abraham that thing which I do;');
+INSERT INTO t1(docid,words) VALUES(1018018,'Seeing that Abraham shall surely become a great and mighty nation, and all the nations of the earth shall be blessed in him?');
+INSERT INTO t1(docid,words) VALUES(1018019,'For I know him, that he will command his children and his household after him, and they shall keep the way of the LORD, to do justice and judgment; that the LORD may bring upon Abraham that which he hath spoken of him.');
+INSERT INTO t1(docid,words) VALUES(1018020,'And the LORD said, Because the cry of Sodom and Gomorrah is great, and because their sin is very grievous;');
+INSERT INTO t1(docid,words) VALUES(1018021,'I will go down now, and see whether they have done altogether according to the cry of it, which is come unto me; and if not, I will know.');
+INSERT INTO t1(docid,words) VALUES(1018022,'And the men turned their faces from thence, and went toward Sodom: but Abraham stood yet before the LORD.');
+INSERT INTO t1(docid,words) VALUES(1018023,'And Abraham drew near, and said, Wilt thou also destroy the righteous with the wicked?');
+INSERT INTO t1(docid,words) VALUES(1018024,'Peradventure there be fifty righteous within the city: wilt thou also destroy and not spare the place for the fifty righteous that are therein?');
+INSERT INTO t1(docid,words) VALUES(1018025,'That be far from thee to do after this manner, to slay the righteous with the wicked: and that the righteous should be as the wicked, that be far from thee: Shall not the Judge of all the earth do right?');
+INSERT INTO t1(docid,words) VALUES(1018026,'And the LORD said, If I find in Sodom fifty righteous within the city, then I will spare all the place for their sakes.');
+INSERT INTO t1(docid,words) VALUES(1018027,'And Abraham answered and said, Behold now, I have taken upon me to speak unto the LORD, which am but dust and ashes:');
+INSERT INTO t1(docid,words) VALUES(1018028,'Peradventure there shall lack five of the fifty righteous: wilt thou destroy all the city for lack of five? And he said, If I find there forty and five, I will not destroy it.');
+INSERT INTO t1(docid,words) VALUES(1018029,'And he spake unto him yet again, and said, Peradventure there shall be forty found there. And he said, I will not do it for forty''s sake.');
+INSERT INTO t1(docid,words) VALUES(1018030,'And he said unto him, Oh let not the LORD be angry, and I will speak: Peradventure there shall thirty be found there. And he said, I will not do it, if I find thirty there.');
+INSERT INTO t1(docid,words) VALUES(1018031,'And he said, Behold now, I have taken upon me to speak unto the LORD: Peradventure there shall be twenty found there. And he said, I will not destroy it for twenty''s sake.');
+INSERT INTO t1(docid,words) VALUES(1018032,'And he said, Oh let not the LORD be angry, and I will speak yet but this once: Peradventure ten shall be found there. And he said, I will not destroy it for ten''s sake.');
+INSERT INTO t1(docid,words) VALUES(1018033,'And the LORD went his way, as soon as he had left communing with Abraham: and Abraham returned unto his place.');
+INSERT INTO t1(docid,words) VALUES(1019001,'And there came two angels to Sodom at even; and Lot sat in the gate of Sodom: and Lot seeing them rose up to meet them; and he bowed himself with his face toward the ground;');
+INSERT INTO t1(docid,words) VALUES(1019002,'And he said, Behold now, my lords, turn in, I pray you, into your servant''s house, and tarry all night, and wash your feet, and ye shall rise up early, and go on your ways. And they said, Nay; but we will abide in the street all night.');
+INSERT INTO t1(docid,words) VALUES(1019003,'And he pressed upon them greatly; and they turned in unto him, and entered into his house; and he made them a feast, and did bake unleavened bread, and they did eat.');
+INSERT INTO t1(docid,words) VALUES(1019004,'But before they lay down, the men of the city, even the men of Sodom, compassed the house round, both old and young, all the people from every quarter:');
+INSERT INTO t1(docid,words) VALUES(1019005,'And they called unto Lot, and said unto him, Where are the men which came in to thee this night? bring them out unto us, that we may know them.');
+INSERT INTO t1(docid,words) VALUES(1019006,'And Lot went out at the door unto them, and shut the door after him,');
+INSERT INTO t1(docid,words) VALUES(1019007,'And said, I pray you, brethren, do not so wickedly.');
+INSERT INTO t1(docid,words) VALUES(1019008,'Behold now, I have two daughters which have not known man; let me, I pray you, bring them out unto you, and do ye to them as is good in your eyes: only unto these men do nothing; for therefore came they under the shadow of my roof.');
+INSERT INTO t1(docid,words) VALUES(1019009,'And they said, Stand back. And they said again, This one fellow came in to sojourn, and he will needs be a judge: now will we deal worse with thee, than with them. And they pressed sore upon the man, even Lot, and came near to break the door.');
+INSERT INTO t1(docid,words) VALUES(1019010,'But the men put forth their hand, and pulled Lot into the house to them, and shut to the door.');
+INSERT INTO t1(docid,words) VALUES(1019011,'And they smote the men that were at the door of the house with blindness, both small and great: so that they wearied themselves to find the door.');
+INSERT INTO t1(docid,words) VALUES(1019012,'And the men said unto Lot, Hast thou here any besides? son in law, and thy sons, and thy daughters, and whatsoever thou hast in the city, bring them out of this place:');
+INSERT INTO t1(docid,words) VALUES(1019013,'For we will destroy this place, because the cry of them is waxen great before the face of the LORD; and the LORD hath sent us to destroy it.');
+INSERT INTO t1(docid,words) VALUES(1019014,'And Lot went out, and spake unto his sons in law, which married his daughters, and said, Up, get you out of this place; for the LORD will destroy this city. But he seemed as one that mocked unto his sons in law.');
+INSERT INTO t1(docid,words) VALUES(1019015,'And when the morning arose, then the angels hastened Lot, saying, Arise, take thy wife, and thy two daughters, which are here; lest thou be consumed in the iniquity of the city.');
+INSERT INTO t1(docid,words) VALUES(1019016,'And while he lingered, the men laid hold upon his hand, and upon the hand of his wife, and upon the hand of his two daughters; the LORD being merciful unto him: and they brought him forth, and set him without the city.');
+INSERT INTO t1(docid,words) VALUES(1019017,'And it came to pass, when they had brought them forth abroad, that he said, Escape for thy life; look not behind thee, neither stay thou in all the plain; escape to the mountain, lest thou be consumed.');
+INSERT INTO t1(docid,words) VALUES(1019018,'And Lot said unto them, Oh, not so, my LORD:');
+INSERT INTO t1(docid,words) VALUES(1019019,'Behold now, thy servant hath found grace in thy sight, and thou hast magnified thy mercy, which thou hast shewed unto me in saving my life; and I cannot escape to the mountain, lest some evil take me, and I die:');
+INSERT INTO t1(docid,words) VALUES(1019020,'Behold now, this city is near to flee unto, and it is a little one: Oh, let me escape thither, (is it not a little one?) and my soul shall live.');
+INSERT INTO t1(docid,words) VALUES(1019021,'And he said unto him, See, I have accepted thee concerning this thing also, that I will not overthrow this city, for the which thou hast spoken.');
+INSERT INTO t1(docid,words) VALUES(1019022,'Haste thee, escape thither; for I cannot do anything till thou be come thither. Therefore the name of the city was called Zoar.');
+INSERT INTO t1(docid,words) VALUES(1019023,'The sun was risen upon the earth when Lot entered into Zoar.');
+INSERT INTO t1(docid,words) VALUES(1019024,'Then the LORD rained upon Sodom and upon Gomorrah brimstone and fire from the LORD out of heaven;');
+INSERT INTO t1(docid,words) VALUES(1019025,'And he overthrew those cities, and all the plain, and all the inhabitants of the cities, and that which grew upon the ground.');
+INSERT INTO t1(docid,words) VALUES(1019026,'But his wife looked back from behind him, and she became a pillar of salt.');
+INSERT INTO t1(docid,words) VALUES(1019027,'And Abraham gat up early in the morning to the place where he stood before the LORD:');
+INSERT INTO t1(docid,words) VALUES(1019028,'And he looked toward Sodom and Gomorrah, and toward all the land of the plain, and beheld, and, lo, the smoke of the country went up as the smoke of a furnace.');
+INSERT INTO t1(docid,words) VALUES(1019029,'And it came to pass, when God destroyed the cities of the plain, that God remembered Abraham, and sent Lot out of the midst of the overthrow, when he overthrew the cities in the which Lot dwelt.');
+INSERT INTO t1(docid,words) VALUES(1019030,'And Lot went up out of Zoar, and dwelt in the mountain, and his two daughters with him; for he feared to dwell in Zoar: and he dwelt in a cave, he and his two daughters.');
+INSERT INTO t1(docid,words) VALUES(1019031,'And the firstborn said unto the younger, Our father is old, and there is not a man in the earth to come in unto us after the manner of all the earth:');
+INSERT INTO t1(docid,words) VALUES(1019032,'Come, let us make our father drink wine, and we will lie with him, that we may preserve seed of our father.');
+INSERT INTO t1(docid,words) VALUES(1019033,'And they made their father drink wine that night: and the firstborn went in, and lay with her father; and he perceived not when she lay down, nor when she arose.');
+INSERT INTO t1(docid,words) VALUES(1019034,'And it came to pass on the morrow, that the firstborn said unto the younger, Behold, I lay yesternight with my father: let us make him drink wine this night also; and go thou in, and lie with him, that we may preserve seed of our father.');
+INSERT INTO t1(docid,words) VALUES(1019035,'And they made their father drink wine that night also: and the younger arose, and lay with him; and he perceived not when she lay down, nor when she arose.');
+INSERT INTO t1(docid,words) VALUES(1019036,'Thus were both the daughters of Lot with child by their father.');
+INSERT INTO t1(docid,words) VALUES(1019037,'And the first born bare a son, and called his name Moab: the same is the father of the Moabites unto this day.');
+INSERT INTO t1(docid,words) VALUES(1019038,'And the younger, she also bare a son, and called his name Benammi: the same is the father of the children of Ammon unto this day.');
+INSERT INTO t1(docid,words) VALUES(1020001,'And Abraham journeyed from thence toward the south country, and dwelled between Kadesh and Shur, and sojourned in Gerar.');
+INSERT INTO t1(docid,words) VALUES(1020002,'And Abraham said of Sarah his wife, She is my sister: and Abimelech king of Gerar sent, and took Sarah.');
+INSERT INTO t1(docid,words) VALUES(1020003,'But God came to Abimelech in a dream by night, and said to him, Behold, thou art but a dead man, for the woman which thou hast taken; for she is a man''s wife.');
+INSERT INTO t1(docid,words) VALUES(1020004,'But Abimelech had not come near her: and he said, LORD, wilt thou slay also a righteous nation?');
+INSERT INTO t1(docid,words) VALUES(1020005,'Said he not unto me, She is my sister? and she, even she herself said, He is my brother: in the integrity of my heart and innocency of my hands have I done this.');
+INSERT INTO t1(docid,words) VALUES(1020006,'And God said unto him in a dream, Yea, I know that thou didst this in the integrity of thy heart; for I also withheld thee from sinning against me: therefore suffered I thee not to touch her.');
+INSERT INTO t1(docid,words) VALUES(1020007,'Now therefore restore the man his wife; for he is a prophet, and he shall pray for thee, and thou shalt live: and if thou restore her not, know thou that thou shalt surely die, thou, and all that are thine.');
+INSERT INTO t1(docid,words) VALUES(1020008,'Therefore Abimelech rose early in the morning, and called all his servants, and told all these things in their ears: and the men were sore afraid.');
+INSERT INTO t1(docid,words) VALUES(1020009,'Then Abimelech called Abraham, and said unto him, What hast thou done unto us? and what have I offended thee, that thou hast brought on me and on my kingdom a great sin? thou hast done deeds unto me that ought not to be done.');
+INSERT INTO t1(docid,words) VALUES(1020010,'And Abimelech said unto Abraham, What sawest thou, that thou hast done this thing?');
+INSERT INTO t1(docid,words) VALUES(1020011,'And Abraham said, Because I thought, Surely the fear of God is not in this place; and they will slay me for my wife''s sake.');
+INSERT INTO t1(docid,words) VALUES(1020012,'And yet indeed she is my sister; she is the daughter of my father, but not the daughter of my mother; and she became my wife.');
+INSERT INTO t1(docid,words) VALUES(1020013,'And it came to pass, when God caused me to wander from my father''s house, that I said unto her, This is thy kindness which thou shalt shew unto me; at every place whither we shall come, say of me, He is my brother.');
+INSERT INTO t1(docid,words) VALUES(1020014,'And Abimelech took sheep, and oxen, and menservants, and womenservants, and gave them unto Abraham, and restored him Sarah his wife.');
+INSERT INTO t1(docid,words) VALUES(1020015,'And Abimelech said, Behold, my land is before thee: dwell where it pleaseth thee.');
+INSERT INTO t1(docid,words) VALUES(1020016,'And unto Sarah he said, Behold, I have given thy brother a thousand pieces of silver: behold, he is to thee a covering of the eyes, unto all that are with thee, and with all other: thus she was reproved.');
+INSERT INTO t1(docid,words) VALUES(1020017,'So Abraham prayed unto God: and God healed Abimelech, and his wife, and his maidservants; and they bare children.');
+INSERT INTO t1(docid,words) VALUES(1020018,'For the LORD had fast closed up all the wombs of the house of Abimelech, because of Sarah Abraham''s wife.');
+INSERT INTO t1(docid,words) VALUES(1021001,'And the LORD visited Sarah as he had said, and the LORD did unto Sarah as he had spoken.');
+INSERT INTO t1(docid,words) VALUES(1021002,'For Sarah conceived, and bare Abraham a son in his old age, at the set time of which God had spoken to him.');
+INSERT INTO t1(docid,words) VALUES(1021003,'And Abraham called the name of his son that was born unto him, whom Sarah bare to him, Isaac.');
+INSERT INTO t1(docid,words) VALUES(1021004,'And Abraham circumcised his son Isaac being eight days old, as God had commanded him.');
+INSERT INTO t1(docid,words) VALUES(1021005,'And Abraham was an hundred years old, when his son Isaac was born unto him.');
+INSERT INTO t1(docid,words) VALUES(1021006,'And Sarah said, God hath made me to laugh, so that all that hear will laugh with me.');
+INSERT INTO t1(docid,words) VALUES(1021007,'And she said, Who would have said unto Abraham, that Sarah should have given children suck? for I have born him a son in his old age.');
+INSERT INTO t1(docid,words) VALUES(1021008,'And the child grew, and was weaned: and Abraham made a great feast the same day that Isaac was weaned.');
+INSERT INTO t1(docid,words) VALUES(1021009,'And Sarah saw the son of Hagar the Egyptian, which she had born unto Abraham, mocking.');
+INSERT INTO t1(docid,words) VALUES(1021010,'Wherefore she said unto Abraham, Cast out this bondwoman and her son: for the son of this bondwoman shall not be heir with my son, even with Isaac.');
+INSERT INTO t1(docid,words) VALUES(1021011,'And the thing was very grievous in Abraham''s sight because of his son.');
+INSERT INTO t1(docid,words) VALUES(1021012,'And God said unto Abraham, Let it not be grievous in thy sight because of the lad, and because of thy bondwoman; in all that Sarah hath said unto thee, hearken unto her voice; for in Isaac shall thy seed be called.');
+INSERT INTO t1(docid,words) VALUES(1021013,'And also of the son of the bondwoman will I make a nation, because he is thy seed.');
+INSERT INTO t1(docid,words) VALUES(1021014,'And Abraham rose up early in the morning, and took bread, and a bottle of water, and gave it unto Hagar, putting it on her shoulder, and the child, and sent her away: and she departed, and wandered in the wilderness of Beersheba.');
+INSERT INTO t1(docid,words) VALUES(1021015,'And the water was spent in the bottle, and she cast the child under one of the shrubs.');
+INSERT INTO t1(docid,words) VALUES(1021016,'And she went, and sat her down over against him a good way off, as it were a bow shot: for she said, Let me not see the death of the child. And she sat over against him, and lift up her voice, and wept.');
+INSERT INTO t1(docid,words) VALUES(1021017,'And God heard the voice of the lad; and the angel of God called to Hagar out of heaven, and said unto her, What aileth thee, Hagar? fear not; for God hath heard the voice of the lad where he is.');
+INSERT INTO t1(docid,words) VALUES(1021018,'Arise, lift up the lad, and hold him in thine hand; for I will make him a great nation.');
+INSERT INTO t1(docid,words) VALUES(1021019,'And God opened her eyes, and she saw a well of water; and she went, and filled the bottle with water, and gave the lad drink.');
+INSERT INTO t1(docid,words) VALUES(1021020,'And God was with the lad; and he grew, and dwelt in the wilderness, and became an archer.');
+INSERT INTO t1(docid,words) VALUES(1021021,'And he dwelt in the wilderness of Paran: and his mother took him a wife out of the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1021022,'And it came to pass at that time, that Abimelech and Phichol the chief captain of his host spake unto Abraham, saying, God is with thee in all that thou doest:');
+INSERT INTO t1(docid,words) VALUES(1021023,'Now therefore swear unto me here by God that thou wilt not deal falsely with me, nor with my son, nor with my son''s son: but according to the kindness that I have done unto thee, thou shalt do unto me, and to the land wherein thou hast sojourned.');
+INSERT INTO t1(docid,words) VALUES(1021024,'And Abraham said, I will swear.');
+INSERT INTO t1(docid,words) VALUES(1021025,'And Abraham reproved Abimelech because of a well of water, which Abimelech''s servants had violently taken away.');
+INSERT INTO t1(docid,words) VALUES(1021026,'And Abimelech said, I wot not who hath done this thing; neither didst thou tell me, neither yet heard I of it, but to day.');
+INSERT INTO t1(docid,words) VALUES(1021027,'And Abraham took sheep and oxen, and gave them unto Abimelech; and both of them made a covenant.');
+INSERT INTO t1(docid,words) VALUES(1021028,'And Abraham set seven ewe lambs of the flock by themselves.');
+INSERT INTO t1(docid,words) VALUES(1021029,'And Abimelech said unto Abraham, What mean these seven ewe lambs which thou hast set by themselves?');
+INSERT INTO t1(docid,words) VALUES(1021030,'And he said, For these seven ewe lambs shalt thou take of my hand, that they may be a witness unto me, that I have digged this well.');
+INSERT INTO t1(docid,words) VALUES(1021031,'Wherefore he called that place Beersheba; because there they sware both of them.');
+INSERT INTO t1(docid,words) VALUES(1021032,'Thus they made a covenant at Beersheba: then Abimelech rose up, and Phichol the chief captain of his host, and they returned into the land of the Philistines.');
+INSERT INTO t1(docid,words) VALUES(1021033,'And Abraham planted a grove in Beersheba, and called there on the name of the LORD, the everlasting God.');
+INSERT INTO t1(docid,words) VALUES(1021034,'And Abraham sojourned in the Philistines'' land many days.');
+INSERT INTO t1(docid,words) VALUES(1022001,'And it came to pass after these things, that God did tempt Abraham, and said unto him, Abraham: and he said, Behold, here I am.');
+INSERT INTO t1(docid,words) VALUES(1022002,'And he said, Take now thy son, thine only son Isaac, whom thou lovest, and get thee into the land of Moriah; and offer him there for a burnt offering upon one of the mountains which I will tell thee of.');
+INSERT INTO t1(docid,words) VALUES(1022003,'And Abraham rose up early in the morning, and saddled his ass, and took two of his young men with him, and Isaac his son, and clave the wood for the burnt offering, and rose up, and went unto the place of which God had told him.');
+INSERT INTO t1(docid,words) VALUES(1022004,'Then on the third day Abraham lifted up his eyes, and saw the place afar off.');
+INSERT INTO t1(docid,words) VALUES(1022005,'And Abraham said unto his young men, Abide ye here with the ass; and I and the lad will go yonder and worship, and come again to you.');
+INSERT INTO t1(docid,words) VALUES(1022006,'And Abraham took the wood of the burnt offering, and laid it upon Isaac his son; and he took the fire in his hand, and a knife; and they went both of them together.');
+INSERT INTO t1(docid,words) VALUES(1022007,'And Isaac spake unto Abraham his father, and said, My father: and he said, Here am I, my son. And he said, Behold the fire and the wood: but where is the lamb for a burnt offering?');
+INSERT INTO t1(docid,words) VALUES(1022008,'And Abraham said, My son, God will provide himself a lamb for a burnt offering: so they went both of them together.');
+INSERT INTO t1(docid,words) VALUES(1022009,'And they came to the place which God had told him of; and Abraham built an altar there, and laid the wood in order, and bound Isaac his son, and laid him on the altar upon the wood.');
+INSERT INTO t1(docid,words) VALUES(1022010,'And Abraham stretched forth his hand, and took the knife to slay his son.');
+INSERT INTO t1(docid,words) VALUES(1022011,'And the angel of the LORD called unto him out of heaven, and said, Abraham, Abraham: and he said, Here am I.');
+INSERT INTO t1(docid,words) VALUES(1022012,'And he said, Lay not thine hand upon the lad, neither do thou any thing unto him: for now I know that thou fearest God, seeing thou hast not withheld thy son, thine only son from me.');
+INSERT INTO t1(docid,words) VALUES(1022013,'And Abraham lifted up his eyes, and looked, and behold behind him a ram caught in a thicket by his horns: and Abraham went and took the ram, and offered him up for a burnt offering in the stead of his son.');
+INSERT INTO t1(docid,words) VALUES(1022014,'And Abraham called the name of that place Jehovahjireh: as it is said to this day, In the mount of the LORD it shall be seen.');
+INSERT INTO t1(docid,words) VALUES(1022015,'And the angel of the LORD called unto Abraham out of heaven the second time,');
+INSERT INTO t1(docid,words) VALUES(1022016,'And said, By myself have I sworn, saith the LORD, for because thou hast done this thing, and hast not withheld thy son, thine only son:');
+INSERT INTO t1(docid,words) VALUES(1022017,'That in blessing I will bless thee, and in multiplying I will multiply thy seed as the stars of the heaven, and as the sand which is upon the sea shore; and thy seed shall possess the gate of his enemies;');
+INSERT INTO t1(docid,words) VALUES(1022018,'And in thy seed shall all the nations of the earth be blessed; because thou hast obeyed my voice.');
+INSERT INTO t1(docid,words) VALUES(1022019,'So Abraham returned unto his young men, and they rose up and went together to Beersheba; and Abraham dwelt at Beersheba.');
+INSERT INTO t1(docid,words) VALUES(1022020,'And it came to pass after these things, that it was told Abraham, saying, Behold, Milcah, she hath also born children unto thy brother Nahor;');
+INSERT INTO t1(docid,words) VALUES(1022021,'Huz his firstborn, and Buz his brother, and Kemuel the father of Aram,');
+INSERT INTO t1(docid,words) VALUES(1022022,'And Chesed, and Hazo, and Pildash, and Jidlaph, and Bethuel.');
+INSERT INTO t1(docid,words) VALUES(1022023,'And Bethuel begat Rebekah: these eight Milcah did bear to Nahor, Abraham''s brother.');
+INSERT INTO t1(docid,words) VALUES(1022024,'And his concubine, whose name was Reumah, she bare also Tebah, and Gaham, and Thahash, and Maachah.');
+INSERT INTO t1(docid,words) VALUES(1023001,'And Sarah was an hundred and seven and twenty years old: these were the years of the life of Sarah.');
+INSERT INTO t1(docid,words) VALUES(1023002,'And Sarah died in Kirjatharba; the same is Hebron in the land of Canaan: and Abraham came to mourn for Sarah, and to weep for her.');
+INSERT INTO t1(docid,words) VALUES(1023003,'And Abraham stood up from before his dead, and spake unto the sons of Heth, saying,');
+INSERT INTO t1(docid,words) VALUES(1023004,'I am a stranger and a sojourner with you: give me a possession of a buryingplace with you, that I may bury my dead out of my sight.');
+INSERT INTO t1(docid,words) VALUES(1023005,'And the children of Heth answered Abraham, saying unto him,');
+INSERT INTO t1(docid,words) VALUES(1023006,'Hear us, my lord: thou art a mighty prince among us: in the choice of our sepulchres bury thy dead; none of us shall withhold from thee his sepulchre, but that thou mayest bury thy dead.');
+INSERT INTO t1(docid,words) VALUES(1023007,'And Abraham stood up, and bowed himself to the people of the land, even to the children of Heth.');
+INSERT INTO t1(docid,words) VALUES(1023008,'And he communed with them, saying, If it be your mind that I should bury my dead out of my sight; hear me, and intreat for me to Ephron the son of Zohar,');
+INSERT INTO t1(docid,words) VALUES(1023009,'That he may give me the cave of Machpelah, which he hath, which is in the end of his field; for as much money as it is worth he shall give it me for a possession of a buryingplace amongst you.');
+INSERT INTO t1(docid,words) VALUES(1023010,'And Ephron dwelt among the children of Heth: and Ephron the Hittite answered Abraham in the audience of the children of Heth, even of all that went in at the gate of his city, saying,');
+INSERT INTO t1(docid,words) VALUES(1023011,'Nay, my lord, hear me: the field give I thee, and the cave that is therein, I give it thee; in the presence of the sons of my people give I it thee: bury thy dead.');
+INSERT INTO t1(docid,words) VALUES(1023012,'And Abraham bowed down himself before the people of the land.');
+INSERT INTO t1(docid,words) VALUES(1023013,'And he spake unto Ephron in the audience of the people of the land, saying, But if thou wilt give it, I pray thee, hear me: I will give thee money for the field; take it of me, and I will bury my dead there.');
+INSERT INTO t1(docid,words) VALUES(1023014,'And Ephron answered Abraham, saying unto him,');
+INSERT INTO t1(docid,words) VALUES(1023015,'My lord, hearken unto me: the land is worth four hundred shekels of silver; what is that betwixt me and thee? bury therefore thy dead.');
+INSERT INTO t1(docid,words) VALUES(1023016,'And Abraham hearkened unto Ephron; and Abraham weighed to Ephron the silver, which he had named in the audience of the sons of Heth, four hundred shekels of silver, current money with the merchant.');
+INSERT INTO t1(docid,words) VALUES(1023017,'And the field of Ephron which was in Machpelah, which was before Mamre, the field, and the cave which was therein, and all the trees that were in the field, that were in all the borders round about, were made sure');
+INSERT INTO t1(docid,words) VALUES(1023018,'Unto Abraham for a possession in the presence of the children of Heth, before all that went in at the gate of his city.');
+INSERT INTO t1(docid,words) VALUES(1023019,'And after this, Abraham buried Sarah his wife in the cave of the field of Machpelah before Mamre: the same is Hebron in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1023020,'And the field, and the cave that is therein, were made sure unto Abraham for a possession of a buryingplace by the sons of Heth.');
+INSERT INTO t1(docid,words) VALUES(1024001,'And Abraham was old, and well stricken in age: and the LORD had blessed Abraham in all things.');
+INSERT INTO t1(docid,words) VALUES(1024002,'And Abraham said unto his eldest servant of his house, that ruled over all that he had, Put, I pray thee, thy hand under my thigh:');
+INSERT INTO t1(docid,words) VALUES(1024003,'And I will make thee swear by the LORD, the God of heaven, and the God of the earth, that thou shalt not take a wife unto my son of the daughters of the Canaanites, among whom I dwell:');
+INSERT INTO t1(docid,words) VALUES(1024004,'But thou shalt go unto my country, and to my kindred, and take a wife unto my son Isaac.');
+INSERT INTO t1(docid,words) VALUES(1024005,'And the servant said unto him, Peradventure the woman will not be willing to follow me unto this land: must I needs bring thy son again unto the land from whence thou camest?');
+INSERT INTO t1(docid,words) VALUES(1024006,'And Abraham said unto him, Beware thou that thou bring not my son thither again.');
+INSERT INTO t1(docid,words) VALUES(1024007,'The LORD God of heaven, which took me from my father''s house, and from the land of my kindred, and which spake unto me, and that sware unto me, saying, Unto thy seed will I give this land; he shall send his angel before thee, and thou shalt take a wife unto my son from thence.');
+INSERT INTO t1(docid,words) VALUES(1024008,'And if the woman will not be willing to follow thee, then thou shalt be clear from this my oath: only bring not my son thither again.');
+INSERT INTO t1(docid,words) VALUES(1024009,'And the servant put his hand under the thigh of Abraham his master, and sware to him concerning that matter.');
+INSERT INTO t1(docid,words) VALUES(1024010,'And the servant took ten camels of the camels of his master, and departed; for all the goods of his master were in his hand: and he arose, and went to Mesopotamia, unto the city of Nahor.');
+INSERT INTO t1(docid,words) VALUES(1024011,'And he made his camels to kneel down without the city by a well of water at the time of the evening, even the time that women go out to draw water.');
+INSERT INTO t1(docid,words) VALUES(1024012,'And he said O LORD God of my master Abraham, I pray thee, send me good speed this day, and shew kindness unto my master Abraham.');
+INSERT INTO t1(docid,words) VALUES(1024013,'Behold, I stand here by the well of water; and the daughters of the men of the city come out to draw water:');
+INSERT INTO t1(docid,words) VALUES(1024014,'And let it come to pass, that the damsel to whom I shall say, Let down thy pitcher, I pray thee, that I may drink; and she shall say, Drink, and I will give thy camels drink also: let the same be she that thou hast appointed for thy servant Isaac; and thereby shall I know that thou hast shewed kindness unto my master.');
+INSERT INTO t1(docid,words) VALUES(1024015,'And it came to pass, before he had done speaking, that, behold, Rebekah came out, who was born to Bethuel, son of Milcah, the wife of Nahor, Abraham''s brother, with her pitcher upon her shoulder.');
+INSERT INTO t1(docid,words) VALUES(1024016,'And the damsel was very fair to look upon, a virgin, neither had any man known her: and she went down to the well, and filled her pitcher, and came up.');
+INSERT INTO t1(docid,words) VALUES(1024017,'And the servant ran to meet her, and said, Let me, I pray thee, drink a little water of thy pitcher.');
+INSERT INTO t1(docid,words) VALUES(1024018,'And she said, Drink, my lord: and she hasted, and let down her pitcher upon her hand, and gave him drink.');
+INSERT INTO t1(docid,words) VALUES(1024019,'And when she had done giving him drink, she said, I will draw water for thy camels also, until they have done drinking.');
+INSERT INTO t1(docid,words) VALUES(1024020,'And she hasted, and emptied her pitcher into the trough, and ran again unto the well to draw water, and drew for all his camels.');
+INSERT INTO t1(docid,words) VALUES(1024021,'And the man wondering at her held his peace, to wit whether the LORD had made his journey prosperous or not.');
+INSERT INTO t1(docid,words) VALUES(1024022,'And it came to pass, as the camels had done drinking, that the man took a golden earring of half a shekel weight, and two bracelets for her hands of ten shekels weight of gold;');
+INSERT INTO t1(docid,words) VALUES(1024023,'And said, Whose daughter art thou? tell me, I pray thee: is there room in thy father''s house for us to lodge in?');
+INSERT INTO t1(docid,words) VALUES(1024024,'And she said unto him, I am the daughter of Bethuel the son of Milcah, which she bare unto Nahor.');
+INSERT INTO t1(docid,words) VALUES(1024025,'She said moreover unto him, We have both straw and provender enough, and room to lodge in.');
+INSERT INTO t1(docid,words) VALUES(1024026,'And the man bowed down his head, and worshipped the LORD.');
+INSERT INTO t1(docid,words) VALUES(1024027,'And he said, Blessed be the LORD God of my master Abraham, who hath not left destitute my master of his mercy and his truth: I being in the way, the LORD led me to the house of my master''s brethren.');
+INSERT INTO t1(docid,words) VALUES(1024028,'And the damsel ran, and told them of her mother''s house these things.');
+INSERT INTO t1(docid,words) VALUES(1024029,'And Rebekah had a brother, and his name was Laban: and Laban ran out unto the man, unto the well.');
+INSERT INTO t1(docid,words) VALUES(1024030,'And it came to pass, when he saw the earring and bracelets upon his sister''s hands, and when he heard the words of Rebekah his sister, saying, Thus spake the man unto me; that he came unto the man; and, behold, he stood by the camels at the well.');
+INSERT INTO t1(docid,words) VALUES(1024031,'And he said, Come in, thou blessed of the LORD; wherefore standest thou without? for I have prepared the house, and room for the camels.');
+INSERT INTO t1(docid,words) VALUES(1024032,'And the man came into the house: and he ungirded his camels, and gave straw and provender for the camels, and water to wash his feet, and the men''s feet that were with him.');
+INSERT INTO t1(docid,words) VALUES(1024033,'And there was set meat before him to eat: but he said, I will not eat, until I have told mine errand. And he said, Speak on.');
+INSERT INTO t1(docid,words) VALUES(1024034,'And he said, I am Abraham''s servant.');
+INSERT INTO t1(docid,words) VALUES(1024035,'And the LORD hath blessed my master greatly; and he is become great: and he hath given him flocks, and herds, and silver, and gold, and menservants, and maidservants, and camels, and asses.');
+INSERT INTO t1(docid,words) VALUES(1024036,'And Sarah my master''s wife bare a son to my master when she was old: and unto him hath he given all that he hath.');
+INSERT INTO t1(docid,words) VALUES(1024037,'And my master made me swear, saying, Thou shalt not take a wife to my son of the daughters of the Canaanites, in whose land I dwell:');
+INSERT INTO t1(docid,words) VALUES(1024038,'But thou shalt go unto my father''s house, and to my kindred, and take a wife unto my son.');
+INSERT INTO t1(docid,words) VALUES(1024039,'And I said unto my master, Peradventure the woman will not follow me.');
+INSERT INTO t1(docid,words) VALUES(1024040,'And he said unto me, The LORD, before whom I walk, will send his angel with thee, and prosper thy way; and thou shalt take a wife for my son of my kindred, and of my father''s house:');
+INSERT INTO t1(docid,words) VALUES(1024041,'Then shalt thou be clear from this my oath, when thou comest to my kindred; and if they give not thee one, thou shalt be clear from my oath.');
+INSERT INTO t1(docid,words) VALUES(1024042,'And I came this day unto the well, and said, O LORD God of my master Abraham, if now thou do prosper my way which I go:');
+INSERT INTO t1(docid,words) VALUES(1024043,'Behold, I stand by the well of water; and it shall come to pass, that when the virgin cometh forth to draw water, and I say to her, Give me, I pray thee, a little water of thy pitcher to drink;');
+INSERT INTO t1(docid,words) VALUES(1024044,'And she say to me, Both drink thou, and I will also draw for thy camels: let the same be the woman whom the LORD hath appointed out for my master''s son.');
+INSERT INTO t1(docid,words) VALUES(1024045,'And before I had done speaking in mine heart, behold, Rebekah came forth with her pitcher on her shoulder; and she went down unto the well, and drew water: and I said unto her, Let me drink, I pray thee.');
+INSERT INTO t1(docid,words) VALUES(1024046,'And she made haste, and let down her pitcher from her shoulder, and said, Drink, and I will give thy camels drink also: so I drank, and she made the camels drink also.');
+INSERT INTO t1(docid,words) VALUES(1024047,'And I asked her, and said, Whose daughter art thou? And she said, the daughter of Bethuel, Nahor''s son, whom Milcah bare unto him: and I put the earring upon her face, and the bracelets upon her hands.');
+INSERT INTO t1(docid,words) VALUES(1024048,'And I bowed down my head, and worshipped the LORD, and blessed the LORD God of my master Abraham, which had led me in the right way to take my master''s brother''s daughter unto his son.');
+INSERT INTO t1(docid,words) VALUES(1024049,'And now if ye will deal kindly and truly with my master, tell me: and if not, tell me; that I may turn to the right hand, or to the left.');
+INSERT INTO t1(docid,words) VALUES(1024050,'Then Laban and Bethuel answered and said, The thing proceedeth from the LORD: we cannot speak unto thee bad or good.');
+INSERT INTO t1(docid,words) VALUES(1024051,'Behold, Rebekah is before thee, take her, and go, and let her be thy master''s son''s wife, as the LORD hath spoken.');
+INSERT INTO t1(docid,words) VALUES(1024052,'And it came to pass, that, when Abraham''s servant heard their words, he worshipped the LORD, bowing himself to the earth.');
+INSERT INTO t1(docid,words) VALUES(1024053,'And the servant brought forth jewels of silver, and jewels of gold, and raiment, and gave them to Rebekah: he gave also to her brother and to her mother precious things.');
+INSERT INTO t1(docid,words) VALUES(1024054,'And they did eat and drink, he and the men that were with him, and tarried all night; and they rose up in the morning, and he said, Send me away unto my master.');
+INSERT INTO t1(docid,words) VALUES(1024055,'And her brother and her mother said, Let the damsel abide with us a few days, at the least ten; after that she shall go.');
+INSERT INTO t1(docid,words) VALUES(1024056,'And he said unto them, Hinder me not, seeing the LORD hath prospered my way; send me away that I may go to my master.');
+INSERT INTO t1(docid,words) VALUES(1024057,'And they said, We will call the damsel, and enquire at her mouth.');
+INSERT INTO t1(docid,words) VALUES(1024058,'And they called Rebekah, and said unto her, Wilt thou go with this man? And she said, I will go.');
+INSERT INTO t1(docid,words) VALUES(1024059,'And they sent away Rebekah their sister, and her nurse, and Abraham''s servant, and his men.');
+INSERT INTO t1(docid,words) VALUES(1024060,'And they blessed Rebekah, and said unto her, Thou art our sister, be thou the mother of thousands of millions, and let thy seed possess the gate of those which hate them.');
+INSERT INTO t1(docid,words) VALUES(1024061,'And Rebekah arose, and her damsels, and they rode upon the camels, and followed the man: and the servant took Rebekah, and went his way.');
+INSERT INTO t1(docid,words) VALUES(1024062,'And Isaac came from the way of the well Lahairoi; for he dwelt in the south country.');
+INSERT INTO t1(docid,words) VALUES(1024063,'And Isaac went out to meditate in the field at the eventide: and he lifted up his eyes, and saw, and, behold, the camels were coming.');
+INSERT INTO t1(docid,words) VALUES(1024064,'And Rebekah lifted up her eyes, and when she saw Isaac, she lighted off the camel.');
+INSERT INTO t1(docid,words) VALUES(1024065,'For she had said unto the servant, What man is this that walketh in the field to meet us? And the servant had said, It is my master: therefore she took a vail, and covered herself.');
+INSERT INTO t1(docid,words) VALUES(1024066,'And the servant told Isaac all things that he had done.');
+INSERT INTO t1(docid,words) VALUES(1024067,'And Isaac brought her into his mother Sarah''s tent, and took Rebekah, and she became his wife; and he loved her: and Isaac was comforted after his mother''s death.');
+INSERT INTO t1(docid,words) VALUES(1025001,'Then again Abraham took a wife, and her name was Keturah.');
+INSERT INTO t1(docid,words) VALUES(1025002,'And she bare him Zimran, and Jokshan, and Medan, and Midian, and Ishbak, and Shuah.');
+INSERT INTO t1(docid,words) VALUES(1025003,'And Jokshan begat Sheba, and Dedan. And the sons of Dedan were Asshurim, and Letushim, and Leummim.');
+INSERT INTO t1(docid,words) VALUES(1025004,'And the sons of Midian; Ephah, and Epher, and Hanoch, and Abidah, and Eldaah. All these were the children of Keturah.');
+INSERT INTO t1(docid,words) VALUES(1025005,'And Abraham gave all that he had unto Isaac.');
+INSERT INTO t1(docid,words) VALUES(1025006,'But unto the sons of the concubines, which Abraham had, Abraham gave gifts, and sent them away from Isaac his son, while he yet lived, eastward, unto the east country.');
+INSERT INTO t1(docid,words) VALUES(1025007,'And these are the days of the years of Abraham''s life which he lived, an hundred threescore and fifteen years.');
+INSERT INTO t1(docid,words) VALUES(1025008,'Then Abraham gave up the ghost, and died in a good old age, an old man, and full of years; and was gathered to his people.');
+INSERT INTO t1(docid,words) VALUES(1025009,'And his sons Isaac and Ishmael buried him in the cave of Machpelah, in the field of Ephron the son of Zohar the Hittite, which is before Mamre;');
+INSERT INTO t1(docid,words) VALUES(1025010,'The field which Abraham purchased of the sons of Heth: there was Abraham buried, and Sarah his wife.');
+INSERT INTO t1(docid,words) VALUES(1025011,'And it came to pass after the death of Abraham, that God blessed his son Isaac; and Isaac dwelt by the well Lahairoi.');
+INSERT INTO t1(docid,words) VALUES(1025012,'Now these are the generations of Ishmael, Abraham''s son, whom Hagar the Egyptian, Sarah''s handmaid, bare unto Abraham:');
+INSERT INTO t1(docid,words) VALUES(1025013,'And these are the names of the sons of Ishmael, by their names, according to their generations: the firstborn of Ishmael, Nebajoth; and Kedar, and Adbeel, and Mibsam,');
+INSERT INTO t1(docid,words) VALUES(1025014,'And Mishma, and Dumah, and Massa,');
+INSERT INTO t1(docid,words) VALUES(1025015,'Hadar, and Tema, Jetur, Naphish, and Kedemah:');
+INSERT INTO t1(docid,words) VALUES(1025016,'These are the sons of Ishmael, and these are their names, by their towns, and by their castles; twelve princes according to their nations.');
+INSERT INTO t1(docid,words) VALUES(1025017,'And these are the years of the life of Ishmael, an hundred and thirty and seven years: and he gave up the ghost and died; and was gathered unto his people.');
+INSERT INTO t1(docid,words) VALUES(1025018,'And they dwelt from Havilah unto Shur, that is before Egypt, as thou goest toward Assyria: and he died in the presence of all his brethren.');
+INSERT INTO t1(docid,words) VALUES(1025019,'And these are the generations of Isaac, Abraham''s son: Abraham begat Isaac:');
+INSERT INTO t1(docid,words) VALUES(1025020,'And Isaac was forty years old when he took Rebekah to wife, the daughter of Bethuel the Syrian of Padanaram, the sister to Laban the Syrian.');
+INSERT INTO t1(docid,words) VALUES(1025021,'And Isaac intreated the LORD for his wife, because she was barren: and the LORD was intreated of him, and Rebekah his wife conceived.');
+INSERT INTO t1(docid,words) VALUES(1025022,'And the children struggled together within her; and she said, If it be so, why am I thus? And she went to enquire of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1025023,'And the LORD said unto her, Two nations are in thy womb, and two manner of people shall be separated from thy bowels; and the one people shall be stronger than the other people; and the elder shall serve the younger.');
+INSERT INTO t1(docid,words) VALUES(1025024,'And when her days to be delivered were fulfilled, behold, there were twins in her womb.');
+INSERT INTO t1(docid,words) VALUES(1025025,'And the first came out red, all over like an hairy garment; and they called his name Esau.');
+INSERT INTO t1(docid,words) VALUES(1025026,'And after that came his brother out, and his hand took hold on Esau''s heel; and his name was called Jacob: and Isaac was threescore years old when she bare them.');
+INSERT INTO t1(docid,words) VALUES(1025027,'And the boys grew: and Esau was a cunning hunter, a man of the field; and Jacob was a plain man, dwelling in tents.');
+INSERT INTO t1(docid,words) VALUES(1025028,'And Isaac loved Esau, because he did eat of his venison: but Rebekah loved Jacob.');
+INSERT INTO t1(docid,words) VALUES(1025029,'And Jacob sod pottage: and Esau came from the field, and he was faint:');
+INSERT INTO t1(docid,words) VALUES(1025030,'And Esau said to Jacob, Feed me, I pray thee, with that same red pottage; for I am faint: therefore was his name called Edom.');
+INSERT INTO t1(docid,words) VALUES(1025031,'And Jacob said, Sell me this day thy birthright.');
+INSERT INTO t1(docid,words) VALUES(1025032,'And Esau said, Behold, I am at the point to die: and what profit shall this birthright do to me?');
+INSERT INTO t1(docid,words) VALUES(1025033,'And Jacob said, Swear to me this day; and he sware unto him: and he sold his birthright unto Jacob.');
+INSERT INTO t1(docid,words) VALUES(1025034,'Then Jacob gave Esau bread and pottage of lentiles; and he did eat and drink, and rose up, and went his way: thus Esau despised his birthright.');
+INSERT INTO t1(docid,words) VALUES(1026001,'And there was a famine in the land, beside the first famine that was in the days of Abraham. And Isaac went unto Abimelech king of the Philistines unto Gerar.');
+INSERT INTO t1(docid,words) VALUES(1026002,'And the LORD appeared unto him, and said, Go not down into Egypt; dwell in the land which I shall tell thee of:');
+INSERT INTO t1(docid,words) VALUES(1026003,'Sojourn in this land, and I will be with thee, and will bless thee; for unto thee, and unto thy seed, I will give all these countries, and I will perform the oath which I sware unto Abraham thy father;');
+INSERT INTO t1(docid,words) VALUES(1026004,'And I will make thy seed to multiply as the stars of heaven, and will give unto thy seed all these countries; and in thy seed shall all the nations of the earth be blessed;');
+INSERT INTO t1(docid,words) VALUES(1026005,'Because that Abraham obeyed my voice, and kept my charge, my commandments, my statutes, and my laws.');
+INSERT INTO t1(docid,words) VALUES(1026006,'And Isaac dwelt in Gerar:');
+INSERT INTO t1(docid,words) VALUES(1026007,'And the men of the place asked him of his wife; and he said, She is my sister: for he feared to say, She is my wife; lest, said he, the men of the place should kill me for Rebekah; because she was fair to look upon.');
+INSERT INTO t1(docid,words) VALUES(1026008,'And it came to pass, when he had been there a long time, that Abimelech king of the Philistines looked out at a window, and saw, and, behold, Isaac was sporting with Rebekah his wife.');
+INSERT INTO t1(docid,words) VALUES(1026009,'And Abimelech called Isaac, and said, Behold, of a surety she is thy wife; and how saidst thou, She is my sister? And Isaac said unto him, Because I said, Lest I die for her.');
+INSERT INTO t1(docid,words) VALUES(1026010,'And Abimelech said, What is this thou hast done unto us? one of the people might lightly have lien with thy wife, and thou shouldest have brought guiltiness upon us.');
+INSERT INTO t1(docid,words) VALUES(1026011,'And Abimelech charged all his people, saying, He that toucheth this man or his wife shall surely be put to death.');
+INSERT INTO t1(docid,words) VALUES(1026012,'Then Isaac sowed in that land, and received in the same year an hundredfold: and the LORD blessed him.');
+INSERT INTO t1(docid,words) VALUES(1026013,'And the man waxed great, and went forward, and grew until he became very great:');
+INSERT INTO t1(docid,words) VALUES(1026014,'For he had possession of flocks, and possession of herds, and great store of servants: and the Philistines envied him.');
+INSERT INTO t1(docid,words) VALUES(1026015,'For all the wells which his father''s servants had digged in the days of Abraham his father, the Philistines had stopped them, and filled them with earth.');
+INSERT INTO t1(docid,words) VALUES(1026016,'And Abimelech said unto Isaac, Go from us; for thou art much mightier than we.');
+INSERT INTO t1(docid,words) VALUES(1026017,'And Isaac departed thence, and pitched his tent in the valley of Gerar, and dwelt there.');
+INSERT INTO t1(docid,words) VALUES(1026018,'And Isaac digged again the wells of water, which they had digged in the days of Abraham his father; for the Philistines had stopped them after the death of Abraham: and he called their names after the names by which his father had called them.');
+INSERT INTO t1(docid,words) VALUES(1026019,'And Isaac''s servants digged in the valley, and found there a well of springing water.');
+INSERT INTO t1(docid,words) VALUES(1026020,'And the herdmen of Gerar did strive with Isaac''s herdmen, saying, The water is ours: and he called the name of the well Esek; because they strove with him.');
+INSERT INTO t1(docid,words) VALUES(1026021,'And they digged another well, and strove for that also: and he called the name of it Sitnah.');
+INSERT INTO t1(docid,words) VALUES(1026022,'And he removed from thence, and digged another well; and for that they strove not: and he called the name of it Rehoboth; and he said, For now the LORD hath made room for us, and we shall be fruitful in the land.');
+INSERT INTO t1(docid,words) VALUES(1026023,'And he went up from thence to Beersheba.');
+INSERT INTO t1(docid,words) VALUES(1026024,'And the LORD appeared unto him the same night, and said, I am the God of Abraham thy father: fear not, for I am with thee, and will bless thee, and multiply thy seed for my servant Abraham''s sake.');
+INSERT INTO t1(docid,words) VALUES(1026025,'And he builded an altar there, and called upon the name of the LORD, and pitched his tent there: and there Isaac''s servants digged a well.');
+INSERT INTO t1(docid,words) VALUES(1026026,'Then Abimelech went to him from Gerar, and Ahuzzath one of his friends, and Phichol the chief captain of his army.');
+INSERT INTO t1(docid,words) VALUES(1026027,'And Isaac said unto them, Wherefore come ye to me, seeing ye hate me, and have sent me away from you?');
+INSERT INTO t1(docid,words) VALUES(1026028,'And they said, We saw certainly that the LORD was with thee: and we said, Let there be now an oath betwixt us, even betwixt us and thee, and let us make a covenant with thee;');
+INSERT INTO t1(docid,words) VALUES(1026029,'That thou wilt do us no hurt, as we have not touched thee, and as we have done unto thee nothing but good, and have sent thee away in peace: thou art now the blessed of the LORD.');
+INSERT INTO t1(docid,words) VALUES(1026030,'And he made them a feast, and they did eat and drink.');
+INSERT INTO t1(docid,words) VALUES(1026031,'And they rose up betimes in the morning, and sware one to another: and Isaac sent them away, and they departed from him in peace.');
+INSERT INTO t1(docid,words) VALUES(1026032,'And it came to pass the same day, that Isaac''s servants came, and told him concerning the well which they had digged, and said unto him, We have found water.');
+INSERT INTO t1(docid,words) VALUES(1026033,'And he called it Shebah: therefore the name of the city is Beersheba unto this day.');
+INSERT INTO t1(docid,words) VALUES(1026034,'And Esau was forty years old when he took to wife Judith the daughter of Beeri the Hittite, and Bashemath the daughter of Elon the Hittite:');
+INSERT INTO t1(docid,words) VALUES(1026035,'Which were a grief of mind unto Isaac and to Rebekah.');
+INSERT INTO t1(docid,words) VALUES(1027001,'And it came to pass, that when Isaac was old, and his eyes were dim, so that he could not see, he called Esau his eldest son, and said unto him, My son: and he said unto him, Behold, here am I.');
+INSERT INTO t1(docid,words) VALUES(1027002,'And he said, Behold now, I am old, I know not the day of my death:');
+INSERT INTO t1(docid,words) VALUES(1027003,'Now therefore take, I pray thee, thy weapons, thy quiver and thy bow, and go out to the field, and take me some venison;');
+INSERT INTO t1(docid,words) VALUES(1027004,'And make me savoury meat, such as I love, and bring it to me, that I may eat; that my soul may bless thee before I die.');
+INSERT INTO t1(docid,words) VALUES(1027005,'And Rebekah heard when Isaac spake to Esau his son. And Esau went to the field to hunt for venison, and to bring it.');
+INSERT INTO t1(docid,words) VALUES(1027006,'And Rebekah spake unto Jacob her son, saying, Behold, I heard thy father speak unto Esau thy brother, saying,');
+INSERT INTO t1(docid,words) VALUES(1027007,'Bring me venison, and make me savoury meat, that I may eat, and bless thee before the LORD before my death.');
+INSERT INTO t1(docid,words) VALUES(1027008,'Now therefore, my son, obey my voice according to that which I command thee.');
+INSERT INTO t1(docid,words) VALUES(1027009,'Go now to the flock, and fetch me from thence two good kids of the goats; and I will make them savoury meat for thy father, such as he loveth:');
+INSERT INTO t1(docid,words) VALUES(1027010,'And thou shalt bring it to thy father, that he may eat, and that he may bless thee before his death.');
+INSERT INTO t1(docid,words) VALUES(1027011,'And Jacob said to Rebekah his mother, Behold, Esau my brother is a hairy man, and I am a smooth man:');
+INSERT INTO t1(docid,words) VALUES(1027012,'My father peradventure will feel me, and I shall seem to him as a deceiver; and I shall bring a curse upon me, and not a blessing.');
+INSERT INTO t1(docid,words) VALUES(1027013,'And his mother said unto him, Upon me be thy curse, my son: only obey my voice, and go fetch me them.');
+INSERT INTO t1(docid,words) VALUES(1027014,'And he went, and fetched, and brought them to his mother: and his mother made savoury meat, such as his father loved.');
+INSERT INTO t1(docid,words) VALUES(1027015,'And Rebekah took goodly raiment of her eldest son Esau, which were with her in the house, and put them upon Jacob her younger son:');
+INSERT INTO t1(docid,words) VALUES(1027016,'And she put the skins of the kids of the goats upon his hands, and upon the smooth of his neck:');
+INSERT INTO t1(docid,words) VALUES(1027017,'And she gave the savoury meat and the bread, which she had prepared, into the hand of her son Jacob.');
+INSERT INTO t1(docid,words) VALUES(1027018,'And he came unto his father, and said, My father: and he said, Here am I; who art thou, my son?');
+INSERT INTO t1(docid,words) VALUES(1027019,'And Jacob said unto his father, I am Esau thy first born; I have done according as thou badest me: arise, I pray thee, sit and eat of my venison, that thy soul may bless me.');
+INSERT INTO t1(docid,words) VALUES(1027020,'And Isaac said unto his son, How is it that thou hast found it so quickly, my son? And he said, Because the LORD thy God brought it to me.');
+INSERT INTO t1(docid,words) VALUES(1027021,'And Isaac said unto Jacob, Come near, I pray thee, that I may feel thee, my son, whether thou be my very son Esau or not.');
+INSERT INTO t1(docid,words) VALUES(1027022,'And Jacob went near unto Isaac his father; and he felt him, and said, The voice is Jacob''s voice, but the hands are the hands of Esau.');
+INSERT INTO t1(docid,words) VALUES(1027023,'And he discerned him not, because his hands were hairy, as his brother Esau''s hands: so he blessed him.');
+INSERT INTO t1(docid,words) VALUES(1027024,'And he said, Art thou my very son Esau? And he said, I am.');
+INSERT INTO t1(docid,words) VALUES(1027025,'And he said, Bring it near to me, and I will eat of my son''s venison, that my soul may bless thee. And he brought it near to him, and he did eat: and he brought him wine and he drank.');
+INSERT INTO t1(docid,words) VALUES(1027026,'And his father Isaac said unto him, Come near now, and kiss me, my son.');
+INSERT INTO t1(docid,words) VALUES(1027027,'And he came near, and kissed him: and he smelled the smell of his raiment, and blessed him, and said, See, the smell of my son is as the smell of a field which the LORD hath blessed:');
+INSERT INTO t1(docid,words) VALUES(1027028,'Therefore God give thee of the dew of heaven, and the fatness of the earth, and plenty of corn and wine:');
+INSERT INTO t1(docid,words) VALUES(1027029,'Let people serve thee, and nations bow down to thee: be lord over thy brethren, and let thy mother''s sons bow down to thee: cursed be every one that curseth thee, and blessed be he that blesseth thee.');
+INSERT INTO t1(docid,words) VALUES(1027030,'And it came to pass, as soon as Isaac had made an end of blessing Jacob, and Jacob was yet scarce gone out from the presence of Isaac his father, that Esau his brother came in from his hunting.');
+INSERT INTO t1(docid,words) VALUES(1027031,'And he also had made savoury meat, and brought it unto his father, and said unto his father, Let my father arise, and eat of his son''s venison, that thy soul may bless me.');
+INSERT INTO t1(docid,words) VALUES(1027032,'And Isaac his father said unto him, Who art thou? And he said, I am thy son, thy firstborn Esau.');
+INSERT INTO t1(docid,words) VALUES(1027033,'And Isaac trembled very exceedingly, and said, Who? where is he that hath taken venison, and brought it me, and I have eaten of all before thou camest, and have blessed him? yea, and he shall be blessed.');
+INSERT INTO t1(docid,words) VALUES(1027034,'And when Esau heard the words of his father, he cried with a great and exceeding bitter cry, and said unto his father, Bless me, even me also, O my father.');
+INSERT INTO t1(docid,words) VALUES(1027035,'And he said, Thy brother came with subtilty, and hath taken away thy blessing.');
+INSERT INTO t1(docid,words) VALUES(1027036,'And he said, Is not he rightly named Jacob? for he hath supplanted me these two times: he took away my birthright; and, behold, now he hath taken away my blessing. And he said, Hast thou not reserved a blessing for me?');
+INSERT INTO t1(docid,words) VALUES(1027037,'And Isaac answered and said unto Esau, Behold, I have made him thy lord, and all his brethren have I given to him for servants; and with corn and wine have I sustained him: and what shall I do now unto thee, my son?');
+INSERT INTO t1(docid,words) VALUES(1027038,'And Esau said unto his father, Hast thou but one blessing, my father? bless me, even me also, O my father. And Esau lifted up his voice, and wept.');
+INSERT INTO t1(docid,words) VALUES(1027039,'And Isaac his father answered and said unto him, Behold, thy dwelling shall be the fatness of the earth, and of the dew of heaven from above;');
+INSERT INTO t1(docid,words) VALUES(1027040,'And by thy sword shalt thou live, and shalt serve thy brother; and it shall come to pass when thou shalt have the dominion, that thou shalt break his yoke from off thy neck.');
+INSERT INTO t1(docid,words) VALUES(1027041,'And Esau hated Jacob because of the blessing wherewith his father blessed him: and Esau said in his heart, The days of mourning for my father are at hand; then will I slay my brother Jacob.');
+INSERT INTO t1(docid,words) VALUES(1027042,'And these words of Esau her elder son were told to Rebekah: and she sent and called Jacob her younger son, and said unto him, Behold, thy brother Esau, as touching thee, doth comfort himself, purposing to kill thee.');
+INSERT INTO t1(docid,words) VALUES(1027043,'Now therefore, my son, obey my voice; arise, flee thou to Laban my brother to Haran;');
+INSERT INTO t1(docid,words) VALUES(1027044,'And tarry with him a few days, until thy brother''s fury turn away;');
+INSERT INTO t1(docid,words) VALUES(1027045,'Until thy brother''s anger turn away from thee, and he forget that which thou hast done to him: then I will send, and fetch thee from thence: why should I be deprived also of you both in one day?');
+INSERT INTO t1(docid,words) VALUES(1027046,'And Rebekah said to Isaac, I am weary of my life because of the daughters of Heth: if Jacob take a wife of the daughters of Heth, such as these which are of the daughters of the land, what good shall my life do me?');
+INSERT INTO t1(docid,words) VALUES(1028001,'And Isaac called Jacob, and blessed him, and charged him, and said unto him, Thou shalt not take a wife of the daughters of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1028002,'Arise, go to Padanaram, to the house of Bethuel thy mother''s father; and take thee a wife from thence of the daughers of Laban thy mother''s brother.');
+INSERT INTO t1(docid,words) VALUES(1028003,'And God Almighty bless thee, and make thee fruitful, and multiply thee, that thou mayest be a multitude of people;');
+INSERT INTO t1(docid,words) VALUES(1028004,'And give thee the blessing of Abraham, to thee, and to thy seed with thee; that thou mayest inherit the land wherein thou art a stranger, which God gave unto Abraham.');
+INSERT INTO t1(docid,words) VALUES(1028005,'And Isaac sent away Jacob: and he went to Padanaram unto Laban, son of Bethuel the Syrian, the brother of Rebekah, Jacob''s and Esau''s mother.');
+INSERT INTO t1(docid,words) VALUES(1028006,'When Esau saw that Isaac had blessed Jacob, and sent him away to Padanaram, to take him a wife from thence; and that as he blessed him he gave him a charge, saying, Thou shalt not take a wife of the daughers of Canaan;');
+INSERT INTO t1(docid,words) VALUES(1028007,'And that Jacob obeyed his father and his mother, and was gone to Padanaram;');
+INSERT INTO t1(docid,words) VALUES(1028008,'And Esau seeing that the daughters of Canaan pleased not Isaac his father;');
+INSERT INTO t1(docid,words) VALUES(1028009,'Then went Esau unto Ishmael, and took unto the wives which he had Mahalath the daughter of Ishmael Abraham''s son, the sister of Nebajoth, to be his wife.');
+INSERT INTO t1(docid,words) VALUES(1028010,'And Jacob went out from Beersheba, and went toward Haran.');
+INSERT INTO t1(docid,words) VALUES(1028011,'And he lighted upon a certain place, and tarried there all night, because the sun was set; and he took of the stones of that place, and put them for his pillows, and lay down in that place to sleep.');
+INSERT INTO t1(docid,words) VALUES(1028012,'And he dreamed, and behold a ladder set up on the earth, and the top of it reached to heaven: and behold the angels of God ascending and descending on it.');
+INSERT INTO t1(docid,words) VALUES(1028013,'And, behold, the LORD stood above it, and said, I am the LORD God of Abraham thy father, and the God of Isaac: the land whereon thou liest, to thee will I give it, and to thy seed;');
+INSERT INTO t1(docid,words) VALUES(1028014,'And thy seed shall be as the dust of the earth, and thou shalt spread abroad to the west, and to the east, and to the north, and to the south: and in thee and in thy seed shall all the families of the earth be blessed.');
+INSERT INTO t1(docid,words) VALUES(1028015,'And, behold, I am with thee, and will keep thee in all places whither thou goest, and will bring thee again into this land; for I will not leave thee, until I have done that which I have spoken to thee of.');
+INSERT INTO t1(docid,words) VALUES(1028016,'And Jacob awaked out of his sleep, and he said, Surely the LORD is in this place; and I knew it not.');
+INSERT INTO t1(docid,words) VALUES(1028017,'And he was afraid, and said, How dreadful is this place! this is none other but the house of God, and this is the gate of heaven.');
+INSERT INTO t1(docid,words) VALUES(1028018,'And Jacob rose up early in the morning, and took the stone that he had put for his pillows, and set it up for a pillar, and poured oil upon the top of it.');
+INSERT INTO t1(docid,words) VALUES(1028019,'And he called the name of that place Bethel: but the name of that city was called Luz at the first.');
+INSERT INTO t1(docid,words) VALUES(1028020,'And Jacob vowed a vow, saying, If God will be with me, and will keep me in this way that I go, and will give me bread to eat, and raiment to put on,');
+INSERT INTO t1(docid,words) VALUES(1028021,'So that I come again to my father''s house in peace; then shall the LORD be my God:');
+INSERT INTO t1(docid,words) VALUES(1028022,'And this stone, which I have set for a pillar, shall be God''s house: and of all that thou shalt give me I will surely give the tenth unto thee.');
+INSERT INTO t1(docid,words) VALUES(1029001,'Then Jacob went on his journey, and came into the land of the people of the east.');
+INSERT INTO t1(docid,words) VALUES(1029002,'And he looked, and behold a well in the field, and, lo, there were three flocks of sheep lying by it; for out of that well they watered the flocks: and a great stone was upon the well''s mouth.');
+INSERT INTO t1(docid,words) VALUES(1029003,'And thither were all the flocks gathered: and they rolled the stone from the well''s mouth, and watered the sheep, and put the stone again upon the well''s mouth in his place.');
+INSERT INTO t1(docid,words) VALUES(1029004,'And Jacob said unto them, My brethren, whence be ye? And they said, Of Haran are we.');
+INSERT INTO t1(docid,words) VALUES(1029005,'And he said unto them, Know ye Laban the son of Nahor? And they said, We know him.');
+INSERT INTO t1(docid,words) VALUES(1029006,'And he said unto them, Is he well? And they said, He is well: and, behold, Rachel his daughter cometh with the sheep.');
+INSERT INTO t1(docid,words) VALUES(1029007,'And he said, Lo, it is yet high day, neither is it time that the cattle should be gathered together: water ye the sheep, and go and feed them.');
+INSERT INTO t1(docid,words) VALUES(1029008,'And they said, We cannot, until all the flocks be gathered together, and till they roll the stone from the well''s mouth; then we water the sheep.');
+INSERT INTO t1(docid,words) VALUES(1029009,'And while he yet spake with them, Rachel came with her father''s sheep; for she kept them.');
+INSERT INTO t1(docid,words) VALUES(1029010,'And it came to pass, when Jacob saw Rachel the daughter of Laban his mother''s brother, and the sheep of Laban his mother''s brother, that Jacob went near, and rolled the stone from the well''s mouth, and watered the flock of Laban his mother''s brother.');
+INSERT INTO t1(docid,words) VALUES(1029011,'And Jacob kissed Rachel, and lifted up his voice, and wept.');
+INSERT INTO t1(docid,words) VALUES(1029012,'And Jacob told Rachel that he was her father''s brother, and that he was Rebekah''s son: and she ran and told her father.');
+INSERT INTO t1(docid,words) VALUES(1029013,'And it came to pass, when Laban heard the tidings of Jacob his sister''s son, that he ran to meet him, and embraced him, and kissed him, and brought him to his house. And he told Laban all these things.');
+INSERT INTO t1(docid,words) VALUES(1029014,'And Laban said to him, Surely thou art my bone and my flesh. And he abode with him the space of a month.');
+INSERT INTO t1(docid,words) VALUES(1029015,'And Laban said unto Jacob, Because thou art my brother, shouldest thou therefore serve me for nought? tell me, what shall thy wages be?');
+INSERT INTO t1(docid,words) VALUES(1029016,'And Laban had two daughters: the name of the elder was Leah, and the name of the younger was Rachel.');
+INSERT INTO t1(docid,words) VALUES(1029017,'Leah was tender eyed; but Rachel was beautiful and well favoured.');
+INSERT INTO t1(docid,words) VALUES(1029018,'And Jacob loved Rachel; and said, I will serve thee seven years for Rachel thy younger daughter.');
+INSERT INTO t1(docid,words) VALUES(1029019,'And Laban said, It is better that I give her to thee, than that I should give her to another man: abide with me.');
+INSERT INTO t1(docid,words) VALUES(1029020,'And Jacob served seven years for Rachel; and they seemed unto him but a few days, for the love he had to her.');
+INSERT INTO t1(docid,words) VALUES(1029021,'And Jacob said unto Laban, Give me my wife, for my days are fulfilled, that I may go in unto her.');
+INSERT INTO t1(docid,words) VALUES(1029022,'And Laban gathered together all the men of the place, and made a feast.');
+INSERT INTO t1(docid,words) VALUES(1029023,'And it came to pass in the evening, that he took Leah his daughter, and brought her to him; and he went in unto her.');
+INSERT INTO t1(docid,words) VALUES(1029024,'And Laban gave unto his daughter Leah Zilpah his maid for an handmaid.');
+INSERT INTO t1(docid,words) VALUES(1029025,'And it came to pass, that in the morning, behold, it was Leah: and he said to Laban, What is this thou hast done unto me? did not I serve with thee for Rachel? wherefore then hast thou beguiled me?');
+INSERT INTO t1(docid,words) VALUES(1029026,'And Laban said, It must not be so done in our country, to give the younger before the firstborn.');
+INSERT INTO t1(docid,words) VALUES(1029027,'Fulfil her week, and we will give thee this also for the service which thou shalt serve with me yet seven other years.');
+INSERT INTO t1(docid,words) VALUES(1029028,'And Jacob did so, and fulfilled her week: and he gave him Rachel his daughter to wife also.');
+INSERT INTO t1(docid,words) VALUES(1029029,'And Laban gave to Rachel his daughter Bilhah his handmaid to be her maid.');
+INSERT INTO t1(docid,words) VALUES(1029030,'And he went in also unto Rachel, and he loved also Rachel more than Leah, and served with him yet seven other years.');
+INSERT INTO t1(docid,words) VALUES(1029031,'And when the LORD saw that Leah was hated, he opened her womb: but Rachel was barren.');
+INSERT INTO t1(docid,words) VALUES(1029032,'And Leah conceived, and bare a son, and she called his name Reuben: for she said, Surely the LORD hath looked upon my affliction; now therefore my husband will love me.');
+INSERT INTO t1(docid,words) VALUES(1029033,'And she conceived again, and bare a son; and said, Because the LORD hath heard I was hated, he hath therefore given me this son also: and she called his name Simeon.');
+INSERT INTO t1(docid,words) VALUES(1029034,'And she conceived again, and bare a son; and said, Now this time will my husband be joined unto me, because I have born him three sons: therefore was his name called Levi.');
+INSERT INTO t1(docid,words) VALUES(1029035,'And she conceived again, and bare a son: and she said, Now will I praise the LORD: therefore she called his name Judah; and left bearing.');
+INSERT INTO t1(docid,words) VALUES(1030001,'And when Rachel saw that she bare Jacob no children, Rachel envied her sister; and said unto Jacob, Give me children, or else I die.');
+INSERT INTO t1(docid,words) VALUES(1030002,'And Jacob''s anger was kindled against Rachel: and he said, Am I in God''s stead, who hath withheld from thee the fruit of the womb?');
+INSERT INTO t1(docid,words) VALUES(1030003,'And she said, Behold my maid Bilhah, go in unto her; and she shall bear upon my knees, that I may also have children by her.');
+INSERT INTO t1(docid,words) VALUES(1030004,'And she gave him Bilhah her handmaid to wife: and Jacob went in unto her.');
+INSERT INTO t1(docid,words) VALUES(1030005,'And Bilhah conceived, and bare Jacob a son.');
+INSERT INTO t1(docid,words) VALUES(1030006,'And Rachel said, God hath judged me, and hath also heard my voice, and hath given me a son: therefore called she his name Dan.');
+INSERT INTO t1(docid,words) VALUES(1030007,'And Bilhah Rachel''s maid conceived again, and bare Jacob a second son.');
+INSERT INTO t1(docid,words) VALUES(1030008,'And Rachel said, With great wrestlings have I wrestled with my sister, and I have prevailed: and she called his name Naphtali.');
+INSERT INTO t1(docid,words) VALUES(1030009,'When Leah saw that she had left bearing, she took Zilpah her maid, and gave her Jacob to wife.');
+INSERT INTO t1(docid,words) VALUES(1030010,'And Zilpah Leah''s maid bare Jacob a son.');
+INSERT INTO t1(docid,words) VALUES(1030011,'And Leah said, A troop cometh: and she called his name Gad.');
+INSERT INTO t1(docid,words) VALUES(1030012,'And Zilpah Leah''s maid bare Jacob a second son.');
+INSERT INTO t1(docid,words) VALUES(1030013,'And Leah said, Happy am I, for the daughters will call me blessed: and she called his name Asher.');
+INSERT INTO t1(docid,words) VALUES(1030014,'And Reuben went in the days of wheat harvest, and found mandrakes in the field, and brought them unto his mother Leah. Then Rachel said to Leah, Give me, I pray thee, of thy son''s mandrakes.');
+INSERT INTO t1(docid,words) VALUES(1030015,'And she said unto her, Is it a small matter that thou hast taken my husband? and wouldest thou take away my son''s mandrakes also? And Rachel said, Therefore he shall lie with thee to night for thy son''s mandrakes.');
+INSERT INTO t1(docid,words) VALUES(1030016,'And Jacob came out of the field in the evening, and Leah went out to meet him, and said, Thou must come in unto me; for surely I have hired thee with my son''s mandrakes. And he lay with her that night.');
+INSERT INTO t1(docid,words) VALUES(1030017,'And God hearkened unto Leah, and she conceived, and bare Jacob the fifth son.');
+INSERT INTO t1(docid,words) VALUES(1030018,'And Leah said, God hath given me my hire, because I have given my maiden to my husband: and she called his name Issachar.');
+INSERT INTO t1(docid,words) VALUES(1030019,'And Leah conceived again, and bare Jacob the sixth son.');
+INSERT INTO t1(docid,words) VALUES(1030020,'And Leah said, God hath endued me with a good dowry; now will my husband dwell with me, because I have born him six sons: and she called his name Zebulun.');
+INSERT INTO t1(docid,words) VALUES(1030021,'And afterwards she bare a daughter, and called her name Dinah.');
+INSERT INTO t1(docid,words) VALUES(1030022,'And God remembered Rachel, and God hearkened to her, and opened her womb.');
+INSERT INTO t1(docid,words) VALUES(1030023,'And she conceived, and bare a son; and said, God hath taken away my reproach:');
+INSERT INTO t1(docid,words) VALUES(1030024,'And she called his name Joseph; and said, The LORD shall add to me another son.');
+INSERT INTO t1(docid,words) VALUES(1030025,'And it came to pass, when Rachel had born Joseph, that Jacob said unto Laban, Send me away, that I may go unto mine own place, and to my country.');
+INSERT INTO t1(docid,words) VALUES(1030026,'Give me my wives and my children, for whom I have served thee, and let me go: for thou knowest my service which I have done thee.');
+INSERT INTO t1(docid,words) VALUES(1030027,'And Laban said unto him, I pray thee, if I have found favour in thine eyes, tarry: for I have learned by experience that the LORD hath blessed me for thy sake.');
+INSERT INTO t1(docid,words) VALUES(1030028,'And he said, Appoint me thy wages, and I will give it.');
+INSERT INTO t1(docid,words) VALUES(1030029,'And he said unto him, Thou knowest how I have served thee, and how thy cattle was with me.');
+INSERT INTO t1(docid,words) VALUES(1030030,'For it was little which thou hadst before I came, and it is now increased unto a multitude; and the LORD hath blessed thee since my coming: and now when shall I provide for mine own house also?');
+INSERT INTO t1(docid,words) VALUES(1030031,'And he said, What shall I give thee? And Jacob said, Thou shalt not give me any thing: if thou wilt do this thing for me, I will again feed and keep thy flock.');
+INSERT INTO t1(docid,words) VALUES(1030032,'I will pass through all thy flock to day, removing from thence all the speckled and spotted cattle, and all the brown cattle among the sheep, and the spotted and speckled among the goats: and of such shall be my hire.');
+INSERT INTO t1(docid,words) VALUES(1030033,'So shall my righteousness answer for me in time to come, when it shall come for my hire before thy face: every one that is not speckled and spotted among the goats, and brown among the sheep, that shall be counted stolen with me.');
+INSERT INTO t1(docid,words) VALUES(1030034,'And Laban said, Behold, I would it might be according to thy word.');
+INSERT INTO t1(docid,words) VALUES(1030035,'And he removed that day the he goats that were ringstraked and spotted, and all the she goats that were speckled and spotted, and every one that had some white in it, and all the brown among the sheep, and gave them into the hand of his sons.');
+INSERT INTO t1(docid,words) VALUES(1030036,'And he set three days'' journey betwixt himself and Jacob: and Jacob fed the rest of Laban''s flocks.');
+INSERT INTO t1(docid,words) VALUES(1030037,'And Jacob took him rods of green poplar, and of the hazel and chesnut tree; and pilled white strakes in them, and made the white appear which was in the rods.');
+INSERT INTO t1(docid,words) VALUES(1030038,'And he set the rods which he had pilled before the flocks in the gutters in the watering troughs when the flocks came to drink, that they should conceive when they came to drink.');
+INSERT INTO t1(docid,words) VALUES(1030039,'And the flocks conceived before the rods, and brought forth cattle ringstraked, speckled, and spotted.');
+INSERT INTO t1(docid,words) VALUES(1030040,'And Jacob did separate the lambs, and set the faces of the flocks toward the ringstraked, and all the brown in the flock of Laban; and he put his own flocks by themselves, and put them not unto Laban''s cattle.');
+INSERT INTO t1(docid,words) VALUES(1030041,'And it came to pass, whensoever the stronger cattle did conceive, that Jacob laid the rods before the eyes of the cattle in the gutters, that they might conceive among the rods.');
+INSERT INTO t1(docid,words) VALUES(1030042,'But when the cattle were feeble, he put them not in: so the feebler were Laban''s, and the stronger Jacob''s.');
+INSERT INTO t1(docid,words) VALUES(1030043,'And the man increased exceedingly, and had much cattle, and maidservants, and menservants, and camels, and asses.');
+INSERT INTO t1(docid,words) VALUES(1031001,'And he heard the words of Laban''s sons, saying, Jacob hath taken away all that was our father''s; and of that which was our father''s hath he gotten all this glory.');
+INSERT INTO t1(docid,words) VALUES(1031002,'And Jacob beheld the countenance of Laban, and, behold, it was not toward him as before.');
+INSERT INTO t1(docid,words) VALUES(1031003,'And the LORD said unto Jacob, Return unto the land of thy fathers, and to thy kindred; and I will be with thee.');
+INSERT INTO t1(docid,words) VALUES(1031004,'And Jacob sent and called Rachel and Leah to the field unto his flock,');
+INSERT INTO t1(docid,words) VALUES(1031005,'And said unto them, I see your father''s countenance, that it is not toward me as before; but the God of my father hath been with me.');
+INSERT INTO t1(docid,words) VALUES(1031006,'And ye know that with all my power I have served your father.');
+INSERT INTO t1(docid,words) VALUES(1031007,'And your father hath deceived me, and changed my wages ten times; but God suffered him not to hurt me.');
+INSERT INTO t1(docid,words) VALUES(1031008,'If he said thus, The speckled shall be thy wages; then all the cattle bare speckled: and if he said thus, The ringstraked shall be thy hire; then bare all the cattle ringstraked.');
+INSERT INTO t1(docid,words) VALUES(1031009,'Thus God hath taken away the cattle of your father, and given them to me.');
+INSERT INTO t1(docid,words) VALUES(1031010,'And it came to pass at the time that the cattle conceived, that I lifted up mine eyes, and saw in a dream, and, behold, the rams which leaped upon the cattle were ringstraked, speckled, and grisled.');
+INSERT INTO t1(docid,words) VALUES(1031011,'And the angel of God spake unto me in a dream, saying, Jacob: And I said, Here am I.');
+INSERT INTO t1(docid,words) VALUES(1031012,'And he said, Lift up now thine eyes, and see, all the rams which leap upon the cattle are ringstraked, speckled, and grisled: for I have seen all that Laban doeth unto thee.');
+INSERT INTO t1(docid,words) VALUES(1031013,'I am the God of Bethel, where thou anointedst the pillar, and where thou vowedst a vow unto me: now arise, get thee out from this land, and return unto the land of thy kindred.');
+INSERT INTO t1(docid,words) VALUES(1031014,'And Rachel and Leah answered and said unto him, Is there yet any portion or inheritance for us in our father''s house?');
+INSERT INTO t1(docid,words) VALUES(1031015,'Are we not counted of him strangers? for he hath sold us, and hath quite devoured also our money.');
+INSERT INTO t1(docid,words) VALUES(1031016,'For all the riches which God hath taken from our father, that is ours, and our children''s: now then, whatsoever God hath said unto thee, do.');
+INSERT INTO t1(docid,words) VALUES(1031017,'Then Jacob rose up, and set his sons and his wives upon camels;');
+INSERT INTO t1(docid,words) VALUES(1031018,'And he carried away all his cattle, and all his goods which he had gotten, the cattle of his getting, which he had gotten in Padanaram, for to go to Isaac his father in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1031019,'And Laban went to shear his sheep: and Rachel had stolen the images that were her father''s.');
+INSERT INTO t1(docid,words) VALUES(1031020,'And Jacob stole away unawares to Laban the Syrian, in that he told him not that he fled.');
+INSERT INTO t1(docid,words) VALUES(1031021,'So he fled with all that he had; and he rose up, and passed over the river, and set his face toward the mount Gilead.');
+INSERT INTO t1(docid,words) VALUES(1031022,'And it was told Laban on the third day that Jacob was fled.');
+INSERT INTO t1(docid,words) VALUES(1031023,'And he took his brethren with him, and pursued after him seven days'' journey; and they overtook him in the mount Gilead.');
+INSERT INTO t1(docid,words) VALUES(1031024,'And God came to Laban the Syrian in a dream by night, and said unto him, Take heed that thou speak not to Jacob either good or bad.');
+INSERT INTO t1(docid,words) VALUES(1031025,'Then Laban overtook Jacob. Now Jacob had pitched his tent in the mount: and Laban with his brethren pitched in the mount of Gilead.');
+INSERT INTO t1(docid,words) VALUES(1031026,'And Laban said to Jacob, What hast thou done, that thou hast stolen away unawares to me, and carried away my daughters, as captives taken with the sword?');
+INSERT INTO t1(docid,words) VALUES(1031027,'Wherefore didst thou flee away secretly, and steal away from me; and didst not tell me, that I might have sent thee away with mirth, and with songs, with tabret, and with harp?');
+INSERT INTO t1(docid,words) VALUES(1031028,'And hast not suffered me to kiss my sons and my daughters? thou hast now done foolishly in so doing.');
+INSERT INTO t1(docid,words) VALUES(1031029,'It is in the power of my hand to do you hurt: but the God of your father spake unto me yesternight, saying, Take thou heed that thou speak not to Jacob either good or bad.');
+INSERT INTO t1(docid,words) VALUES(1031030,'And now, though thou wouldest needs be gone, because thou sore longedst after thy father''s house, yet wherefore hast thou stolen my gods?');
+INSERT INTO t1(docid,words) VALUES(1031031,'And Jacob answered and said to Laban, Because I was afraid: for I said, Peradventure thou wouldest take by force thy daughters from me.');
+INSERT INTO t1(docid,words) VALUES(1031032,'With whomsoever thou findest thy gods, let him not live: before our brethren discern thou what is thine with me, and take it to thee. For Jacob knew not that Rachel had stolen them.');
+INSERT INTO t1(docid,words) VALUES(1031033,'And Laban went into Jacob''s tent, and into Leah''s tent, and into the two maidservants'' tents; but he found them not. Then went he out of Leah''s tent, and entered into Rachel''s tent.');
+INSERT INTO t1(docid,words) VALUES(1031034,'Now Rachel had taken the images, and put them in the camel''s furniture, and sat upon them. And Laban searched all the tent, but found them not.');
+INSERT INTO t1(docid,words) VALUES(1031035,'And she said to her father, Let it not displease my lord that I cannot rise up before thee; for the custom of women is upon me. And he searched but found not the images.');
+INSERT INTO t1(docid,words) VALUES(1031036,'And Jacob was wroth, and chode with Laban: and Jacob answered and said to Laban, What is my trespass? what is my sin, that thou hast so hotly pursued after me?');
+INSERT INTO t1(docid,words) VALUES(1031037,'Whereas thou hast searched all my stuff, what hast thou found of all thy household stuff? set it here before my brethren and thy brethren, that they may judge betwixt us both.');
+INSERT INTO t1(docid,words) VALUES(1031038,'This twenty years have I been with thee; thy ewes and thy she goats have not cast their young, and the rams of thy flock have I not eaten.');
+INSERT INTO t1(docid,words) VALUES(1031039,'That which was torn of beasts I brought not unto thee; I bare the loss of it; of my hand didst thou require it, whether stolen by day, or stolen by night.');
+INSERT INTO t1(docid,words) VALUES(1031040,'Thus I was; in the day the drought consumed me, and the frost by night; and my sleep departed from mine eyes.');
+INSERT INTO t1(docid,words) VALUES(1031041,'Thus have I been twenty years in thy house; I served thee fourteen years for thy two daughters, and six years for thy cattle: and thou hast changed my wages ten times.');
+INSERT INTO t1(docid,words) VALUES(1031042,'Except the God of my father, the God of Abraham, and the fear of Isaac, had been with me, surely thou hadst sent me away now empty. God hath seen mine affliction and the labour of my hands, and rebuked thee yesternight.');
+INSERT INTO t1(docid,words) VALUES(1031043,'And Laban answered and said unto Jacob, These daughters are my daughters, and these children are my children, and these cattle are my cattle, and all that thou seest is mine: and what can I do this day unto these my daughters, or unto their children which they have born?');
+INSERT INTO t1(docid,words) VALUES(1031044,'Now therefore come thou, let us make a covenant, I and thou; and let it be for a witness between me and thee.');
+INSERT INTO t1(docid,words) VALUES(1031045,'And Jacob took a stone, and set it up for a pillar.');
+INSERT INTO t1(docid,words) VALUES(1031046,'And Jacob said unto his brethren, Gather stones; and they took stones, and made an heap: and they did eat there upon the heap.');
+INSERT INTO t1(docid,words) VALUES(1031047,'And Laban called it Jegarsahadutha: but Jacob called it Galeed.');
+INSERT INTO t1(docid,words) VALUES(1031048,'And Laban said, This heap is a witness between me and thee this day. Therefore was the name of it called Galeed;');
+INSERT INTO t1(docid,words) VALUES(1031049,'And Mizpah; for he said, The LORD watch between me and thee, when we are absent one from another.');
+INSERT INTO t1(docid,words) VALUES(1031050,'If thou shalt afflict my daughters, or if thou shalt take other wives beside my daughters, no man is with us; see, God is witness betwixt me and thee.');
+INSERT INTO t1(docid,words) VALUES(1031051,'And Laban said to Jacob, Behold this heap, and behold this pillar, which I have cast betwixt me and thee:');
+INSERT INTO t1(docid,words) VALUES(1031052,'This heap be witness, and this pillar be witness, that I will not pass over this heap to thee, and that thou shalt not pass over this heap and this pillar unto me, for harm.');
+INSERT INTO t1(docid,words) VALUES(1031053,'The God of Abraham, and the God of Nahor, the God of their father, judge betwixt us. And Jacob sware by the fear of his father Isaac.');
+INSERT INTO t1(docid,words) VALUES(1031054,'Then Jacob offered sacrifice upon the mount, and called his brethren to eat bread: and they did eat bread, and tarried all night in the mount.');
+INSERT INTO t1(docid,words) VALUES(1031055,'And early in the morning Laban rose up, and kissed his sons and his daughters, and blessed them: and Laban departed, and returned unto his place.');
+INSERT INTO t1(docid,words) VALUES(1032001,'And Jacob went on his way, and the angels of God met him.');
+INSERT INTO t1(docid,words) VALUES(1032002,'And when Jacob saw them, he said, This is God''s host: and he called the name of that place Mahanaim.');
+INSERT INTO t1(docid,words) VALUES(1032003,'And Jacob sent messengers before him to Esau his brother unto the land of Seir, the country of Edom.');
+INSERT INTO t1(docid,words) VALUES(1032004,'And he commanded them, saying, Thus shall ye speak unto my lord Esau; Thy servant Jacob saith thus, I have sojourned with Laban, and stayed there until now:');
+INSERT INTO t1(docid,words) VALUES(1032005,'And I have oxen, and asses, flocks, and menservants, and womenservants: and I have sent to tell my lord, that I may find grace in thy sight.');
+INSERT INTO t1(docid,words) VALUES(1032006,'And the messengers returned to Jacob, saying, We came to thy brother Esau, and also he cometh to meet thee, and four hundred men with him.');
+INSERT INTO t1(docid,words) VALUES(1032007,'Then Jacob was greatly afraid and distressed: and he divided the people that was with him, and the flocks, and herds, and the camels, into two bands;');
+INSERT INTO t1(docid,words) VALUES(1032008,'And said, If Esau come to the one company, and smite it, then the other company which is left shall escape.');
+INSERT INTO t1(docid,words) VALUES(1032009,'And Jacob said, O God of my father Abraham, and God of my father Isaac, the LORD which saidst unto me, Return unto thy country, and to thy kindred, and I will deal well with thee:');
+INSERT INTO t1(docid,words) VALUES(1032010,'I am not worthy of the least of all the mercies, and of all the truth, which thou hast shewed unto thy servant; for with my staff I passed over this Jordan; and now I am become two bands.');
+INSERT INTO t1(docid,words) VALUES(1032011,'Deliver me, I pray thee, from the hand of my brother, from the hand of Esau: for I fear him, lest he will come and smite me, and the mother with the children.');
+INSERT INTO t1(docid,words) VALUES(1032012,'And thou saidst, I will surely do thee good, and make thy seed as the sand of the sea, which cannot be numbered for multitude.');
+INSERT INTO t1(docid,words) VALUES(1032013,'And he lodged there that same night; and took of that which came to his hand a present for Esau his brother;');
+INSERT INTO t1(docid,words) VALUES(1032014,'Two hundred she goats, and twenty he goats, two hundred ewes, and twenty rams,');
+INSERT INTO t1(docid,words) VALUES(1032015,'Thirty milch camels with their colts, forty kine, and ten bulls, twenty she asses, and ten foals.');
+INSERT INTO t1(docid,words) VALUES(1032016,'And he delivered them into the hand of his servants, every drove by themselves; and said unto his servants, Pass over before me, and put a space betwixt drove and drove.');
+INSERT INTO t1(docid,words) VALUES(1032017,'And he commanded the foremost, saying, When Esau my brother meeteth thee, and asketh thee, saying, Whose art thou? and whither goest thou? and whose are these before thee?');
+INSERT INTO t1(docid,words) VALUES(1032018,'Then thou shalt say, They be thy servant Jacob''s; it is a present sent unto my lord Esau: and, behold, also he is behind us.');
+INSERT INTO t1(docid,words) VALUES(1032019,'And so commanded he the second, and the third, and all that followed the droves, saying, On this manner shall ye speak unto Esau, when ye find him.');
+INSERT INTO t1(docid,words) VALUES(1032020,'And say ye moreover, Behold, thy servant Jacob is behind us. For he said, I will appease him with the present that goeth before me, and afterward I will see his face; peradventure he will accept of me.');
+INSERT INTO t1(docid,words) VALUES(1032021,'So went the present over before him: and himself lodged that night in the company.');
+INSERT INTO t1(docid,words) VALUES(1032022,'And he rose up that night, and took his two wives, and his two womenservants, and his eleven sons, and passed over the ford Jabbok.');
+INSERT INTO t1(docid,words) VALUES(1032023,'And he took them, and sent them over the brook, and sent over that he had.');
+INSERT INTO t1(docid,words) VALUES(1032024,'And Jacob was left alone; and there wrestled a man with him until the breaking of the day.');
+INSERT INTO t1(docid,words) VALUES(1032025,'And when he saw that he prevailed not against him, he touched the hollow of his thigh; and the hollow of Jacob''s thigh was out of joint, as he wrestled with him.');
+INSERT INTO t1(docid,words) VALUES(1032026,'And he said, Let me go, for the day breaketh. And he said, I will not let thee go, except thou bless me.');
+INSERT INTO t1(docid,words) VALUES(1032027,'And he said unto him, What is thy name? And he said, Jacob.');
+INSERT INTO t1(docid,words) VALUES(1032028,'And he said, Thy name shall be called no more Jacob, but Israel: for as a prince hast thou power with God and with men, and hast prevailed.');
+INSERT INTO t1(docid,words) VALUES(1032029,'And Jacob asked him, and said, Tell me, I pray thee, thy name. And he said, Wherefore is it that thou dost ask after my name? And he blessed him there.');
+INSERT INTO t1(docid,words) VALUES(1032030,'And Jacob called the name of the place Peniel: for I have seen God face to face, and my life is preserved.');
+INSERT INTO t1(docid,words) VALUES(1032031,'And as he passed over Penuel the sun rose upon him, and he halted upon his thigh.');
+INSERT INTO t1(docid,words) VALUES(1032032,'Therefore the children of Israel eat not of the sinew which shrank, which is upon the hollow of the thigh, unto this day: because he touched the hollow of Jacob''s thigh in the sinew that shrank.');
+INSERT INTO t1(docid,words) VALUES(1033001,'And Jacob lifted up his eyes, and looked, and, behold, Esau came, and with him four hundred men. And he divided the children unto Leah, and unto Rachel, and unto the two handmaids.');
+INSERT INTO t1(docid,words) VALUES(1033002,'And he put the handmaids and their children foremost, and Leah and her children after, and Rachel and Joseph hindermost.');
+INSERT INTO t1(docid,words) VALUES(1033003,'And he passed over before them, and bowed himself to the ground seven times, until he came near to his brother.');
+INSERT INTO t1(docid,words) VALUES(1033004,'And Esau ran to meet him, and embraced him, and fell on his neck, and kissed him: and they wept.');
+INSERT INTO t1(docid,words) VALUES(1033005,'And he lifted up his eyes, and saw the women and the children; and said, Who are those with thee? And he said, The children which God hath graciously given thy servant.');
+INSERT INTO t1(docid,words) VALUES(1033006,'Then the handmaidens came near, they and their children, and they bowed themselves.');
+INSERT INTO t1(docid,words) VALUES(1033007,'And Leah also with her children came near, and bowed themselves: and after came Joseph near and Rachel, and they bowed themselves.');
+INSERT INTO t1(docid,words) VALUES(1033008,'And he said, What meanest thou by all this drove which I met? And he said, These are to find grace in the sight of my lord.');
+INSERT INTO t1(docid,words) VALUES(1033009,'And Esau said, I have enough, my brother; keep that thou hast unto thyself.');
+INSERT INTO t1(docid,words) VALUES(1033010,'And Jacob said, Nay, I pray thee, if now I have found grace in thy sight, then receive my present at my hand: for therefore I have seen thy face, as though I had seen the face of God, and thou wast pleased with me.');
+INSERT INTO t1(docid,words) VALUES(1033011,'Take, I pray thee, my blessing that is brought to thee; because God hath dealt graciously with me, and because I have enough. And he urged him, and he took it.');
+INSERT INTO t1(docid,words) VALUES(1033012,'And he said, Let us take our journey, and let us go, and I will go before thee.');
+INSERT INTO t1(docid,words) VALUES(1033013,'And he said unto him, My lord knoweth that the children are tender, and the flocks and herds with young are with me: and if men should overdrive them one day, all the flock will die.');
+INSERT INTO t1(docid,words) VALUES(1033014,'Let my lord, I pray thee, pass over before his servant: and I will lead on softly, according as the cattle that goeth before me and the children be able to endure, until I come unto my lord unto Seir.');
+INSERT INTO t1(docid,words) VALUES(1033015,'And Esau said, Let me now leave with thee some of the folk that are with me. And he said, What needeth it? let me find grace in the sight of my lord.');
+INSERT INTO t1(docid,words) VALUES(1033016,'So Esau returned that day on his way unto Seir.');
+INSERT INTO t1(docid,words) VALUES(1033017,'And Jacob journeyed to Succoth, and built him an house, and made booths for his cattle: therefore the name of the place is called Succoth.');
+INSERT INTO t1(docid,words) VALUES(1033018,'And Jacob came to Shalem, a city of Shechem, which is in the land of Canaan, when he came from Padanaram; and pitched his tent before the city.');
+INSERT INTO t1(docid,words) VALUES(1033019,'And he bought a parcel of a field, where he had spread his tent, at the hand of the children of Hamor, Shechem''s father, for an hundred pieces of money.');
+INSERT INTO t1(docid,words) VALUES(1033020,'And he erected there an altar, and called it EleloheIsrael.');
+INSERT INTO t1(docid,words) VALUES(1034001,'And Dinah the daughter of Leah, which she bare unto Jacob, went out to see the daughters of the land.');
+INSERT INTO t1(docid,words) VALUES(1034002,'And when Shechem the son of Hamor the Hivite, prince of the country, saw her, he took her, and lay with her, and defiled her.');
+INSERT INTO t1(docid,words) VALUES(1034003,'And his soul clave unto Dinah the daughter of Jacob, and he loved the damsel, and spake kindly unto the damsel.');
+INSERT INTO t1(docid,words) VALUES(1034004,'And Shechem spake unto his father Hamor, saying, Get me this damsel to wife.');
+INSERT INTO t1(docid,words) VALUES(1034005,'And Jacob heard that he had defiled Dinah his daughter: now his sons were with his cattle in the field: and Jacob held his peace until they were come.');
+INSERT INTO t1(docid,words) VALUES(1034006,'And Hamor the father of Shechem went out unto Jacob to commune with him.');
+INSERT INTO t1(docid,words) VALUES(1034007,'And the sons of Jacob came out of the field when they heard it: and the men were grieved, and they were very wroth, because he had wrought folly in Israel in lying with Jacob''s daughter: which thing ought not to be done.');
+INSERT INTO t1(docid,words) VALUES(1034008,'And Hamor communed with them, saying, The soul of my son Shechem longeth for your daughter: I pray you give her him to wife.');
+INSERT INTO t1(docid,words) VALUES(1034009,'And make ye marriages with us, and give your daughters unto us, and take our daughters unto you.');
+INSERT INTO t1(docid,words) VALUES(1034010,'And ye shall dwell with us: and the land shall be before you; dwell and trade ye therein, and get you possessions therein.');
+INSERT INTO t1(docid,words) VALUES(1034011,'And Shechem said unto her father and unto her brethren, Let me find grace in your eyes, and what ye shall say unto me I will give.');
+INSERT INTO t1(docid,words) VALUES(1034012,'Ask me never so much dowry and gift, and I will give according as ye shall say unto me: but give me the damsel to wife.');
+INSERT INTO t1(docid,words) VALUES(1034013,'And the sons of Jacob answered Shechem and Hamor his father deceitfully, and said, because he had defiled Dinah their sister:');
+INSERT INTO t1(docid,words) VALUES(1034014,'And they said unto them, We cannot do this thing, to give our sister to one that is uncircumcised; for that were a reproach unto us:');
+INSERT INTO t1(docid,words) VALUES(1034015,'But in this will we consent unto you: If ye will be as we be, that every male of you be circumcised;');
+INSERT INTO t1(docid,words) VALUES(1034016,'Then will we give our daughters unto you, and we will take your daughters to us, and we will dwell with you, and we will become one people.');
+INSERT INTO t1(docid,words) VALUES(1034017,'But if ye will not hearken unto us, to be circumcised; then will we take our daughter, and we will be gone.');
+INSERT INTO t1(docid,words) VALUES(1034018,'And their words pleased Hamor, and Shechem Hamor''s son.');
+INSERT INTO t1(docid,words) VALUES(1034019,'And the young man deferred not to do the thing, because he had delight in Jacob''s daughter: and he was more honourable than all the house of his father.');
+INSERT INTO t1(docid,words) VALUES(1034020,'And Hamor and Shechem his son came unto the gate of their city, and communed with the men of their city, saying,');
+INSERT INTO t1(docid,words) VALUES(1034021,'These men are peaceable with us; therefore let them dwell in the land, and trade therein; for the land, behold, it is large enough for them; let us take their daughters to us for wives, and let us give them our daughters.');
+INSERT INTO t1(docid,words) VALUES(1034022,'Only herein will the men consent unto us for to dwell with us, to be one people, if every male among us be circumcised, as they are circumcised.');
+INSERT INTO t1(docid,words) VALUES(1034023,'Shall not their cattle and their substance and every beast of their''s be our''s? only let us consent unto them, and they will dwell with us.');
+INSERT INTO t1(docid,words) VALUES(1034024,'And unto Hamor and unto Shechem his son hearkened all that went out of the gate of his city; and every male was circumcised, all that went out of the gate of his city.');
+INSERT INTO t1(docid,words) VALUES(1034025,'And it came to pass on the third day, when they were sore, that two of the sons of Jacob, Simeon and Levi, Dinah''s brethren, took each man his sword, and came upon the city boldly, and slew all the males.');
+INSERT INTO t1(docid,words) VALUES(1034026,'And they slew Hamor and Shechem his son with the edge of the sword, and took Dinah out of Shechem''s house, and went out.');
+INSERT INTO t1(docid,words) VALUES(1034027,'The sons of Jacob came upon the slain, and spoiled the city, because they had defiled their sister.');
+INSERT INTO t1(docid,words) VALUES(1034028,'They took their sheep, and their oxen, and their asses, and that which was in the city, and that which was in the field,');
+INSERT INTO t1(docid,words) VALUES(1034029,'And all their wealth, and all their little ones, and their wives took they captive, and spoiled even all that was in the house.');
+INSERT INTO t1(docid,words) VALUES(1034030,'And Jacob said to Simeon and Levi, Ye have troubled me to make me to stink among the inhabitants of the land, among the Canaanites and the Perizzites: and I being few in number, they shall gather themselves together against me, and slay me; and I shall be destroyed, I and my house.');
+INSERT INTO t1(docid,words) VALUES(1034031,'And they said, Should he deal with our sister as with an harlot?');
+INSERT INTO t1(docid,words) VALUES(1035001,'And God said unto Jacob, Arise, go up to Bethel, and dwell there: and make there an altar unto God, that appeared unto thee when thou fleddest from the face of Esau thy brother.');
+INSERT INTO t1(docid,words) VALUES(1035002,'Then Jacob said unto his household, and to all that were with him, Put away the strange gods that are among you, and be clean, and change your garments:');
+INSERT INTO t1(docid,words) VALUES(1035003,'And let us arise, and go up to Bethel; and I will make there an altar unto God, who answered me in the day of my distress, and was with me in the way which I went.');
+INSERT INTO t1(docid,words) VALUES(1035004,'And they gave unto Jacob all the strange gods which were in their hand, and all their earrings which were in their ears; and Jacob hid them under the oak which was by Shechem.');
+INSERT INTO t1(docid,words) VALUES(1035005,'And they journeyed: and the terror of God was upon the cities that were round about them, and they did not pursue after the sons of Jacob.');
+INSERT INTO t1(docid,words) VALUES(1035006,'So Jacob came to Luz, which is in the land of Canaan, that is, Bethel, he and all the people that were with him.');
+INSERT INTO t1(docid,words) VALUES(1035007,'And he built there an altar, and called the place Elbethel: because there God appeared unto him, when he fled from the face of his brother.');
+INSERT INTO t1(docid,words) VALUES(1035008,'But Deborah Rebekah''s nurse died, and she was buried beneath Bethel under an oak: and the name of it was called Allonbachuth.');
+INSERT INTO t1(docid,words) VALUES(1035009,'And God appeared unto Jacob again, when he came out of Padanaram, and blessed him.');
+INSERT INTO t1(docid,words) VALUES(1035010,'And God said unto him, Thy name is Jacob: thy name shall not be called any more Jacob, but Israel shall be thy name: and he called his name Israel.');
+INSERT INTO t1(docid,words) VALUES(1035011,'And God said unto him, I am God Almighty: be fruitful and multiply; a nation and a company of nations shall be of thee, and kings shall come out of thy loins;');
+INSERT INTO t1(docid,words) VALUES(1035012,'And the land which I gave Abraham and Isaac, to thee I will give it, and to thy seed after thee will I give the land.');
+INSERT INTO t1(docid,words) VALUES(1035013,'And God went up from him in the place where he talked with him.');
+INSERT INTO t1(docid,words) VALUES(1035014,'And Jacob set up a pillar in the place where he talked with him, even a pillar of stone: and he poured a drink offering thereon, and he poured oil thereon.');
+INSERT INTO t1(docid,words) VALUES(1035015,'And Jacob called the name of the place where God spake with him, Bethel.');
+INSERT INTO t1(docid,words) VALUES(1035016,'And they journeyed from Bethel; and there was but a little way to come to Ephrath: and Rachel travailed, and she had hard labour.');
+INSERT INTO t1(docid,words) VALUES(1035017,'And it came to pass, when she was in hard labour, that the midwife said unto her, Fear not; thou shalt have this son also.');
+INSERT INTO t1(docid,words) VALUES(1035018,'And it came to pass, as her soul was in departing, (for she died) that she called his name Benoni: but his father called him Benjamin.');
+INSERT INTO t1(docid,words) VALUES(1035019,'And Rachel died, and was buried in the way to Ephrath, which is Bethlehem.');
+INSERT INTO t1(docid,words) VALUES(1035020,'And Jacob set a pillar upon her grave: that is the pillar of Rachel''s grave unto this day.');
+INSERT INTO t1(docid,words) VALUES(1035021,'And Israel journeyed, and spread his tent beyond the tower of Edar.');
+INSERT INTO t1(docid,words) VALUES(1035022,'And it came to pass, when Israel dwelt in that land, that Reuben went and lay with Bilhah his father''s concubine: and Israel heard it. Now the sons of Jacob were twelve:');
+INSERT INTO t1(docid,words) VALUES(1035023,'The sons of Leah; Reuben, Jacob''s firstborn, and Simeon, and Levi, and Judah, and Issachar, and Zebulun:');
+INSERT INTO t1(docid,words) VALUES(1035024,'The sons of Rachel; Joseph, and Benjamin:');
+INSERT INTO t1(docid,words) VALUES(1035025,'And the sons of Bilhah, Rachel''s handmaid; Dan, and Naphtali:');
+INSERT INTO t1(docid,words) VALUES(1035026,'And the sons of Zilpah, Leah''s handmaid: Gad, and Asher: these are the sons of Jacob, which were born to him in Padanaram.');
+INSERT INTO t1(docid,words) VALUES(1035027,'And Jacob came unto Isaac his father unto Mamre, unto the city of Arbah, which is Hebron, where Abraham and Isaac sojourned.');
+INSERT INTO t1(docid,words) VALUES(1035028,'And the days of Isaac were an hundred and fourscore years.');
+INSERT INTO t1(docid,words) VALUES(1035029,'And Isaac gave up the ghost, and died, and was gathered unto his people, being old and full of days: and his sons Esau and Jacob buried him.');
+INSERT INTO t1(docid,words) VALUES(1036001,'Now these are the generations of Esau, who is Edom.');
+INSERT INTO t1(docid,words) VALUES(1036002,'Esau took his wives of the daughters of Canaan; Adah the daughter of Elon the Hittite, and Aholibamah the daughter of Anah the daughter of Zibeon the Hivite;');
+INSERT INTO t1(docid,words) VALUES(1036003,'And Bashemath Ishmael''s daughter, sister of Nebajoth.');
+INSERT INTO t1(docid,words) VALUES(1036004,'And Adah bare to Esau Eliphaz; and Bashemath bare Reuel;');
+INSERT INTO t1(docid,words) VALUES(1036005,'And Aholibamah bare Jeush, and Jaalam, and Korah: these are the sons of Esau, which were born unto him in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1036006,'And Esau took his wives, and his sons, and his daughters, and all the persons of his house, and his cattle, and all his beasts, and all his substance, which he had got in the land of Canaan; and went into the country from the face of his brother Jacob.');
+INSERT INTO t1(docid,words) VALUES(1036007,'For their riches were more than that they might dwell together; and the land wherein they were strangers could not bear them because of their cattle.');
+INSERT INTO t1(docid,words) VALUES(1036008,'Thus dwelt Esau in mount Seir: Esau is Edom.');
+INSERT INTO t1(docid,words) VALUES(1036009,'And these are the generations of Esau the father of the Edomites in mount Seir:');
+INSERT INTO t1(docid,words) VALUES(1036010,'These are the names of Esau''s sons; Eliphaz the son of Adah the wife of Esau, Reuel the son of Bashemath the wife of Esau.');
+INSERT INTO t1(docid,words) VALUES(1036011,'And the sons of Eliphaz were Teman, Omar, Zepho, and Gatam, and Kenaz.');
+INSERT INTO t1(docid,words) VALUES(1036012,'And Timna was concubine to Eliphaz Esau''s son; and she bare to Eliphaz Amalek: these were the sons of Adah Esau''s wife.');
+INSERT INTO t1(docid,words) VALUES(1036013,'And these are the sons of Reuel; Nahath, and Zerah, Shammah, and Mizzah: these were the sons of Bashemath Esau''s wife.');
+INSERT INTO t1(docid,words) VALUES(1036014,'And these were the sons of Aholibamah, the daughter of Anah the daughter of Zibeon, Esau''s wife: and she bare to Esau Jeush, and Jaalam, and Korah.');
+INSERT INTO t1(docid,words) VALUES(1036015,'These were dukes of the sons of Esau: the sons of Eliphaz the firstborn son of Esau; duke Teman, duke Omar, duke Zepho, duke Kenaz,');
+INSERT INTO t1(docid,words) VALUES(1036016,'Duke Korah, duke Gatam, and duke Amalek: these are the dukes that came of Eliphaz in the land of Edom; these were the sons of Adah.');
+INSERT INTO t1(docid,words) VALUES(1036017,'And these are the sons of Reuel Esau''s son; duke Nahath, duke Zerah, duke Shammah, duke Mizzah: these are the dukes that came of Reuel in the land of Edom; these are the sons of Bashemath Esau''s wife.');
+INSERT INTO t1(docid,words) VALUES(1036018,'And these are the sons of Aholibamah Esau''s wife; duke Jeush, duke Jaalam, duke Korah: these were the dukes that came of Aholibamah the daughter of Anah, Esau''s wife.');
+INSERT INTO t1(docid,words) VALUES(1036019,'These are the sons of Esau, who is Edom, and these are their dukes.');
+INSERT INTO t1(docid,words) VALUES(1036020,'These are the sons of Seir the Horite, who inhabited the land; Lotan, and Shobal, and Zibeon, and Anah,');
+INSERT INTO t1(docid,words) VALUES(1036021,'And Dishon, and Ezer, and Dishan: these are the dukes of the Horites, the children of Seir in the land of Edom.');
+INSERT INTO t1(docid,words) VALUES(1036022,'And the children of Lotan were Hori and Hemam; and Lotan''s sister was Timna.');
+INSERT INTO t1(docid,words) VALUES(1036023,'And the children of Shobal were these; Alvan, and Manahath, and Ebal, Shepho, and Onam.');
+INSERT INTO t1(docid,words) VALUES(1036024,'And these are the children of Zibeon; both Ajah, and Anah: this was that Anah that found the mules in the wilderness, as he fed the asses of Zibeon his father.');
+INSERT INTO t1(docid,words) VALUES(1036025,'And the children of Anah were these; Dishon, and Aholibamah the daughter of Anah.');
+INSERT INTO t1(docid,words) VALUES(1036026,'And these are the children of Dishon; Hemdan, and Eshban, and Ithran, and Cheran.');
+INSERT INTO t1(docid,words) VALUES(1036027,'The children of Ezer are these; Bilhan, and Zaavan, and Akan.');
+INSERT INTO t1(docid,words) VALUES(1036028,'The children of Dishan are these; Uz, and Aran.');
+INSERT INTO t1(docid,words) VALUES(1036029,'These are the dukes that came of the Horites; duke Lotan, duke Shobal, duke Zibeon, duke Anah,');
+INSERT INTO t1(docid,words) VALUES(1036030,'Duke Dishon, duke Ezer, duke Dishan: these are the dukes that came of Hori, among their dukes in the land of Seir.');
+INSERT INTO t1(docid,words) VALUES(1036031,'And these are the kings that reigned in the land of Edom, before there reigned any king over the children of Israel.');
+INSERT INTO t1(docid,words) VALUES(1036032,'And Bela the son of Beor reigned in Edom: and the name of his city was Dinhabah.');
+INSERT INTO t1(docid,words) VALUES(1036033,'And Bela died, and Jobab the son of Zerah of Bozrah reigned in his stead.');
+INSERT INTO t1(docid,words) VALUES(1036034,'And Jobab died, and Husham of the land of Temani reigned in his stead.');
+INSERT INTO t1(docid,words) VALUES(1036035,'And Husham died, and Hadad the son of Bedad, who smote Midian in the field of Moab, reigned in his stead: and the name of his city was Avith.');
+INSERT INTO t1(docid,words) VALUES(1036036,'And Hadad died, and Samlah of Masrekah reigned in his stead.');
+INSERT INTO t1(docid,words) VALUES(1036037,'And Samlah died, and Saul of Rehoboth by the river reigned in his stead.');
+INSERT INTO t1(docid,words) VALUES(1036038,'And Saul died, and Baalhanan the son of Achbor reigned in his stead.');
+INSERT INTO t1(docid,words) VALUES(1036039,'And Baalhanan the son of Achbor died, and Hadar reigned in his stead: and the name of his city was Pau; and his wife''s name was Mehetabel, the daughter of Matred, the daughter of Mezahab.');
+INSERT INTO t1(docid,words) VALUES(1036040,'And these are the names of the dukes that came of Esau, according to their families, after their places, by their names; duke Timnah, duke Alvah, duke Jetheth,');
+INSERT INTO t1(docid,words) VALUES(1036041,'Duke Aholibamah, duke Elah, duke Pinon,');
+INSERT INTO t1(docid,words) VALUES(1036042,'Duke Kenaz, duke Teman, duke Mibzar,');
+INSERT INTO t1(docid,words) VALUES(1036043,'Duke Magdiel, duke Iram: these be the dukes of Edom, according to their habitations in the land of their possession: he is Esau the father of the Edomites.');
+INSERT INTO t1(docid,words) VALUES(1037001,'And Jacob dwelt in the land wherein his father was a stranger, in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1037002,'These are the generations of Jacob. Joseph, being seventeen years old, was feeding the flock with his brethren; and the lad was with the sons of Bilhah, and with the sons of Zilpah, his father''s wives: and Joseph brought unto his father their evil report.');
+INSERT INTO t1(docid,words) VALUES(1037003,'Now Israel loved Joseph more than all his children, because he was the son of his old age: and he made him a coat of many colours.');
+INSERT INTO t1(docid,words) VALUES(1037004,'And when his brethren saw that their father loved him more than all his brethren, they hated him, and could not speak peaceably unto him.');
+INSERT INTO t1(docid,words) VALUES(1037005,'And Joseph dreamed a dream, and he told it his brethren: and they hated him yet the more.');
+INSERT INTO t1(docid,words) VALUES(1037006,'And he said unto them, Hear, I pray you, this dream which I have dreamed:');
+INSERT INTO t1(docid,words) VALUES(1037007,'For, behold, we were binding sheaves in the field, and, lo, my sheaf arose, and also stood upright; and, behold, your sheaves stood round about, and made obeisance to my sheaf.');
+INSERT INTO t1(docid,words) VALUES(1037008,'And his brethren said to him, Shalt thou indeed reign over us? or shalt thou indeed have dominion over us? And they hated him yet the more for his dreams, and for his words.');
+INSERT INTO t1(docid,words) VALUES(1037009,'And he dreamed yet another dream, and told it his brethren, and said, Behold, I have dreamed a dream more; and, behold, the sun and the moon and the eleven stars made obeisance to me.');
+INSERT INTO t1(docid,words) VALUES(1037010,'And he told it to his father, and to his brethren: and his father rebuked him, and said unto him, What is this dream that thou hast dreamed? Shall I and thy mother and thy brethren indeed come to bow down ourselves to thee to the earth?');
+INSERT INTO t1(docid,words) VALUES(1037011,'And his brethren envied him; but his father observed the saying.');
+INSERT INTO t1(docid,words) VALUES(1037012,'And his brethren went to feed their father''s flock in Shechem.');
+INSERT INTO t1(docid,words) VALUES(1037013,'And Israel said unto Joseph, Do not thy brethren feed the flock in Shechem? come, and I will send thee unto them. And he said to him, Here am I.');
+INSERT INTO t1(docid,words) VALUES(1037014,'And he said to him, Go, I pray thee, see whether it be well with thy brethren, and well with the flocks; and bring me word again. So he sent him out of the vale of Hebron, and he came to Shechem.');
+INSERT INTO t1(docid,words) VALUES(1037015,'And a certain man found him, and, behold, he was wandering in the field: and the man asked him, saying, What seekest thou?');
+INSERT INTO t1(docid,words) VALUES(1037016,'And he said, I seek my brethren: tell me, I pray thee, where they feed their flocks.');
+INSERT INTO t1(docid,words) VALUES(1037017,'And the man said, They are departed hence; for I heard them say, Let us go to Dothan. And Joseph went after his brethren, and found them in Dothan.');
+INSERT INTO t1(docid,words) VALUES(1037018,'And when they saw him afar off, even before he came near unto them, they conspired against him to slay him.');
+INSERT INTO t1(docid,words) VALUES(1037019,'And they said one to another, Behold, this dreamer cometh.');
+INSERT INTO t1(docid,words) VALUES(1037020,'Come now therefore, and let us slay him, and cast him into some pit, and we will say, Some evil beast hath devoured him: and we shall see what will become of his dreams.');
+INSERT INTO t1(docid,words) VALUES(1037021,'And Reuben heard it, and he delivered him out of their hands; and said, Let us not kill him.');
+INSERT INTO t1(docid,words) VALUES(1037022,'And Reuben said unto them, Shed no blood, but cast him into this pit that is in the wilderness, and lay no hand upon him; that he might rid him out of their hands, to deliver him to his father again.');
+INSERT INTO t1(docid,words) VALUES(1037023,'And it came to pass, when Joseph was come unto his brethren, that they stript Joseph out of his coat, his coat of many colours that was on him;');
+INSERT INTO t1(docid,words) VALUES(1037024,'And they took him, and cast him into a pit: and the pit was empty, there was no water in it.');
+INSERT INTO t1(docid,words) VALUES(1037025,'And they sat down to eat bread: and they lifted up their eyes and looked, and, behold, a company of Ishmeelites came from Gilead with their camels bearing spicery and balm and myrrh, going to carry it down to Egypt.');
+INSERT INTO t1(docid,words) VALUES(1037026,'And Judah said unto his brethren, What profit is it if we slay our brother, and conceal his blood?');
+INSERT INTO t1(docid,words) VALUES(1037027,'Come, and let us sell him to the Ishmeelites, and let not our hand be upon him; for he is our brother and our flesh. And his brethren were content.');
+INSERT INTO t1(docid,words) VALUES(1037028,'Then there passed by Midianites merchantmen; and they drew and lifted up Joseph out of the pit, and sold Joseph to the Ishmeelites for twenty pieces of silver: and they brought Joseph into Egypt.');
+INSERT INTO t1(docid,words) VALUES(1037029,'And Reuben returned unto the pit; and, behold, Joseph was not in the pit; and he rent his clothes.');
+INSERT INTO t1(docid,words) VALUES(1037030,'And he returned unto his brethren, and said, The child is not; and I, whither shall I go?');
+INSERT INTO t1(docid,words) VALUES(1037031,'And they took Joseph''s coat, and killed a kid of the goats, and dipped the coat in the blood;');
+INSERT INTO t1(docid,words) VALUES(1037032,'And they sent the coat of many colours, and they brought it to their father; and said, This have we found: know now whether it be thy son''s coat or no.');
+INSERT INTO t1(docid,words) VALUES(1037033,'And he knew it, and said, It is my son''s coat; an evil beast hath devoured him; Joseph is without doubt rent in pieces.');
+INSERT INTO t1(docid,words) VALUES(1037034,'And Jacob rent his clothes, and put sackcloth upon his loins, and mourned for his son many days.');
+INSERT INTO t1(docid,words) VALUES(1037035,'And all his sons and all his daughters rose up to comfort him; but he refused to be comforted; and he said, For I will go down into the grave unto my son mourning. Thus his father wept for him.');
+INSERT INTO t1(docid,words) VALUES(1037036,'And the Midianites sold him into Egypt unto Potiphar, an officer of Pharaoh''s, and captain of the guard.');
+INSERT INTO t1(docid,words) VALUES(1038001,'And it came to pass at that time, that Judah went down from his brethren, and turned in to a certain Adullamite, whose name was Hirah.');
+INSERT INTO t1(docid,words) VALUES(1038002,'And Judah saw there a daughter of a certain Canaanite, whose name was Shuah; and he took her, and went in unto her.');
+INSERT INTO t1(docid,words) VALUES(1038003,'And she conceived, and bare a son; and he called his name Er.');
+INSERT INTO t1(docid,words) VALUES(1038004,'And she conceived again, and bare a son; and she called his name Onan.');
+INSERT INTO t1(docid,words) VALUES(1038005,'And she yet again conceived, and bare a son; and called his name Shelah: and he was at Chezib, when she bare him.');
+INSERT INTO t1(docid,words) VALUES(1038006,'And Judah took a wife for Er his firstborn, whose name was Tamar.');
+INSERT INTO t1(docid,words) VALUES(1038007,'And Er, Judah''s firstborn, was wicked in the sight of the LORD; and the LORD slew him.');
+INSERT INTO t1(docid,words) VALUES(1038008,'And Judah said unto Onan, Go in unto thy brother''s wife, and marry her, and raise up seed to thy brother.');
+INSERT INTO t1(docid,words) VALUES(1038009,'And Onan knew that the seed should not be his; and it came to pass, when he went in unto his brother''s wife, that he spilled it on the ground, lest that he should give seed to his brother.');
+INSERT INTO t1(docid,words) VALUES(1038010,'And the thing which he did displeased the LORD: wherefore he slew him also.');
+INSERT INTO t1(docid,words) VALUES(1038011,'Then said Judah to Tamar his daughter in law, Remain a widow at thy father''s house, till Shelah my son be grown: for he said, Lest peradventure he die also, as his brethren did. And Tamar went and dwelt in her father''s house.');
+INSERT INTO t1(docid,words) VALUES(1038012,'And in process of time the daughter of Shuah Judah''s wife died; and Judah was comforted, and went up unto his sheepshearers to Timnath, he and his friend Hirah the Adullamite.');
+INSERT INTO t1(docid,words) VALUES(1038013,'And it was told Tamar, saying, Behold thy father in law goeth up to Timnath to shear his sheep.');
+INSERT INTO t1(docid,words) VALUES(1038014,'And she put her widow''s garments off from her, and covered her with a vail, and wrapped herself, and sat in an open place, which is by the way to Timnath; for she saw that Shelah was grown, and she was not given unto him to wife.');
+INSERT INTO t1(docid,words) VALUES(1038015,'When Judah saw her, he thought her to be an harlot; because she had covered her face.');
+INSERT INTO t1(docid,words) VALUES(1038016,'And he turned unto her by the way, and said, Go to, I pray thee, let me come in unto thee; (for he knew not that she was his daughter in law.) And she said, What wilt thou give me, that thou mayest come in unto me?');
+INSERT INTO t1(docid,words) VALUES(1038017,'And he said, I will send thee a kid from the flock. And she said, Wilt thou give me a pledge, till thou send it?');
+INSERT INTO t1(docid,words) VALUES(1038018,'And he said, What pledge shall I give thee? And she said, Thy signet, and thy bracelets, and thy staff that is in thine hand. And he gave it her, and came in unto her, and she conceived by him.');
+INSERT INTO t1(docid,words) VALUES(1038019,'And she arose, and went away, and laid by her vail from her, and put on the garments of her widowhood.');
+INSERT INTO t1(docid,words) VALUES(1038020,'And Judah sent the kid by the hand of his friend the Adullamite, to receive his pledge from the woman''s hand: but he found her not.');
+INSERT INTO t1(docid,words) VALUES(1038021,'Then he asked the men of that place, saying, Where is the harlot, that was openly by the way side? And they said, There was no harlot in this place.');
+INSERT INTO t1(docid,words) VALUES(1038022,'And he returned to Judah, and said, I cannot find her; and also the men of the place said, that there was no harlot in this place.');
+INSERT INTO t1(docid,words) VALUES(1038023,'And Judah said, Let her take it to her, lest we be shamed: behold, I sent this kid, and thou hast not found her.');
+INSERT INTO t1(docid,words) VALUES(1038024,'And it came to pass about three months after, that it was told Judah, saying, Tamar thy daughter in law hath played the harlot; and also, behold, she is with child by whoredom. And Judah said, Bring her forth, and let her be burnt.');
+INSERT INTO t1(docid,words) VALUES(1038025,'When she was brought forth, she sent to her father in law, saying, By the man, whose these are, am I with child: and she said, Discern, I pray thee, whose are these, the signet, and bracelets, and staff.');
+INSERT INTO t1(docid,words) VALUES(1038026,'And Judah acknowledged them, and said, She hath been more righteous than I; because that I gave her not to Shelah my son. And he knew her again no more.');
+INSERT INTO t1(docid,words) VALUES(1038027,'And it came to pass in the time of her travail, that, behold, twins were in her womb.');
+INSERT INTO t1(docid,words) VALUES(1038028,'And it came to pass, when she travailed, that the one put out his hand: and the midwife took and bound upon his hand a scarlet thread, saying, This came out first.');
+INSERT INTO t1(docid,words) VALUES(1038029,'And it came to pass, as he drew back his hand, that, behold, his brother came out: and she said, How hast thou broken forth? this breach be upon thee: therefore his name was called Pharez.');
+INSERT INTO t1(docid,words) VALUES(1038030,'And afterward came out his brother, that had the scarlet thread upon his hand: and his name was called Zarah.');
+INSERT INTO t1(docid,words) VALUES(1039001,'And Joseph was brought down to Egypt; and Potiphar, an officer of Pharaoh, captain of the guard, an Egyptian, bought him of the hands of the Ishmeelites, which had brought him down thither.');
+INSERT INTO t1(docid,words) VALUES(1039002,'And the LORD was with Joseph, and he was a prosperous man; and he was in the house of his master the Egyptian.');
+INSERT INTO t1(docid,words) VALUES(1039003,'And his master saw that the LORD was with him, and that the LORD made all that he did to prosper in his hand.');
+INSERT INTO t1(docid,words) VALUES(1039004,'And Joseph found grace in his sight, and he served him: and he made him overseer over his house, and all that he had he put into his hand.');
+INSERT INTO t1(docid,words) VALUES(1039005,'And it came to pass from the time that he had made him overseer in his house, and over all that he had, that the LORD blessed the Egyptian''s house for Joseph''s sake; and the blessing of the LORD was upon all that he had in the house, and in the field.');
+INSERT INTO t1(docid,words) VALUES(1039006,'And he left all that he had in Joseph''s hand; and he knew not ought he had, save the bread which he did eat. And Joseph was a goodly person, and well favoured.');
+INSERT INTO t1(docid,words) VALUES(1039007,'And it came to pass after these things, that his master''s wife cast her eyes upon Joseph; and she said, Lie with me.');
+INSERT INTO t1(docid,words) VALUES(1039008,'But he refused, and said unto his master''s wife, Behold, my master wotteth not what is with me in the house, and he hath committed all that he hath to my hand;');
+INSERT INTO t1(docid,words) VALUES(1039009,'There is none greater in this house than I; neither hath he kept back any thing from me but thee, because thou art his wife: how then can I do this great wickedness, and sin against God?');
+INSERT INTO t1(docid,words) VALUES(1039010,'And it came to pass, as she spake to Joseph day by day, that he hearkened not unto her, to lie by her, or to be with her.');
+INSERT INTO t1(docid,words) VALUES(1039011,'And it came to pass about this time, that Joseph went into the house to do his business; and there was none of the men of the house there within.');
+INSERT INTO t1(docid,words) VALUES(1039012,'And she caught him by his garment, saying, Lie with me: and he left his garment in her hand, and fled, and got him out.');
+INSERT INTO t1(docid,words) VALUES(1039013,'And it came to pass, when she saw that he had left his garment in her hand, and was fled forth,');
+INSERT INTO t1(docid,words) VALUES(1039014,'That she called unto the men of her house, and spake unto them, saying, See, he hath brought in an Hebrew unto us to mock us; he came in unto me to lie with me, and I cried with a loud voice:');
+INSERT INTO t1(docid,words) VALUES(1039015,'And it came to pass, when he heard that I lifted up my voice and cried, that he left his garment with me, and fled, and got him out.');
+INSERT INTO t1(docid,words) VALUES(1039016,'And she laid up his garment by her, until his lord came home.');
+INSERT INTO t1(docid,words) VALUES(1039017,'And she spake unto him according to these words, saying, The Hebrew servant, which thou hast brought unto us, came in unto me to mock me:');
+INSERT INTO t1(docid,words) VALUES(1039018,'And it came to pass, as I lifted up my voice and cried, that he left his garment with me, and fled out.');
+INSERT INTO t1(docid,words) VALUES(1039019,'And it came to pass, when his master heard the words of his wife, which she spake unto him, saying, After this manner did thy servant to me; that his wrath was kindled.');
+INSERT INTO t1(docid,words) VALUES(1039020,'And Joseph''s master took him, and put him into the prison, a place where the king''s prisoners were bound: and he was there in the prison.');
+INSERT INTO t1(docid,words) VALUES(1039021,'But the LORD was with Joseph, and shewed him mercy, and gave him favour in the sight of the keeper of the prison.');
+INSERT INTO t1(docid,words) VALUES(1039022,'And the keeper of the prison committed to Joseph''s hand all the prisoners that were in the prison; and whatsoever they did there, he was the doer of it.');
+INSERT INTO t1(docid,words) VALUES(1039023,'The keeper of the prison looked not to any thing that was under his hand; because the LORD was with him, and that which he did, the LORD made it to prosper.');
+INSERT INTO t1(docid,words) VALUES(1040001,'And it came to pass after these things, that the butler of the king of Egypt and his baker had offended their lord the king of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1040002,'And Pharaoh was wroth against two of his officers, against the chief of the butlers, and against the chief of the bakers.');
+INSERT INTO t1(docid,words) VALUES(1040003,'And he put them in ward in the house of the captain of the guard, into the prison, the place where Joseph was bound.');
+INSERT INTO t1(docid,words) VALUES(1040004,'And the captain of the guard charged Joseph with them, and he served them: and they continued a season in ward.');
+INSERT INTO t1(docid,words) VALUES(1040005,'And they dreamed a dream both of them, each man his dream in one night, each man according to the interpretation of his dream, the butler and the baker of the king of Egypt, which were bound in the prison.');
+INSERT INTO t1(docid,words) VALUES(1040006,'And Joseph came in unto them in the morning, and looked upon them, and, behold, they were sad.');
+INSERT INTO t1(docid,words) VALUES(1040007,'And he asked Pharaoh''s officers that were with him in the ward of his lord''s house, saying, Wherefore look ye so sadly to day?');
+INSERT INTO t1(docid,words) VALUES(1040008,'And they said unto him, We have dreamed a dream, and there is no interpreter of it. And Joseph said unto them, Do not interpretations belong to God? tell me them, I pray you.');
+INSERT INTO t1(docid,words) VALUES(1040009,'And the chief butler told his dream to Joseph, and said to him, In my dream, behold, a vine was before me;');
+INSERT INTO t1(docid,words) VALUES(1040010,'And in the vine were three branches: and it was as though it budded, and her blossoms shot forth; and the clusters thereof brought forth ripe grapes:');
+INSERT INTO t1(docid,words) VALUES(1040011,'And Pharaoh''s cup was in my hand: and I took the grapes, and pressed them into Pharaoh''s cup, and I gave the cup into Pharaoh''s hand.');
+INSERT INTO t1(docid,words) VALUES(1040012,'And Joseph said unto him, This is the interpretation of it: The three branches are three days:');
+INSERT INTO t1(docid,words) VALUES(1040013,'Yet within three days shall Pharaoh lift up thine head, and restore thee unto thy place: and thou shalt deliver Pharaoh''s cup into his hand, after the former manner when thou wast his butler.');
+INSERT INTO t1(docid,words) VALUES(1040014,'But think on me when it shall be well with thee, and shew kindness, I pray thee, unto me, and make mention of me unto Pharaoh, and bring me out of this house:');
+INSERT INTO t1(docid,words) VALUES(1040015,'For indeed I was stolen away out of the land of the Hebrews: and here also have I done nothing that they should put me into the dungeon.');
+INSERT INTO t1(docid,words) VALUES(1040016,'When the chief baker saw that the interpretation was good, he said unto Joseph, I also was in my dream, and, behold, I had three white baskets on my head:');
+INSERT INTO t1(docid,words) VALUES(1040017,'And in the uppermost basket there was of all manner of bakemeats for Pharaoh; and the birds did eat them out of the basket upon my head.');
+INSERT INTO t1(docid,words) VALUES(1040018,'And Joseph answered and said, This is the interpretation thereof: The three baskets are three days:');
+INSERT INTO t1(docid,words) VALUES(1040019,'Yet within three days shall Pharaoh lift up thy head from off thee, and shall hang thee on a tree; and the birds shall eat thy flesh from off thee.');
+INSERT INTO t1(docid,words) VALUES(1040020,'And it came to pass the third day, which was Pharaoh''s birthday, that he made a feast unto all his servants: and he lifted up the head of the chief butler and of the chief baker among his servants.');
+INSERT INTO t1(docid,words) VALUES(1040021,'And he restored the chief butler unto his butlership again; and he gave the cup into Pharaoh''s hand:');
+INSERT INTO t1(docid,words) VALUES(1040022,'But he hanged the chief baker: as Joseph had interpreted to them.');
+INSERT INTO t1(docid,words) VALUES(1040023,'Yet did not the chief butler remember Joseph, but forgat him.');
+INSERT INTO t1(docid,words) VALUES(1041001,'And it came to pass at the end of two full years, that Pharaoh dreamed: and, behold, he stood by the river.');
+INSERT INTO t1(docid,words) VALUES(1041002,'And, behold, there came up out of the river seven well favoured kine and fatfleshed; and they fed in a meadow.');
+INSERT INTO t1(docid,words) VALUES(1041003,'And, behold, seven other kine came up after them out of the river, ill favoured and leanfleshed; and stood by the other kine upon the brink of the river.');
+INSERT INTO t1(docid,words) VALUES(1041004,'And the ill favoured and leanfleshed kine did eat up the seven well favoured and fat kine. So Pharaoh awoke.');
+INSERT INTO t1(docid,words) VALUES(1041005,'And he slept and dreamed the second time: and, behold, seven ears of corn came up upon one stalk, rank and good.');
+INSERT INTO t1(docid,words) VALUES(1041006,'And, behold, seven thin ears and blasted with the east wind sprung up after them.');
+INSERT INTO t1(docid,words) VALUES(1041007,'And the seven thin ears devoured the seven rank and full ears. And Pharaoh awoke, and, behold, it was a dream.');
+INSERT INTO t1(docid,words) VALUES(1041008,'And it came to pass in the morning that his spirit was troubled; and he sent and called for all the magicians of Egypt, and all the wise men thereof: and Pharaoh told them his dream; but there was none that could interpret them unto Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1041009,'Then spake the chief butler unto Pharaoh, saying, I do remember my faults this day:');
+INSERT INTO t1(docid,words) VALUES(1041010,'Pharaoh was wroth with his servants, and put me in ward in the captain of the guard''s house, both me and the chief baker:');
+INSERT INTO t1(docid,words) VALUES(1041011,'And we dreamed a dream in one night, I and he; we dreamed each man according to the interpretation of his dream.');
+INSERT INTO t1(docid,words) VALUES(1041012,'And there was there with us a young man, an Hebrew, servant to the captain of the guard; and we told him, and he interpreted to us our dreams; to each man according to his dream he did interpret.');
+INSERT INTO t1(docid,words) VALUES(1041013,'And it came to pass, as he interpreted to us, so it was; me he restored unto mine office, and him he hanged.');
+INSERT INTO t1(docid,words) VALUES(1041014,'Then Pharaoh sent and called Joseph, and they brought him hastily out of the dungeon: and he shaved himself, and changed his raiment, and came in unto Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1041015,'And Pharaoh said unto Joseph, I have dreamed a dream, and there is none that can interpret it: and I have heard say of thee, that thou canst understand a dream to interpret it.');
+INSERT INTO t1(docid,words) VALUES(1041016,'And Joseph answered Pharaoh, saying, It is not in me: God shall give Pharaoh an answer of peace.');
+INSERT INTO t1(docid,words) VALUES(1041017,'And Pharaoh said unto Joseph, In my dream, behold, I stood upon the bank of the river:');
+INSERT INTO t1(docid,words) VALUES(1041018,'And, behold, there came up out of the river seven kine, fatfleshed and well favoured; and they fed in a meadow:');
+INSERT INTO t1(docid,words) VALUES(1041019,'And, behold, seven other kine came up after them, poor and very ill favoured and leanfleshed, such as I never saw in all the land of Egypt for badness:');
+INSERT INTO t1(docid,words) VALUES(1041020,'And the lean and the ill favoured kine did eat up the first seven fat kine:');
+INSERT INTO t1(docid,words) VALUES(1041021,'And when they had eaten them up, it could not be known that they had eaten them; but they were still ill favoured, as at the beginning. So I awoke.');
+INSERT INTO t1(docid,words) VALUES(1041022,'And I saw in my dream, and, behold, seven ears came up in one stalk, full and good:');
+INSERT INTO t1(docid,words) VALUES(1041023,'And, behold, seven ears, withered, thin, and blasted with the east wind, sprung up after them:');
+INSERT INTO t1(docid,words) VALUES(1041024,'And the thin ears devoured the seven good ears: and I told this unto the magicians; but there was none that could declare it to me.');
+INSERT INTO t1(docid,words) VALUES(1041025,'And Joseph said unto Pharaoh, The dream of Pharaoh is one: God hath shewed Pharaoh what he is about to do.');
+INSERT INTO t1(docid,words) VALUES(1041026,'The seven good kine are seven years; and the seven good ears are seven years: the dream is one.');
+INSERT INTO t1(docid,words) VALUES(1041027,'And the seven thin and ill favoured kine that came up after them are seven years; and the seven empty ears blasted with the east wind shall be seven years of famine.');
+INSERT INTO t1(docid,words) VALUES(1041028,'This is the thing which I have spoken unto Pharaoh: What God is about to do he sheweth unto Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1041029,'Behold, there come seven years of great plenty throughout all the land of Egypt:');
+INSERT INTO t1(docid,words) VALUES(1041030,'And there shall arise after them seven years of famine; and all the plenty shall be forgotten in the land of Egypt; and the famine shall consume the land;');
+INSERT INTO t1(docid,words) VALUES(1041031,'And the plenty shall not be known in the land by reason of that famine following; for it shall be very grievous.');
+INSERT INTO t1(docid,words) VALUES(1041032,'And for that the dream was doubled unto Pharaoh twice; it is because the thing is established by God, and God will shortly bring it to pass.');
+INSERT INTO t1(docid,words) VALUES(1041033,'Now therefore let Pharaoh look out a man discreet and wise, and set him over the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041034,'Let Pharaoh do this, and let him appoint officers over the land, and take up the fifth part of the land of Egypt in the seven plenteous years.');
+INSERT INTO t1(docid,words) VALUES(1041035,'And let them gather all the food of those good years that come, and lay up corn under the hand of Pharaoh, and let them keep food in the cities.');
+INSERT INTO t1(docid,words) VALUES(1041036,'And that food shall be for store to the land against the seven years of famine, which shall be in the land of Egypt; that the land perish not through the famine.');
+INSERT INTO t1(docid,words) VALUES(1041037,'And the thing was good in the eyes of Pharaoh, and in the eyes of all his servants.');
+INSERT INTO t1(docid,words) VALUES(1041038,'And Pharaoh said unto his servants, Can we find such a one as this is, a man in whom the Spirit of God is?');
+INSERT INTO t1(docid,words) VALUES(1041039,'And Pharaoh said unto Joseph, Forasmuch as God hath shewed thee all this, there is none so discreet and wise as thou art:');
+INSERT INTO t1(docid,words) VALUES(1041040,'Thou shalt be over my house, and according unto thy word shall all my people be ruled: only in the throne will I be greater than thou.');
+INSERT INTO t1(docid,words) VALUES(1041041,'And Pharaoh said unto Joseph, See, I have set thee over all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041042,'And Pharaoh took off his ring from his hand, and put it upon Joseph''s hand, and arrayed him in vestures of fine linen, and put a gold chain about his neck;');
+INSERT INTO t1(docid,words) VALUES(1041043,'And he made him to ride in the second chariot which he had; and they cried before him, Bow the knee: and he made him ruler over all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041044,'And Pharaoh said unto Joseph, I am Pharaoh, and without thee shall no man lift up his hand or foot in all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041045,'And Pharaoh called Joseph''s name Zaphnathpaaneah; and he gave him to wife Asenath the daughter of Potipherah priest of On. And Joseph went out over all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041046,'And Joseph was thirty years old when he stood before Pharaoh king of Egypt. And Joseph went out from the presence of Pharaoh, and went throughout all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041047,'And in the seven plenteous years the earth brought forth by handfuls.');
+INSERT INTO t1(docid,words) VALUES(1041048,'And he gathered up all the food of the seven years, which were in the land of Egypt, and laid up the food in the cities: the food of the field, which was round about every city, laid he up in the same.');
+INSERT INTO t1(docid,words) VALUES(1041049,'And Joseph gathered corn as the sand of the sea, very much, until he left numbering; for it was without number.');
+INSERT INTO t1(docid,words) VALUES(1041050,'And unto Joseph were born two sons before the years of famine came, which Asenath the daughter of Potipherah priest of On bare unto him.');
+INSERT INTO t1(docid,words) VALUES(1041051,'And Joseph called the name of the firstborn Manasseh: For God, said he, hath made me forget all my toil, and all my father''s house.');
+INSERT INTO t1(docid,words) VALUES(1041052,'And the name of the second called he Ephraim: For God hath caused me to be fruitful in the land of my affliction.');
+INSERT INTO t1(docid,words) VALUES(1041053,'And the seven years of plenteousness, that was in the land of Egypt, were ended.');
+INSERT INTO t1(docid,words) VALUES(1041054,'And the seven years of dearth began to come, according as Joseph had said: and the dearth was in all lands; but in all the land of Egypt there was bread.');
+INSERT INTO t1(docid,words) VALUES(1041055,'And when all the land of Egypt was famished, the people cried to Pharaoh for bread: and Pharaoh said unto all the Egyptians, Go unto Joseph; what he saith to you, do.');
+INSERT INTO t1(docid,words) VALUES(1041056,'And the famine was over all the face of the earth: and Joseph opened all the storehouses, and sold unto the Egyptians; and the famine waxed sore in the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1041057,'And all countries came into Egypt to Joseph for to buy corn; because that the famine was so sore in all lands.');
+INSERT INTO t1(docid,words) VALUES(1042001,'Now when Jacob saw that there was corn in Egypt, Jacob said unto his sons, Why do ye look one upon another?');
+INSERT INTO t1(docid,words) VALUES(1042002,'And he said, Behold, I have heard that there is corn in Egypt: get you down thither, and buy for us from thence; that we may live, and not die.');
+INSERT INTO t1(docid,words) VALUES(1042003,'And Joseph''s ten brethren went down to buy corn in Egypt.');
+INSERT INTO t1(docid,words) VALUES(1042004,'But Benjamin, Joseph''s brother, Jacob sent not with his brethren; for he said, Lest peradventure mischief befall him.');
+INSERT INTO t1(docid,words) VALUES(1042005,'And the sons of Israel came to buy corn among those that came: for the famine was in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1042006,'And Joseph was the governor over the land, and he it was that sold to all the people of the land: and Joseph''s brethren came, and bowed down themselves before him with their faces to the earth.');
+INSERT INTO t1(docid,words) VALUES(1042007,'And Joseph saw his brethren, and he knew them, but made himself strange unto them, and spake roughly unto them; and he said unto them, Whence come ye? And they said, From the land of Canaan to buy food.');
+INSERT INTO t1(docid,words) VALUES(1042008,'And Joseph knew his brethren, but they knew not him.');
+INSERT INTO t1(docid,words) VALUES(1042009,'And Joseph remembered the dreams which he dreamed of them, and said unto them, Ye are spies; to see the nakedness of the land ye are come.');
+INSERT INTO t1(docid,words) VALUES(1042010,'And they said unto him, Nay, my lord, but to buy food are thy servants come.');
+INSERT INTO t1(docid,words) VALUES(1042011,'We are all one man''s sons; we are true men, thy servants are no spies.');
+INSERT INTO t1(docid,words) VALUES(1042012,'And he said unto them, Nay, but to see the nakedness of the land ye are come.');
+INSERT INTO t1(docid,words) VALUES(1042013,'And they said, Thy servants are twelve brethren, the sons of one man in the land of Canaan; and, behold, the youngest is this day with our father, and one is not.');
+INSERT INTO t1(docid,words) VALUES(1042014,'And Joseph said unto them, That is it that I spake unto you, saying, Ye are spies:');
+INSERT INTO t1(docid,words) VALUES(1042015,'Hereby ye shall be proved: By the life of Pharaoh ye shall not go forth hence, except your youngest brother come hither.');
+INSERT INTO t1(docid,words) VALUES(1042016,'Send one of you, and let him fetch your brother, and ye shall be kept in prison, that your words may be proved, whether there be any truth in you: or else by the life of Pharaoh surely ye are spies.');
+INSERT INTO t1(docid,words) VALUES(1042017,'And he put them all together into ward three days.');
+INSERT INTO t1(docid,words) VALUES(1042018,'And Joseph said unto them the third day, This do, and live; for I fear God:');
+INSERT INTO t1(docid,words) VALUES(1042019,'If ye be true men, let one of your brethren be bound in the house of your prison: go ye, carry corn for the famine of your houses:');
+INSERT INTO t1(docid,words) VALUES(1042020,'But bring your youngest brother unto me; so shall your words be verified, and ye shall not die. And they did so.');
+INSERT INTO t1(docid,words) VALUES(1042021,'And they said one to another, We are verily guilty concerning our brother, in that we saw the anguish of his soul, when he besought us, and we would not hear; therefore is this distress come upon us.');
+INSERT INTO t1(docid,words) VALUES(1042022,'And Reuben answered them, saying, Spake I not unto you, saying, Do not sin against the child; and ye would not hear? therefore, behold, also his blood is required.');
+INSERT INTO t1(docid,words) VALUES(1042023,'And they knew not that Joseph understood them; for he spake unto them by an interpreter.');
+INSERT INTO t1(docid,words) VALUES(1042024,'And he turned himself about from them, and wept; and returned to them again, and communed with them, and took from them Simeon, and bound him before their eyes.');
+INSERT INTO t1(docid,words) VALUES(1042025,'Then Joseph commanded to fill their sacks with corn, and to restore every man''s money into his sack, and to give them provision for the way: and thus did he unto them.');
+INSERT INTO t1(docid,words) VALUES(1042026,'And they laded their asses with the corn, and departed thence.');
+INSERT INTO t1(docid,words) VALUES(1042027,'And as one of them opened his sack to give his ass provender in the inn, he espied his money; for, behold, it was in his sack''s mouth.');
+INSERT INTO t1(docid,words) VALUES(1042028,'And he said unto his brethren, My money is restored; and, lo, it is even in my sack: and their heart failed them, and they were afraid, saying one to another, What is this that God hath done unto us?');
+INSERT INTO t1(docid,words) VALUES(1042029,'And they came unto Jacob their father unto the land of Canaan, and told him all that befell unto them; saying,');
+INSERT INTO t1(docid,words) VALUES(1042030,'The man, who is the lord of the land, spake roughly to us, and took us for spies of the country.');
+INSERT INTO t1(docid,words) VALUES(1042031,'And we said unto him, We are true men; we are no spies:');
+INSERT INTO t1(docid,words) VALUES(1042032,'We be twelve brethren, sons of our father; one is not, and the youngest is this day with our father in the land of Canaan.');
+INSERT INTO t1(docid,words) VALUES(1042033,'And the man, the lord of the country, said unto us, Hereby shall I know that ye are true men; leave one of your brethren here with me, and take food for the famine of your households, and be gone:');
+INSERT INTO t1(docid,words) VALUES(1042034,'And bring your youngest brother unto me: then shall I know that ye are no spies, but that ye are true men: so will I deliver you your brother, and ye shall traffick in the land.');
+INSERT INTO t1(docid,words) VALUES(1042035,'And it came to pass as they emptied their sacks, that, behold, every man''s bundle of money was in his sack: and when both they and their father saw the bundles of money, they were afraid.');
+INSERT INTO t1(docid,words) VALUES(1042036,'And Jacob their father said unto them, Me have ye bereaved of my children: Joseph is not, and Simeon is not, and ye will take Benjamin away: all these things are against me.');
+INSERT INTO t1(docid,words) VALUES(1042037,'And Reuben spake unto his father, saying, Slay my two sons, if I bring him not to thee: deliver him into my hand, and I will bring him to thee again.');
+INSERT INTO t1(docid,words) VALUES(1042038,'And he said, My son shall not go down with you; for his brother is dead, and he is left alone: if mischief befall him by the way in the which ye go, then shall ye bring down my gray hairs with sorrow to the grave.');
+INSERT INTO t1(docid,words) VALUES(1043001,'And the famine was sore in the land.');
+INSERT INTO t1(docid,words) VALUES(1043002,'And it came to pass, when they had eaten up the corn which they had brought out of Egypt, their father said unto them, Go again, buy us a little food.');
+INSERT INTO t1(docid,words) VALUES(1043003,'And Judah spake unto him, saying, The man did solemnly protest unto us, saying, Ye shall not see my face, except your brother be with you.');
+INSERT INTO t1(docid,words) VALUES(1043004,'If thou wilt send our brother with us, we will go down and buy thee food:');
+INSERT INTO t1(docid,words) VALUES(1043005,'But if thou wilt not send him, we will not go down: for the man said unto us, Ye shall not see my face, except your brother be with you.');
+INSERT INTO t1(docid,words) VALUES(1043006,'And Israel said, Wherefore dealt ye so ill with me, as to tell the man whether ye had yet a brother?');
+INSERT INTO t1(docid,words) VALUES(1043007,'And they said, The man asked us straitly of our state, and of our kindred, saying, Is your father yet alive? have ye another brother? and we told him according to the tenor of these words: could we certainly know that he would say, Bring your brother down?');
+INSERT INTO t1(docid,words) VALUES(1043008,'And Judah said unto Israel his father, Send the lad with me, and we will arise and go; that we may live, and not die, both we, and thou, and also our little ones.');
+INSERT INTO t1(docid,words) VALUES(1043009,'I will be surety for him; of my hand shalt thou require him: if I bring him not unto thee, and set him before thee, then let me bear the blame for ever:');
+INSERT INTO t1(docid,words) VALUES(1043010,'For except we had lingered, surely now we had returned this second time.');
+INSERT INTO t1(docid,words) VALUES(1043011,'And their father Israel said unto them, If it must be so now, do this; take of the best fruits in the land in your vessels, and carry down the man a present, a little balm, and a little honey, spices, and myrrh, nuts, and almonds:');
+INSERT INTO t1(docid,words) VALUES(1043012,'And take double money in your hand; and the money that was brought again in the mouth of your sacks, carry it again in your hand; peradventure it was an oversight:');
+INSERT INTO t1(docid,words) VALUES(1043013,'Take also your brother, and arise, go again unto the man:');
+INSERT INTO t1(docid,words) VALUES(1043014,'And God Almighty give you mercy before the man, that he may send away your other brother, and Benjamin. If I be bereaved of my children, I am bereaved.');
+INSERT INTO t1(docid,words) VALUES(1043015,'And the men took that present, and they took double money in their hand and Benjamin; and rose up, and went down to Egypt, and stood before Joseph.');
+INSERT INTO t1(docid,words) VALUES(1043016,'And when Joseph saw Benjamin with them, he said to the ruler of his house, Bring these men home, and slay, and make ready; for these men shall dine with me at noon.');
+INSERT INTO t1(docid,words) VALUES(1043017,'And the man did as Joseph bade; and the man brought the men into Joseph''s house.');
+INSERT INTO t1(docid,words) VALUES(1043018,'And the men were afraid, because they were brought into Joseph''s house; and they said, Because of the money that was returned in our sacks at the first time are we brought in; that he may seek occasion against us, and fall upon us, and take us for bondmen, and our asses.');
+INSERT INTO t1(docid,words) VALUES(1043019,'And they came near to the steward of Joseph''s house, and they communed with him at the door of the house,');
+INSERT INTO t1(docid,words) VALUES(1043020,'And said, O sir, we came indeed down at the first time to buy food:');
+INSERT INTO t1(docid,words) VALUES(1043021,'And it came to pass, when we came to the inn, that we opened our sacks, and, behold, every man''s money was in the mouth of his sack, our money in full weight: and we have brought it again in our hand.');
+INSERT INTO t1(docid,words) VALUES(1043022,'And other money have we brought down in our hands to buy food: we cannot tell who put our money in our sacks.');
+INSERT INTO t1(docid,words) VALUES(1043023,'And he said, Peace be to you, fear not: your God, and the God of your father, hath given you treasure in your sacks: I had your money. And he brought Simeon out unto them.');
+INSERT INTO t1(docid,words) VALUES(1043024,'And the man brought the men into Joseph''s house, and gave them water, and they washed their feet; and he gave their asses provender.');
+INSERT INTO t1(docid,words) VALUES(1043025,'And they made ready the present against Joseph came at noon: for they heard that they should eat bread there.');
+INSERT INTO t1(docid,words) VALUES(1043026,'And when Joseph came home, they brought him the present which was in their hand into the house, and bowed themselves to him to the earth.');
+INSERT INTO t1(docid,words) VALUES(1043027,'And he asked them of their welfare, and said, Is your father well, the old man of whom ye spake? Is he yet alive?');
+INSERT INTO t1(docid,words) VALUES(1043028,'And they answered, Thy servant our father is in good health, he is yet alive. And they bowed down their heads, and made obeisance.');
+INSERT INTO t1(docid,words) VALUES(1043029,'And he lifted up his eyes, and saw his brother Benjamin, his mother''s son, and said, Is this your younger brother, of whom ye spake unto me? And he said, God be gracious unto thee, my son.');
+INSERT INTO t1(docid,words) VALUES(1043030,'And Joseph made haste; for his bowels did yearn upon his brother: and he sought where to weep; and he entered into his chamber, and wept there.');
+INSERT INTO t1(docid,words) VALUES(1043031,'And he washed his face, and went out, and refrained himself, and said, Set on bread.');
+INSERT INTO t1(docid,words) VALUES(1043032,'And they set on for him by himself, and for them by themselves, and for the Egyptians, which did eat with him, by themselves: because the Egyptians might not eat bread with the Hebrews; for that is an abomination unto the Egyptians.');
+INSERT INTO t1(docid,words) VALUES(1043033,'And they sat before him, the firstborn according to his birthright, and the youngest according to his youth: and the men marvelled one at another.');
+INSERT INTO t1(docid,words) VALUES(1043034,'And he took and sent messes unto them from before him: but Benjamin''s mess was five times so much as any of their''s. And they drank, and were merry with him.');
+INSERT INTO t1(docid,words) VALUES(1044001,'And he commanded the steward of his house, saying, Fill the men''s sacks with food, as much as they can carry, and put every man''s money in his sack''s mouth.');
+INSERT INTO t1(docid,words) VALUES(1044002,'And put my cup, the silver cup, in the sack''s mouth of the youngest, and his corn money. And he did according to the word that Joseph had spoken.');
+INSERT INTO t1(docid,words) VALUES(1044003,'As soon as the morning was light, the men were sent away, they and their asses.');
+INSERT INTO t1(docid,words) VALUES(1044004,'And when they were gone out of the city, and not yet far off, Joseph said unto his steward, Up, follow after the men; and when thou dost overtake them, say unto them, Wherefore have ye rewarded evil for good?');
+INSERT INTO t1(docid,words) VALUES(1044005,'Is not this it in which my lord drinketh, and whereby indeed he divineth? ye have done evil in so doing.');
+INSERT INTO t1(docid,words) VALUES(1044006,'And he overtook them, and he spake unto them these same words.');
+INSERT INTO t1(docid,words) VALUES(1044007,'And they said unto him, Wherefore saith my lord these words? God forbid that thy servants should do according to this thing:');
+INSERT INTO t1(docid,words) VALUES(1044008,'Behold, the money, which we found in our sacks'' mouths, we brought again unto thee out of the land of Canaan: how then should we steal out of thy lord''s house silver or gold?');
+INSERT INTO t1(docid,words) VALUES(1044009,'With whomsoever of thy servants it be found, both let him die, and we also will be my lord''s bondmen.');
+INSERT INTO t1(docid,words) VALUES(1044010,'And he said, Now also let it be according unto your words: he with whom it is found shall be my servant; and ye shall be blameless.');
+INSERT INTO t1(docid,words) VALUES(1044011,'Then they speedily took down every man his sack to the ground, and opened every man his sack.');
+INSERT INTO t1(docid,words) VALUES(1044012,'And he searched, and began at the eldest, and left at the youngest: and the cup was found in Benjamin''s sack.');
+INSERT INTO t1(docid,words) VALUES(1044013,'Then they rent their clothes, and laded every man his ass, and returned to the city.');
+INSERT INTO t1(docid,words) VALUES(1044014,'And Judah and his brethren came to Joseph''s house; for he was yet there: and they fell before him on the ground.');
+INSERT INTO t1(docid,words) VALUES(1044015,'And Joseph said unto them, What deed is this that ye have done? wot ye not that such a man as I can certainly divine?');
+INSERT INTO t1(docid,words) VALUES(1044016,'And Judah said, What shall we say unto my lord? what shall we speak? or how shall we clear ourselves? God hath found out the iniquity of thy servants: behold, we are my lord''s servants, both we, and he also with whom the cup is found.');
+INSERT INTO t1(docid,words) VALUES(1044017,'And he said, God forbid that I should do so: but the man in whose hand the cup is found, he shall be my servant; and as for you, get you up in peace unto your father.');
+INSERT INTO t1(docid,words) VALUES(1044018,'Then Judah came near unto him, and said, Oh my lord, let thy servant, I pray thee, speak a word in my lord''s ears, and let not thine anger burn against thy servant: for thou art even as Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1044019,'My lord asked his servants, saying, Have ye a father, or a brother?');
+INSERT INTO t1(docid,words) VALUES(1044020,'And we said unto my lord, We have a father, an old man, and a child of his old age, a little one; and his brother is dead, and he alone is left of his mother, and his father loveth him.');
+INSERT INTO t1(docid,words) VALUES(1044021,'And thou saidst unto thy servants, Bring him down unto me, that I may set mine eyes upon him.');
+INSERT INTO t1(docid,words) VALUES(1044022,'And we said unto my lord, The lad cannot leave his father: for if he should leave his father, his father would die.');
+INSERT INTO t1(docid,words) VALUES(1044023,'And thou saidst unto thy servants, Except your youngest brother come down with you, ye shall see my face no more.');
+INSERT INTO t1(docid,words) VALUES(1044024,'And it came to pass when we came up unto thy servant my father, we told him the words of my lord.');
+INSERT INTO t1(docid,words) VALUES(1044025,'And our father said, Go again, and buy us a little food.');
+INSERT INTO t1(docid,words) VALUES(1044026,'And we said, We cannot go down: if our youngest brother be with us, then will we go down: for we may not see the man''s face, except our youngest brother be with us.');
+INSERT INTO t1(docid,words) VALUES(1044027,'And thy servant my father said unto us, Ye know that my wife bare me two sons:');
+INSERT INTO t1(docid,words) VALUES(1044028,'And the one went out from me, and I said, Surely he is torn in pieces; and I saw him not since:');
+INSERT INTO t1(docid,words) VALUES(1044029,'And if ye take this also from me, and mischief befall him, ye shall bring down my gray hairs with sorrow to the grave.');
+INSERT INTO t1(docid,words) VALUES(1044030,'Now therefore when I come to thy servant my father, and the lad be not with us; seeing that his life is bound up in the lad''s life;');
+INSERT INTO t1(docid,words) VALUES(1044031,'It shall come to pass, when he seeth that the lad is not with us, that he will die: and thy servants shall bring down the gray hairs of thy servant our father with sorrow to the grave.');
+INSERT INTO t1(docid,words) VALUES(1044032,'For thy servant became surety for the lad unto my father, saying, If I bring him not unto thee, then I shall bear the blame to my father for ever.');
+INSERT INTO t1(docid,words) VALUES(1044033,'Now therefore, I pray thee, let thy servant abide instead of the lad a bondman to my lord; and let the lad go up with his brethren.');
+INSERT INTO t1(docid,words) VALUES(1044034,'For how shall I go up to my father, and the lad be not with me? lest peradventure I see the evil that shall come on my father.');
+INSERT INTO t1(docid,words) VALUES(1045001,'Then Joseph could not refrain himself before all them that stood by him; and he cried, Cause every man to go out from me. And there stood no man with him, while Joseph made himself known unto his brethren.');
+INSERT INTO t1(docid,words) VALUES(1045002,'And he wept aloud: and the Egyptians and the house of Pharaoh heard.');
+INSERT INTO t1(docid,words) VALUES(1045003,'And Joseph said unto his brethren, I am Joseph; doth my father yet live? And his brethren could not answer him; for they were troubled at his presence.');
+INSERT INTO t1(docid,words) VALUES(1045004,'And Joseph said unto his brethren, Come near to me, I pray you. And they came near. And he said, I am Joseph your brother, whom ye sold into Egypt.');
+INSERT INTO t1(docid,words) VALUES(1045005,'Now therefore be not grieved, nor angry with yourselves, that ye sold me hither: for God did send me before you to preserve life.');
+INSERT INTO t1(docid,words) VALUES(1045006,'For these two years hath the famine been in the land: and yet there are five years, in the which there shall neither be earing nor harvest.');
+INSERT INTO t1(docid,words) VALUES(1045007,'And God sent me before you to preserve you a posterity in the earth, and to save your lives by a great deliverance.');
+INSERT INTO t1(docid,words) VALUES(1045008,'So now it was not you that sent me hither, but God: and he hath made me a father to Pharaoh, and lord of all his house, and a ruler throughout all the land of Egypt.');
+INSERT INTO t1(docid,words) VALUES(1045009,'Haste ye, and go up to my father, and say unto him, Thus saith thy son Joseph, God hath made me lord of all Egypt: come down unto me, tarry not:');
+INSERT INTO t1(docid,words) VALUES(1045010,'And thou shalt dwell in the land of Goshen, and thou shalt be near unto me, thou, and thy children, and thy children''s children, and thy flocks, and thy herds, and all that thou hast:');
+INSERT INTO t1(docid,words) VALUES(1045011,'And there will I nourish thee; for yet there are five years of famine; lest thou, and thy household, and all that thou hast, come to poverty.');
+INSERT INTO t1(docid,words) VALUES(1045012,'And, behold, your eyes see, and the eyes of my brother Benjamin, that it is my mouth that speaketh unto you.');
+INSERT INTO t1(docid,words) VALUES(1045013,'And ye shall tell my father of all my glory in Egypt, and of all that ye have seen; and ye shall haste and bring down my father hither.');
+INSERT INTO t1(docid,words) VALUES(1045014,'And he fell upon his brother Benjamin''s neck, and wept; and Benjamin wept upon his neck.');
+INSERT INTO t1(docid,words) VALUES(1045015,'Moreover he kissed all his brethren, and wept upon them: and after that his brethren talked with him.');
+INSERT INTO t1(docid,words) VALUES(1045016,'And the fame thereof was heard in Pharaoh''s house, saying, Joseph''s brethren are come: and it pleased Pharaoh well, and his servants.');
+INSERT INTO t1(docid,words) VALUES(1045017,'And Pharaoh said unto Joseph, Say unto thy brethren, This do ye; lade your beasts, and go, get you unto the land of Canaan;');
+INSERT INTO t1(docid,words) VALUES(1045018,'And take your father and your households, and come unto me: and I will give you the good of the land of Egypt, and ye shall eat the fat of the land.');
+INSERT INTO t1(docid,words) VALUES(1045019,'Now thou art commanded, this do ye; take you wagons out of the land of Egypt for your little ones, and for your wives, and bring your father, and come.');
+INSERT INTO t1(docid,words) VALUES(1045020,'Also regard not your stuff; for the good of all the land of Egypt is your''s.');
+INSERT INTO t1(docid,words) VALUES(1045021,'And the children of Israel did so: and Joseph gave them wagons, according to the commandment of Pharaoh, and gave them provision for the way.');
+INSERT INTO t1(docid,words) VALUES(1045022,'To all of them he gave each man changes of raiment; but to Benjamin he gave three hundred pieces of silver, and five changes of raiment.');
+INSERT INTO t1(docid,words) VALUES(1045023,'And to his father he sent after this manner; ten asses laden with the good things of Egypt, and ten she asses laden with corn and bread and meat for his father by the way.');
+INSERT INTO t1(docid,words) VALUES(1045024,'So he sent his brethren away, and they departed: and he said unto them, See that ye fall not out by the way.');
+INSERT INTO t1(docid,words) VALUES(1045025,'And they went up out of Egypt, and came into the land of Canaan unto Jacob their father,');
+INSERT INTO t1(docid,words) VALUES(1045026,'And told him, saying, Joseph is yet alive, and he is governor over all the land of Egypt. And Jacob''s heart fainted, for he believed them not.');
+INSERT INTO t1(docid,words) VALUES(1045027,'And they told him all the words of Joseph, which he had said unto them: and when he saw the wagons which Joseph had sent to carry him, the spirit of Jacob their father revived:');
+INSERT INTO t1(docid,words) VALUES(1045028,'And Israel said, It is enough; Joseph my son is yet alive: I will go and see him before I die.');
+INSERT INTO t1(docid,words) VALUES(1046001,'And Israel took his journey with all that he had, and came to Beersheba, and offered sacrifices unto the God of his father Isaac.');
+INSERT INTO t1(docid,words) VALUES(1046002,'And God spake unto Israel in the visions of the night, and said, Jacob, Jacob. And he said, Here am I.');
+INSERT INTO t1(docid,words) VALUES(1046003,'And he said, I am God, the God of thy father: fear not to go down into Egypt; for I will there make of thee a great nation:');
+INSERT INTO t1(docid,words) VALUES(1046004,'I will go down with thee into Egypt; and I will also surely bring thee up again: and Joseph shall put his hand upon thine eyes.');
+INSERT INTO t1(docid,words) VALUES(1046005,'And Jacob rose up from Beersheba: and the sons of Israel carried Jacob their father, and their little ones, and their wives, in the wagons which Pharaoh had sent to carry him.');
+INSERT INTO t1(docid,words) VALUES(1046006,'And they took their cattle, and their goods, which they had gotten in the land of Canaan, and came into Egypt, Jacob, and all his seed with him:');
+INSERT INTO t1(docid,words) VALUES(1046007,'His sons, and his sons'' sons with him, his daughters, and his sons'' daughters, and all his seed brought he with him into Egypt.');
+INSERT INTO t1(docid,words) VALUES(1046008,'And these are the names of the children of Israel, which came into Egypt, Jacob and his sons: Reuben, Jacob''s firstborn.');
+INSERT INTO t1(docid,words) VALUES(1046009,'And the sons of Reuben; Hanoch, and Phallu, and Hezron, and Carmi.');
+INSERT INTO t1(docid,words) VALUES(1046010,'And the sons of Simeon; Jemuel, and Jamin, and Ohad, and Jachin, and Zohar, and Shaul the son of a Canaanitish woman.');
+INSERT INTO t1(docid,words) VALUES(1046011,'And the sons of Levi; Gershon, Kohath, and Merari.');
+INSERT INTO t1(docid,words) VALUES(1046012,'And the sons of Judah; Er, and Onan, and Shelah, and Pharez, and Zarah: but Er and Onan died in the land of Canaan. And the sons of Pharez were Hezron and Hamul.');
+INSERT INTO t1(docid,words) VALUES(1046013,'And the sons of Issachar; Tola, and Phuvah, and Job, and Shimron.');
+INSERT INTO t1(docid,words) VALUES(1046014,'And the sons of Zebulun; Sered, and Elon, and Jahleel.');
+INSERT INTO t1(docid,words) VALUES(1046015,'These be the sons of Leah, which she bare unto Jacob in Padanaram, with his daughter Dinah: all the souls of his sons and his daughters were thirty and three.');
+INSERT INTO t1(docid,words) VALUES(1046016,'And the sons of Gad; Ziphion, and Haggi, Shuni, and Ezbon, Eri, and Arodi, and Areli.');
+INSERT INTO t1(docid,words) VALUES(1046017,'And the sons of Asher; Jimnah, and Ishuah, and Isui, and Beriah, and Serah their sister: and the sons of Beriah; Heber, and Malchiel.');
+INSERT INTO t1(docid,words) VALUES(1046018,'These are the sons of Zilpah, whom Laban gave to Leah his daughter, and these she bare unto Jacob, even sixteen souls.');
+INSERT INTO t1(docid,words) VALUES(1046019,'The sons of Rachel Jacob''s wife; Joseph, and Benjamin.');
+INSERT INTO t1(docid,words) VALUES(1046020,'And unto Joseph in the land of Egypt were born Manasseh and Ephraim, which Asenath the daughter of Potipherah priest of On bare unto him.');
+INSERT INTO t1(docid,words) VALUES(1046021,'And the sons of Benjamin were Belah, and Becher, and Ashbel, Gera, and Naaman, Ehi, and Rosh, Muppim, and Huppim, and Ard.');
+INSERT INTO t1(docid,words) VALUES(1046022,'These are the sons of Rachel, which were born to Jacob: all the souls were fourteen.');
+INSERT INTO t1(docid,words) VALUES(1046023,'And the sons of Dan; Hushim.');
+INSERT INTO t1(docid,words) VALUES(1046024,'And the sons of Naphtali; Jahzeel, and Guni, and Jezer, and Shillem.');
+INSERT INTO t1(docid,words) VALUES(1046025,'These are the sons of Bilhah, which Laban gave unto Rachel his daughter, and she bare these unto Jacob: all the souls were seven.');
+INSERT INTO t1(docid,words) VALUES(1046026,'All the souls that came with Jacob into Egypt, which came out of his loins, besides Jacob''s sons'' wives, all the souls were threescore and six;');
+INSERT INTO t1(docid,words) VALUES(1046027,'And the sons of Joseph, which were born him in Egypt, were two souls: all the souls of the house of Jacob, which came into Egypt, were threescore and ten.');
+INSERT INTO t1(docid,words) VALUES(1046028,'And he sent Judah before him unto Joseph, to direct his face unto Goshen; and they came into the land of Goshen.');
+INSERT INTO t1(docid,words) VALUES(1046029,'And Joseph made ready his chariot, and went up to meet Israel his father, to Goshen, and presented himself unto him; and he fell on his neck, and wept on his neck a good while.');
+INSERT INTO t1(docid,words) VALUES(1046030,'And Israel said unto Joseph, Now let me die, since I have seen thy face, because thou art yet alive.');
+INSERT INTO t1(docid,words) VALUES(1046031,'And Joseph said unto his brethren, and unto his father''s house, I will go up, and shew Pharaoh, and say unto him, My brethren, and my father''s house, which were in the land of Canaan, are come unto me;');
+INSERT INTO t1(docid,words) VALUES(1046032,'And the men are shepherds, for their trade hath been to feed cattle; and they have brought their flocks, and their herds, and all that they have.');
+INSERT INTO t1(docid,words) VALUES(1046033,'And it shall come to pass, when Pharaoh shall call you, and shall say, What is your occupation?');
+INSERT INTO t1(docid,words) VALUES(1046034,'That ye shall say, Thy servants'' trade hath been about cattle from our youth even until now, both we, and also our fathers: that ye may dwell in the land of Goshen; for every shepherd is an abomination unto the Egyptians.');
+INSERT INTO t1(docid,words) VALUES(1047001,'Then Joseph came and told Pharaoh, and said, My father and my brethren, and their flocks, and their herds, and all that they have, are come out of the land of Canaan; and, behold, they are in the land of Goshen.');
+INSERT INTO t1(docid,words) VALUES(1047002,'And he took some of his brethren, even five men, and presented them unto Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1047003,'And Pharaoh said unto his brethren, What is your occupation? And they said unto Pharaoh, Thy servants are shepherds, both we, and also our fathers.');
+INSERT INTO t1(docid,words) VALUES(1047004,'They said morever unto Pharaoh, For to sojourn in the land are we come; for thy servants have no pasture for their flocks; for the famine is sore in the land of Canaan: now therefore, we pray thee, let thy servants dwell in the land of Goshen.');
+INSERT INTO t1(docid,words) VALUES(1047005,'And Pharaoh spake unto Joseph, saying, Thy father and thy brethren are come unto thee:');
+INSERT INTO t1(docid,words) VALUES(1047006,'The land of Egypt is before thee; in the best of the land make thy father and brethren to dwell; in the land of Goshen let them dwell: and if thou knowest any men of activity among them, then make them rulers over my cattle.');
+INSERT INTO t1(docid,words) VALUES(1047007,'And Joseph brought in Jacob his father, and set him before Pharaoh: and Jacob blessed Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1047008,'And Pharaoh said unto Jacob, How old art thou?');
+INSERT INTO t1(docid,words) VALUES(1047009,'And Jacob said unto Pharaoh, The days of the years of my pilgrimage are an hundred and thirty years: few and evil have the days of the years of my life been, and have not attained unto the days of the years of the life of my fathers in the days of their pilgrimage.');
+INSERT INTO t1(docid,words) VALUES(1047010,'And Jacob blessed Pharaoh, and went out from before Pharaoh.');
+INSERT INTO t1(docid,words) VALUES(1047011,'And Joseph placed his father and his brethren, and gave them a possession in the land of Egypt, in the best of the land, in the land of Rameses, as Pharaoh had commanded.');
+INSERT INTO t1(docid,words) VALUES(1047012,'And Joseph nourished his father, and his brethren, and all his father''s household, with bread, according to their families.');
+INSERT INTO t1(docid,words) VALUES(1047013,'And there was no bread in all the land; for the famine was very sore, so that the land of Egypt and all the land of Canaan fainted by reason of the famine.');
+INSERT INTO t1(docid,words) VALUES(1047014,'And Joseph gathered up all the money that was found in the land of Egypt, and in the land of Canaan, for the corn which they bought: and Joseph brought the money into Pharaoh''s house.');
+INSERT INTO t1(docid,words) VALUES(1047015,'And when money failed in the land of Egypt, and in the land of Canaan, all the Egyptians came unto Joseph, and said, Give us bread: for why should we die in thy presence? for the money faileth.');
+INSERT INTO t1(docid,words) VALUES(1047016,'And Joseph said, Give your cattle; and I will give you for your cattle, if money fail.');
+INSERT INTO t1(docid,words) VALUES(1047017,'And they brought their cattle unto Joseph: and Joseph gave them bread in exchange for horses, and for the flocks, and for the cattle of the herds, and for the asses: and he fed them with bread for all their cattle for that year.');
+INSERT INTO t1(docid,words) VALUES(1047018,'When that year was ended, they came unto him the second year, and said unto him, We will not hide it from my lord, how that our money is spent; my lord also hath our herds of cattle; there is not ought left in the sight of my lord, but our bodies, and our lands:');
+INSERT INTO t1(docid,words) VALUES(1047019,'Wherefore shall we die before thine eyes, both we and our land? buy us and our land for bread, and we and our land will be servants unto Pharaoh: and give us seed, that we may live, and not die, that the land be not desolate.');
+INSERT INTO t1(docid,words) VALUES(1047020,'And Joseph bought all the land of Egypt for Pharaoh; for the Egyptians sold every man his field, because the famine prevailed over them: so the land became Pharaoh''s.');
+INSERT INTO t1(docid,words) VALUES(1047021,'And as for the people, he removed them to cities from one end of the borders of Egypt even to the other end thereof.');
+INSERT INTO t1(docid,words) VALUES(1047022,'Only the land of the priests bought he not; for the priests had a portion assigned them of Pharaoh, and did eat their portion which Pharaoh gave them: wherefore they sold not their lands.');
+INSERT INTO t1(docid,words) VALUES(1047023,'Then Joseph said unto the people, Behold, I have bought you this day and your land for Pharaoh: lo, here is seed for you, and ye shall sow the land.');
+INSERT INTO t1(docid,words) VALUES(1047024,'And it shall come to pass in the increase, that ye shall give the fifth part unto Pharaoh, and four parts shall be your own, for seed of the field, and for your food, and for them of your households, and for food for your little ones.');
+INSERT INTO t1(docid,words) VALUES(1047025,'And they said, Thou hast saved our lives: let us find grace in the sight of my lord, and we will be Pharaoh''s servants.');
+INSERT INTO t1(docid,words) VALUES(1047026,'And Joseph made it a law over the land of Egypt unto this day, that Pharaoh should have the fifth part, except the land of the priests only, which became not Pharaoh''s.');
+INSERT INTO t1(docid,words) VALUES(1047027,'And Israel dwelt in the land of Egypt, in the country of Goshen; and they had possessions therein, and grew, and multiplied exceedingly.');
+INSERT INTO t1(docid,words) VALUES(1047028,'And Jacob lived in the land of Egypt seventeen years: so the whole age of Jacob was an hundred forty and seven years.');
+INSERT INTO t1(docid,words) VALUES(1047029,'And the time drew nigh that Israel must die: and he called his son Joseph, and said unto him, If now I have found grace in thy sight, put, I pray thee, thy hand under my thigh, and deal kindly and truly with me; bury me not, I pray thee, in Egypt:');
+INSERT INTO t1(docid,words) VALUES(1047030,'But I will lie with my fathers, and thou shalt carry me out of Egypt, and bury me in their buryingplace. And he said, I will do as thou hast said.');
+INSERT INTO t1(docid,words) VALUES(1047031,'And he said, Swear unto me. And he sware unto him. And Israel bowed himself upon the bed''s head.');
+INSERT INTO t1(docid,words) VALUES(1048001,'And it came to pass after these things, that one told Joseph, Behold, thy father is sick: and he took with him his two sons, Manasseh and Ephraim.');
+INSERT INTO t1(docid,words) VALUES(1048002,'And one told Jacob, and said, Behold, thy son Joseph cometh unto thee: and Israel strengthened himself, and sat upon the bed.');
+INSERT INTO t1(docid,words) VALUES(1048003,'And Jacob said unto Joseph, God Almighty appeared unto me at Luz in the land of Canaan, and blessed me,');
+INSERT INTO t1(docid,words) VALUES(1048004,'And said unto me, Behold, I will make thee fruitful, and multiply thee, and I will make of thee a multitude of people; and will give this land to thy seed after thee for an everlasting possession.');
+INSERT INTO t1(docid,words) VALUES(1048005,'And now thy two sons, Ephraim and Manasseh, which were born unto thee in the land of Egypt before I came unto thee into Egypt, are mine; as Reuben and Simeon, they shall be mine.');
+INSERT INTO t1(docid,words) VALUES(1048006,'And thy issue, which thou begettest after them, shall be thine, and shall be called after the name of their brethren in their inheritance.');
+INSERT INTO t1(docid,words) VALUES(1048007,'And as for me, when I came from Padan, Rachel died by me in the land of Canaan in the way, when yet there was but a little way to come unto Ephrath: and I buried her there in the way of Ephrath; the same is Bethlehem.');
+INSERT INTO t1(docid,words) VALUES(1048008,'And Israel beheld Joseph''s sons, and said, Who are these?');
+INSERT INTO t1(docid,words) VALUES(1048009,'And Joseph said unto his father, They are my sons, whom God hath given me in this place. And he said, Bring them, I pray thee, unto me, and I will bless them.');
+INSERT INTO t1(docid,words) VALUES(1048010,'Now the eyes of Israel were dim for age, so that he could not see. And he brought them near unto him; and he kissed them, and embraced them.');
+INSERT INTO t1(docid,words) VALUES(1048011,'And Israel said unto Joseph, I had not thought to see thy face: and, lo, God hath shewed me also thy seed.');
+INSERT INTO t1(docid,words) VALUES(1048012,'And Joseph brought them out from between his knees, and he bowed himself with his face to the earth.');
+INSERT INTO t1(docid,words) VALUES(1048013,'And Joseph took them both, Ephraim in his right hand toward Israel''s left hand, and Manasseh in his left hand toward Israel''s right hand, and brought them near unto him.');
+INSERT INTO t1(docid,words) VALUES(1048014,'And Israel stretched out his right hand, and laid it upon Ephraim''s head, who was the younger, and his left hand upon Manasseh''s head, guiding his hands wittingly; for Manasseh was the firstborn.');
+INSERT INTO t1(docid,words) VALUES(1048015,'And he blessed Joseph, and said, God, before whom my fathers Abraham and Isaac did walk, the God which fed me all my life long unto this day,');
+INSERT INTO t1(docid,words) VALUES(1048016,'The Angel which redeemed me from all evil, bless the lads; and let my name be named on them, and the name of my fathers Abraham and Isaac; and let them grow into a multitude in the midst of the earth.');
+INSERT INTO t1(docid,words) VALUES(1048017,'And when Joseph saw that his father laid his right hand upon the head of Ephraim, it displeased him: and he held up his father''s hand, to remove it from Ephraim''s head unto Manasseh''s head.');
+INSERT INTO t1(docid,words) VALUES(1048018,'And Joseph said unto his father, Not so, my father: for this is the firstborn; put thy right hand upon his head.');
+INSERT INTO t1(docid,words) VALUES(1048019,'And his father refused, and said, I know it, my son, I know it: he also shall become a people, and he also shall be great: but truly his younger brother shall be greater than he, and his seed shall become a multitude of nations.');
+INSERT INTO t1(docid,words) VALUES(1048020,'And he blessed them that day, saying, In thee shall Israel bless, saying, God make thee as Ephraim and as Manasseh: and he set Ephraim before Manasseh.');
+INSERT INTO t1(docid,words) VALUES(1048021,'And Israel said unto Joseph, Behold, I die: but God shall be with you, and bring you again unto the land of your fathers.');
+INSERT INTO t1(docid,words) VALUES(1048022,'Moreover I have given to thee one portion above thy brethren, which I took out of the hand of the Amorite with my sword and with my bow.');
+INSERT INTO t1(docid,words) VALUES(1049001,'And Jacob called unto his sons, and said, Gather yourselves together, that I may tell you that which shall befall you in the last days.');
+INSERT INTO t1(docid,words) VALUES(1049002,'Gather yourselves together, and hear, ye sons of Jacob; and hearken unto Israel your father.');
+INSERT INTO t1(docid,words) VALUES(1049003,'Reuben, thou art my firstborn, my might, and the beginning of my strength, the excellency of dignity, and the excellency of power:');
+INSERT INTO t1(docid,words) VALUES(1049004,'Unstable as water, thou shalt not excel; because thou wentest up to thy father''s bed; then defiledst thou it: he went up to my couch.');
+INSERT INTO t1(docid,words) VALUES(1049005,'Simeon and Levi are brethren; instruments of cruelty are in their habitations.');
+INSERT INTO t1(docid,words) VALUES(1049006,'O my soul, come not thou into their secret; unto their assembly, mine honour, be not thou united: for in their anger they slew a man, and in their selfwill they digged down a wall.');
+INSERT INTO t1(docid,words) VALUES(1049007,'Cursed be their anger, for it was fierce; and their wrath, for it was cruel: I will divide them in Jacob, and scatter them in Israel.');
+INSERT INTO t1(docid,words) VALUES(1049008,'Judah, thou art he whom thy brethren shall praise: thy hand shall be in the neck of thine enemies; thy father''s children shall bow down before thee.');
+INSERT INTO t1(docid,words) VALUES(1049009,'Judah is a lion''s whelp: from the prey, my son, thou art gone up: he stooped down, he couched as a lion, and as an old lion; who shall rouse him up?');
+INSERT INTO t1(docid,words) VALUES(1049010,'The sceptre shall not depart from Judah, nor a lawgiver from between his feet, until Shiloh come; and unto him shall the gathering of the people be.');
+INSERT INTO t1(docid,words) VALUES(1049011,'Binding his foal unto the vine, and his ass''s colt unto the choice vine; he washed his garments in wine, and his clothes in the blood of grapes:');
+INSERT INTO t1(docid,words) VALUES(1049012,'His eyes shall be red with wine, and his teeth white with milk.');
+INSERT INTO t1(docid,words) VALUES(1049013,'Zebulun shall dwell at the haven of the sea; and he shall be for an haven of ships; and his border shall be unto Zidon.');
+INSERT INTO t1(docid,words) VALUES(1049014,'Issachar is a strong ass couching down between two burdens:');
+INSERT INTO t1(docid,words) VALUES(1049015,'And he saw that rest was good, and the land that it was pleasant; and bowed his shoulder to bear, and became a servant unto tribute.');
+INSERT INTO t1(docid,words) VALUES(1049016,'Dan shall judge his people, as one of the tribes of Israel.');
+INSERT INTO t1(docid,words) VALUES(1049017,'Dan shall be a serpent by the way, an adder in the path, that biteth the horse heels, so that his rider shall fall backward.');
+INSERT INTO t1(docid,words) VALUES(1049018,'I have waited for thy salvation, O LORD.');
+INSERT INTO t1(docid,words) VALUES(1049019,'Gad, a troop shall overcome him: but he shall overcome at the last.');
+INSERT INTO t1(docid,words) VALUES(1049020,'Out of Asher his bread shall be fat, and he shall yield royal dainties.');
+INSERT INTO t1(docid,words) VALUES(1049021,'Naphtali is a hind let loose: he giveth goodly words.');
+INSERT INTO t1(docid,words) VALUES(1049022,'Joseph is a fruitful bough, even a fruitful bough by a well; whose branches run over the wall:');
+INSERT INTO t1(docid,words) VALUES(1049023,'The archers have sorely grieved him, and shot at him, and hated him:');
+INSERT INTO t1(docid,words) VALUES(1049024,'But his bow abode in strength, and the arms of his hands were made strong by the hands of the mighty God of Jacob; (from thence is the shepherd, the stone of Israel:)');
+INSERT INTO t1(docid,words) VALUES(1049025,'Even by the God of thy father, who shall help thee; and by the Almighty, who shall bless thee with blessings of heaven above, blessings of the deep that lieth under, blessings of the breasts, and of the womb:');
+INSERT INTO t1(docid,words) VALUES(1049026,'The blessings of thy father have prevailed above the blessings of my progenitors unto the utmost bound of the everlasting hills: they shall be on the head of Joseph, and on the crown of the head of him that was separate from his brethren.');
+INSERT INTO t1(docid,words) VALUES(1049027,'Benjamin shall ravin as a wolf: in the morning he shall devour the prey, and at night he shall divide the spoil.');
+INSERT INTO t1(docid,words) VALUES(1049028,'All these are the twelve tribes of Israel: and this is it that their father spake unto them, and blessed them; every one according to his blessing he blessed them.');
+INSERT INTO t1(docid,words) VALUES(1049029,'And he charged them, and said unto them, I am to be gathered unto my people: bury me with my fathers in the cave that is in the field of Ephron the Hittite,');
+INSERT INTO t1(docid,words) VALUES(1049030,'In the cave that is in the field of Machpelah, which is before Mamre, in the land of Canaan, which Abraham bought with the field of Ephron the Hittite for a possession of a buryingplace.');
+INSERT INTO t1(docid,words) VALUES(1049031,'There they buried Abraham and Sarah his wife; there they buried Isaac and Rebekah his wife; and there I buried Leah.');
+INSERT INTO t1(docid,words) VALUES(1049032,'The purchase of the field and of the cave that is therein was from the children of Heth.');
+INSERT INTO t1(docid,words) VALUES(1049033,'And when Jacob had made an end of commanding his sons, he gathered up his feet into the bed, and yielded up the ghost, and was gathered unto his people.');
+INSERT INTO t1(docid,words) VALUES(1050001,'And Joseph fell upon his father''s face, and wept upon him, and kissed him.');
+INSERT INTO t1(docid,words) VALUES(1050002,'And Joseph commanded his servants the physicians to embalm his father: and the physicians embalmed Israel.');
+INSERT INTO t1(docid,words) VALUES(1050003,'And forty days were fulfilled for him; for so are fulfilled the days of those which are embalmed: and the Egyptians mourned for him threescore and ten days.');
+INSERT INTO t1(docid,words) VALUES(1050004,'And when the days of his mourning were past, Joseph spake unto the house of Pharaoh, saying, If now I have found grace in your eyes, speak, I pray you, in the ears of Pharaoh, saying,');
+INSERT INTO t1(docid,words) VALUES(1050005,'My father made me swear, saying, Lo, I die: in my grave which I have digged for me in the land of Canaan, there shalt thou bury me. Now therefore let me go up, I pray thee, and bury my father, and I will come again.');
+INSERT INTO t1(docid,words) VALUES(1050006,'And Pharaoh said, Go up, and bury thy father, according as he made thee swear.');
+INSERT INTO t1(docid,words) VALUES(1050007,'And Joseph went up to bury his father: and with him went up all the servants of Pharaoh, the elders of his house, and all the elders of the land of Egypt,');
+INSERT INTO t1(docid,words) VALUES(1050008,'And all the house of Joseph, and his brethren, and his father''s house: only their little ones, and their flocks, and their herds, they left in the land of Goshen.');
+INSERT INTO t1(docid,words) VALUES(1050009,'And there went up with him both chariots and horsemen: and it was a very great company.');
+INSERT INTO t1(docid,words) VALUES(1050010,'And they came to the threshingfloor of Atad, which is beyond Jordan, and there they mourned with a great and very sore lamentation: and he made a mourning for his father seven days.');
+INSERT INTO t1(docid,words) VALUES(1050011,'And when the inhabitants of the land, the Canaanites, saw the mourning in the floor of Atad, they said, This is a grievous mourning to the Egyptians: wherefore the name of it was called Abelmizraim, which is beyond Jordan.');
+INSERT INTO t1(docid,words) VALUES(1050012,'And his sons did unto him according as he commanded them:');
+INSERT INTO t1(docid,words) VALUES(1050013,'For his sons carried him into the land of Canaan, and buried him in the cave of the field of Machpelah, which Abraham bought with the field for a possession of a buryingplace of Ephron the Hittite, before Mamre.');
+INSERT INTO t1(docid,words) VALUES(1050014,'And Joseph returned into Egypt, he, and his brethren, and all that went up with him to bury his father, after he had buried his father.');
+INSERT INTO t1(docid,words) VALUES(1050015,'And when Joseph''s brethren saw that their father was dead, they said, Joseph will peradventure hate us, and will certainly requite us all the evil which we did unto him.');
+INSERT INTO t1(docid,words) VALUES(1050016,'And they sent a messenger unto Joseph, saying, Thy father did command before he died, saying,');
+INSERT INTO t1(docid,words) VALUES(1050017,'So shall ye say unto Joseph, Forgive, I pray thee now, the trespass of thy brethren, and their sin; for they did unto thee evil: and now, we pray thee, forgive the trespass of the servants of the God of thy father. And Joseph wept when they spake unto him.');
+INSERT INTO t1(docid,words) VALUES(1050018,'And his brethren also went and fell down before his face; and they said, Behold, we be thy servants.');
+INSERT INTO t1(docid,words) VALUES(1050019,'And Joseph said unto them, Fear not: for am I in the place of God?');
+INSERT INTO t1(docid,words) VALUES(1050020,'But as for you, ye thought evil against me; but God meant it unto good, to bring to pass, as it is this day, to save much people alive.');
+INSERT INTO t1(docid,words) VALUES(1050021,'Now therefore fear ye not: I will nourish you, and your little ones. And he comforted them, and spake kindly unto them.');
+INSERT INTO t1(docid,words) VALUES(1050022,'And Joseph dwelt in Egypt, he, and his father''s house: and Joseph lived an hundred and ten years.');
+INSERT INTO t1(docid,words) VALUES(1050023,'And Joseph saw Ephraim''s children of the third generation: the children also of Machir the son of Manasseh were brought up upon Joseph''s knees.');
+INSERT INTO t1(docid,words) VALUES(1050024,'And Joseph said unto his brethren, I die: and God will surely visit you, and bring you out of this land unto the land which he sware to Abraham, to Isaac, and to Jacob.');
+INSERT INTO t1(docid,words) VALUES(1050025,'And Joseph took an oath of the children of Israel, saying, God will surely visit you, and ye shall carry up my bones from hence.');
+INSERT INTO t1(docid,words) VALUES(1050026,'So Joseph died, being an hundred and ten years old: and they embalmed him, and he was put in a coffin in Egypt.');
+COMMIT;
+}
+}
diff --git a/test/hexlit.test b/test/hexlit.test
new file mode 100644
index 0000000..10909e6
--- /dev/null
+++ b/test/hexlit.test
@@ -0,0 +1,114 @@
+# 2014-07-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 tests for hexadecimal literals
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+proc hexlit1 {tnum val ans} {
+ do_execsql_test hexlit-$tnum "SELECT $val" $ans
+}
+
+hexlit1 100 0x0 0
+hexlit1 101 0x0000000000000000000000000000000000000000000001 1
+hexlit1 102 0x2 2
+hexlit1 103 0x4 4
+hexlit1 104 0x8 8
+hexlit1 105 0x00000000000000000000000000000000000000000000010 16
+hexlit1 103 0x20 32
+hexlit1 106 0x40 64
+hexlit1 107 0x80 128
+hexlit1 108 0x100 256
+hexlit1 109 0x200 512
+hexlit1 110 0X400 1024
+hexlit1 111 0x800 2048
+hexlit1 112 0x1000 4096
+hexlit1 113 0x2000 8192
+hexlit1 114 0x4000 16384
+hexlit1 115 0x8000 32768
+hexlit1 116 0x10000 65536
+hexlit1 117 0x20000 131072
+hexlit1 118 0x40000 262144
+hexlit1 119 0x80000 524288
+hexlit1 120 0x100000 1048576
+hexlit1 121 0x200000 2097152
+hexlit1 122 0x400000 4194304
+hexlit1 123 0x800000 8388608
+hexlit1 124 0x1000000 16777216
+hexlit1 125 0x2000000 33554432
+hexlit1 126 0x4000000 67108864
+hexlit1 127 0x8000000 134217728
+hexlit1 128 0x10000000 268435456
+hexlit1 129 0x20000000 536870912
+hexlit1 130 0x40000000 1073741824
+hexlit1 131 0x80000000 2147483648
+hexlit1 132 0x100000000 4294967296
+hexlit1 133 0x200000000 8589934592
+hexlit1 134 0x400000000 17179869184
+hexlit1 135 0x800000000 34359738368
+hexlit1 136 0x1000000000 68719476736
+hexlit1 137 0x2000000000 137438953472
+hexlit1 138 0x4000000000 274877906944
+hexlit1 139 0x8000000000 549755813888
+hexlit1 140 0x10000000000 1099511627776
+hexlit1 141 0x20000000000 2199023255552
+hexlit1 142 0x40000000000 4398046511104
+hexlit1 143 0x80000000000 8796093022208
+hexlit1 144 0x100000000000 17592186044416
+hexlit1 145 0x200000000000 35184372088832
+hexlit1 146 0x400000000000 70368744177664
+hexlit1 147 0x800000000000 140737488355328
+hexlit1 148 0x1000000000000 281474976710656
+hexlit1 149 0x2000000000000 562949953421312
+hexlit1 150 0x4000000000000 1125899906842624
+hexlit1 151 0x8000000000000 2251799813685248
+hexlit1 152 0x10000000000000 4503599627370496
+hexlit1 153 0x20000000000000 9007199254740992
+hexlit1 154 0x40000000000000 18014398509481984
+hexlit1 155 0x80000000000000 36028797018963968
+hexlit1 156 0x100000000000000 72057594037927936
+hexlit1 157 0x200000000000000 144115188075855872
+hexlit1 158 0x400000000000000 288230376151711744
+hexlit1 159 0x800000000000000 576460752303423488
+hexlit1 160 0X1000000000000000 1152921504606846976
+hexlit1 161 0x2000000000000000 2305843009213693952
+hexlit1 162 0X4000000000000000 4611686018427387904
+hexlit1 163 0x8000000000000000 -9223372036854775808
+hexlit1 164 0XFFFFFFFFFFFFFFFF -1
+
+for {set n 1} {$n < 0x10} {incr n} {
+ hexlit1 200.$n.1 0X[format %03X $n] $n
+ hexlit1 200.$n.2 0x[format %03X $n] $n
+ hexlit1 200.$n.3 0X[format %03x $n] $n
+ hexlit1 200.$n.4 0x[format %03x $n] $n
+}
+
+# String literals that look like hex do not get cast or coerced.
+#
+do_execsql_test hexlit-300 {
+ CREATE TABLE t1(x INT, y REAL);
+ INSERT INTO t1 VALUES('1234','4567'),('0x1234','0x4567');
+ SELECT typeof(x), x, typeof(y), y, '#' FROM t1 ORDER BY rowid;
+} {integer 1234 real 4567.0 # text 0x1234 text 0x4567 #}
+do_execsql_test hexlit-301 {
+ SELECT CAST('0x1234' AS INTEGER);
+} {0}
+
+# Oversized hex literals are rejected
+#
+do_catchsql_test hexlist-400 {
+ SELECT 0x10000000000000000;
+} {1 {hex literal too big: 0x10000000000000000}}
+
+
+finish_test
diff --git a/test/hook.test b/test/hook.test
index 6346cc7..de6fbdd 100644
--- a/test/hook.test
+++ b/test/hook.test
@@ -127,21 +127,52 @@ db2 close
# depopulation of indices, to make sure the update-hook is not
# invoked incorrectly.
#
+# EVIDENCE-OF: R-21999-45122 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 in a rowid table.
# Simple tests
-do_test hook-4.1.1 {
+do_test hook-4.1.1a {
catchsql {
DROP TABLE t1;
}
+ unset -nocomplain ::update_hook
+ set ::update_hook {}
+ db update_hook [list lappend ::update_hook]
+ #
+ # EVIDENCE-OF: R-52223-27275 The update hook is not invoked when
+ # internal system tables are modified (i.e. sqlite_master and
+ # sqlite_sequence).
+ #
execsql {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ CREATE TABLE t1w(a INT PRIMARY KEY, b) WITHOUT ROWID;
+ }
+ set ::update_hook
+} {}
+do_test hook-4.1.1b {
+ execsql {
INSERT INTO t1 VALUES(1, 'one');
INSERT INTO t1 VALUES(2, 'two');
INSERT INTO t1 VALUES(3, 'three');
+ INSERT INTO t1w SELECT * FROM t1;
}
- db update_hook [list lappend ::update_hook]
} {}
+
+# EVIDENCE-OF: R-15506-57666 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.
+#
+# EVIDENCE-OF: R-29213-61195 The third and fourth arguments to the
+# callback contain pointers to the database and table name containing
+# the affected row.
+#
+# EVIDENCE-OF: R-30809-57812 The final callback parameter is the rowid
+# of the row.
+#
do_test hook-4.1.2 {
+ set ::update_hook {}
execsql {
INSERT INTO t1 VALUES(4, 'four');
DELETE FROM t1 WHERE b = 'two';
@@ -159,6 +190,23 @@ do_test hook-4.1.2 {
DELETE main t1 4 \
]
+# EVIDENCE-OF: R-61808-14344 The sqlite3_update_hook() interface does
+# not fire callbacks for changes to a WITHOUT ROWID table.
+#
+# EVIDENCE-OF: R-33257-44249 The update hook is not invoked when WITHOUT
+# ROWID tables are modified.
+#
+do_test hook-4.1.2w {
+ set ::update_hook {}
+ execsql {
+ INSERT INTO t1w VALUES(4, 'four');
+ DELETE FROM t1w WHERE b = 'two';
+ UPDATE t1w SET b = '' WHERE a = 1 OR a = 3;
+ DELETE FROM t1w WHERE 1; -- Avoid the truncate optimization (for now)
+ }
+ set ::update_hook
+} {}
+
ifcapable trigger {
# Update hook is not invoked for changes to sqlite_master
#
diff --git a/test/in.test b/test/in.test
index 3b23f04..515e598 100644
--- a/test/in.test
+++ b/test/in.test
@@ -332,7 +332,7 @@ do_test in-10.2 {
catchsql {
INSERT INTO t5 VALUES(4);
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t5}}
# Ticket #1821
#
diff --git a/test/in4.test b/test/in4.test
index 470f4f0..a89961f 100644
--- a/test/in4.test
+++ b/test/in4.test
@@ -159,4 +159,181 @@ do_test in4-3.12 {
execsql { SELECT * FROM t3 WHERE x IN (1, 2) AND y IN ()}
} {}
+# Tests for "... IN (?)" and "... NOT IN (?)". In other words, tests
+# for when the RHS of IN is a single expression. This should work the
+# same as the == and <> operators.
+#
+do_execsql_test in4-3.21 {
+ SELECT * FROM t3 WHERE x=10 AND y IN (10);
+} {10 10 10}
+do_execsql_test in4-3.22 {
+ SELECT * FROM t3 WHERE x IN (10) AND y=10;
+} {10 10 10}
+do_execsql_test in4-3.23 {
+ SELECT * FROM t3 WHERE x IN (10) AND y IN (10);
+} {10 10 10}
+do_execsql_test in4-3.24 {
+ SELECT * FROM t3 WHERE x=1 AND y NOT IN (10);
+} {1 1 1}
+do_execsql_test in4-3.25 {
+ SELECT * FROM t3 WHERE x NOT IN (10) AND y=1;
+} {1 1 1}
+do_execsql_test in4-3.26 {
+ SELECT * FROM t3 WHERE x NOT IN (10) AND y NOT IN (10);
+} {1 1 1}
+
+# The query planner recognizes that "x IN (?)" only generates a
+# single match and can use this information to optimize-out ORDER BY
+# clauses.
+#
+do_execsql_test in4-3.31 {
+ DROP INDEX t3i1;
+ CREATE UNIQUE INDEX t3xy ON t3(x,y);
+
+ SELECT *, '|' FROM t3 A, t3 B
+ WHERE A.x=10 AND A.y IN (10)
+ AND B.x=1 AND B.y IN (1);
+} {10 10 10 1 1 1 |}
+do_execsql_test in4-3.32 {
+ EXPLAIN QUERY PLAN
+ SELECT *, '|' FROM t3 A, t3 B
+ WHERE A.x=10 AND A.y IN (10)
+ AND B.x=1 AND B.y IN (1);
+} {~/B-TREE/} ;# No separate sorting pass
+do_execsql_test in4-3.33 {
+ SELECT *, '|' FROM t3 A, t3 B
+ WHERE A.x IN (10) AND A.y=10
+ AND B.x IN (1) AND B.y=1;
+} {10 10 10 1 1 1 |}
+do_execsql_test in4-3.34 {
+ EXPLAIN QUERY PLAN
+ SELECT *, '|' FROM t3 A, t3 B
+ WHERE A.x IN (10) AND A.y=10
+ AND B.x IN (1) AND B.y=1;
+} {~/B-TREE/} ;# No separate sorting pass
+
+# An expression of the form "x IN (?,?)" creates an ephemeral table to
+# hold the list of values on the RHS. But "x IN (?)" does not create
+# an ephemeral table.
+#
+do_execsql_test in4-3.41 {
+ SELECT * FROM t3 WHERE x IN (10,11);
+} {10 10 10}
+do_execsql_test in4-3.42 {
+ EXPLAIN
+ SELECT * FROM t3 WHERE x IN (10,11);
+} {/OpenEphemeral/}
+do_execsql_test in4-3.43 {
+ SELECT * FROM t3 WHERE x IN (10);
+} {10 10 10}
+do_execsql_test in4-3.44 {
+ EXPLAIN
+ SELECT * FROM t3 WHERE x IN (10);
+} {~/OpenEphemeral/}
+do_execsql_test in4-3.45 {
+ SELECT * FROM t3 WHERE x NOT IN (10,11,99999);
+} {1 1 1}
+do_execsql_test in4-3.46 {
+ EXPLAIN
+ SELECT * FROM t3 WHERE x NOT IN (10,11,99999);
+} {/OpenEphemeral/}
+do_execsql_test in4-3.47 {
+ SELECT * FROM t3 WHERE x NOT IN (10);
+} {1 1 1}
+do_execsql_test in4-3.48 {
+ EXPLAIN
+ SELECT * FROM t3 WHERE x NOT IN (10);
+} {~/OpenEphemeral/}
+
+# Make sure that when "x IN (?)" is converted into "x==?" that collating
+# sequence and affinity computations do not get messed up.
+#
+do_execsql_test in4-4.1 {
+ CREATE TABLE t4a(a TEXT, b TEXT COLLATE nocase, c);
+ INSERT INTO t4a VALUES('ABC','abc',1);
+ INSERT INTO t4a VALUES('def','xyz',2);
+ INSERT INTO t4a VALUES('ghi','ghi',3);
+ SELECT c FROM t4a WHERE a=b ORDER BY c;
+} {3}
+do_execsql_test in4-4.2 {
+ SELECT c FROM t4a WHERE b=a ORDER BY c;
+} {1 3}
+do_execsql_test in4-4.3 {
+ SELECT c FROM t4a WHERE (a||'')=b ORDER BY c;
+} {1 3}
+do_execsql_test in4-4.4 {
+ SELECT c FROM t4a WHERE (a||'')=(b||'') ORDER BY c;
+} {3}
+do_execsql_test in4-4.5 {
+ SELECT c FROM t4a WHERE a IN (b) ORDER BY c;
+} {3}
+do_execsql_test in4-4.6 {
+ SELECT c FROM t4a WHERE (a||'') IN (b) ORDER BY c;
+} {3}
+
+
+do_execsql_test in4-4.11 {
+ CREATE TABLE t4b(a TEXT, b NUMERIC, c);
+ INSERT INTO t4b VALUES('1.0',1,4);
+ SELECT c FROM t4b WHERE a=b;
+} {4}
+do_execsql_test in4-4.12 {
+ SELECT c FROM t4b WHERE b=a;
+} {4}
+do_execsql_test in4-4.13 {
+ SELECT c FROM t4b WHERE +a=b;
+} {4}
+do_execsql_test in4-4.14 {
+ SELECT c FROM t4b WHERE a=+b;
+} {}
+do_execsql_test in4-4.15 {
+ SELECT c FROM t4b WHERE +b=a;
+} {}
+do_execsql_test in4-4.16 {
+ SELECT c FROM t4b WHERE b=+a;
+} {4}
+do_execsql_test in4-4.17 {
+ SELECT c FROM t4b WHERE a IN (b);
+} {}
+do_execsql_test in4-4.18 {
+ SELECT c FROM t4b WHERE b IN (a);
+} {4}
+do_execsql_test in4-4.19 {
+ SELECT c FROM t4b WHERE +b IN (a);
+} {}
+
+do_execsql_test in4-5.1 {
+ CREATE TABLE t5(c INTEGER PRIMARY KEY, d TEXT COLLATE nocase);
+ INSERT INTO t5 VALUES(17, 'fuzz');
+ SELECT 1 FROM t5 WHERE 'fuzz' IN (d); -- match
+ SELECT 2 FROM t5 WHERE 'FUZZ' IN (d); -- no match
+ SELECT 3 FROM t5 WHERE d IN ('fuzz'); -- match
+ SELECT 4 FROM t5 WHERE d IN ('FUZZ'); -- match
+} {1 3 4}
+
+# An expression of the form "x IN (y)" can be used as "x=y" by the
+# query planner when computing transitive constraints or to run the
+# query using an index on y.
+#
+do_execsql_test in4-6.1 {
+ CREATE TABLE t6a(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t6a VALUES(1,2),(3,4),(5,6);
+ CREATE TABLE t6b(c INTEGER PRIMARY KEY, d);
+ INSERT INTO t6b VALUES(4,44),(5,55),(6,66);
+
+ SELECT * FROM t6a, t6b WHERE a=3 AND b IN (c);
+} {3 4 4 44}
+do_execsql_test in4-6.1-eqp {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t6a, t6b WHERE a=3 AND b IN (c);
+} {~/SCAN/}
+do_execsql_test in4-6.2 {
+ SELECT * FROM t6a, t6b WHERE a=3 AND c IN (b);
+} {3 4 4 44}
+do_execsql_test in4-6.2-eqp {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t6a, t6b WHERE a=3 AND c IN (b);
+} {~/SCAN/}
+
+
finish_test
diff --git a/test/incrblob2.test b/test/incrblob2.test
index 9046de2..a8f40f0 100644
--- a/test/incrblob2.test
+++ b/test/incrblob2.test
@@ -397,16 +397,16 @@ do_test incrblob2-8.4 {
} {cccccccccccccccccccc}
do_test incrblob2-8.5 {
catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t3.a}}
do_test incrblob2-8.6 {
catchsql {UPDATE t3 SET a = 6 WHERE a > 3}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t3.a}}
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}}
+} {1 {UNIQUE constraint failed: t3.a}}
do_test incrblob2-8.9 {
set rc [catch {sqlite3_blob_read $h 0 20} msg]
list $rc $msg
diff --git a/test/incrblob3.test b/test/incrblob3.test
index 4c49f15..5f2e860 100644
--- a/test/incrblob3.test
+++ b/test/incrblob3.test
@@ -269,4 +269,3 @@ db close
tvfs delete
finish_test
-
diff --git a/test/incrblob4.test b/test/incrblob4.test
index a96356b..a92e373 100644
--- a/test/incrblob4.test
+++ b/test/incrblob4.test
@@ -87,4 +87,3 @@ do_test 3.3 {
} {}
finish_test
-
diff --git a/test/incrblob_err.test b/test/incrblob_err.test
index 35d37fe..a08bea3 100644
--- a/test/incrblob_err.test
+++ b/test/incrblob_err.test
@@ -14,6 +14,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set ::testprefix incrblob_err
ifcapable {!incrblob || !memdebug || !tclvar} {
finish_test
diff --git a/test/incrblobfault.test b/test/incrblobfault.test
index d125471..0c1d93d 100644
--- a/test/incrblobfault.test
+++ b/test/incrblobfault.test
@@ -67,4 +67,3 @@ do_faultsim_test 3 -prep {
}
finish_test
-
diff --git a/test/incrvacuum3.test b/test/incrvacuum3.test
index f01dc10..e901bfe 100644
--- a/test/incrvacuum3.test
+++ b/test/incrvacuum3.test
@@ -151,4 +151,3 @@ foreach {T jrnl_mode} {
}
finish_test
-
diff --git a/test/index.test b/test/index.test
index 790bed9..59c0ea6 100644
--- a/test/index.test
+++ b/test/index.test
@@ -666,7 +666,7 @@ ifcapable conflict {
BEGIN;
INSERT INTO t7 VALUES(1);
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: t7.a}}
do_test index-19.3 {
catchsql {
BEGIN;
@@ -676,7 +676,7 @@ ifcapable conflict {
catchsql {
INSERT INTO t8 VALUES(1);
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: t8.a}}
do_test index-19.5 {
catchsql {
BEGIN;
@@ -715,6 +715,23 @@ do_test index-20.2 {
DROP INDEX "t6i1";
}
} {}
+
+# Try to create a TEMP index on a non-TEMP table. */
+#
+do_test index-21.1 {
+ catchsql {
+ CREATE INDEX temp.i21 ON t6(c);
+ }
+} {1 {cannot create a TEMP index on non-TEMP table "t6"}}
+do_test index-21.2 {
+ catchsql {
+ CREATE TEMP TABLE t6(x);
+ INSERT INTO temp.t6 values(1),(5),(9);
+ CREATE INDEX temp.i21 ON t6(x);
+ SELECT x FROM t6 ORDER BY x DESC;
+ }
+} {0 {9 5 1}}
+
finish_test
diff --git a/test/index3.test b/test/index3.test
index 161ddec..a9f9b7a 100644
--- a/test/index3.test
+++ b/test/index3.test
@@ -34,7 +34,7 @@ do_test index3-1.2 {
BEGIN;
CREATE UNIQUE INDEX i1 ON t1(a);
}
-} {1 {indexed columns are not unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test index3-1.3 {
catchsql COMMIT;
} {0 {}}
diff --git a/test/index4.test b/test/index4.test
index 018ed74..4fbfa52 100644
--- a/test/index4.test
+++ b/test/index4.test
@@ -120,7 +120,7 @@ do_execsql_test 2.1 {
}
do_catchsql_test 2.2 {
CREATE UNIQUE INDEX i3 ON t2(x);
-} {1 {indexed columns are not unique}}
+} {1 {UNIQUE constraint failed: t2.x}}
finish_test
diff --git a/test/index6.test b/test/index6.test
new file mode 100644
index 0000000..68bdd06
--- /dev/null
+++ b/test/index6.test
@@ -0,0 +1,271 @@
+# 2013-07-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.
+#
+#***********************************************************************
+#
+# Test cases for partial indices
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+load_static_extension db wholenumber;
+do_test index6-1.1 {
+ # Able to parse and manage partial indices
+ execsql {
+ CREATE TABLE t1(a,b,c);
+ CREATE INDEX t1a ON t1(a) WHERE a IS NOT NULL;
+ CREATE INDEX t1b ON t1(b) WHERE b>10;
+ CREATE VIRTUAL TABLE nums USING wholenumber;
+ INSERT INTO t1(a,b,c)
+ SELECT CASE WHEN value%3!=0 THEN value END, value, value
+ FROM nums WHERE value<=20;
+ SELECT count(a), count(b) FROM t1;
+ PRAGMA integrity_check;
+ }
+} {14 20 ok}
+
+# Make sure the count(*) optimization works correctly with
+# partial indices. Ticket [a5c8ed66cae16243be6] 2013-10-03.
+#
+do_execsql_test index6-1.1.1 {
+ SELECT count(*) FROM t1;
+} {20}
+
+# Error conditions during parsing...
+#
+do_test index6-1.2 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE x IS NOT NULL;
+ }
+} {1 {no such column: x}}
+do_test index6-1.3 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE EXISTS(SELECT * FROM t1);
+ }
+} {1 {subqueries prohibited in partial index WHERE clauses}}
+do_test index6-1.4 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a!=?1;
+ }
+} {1 {parameters prohibited in partial index WHERE clauses}}
+do_test index6-1.5 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a!=random();
+ }
+} {1 {functions prohibited in partial index WHERE clauses}}
+do_test index6-1.6 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a NOT LIKE 'abc%';
+ }
+} {1 {functions prohibited in partial index WHERE clauses}}
+
+do_test index6-1.10 {
+ execsql {
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 20 t1a {14 1} t1b {10 1} ok}
+
+# STAT1 shows the partial indices have a reduced number of
+# rows.
+#
+do_test index6-1.11 {
+ execsql {
+ UPDATE t1 SET a=b;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 20 t1a {20 1} t1b {10 1} ok}
+
+do_test index6-1.11 {
+ execsql {
+ UPDATE t1 SET a=NULL WHERE b%3!=0;
+ UPDATE t1 SET b=b+100;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 20 t1a {6 1} t1b {20 1} ok}
+
+do_test index6-1.12 {
+ execsql {
+ UPDATE t1 SET a=CASE WHEN b%3!=0 THEN b END;
+ UPDATE t1 SET b=b-100;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 20 t1a {13 1} t1b {10 1} ok}
+
+do_test index6-1.13 {
+ execsql {
+ DELETE FROM t1 WHERE b BETWEEN 8 AND 12;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 15 t1a {10 1} t1b {8 1} ok}
+
+do_test index6-1.14 {
+ execsql {
+ REINDEX;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {{} 15 t1a {10 1} t1b {8 1} ok}
+
+do_test index6-1.15 {
+ execsql {
+ CREATE INDEX t1c ON t1(c);
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1a {10 1} t1b {8 1} t1c {15 1} ok}
+
+# Queries use partial indices as appropriate times.
+#
+do_test index6-2.1 {
+ execsql {
+ CREATE TABLE t2(a,b);
+ INSERT INTO t2(a,b) SELECT value, value FROM nums WHERE value<1000;
+ UPDATE t2 SET a=NULL WHERE b%2==0;
+ CREATE INDEX t2a1 ON t2(a) WHERE a IS NOT NULL;
+ SELECT count(*) FROM t2 WHERE a IS NOT NULL;
+ }
+} {500}
+do_test index6-2.2 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a=5;
+ }
+} {/.* TABLE t2 USING INDEX t2a1 .*/}
+ifcapable stat4||stat3 {
+ execsql ANALYZE
+ do_test index6-2.3stat4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NOT NULL;
+ }
+ } {/.* TABLE t2 USING INDEX t2a1 .*/}
+} else {
+ do_test index6-2.3stat4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NOT NULL AND a>0;
+ }
+ } {/.* TABLE t2 USING INDEX t2a1 .*/}
+}
+do_test index6-2.4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NULL;
+ }
+} {~/.*INDEX t2a1.*/}
+
+do_execsql_test index6-2.101 {
+ DROP INDEX t2a1;
+ UPDATE t2 SET a=b, b=b+10000;
+ SELECT b FROM t2 WHERE a=15;
+} {10015}
+do_execsql_test index6-2.102 {
+ CREATE INDEX t2a2 ON t2(a) WHERE a<100 OR a>200;
+ SELECT b FROM t2 WHERE a=15;
+ PRAGMA integrity_check;
+} {10015 ok}
+do_execsql_test index6-2.102eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=15;
+} {~/.*INDEX t2a2.*/}
+do_execsql_test index6-2.103 {
+ SELECT b FROM t2 WHERE a=15 AND a<100;
+} {10015}
+do_execsql_test index6-2.103eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=15 AND a<100;
+} {/.*INDEX t2a2.*/}
+do_execsql_test index6-2.104 {
+ SELECT b FROM t2 WHERE a=515 AND a>200;
+} {10515}
+do_execsql_test index6-2.104eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=515 AND a>200;
+} {/.*INDEX t2a2.*/}
+
+# Partial UNIQUE indices
+#
+do_execsql_test index6-3.1 {
+ CREATE TABLE t3(a,b);
+ INSERT INTO t3 SELECT value, value FROM nums WHERE value<200;
+ UPDATE t3 SET a=999 WHERE b%5!=0;
+ CREATE UNIQUE INDEX t3a ON t3(a) WHERE a<>999;
+} {}
+do_test index6-3.2 {
+ # unable to insert a duplicate row a-value that is not 999.
+ catchsql {
+ INSERT INTO t3(a,b) VALUES(150, 'test1');
+ }
+} {1 {UNIQUE constraint failed: t3.a}}
+do_test index6-3.3 {
+ # can insert multiple rows with a==999 because such rows are not
+ # part of the unique index.
+ catchsql {
+ INSERT INTO t3(a,b) VALUES(999, 'test1'), (999, 'test2');
+ }
+} {0 {}}
+do_execsql_test index6-3.4 {
+ SELECT count(*) FROM t3 WHERE a=999;
+} {162}
+integrity_check index6-3.5
+
+do_execsql_test index6-4.0 {
+ VACUUM;
+ PRAGMA integrity_check;
+} {ok}
+
+# Silently ignore database name qualifiers in partial indices.
+#
+do_execsql_test index6-5.0 {
+ CREATE INDEX t3b ON t3(b) WHERE xyzzy.t3.b BETWEEN 5 AND 10;
+ /* ^^^^^-- ignored */
+ ANALYZE;
+ SELECT count(*) FROM t3 WHERE t3.b BETWEEN 5 AND 10;
+ SELECT stat+0 FROM sqlite_stat1 WHERE idx='t3b';
+} {6 6}
+
+# Test case for ticket [2ea3e9fe6379fc3f6ce7e090ce483c1a3a80d6c9] from
+# 2014-04-13: Partial index causes assertion fault on UPDATE OR REPLACE.
+#
+do_execsql_test index6-6.0 {
+ CREATE TABLE t6(a,b);
+ CREATE UNIQUE INDEX t6ab ON t1(a,b);
+ CREATE INDEX t6b ON t6(b) WHERE b=1;
+ INSERT INTO t6(a,b) VALUES(123,456);
+ SELECT * FROM t6;
+} {123 456}
+do_execsql_test index6-6.1 {
+ UPDATE OR REPLACE t6 SET b=789;
+ SELECT * FROM t6;
+} {123 789}
+do_execsql_test index6-6.2 {
+ PRAGMA integrity_check;
+} {ok}
+
+
+finish_test
diff --git a/test/index7.test b/test/index7.test
new file mode 100644
index 0000000..1c81f60
--- /dev/null
+++ b/test/index7.test
@@ -0,0 +1,251 @@
+# 2013-11-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.
+#
+#***********************************************************************
+#
+# Test cases for partial indices in WITHOUT ROWID tables
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !vtab {
+ finish_test
+ return
+}
+
+load_static_extension db wholenumber;
+do_test index7-1.1 {
+ # Able to parse and manage partial indices
+ execsql {
+ CREATE TABLE t1(a,b,c PRIMARY KEY) WITHOUT rowid;
+ CREATE INDEX t1a ON t1(a) WHERE a IS NOT NULL;
+ CREATE INDEX t1b ON t1(b) WHERE b>10;
+ CREATE VIRTUAL TABLE nums USING wholenumber;
+ INSERT INTO t1(a,b,c)
+ SELECT CASE WHEN value%3!=0 THEN value END, value, value
+ FROM nums WHERE value<=20;
+ SELECT count(a), count(b) FROM t1;
+ PRAGMA integrity_check;
+ }
+} {14 20 ok}
+
+# Make sure the count(*) optimization works correctly with
+# partial indices. Ticket [a5c8ed66cae16243be6] 2013-10-03.
+#
+do_execsql_test index7-1.1.1 {
+ SELECT count(*) FROM t1;
+} {20}
+
+# Error conditions during parsing...
+#
+do_test index7-1.2 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE x IS NOT NULL;
+ }
+} {1 {no such column: x}}
+do_test index7-1.3 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE EXISTS(SELECT * FROM t1);
+ }
+} {1 {subqueries prohibited in partial index WHERE clauses}}
+do_test index7-1.4 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a!=?1;
+ }
+} {1 {parameters prohibited in partial index WHERE clauses}}
+do_test index7-1.5 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a!=random();
+ }
+} {1 {functions prohibited in partial index WHERE clauses}}
+do_test index7-1.6 {
+ catchsql {
+ CREATE INDEX bad1 ON t1(a,b) WHERE a NOT LIKE 'abc%';
+ }
+} {1 {functions prohibited in partial index WHERE clauses}}
+
+do_test index7-1.10 {
+ execsql {
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {20 1} t1a {14 1} t1b {10 1} ok}
+
+# STAT1 shows the partial indices have a reduced number of
+# rows.
+#
+do_test index7-1.11 {
+ execsql {
+ UPDATE t1 SET a=b;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {20 1} t1a {20 1} t1b {10 1} ok}
+
+do_test index7-1.11b {
+ execsql {
+ UPDATE t1 SET a=NULL WHERE b%3!=0;
+ UPDATE t1 SET b=b+100;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {20 1} t1a {6 1} t1b {20 1} ok}
+
+do_test index7-1.12 {
+ execsql {
+ UPDATE t1 SET a=CASE WHEN b%3!=0 THEN b END;
+ UPDATE t1 SET b=b-100;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {20 1} t1a {13 1} t1b {10 1} ok}
+
+do_test index7-1.13 {
+ execsql {
+ DELETE FROM t1 WHERE b BETWEEN 8 AND 12;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {15 1} t1a {10 1} t1b {8 1} ok}
+
+do_test index7-1.14 {
+ execsql {
+ REINDEX;
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {15 1} t1a {10 1} t1b {8 1} ok}
+
+do_test index7-1.15 {
+ execsql {
+ CREATE INDEX t1c ON t1(c);
+ ANALYZE;
+ SELECT idx, stat FROM sqlite_stat1 ORDER BY idx;
+ PRAGMA integrity_check;
+ }
+} {t1 {15 1} t1a {10 1} t1b {8 1} t1c {15 1} ok}
+
+# Queries use partial indices as appropriate times.
+#
+do_test index7-2.1 {
+ execsql {
+ CREATE TABLE t2(a,b PRIMARY KEY) without rowid;
+ INSERT INTO t2(a,b) SELECT value, value FROM nums WHERE value<1000;
+ UPDATE t2 SET a=NULL WHERE b%5==0;
+ CREATE INDEX t2a1 ON t2(a) WHERE a IS NOT NULL;
+ SELECT count(*) FROM t2 WHERE a IS NOT NULL;
+ }
+} {800}
+do_test index7-2.2 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a=5;
+ }
+} {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
+ifcapable stat4||stat3 {
+ do_test index7-2.3stat4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NOT NULL;
+ }
+ } {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
+} else {
+ do_test index7-2.3stat4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NOT NULL AND a>0;
+ }
+ } {/.* TABLE t2 USING COVERING INDEX t2a1 .*/}
+}
+do_test index7-2.4 {
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a IS NULL;
+ }
+} {~/.*INDEX t2a1.*/}
+
+do_execsql_test index7-2.101 {
+ DROP INDEX t2a1;
+ UPDATE t2 SET a=b, b=b+10000;
+ SELECT b FROM t2 WHERE a=15;
+} {10015}
+do_execsql_test index7-2.102 {
+ CREATE INDEX t2a2 ON t2(a) WHERE a<100 OR a>200;
+ SELECT b FROM t2 WHERE a=15;
+ PRAGMA integrity_check;
+} {10015 ok}
+do_execsql_test index7-2.102eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=15;
+} {~/.*INDEX t2a2.*/}
+do_execsql_test index7-2.103 {
+ SELECT b FROM t2 WHERE a=15 AND a<100;
+} {10015}
+do_execsql_test index7-2.103eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=15 AND a<100;
+} {/.*INDEX t2a2.*/}
+do_execsql_test index7-2.104 {
+ SELECT b FROM t2 WHERE a=515 AND a>200;
+} {10515}
+do_execsql_test index7-2.104eqp {
+ EXPLAIN QUERY PLAN
+ SELECT b FROM t2 WHERE a=515 AND a>200;
+} {/.*INDEX t2a2.*/}
+
+# Partial UNIQUE indices
+#
+do_execsql_test index7-3.1 {
+ CREATE TABLE t3(a,b PRIMARY KEY) without rowid;
+ INSERT INTO t3 SELECT value, value FROM nums WHERE value<200;
+ UPDATE t3 SET a=999 WHERE b%5!=0;
+ CREATE UNIQUE INDEX t3a ON t3(a) WHERE a<>999;
+} {}
+do_test index7-3.2 {
+ # unable to insert a duplicate row a-value that is not 999.
+ catchsql {
+ INSERT INTO t3(a,b) VALUES(150, 'test1');
+ }
+} {1 {UNIQUE constraint failed: t3.a}}
+do_test index7-3.3 {
+ # can insert multiple rows with a==999 because such rows are not
+ # part of the unique index.
+ catchsql {
+ INSERT INTO t3(a,b) VALUES(999, 'test1'), (999, 'test2');
+ }
+} {0 {}}
+do_execsql_test index7-3.4 {
+ SELECT count(*) FROM t3 WHERE a=999;
+} {162}
+integrity_check index7-3.5
+
+do_execsql_test index7-4.0 {
+ VACUUM;
+ PRAGMA integrity_check;
+} {ok}
+
+# Silently ignore database name qualifiers in partial indices.
+#
+do_execsql_test index7-5.0 {
+ CREATE INDEX t3b ON t3(b) WHERE xyzzy.t3.b BETWEEN 5 AND 10;
+ /* ^^^^^-- ignored */
+ ANALYZE;
+ SELECT count(*) FROM t3 WHERE t3.b BETWEEN 5 AND 10;
+ SELECT stat+0 FROM sqlite_stat1 WHERE idx='t3b';
+} {6 6}
+
+finish_test
diff --git a/test/indexedby.test b/test/indexedby.test
index 7ccc4de..f95c167 100644
--- a/test/indexedby.test
+++ b/test/indexedby.test
@@ -13,6 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set ::testprefix indexedby
# Create a schema with some indexes.
#
@@ -42,15 +43,15 @@ proc EQP {sql} {
#
do_execsql_test indexedby-1.2 {
EXPLAIN QUERY PLAN select * from t1 WHERE a = 10;
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
do_execsql_test indexedby-1.3 {
EXPLAIN QUERY PLAN select * from t1 ;
-} {0 0 0 {SCAN TABLE t1 (~1000000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_execsql_test indexedby-1.4 {
EXPLAIN QUERY PLAN select * from t1, t2 WHERE c = 10;
} {
- 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)}
- 0 1 0 {SCAN TABLE t1 (~1000000 rows)}
+ 0 0 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)}
+ 0 1 0 {SCAN TABLE t1}
}
# Parser tests. Test that an INDEXED BY or NOT INDEX clause can be
@@ -85,21 +86,21 @@ do_test indexedby-2.7 {
#
do_execsql_test indexedby-3.1 {
EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE a = 'one' AND b = 'two'
-} {0 0 0 {SCAN TABLE t1 (~10000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_execsql_test indexedby-3.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' AND b = 'two'
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
do_execsql_test indexedby-3.3 {
EXPLAIN QUERY PLAN
SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' AND b = 'two'
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}}
do_test indexedby-3.4 {
catchsql { SELECT * FROM t1 INDEXED BY i2 WHERE a = 'one' }
-} {1 {cannot use index: i2}}
+} {1 {no query solution}}
do_test indexedby-3.5 {
catchsql { SELECT * FROM t1 INDEXED BY i2 ORDER BY a }
-} {1 {cannot use index: i2}}
+} {1 {no query solution}}
do_test indexedby-3.6 {
catchsql { SELECT * FROM t1 INDEXED BY i1 WHERE a = 'one' }
} {0 {}}
@@ -110,14 +111,14 @@ do_test indexedby-3.7 {
do_execsql_test indexedby-3.8 {
EXPLAIN QUERY PLAN
SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 ORDER BY e
-} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1 (~1000000 rows)}}
+} {0 0 0 {SCAN TABLE t3 USING INDEX sqlite_autoindex_t3_1}}
do_execsql_test indexedby-3.9 {
EXPLAIN QUERY PLAN
SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE e = 10
-} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?) (~1 rows)}}
+} {0 0 0 {SEARCH TABLE t3 USING INDEX sqlite_autoindex_t3_1 (e=?)}}
do_test indexedby-3.10 {
catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_1 WHERE f = 10 }
-} {1 {cannot use index: sqlite_autoindex_t3_1}}
+} {1 {no query solution}}
do_test indexedby-3.11 {
catchsql { SELECT * FROM t3 INDEXED BY sqlite_autoindex_t3_2 WHERE f = 10 }
} {1 {no such index: sqlite_autoindex_t3_2}}
@@ -127,25 +128,25 @@ do_test indexedby-3.11 {
do_execsql_test indexedby-4.1 {
EXPLAIN QUERY PLAN SELECT * FROM t1, t2 WHERE a = c
} {
- 0 0 0 {SCAN TABLE t1 (~1000000 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?) (~10 rows)}
+ 0 0 0 {SCAN TABLE t1}
+ 0 1 1 {SEARCH TABLE t2 USING INDEX i3 (c=?)}
}
do_execsql_test indexedby-4.2 {
EXPLAIN QUERY PLAN SELECT * FROM t1 INDEXED BY i1, t2 WHERE a = c
} {
- 0 0 1 {SCAN TABLE t2 (~1000000 rows)}
- 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~10 rows)}
+ 0 0 1 {SCAN TABLE t2}
+ 0 1 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
}
do_test indexedby-4.3 {
catchsql {
SELECT * FROM t1 INDEXED BY i1, t2 INDEXED BY i3 WHERE a=c
}
-} {1 {cannot use index: i1}}
+} {1 {no query solution}}
do_test indexedby-4.4 {
catchsql {
SELECT * FROM t2 INDEXED BY i3, t1 INDEXED BY i1 WHERE a=c
}
-} {1 {cannot use index: i3}}
+} {1 {no query solution}}
# Test embedding an INDEXED BY in a CREATE VIEW statement. This block
# also tests that nothing bad happens if an index refered to by
@@ -154,10 +155,10 @@ do_test indexedby-4.4 {
do_execsql_test indexedby-5.1 {
CREATE VIEW v2 AS SELECT * FROM t1 INDEXED BY i1 WHERE a > 5;
EXPLAIN QUERY PLAN SELECT * FROM v2
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~250000 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
do_execsql_test indexedby-5.2 {
EXPLAIN QUERY PLAN SELECT * FROM v2 WHERE b = 10
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~25000 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
do_test indexedby-5.3 {
execsql { DROP INDEX i1 }
catchsql { SELECT * FROM v2 }
@@ -166,7 +167,7 @@ do_test indexedby-5.4 {
# Recreate index i1 in such a way as it cannot be used by the view query.
execsql { CREATE INDEX i1 ON t1(b) }
catchsql { SELECT * FROM v2 }
-} {1 {cannot use index: i1}}
+} {1 {no query solution}}
do_test indexedby-5.5 {
# Drop and recreate index i1 again. This time, create it so that it can
# be used by the query.
@@ -178,54 +179,54 @@ do_test indexedby-5.5 {
#
do_execsql_test indexedby-6.1 {
EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE b = 10 ORDER BY rowid
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}}
do_execsql_test indexedby-6.2 {
EXPLAIN QUERY PLAN SELECT * FROM t1 NOT INDEXED WHERE b = 10 ORDER BY rowid
-} {0 0 0 {SCAN TABLE t1 USING INTEGER PRIMARY KEY (~100000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
# Test that "INDEXED BY" can be used in a DELETE statement.
#
do_execsql_test indexedby-7.1 {
EXPLAIN QUERY PLAN DELETE FROM t1 WHERE a = 5
-} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
do_execsql_test indexedby-7.2 {
EXPLAIN QUERY PLAN DELETE FROM t1 NOT INDEXED WHERE a = 5
-} {0 0 0 {SCAN TABLE t1 (~100000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_execsql_test indexedby-7.3 {
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5
-} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
do_execsql_test indexedby-7.4 {
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i1 WHERE a = 5 AND b = 10
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
do_execsql_test indexedby-7.5 {
EXPLAIN QUERY PLAN DELETE FROM t1 INDEXED BY i2 WHERE a = 5 AND b = 10
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}}
do_test indexedby-7.6 {
catchsql { DELETE FROM t1 INDEXED BY i2 WHERE a = 5}
-} {1 {cannot use index: i2}}
+} {1 {no query solution}}
# Test that "INDEXED BY" can be used in an UPDATE statement.
#
do_execsql_test indexedby-8.1 {
EXPLAIN QUERY PLAN UPDATE t1 SET rowid=rowid+1 WHERE a = 5
-} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
do_execsql_test indexedby-8.2 {
EXPLAIN QUERY PLAN UPDATE t1 NOT INDEXED SET rowid=rowid+1 WHERE a = 5
-} {0 0 0 {SCAN TABLE t1 (~100000 rows)}}
+} {0 0 0 {SCAN TABLE t1}}
do_execsql_test indexedby-8.3 {
EXPLAIN QUERY PLAN UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5
-} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (a=?)}}
do_execsql_test indexedby-8.4 {
EXPLAIN QUERY PLAN
UPDATE t1 INDEXED BY i1 SET rowid=rowid+1 WHERE a = 5 AND b = 10
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
do_execsql_test indexedby-8.5 {
EXPLAIN QUERY PLAN
UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5 AND b = 10
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?) (~2 rows)}}
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i2 (b=?)}}
do_test indexedby-8.6 {
catchsql { UPDATE t1 INDEXED BY i2 SET rowid=rowid+1 WHERE a = 5}
-} {1 {cannot use index: i2}}
+} {1 {no query solution}}
# Test that bug #3560 is fixed.
#
@@ -243,10 +244,10 @@ do_test indexedby-9.2 {
joinme as j indexed by joinme_id_text_idx
on ( m.id = j.id_int)
}
-} {1 {cannot use index: joinme_id_text_idx}}
+} {1 {no query solution}}
do_test indexedby-9.3 {
catchsql { select * from maintable, joinme INDEXED by joinme_id_text_idx }
-} {1 {cannot use index: joinme_id_text_idx}}
+} {1 {no query solution}}
# Make sure we can still create tables, indices, and columns whose name
# is "indexed".
@@ -274,4 +275,50 @@ do_test indexedby-10.3 {
}
} {1}
+#-------------------------------------------------------------------------
+# Ensure that the rowid at the end of each index entry may be used
+# for equality constraints in the same way as other indexed fields.
+#
+do_execsql_test 11.1 {
+ CREATE TABLE x1(a, b TEXT);
+ CREATE INDEX x1i ON x1(a, b);
+ INSERT INTO x1 VALUES(1, 1);
+ INSERT INTO x1 VALUES(1, 1);
+ INSERT INTO x1 VALUES(1, 1);
+ INSERT INTO x1 VALUES(1, 1);
+}
+do_execsql_test 11.2 {
+ SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid=3;
+} {1 1 3}
+do_execsql_test 11.3 {
+ SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3';
+} {1 1 3}
+do_execsql_test 11.4 {
+ SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0';
+} {1 1 3}
+do_eqp_test 11.5 {
+ SELECT a,b,rowid FROM x1 INDEXED BY x1i WHERE a=1 AND b=1 AND rowid='3.0';
+} {0 0 0 {SEARCH TABLE x1 USING COVERING INDEX x1i (a=? AND b=? AND rowid=?)}}
+
+do_execsql_test 11.6 {
+ CREATE TABLE x2(c INTEGER PRIMARY KEY, a, b TEXT);
+ CREATE INDEX x2i ON x2(a, b);
+ INSERT INTO x2 VALUES(1, 1, 1);
+ INSERT INTO x2 VALUES(2, 1, 1);
+ INSERT INTO x2 VALUES(3, 1, 1);
+ INSERT INTO x2 VALUES(4, 1, 1);
+}
+do_execsql_test 11.7 {
+ SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c=3;
+} {1 1 3}
+do_execsql_test 11.8 {
+ SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3';
+} {1 1 3}
+do_execsql_test 11.9 {
+ SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0';
+} {1 1 3}
+do_eqp_test 11.10 {
+ SELECT a,b,c FROM x2 INDEXED BY x2i WHERE a=1 AND b=1 AND c='3.0';
+} {0 0 0 {SEARCH TABLE x2 USING COVERING INDEX x2i (a=? AND b=? AND rowid=?)}}
+
finish_test
diff --git a/test/insert.test b/test/insert.test
index e00b9a8..cb675b9 100644
--- a/test/insert.test
+++ b/test/insert.test
@@ -398,11 +398,44 @@ ifcapable compound {
} {1 2 3 4 5 6 7 8 9}
do_test insert-10.2 {
catchsql {
- INSERT INTO t10 VALUES(11,12,13), (14,15);
+ INSERT INTO t10 VALUES(11,12,13), (14,15), (16,17,28);
}
} {1 {all VALUES must have the same number of terms}}
}
+# Need for the OP_SoftNull opcode
+#
+do_execsql_test insert-11.1 {
+ CREATE TABLE t11a AS SELECT '123456789' AS x;
+ CREATE TABLE t11b (a INTEGER PRIMARY KEY, b, c);
+ INSERT INTO t11b SELECT x, x, x FROM t11a;
+ SELECT quote(a), quote(b), quote(c) FROM t11b;
+} {123456789 '123456789' '123456789'}
+
+
+# More columns of input than there are columns in the table.
+# Ticket http://www.sqlite.org/src/info/e9654505cfda9361
+#
+do_execsql_test insert-12.1 {
+ CREATE TABLE t12a(a,b,c,d,e,f,g);
+ INSERT INTO t12a VALUES(101,102,103,104,105,106,107);
+ CREATE TABLE t12b(x);
+ INSERT INTO t12b(x,rowid,x,x,x,x,x) SELECT * FROM t12a;
+ SELECT rowid, x FROM t12b;
+} {102 101}
+do_execsql_test insert-12.2 {
+ CREATE TABLE tab1( value INTEGER);
+ INSERT INTO tab1 (value, _rowid_) values( 11, 1);
+ INSERT INTO tab1 (value, _rowid_) SELECT 22,999;
+ SELECT * FROM tab1;
+} {11 22}
+do_execsql_test insert-12.3 {
+ CREATE TABLE t12c(a, b DEFAULT 'xyzzy', c);
+ INSERT INTO t12c(a, rowid, c) SELECT 'one', 999, 'two';
+ SELECT * FROM t12c;
+} {one xyzzy two}
+
+
integrity_check insert-99.0
finish_test
diff --git a/test/insert4.test b/test/insert4.test
index f4a45c1..889d5e7 100644
--- a/test/insert4.test
+++ b/test/insert4.test
@@ -54,7 +54,7 @@ do_test insert4-1.1 {
catchsql {
INSERT INTO t1 SELECT * FROM t2;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
xferopt_test insert4-1.2 0
do_test insert4-1.3 {
execsql {
@@ -101,7 +101,7 @@ do_test insert4-2.3.3 {
INSERT INTO t1 SELECT * FROM t2 LIMIT 1;
SELECT * FROM t1;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
xferopt_test insert4-2.3.4 0
# Do not run the transfer optimization if there is a DISTINCT
@@ -119,7 +119,7 @@ do_test insert4-2.4.3 {
DELETE FROM t1;
INSERT INTO t1 SELECT DISTINCT * FROM t2;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t1}}
xferopt_test insert4-2.4.4 0
# The following procedure constructs two tables then tries to transfer
@@ -253,7 +253,7 @@ ifcapable vacuum {
#
do_test insert4-5.1 {
# Table does not exist.
- catchsql { INSERT INTO t2 SELECT * FROM nosuchtable }
+ catchsql { INSERT INTO t2 SELECT a, b FROM nosuchtable }
} {1 {no such table: nosuchtable}}
do_test insert4-5.2 {
# Number of columns does not match.
@@ -315,7 +315,7 @@ do_test insert4-6.6 {
catchsql {
INSERT INTO t6b SELECT * FROM t6a;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t6b}}
do_test insert4-6.7 {
execsql {
DROP TABLE t6b;
@@ -324,7 +324,7 @@ do_test insert4-6.7 {
catchsql {
INSERT INTO t6b SELECT * FROM t6a;
}
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: t6b}}
# Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
# Disable the xfer optimization if the destination table contains
@@ -353,7 +353,7 @@ ifcapable foreignkey {
catchsql {
INSERT INTO t7b SELECT * FROM t7c;
}
- } {1 {foreign key constraint failed}}
+ } {1 {FOREIGN KEY constraint failed}}
do_test insert4-7.4 {
execsql {SELECT * FROM t7b}
} {}
@@ -452,7 +452,7 @@ do_test insert4-8.5 {
catchsql {
INSERT INTO t1 SELECT * FROM t2;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test insert4-8.6 {
execsql {
SELECT * FROM t1;
@@ -472,7 +472,7 @@ do_test insert4-8.7 {
catchsql {
INSERT INTO t1 SELECT * FROM t2;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test insert4-8.8 {
execsql {
SELECT * FROM t1;
@@ -494,7 +494,7 @@ do_test insert4-8.9 {
INSERT INTO t1 VALUES(2,3);
INSERT INTO t1 SELECT * FROM t2;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test insert4-8.10 {
catchsql {COMMIT}
} {1 {cannot commit - no transaction is active}}
diff --git a/test/instr.test b/test/instr.test
index b328cd1..c8be486 100644
--- a/test/instr.test
+++ b/test/instr.test
@@ -11,6 +11,11 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the built-in INSTR() functions.
#
+# EVIDENCE-OF: R-27549-59611 The instr(X,Y) function finds the first
+# occurrence of string Y within string X and returns the number of prior
+# characters plus 1, or 0 if Y is nowhere found within X.
+#
+
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -199,12 +204,48 @@ do_test instr-1.54 {
do_test instr-1.55 {
db eval {SELECT instr(x'78c3a4e282ac79','y');}
} {4}
-do_test instr-1.56 {
+
+# EVIDENCE-OF: R-46421-32541 Or, if X and Y are both BLOBs, then
+# instr(X,Y) returns one more than the number bytes prior to the first
+# occurrence of Y, or 0 if Y does not occur anywhere within X.
+#
+do_test instr-1.56.1 {
db eval {SELECT instr(x'78c3a4e282ac79',x'79');}
} {7}
-do_test instr-1.57 {
+do_test instr-1.56.2 {
+ db eval {SELECT instr(x'78c3a4e282ac79',x'7a');}
+} {0}
+do_test instr-1.56.3 {
+ db eval {SELECT instr(x'78c3a4e282ac79',x'78');}
+} {1}
+do_test instr-1.56.3 {
+ db eval {SELECT instr(x'78c3a4e282ac79',x'a4');}
+} {3}
+
+# EVIDENCE-OF: R-17329-35644 If both arguments X and Y to instr(X,Y) are
+# non-NULL and are not BLOBs then both are interpreted as strings.
+#
+do_test instr-1.57.1 {
db eval {SELECT instr('xä€y',x'79');}
} {4}
+do_test instr-1.57.2 {
+ db eval {SELECT instr('xä€y',x'a4');}
+} {0}
+do_test instr-1.57.3 {
+ db eval {SELECT instr(x'78c3a4e282ac79','y');}
+} {4}
+# EVIDENCE-OF: R-14708-27487 If either X or Y are NULL in instr(X,Y)
+# then the result is NULL.
+#
+do_execsql_test instr-1.60 {
+ SELECT coalesce(instr(NULL,'abc'), 999);
+} {999}
+do_execsql_test instr-1.61 {
+ SELECT coalesce(instr('abc',NULL), 999);
+} {999}
+do_execsql_test instr-1.62 {
+ SELECT coalesce(instr(NULL,NULL), 999);
+} {999}
finish_test
diff --git a/test/intpkey.test b/test/intpkey.test
index db39421..41858e5 100644
--- a/test/intpkey.test
+++ b/test/intpkey.test
@@ -76,7 +76,7 @@ do_test intpkey-1.6 {
INSERT INTO t1 VALUES(5,'second','entry');
}} msg]
lappend r $msg
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test intpkey-1.7 {
execsql {
SELECT rowid, * FROM t1;
@@ -125,8 +125,11 @@ do_test intpkey-1.12.1 {
}
} {4 one two}
do_test intpkey-1.12.2 {
- set sqlite_query_plan
-} {t1 *}
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a==4;
+ }
+} {/SEARCH TABLE t1 /}
# Try to insert a non-integer value into the primary key field. This
# should result in a data type mismatch.
diff --git a/test/io.test b/test/io.test
index 11f9cc8..c5086c1 100644
--- a/test/io.test
+++ b/test/io.test
@@ -641,4 +641,3 @@ foreach {tn sql} {
sqlite3_simulate_device -char {} -sectorsize 0
finish_test
-
diff --git a/test/ioerr.test b/test/ioerr.test
index b237439..e59647f 100644
--- a/test/ioerr.test
+++ b/test/ioerr.test
@@ -258,7 +258,7 @@ do_ioerr_test ioerr-10 -ckrefcount true -tclprep {
INSERT INTO t1 SELECT (a+500)%900, 'good string' FROM t1;
}} msg
- if {$msg != "column a is not unique"} {
+ if {$msg != "UNIQUE constraint failed: t1.a"} {
error $msg
}
}
diff --git a/test/ioerr6.test b/test/ioerr6.test
index 66c48ad..d1847b2 100644
--- a/test/ioerr6.test
+++ b/test/ioerr6.test
@@ -89,4 +89,3 @@ do_faultsim_test 3 -faults full* -prep {
}
finish_test
-
diff --git a/test/join.test b/test/join.test
index 88ac04f..4c83fa6 100644
--- a/test/join.test
+++ b/test/join.test
@@ -36,6 +36,17 @@ do_test join-1.2 {
}
} {1 2 3 2 3 4 3 4 5}
+# A FROM clause of the form: "<table>, <table> ON <expr>" is not
+# allowed by the SQLite syntax diagram, nor by any other SQL database
+# engine that we are aware of. Nevertheless, historic versions of
+# SQLite have allowed it. We need to continue to support it moving
+# forward to prevent breakage of legacy applications. Though, we will
+# not advertise it as being supported.
+#
+do_execsql_test join-1.2.1 {
+ SELECT t1.rowid, t2.rowid, '|' FROM t1, t2 ON t1.a=t2.b;
+} {1 1 | 2 2 | 3 3 |}
+
do_test join-1.3 {
execsql2 {
SELECT * FROM t1 NATURAL JOIN t2;
@@ -641,4 +652,39 @@ do_test join-11.10 {
execsql { SELECT * FROM t2 NATURAL JOIN t1 }
} {1 one 2 two}
+#-------------------------------------------------------------------------
+# Test that at most 64 tables are allowed in a join.
+#
+do_execsql_test join-12.1 {
+ CREATE TABLE t14(x);
+ INSERT INTO t14 VALUES('abcdefghij');
+}
+
+proc jointest {tn nTbl res} {
+ set sql "SELECT 1 FROM [string repeat t14, [expr $nTbl-1]] t14;"
+ uplevel [list do_catchsql_test $tn $sql $res]
+}
+
+jointest join-12.2 30 {0 1}
+jointest join-12.3 63 {0 1}
+jointest join-12.4 64 {0 1}
+jointest join-12.5 65 {1 {at most 64 tables in a join}}
+jointest join-12.6 66 {1 {at most 64 tables in a join}}
+jointest join-12.7 127 {1 {at most 64 tables in a join}}
+jointest join-12.8 128 {1 {at most 64 tables in a join}}
+jointest join-12.9 1000 {1 {at most 64 tables in a join}}
+
+# If SQLite is built with SQLITE_MEMDEBUG, then the huge number of realloc()
+# calls made by the following test cases are too time consuming to run.
+# Without SQLITE_MEMDEBUG, realloc() is fast enough that these are not
+# a problem.
+ifcapable pragma&&compileoption_diags {
+ if {[lsearch [db eval {PRAGMA compile_options}] MEMDEBUG]<0} {
+ jointest join-12.10 65534 {1 {at most 64 tables in a join}}
+ jointest join-12.11 65535 {1 {too many references to "t14": max 65535}}
+ jointest join-12.12 65536 {1 {too many references to "t14": max 65535}}
+ jointest join-12.13 65537 {1 {too many references to "t14": max 65535}}
+ }
+}
+
finish_test
diff --git a/test/keyword1.test b/test/keyword1.test
index 9483120..2bc1ff5 100644
--- a/test/keyword1.test
+++ b/test/keyword1.test
@@ -65,6 +65,7 @@ set kwlist {
pragma
query
raise
+ recursive
regexp
reindex
release
@@ -80,6 +81,8 @@ set kwlist {
vacuum
view
virtual
+ with
+ without
};
set exprkw {
cast
diff --git a/test/lastinsert.test b/test/lastinsert.test
index adeb798..c5bc267 100644
--- a/test/lastinsert.test
+++ b/test/lastinsert.test
@@ -36,6 +36,17 @@ do_test lastinsert-1.1 {
}
} {0 3}
+# EVIDENCE-OF: R-47220-63683 The sqlite3_last_insert_rowid() function
+# does not work for WITHOUT ROWID tables.
+#
+do_test lastinsert-1.1w {
+ catchsql {
+ create table t1w (k integer primary key) WITHOUT ROWID;
+ insert into t1w values (123456);
+ select last_insert_rowid(); -- returns 3 from above.
+ }
+} {0 3}
+
# LIRID unchanged after an update on a table
do_test lastinsert-1.2 {
catchsql {
diff --git a/test/like.test b/test/like.test
index 80ba418..923272c 100644
--- a/test/like.test
+++ b/test/like.test
@@ -156,14 +156,27 @@ ifcapable !like_opt {
# 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.
+# it appends the names of the table and index used.
#
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]
+ set eqp [execsql "EXPLAIN QUERY PLAN $sql"]
+ # puts eqp=$eqp
+ foreach {a b c x} $eqp {
+ if {[regexp { TABLE (\w+ AS )?(\w+) USING COVERING INDEX (\w+)\y} \
+ $x all as tab idx]} {
+ lappend data {} $idx
+ } elseif {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \
+ $x all as tab idx]} {
+ lappend data $tab $idx
+ } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} {
+ lappend data $tab *
+ }
+ }
+ return $data
}
# Perform tests on the like optimization.
@@ -176,7 +189,7 @@ do_test like-3.1 {
queryplan {
SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
}
-} {ABC {ABC abc xyz} abc abcd sort t1 {}}
+} {ABC {ABC abc xyz} abc abcd sort t1 *}
do_test like-3.2 {
set sqlite_like_count
} {12}
@@ -269,8 +282,8 @@ do_test like-3.12 {
#
do_test like-3.13 {
set sqlite_like_count 0
+ db eval {PRAGMA case_sensitive_like=off;}
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}
@@ -282,12 +295,14 @@ do_test like-3.14 {
#
do_test like-3.15 {
set sqlite_like_count 0
- queryplan {
+ db eval {
PRAGMA case_sensitive_like=on;
DROP INDEX i1;
+ }
+ queryplan {
SELECT x FROM t1 WHERE x LIKE 'abc%' ORDER BY 1;
}
-} {abc abcd sort t1 {}}
+} {abc abcd sort t1 *}
do_test like-3.16 {
set sqlite_like_count
} 12
@@ -299,7 +314,7 @@ do_test like-3.17 {
queryplan {
SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1;
}
-} {abc abcd sort t1 {}}
+} {abc abcd sort t1 *}
do_test like-3.18 {
set sqlite_like_count
} 12
@@ -318,8 +333,8 @@ do_test like-3.20 {
} 0
do_test like-3.21 {
set sqlite_like_count 0
+ db eval {PRAGMA case_sensitive_like=on;}
queryplan {
- PRAGMA case_sensitive_like=on;
SELECT x FROM t1 WHERE x GLOB 'abc*' ORDER BY 1;
}
} {abc abcd nosort {} i1}
@@ -328,8 +343,8 @@ do_test like-3.22 {
} 0
do_test like-3.23 {
set sqlite_like_count 0
+ db eval {PRAGMA case_sensitive_like=off;}
queryplan {
- PRAGMA case_sensitive_like=off;
SELECT x FROM t1 WHERE x GLOB 'a[bc]d' ORDER BY 1;
}
} {abd acd nosort {} i1}
@@ -809,60 +824,66 @@ do_test like-11.0 {
}
} {12}
do_test like-11.1 {
+ db eval {PRAGMA case_sensitive_like=OFF;}
queryplan {
- PRAGMA case_sensitive_like=OFF;
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a;
}
} {abc abcd ABC ABCD nosort t11 *}
do_test like-11.2 {
+ db eval {PRAGMA case_sensitive_like=ON;}
queryplan {
- PRAGMA case_sensitive_like=ON;
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a;
}
} {abc abcd nosort t11 *}
do_test like-11.3 {
- queryplan {
+ db eval {
PRAGMA case_sensitive_like=OFF;
CREATE INDEX t11b ON t11(b);
+ }
+ queryplan {
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a;
}
} {abc abcd ABC ABCD sort {} t11b}
do_test like-11.4 {
+ db eval {PRAGMA case_sensitive_like=ON;}
queryplan {
- PRAGMA case_sensitive_like=ON;
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY a;
}
} {abc abcd nosort t11 *}
do_test like-11.5 {
- queryplan {
+ db eval {
PRAGMA case_sensitive_like=OFF;
DROP INDEX t11b;
CREATE INDEX t11bnc ON t11(b COLLATE nocase);
+ }
+ queryplan {
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a;
}
} {abc abcd ABC ABCD sort {} t11bnc}
do_test like-11.6 {
+ db eval {CREATE INDEX t11bb ON t11(b COLLATE binary);}
queryplan {
- CREATE INDEX t11bb ON t11(b COLLATE binary);
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a;
}
} {abc abcd ABC ABCD sort {} t11bnc}
do_test like-11.7 {
+ db eval {PRAGMA case_sensitive_like=ON;}
queryplan {
- PRAGMA case_sensitive_like=ON;
SELECT b FROM t11 WHERE b LIKE 'abc%' ORDER BY +a;
}
} {abc abcd sort {} t11bb}
do_test like-11.8 {
+ db eval {PRAGMA case_sensitive_like=OFF;}
queryplan {
- PRAGMA case_sensitive_like=OFF;
SELECT b FROM t11 WHERE b GLOB 'abc*' ORDER BY +a;
}
} {abc abcd sort {} t11bb}
do_test like-11.9 {
- queryplan {
+ db eval {
CREATE INDEX t11cnc ON t11(c COLLATE nocase);
CREATE INDEX t11cb ON t11(c COLLATE binary);
+ }
+ queryplan {
SELECT c FROM t11 WHERE c LIKE 'abc%' ORDER BY +a;
}
} {abc abcd ABC ABCD sort {} t11cnc}
@@ -872,5 +893,60 @@ do_test like-11.10 {
}
} {abc abcd sort {} t11cb}
+# A COLLATE clause on the pattern does not change the result of a
+# LIKE operator.
+#
+do_execsql_test like-12.1 {
+ CREATE TABLE t12nc(id INTEGER, x TEXT UNIQUE COLLATE nocase);
+ INSERT INTO t12nc VALUES(1,'abcde'),(2,'uvwxy'),(3,'ABCDEF');
+ CREATE TABLE t12b(id INTEGER, x TEXT UNIQUE COLLATE binary);
+ INSERT INTO t12b VALUES(1,'abcde'),(2,'uvwxy'),(3,'ABCDEF');
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' ORDER BY +id;
+} {1 3}
+do_execsql_test like-12.2 {
+ SELECT id FROM t12b WHERE x LIKE 'abc%' ORDER BY +id;
+} {1 3}
+do_execsql_test like-12.3 {
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id;
+} {1 3}
+do_execsql_test like-12.4 {
+ SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id;
+} {1 3}
+do_execsql_test like-12.5 {
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id;
+} {1 3}
+do_execsql_test like-12.6 {
+ SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id;
+} {1 3}
+
+# Adding a COLLATE clause to the pattern of a LIKE operator does nothing
+# to change the suitability of using an index to satisfy that LIKE
+# operator.
+#
+do_execsql_test like-12.11 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' ORDER BY +id;
+} {/SEARCH/}
+do_execsql_test like-12.12 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12b WHERE x LIKE 'abc%' ORDER BY +id;
+} {/SCAN/}
+do_execsql_test like-12.13 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id;
+} {/SEARCH/}
+do_execsql_test like-12.14 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE nocase ORDER BY +id;
+} {/SCAN/}
+do_execsql_test like-12.15 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12nc WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id;
+} {/SEARCH/}
+do_execsql_test like-12.16 {
+ EXPLAIN QUERY PLAN
+ SELECT id FROM t12b WHERE x LIKE 'abc%' COLLATE binary ORDER BY +id;
+} {/SCAN/}
+
finish_test
diff --git a/test/limit.test b/test/limit.test
index c5b75c2..5d6ef33 100644
--- a/test/limit.test
+++ b/test/limit.test
@@ -616,4 +616,24 @@ do_test limit-13.81 {
db eval {SELECT z FROM v13c LIMIT 1 OFFSET 8}
} {}
+do_execsql_test limit-14.1 {
+ SELECT 123 LIMIT 1 OFFSET 0
+} {123}
+do_execsql_test limit-14.2 {
+ SELECT 123 LIMIT 1 OFFSET 1
+} {}
+do_execsql_test limit-14.3 {
+ SELECT 123 LIMIT 0 OFFSET 0
+} {}
+do_execsql_test limit-14.4 {
+ SELECT 123 LIMIT 0 OFFSET 1
+} {}
+do_execsql_test limit-14.6 {
+ SELECT 123 LIMIT -1 OFFSET 0
+} {123}
+do_execsql_test limit-14.7 {
+ SELECT 123 LIMIT -1 OFFSET 1
+} {}
+
+
finish_test
diff --git a/test/loadext.test b/test/loadext.test
index 0d5b841..7ba4c0c 100644
--- a/test/loadext.test
+++ b/test/loadext.test
@@ -66,6 +66,12 @@ if {$::tcl_platform(os) eq "Darwin"} {
set dlerror_nosymbol {dlsym(XXX, %2$s): symbol not found}
}
+if {$::tcl_platform(platform) eq "windows"} {
+ set dlerror_nosuchfile {The specified module could not be found.*}
+ set dlerror_notadll {%%1 is not a valid Win32 application.*}
+ set dlerror_nosymbol {The specified procedure could not be 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.
@@ -167,7 +173,7 @@ do_test loadext-2.3 {
regsub {0x[1234567890abcdefABCDEF]*} $msg XXX msg
}
list $rc $msg
-} [list 1 [format $dlerror_nosymbol $testextension icecream]]
+} /[list 1 [format $dlerror_nosymbol $testextension icecream]]/
# Try to load an extension for which the entry point fails (returns non-zero)
#
@@ -267,10 +273,17 @@ do_malloc_test loadext-5 -tclprep {
} -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
+
+# On Windows, this malloc test must be skipped because the winDlOpen
+# function itself can fail due to "out of memory" conditions.
+#
+if {$::tcl_platform(platform) ne "windows"} {
+ 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/test/loadext2.test b/test/loadext2.test
index 3d01539..d5b6ea8 100644
--- a/test/loadext2.test
+++ b/test/loadext2.test
@@ -43,6 +43,19 @@ do_test loadext2-1.2 {
}
} {1 {no such function: cube}}
+# Extensions loaders not currently registered
+#
+do_test loadext2-1.2.1 {
+ sqlite3_cancel_auto_extension_sqr
+} {0}
+do_test loadext2-1.2.2 {
+ sqlite3_cancel_auto_extension_sqr
+} {0}
+do_test loadext2-1.2.3 {
+ sqlite3_cancel_auto_extension_sqr
+} {0}
+
+
# Register auto-loaders. Still functions do not exist.
#
do_test loadext2-1.3 {
@@ -76,8 +89,19 @@ do_test loadext2-1.6 {
# Reset extension auto loading. Existing extensions still exist.
#
-do_test loadext2-1.7 {
- sqlite3_reset_auto_extension
+do_test loadext2-1.7.1 {
+ sqlite3_cancel_auto_extension_sqr
+} {1}
+do_test loadext2-1.7.2 {
+ sqlite3_cancel_auto_extension_sqr
+} {0}
+do_test loadext2-1.7.3 {
+ sqlite3_cancel_auto_extension_cube
+} {1}
+do_test loadext2-1.7.4 {
+ sqlite3_cancel_auto_extension_cube
+} {0}
+do_test loadext2-1.7.5 {
catchsql {
SELECT sqr(2)
}
diff --git a/test/lock7.test b/test/lock7.test
index 49bc147..02f2c6c 100644
--- a/test/lock7.test
+++ b/test/lock7.test
@@ -58,4 +58,3 @@ db1 close
db2 close
finish_test
-
diff --git a/test/malloc.test b/test/malloc.test
index 5d03aa8..4276b58 100644
--- a/test/malloc.test
+++ b/test/malloc.test
@@ -20,6 +20,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set ::testprefix malloc
# Only run these tests if memory debugging is turned on.
diff --git a/test/malloc5.test b/test/malloc5.test
index c02f65e..6abedf7 100644
--- a/test/malloc5.test
+++ b/test/malloc5.test
@@ -205,6 +205,7 @@ do_test malloc5-4.1 {
execsql "INSERT INTO abc VALUES($i, $i, '[string repeat X 100]');"
}
execsql {COMMIT;}
+ db cache flush
sqlite3_release_memory
sqlite3_memory_highwater 1
execsql {SELECT * FROM abc}
@@ -213,6 +214,7 @@ do_test malloc5-4.1 {
expr $nMaxBytes > 1000000
} {1}
do_test malloc5-4.2 {
+ db cache flush
sqlite3_release_memory
sqlite3_soft_heap_limit 100000
sqlite3_memory_highwater 1
diff --git a/test/mallocA.test b/test/mallocA.test
index 8995127..61e88a6 100644
--- a/test/mallocA.test
+++ b/test/mallocA.test
@@ -15,6 +15,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
+set testprefix mallocA
# Only run these tests if memory debugging is turned on.
#
@@ -40,7 +41,6 @@ db eval {
db close
copy_file test.db test.db.bu
-
do_malloc_test mallocA-1 -testdb test.db.bu -sqlbody {
ANALYZE
}
@@ -53,6 +53,7 @@ do_malloc_test mallocA-1.2 -testdb test.db.bu -sqlbody {
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;
@@ -68,6 +69,53 @@ ifcapable reindex {
}
}
+reset_db
+sqlite3_db_config_lookaside db 0 0 0
+do_execsql_test 6-prep {
+ CREATE TABLE t1(a, b);
+ CREATE INDEX i1 ON t1(a, b);
+ INSERT INTO t1 VALUES('abc', 'w'); -- rowid=1
+ INSERT INTO t1 VALUES('abc', 'x'); -- rowid=2
+ INSERT INTO t1 VALUES('abc', 'y'); -- rowid=3
+ INSERT INTO t1 VALUES('abc', 'z'); -- rowid=4
+
+ INSERT INTO t1 VALUES('def', 'w'); -- rowid=5
+ INSERT INTO t1 VALUES('def', 'x'); -- rowid=6
+ INSERT INTO t1 VALUES('def', 'y'); -- rowid=7
+ INSERT INTO t1 VALUES('def', 'z'); -- rowid=8
+
+ ANALYZE;
+}
+
+do_faultsim_test 6.1 -faults oom* -body {
+ execsql { SELECT rowid FROM t1 WHERE a='abc' AND b='x' }
+} -test {
+ faultsim_test_result [list 0 2]
+}
+do_faultsim_test 6.2 -faults oom* -body {
+ execsql { SELECT rowid FROM t1 WHERE a='abc' AND b<'y' }
+} -test {
+ faultsim_test_result [list 0 {1 2}]
+}
+ifcapable stat3 {
+ do_test 6.3-prep {
+ execsql {
+ PRAGMA writable_schema = 1;
+ CREATE TABLE sqlite_stat4 AS
+ SELECT tbl, idx, neq, nlt, ndlt, sqlite_record(sample) AS sample
+ FROM sqlite_stat3;
+ }
+ } {}
+ do_faultsim_test 6.3 -faults oom* -body {
+ execsql {
+ ANALYZE sqlite_master;
+ SELECT rowid FROM t1 WHERE a='abc' AND b<'y';
+ }
+ } -test {
+ faultsim_test_result [list 0 {1 2}]
+ }
+}
+
# Ensure that no file descriptors were leaked.
do_test malloc-99.X {
catch {db close}
diff --git a/test/mallocK.test b/test/mallocK.test
index 971bd56..dcf00da 100644
--- a/test/mallocK.test
+++ b/test/mallocK.test
@@ -16,6 +16,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
+set testprefix mallocK
set sql {SELECT * FROM t1, t2 WHERE (a=1 OR a=2)}
for {set x 1} {$x<5} {incr x} {
@@ -68,5 +69,70 @@ ifcapable vtab {
}
}
+#-------------------------------------------------------------------------
+# Test that OOM errors are correctly handled by the code that uses stat4
+# data to estimate the number of rows visited by a skip-scan range query.
+#
+add_alignment_test_collations db
+do_execsql_test 6.0 {
+ CREATE TABLE t3(a TEXT, b TEXT COLLATE utf16_aligned, c);
+ INSERT INTO t3 VALUES('one', '.....', 0);
+ INSERT INTO t3 VALUES('one', '....x', 1);
+ INSERT INTO t3 VALUES('one', '...x.', 2);
+ INSERT INTO t3 VALUES('one', '...xx', 3);
+ INSERT INTO t3 VALUES('one', '..x..', 4);
+ INSERT INTO t3 VALUES('one', '..x.x', 5);
+ INSERT INTO t3 VALUES('one', '..xx.', 6);
+ INSERT INTO t3 VALUES('one', '..xxx', 7);
+ INSERT INTO t3 VALUES('one', '.x...', 8);
+ INSERT INTO t3 VALUES('one', '.x..x', 9);
+ INSERT INTO t3 VALUES('one', '.x.x.', 10);
+ INSERT INTO t3 VALUES('one', '.x.xx', 11);
+ INSERT INTO t3 VALUES('one', '.xx..', 12);
+ INSERT INTO t3 VALUES('one', '.xx.x', 13);
+ INSERT INTO t3 VALUES('one', '.xxx.', 14);
+ INSERT INTO t3 VALUES('one', '.xxxx', 15);
+
+ INSERT INTO t3 VALUES('two', 'x....', 16);
+ INSERT INTO t3 VALUES('two', 'x...x', 17);
+ INSERT INTO t3 VALUES('two', 'x..x.', 18);
+ INSERT INTO t3 VALUES('two', 'x..xx', 19);
+ INSERT INTO t3 VALUES('two', 'x.x..', 20);
+ INSERT INTO t3 VALUES('two', 'x.x.x', 21);
+ INSERT INTO t3 VALUES('two', 'x.xx.', 22);
+ INSERT INTO t3 VALUES('two', 'x.xxx', 23);
+ INSERT INTO t3 VALUES('two', 'xx...', 24);
+ INSERT INTO t3 VALUES('two', 'xx..x', 25);
+ INSERT INTO t3 VALUES('two', 'xx.x.', 26);
+ INSERT INTO t3 VALUES('two', 'xx.xx', 27);
+ INSERT INTO t3 VALUES('two', 'xxx..', 28);
+ INSERT INTO t3 VALUES('two', 'xxx.x', 29);
+ INSERT INTO t3 VALUES('two', 'xxxx.', 30);
+ INSERT INTO t3 VALUES('two', 'xxxxx', 31);
+
+ INSERT INTO t3 SELECT * FROM t3;
+
+ CREATE INDEX i3 ON t3(a, b);
+ ANALYZE;
+
+ SELECT 'x' > '.';
+} {1}
+
+ifcapable stat4 {
+ do_eqp_test 6.1 {
+ SELECT DISTINCT c FROM t3 WHERE b BETWEEN '.xx..' AND '.xxxx';
+ } {
+ 0 0 0 {SEARCH TABLE t3 USING INDEX i3 (ANY(a) AND b>? AND b<?)}
+ 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ }
+}
+
+do_faultsim_test 6 -faults oom* -body {
+ db cache flush
+ db eval { SELECT DISTINCT c FROM t3 WHERE b BETWEEN '.xx..' AND '.xxxx' }
+} -test {
+ faultsim_test_result {0 {12 13 14 15}}
+}
finish_test
+
diff --git a/test/mallocL.test b/test/mallocL.test
new file mode 100644
index 0000000..6532ca5
--- /dev/null
+++ b/test/mallocL.test
@@ -0,0 +1,43 @@
+# 2014 August 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 test script is designed to show that the assert() fix at
+# [f1cb48f412] really is required.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set testprefix mallocL
+
+do_test 1.0 {
+ for {set i 0} {$i < 40} {incr i} {
+ lappend cols "c$i"
+ lappend vals $i
+ }
+
+ execsql "CREATE TABLE t1([join $cols ,])"
+ execsql "CREATE INDEX i1 ON t1([join $cols ,])"
+ execsql "INSERT INTO t1 VALUES([join $vals ,])"
+} {}
+
+for {set j 1} {$j < 40} {incr j} {
+ set ::sql "SELECT DISTINCT [join [lrange $cols 0 $j] ,] FROM t1"
+ do_faultsim_test 1.$j -faults oom* -body {
+ execsql $::sql
+ } -test {
+ faultsim_test_result [list 0 [lrange $::vals 0 $::j]]
+ }
+}
+
+
+finish_test
+
diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl
index 2ac619b..b586c88 100644
--- a/test/malloc_common.tcl
+++ b/test/malloc_common.tcl
@@ -93,6 +93,14 @@ set FAULTSIM(cantopen-persistent) [list \
-injectuninstall cantopen_injectuninstall \
]
+set FAULTSIM(interrupt) [list \
+ -injectinstall interrupt_injectinstall \
+ -injectstart interrupt_injectstart \
+ -injectstop interrupt_injectstop \
+ -injecterrlist {{1 interrupted} {1 interrupt}} \
+ -injectuninstall interrupt_injectuninstall \
+]
+
#--------------------------------------------------------------------------
@@ -113,7 +121,9 @@ set FAULTSIM(cantopen-persistent) [list \
proc do_faultsim_test {name args} {
global FAULTSIM
- set DEFAULT(-faults) [array names FAULTSIM]
+ foreach n [array names FAULTSIM] {
+ if {$n != "interrupt"} {lappend DEFAULT(-faults) $n}
+ }
set DEFAULT(-prep) ""
set DEFAULT(-body) ""
set DEFAULT(-test) ""
@@ -255,6 +265,22 @@ proc cantopen_injectstop {} {
shmfault cantopen
}
+# The following procs are used as [do_one_faultsim_test] callbacks
+# when injecting SQLITE_INTERRUPT error faults into test cases.
+#
+proc interrupt_injectinstall {} {
+}
+proc interrupt_injectuninstall {} {
+}
+proc interrupt_injectstart {iFail} {
+ set ::sqlite_interrupt_count $iFail
+}
+proc interrupt_injectstop {} {
+ set res [expr $::sqlite_interrupt_count<=0]
+ set ::sqlite_interrupt_count 0
+ set res
+}
+
# This command is not called directly. It is used by the
# [faultsim_test_result] command created by [do_faultsim_test] and used
# by -test scripts.
@@ -383,6 +409,7 @@ proc do_malloc_test {tn args} {
if {[string is integer $tn]} {
set tn malloc-$tn
+ catch { set tn $::testprefix-$tn }
}
if {[info exists ::mallocopts(-start)]} {
set start $::mallocopts(-start)
diff --git a/test/memdb.test b/test/memdb.test
index 802fb1a..a2efc03 100644
--- a/test/memdb.test
+++ b/test/memdb.test
@@ -240,7 +240,7 @@ foreach {i conf1 conf2 cmd t0 t1 t2} {
if {$i>1} break
}
- if {$t0} {set t1 {column a is not unique}}
+ if {$t0} {set t1 {UNIQUE constraint failed: t1.a}}
do_test memdb-5.$i {
if {$conf1!=""} {set conf1 "ON CONFLICT $conf1"}
if {$conf2!=""} {set conf2 "ON CONFLICT $conf2"}
diff --git a/test/memsubsys1.test b/test/memsubsys1.test
index e14a1b3..76b79d0 100644
--- a/test/memsubsys1.test
+++ b/test/memsubsys1.test
@@ -94,7 +94,7 @@ sqlite3_shutdown
sqlite3_config_pagecache [expr 1024+$xtra_size] 20
sqlite3_initialize
reset_highwater_marks
-build_test_db memsubsys1-2 {PRAGMA page_size=1024}
+build_test_db memsubsys1-2 {PRAGMA page_size=1024; PRAGMA mmap_size=0}
#show_memstats
set MEMORY_MANAGEMENT $sqlite_options(memorymanage)
ifcapable !malloc_usable_size {
diff --git a/test/misc1.test b/test/misc1.test
index 188a283..173b77d 100644
--- a/test/misc1.test
+++ b/test/misc1.test
@@ -235,7 +235,7 @@ do_test misc1-7.4 {
catchsql {
INSERT INTO t5 VALUES(1,2,4);
}
-} {1 {columns a, b are not unique}}
+} {1 {UNIQUE constraint failed: t5.a, t5.b}}
do_test misc1-7.5 {
catchsql {
INSERT INTO t5 VALUES(0,2,4);
@@ -592,4 +592,33 @@ do_test misc1-18.1 {
expr {$n>=100}
} {1}
+# 2014-01-10: In a CREATE TABLE AS, if one or more of the column names
+# are an empty string, that is still OK.
+#
+do_execsql_test misc1-19.1 {
+ CREATE TABLE t19 AS SELECT 1, 2 AS '', 3;
+ SELECT * FROM t19;
+} {1 2 3}
+do_execsql_test misc1-19.2 {
+ CREATE TABLE t19b AS SELECT 4 AS '', 5 AS '', 6 AS '';
+ SELECT * FROM t19b;
+} {4 5 6}
+
+# 2014-05-16: Tests for the SQLITE_TESTCTRL_FAULT_INSTALL feature.
+#
+unset -nocomplain fault_callbacks
+set fault_callbacks {}
+proc fault_callback {n} {
+ lappend ::fault_callbacks $n
+ return 0
+}
+do_test misc1-19.1 {
+ sqlite3_test_control_fault_install fault_callback
+ set fault_callbacks
+} {0}
+do_test misc1-19.2 {
+ sqlite3_test_control_fault_install
+ set fault_callbacks
+} {0}
+
finish_test
diff --git a/test/misc3.test b/test/misc3.test
index 81a8266..bc1f0ff 100644
--- a/test/misc3.test
+++ b/test/misc3.test
@@ -283,7 +283,7 @@ ifcapable {explain} {
}]
set y [regexp { 123456789012 } $x]
lappend y [regexp { 4.5678 } $x]
- lappend y [regexp {,-BINARY} $x]
+ lappend y [regexp {,-B} $x]
} {1 1 1}
} else {
do_test misc3-6.11-utf8 {
@@ -293,7 +293,7 @@ ifcapable {explain} {
set y [regexp { 123456789012 } $x]
lappend y [regexp { 4.5678 } $x]
lappend y [regexp { hello } $x]
- lappend y [regexp {,-BINARY} $x]
+ lappend y [regexp {,-B} $x]
} {1 1 1 1}
}
}
diff --git a/test/misc7.test b/test/misc7.test
index 72c1cd6..8fd5fe7 100644
--- a/test/misc7.test
+++ b/test/misc7.test
@@ -15,9 +15,11 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-do_test misc7-1-misuse {
- c_misuse_test
-} {}
+if {[clang_sanitize_address]==0} {
+ do_test misc7-1-misuse {
+ c_misuse_test
+ } {}
+}
do_test misc7-2 {
c_realloc_test
@@ -269,17 +271,17 @@ ifcapable explain {
CREATE TABLE abc(a PRIMARY KEY, b, c);
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE rowid = 1;
} {
- 0 0 0 {SEARCH TABLE abc AS t2 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE abc AS t2 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_execsql_test misc7-14.2 {
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 WHERE a = 1;
} {0 0 0
- {SEARCH TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (a=?) (~1 rows)}
+ {SEARCH TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (a=?)}
}
do_execsql_test misc7-14.3 {
EXPLAIN QUERY PLAN SELECT * FROM abc AS t2 ORDER BY a;
} {0 0 0
- {SCAN TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1 (~1000000 rows)}
+ {SCAN TABLE abc AS t2 USING INDEX sqlite_autoindex_abc_1}
}
}
@@ -355,7 +357,7 @@ do_ioerr_test misc7-16 -sqlprep {
COMMIT;
}} msg]
- if {!$rc || ($rc && [string first "columns" $msg]==0)} {
+ if {!$rc || ($rc && [string first "UNIQUE" $msg]==0)} {
set msg
} else {
error $msg
diff --git a/test/misuse.test b/test/misuse.test
index 71ee011..d5d836c 100644
--- a/test/misuse.test
+++ b/test/misuse.test
@@ -171,37 +171,40 @@ do_test misuse-4.3 {
} msg]
lappend v $msg $r
} {0 {} SQLITE_BUSY}
-do_test misuse-4.4 {
+
+if {[clang_sanitize_address]==0} {
+ 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}}
+ 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}}
+ # 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/test/mmap3.test b/test/mmap3.test
new file mode 100644
index 0000000..07b5152
--- /dev/null
+++ b/test/mmap3.test
@@ -0,0 +1,98 @@
+# 2013-05-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable !mmap||!vtab {
+ finish_test
+ return
+}
+source $testdir/lock_common.tcl
+set testprefix mmap3
+
+do_test mmap3-1.0 {
+ load_static_extension db wholenumber
+ db eval {
+ PRAGMA mmap_size=100000;
+ CREATE TABLE t1(x, y);
+ CREATE VIRTUAL TABLE nums USING wholenumber;
+ INSERT INTO t1 SELECT value, randomblob(value) FROM nums
+ WHERE value BETWEEN 1 and 1000;
+ SELECT sum(x), sum(length(y)) from t1;
+ PRAGMA mmap_size;
+ }
+} {100000 500500 500500 100000}
+do_test mmap3-1.2 {
+ db eval {
+ PRAGMA mmap_size=50000;
+ CREATE TABLE t2(a,b);
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {50000 nums t1 t2 ok 50000}
+do_test mmap3-1.3 {
+ db eval {
+ PRAGMA mmap_size=250000;
+ DROP TABLE t2;
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {250000 nums t1 ok 250000}
+do_test mmap3-1.4 {
+ db eval {SELECT x FROM t1 WHERE +x BETWEEN 10 AND 15} {
+ db eval {PRAGMA mmap_size=150000}
+ }
+ db eval {
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {ok 250000}
+do_test mmap3-1.5 {
+ db eval {SELECT x FROM t1 WHERE +x BETWEEN 10 AND 15} {
+ db eval {PRAGMA mmap_size=0}
+ }
+ db eval {
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {ok 250000}
+do_test mmap3-1.6 {
+ db eval {SELECT x FROM t1 WHERE +x BETWEEN 10 AND 15} {
+ set x [db one {PRAGMA mmap_size}]
+ }
+ set x [concat $x [db eval {
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }]]
+} {250000 ok 250000}
+do_test mmap3-1.7 {
+ db eval {
+ PRAGMA mmap_size(0);
+ CREATE TABLE t3(a,b,c);
+ SELECT name FROM sqlite_master WHERE type='table' ORDER BY 1;
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {0 nums t1 t3 ok 0}
+do_test mmap3-1.8 {
+ db eval {SELECT x FROM t1 WHERE +x BETWEEN 10 AND 15} {
+ db eval {PRAGMA mmap_size=75000}
+ }
+ db eval {
+ PRAGMA quick_check;
+ PRAGMA mmap_size;
+ }
+} {ok 75000}
+
+finish_test
diff --git a/test/mmapfault.test b/test/mmapfault.test
new file mode 100644
index 0000000..10c5e25
--- /dev/null
+++ b/test/mmapfault.test
@@ -0,0 +1,76 @@
+# 2013-05-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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+ifcapable !mmap {
+ finish_test
+ return
+}
+set testprefix mmapfault
+
+set a_string_counter 1
+proc a_string {n} {
+ global a_string_counter
+ incr a_string_counter
+ string range [string repeat "${a_string_counter}." $n] 1 $n
+}
+db func a_string a_string
+
+do_test 1-pre {
+ execsql {
+ CREATE TABLE t1(a UNIQUE, b UNIQUE);
+ INSERT INTO t1 VALUES(a_string(200), a_string(300));
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ }
+ faultsim_save_and_close
+} {}
+
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+ db func a_string a_string
+ execsql {
+ PRAGMA mmap_size = 1000000;
+ PRAGMA cache_size = 5;
+ BEGIN;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ }
+} -body {
+ execsql { INSERT INTO t1 VALUES(a_string(200), a_string(300)) }
+} -test {
+ faultsim_test_result {0 {}}
+
+ if {[sqlite3_get_autocommit db]} {
+ sqlite3 db2 test.db
+ set nRow [db2 one {SELECT count(*) FROM t1}]
+ if {$nRow!=4} { error "Database content appears incorrect (1)" }
+ db2 close
+ }
+
+ execsql { INSERT INTO t1 VALUES(a_string(201), a_string(301)) }
+ set nRow [db one {SELECT count(*) FROM t1}]
+ if {$nRow!=5 && $nRow!=66 && $nRow!=65} {
+ error "Database content appears incorrect (2) ($nRow)"
+ }
+
+ catch { execsql COMMIT }
+}
+
+
+
+finish_test
diff --git a/test/multiplex.test b/test/multiplex.test
index 32c87d9..5db56f2 100644
--- a/test/multiplex.test
+++ b/test/multiplex.test
@@ -68,6 +68,12 @@ proc multiplex_delete {name} {
}
db close
+sqlite3_shutdown
+test_sqlite3_log xLog
+proc xLog {error_code msg} {
+ lappend ::log $error_code $msg
+}
+unset -nocomplain log
multiplex_delete test.db
multiplex_delete test2.db
@@ -188,12 +194,16 @@ do_test multiplex-2.3.1 {
} {}
+unset -nocomplain ::log
do_test multiplex-2.4.1 {
sqlite3_multiplex_shutdown
} {SQLITE_MISUSE}
do_test multiplex-2.4.2 {
execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}
+do_test multiplex-2.4.3 {
+ set ::log
+} {SQLITE_MISUSE {sqlite3_multiplex_shutdown() called while database connections are still open}}
do_test multiplex-2.4.4 { file size [multiplex_name test.x 0] } {7168}
do_test multiplex-2.4.5 {
db close
@@ -583,5 +593,9 @@ do_test multiplex-6.99 {
}
+catch { db close }
catch { sqlite3_multiplex_shutdown }
+sqlite3_shutdown
+test_sqlite3_log
+sqlite3_initialize
finish_test
diff --git a/test/nolock.test b/test/nolock.test
new file mode 100644
index 0000000..331af08
--- /dev/null
+++ b/test/nolock.test
@@ -0,0 +1,185 @@
+# 2014-05-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. The
+# focus of this file is testing the nolock=1 and immutable=1 query
+# parameters and the SQLITE_IOCAP_IMMUTABLE device characteristic.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+unset -nocomplain tvfs_calls
+proc tvfs_reset {} {
+ global tvfs_calls
+ array set tvfs_calls {xLock 0 xUnlock 0 xCheckReservedLock 0 xAccess 0}
+}
+proc tvfs_callback {op args} {
+ global tvfs_calls
+ incr tvfs_calls($op)
+ return SQLITE_OK
+}
+tvfs_reset
+
+testvfs tvfs
+tvfs script tvfs_callback
+tvfs filter {xLock xUnlock xCheckReservedLock xAccess}
+
+############################################################################
+# Verify that the nolock=1 query parameter for URI filenames disables all
+# calls to xLock and xUnlock for rollback databases.
+#
+do_test nolock-1.0 {
+ db close
+ forcedelete test.db
+ tvfs_reset
+ sqlite db test.db -vfs tvfs
+ db eval {CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3);}
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock)
+} {xLock 7 xUnlock 5 xCheckReservedLock 0}
+
+do_test nolock-1.1 {
+ db close
+ forcedelete test.db
+ tvfs_reset
+ sqlite db file:test.db?nolock=0 -vfs tvfs -uri 1
+ db eval {CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3);}
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock)
+} {xLock 7 xUnlock 5 xCheckReservedLock 0}
+
+do_test nolock-1.2 {
+ db close
+ forcedelete test.db
+ tvfs_reset
+ sqlite db file:test.db?nolock=1 -vfs tvfs -uri 1
+ db eval {CREATE TABLE t1(a,b,c); INSERT INTO t1 VALUES(1,2,3);}
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0}
+
+do_test nolock-1.3 {
+ db close
+ tvfs_reset
+ sqlite db file:test.db?nolock=0 -vfs tvfs -uri 1 -readonly 1
+ db eval {SELECT * FROM t1}
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock)
+} {xLock 2 xUnlock 2 xCheckReservedLock 0}
+
+do_test nolock-1.4 {
+ db close
+ tvfs_reset
+ sqlite db file:test.db?nolock=1 -vfs tvfs -uri 1 -readonly 1
+ db eval {SELECT * FROM t1}
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0}
+
+#############################################################################
+# Verify that immutable=1 disables both locking and xAccess calls to the
+# journal files.
+#
+do_test nolock-2.0 {
+ db close
+ forcedelete test.db
+ # begin by creating a test database
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a,b);
+ INSERT INTO t1 VALUES('hello','world');
+ CREATE TABLE t2(x,y);
+ INSERT INTO t2 VALUES(12345,67890);
+ SELECT * FROM t1, t2;
+ }
+} {hello world 12345 67890}
+do_test nolock-2.1 {
+ tvfs_reset
+ sqlite3 db2 test.db -vfs tvfs
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-2.2 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 2 xUnlock 2 xCheckReservedLock 0 xAccess 4}
+
+
+do_test nolock-2.11 {
+ db2 close
+ tvfs_reset
+ sqlite3 db2 file:test.db?immutable=0 -vfs tvfs -uri 1
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-2.12 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 2 xUnlock 2 xCheckReservedLock 0 xAccess 4}
+
+
+do_test nolock-2.21 {
+ db2 close
+ tvfs_reset
+ sqlite3 db2 file:test.db?immutable=1 -vfs tvfs -uri 1
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-2.22 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0 xAccess 0}
+
+do_test nolock-2.31 {
+ db2 close
+ tvfs_reset
+ sqlite3 db2 file:test.db?immutable=1 -vfs tvfs -uri 1 -readonly 1
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-2.32 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0 xAccess 0}
+
+############################################################################
+# Verify that the SQLITE_IOCAP_IMMUTABLE flag works
+#
+do_test nolock-3.1 {
+ db2 close
+ tvfs devchar immutable
+ tvfs_reset
+ sqlite3 db2 test.db -vfs tvfs
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-3.2 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0 xAccess 0}
+
+do_test nolock-3.11 {
+ db2 close
+ tvfs_reset
+ sqlite3 db2 test.db -vfs tvfs -readonly 1
+ db2 eval {SELECT * FROM t1, t2}
+} {hello world 12345 67890}
+do_test nolock-3.12 {
+ list xLock $::tvfs_calls(xLock) xUnlock $::tvfs_calls(xUnlock) \
+ xCheckReservedLock $::tvfs_calls(xCheckReservedLock) \
+ xAccess $::tvfs_calls(xAccess)
+} {xLock 0 xUnlock 0 xCheckReservedLock 0 xAccess 0}
+
+db2 close
+db close
+tvfs delete
+finish_test
diff --git a/test/notify3.test b/test/notify3.test
index 446f010..4b5e801 100644
--- a/test/notify3.test
+++ b/test/notify3.test
@@ -150,4 +150,3 @@ catch { db2 close }
sqlite3_enable_shared_cache $esc
finish_test
-
diff --git a/test/notnull.test b/test/notnull.test
index 01738a4..23fd33d 100644
--- a/test/notnull.test
+++ b/test/notnull.test
@@ -47,7 +47,7 @@ do_test notnull-1.2 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-1.2b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.3 {
catchsql {
@@ -62,7 +62,7 @@ do_test notnull-1.4 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-1.4b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.5 {
catchsql {
@@ -70,7 +70,7 @@ do_test notnull-1.5 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-1.5b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.6 {
catchsql {
@@ -106,7 +106,7 @@ do_test notnull-1.10 {
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}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-1.10b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.11 {
catchsql {
@@ -149,7 +149,7 @@ do_test notnull-1.16 {
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}}
+} {1 {NOT NULL constraint failed: t1.c}}
verify_ex_errcode notnull-1.16b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.17 {
catchsql {
@@ -157,7 +157,7 @@ do_test notnull-1.17 {
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}}
+} {1 {NOT NULL constraint failed: t1.d}}
verify_ex_errcode notnull-1.17b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.18 {
catchsql {
@@ -179,7 +179,7 @@ do_test notnull-1.20 {
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}}
+} {1 {NOT NULL constraint failed: t1.e}}
verify_ex_errcode notnull-1.20b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-1.21 {
catchsql {
@@ -196,7 +196,7 @@ do_test notnull-2.1 {
UPDATE t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-2.1b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-2.2 {
catchsql {
@@ -205,7 +205,7 @@ do_test notnull-2.2 {
UPDATE OR REPLACE t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-2.2b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-2.3 {
catchsql {
@@ -222,7 +222,7 @@ do_test notnull-2.4 {
UPDATE OR ABORT t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-2.4b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-2.5 {
catchsql {
@@ -231,7 +231,7 @@ do_test notnull-2.5 {
UPDATE t1 SET b=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.b may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-2.6b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-2.6 {
catchsql {
@@ -272,7 +272,7 @@ do_test notnull-2.10 {
UPDATE t1 SET e=null, a=b, b=a;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.e may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.e}}
verify_ex_errcode notnull-2.10b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.0 {
@@ -298,7 +298,7 @@ do_test notnull-3.2 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-3.2b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.3 {
catchsql {
@@ -313,7 +313,7 @@ do_test notnull-3.4 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-3.4b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.5 {
catchsql {
@@ -321,7 +321,7 @@ do_test notnull-3.5 {
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}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-3.5b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.6 {
catchsql {
@@ -357,7 +357,7 @@ do_test notnull-3.10 {
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}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-3.10b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.11 {
catchsql {
@@ -400,7 +400,7 @@ do_test notnull-3.16 {
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}}
+} {1 {NOT NULL constraint failed: t1.c}}
verify_ex_errcode notnull-3.16b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.17 {
catchsql {
@@ -408,7 +408,7 @@ do_test notnull-3.17 {
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}}
+} {1 {NOT NULL constraint failed: t1.d}}
verify_ex_errcode notnull-3.17b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.18 {
catchsql {
@@ -430,7 +430,7 @@ do_test notnull-3.20 {
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}}
+} {1 {NOT NULL constraint failed: t1.e}}
verify_ex_errcode notnull-3.20b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-3.21 {
catchsql {
@@ -447,7 +447,7 @@ do_test notnull-4.1 {
UPDATE t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-4.1b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-4.2 {
catchsql {
@@ -456,7 +456,7 @@ do_test notnull-4.2 {
UPDATE OR REPLACE t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-4.2b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-4.3 {
catchsql {
@@ -473,7 +473,7 @@ do_test notnull-4.4 {
UPDATE OR ABORT t1 SET a=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.a}}
verify_ex_errcode notnull-4.4b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-4.5 {
catchsql {
@@ -482,7 +482,7 @@ do_test notnull-4.5 {
UPDATE t1 SET b=null;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.b may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-4.5b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-4.6 {
catchsql {
@@ -523,7 +523,7 @@ do_test notnull-4.10 {
UPDATE t1 SET e=null, a=b, b=a;
SELECT * FROM t1 ORDER BY a;
}
-} {1 {t1.e may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.e}}
verify_ex_errcode notnull-4.10b SQLITE_CONSTRAINT_NOTNULL
# Test that bug 29ab7be99f is fixed.
@@ -542,7 +542,7 @@ do_test notnull-5.2 {
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 SELECT * FROM t2;
}
-} {1 {t1.b may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-5.2b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-5.3 {
execsql { SELECT * FROM t1 }
@@ -555,7 +555,7 @@ do_test notnull-5.4 {
INSERT INTO t1 SELECT * FROM t2;
COMMIT;
}
-} {1 {t1.b may not be NULL}}
+} {1 {NOT NULL constraint failed: t1.b}}
verify_ex_errcode notnull-5.4b SQLITE_CONSTRAINT_NOTNULL
do_test notnull-5.5 {
execsql { SELECT * FROM t1 }
diff --git a/test/orderby1.test b/test/orderby1.test
index f459fc8..e06c9f1 100644
--- a/test/orderby1.test
+++ b/test/orderby1.test
@@ -48,7 +48,7 @@ do_test 1.0 {
} {}
do_test 1.1a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {one-a one-c two-a two-b three-a three-c}
@@ -66,7 +66,7 @@ do_test 1.1b {
#
do_test 1.2a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn
}
} {one-a one-c two-a two-b three-a three-c}
@@ -75,7 +75,7 @@ do_test 1.2a {
do_test 1.2b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn
}
} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms
@@ -85,13 +85,13 @@ do_test 1.3a {
optimization_control db order-by-idx-join 0
db cache flush
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {one-a one-c two-a two-b three-a three-c}
do_test 1.3b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {/ORDER BY/} ;# separate sorting pass due to disabled optimization
optimization_control db all 1
@@ -101,55 +101,57 @@ db cache flush
#
do_test 1.4a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
} {three-a three-c two-a two-b one-a one-c}
do_test 1.4b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn
}
} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting
do_test 1.4c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
-} {~/ORDER BY/} ;# optimized out
-
+} {~/ORDER BY/} ;# ORDER BY suppressed due to uniqueness constraints
do_test 1.5a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
} {one-c one-a two-b two-a three-c three-a}
do_test 1.5b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC
}
} {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting
do_test 1.5c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
-} {~/ORDER BY/} ;# optimized out
+} {~/ORDER BY/} ;# ORDER BY suppressed due to uniqueness constraints
do_test 1.6a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album CROSS JOIN track USING (aid)
+ ORDER BY title DESC, tn DESC
}
} {three-c three-a two-b two-a one-c one-a}
do_test 1.6b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC
+ SELECT name FROM album CROSS JOIN track USING (aid)
+ ORDER BY +title DESC, +tn DESC
}
} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting
do_test 1.6c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album CROSS JOIN track USING (aid)
+ ORDER BY title DESC, tn DESC
}
-} {~/ORDER BY/} ;# ORDER BY optimized-out
+} {~/ORDER BY/} ;# ORDER BY
# Reconstruct the test data to use indices rather than integer primary keys.
@@ -183,7 +185,7 @@ do_test 2.0 {
} {}
do_test 2.1a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {one-a one-c two-a two-b three-a three-c}
@@ -192,28 +194,28 @@ do_test 2.1a {
do_test 2.1b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
-} {~/ORDER BY/} ;# ORDER BY optimized out
+} {/ORDER BY/} ;# ORDER BY required because of missing aid term in ORDER BY
do_test 2.1c {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn
}
} {one-a one-c two-a two-b three-a three-c}
do_test 2.1d {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, aid, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, aid, tn
}
-} {~/ORDER BY/} ;# ORDER BY optimized out
+} {/ORDER BY/} ;# ORDER BY required in this case
# The same query with ORDER BY clause optimization disabled via + operators
# should give exactly the same answer.
#
do_test 2.2a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn
}
} {one-a one-c two-a two-b three-a three-c}
@@ -222,7 +224,7 @@ do_test 2.2a {
do_test 2.2b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn
}
} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms
@@ -232,13 +234,13 @@ do_test 2.3a {
optimization_control db order-by-idx-join 0
db cache flush
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {one-a one-c two-a two-b three-a three-c}
do_test 2.3b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {/ORDER BY/} ;# separate sorting pass due to disabled optimization
optimization_control db all 1
@@ -248,55 +250,55 @@ db cache flush
#
do_test 2.4a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
} {three-a three-c two-a two-b one-a one-c}
do_test 2.4b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn
}
} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting
do_test 2.4c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn
}
-} {~/ORDER BY/} ;# optimized out
+} {/ORDER BY/} ;# separate sorting pass due to mixed DESC/ASC
do_test 2.5a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
} {one-c one-a two-b two-a three-c three-a}
do_test 2.5b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC
}
} {one-c one-a two-b two-a three-c three-a} ;# verify same order after sorting
do_test 2.5c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
-} {~/ORDER BY/} ;# optimized out
+} {/ORDER BY/} ;# separate sorting pass due to mixed ASC/DESC
do_test 2.6a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
} {three-c three-a two-b two-a one-c one-a}
do_test 2.6b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC
}
} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting
do_test 2.6c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
-} {~/ORDER BY/} ;# ORDER BY optimized out
+} {/ORDER BY/} ;# ORDER BY required
# Generate another test dataset, but this time using mixed ASC/DESC indices.
@@ -348,7 +350,7 @@ do_test 3.1b {
#
do_test 3.2a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC
}
} {one-c one-a two-b two-a three-c three-a}
@@ -357,7 +359,7 @@ do_test 3.2a {
do_test 3.2b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn DESC
}
} {/ORDER BY/} ;# separate sorting pass due to "+" on ORDER BY terms
@@ -367,13 +369,13 @@ do_test 3.3a {
optimization_control db order-by-idx-join 0
db cache flush
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
} {one-c one-a two-b two-a three-c three-a}
do_test 3.3b {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn DESC
}
} {/ORDER BY/} ;# separate sorting pass due to disabled optimization
optimization_control db all 1
@@ -383,38 +385,37 @@ db cache flush
#
do_test 3.4a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
} {one-a one-c two-a two-b three-a three-c}
do_test 3.4b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title, +tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title, +tn
}
} {one-a one-c two-a two-b three-a three-c} ;# verify same order after sorting
do_test 3.4c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title, tn
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title, tn
}
-} {~/ORDER BY/} ;# optimized out
-
+} {~/ORDER BY/} ;# ORDER BY suppressed by uniqueness constraints
do_test 3.5a {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
} {three-c three-a two-b two-a one-c one-a}
do_test 3.5b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY +title DESC, +tn DESC
}
} {three-c three-a two-b two-a one-c one-a} ;# verify same order after sorting
do_test 3.5c {
db eval {
EXPLAIN QUERY PLAN
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY title DESC, tn DESC
+ SELECT name FROM album JOIN track USING (aid) ORDER BY title DESC, tn DESC
}
-} {~/ORDER BY/} ;# optimzed out
+} {~/ORDER BY/} ;# ORDER BY suppressed by uniqueness constraints
do_test 3.6a {
@@ -424,7 +425,8 @@ do_test 3.6a {
} {three-a three-c two-a two-b one-a one-c}
do_test 3.6b {
db eval {
- SELECT name FROM album CROSS JOIN track USING (aid) ORDER BY +title DESC, +tn
+ SELECT name FROM album CROSS JOIN track USING (aid)
+ ORDER BY +title DESC, +tn
}
} {three-a three-c two-a two-b one-a one-c} ;# verify same order after sorting
do_test 3.6c {
@@ -434,5 +436,50 @@ do_test 3.6c {
}
} {~/ORDER BY/} ;# inverted ASC/DESC is optimized out
+# Ticket 5ed1772895bf3deeab78c5e3519b1da9165c541b (2013-06-04)
+# Incorrect ORDER BY on an indexed JOIN
+#
+do_test 4.0 {
+ db eval {
+ CREATE TABLE t41(a INT UNIQUE NOT NULL, b INT NOT NULL);
+ CREATE INDEX t41ba ON t41(b,a);
+ CREATE TABLE t42(x INT NOT NULL REFERENCES t41(a), y INT NOT NULL);
+ CREATE UNIQUE INDEX t42xy ON t42(x,y);
+ INSERT INTO t41 VALUES(1,1),(3,1);
+ INSERT INTO t42 VALUES(1,13),(1,15),(3,14),(3,16);
+
+ SELECT b, y FROM t41 CROSS JOIN t42 ON x=a ORDER BY b, y;
+ }
+} {1 13 1 14 1 15 1 16}
+
+# No sorting of queries that omit the FROM clause.
+#
+do_execsql_test 5.0 {
+ EXPLAIN QUERY PLAN SELECT 5 ORDER BY 1
+} {}
+do_execsql_test 5.1 {
+ EXPLAIN QUERY PLAN SELECT 5 UNION ALL SELECT 3 ORDER BY 1
+} {~/B-TREE/}
+do_execsql_test 5.2 {
+ SELECT 5 UNION ALL SELECT 3 ORDER BY 1
+} {3 5}
+
+# The following test (originally derived from a single test within fuzz.test)
+# verifies that a PseudoTable cursor is not closed prematurely in a deeply
+# nested query. This test caused a segfault on 3.8.5 beta.
+#
+do_execsql_test 6.0 {
+ 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);
+ SELECT (
+ SELECT 'hardware' FROM (
+ SELECT 'software' ORDER BY 'firmware' ASC, 'sportswear' DESC
+ ) GROUP BY 1 HAVING length(b)
+ )
+ FROM abc;
+} {hardware hardware hardware}
+
finish_test
diff --git a/test/orderby5.test b/test/orderby5.test
new file mode 100644
index 0000000..c9cce70
--- /dev/null
+++ b/test/orderby5.test
@@ -0,0 +1,130 @@
+# 2013-06-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 that the optimizations that disable
+# ORDER BY clauses work correctly
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix orderby5
+
+# Generate test data for a join. Verify that the join gets the
+# correct answer.
+#
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a,b,c);
+ CREATE INDEX t1bc ON t1(b,c);
+
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT a, b, c FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.2.1 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT a, c, b FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.2.2 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT a, c, b FROM t1 WHERE a='xyz' COLLATE nocase;
+} {/B-TREE/}
+do_execsql_test 1.2.3 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT a COLLATE nocase, c, b FROM t1 WHERE a='xyz';
+} {/B-TREE/}
+do_execsql_test 1.2.4 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT a COLLATE nocase, c, b FROM t1 WHERE a='xyz' COLLATE nocase;
+} {~/B-TREE/}
+do_execsql_test 1.3 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT b, a, c FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.4 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT b, c, a FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.5 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT c, a, b FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.6 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT c, b, a FROM t1 WHERE a=0;
+} {~/B-TREE/}
+do_execsql_test 1.7 {
+ EXPLAIN QUERY PLAN
+ SELECT DISTINCT c, b, a FROM t1 WHERE +a=0;
+} {/B-TREE/}
+
+# In some cases, it is faster to do repeated index lookups than it is to
+# sort. But in other cases, it is faster to sort than to do repeated index
+# lookups.
+#
+do_execsql_test 2.1a {
+ CREATE TABLE t2(a,b,c);
+ CREATE INDEX t2bc ON t2(b,c);
+ ANALYZE;
+ INSERT INTO sqlite_stat1 VALUES('t1','t1bc','1000000 10 9');
+ INSERT INTO sqlite_stat1 VALUES('t2','t2bc','100 10 5');
+ ANALYZE sqlite_master;
+
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t2 WHERE a=0 ORDER BY a, b, c;
+} {~/B-TREE/}
+
+do_execsql_test 2.1b {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE likelihood(a=0, 0.05) ORDER BY a, b, c;
+} {/B-TREE/}
+
+do_execsql_test 2.2 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE +a=0 ORDER BY a, b, c;
+} {/B-TREE/}
+do_execsql_test 2.3 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a=0 ORDER BY b, a, c;
+} {~/B-TREE/}
+do_execsql_test 2.4 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a=0 ORDER BY b, c, a;
+} {~/B-TREE/}
+do_execsql_test 2.5 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a=0 ORDER BY a, c, b;
+} {/B-TREE/}
+do_execsql_test 2.6 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a=0 ORDER BY c, a, b;
+} {/B-TREE/}
+do_execsql_test 2.7 {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t1 WHERE a=0 ORDER BY c, b, a;
+} {/B-TREE/}
+
+
+do_execsql_test 3.0 {
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c, d, e, f);
+ CREATE INDEX t3bcde ON t3(b, c, d, e);
+ EXPLAIN QUERY PLAN
+ SELECT a FROM t3 WHERE b=2 AND c=3 ORDER BY d DESC, e DESC, b, c, a DESC;
+} {~/B-TREE/}
+do_execsql_test 3.1 {
+ DROP TABLE t3;
+ CREATE TABLE t3(a INTEGER PRIMARY KEY, b, c, d, e, f) WITHOUT rowid;
+ CREATE INDEX t3bcde ON t3(b, c, d, e);
+ EXPLAIN QUERY PLAN
+ SELECT a FROM t3 WHERE b=2 AND c=3 ORDER BY d DESC, e DESC, b, c, a DESC;
+} {~/B-TREE/}
+
+
+finish_test
diff --git a/test/orderby6.test b/test/orderby6.test
new file mode 100644
index 0000000..403250d
--- /dev/null
+++ b/test/orderby6.test
@@ -0,0 +1,183 @@
+# 2014-03-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 file is testing that the block-sort optimization.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix orderby6
+
+# Run all tests twice. Once with a normal table and a second time
+# with a WITHOUT ROWID table
+#
+foreach {tn rowidclause} {1 {} 2 {WITHOUT ROWID}} {
+
+ # Construct a table with 1000 rows and a split primary key
+ #
+ reset_db
+ do_test $tn.1 {
+ db eval "CREATE TABLE t1(a,b,c,PRIMARY KEY(b,c)) $rowidclause;"
+ db eval {
+ WITH RECURSIVE
+ cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000)
+ INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;
+ }
+ } {}
+
+ # Run various ORDER BY queries that can benefit from block-sort.
+ # Compare the output to the same output using a full-sort enforced
+ # by adding + to each term of the ORDER BY clause.
+ #
+ do_execsql_test $tn.2 {
+ SELECT b,a,c FROM t1 ORDER BY b,a,c;
+ } [db eval {SELECT b,a,c FROM t1 ORDER BY +b,+a,+c}]
+ do_execsql_test $tn.3 {
+ SELECT b,a,c FROM t1 ORDER BY b,c DESC,a;
+ } [db eval {SELECT b,a,c FROM t1 ORDER BY +b,+c DESC,+a}]
+ do_execsql_test $tn.4 {
+ SELECT b,a,c FROM t1 ORDER BY b DESC,c,a;
+ } [db eval {SELECT b,a,c FROM t1 ORDER BY +b DESC,+c,+a}]
+ do_execsql_test $tn.5 {
+ SELECT b,a,c FROM t1 ORDER BY b DESC,a,c;
+ } [db eval {SELECT b,a,c FROM t1 ORDER BY +b DESC,+a,+c}]
+
+ # LIMIT and OFFSET clauses on block-sort queries.
+ #
+ do_execsql_test $tn.11 {
+ SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;
+ } {840 880 920 960 1000 1 41 81 121 161}
+ do_execsql_test $tn.11x {
+ SELECT a FROM t1 ORDER BY +b, a LIMIT 10 OFFSET 20;
+ } {840 880 920 960 1000 1 41 81 121 161}
+
+ do_execsql_test $tn.12 {
+ SELECT a FROM t1 ORDER BY b DESC, a LIMIT 10 OFFSET 20;
+ } {839 879 919 959 999 38 78 118 158 198}
+ do_execsql_test $tn.12 {
+ SELECT a FROM t1 ORDER BY +b DESC, a LIMIT 10 OFFSET 20;
+ } {839 879 919 959 999 38 78 118 158 198}
+
+ do_execsql_test $tn.13 {
+ SELECT a FROM t1 ORDER BY b, a DESC LIMIT 10 OFFSET 45;
+ } {161 121 81 41 1 962 922 882 842 802}
+ do_execsql_test $tn.13x {
+ SELECT a FROM t1 ORDER BY +b, a DESC LIMIT 10 OFFSET 45;
+ } {161 121 81 41 1 962 922 882 842 802}
+
+ do_execsql_test $tn.14 {
+ SELECT a FROM t1 ORDER BY b DESC, a LIMIT 10 OFFSET 45;
+ } {838 878 918 958 998 37 77 117 157 197}
+ do_execsql_test $tn.14x {
+ SELECT a FROM t1 ORDER BY +b DESC, a LIMIT 10 OFFSET 45;
+ } {838 878 918 958 998 37 77 117 157 197}
+
+ # Many test cases where the LIMIT+OFFSET window is in various
+ # alignments with block-sort boundaries.
+ #
+ foreach {tx limit offset orderby} {
+ 1 10 24 {+b,+a}
+ 2 10 25 {+b,+a}
+ 3 10 26 {+b,+a}
+ 4 10 39 {+b,+a}
+ 5 10 40 {+b,+a}
+ 6 10 41 {+b,+a}
+ 7 27 24 {+b,+a}
+ 8 27 49 {+b,+a}
+ 11 10 24 {+b DESC,+a}
+ 12 10 25 {+b DESC,+a}
+ 13 10 26 {+b DESC,+a}
+ 14 10 39 {+b DESC,+a}
+ 15 10 40 {+b DESC,+a}
+ 16 10 41 {+b DESC,+a}
+ 17 27 24 {+b DESC,+a}
+ 18 27 49 {+b DESC,+a}
+ 21 10 24 {+b,+a DESC}
+ 22 10 25 {+b,+a DESC}
+ 23 10 26 {+b,+a DESC}
+ 24 10 39 {+b,+a DESC}
+ 25 10 40 {+b,+a DESC}
+ 26 10 41 {+b,+a DESC}
+ 27 27 24 {+b,+a DESC}
+ 28 27 49 {+b,+a DESC}
+ 31 10 24 {+b DESC,+a DESC}
+ 32 10 25 {+b DESC,+a DESC}
+ 33 10 26 {+b DESC,+a DESC}
+ 34 10 39 {+b DESC,+a DESC}
+ 35 10 40 {+b DESC,+a DESC}
+ 36 10 41 {+b DESC,+a DESC}
+ 37 27 24 {+b DESC,+a DESC}
+ 38 27 49 {+b DESC,+a DESC}
+ } {
+ set sql1 "SELECT a FROM t1 ORDER BY $orderby LIMIT $limit OFFSET $offset;"
+ set sql2 [string map {+ {}} $sql1]
+ # puts $sql2\n$sql1\n[db eval $sql2]
+ do_test $tn.21.$tx {db eval $::sql2} [db eval $sql1]
+ }
+
+ ########################################################################
+ # A second test table, t2, has many columns open to sorting.
+ do_test $tn.31 {
+ db eval "CREATE TABLE t2(a,b,c,d,e,f,PRIMARY KEY(b,c,d,e,f)) $rowidclause;"
+ db eval {
+ WITH RECURSIVE
+ cnt(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM cnt WHERE x<242)
+ INSERT INTO t2 SELECT x, x%3, (x/3)%3, (x/9)%3, (x/27)%3, (x/81)%3
+ FROM cnt;
+ }
+ } {}
+
+ do_execsql_test $tn.32 {
+ SELECT a FROM t2 ORDER BY b,c,d,e,f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
+ do_execsql_test $tn.33 {
+ SELECT a FROM t2 ORDER BY b,c,d,e,+f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
+ do_execsql_test $tn.34 {
+ SELECT a FROM t2 ORDER BY b,c,d,+e,+f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
+ do_execsql_test $tn.35 {
+ SELECT a FROM t2 ORDER BY b,c,+d,+e,+f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
+ do_execsql_test $tn.36 {
+ SELECT a FROM t2 ORDER BY b,+c,+d,+e,+f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
+
+ do_execsql_test $tn.37 {
+ SELECT a FROM t2 ORDER BY b,c,d,e,f DESC;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f DESC;}]
+ do_execsql_test $tn.38 {
+ SELECT a FROM t2 ORDER BY b,c,d,e DESC,f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e DESC,+f;}]
+ do_execsql_test $tn.39 {
+ SELECT a FROM t2 ORDER BY b,c,d DESC,e,f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d DESC,+e,+f;}]
+ do_execsql_test $tn.40 {
+ SELECT a FROM t2 ORDER BY b,c DESC,d,e,f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c DESC,+d,+e,+f;}]
+ do_execsql_test $tn.41 {
+ SELECT a FROM t2 ORDER BY b DESC,c,d,e,f;
+ } [db eval {SELECT a FROM t2 ORDER BY +b DESC,+c,+d,+e,+f;}]
+
+ do_execsql_test $tn.42 {
+ SELECT a FROM t2 ORDER BY b DESC,c DESC,d,e,f LIMIT 31;
+ } [db eval {SELECT a FROM t2 ORDER BY +b DESC,+c DESC,+d,+e,+f LIMIT 31}]
+ do_execsql_test $tn.43 {
+ SELECT a FROM t2 ORDER BY b,c,d,e,f DESC LIMIT 8 OFFSET 7;
+ } [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f DESC LIMIT 8 OFFSET 7}]
+
+
+}
+
+
+
+finish_test
diff --git a/test/orderby7.test b/test/orderby7.test
new file mode 100644
index 0000000..8c5fc9a
--- /dev/null
+++ b/test/orderby7.test
@@ -0,0 +1,106 @@
+# 2014-04-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 ORDER BY optimizations on joins
+# that involve virtual tables.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix orderby7
+
+ifcapable !fts3 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE fts USING fts3(content TEXT);
+ INSERT INTO fts(rowid,content)
+ VALUES(1,'this is a test of the fts3 virtual'),
+ (2,'table used as part of a join together'),
+ (3,'with the DISTINCT keyword. There was'),
+ (4,'a bug at one time (2013-06 through 2014-04)'),
+ (5,'that prevented this from working correctly.'),
+ (11,'a row that occurs twice'),
+ (12,'a row that occurs twice');
+
+ CREATE TABLE t1(x TEXT PRIMARY KEY, y);
+ INSERT OR IGNORE INTO t1 SELECT content, rowid+100 FROM fts;
+} {}
+do_execsql_test 1.1 {
+ SELECT DISTINCT fts.rowid, t1.y
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x
+ ORDER BY y;
+} {11 111 12 111}
+do_execsql_test 1.2 {
+ SELECT DISTINCT fts.rowid, t1.x
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x
+ ORDER BY 1;
+} {11 {a row that occurs twice} 12 {a row that occurs twice}}
+do_execsql_test 1.3 {
+ SELECT DISTINCT t1.x
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x
+ ORDER BY 1;
+} {{a row that occurs twice}}
+do_execsql_test 1.4 {
+ SELECT t1.x
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x
+ ORDER BY 1;
+} {{a row that occurs twice} {a row that occurs twice}}
+do_execsql_test 1.5 {
+ SELECT DISTINCT t1.x
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x;
+} {{a row that occurs twice}}
+do_execsql_test 1.6 {
+ SELECT t1.x
+ FROM fts, t1
+ WHERE fts MATCH 'that twice'
+ AND content=x;
+} {{a row that occurs twice} {a row that occurs twice}}
+
+do_execsql_test 2.1 {
+ SELECT DISTINCT t1.x
+ FROM fts, t1
+ WHERE fts.rowid=11
+ AND content=x
+ ORDER BY fts.rowid;
+} {{a row that occurs twice}}
+do_execsql_test 2.2 {
+ SELECT DISTINCT t1.*
+ FROM fts, t1
+ WHERE fts.rowid=11
+ AND content=x
+ ORDER BY fts.rowid;
+} {{a row that occurs twice} 111}
+do_execsql_test 2.3 {
+ SELECT DISTINCT t1.*
+ FROM fts, t1
+ WHERE fts.rowid=11
+ AND content=x
+ ORDER BY t1.y
+} {{a row that occurs twice} 111}
+
+
+
+
+finish_test
diff --git a/test/pager1.test b/test/pager1.test
index 72e4780..005b356 100644
--- a/test/pager1.test
+++ b/test/pager1.test
@@ -271,7 +271,7 @@ do_execsql_test pager1-3.1.2 {
} {3 0}
do_catchsql_test pager1-3.1.3 {
INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1
-} {1 {constraint failed}}
+} {1 {CHECK constraint failed: counter}}
do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0}
do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3}
do_execsql_test pager1-3.6 { COMMIT } {}
@@ -1703,7 +1703,7 @@ do_catchsql_test pager1-14.1.4 {
BEGIN;
INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
INSERT INTO t1(rowid, a, b) SELECT a+3, b, b FROM t1;
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.rowid}}
do_execsql_test pager1-14.1.5 {
COMMIT;
SELECT * FROM t1;
@@ -2815,4 +2815,3 @@ do_test 43.3 {
} {0 1 0}
finish_test
-
diff --git a/test/pager4.test b/test/pager4.test
new file mode 100644
index 0000000..2cf73d1
--- /dev/null
+++ b/test/pager4.test
@@ -0,0 +1,99 @@
+# 2013-12-06
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you 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 SQLITE_READONLY_DBMOVED error condition: the database file
+# is unlinked or renamed out from under SQLite.
+#
+
+if {$tcl_platform(platform)!="unix"} return
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+if {[permutation]=="inmemory_journal"} {
+ finish_test
+ return
+}
+
+# Create a database file for testing
+#
+do_execsql_test pager4-1.1 {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(673,'stone','philips');
+ SELECT * FROM t1;
+} {673 stone philips}
+
+# After renaming the database file while it is open, one can still
+# read from the database, but writing returns a READONLY error.
+#
+file delete -force test-xyz.db
+file rename test.db test-xyz.db
+do_catchsql_test pager4-1.2 {
+ SELECT * FROM t1;
+} {0 {673 stone philips}}
+do_catchsql_test pager4-1.3 {
+ UPDATE t1 SET a=537;
+} {1 {attempt to write a readonly database}}
+
+# Creating a different database file with the same name of the original
+# is detected and still leaves the database read-only.
+#
+sqlite3 db2 test.db
+db2 eval {CREATE TABLE t2(x,y,z)}
+do_catchsql_test pager4-1.4 {
+ UPDATE t1 SET a=948;
+} {1 {attempt to write a readonly database}}
+
+# Changing the name back clears the READONLY error
+#
+db2 close
+file delete -force test.db
+file rename test-xyz.db test.db
+do_catchsql_test pager4-1.5 {
+ SELECT * FROM t1;
+} {0 {673 stone philips}}
+do_catchsql_test pager4-1.6 {
+ UPDATE t1 SET a=537;
+ SELECT * FROM t1;
+} {0 {537 stone philips}}
+
+# We can write to a renamed database if journal_mode=OFF or
+# journal_mode=MEMORY.
+#
+file rename test.db test-xyz.db
+do_catchsql_test pager4-1.7 {
+ PRAGMA journal_mode=OFF;
+ UPDATE t1 SET a=107;
+ SELECT * FROM t1;
+} {0 {off 107 stone philips}}
+do_catchsql_test pager4-1.8 {
+ PRAGMA journal_mode=MEMORY;
+ UPDATE t1 SET b='magpie';
+ SELECT * FROM t1;
+} {0 {memory 107 magpie philips}}
+
+# Any other journal mode gives a READONLY error
+#
+do_catchsql_test pager4-1.9 {
+ PRAGMA journal_mode=DELETE;
+ UPDATE t1 SET c='jaguar';
+} {1 {attempt to write a readonly database}}
+do_catchsql_test pager4-1.10 {
+ PRAGMA journal_mode=TRUNCATE;
+ UPDATE t1 SET c='jaguar';
+} {1 {attempt to write a readonly database}}
+do_catchsql_test pager4-1.11 {
+ PRAGMA journal_mode=PERSIST;
+ UPDATE t1 SET c='jaguar';
+} {1 {attempt to write a readonly database}}
+
+
+finish_test
diff --git a/test/pagerfault.test b/test/pagerfault.test
index 23754fa..c0f5de6 100644
--- a/test/pagerfault.test
+++ b/test/pagerfault.test
@@ -1544,6 +1544,6 @@ do_faultsim_test pagerfault-36 -prep {
sqlite3_shutdown
sqlite3_config_uri 0
+sqlite3_initialize
finish_test
-
diff --git a/test/pagerfault2.test b/test/pagerfault2.test
index 6cdb99a..7f6c995 100644
--- a/test/pagerfault2.test
+++ b/test/pagerfault2.test
@@ -96,4 +96,3 @@ do_faultsim_test pagerfault2-2 -faults oom-transient -prep {
sqlite3_memdebug_vfs_oom_test 1
finish_test
-
diff --git a/test/pagerfault3.test b/test/pagerfault3.test
index 3d192e6..a4301cd 100644
--- a/test/pagerfault3.test
+++ b/test/pagerfault3.test
@@ -61,4 +61,3 @@ do_faultsim_test pagerfault3-1 -faults ioerr-transient -prep {
}
finish_test
-
diff --git a/test/pcache.test b/test/pcache.test
index 5dc3059..0766fce 100644
--- a/test/pcache.test
+++ b/test/pcache.test
@@ -44,6 +44,7 @@ do_test pcache-1.2 {
execsql {
PRAGMA cache_size=12;
PRAGMA auto_vacuum=0;
+ PRAGMA mmap_size=0;
}
pcache_stats
} {current 1 max 12 min 10 recyclable 1}
diff --git a/test/percentile.test b/test/percentile.test
new file mode 100644
index 0000000..9d471df
--- /dev/null
+++ b/test/percentile.test
@@ -0,0 +1,209 @@
+# 2013-05-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 percentile.c extension
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Basic test of the percentile() function.
+#
+do_test percentile-1.0 {
+ load_static_extension db percentile
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1),(4),(6),(7),(8),(9),(11),(11),(11);
+ }
+ execsql {SELECT percentile(x,0) FROM t1}
+} {1.0}
+foreach {in out} {
+ 100 11.0
+ 50 8.0
+ 12.5 4.0
+ 15 4.4
+ 20 5.2
+ 80 11.0
+ 89 11.0
+} {
+ do_test percentile-1.1.$in {
+ execsql {SELECT percentile(x,$in) FROM t1}
+ } $out
+}
+
+# Add some NULL values.
+#
+do_test percentile-1.2 {
+ execsql {INSERT INTO t1 VALUES(NULL),(NULL);}
+} {}
+foreach {in out} {
+ 100 11.0
+ 50 8.0
+ 12.5 4.0
+ 15 4.4
+ 20 5.2
+ 80 11.0
+ 89 11.0
+} {
+ do_test percentile-1.3.$in {
+ execsql {SELECT percentile(x,$in) FROM t1}
+ } $out
+}
+
+# The second argument to percentile can change some, but not much.
+#
+do_test percentile-1.4 {
+ catchsql {SELECT round(percentile(x, 15+0.000001*rowid),1) FROM t1}
+} {0 4.4}
+do_test percentile-1.5 {
+ catchsql {SELECT round(percentile(x, 15+0.1*rowid),1) FROM t1}
+} {1 {2nd argument to percentile() is not the same for all input rows}}
+
+# Input values in a random order
+#
+do_test percentile-1.6 {
+ execsql {
+ CREATE TABLE t2(x);
+ INSERT INTO t2 SELECT x+0.0 FROM t1 ORDER BY random();
+ }
+} {}
+foreach {in out} {
+ 100 11.0
+ 50 8.0
+ 12.5 4.0
+ 15 4.4
+ 20 5.2
+ 80 11.0
+ 89 11.0
+} {
+ do_test percentile-1.7.$in {
+ execsql {SELECT percentile(x,$in) FROM t2}
+ } $out
+}
+
+# Wrong number of arguments
+#
+do_test percentile-1.8 {
+ catchsql {SELECT percentile(x,0,1) FROM t1}
+} {1 {wrong number of arguments to function percentile()}}
+do_test percentile-1.9 {
+ catchsql {SELECT percentile(x) FROM t1}
+} {1 {wrong number of arguments to function percentile()}}
+
+# Second argument must be numeric
+#
+do_test percentile-1.10 {
+ catchsql {SELECT percentile(x,null) FROM t1}
+} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+do_test percentile-1.11 {
+ catchsql {SELECT percentile(x,'fifty') FROM t1}
+} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+do_test percentile-1.12 {
+ catchsql {SELECT percentile(x,x'3530') FROM t1}
+} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+
+# Second argument is out of range
+#
+do_test percentile-1.13 {
+ catchsql {SELECT percentile(x,-0.0000001) FROM t1}
+} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+do_test percentile-1.14 {
+ catchsql {SELECT percentile(x,100.0000001) FROM t1}
+} {1 {2nd argument to percentile() is not a number between 0.0 and 100.0}}
+
+# First argument is not NULL and is not NUMERIC
+#
+do_test percentile-1.15 {
+ catchsql {
+ BEGIN;
+ UPDATE t1 SET x='50' WHERE x IS NULL;
+ SELECT percentile(x, 50) FROM t1;
+ }
+} {1 {1st argument to percentile() is not numeric}}
+do_test percentile-1.16 {
+ catchsql {
+ ROLLBACK;
+ BEGIN;
+ UPDATE t1 SET x=x'3530' WHERE x IS NULL;
+ SELECT percentile(x, 50) FROM t1;
+ }
+} {1 {1st argument to percentile() is not numeric}}
+do_test percentile-1.17 {
+ catchsql {
+ ROLLBACK;
+ SELECT percentile(x, 50) FROM t1;
+ }
+} {0 8.0}
+
+# No non-NULL entries.
+#
+do_test percentile-1.18 {
+ execsql {
+ UPDATE t1 SET x=NULL;
+ SELECT ifnull(percentile(x, 50),'NULL') FROM t1
+ }
+} {NULL}
+
+# Exactly one non-NULL entry
+#
+do_test percentile-1.19 {
+ execsql {
+ UPDATE t1 SET x=12345 WHERE rowid=5;
+ SELECT percentile(x, 0), percentile(x, 50), percentile(x,100) FROM t1
+ }
+} {12345.0 12345.0 12345.0}
+
+# Infinity as an input
+#
+do_test percentile-1.20 {
+ catchsql {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT x+0.0 FROM t2;
+ UPDATE t1 SET x=1.0e300*1.0e300 WHERE rowid=5;
+ SELECT percentile(x,50) from t1;
+ }
+} {1 {Inf input to percentile()}}
+do_test percentile-1.21 {
+ catchsql {
+ UPDATE t1 SET x=-1.0e300*1.0e300 WHERE rowid=5;
+ SELECT percentile(x,50) from t1;
+ }
+} {1 {Inf input to percentile()}}
+
+# Million-row Inputs
+#
+ifcapable vtab {
+ do_test percentile-2.0 {
+ load_static_extension db wholenumber
+ execsql {
+ CREATE VIRTUAL TABLE nums USING wholenumber;
+ CREATE TABLE t3(x);
+ INSERT INTO t3 SELECT value-1 FROM nums WHERE value BETWEEN 1 AND 500000;
+ INSERT INTO t3 SELECT value*10 FROM nums
+ WHERE value BETWEEN 500000 AND 999999;
+ SELECT count(*) FROM t3;
+ }
+ } {1000000}
+ foreach {in out} {
+ 0 0.0
+ 100 9999990.0
+ 50 2749999.5
+ 10 99999.9
+ } {
+ do_test percentile-2.1.$in {
+ execsql {
+ SELECT percentile(x, $in) from t3;
+ }
+ } $out
+ }
+}
+
+finish_test
diff --git a/test/permutations.test b/test/permutations.test
index bc3ceb8..c3f4ddf 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -112,6 +112,7 @@ set allquicktests [test_set $alltests -exclude {
incrvacuum_ioerr.test autovacuum_crash.test btree8.test shared_err.test
vtab_err.test walslow.test walcrash.test walcrash3.test
walthread.test rtree3.test indexfault.test securedel2.test
+ fts4growth.test fts4growth2.test
}]
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
@@ -157,6 +158,28 @@ test_suite "valgrind" -prefix "" -description {
unset -nocomplain ::G(valgrind)
}
+test_suite "valgrind-nolookaside" -prefix "" -description {
+ Run the "veryquick" test suite with a couple of multi-process tests (that
+ fail under valgrind) omitted.
+} -files [
+ test_set $allquicktests -exclude *malloc* *ioerr* *fault* wal.test atof1.test
+] -initialize {
+ set ::G(valgrind) 1
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_lookaside 0 0
+ sqlite3_initialize
+ autoinstall_test_functions
+} -shutdown {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_lookaside 100 500
+ sqlite3_initialize
+ autoinstall_test_functions
+ unset -nocomplain ::G(valgrind)
+}
+
+
test_suite "quick" -prefix "" -description {
Quick test suite. Runs in around 10 minutes on a workstation.
} -files [
@@ -194,7 +217,9 @@ test_suite "fts3" -prefix "" -description {
fts4aa.test fts4content.test
fts3conf.test fts3prefix.test fts3fault2.test fts3corrupt.test
fts3corrupt2.test fts3first.test fts4langid.test fts4merge.test
- fts4check.test fts4unicode.test
+ fts4check.test fts4unicode.test fts4noti.test
+ fts3varint.test
+ fts4growth.test fts4growth2.test
}
test_suite "nofaultsim" -prefix "" -description {
@@ -213,6 +238,93 @@ test_suite "nofaultsim" -prefix "" -description {
unset -nocomplain ::G(valgrind)
}
+test_suite "queryplanner" -prefix "" -description {
+ Tests of the query planner and query optimizer
+} -files {
+ alter2.test alter3.test alter4.test alter.test analyze3.test
+ analyze4.test analyze5.test analyze6.test analyze7.test analyze8.test
+ analyze.test attach2.test attach3.test attach4.test
+ attach.test autoinc.test autoindex1.test between.test cast.test
+ check.test closure01.test coalesce.test collate1.test collate2.test
+ collate3.test collate4.test collate5.test collate6.test collate7.test
+ collate8.test collate9.test collateA.test colmeta.test colname.test
+ conflict.test count.test coveridxscan.test createtab.test cse.test
+ date.test dbstatus2.test dbstatus.test default.test delete2.test
+ delete3.test delete.test descidx1.test descidx2.test descidx3.test
+ distinctagg.test distinct.test e_createtable.test e_delete.test
+ e_droptrigger.test e_dropview.test e_expr.test e_insert.test
+ eqp.test e_reindex.test e_resolve.test e_select2.test e_select.test
+ e_update.test exists.test expr.test fkey1.test fkey2.test fkey3.test
+ fkey4.test fkey5.test func2.test func3.test func.test
+ in3.test in4.test in5.test index2.test index3.test
+ index4.test index5.test indexedby.test index.test
+ insert2.test insert3.test insert4.test insert5.test insert.test
+ instr.test in.test intpkey.test join2.test join3.test join4.test
+ join5.test join6.test join.test like2.test like.test limit.test
+ minmax2.test minmax3.test minmax4.test minmax.test misc1.test misc2.test
+ misc3.test misc4.test misc5.test misc6.test misc7.test orderby1.test
+ orderby2.test orderby3.test orderby4.test randexpr1.test regexp1.test
+ reindex.test rowhash.test rowid.test schema2.test schema3.test
+ schema4.test schema5.test schema.test
+ select1.test select2.test select3.test select4.test select5.test
+ select6.test select7.test select8.test select9.test selectA.test
+ selectB.test selectC.test selectD.test selectE.test sidedelete.test
+ sort.test spellfix.test subquery2.test subquery.test subselect.test
+ substr.test tkt-02a8e81d44.test tkt1435.test tkt1443.test tkt1444.test
+ tkt1449.test tkt1473.test tkt1501.test tkt1512.test tkt1514.test
+ tkt1536.test tkt1537.test tkt1567.test tkt1644.test tkt1667.test
+ tkt1873.test tkt2141.test tkt2192.test tkt2213.test tkt2251.test
+ tkt2285.test tkt2332.test tkt2339.test tkt2391.test tkt2409.test
+ tkt2450.test tkt2565.test tkt2640.test tkt2643.test tkt2686.test
+ tkt-26ff0c2d1e.test tkt2767.test tkt2817.test tkt2820.test tkt2822.test
+ tkt2832.test tkt2854.test tkt2920.test tkt2927.test tkt2942.test
+ tkt-2a5629202f.test tkt-2d1a5c67d.test tkt-2ea2425d34.test tkt3080.test
+ tkt3093.test tkt3121.test tkt-31338dca7e.test tkt-313723c356.test
+ tkt3201.test tkt3292.test tkt3298.test tkt3334.test tkt3346.test
+ tkt3357.test tkt3419.test tkt3424.test tkt3442.test tkt3457.test
+ tkt3461.test tkt3493.test tkt3508.test tkt3522.test tkt3527.test
+ tkt3541.test tkt3554.test tkt3581.test tkt35xx.test tkt3630.test
+ tkt3718.test tkt3731.test tkt3757.test tkt3761.test tkt3762.test
+ tkt3773.test tkt3791.test tkt3793.test tkt3810.test tkt3824.test
+ tkt3832.test tkt3838.test tkt3841.test tkt-385a5b56b9.test tkt3871.test
+ tkt3879.test tkt-38cb5df375.test tkt3911.test tkt3918.test tkt3922.test
+ tkt3929.test tkt3935.test tkt3992.test tkt3997.test tkt-3998683a16.test
+ tkt-3a77c9714e.test tkt-3fe897352e.test tkt4018.test tkt-4a03edc4c8.test
+ tkt-4dd95f6943.test tkt-54844eea3f.test tkt-5d863f876e.test
+ tkt-5e10420e8d.test tkt-5ee23731f.test tkt-6bfb98dfc0.test
+ tkt-752e1646fc.test tkt-78e04e52ea.test tkt-7a31705a7e6.test
+ tkt-7bbfb7d442.test tkt-80ba201079.test tkt-80e031a00f.test
+ tkt-8454a207b9.test tkt-91e2e8ba6f.test tkt-94c04eaadb.test
+ tkt-9d68c883.test tkt-a7b7803e.test tkt-b1d3a2e531.test
+ tkt-b351d95f9.test tkt-b72787b1.test tkt-bd484a090c.test
+ tkt-bdc6bbbb38.test tkt-c48d99d690.test tkt-cbd054fa6b.test
+ tkt-d11f09d36e.test tkt-d635236375.test tkt-d82e3f3721.test
+ tkt-f3e5abed55.test tkt-f777251dc7a.test tkt-f7b4edec.test
+ tkt-f973c7ac31.test tkt-fa7bf5ec.test tkt-fc62af4523.test
+ tkt-fc7bd6358f.test trigger1.test trigger2.test trigger3.test
+ trigger4.test trigger5.test trigger6.test trigger7.test trigger8.test
+ trigger9.test triggerA.test triggerB.test triggerC.test triggerD.test
+ types2.test types3.test types.test unique.test unordered.test
+ update.test view.test vtab1.test vtab2.test vtab3.test vtab4.test
+ vtab5.test vtab6.test vtab7.test vtab8.test vtab9.test vtab_alter.test
+ vtabA.test vtabB.test vtabC.test vtabD.test vtabE.test
+ vtabF.test where2.test where3.test where4.test where5.test where6.test
+ where7.test where8m.test where8.test where9.test whereA.test whereB.test
+ whereC.test whereD.test whereE.test whereF.test wherelimit.test
+ where.test
+}
+
+test_suite "vfslog" -prefix "" -description {
+ "Vfslog" quick test suite. Like "veryquick" except does not omits
+ a few tests that do not work with a version 1 VFS. And the quota* tests,
+ which do not work with a VFS that uses the pVfs argument passed to
+ sqlite3_vfs methods.
+} -files [
+ test_set $allquicktests -exclude *malloc* *ioerr* *fault* oserror.test \
+ pager1.test syscall.test sysfault.test tkt3457.test quota* superlock* \
+ wal* mmap*
+]
+
lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
# Define the coverage related test suites:
@@ -235,6 +347,14 @@ test_suite "coverage-pager" -description {
walfault.test walbak.test journal2.test tkt-9d68c883.test
}
+test_suite "coverage-analyze" -description {
+ Coverage tests for file analyze.c.
+} -files {
+ analyze3.test analyze4.test analyze5.test analyze6.test
+ analyze7.test analyze8.test analyze9.test analyzeA.test
+ analyze.test analyzeB.test mallocA.test
+}
+
lappend ::testsuitelist xxx
#-------------------------------------------------------------------------
@@ -424,6 +544,8 @@ test_suite "utf16" -description {
pragma encoding = 'UTF-16'
} -files {
alter.test alter3.test
+ analyze.test analyze3.test analyze4.test analyze5.test analyze6.test
+ analyze7.test analyze8.test analyze9.test analyzeA.test analyzeB.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
@@ -544,7 +666,7 @@ test_suite "inmemory_journal" -description {
ioerr.test ioerr2.test ioerr3.test ioerr4.test ioerr5.test
vacuum3.test incrblob_err.test diskfull.test backup_ioerr.test
e_fts3.test fts3cov.test fts3malloc.test fts3rnd.test
- fts3snippet.test
+ fts3snippet.test mmapfault.test
# Exclude test scripts that use tcl IO to access journal files or count
# the number of fsync() calls.
diff --git a/test/pragma.test b/test/pragma.test
index a6d198e..539d867 100644
--- a/test/pragma.test
+++ b/test/pragma.test
@@ -285,31 +285,31 @@ ifcapable attach {
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}}
+ } {{row 1 missing from index i2} {row 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}}
+ } {{row 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}}
+ } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 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}}
+ } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 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}}
+ } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 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}}
+ } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 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
@@ -344,7 +344,7 @@ ifcapable attach {
} {{*** 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}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
do_test pragma-3.10 {
execsql {
PRAGMA integrity_check=1
@@ -358,7 +358,7 @@ Page 4 is never used}}
} {{*** 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}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2}}
do_test pragma-3.12 {
execsql {
PRAGMA integrity_check=4
@@ -366,7 +366,7 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
} {{*** in database t2 ***
Page 4 is never used
Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2}}
do_test pragma-3.13 {
execsql {
PRAGMA integrity_check=3
@@ -390,10 +390,10 @@ Page 5 is never used}}
} {{*** 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 6 is never used} {row 1 missing from index i2} {row 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}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
do_test pragma-3.16 {
execsql {
PRAGMA integrity_check(10)
@@ -401,10 +401,10 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
} {{*** 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 6 is never used} {row 1 missing from index i2} {row 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}}
+Page 6 is never used} {row 1 missing from index i2}}
do_test pragma-3.17 {
execsql {
PRAGMA integrity_check=8
@@ -412,7 +412,7 @@ Page 6 is never used} {rowid 1 missing from index i2}}
} {{*** 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 6 is never used} {row 1 missing from index i2} {row 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 {
@@ -422,7 +422,7 @@ Page 5 is never used}}
} {{*** in database t2 ***
Page 4 is never used
Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2}}
}
do_test pragma-3.19 {
catch {db close}
@@ -431,7 +431,32 @@ Page 6 is never used} {rowid 1 missing from index i2}}
db eval {PRAGMA integrity_check}
} {ok}
}
-#exit
+
+# Verify that PRAGMA integrity_check catches UNIQUE and NOT NULL
+# constraint violations.
+#
+do_execsql_test pragma-3.20 {
+ CREATE TABLE t1(a,b);
+ CREATE INDEX t1a ON t1(a);
+ INSERT INTO t1 VALUES(1,1),(2,2),(3,3),(2,4),(NULL,5),(NULL,6);
+ PRAGMA writable_schema=ON;
+ UPDATE sqlite_master SET sql='CREATE UNIQUE INDEX t1a ON t1(a)'
+ WHERE name='t1a';
+ UPDATE sqlite_master SET sql='CREATE TABLE t1(a NOT NULL,b)'
+ WHERE name='t1';
+ PRAGMA writable_schema=OFF;
+ ALTER TABLE t1 RENAME TO t1x;
+ PRAGMA integrity_check;
+} {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a} {NULL value in t1x.a}}
+do_execsql_test pragma-3.21 {
+ PRAGMA integrity_check(3);
+} {{non-unique entry in index t1a} {NULL value in t1x.a} {non-unique entry in index t1a}}
+do_execsql_test pragma-3.22 {
+ PRAGMA integrity_check(2);
+} {{non-unique entry in index t1a} {NULL value in t1x.a}}
+do_execsql_test pragma-3.21 {
+ PRAGMA integrity_check(1);
+} {{non-unique entry in index t1a}}
# Test modifying the cache_size of an attached database.
ifcapable pager_pragmas&&attach {
@@ -1575,6 +1600,8 @@ do_test pragma-20.8 {
forcedelete data_dir
} ;# endif windows
+database_may_be_corrupt
+
do_test 21.1 {
# Create a corrupt database in testerr.db. And a non-corrupt at test.db.
#
@@ -1600,11 +1627,16 @@ Multiple uses for byte 672 of page 15}
set auxerr {*** in database aux ***
Multiple uses for byte 672 of page 15}
+set mainerr {/{\*\*\* in database main \*\*\*
+Multiple uses for byte 672 of page 15}.*/}
+set auxerr {/{\*\*\* in database aux \*\*\*
+Multiple uses for byte 672 of page 15}.*/}
+
do_test 22.2 {
catch { db close }
sqlite3 db testerr.db
execsql { PRAGMA integrity_check }
-} [list $mainerr]
+} $mainerr
do_test 22.3.1 {
catch { db close }
@@ -1613,13 +1645,13 @@ do_test 22.3.1 {
ATTACH 'testerr.db' AS 'aux';
PRAGMA integrity_check;
}
-} [list $auxerr]
+} $auxerr
do_test 22.3.2 {
execsql { PRAGMA main.integrity_check; }
} {ok}
do_test 22.3.3 {
execsql { PRAGMA aux.integrity_check; }
-} [list $auxerr]
+} $auxerr
do_test 22.4.1 {
catch { db close }
@@ -1628,10 +1660,10 @@ do_test 22.4.1 {
ATTACH 'test.db' AS 'aux';
PRAGMA integrity_check;
}
-} [list $mainerr]
+} $mainerr
do_test 22.4.2 {
execsql { PRAGMA main.integrity_check; }
-} [list $mainerr]
+} $mainerr
do_test 22.4.3 {
execsql { PRAGMA aux.integrity_check; }
} {ok}
@@ -1680,4 +1712,5 @@ do_test 23.5 {
}
} {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE}
+database_never_corrupt
finish_test
diff --git a/test/pragma2.test b/test/pragma2.test
index 1111a98..0dbc977 100644
--- a/test/pragma2.test
+++ b/test/pragma2.test
@@ -22,6 +22,7 @@ source $testdir/tester.tcl
# 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.
+# pragma2-4.*: Tests for PRAGMA cache_spill
#
ifcapable !pragma||!schema_pragmas {
@@ -116,4 +117,92 @@ ifcapable attach {
} {9 9}
}
+# Default setting of PRAGMA cache_spill is always ON
+#
+# EVIDENCE-OF: R-51036-62828 PRAGMA cache_spill; PRAGMA
+# cache_spill=boolean;
+#
+# EVIDENCE-OF: R-23955-02765 Cache_spill is enabled by default
+#
+db close
+delete_file test.db test.db-journal
+delete_file test2.db test2.db-journal
+sqlite3 db test.db
+do_execsql_test pragma2-4.1 {
+ PRAGMA cache_spill;
+ PRAGMA main.cache_spill;
+ PRAGMA temp.cache_spill;
+} {1 1 1}
+do_execsql_test pragma2-4.2 {
+ PRAGMA cache_spill=OFF;
+ PRAGMA cache_spill;
+ PRAGMA main.cache_spill;
+ PRAGMA temp.cache_spill;
+} {0 0 0}
+do_execsql_test pragma2-4.3 {
+ PRAGMA page_size=1024;
+ PRAGMA cache_size=50;
+ BEGIN;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+ INSERT INTO t1 VALUES(1, randomblob(400), 1, randomblob(400));
+ INSERT INTO t1 SELECT a+1, randomblob(400), a+1, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+2, randomblob(400), a+2, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+4, randomblob(400), a+4, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+8, randomblob(400), a+8, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+16, randomblob(400), a+16, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+32, randomblob(400), a+32, randomblob(400) FROM t1;
+ INSERT INTO t1 SELECT a+64, randomblob(400), a+64, randomblob(400) FROM t1;
+ COMMIT;
+ ATTACH 'test2.db' AS aux1;
+ CREATE TABLE aux1.t2(a INTEGER PRIMARY KEY, b, c, d);
+ INSERT INTO t2 SELECT * FROM t1;
+ DETACH aux1;
+ PRAGMA cache_spill=ON;
+} {}
+sqlite3_release_memory
+#
+# EVIDENCE-OF: R-07634-40532 The cache_spill pragma enables or disables
+# the ability of the pager to spill dirty cache pages to the database
+# file in the middle of a transaction.
+#
+do_test pragma2-4.4 {
+ db eval {
+ BEGIN;
+ UPDATE t1 SET c=c+1;
+ PRAGMA lock_status;
+ }
+} {main exclusive temp unknown} ;# EXCLUSIVE lock due to cache spill
+do_test pragma2-4.5 {
+ db eval {
+ COMMIT;
+ PRAGMA cache_spill=OFF;
+ BEGIN;
+ UPDATE t1 SET c=c-1;
+ PRAGMA lock_status;
+ }
+} {main reserved temp unknown} ;# No cache spill, so no exclusive lock
+
+# Verify that newly attached databases inherit the cache_spill=OFF
+# setting.
+#
+do_execsql_test pragma2-4.6 {
+ COMMIT;
+ ATTACH 'test2.db' AS aux1;
+ PRAGMA aux1.cache_size=50;
+ BEGIN;
+ UPDATE t2 SET c=c+1;
+ PRAGMA lock_status;
+} {main unlocked temp unknown aux1 reserved}
+do_execsql_test pragma2-4.7 {
+ COMMIT;
+}
+sqlite3_release_memory
+do_execsql_test pragma2-4.8 {
+ PRAGMA cache_spill=ON; -- Applies to all databases
+ BEGIN;
+ UPDATE t2 SET c=c-1;
+ PRAGMA lock_status;
+} {main unlocked temp unknown aux1 exclusive}
+
+
finish_test
diff --git a/test/printf2.test b/test/printf2.test
new file mode 100644
index 0000000..4cb1783
--- /dev/null
+++ b/test/printf2.test
@@ -0,0 +1,99 @@
+# 2013-12-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 the printf() SQL function.
+#
+#
+# EVIDENCE-OF: R-63057-40065 The printf(FORMAT,...) SQL function works
+# like the sqlite3_mprintf() C-language function and the printf()
+# function from the standard C library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# EVIDENCE-OF: R-40086-60101 If the FORMAT argument is missing or NULL
+# then the result is NULL.
+#
+do_execsql_test printf2-1.1 {
+ SELECT quote(printf()), quote(printf(NULL,1,2,3));
+} {NULL NULL}
+
+
+do_execsql_test printf2-1.2 {
+ SELECT printf('hello');
+} {hello}
+do_execsql_test printf2-1.3 {
+ SELECT printf('%d,%d,%d',55,-11,3421);
+} {55,-11,3421}
+do_execsql_test printf2-1.4 {
+ SELECT printf('%d,%d,%d',55,'-11',3421);
+} {55,-11,3421}
+do_execsql_test printf2-1.5 {
+ SELECT printf('%d,%d,%d,%d',55,'-11',3421);
+} {55,-11,3421,0}
+do_execsql_test printf2-1.6 {
+ SELECT printf('%.2f',3.141592653);
+} {3.14}
+do_execsql_test printf2-1.7 {
+ SELECT printf('%.*f',2,3.141592653);
+} {3.14}
+do_execsql_test printf2-1.8 {
+ SELECT printf('%*.*f',5,2,3.141592653);
+} {{ 3.14}}
+do_execsql_test printf2-1.9 {
+ SELECT printf('%d',314159.2653);
+} {314159}
+do_execsql_test printf2-1.10 {
+ SELECT printf('%lld',314159.2653);
+} {314159}
+do_execsql_test printf2-1.11 {
+ SELECT printf('%lld%n',314159.2653,'hi');
+} {314159}
+
+# EVIDENCE-OF: R-17002-27534 The %z format is interchangeable with %s.
+#
+do_execsql_test printf2-1.12 {
+ SELECT printf('%.*z',5,'abcdefghijklmnop');
+} {abcde}
+do_execsql_test printf2-1.13 {
+ SELECT printf('%c','abcdefghijklmnop');
+} {a}
+
+# EVIDENCE-OF: R-02347-27622 The %n format is silently ignored and does
+# not consume an argument.
+#
+do_execsql_test printf2-2.1 {
+ CREATE TABLE t1(a,b,c);
+ INSERT INTO t1 VALUES(1,2,3);
+ INSERT INTO t1 VALUES(-1,-2,-3);
+ INSERT INTO t1 VALUES('abc','def','ghi');
+ INSERT INTO t1 VALUES(1.5,2.25,3.125);
+ SELECT printf('(%s)-%n-(%s)',a,b,c) FROM t1 ORDER BY rowid;
+} {(1)--(2) (-1)--(-2) (abc)--(def) (1.5)--(2.25)}
+
+# EVIDENCE-OF: R-56064-04001 The %p format is an alias for %X.
+#
+do_execsql_test printf2-2.2 {
+ SELECT printf('%s=(%p)',a,a) FROM t1 ORDER BY a;
+} {-1=(FFFFFFFFFFFFFFFF) 1=(1) 1.5=(1) abc=(0)}
+
+# EVIDENCE-OF: R-29410-53018 If there are too few arguments in the
+# argument list, missing arguments are assumed to have a NULL value,
+# which is translated into 0 or 0.0 for numeric formats or an empty
+# string for %s.
+#
+do_execsql_test printf2-2.3 {
+ SELECT printf('%s=(%d/%g/%s)',a) FROM t1 ORDER BY a;
+} {-1=(0/0/) 1=(0/0/) 1.5=(0/0/) abc=(0/0/)}
+
+
+finish_test
diff --git a/test/progress.test b/test/progress.test
index b25a100..993426a 100644
--- a/test/progress.test
+++ b/test/progress.test
@@ -164,10 +164,12 @@ do_test progress-1.7 {
}
set ::res [list]
+ explain {SELECT a, b, c FROM abc}
db eval {SELECT a, b, c FROM abc} {
lappend ::res $a $b $c
- db progress 10 "expr 1"
+ db progress 5 "expr 1"
catch {db eval {SELECT a, b, c FROM abc} { }} msg
+ db progress 5 "expr 0"
lappend ::res $msg
}
diff --git a/test/queryonly.test b/test/queryonly.test
new file mode 100644
index 0000000..2774e5d
--- /dev/null
+++ b/test/queryonly.test
@@ -0,0 +1,72 @@
+# 2013-07-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 tests the "query_only" pragma.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test queryonly-1.1 {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(123),(456);
+ SELECT a FROM t1 ORDER BY a;
+} {123 456}
+do_execsql_test queryonly-1.2 {
+ PRAGMA query_only;
+} {0}
+do_execsql_test queryonly-1.3 {
+ PRAGMA query_only=ON;
+ PRAGMA query_only;
+} {1}
+do_test queryonly-1.4 {
+ catchsql {INSERT INTO t1 VALUES(789);}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.5 {
+ catchsql {DELETE FROM t1;}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.6 {
+ catchsql {UPDATE t1 SET a=a+1;}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.7 {
+ catchsql {CREATE TABLE t2(b);}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.8 {
+ catchsql {CREATE INDEX t1a ON t1(a);}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.9 {
+ catchsql {DROP TABLE t1;}
+} {1 {attempt to write a readonly database}}
+do_test queryonly-1.10 {
+ catchsql {ANALYZE;}
+} {1 {attempt to write a readonly database}}
+do_execsql_test queryonly-1.11 {
+ SELECT a FROM t1 ORDER BY a;
+} {123 456}
+
+do_execsql_test queryonly-2.2 {
+ PRAGMA query_only;
+} {1}
+do_execsql_test queryonly-2.3 {
+ PRAGMA query_only=OFF;
+ PRAGMA query_only;
+} {0}
+do_execsql_test queryonly-2.4 {
+ INSERT INTO t1 VALUES(789);
+ SELECT a FROM t1 ORDER BY a;
+} {123 456 789}
+do_execsql_test queryonly-2.5 {
+ UPDATE t1 SET a=a+1;
+ SELECT a FROM t1 ORDER BY a;
+} {124 457 790}
+
+finish_test
diff --git a/test/quota.test b/test/quota.test
index 816dec8..f9655fb 100644
--- a/test/quota.test
+++ b/test/quota.test
@@ -361,7 +361,7 @@ foreach file [glob -nocomplain quota-test-A*] {
do_test quota-4.4.1 {
set ::quota {}
sqlite3_quota_set $::quotagroup 10000 quota_callback
- file delete -force ./quota-test-A1.db ./quota-test-A2.db
+ forcedelete ./quota-test-A1.db ./quota-test-A2.db
sqlite3 db ./quota-test-A1.db
db eval {
CREATE TABLE t1(x);
diff --git a/test/quota2.test b/test/quota2.test
index 1482db6..8682bd8 100644
--- a/test/quota2.test
+++ b/test/quota2.test
@@ -25,7 +25,7 @@ db close
sqlite3_quota_initialize "" 1
foreach dir {quota2a/x1 quota2a/x2 quota2a quota2b quota2c} {
- file delete -force $dir
+ forcedelete $dir
}
foreach dir {quota2a quota2a/x1 quota2a/x2 quota2b quota2c} {
file mkdir $dir
diff --git a/test/rdonly.test b/test/rdonly.test
index bf19597..938cc78 100644
--- a/test/rdonly.test
+++ b/test/rdonly.test
@@ -32,6 +32,9 @@ do_test rdonly-1.1 {
SELECT * FROM t1;
}
} {1}
+do_test rdonly-1.1.1 {
+ sqlite3_db_readonly db main
+} {0}
# Changes the write version from 1 to 3. Verify that the database
# can be read but not written.
@@ -47,6 +50,9 @@ do_test rdonly-1.3 {
SELECT * FROM t1;
}
} {1}
+do_test rdonly-1.3.1 {
+ sqlite3_db_readonly db main
+} {1}
do_test rdonly-1.4 {
catchsql {
INSERT INTO t1 VALUES(2)
diff --git a/test/releasetest.tcl b/test/releasetest.tcl
index b635061..eb2e440 100644
--- a/test/releasetest.tcl
+++ b/test/releasetest.tcl
@@ -176,6 +176,12 @@ array set ::Configs {
-DSQLITE_DISABLE_FTS4_DEFERRED
-DSQLITE_ENABLE_RTREE
}
+
+ "No-lookaside" {
+ -DSQLITE_TEST_REALLOC_STRESS=1
+ -DSQLITE_OMIT_LOOKASIDE=1
+ -DHAVE_USLEEP=1
+ }
}
array set ::Platforms {
@@ -188,6 +194,7 @@ array set ::Platforms {
"Extra-Robustness" test
"Device-Two" test
"Ftrapv" test
+ "No-lookaside" test
"Default" "threadtest test"
"Device-One" fulltest
}
diff --git a/test/resolver01.test b/test/resolver01.test
index 3ca6ace..7d95a21 100644
--- a/test/resolver01.test
+++ b/test/resolver01.test
@@ -13,10 +13,18 @@
# figures out what identifiers in the SQL statement refer to) that
# were fixed by ticket [2500cdb9be]
#
+# See also tickets [1c69be2daf] and [f617ea3125] from 2013-08-14.
+#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+# "ORDER BY y" binds to the output result-set column named "y"
+# if available. If no output column is named "y", then try to
+# bind against an input column named "y".
+#
+# This is classical SQL92 behavior.
+#
do_test resolver01-1.1 {
catchsql {
CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(11,22);
@@ -26,14 +34,175 @@ do_test resolver01-1.1 {
} {0 1}
do_test resolver01-1.2 {
catchsql {
+ SELECT 1 AS yy FROM t1, t2 ORDER BY y;
+ }
+} {1 {ambiguous column name: y}}
+do_test resolver01-1.3 {
+ catchsql {
+ CREATE TABLE t3(x,y); INSERT INTO t3 VALUES(11,44),(33,22);
+ SELECT x AS y FROM t3 ORDER BY y;
+ }
+} {0 {11 33}}
+do_test resolver01-1.4 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY y;
+ }
+} {0 {33 11}}
+
+# SQLite allows the WHERE clause to reference output columns if there is
+# no other way to resolve the name.
+#
+do_test resolver01-1.5 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY yy;
+ }
+} {0 {11 33}}
+do_test resolver01-1.6 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY 1;
+ }
+} {0 {11 33}}
+
+# The "ORDER BY y COLLATE nocase" form works the same as "ORDER BY y".
+# The "y" binds more tightly to output columns than to input columns.
+#
+# This is for compatibility with SQL92 and with historical SQLite behavior.
+# Note that PostgreSQL considers "y COLLATE nocase" to be an expression
+# and thus PostgreSQL treats this case as if it where the 3.x case below.
+#
+do_test resolver01-2.1 {
+ catchsql {
SELECT 2 AS y FROM t1, t2 ORDER BY y COLLATE nocase;
}
} {0 2}
-do_test resolver01-1.3 {
+do_test resolver01-2.2 {
+ catchsql {
+ SELECT 2 AS yy FROM t1, t2 ORDER BY y COLLATE nocase;
+ }
+} {1 {ambiguous column name: y}}
+do_test resolver01-2.3 {
+ catchsql {
+ SELECT x AS y FROM t3 ORDER BY y COLLATE nocase;
+ }
+} {0 {11 33}}
+do_test resolver01-2.4 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY y COLLATE nocase;
+ }
+} {0 {33 11}}
+do_test resolver01-2.5 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY yy COLLATE nocase;
+ }
+} {0 {11 33}}
+do_test resolver01-2.6 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY 1 COLLATE nocase;
+ }
+} {0 {11 33}}
+
+# But if the form is "ORDER BY expr" then bind more tightly to the
+# the input column names and only use the output column names if no
+# input column name matches.
+#
+# This is SQL99 behavior, as implemented by PostgreSQL and MS-SQL.
+# Note that Oracle works differently.
+#
+do_test resolver01-3.1 {
catchsql {
SELECT 3 AS y FROM t1, t2 ORDER BY +y;
}
-} {0 3}
+} {1 {ambiguous column name: y}}
+do_test resolver01-3.2 {
+ catchsql {
+ SELECT 2 AS yy FROM t1, t2 ORDER BY +y;
+ }
+} {1 {ambiguous column name: y}}
+do_test resolver01-3.3 {
+ catchsql {
+ SELECT x AS y FROM t3 ORDER BY +y;
+ }
+} {0 {33 11}}
+do_test resolver01-3.4 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY +y;
+ }
+} {0 {33 11}}
+do_test resolver01-3.5 {
+ catchsql {
+ SELECT x AS yy FROM t3 ORDER BY +yy
+ }
+} {0 {11 33}}
+
+# This is the test case given in ticket [f617ea3125e9] (with table name
+# changed from "t1" to "t4". The behavior of (1) and (3) match with
+# PostgreSQL, but we intentionally break with PostgreSQL to provide
+# SQL92 behavior for case (2).
+#
+do_execsql_test resolver01-4.1 {
+ CREATE TABLE t4(m CHAR(2));
+ INSERT INTO t4 VALUES('az');
+ INSERT INTO t4 VALUES('by');
+ INSERT INTO t4 VALUES('cx');
+ SELECT '1', substr(m,2) AS m FROM t4 ORDER BY m;
+ SELECT '2', substr(m,2) AS m FROM t4 ORDER BY m COLLATE binary;
+ SELECT '3', substr(m,2) AS m FROM t4 ORDER BY lower(m);
+} {1 x 1 y 1 z 2 x 2 y 2 z 3 z 3 y 3 x}
+
+##########################################################################
+# Test cases for ticket [1c69be2dafc28]: Make sure the GROUP BY binds
+# more tightly to the input tables in all cases.
+#
+# This first case case has been wrong in SQLite for time out of mind.
+# For SQLite version 3.7.17 the answer was two rows, which is wrong.
+#
+do_execsql_test resolver01-5.1 {
+ CREATE TABLE t5(m CHAR(2));
+ INSERT INTO t5 VALUES('ax');
+ INSERT INTO t5 VALUES('bx');
+ INSERT INTO t5 VALUES('cy');
+ SELECT count(*), substr(m,2,1) AS m FROM t5 GROUP BY m ORDER BY 1, 2;
+} {1 x 1 x 1 y}
+
+# This case is unambiguous and has always been correct.
+#
+do_execsql_test resolver01-5.2 {
+ SELECT count(*), substr(m,2,1) AS mx FROM t5 GROUP BY m ORDER BY 1, 2;
+} {1 x 1 x 1 y}
+
+# This case is not allowed in standard SQL, but SQLite allows and does
+# the sensible thing.
+#
+do_execsql_test resolver01-5.3 {
+ SELECT count(*), substr(m,2,1) AS mx FROM t5 GROUP BY mx ORDER BY 1, 2;
+} {1 y 2 x}
+do_execsql_test resolver01-5.4 {
+ SELECT count(*), substr(m,2,1) AS mx FROM t5
+ GROUP BY substr(m,2,1) ORDER BY 1, 2;
+} {1 y 2 x}
+
+# These test case weere provided in the 2013-08-14 email from Rob Golsteijn
+# that originally reported the problem of ticket [1c69be2dafc28].
+#
+do_execsql_test resolver01-6.1 {
+ CREATE TABLE t61(name);
+ SELECT min(name) FROM t61 GROUP BY lower(name);
+} {}
+do_execsql_test resolver01-6.2 {
+ SELECT min(name) AS name FROM t61 GROUP BY lower(name);
+} {}
+do_execsql_test resolver01-6.3 {
+ CREATE TABLE t63(name);
+ INSERT INTO t63 VALUES (NULL);
+ INSERT INTO t63 VALUES ('abc');
+ SELECT count(),
+ NULLIF(name,'abc') AS name
+ FROM t63
+ GROUP BY lower(name);
+} {1 {} 1 {}}
+
+
+
finish_test
diff --git a/test/rollback.test b/test/rollback.test
index fc123ab..c339c5d 100644
--- a/test/rollback.test
+++ b/test/rollback.test
@@ -54,7 +54,7 @@ ifcapable conflict {
catchsql {
INSERT INTO t3 SELECT a FROM t1;
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: t3.a}}
# Try to continue with the SELECT statement
#
diff --git a/test/rowid.test b/test/rowid.test
index 5daf581..6d068d7 100644
--- a/test/rowid.test
+++ b/test/rowid.test
@@ -12,7 +12,9 @@
# focus of this file is testing the magic ROWID column that is
# found on all tables.
#
-# $Id: rowid.test,v 1.21 2009/06/26 15:14:55 drh Exp $
+# EVIDENCE-OF: R-36924-43758 By default, every row in SQLite has a
+# special column, usually called the "rowid", that uniquely identifies
+# that row within the table.
set testdir [file dirname $argv0]
source $testdir/tester.tcl
diff --git a/test/run-wordcount.sh b/test/run-wordcount.sh
new file mode 100644
index 0000000..1755d58
--- /dev/null
+++ b/test/run-wordcount.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# This script runs the wordcount program in different ways, comparing
+# the output from each.
+#
+
+# Select the source text to be analyzed.
+#
+if test "x$1" = "x";
+then echo "Usage: $0 FILENAME [ARGS...]"; exit 1;
+fi
+
+# Do test runs
+#
+rm -f wcdb1.db
+./wordcount --timer --summary wcdb1.db $* --insert >wc-out.txt
+mv wc-out.txt wc-baseline.txt
+rm -f wcdb2.db
+./wordcount --timer --summary wcdb2.db $* --insert --without-rowid >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+rm -f wcdb1.db
+./wordcount --timer --summary wcdb1.db $* --replace >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+rm -f wcdb2.db
+./wordcount --timer --summary wcdb2.db $* --replace --without-rowid >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+rm -f wcdb1.db
+./wordcount --timer --summary wcdb1.db $* --select >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+rm -f wcdb2.db
+./wordcount --timer --summary wcdb2.db $* --select --without-rowid >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+./wordcount --timer --summary wcdb1.db $* --query >wc-out.txt
+mv wc-out.txt wc-baseline.txt
+./wordcount --timer --summary wcdb2.db $* --query --without-rowid >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+./wordcount --timer --summary wcdb1.db $* --delete >wc-out.txt
+mv wc-out.txt wc-baseline.txt
+./wordcount --timer --summary wcdb2.db $* --delete --without-rowid >wc-out.txt
+ if cmp -s wc-out.txt wc-baseline.txt;
+ then echo hi >/dev/null;
+ else echo ERROR:;
+ diff -u wc-baseline.txt wc-out.txt;
+ fi
+
+
+# Clean up temporary files created.
+#
+rm -rf wcdb1.db wcdb2.db wc-out.txt wc-baseline.txt
diff --git a/test/savepoint.test b/test/savepoint.test
index 015d97f..9f4571a 100644
--- a/test/savepoint.test
+++ b/test/savepoint.test
@@ -858,7 +858,7 @@ do_test savepoint-12.2 {
SAVEPOINT sp2;
INSERT OR ROLLBACK INTO t4 VALUES(1, 'one');
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t4.a}}
do_test savepoint-12.3 {
sqlite3_get_autocommit db
} {1}
diff --git a/test/schema5.test b/test/schema5.test
index 6dea5e8..29df3f1 100644
--- a/test/schema5.test
+++ b/test/schema5.test
@@ -30,7 +30,7 @@ do_test schema5-1.1 {
} {1 2 3}
do_test schema5-1.2 {
catchsql {INSERT INTO t1 VALUES(1,3,4);}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test schema5-1.3 {
db eval {
DROP TABLE t1;
@@ -44,7 +44,7 @@ do_test schema5-1.3 {
} {1 2 3}
do_test schema5-1.4 {
catchsql {INSERT INTO t1 VALUES(10,11,12);}
-} {1 {constraint two failed}}
+} {1 {CHECK constraint failed: two}}
do_test schema5-1.5 {
db eval {
DROP TABLE t1;
@@ -57,10 +57,10 @@ do_test schema5-1.5 {
} {}
do_test schema5-1.6 {
catchsql {INSERT INTO t1 VALUES(1,3,4)}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test schema5-1.7 {
catchsql {INSERT INTO t1 VALUES(10,2,3)}
-} {1 {columns b, c are not unique}}
+} {1 {UNIQUE constraint failed: t1.b, t1.c}}
diff --git a/test/securedel.test b/test/securedel.test
index 7ff5a62..7111e08 100644
--- a/test/securedel.test
+++ b/test/securedel.test
@@ -47,7 +47,6 @@ do_test securedel-1.3 {
}
} {0 0}
do_test securedel-1.4 {
-breakpoint
db eval {
PRAGMA secure_delete=ON;
PRAGMA db2.secure_delete;
diff --git a/test/securedel2.test b/test/securedel2.test
index b20f4f9..9badc56 100644
--- a/test/securedel2.test
+++ b/test/securedel2.test
@@ -92,4 +92,3 @@ do_test 1.6.2 {
} {0}
finish_test
-
diff --git a/test/select1.test b/test/select1.test
index 852b52e..875c87c 100644
--- a/test/select1.test
+++ b/test/select1.test
@@ -542,7 +542,7 @@ 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
+ regsub -all {sq_[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 {
diff --git a/test/select4.test b/test/select4.test
index e205b37..8fc200d 100644
--- a/test/select4.test
+++ b/test/select4.test
@@ -824,4 +824,40 @@ do_test select4-13.1 {
}
} {1 2}
+# 2014-02-18: Make sure compound SELECTs work with VALUES clauses
+#
+do_execsql_test select4-14.1 {
+ CREATE TABLE t14(a,b,c);
+ INSERT INTO t14 VALUES(1,2,3),(4,5,6);
+ SELECT * FROM t14 INTERSECT VALUES(3,2,1),(2,3,1),(1,2,3),(2,1,3);
+} {1 2 3}
+do_execsql_test select4-14.2 {
+ SELECT * FROM t14 INTERSECT VALUES(1,2,3);
+} {1 2 3}
+do_execsql_test select4-14.3 {
+ SELECT * FROM t14
+ UNION VALUES(3,2,1),(2,3,1),(1,2,3),(7,8,9),(4,5,6)
+ UNION SELECT * FROM t14 ORDER BY 1, 2, 3
+} {1 2 3 2 3 1 3 2 1 4 5 6 7 8 9}
+do_execsql_test select4-14.4 {
+ SELECT * FROM t14
+ UNION VALUES(3,2,1)
+ UNION SELECT * FROM t14 ORDER BY 1, 2, 3
+} {1 2 3 3 2 1 4 5 6}
+do_execsql_test select4-14.5 {
+ SELECT * FROM t14 EXCEPT VALUES(3,2,1),(2,3,1),(1,2,3),(2,1,3);
+} {4 5 6}
+do_execsql_test select4-14.6 {
+ SELECT * FROM t14 EXCEPT VALUES(1,2,3)
+} {4 5 6}
+do_execsql_test select4-14.7 {
+ SELECT * FROM t14 EXCEPT VALUES(1,2,3) EXCEPT VALUES(4,5,6)
+} {}
+do_execsql_test select4-14.8 {
+ SELECT * FROM t14 EXCEPT VALUES('a','b','c') EXCEPT VALUES(4,5,6)
+} {1 2 3}
+do_execsql_test select4-14.9 {
+ SELECT * FROM t14 UNION ALL VALUES(3,2,1),(2,3,1),(1,2,3),(2,1,3);
+} {1 2 3 4 5 6 3 2 1 2 3 1 1 2 3 2 1 3}
+
finish_test
diff --git a/test/select7.test b/test/select7.test
index e8fc440..6816b9f 100644
--- a/test/select7.test
+++ b/test/select7.test
@@ -138,21 +138,23 @@ ifcapable {subquery && compound} {
# 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
+if {[clang_sanitize_address]==0} {
+ 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}}
}
- 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}}
}
}
diff --git a/test/select9.test b/test/select9.test
index 9f54014..4c42236 100644
--- a/test/select9.test
+++ b/test/select9.test
@@ -450,5 +450,23 @@ do_test select9-5.3 {
}
} {/SCAN TABLE/} ;# Full table scan if the "+x" prevents index usage.
+# 2013-07-09: Ticket [490a4b7235624298]:
+# "WHERE 0" on the first element of a UNION causes an assertion fault
+#
+do_execsql_test select9-6.1 {
+ CREATE TABLE t61(a);
+ CREATE TABLE t62(b);
+ INSERT INTO t61 VALUES(111);
+ INSERT INTO t62 VALUES(222);
+ SELECT a FROM t61 WHERE 0 UNION SELECT b FROM t62;
+} {222}
+do_execsql_test select9-6.2 {
+ SELECT a FROM t61 WHERE 0 UNION ALL SELECT b FROM t62;
+} {222}
+do_execsql_test select9-6.3 {
+ SELECT a FROM t61 UNION SELECT b FROM t62 WHERE 0;
+} {111}
+
+
finish_test
diff --git a/test/selectA.test b/test/selectA.test
index 5fd2288..6e593e8 100644
--- a/test/selectA.test
+++ b/test/selectA.test
@@ -21,6 +21,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix selectA
ifcapable !compound {
finish_test
@@ -1292,5 +1293,86 @@ do_test selectA-3.97 {
ORDER BY y COLLATE NOCASE DESC,x,z)))
}
} {MAD}
+do_execsql_test selectA-3.98 {
+ WITH RECURSIVE
+ xyz(n) AS (
+ 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)))
+ UNION ALL
+ SELECT n || '+' FROM xyz WHERE length(n)<5
+ )
+ SELECT n FROM xyz ORDER BY +n;
+} {MAD MAD+ MAD++}
+
+#-------------------------------------------------------------------------
+# At one point the following code exposed a temp register reuse problem.
+#
+proc f {args} { return 1 }
+db func f f
+
+do_execsql_test 4.1.1 {
+ CREATE TABLE t4(a, b);
+ CREATE TABLE t5(c, d);
+
+ INSERT INTO t5 VALUES(1, 'x');
+ INSERT INTO t5 VALUES(2, 'x');
+ INSERT INTO t4 VALUES(3, 'x');
+ INSERT INTO t4 VALUES(4, 'x');
+
+ CREATE INDEX i1 ON t4(a);
+ CREATE INDEX i2 ON t5(c);
+}
+
+do_eqp_test 4.1.2 {
+ SELECT c, d FROM t5
+ UNION ALL
+ SELECT a, b FROM t4 WHERE f()==f()
+ ORDER BY 1,2
+} {
+ 1 0 0 {SCAN TABLE t5 USING INDEX i2}
+ 1 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
+ 2 0 0 {SCAN TABLE t4 USING INDEX i1}
+ 2 0 0 {USE TEMP B-TREE FOR RIGHT PART OF ORDER BY}
+ 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
+}
+
+do_execsql_test 4.1.3 {
+ SELECT c, d FROM t5
+ UNION ALL
+ SELECT a, b FROM t4 WHERE f()==f()
+ ORDER BY 1,2
+} {
+ 1 x 2 x 3 x 4 x
+}
+
+do_execsql_test 4.2.1 {
+ CREATE TABLE t6(a, b);
+ CREATE TABLE t7(c, d);
+
+ INSERT INTO t7 VALUES(2, 9);
+ INSERT INTO t6 VALUES(3, 0);
+ INSERT INTO t6 VALUES(4, 1);
+ INSERT INTO t7 VALUES(5, 6);
+ INSERT INTO t6 VALUES(6, 0);
+ INSERT INTO t7 VALUES(7, 6);
+
+ CREATE INDEX i6 ON t6(a);
+ CREATE INDEX i7 ON t7(c);
+}
+
+do_execsql_test 4.2.2 {
+ SELECT c, f(d,c,d,c,d) FROM t7
+ UNION ALL
+ SELECT a, b FROM t6
+ ORDER BY 1,2
+} {/2 . 3 . 4 . 5 . 6 . 7 ./}
+
finish_test
diff --git a/test/selectF.test b/test/selectF.test
new file mode 100644
index 0000000..3fb226e
--- /dev/null
+++ b/test/selectF.test
@@ -0,0 +1,49 @@
+# 2014-03-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 verifies that an OP_Copy operation is used instead of OP_SCopy
+# in a compound select in a case where the source register might be changed
+# before the copy is used.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix selectF
+
+do_execsql_test 1 {
+ BEGIN TRANSACTION;
+ CREATE TABLE t1(a, b, c);
+ INSERT INTO "t1" VALUES(1,'one','I');
+ CREATE TABLE t2(d, e, f);
+ INSERT INTO "t2" VALUES(5,'ten','XX');
+ INSERT INTO "t2" VALUES(6,NULL,NULL);
+
+ CREATE INDEX i1 ON t1(b, a);
+ COMMIT;
+}
+
+#explain_i {
+# SELECT * FROM t2
+# UNION ALL
+# SELECT * FROM t1 WHERE a<5
+# ORDER BY 2, 1
+#}
+
+do_execsql_test 2 {
+ SELECT * FROM t2
+ UNION ALL
+ SELECT * FROM t1 WHERE a<5
+ ORDER BY 2, 1
+} {6 {} {} 1 one I 5 ten XX}
+
+
+
+finish_test
diff --git a/test/shared3.test b/test/shared3.test
index 783ae6a..acc86d2 100644
--- a/test/shared3.test
+++ b/test/shared3.test
@@ -13,6 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix shared3
db close
ifcapable !shared_cache {
@@ -103,5 +104,39 @@ db1 close
db2 close
db3 close
+#-------------------------------------------------------------------------
+# At one point this was causing a faulty assert to fail.
+#
+forcedelete test.db
+sqlite3 db test.db
+sqlite3 db2 test.db
+do_execsql_test 3.1 {
+ PRAGMA auto_vacuum = 2;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES(randomblob(500), randomblob(500));
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+ INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1;
+}
+do_test 3.2 {
+ execsql { SELECT count(*) FROM sqlite_master } db2
+} {1}
+do_execsql_test 3.3 {
+ BEGIN;
+ DELETE FROM t1 WHERE 1;
+ PRAGMA incremental_vacuum;
+} {}
+do_test 3.4 {
+ execsql { SELECT count(*) FROM sqlite_master } db2
+} {1}
+do_test 3.5 {
+ execsql { COMMIT }
+} {}
+
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
+
diff --git a/test/shared7.test b/test/shared7.test
index 5c4a1da..9f4880f 100644
--- a/test/shared7.test
+++ b/test/shared7.test
@@ -23,6 +23,9 @@ do_test shared7-1.1 {
sqlite3_enable_shared_cache
} {1}
+# EVIDENCE-OF: R-05098-06501 In shared cache mode, attempting to attach
+# the same database file more than once results in an error.
+#
do_test shared7-1.2 {
db close
sqlite3 db test.db
diff --git a/test/shared8.test b/test/shared8.test
index 600e02b..73f0d47 100644
--- a/test/shared8.test
+++ b/test/shared8.test
@@ -110,4 +110,3 @@ do_test 1.8 {
foreach db {db1 db2 db3 db4} { catch { $db close } }
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
-
diff --git a/test/sharedlock.test b/test/sharedlock.test
index 1e78eea..caa48f5 100644
--- a/test/sharedlock.test
+++ b/test/sharedlock.test
@@ -13,6 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix sharedlock
db close
ifcapable !shared_cache {
@@ -47,9 +48,35 @@ do_test sharedlock-1.2 {
set res
} {1 one 2 two 3 three}
+
+#-------------------------------------------------------------------------
+# Test that a write-lock is taken on a table when its entire contents
+# are deleted using the OP_Clear optimization.
+#
+foreach {tn delete_sql} {
+ 1 { DELETE FROM t2 WHERE 1 }
+ 2 { DELETE FROM t2 }
+} {
+ do_execsql_test 2.1 {
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t2(x, y);
+ INSERT INTO t2 VALUES(1, 2);
+ INSERT INTO t2 VALUES(3, 4);
+ }
+
+ do_test 2.2 { execsql { SELECT * FROM t2 } db2 } {1 2 3 4}
+
+ do_execsql_test 2.3 " BEGIN; $delete_sql; "
+
+ do_test 2.4 {
+ catchsql { SELECT * FROM t2 } db2
+ } {1 {database table is locked: t2}}
+
+ do_execsql_test 2.5 COMMIT
+}
+
+
db close
db2 close
-
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
-
diff --git a/test/shell1.test b/test/shell1.test
index c60f3af..ab382e7 100644
--- a/test/shell1.test
+++ b/test/shell1.test
@@ -105,7 +105,7 @@ do_test shell1-1.7.1 {
set rc [lindex $res 0]
list $rc \
[regexp {SQLite version} $res] \
- [regexp {Enter SQL statements} $res]
+ [regexp {Enter ".help" for usage hints} $res]
} {0 1 1}
# -batch force batch I/O
@@ -268,7 +268,7 @@ do_test shell1-3.1.4 {
# .bail ON|OFF Stop after hitting an error. Default OFF
do_test shell1-3.2.1 {
catchcmd "test.db" ".bail"
-} {1 {Error: unknown command or invalid arguments: "bail". Enter ".help" for help}}
+} {1 {Usage: .bail on|off}}
do_test shell1-3.2.2 {
catchcmd "test.db" ".bail ON"
} {0 {}}
@@ -278,16 +278,16 @@ do_test shell1-3.2.3 {
do_test shell1-3.2.4 {
# too many arguments
catchcmd "test.db" ".bail OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "bail". Enter ".help" for help}}
+} {1 {Usage: .bail on|off}}
# .databases List names and files of attached databases
do_test shell1-3.3.1 {
catchcmd "-csv test.db" ".databases"
} "/0 +.*main +[string map {/ .} [string range [get_pwd] 0 10]].*/"
do_test shell1-3.3.2 {
- # too many arguments
+ # extra arguments ignored
catchcmd "test.db" ".databases BAD"
-} {1 {Error: unknown command or invalid arguments: "databases". Enter ".help" for help}}
+} "/0 +.*main +[string map {/ .} [string range [get_pwd] 0 10]].*/"
# .dump ?TABLE? ... Dump the database in an SQL text format
# If TABLE specified, only dump tables matching
@@ -305,12 +305,12 @@ do_test shell1-3.4.2 {
do_test shell1-3.4.3 {
# too many arguments
catchcmd "test.db" ".dump FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "dump". Enter ".help" for help}}
+} {1 {Usage: .dump ?LIKE-PATTERN?}}
# .echo ON|OFF Turn command echo on or off
do_test shell1-3.5.1 {
catchcmd "test.db" ".echo"
-} {1 {Error: unknown command or invalid arguments: "echo". Enter ".help" for help}}
+} {1 {Usage: .echo on|off}}
do_test shell1-3.5.2 {
catchcmd "test.db" ".echo ON"
} {0 {}}
@@ -320,7 +320,7 @@ do_test shell1-3.5.3 {
do_test shell1-3.5.4 {
# too many arguments
catchcmd "test.db" ".echo OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "echo". Enter ".help" for help}}
+} {1 {Usage: .echo on|off}}
# .exit Exit this program
do_test shell1-3.6.1 {
@@ -339,15 +339,15 @@ do_test shell1-3.7.3 {
catchcmd "test.db" ".explain OFF"
} {0 {}}
do_test shell1-3.7.4 {
- # too many arguments
+ # extra arguments ignored
catchcmd "test.db" ".explain OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "explain". Enter ".help" for help}}
+} {0 {}}
# .header(s) ON|OFF Turn display of headers on or off
do_test shell1-3.9.1 {
catchcmd "test.db" ".header"
-} {1 {Error: unknown command or invalid arguments: "header". Enter ".help" for help}}
+} {1 {Usage: .headers on|off}}
do_test shell1-3.9.2 {
catchcmd "test.db" ".header ON"
} {0 {}}
@@ -357,11 +357,11 @@ do_test shell1-3.9.3 {
do_test shell1-3.9.4 {
# too many arguments
catchcmd "test.db" ".header OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "header". Enter ".help" for help}}
+} {1 {Usage: .headers on|off}}
do_test shell1-3.9.5 {
catchcmd "test.db" ".headers"
-} {1 {Error: unknown command or invalid arguments: "headers". Enter ".help" for help}}
+} {1 {Usage: .headers on|off}}
do_test shell1-3.9.6 {
catchcmd "test.db" ".headers ON"
} {0 {}}
@@ -371,7 +371,7 @@ do_test shell1-3.9.7 {
do_test shell1-3.9.8 {
# too many arguments
catchcmd "test.db" ".headers OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "headers". Enter ".help" for help}}
+} {1 {Usage: .headers on|off}}
# .help Show this message
do_test shell1-3.10.1 {
@@ -393,17 +393,17 @@ do_test shell1-3.10.2 {
# .import FILE TABLE Import data from FILE into TABLE
do_test shell1-3.11.1 {
catchcmd "test.db" ".import"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
+} {1 {Usage: .import FILE TABLE}}
do_test shell1-3.11.2 {
catchcmd "test.db" ".import FOO"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
-do_test shell1-3.11.2 {
- catchcmd "test.db" ".import FOO BAR"
-} {1 {Error: no such table: BAR}}
+} {1 {Usage: .import FILE TABLE}}
+#do_test shell1-3.11.2 {
+# catchcmd "test.db" ".import FOO BAR"
+#} {1 {Error: no such table: BAR}}
do_test shell1-3.11.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
+} {1 {Usage: .import FILE TABLE}}
# .indices ?TABLE? Show names of all indices
# If TABLE specified, only show indices for tables
@@ -417,7 +417,7 @@ do_test shell1-3.12.2 {
do_test shell1-3.12.3 {
# too many arguments
catchcmd "test.db" ".indices FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "indices". Enter ".help" for help}}
+} {1 {Usage: .indices ?LIKE-PATTERN?}}
# .mode MODE ?TABLE? Set output mode where MODE is one of:
# csv Comma-separated values
@@ -430,7 +430,7 @@ do_test shell1-3.12.3 {
# tcl TCL list elements
do_test shell1-3.13.1 {
catchcmd "test.db" ".mode"
-} {1 {Error: unknown command or invalid arguments: "mode". Enter ".help" for help}}
+} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
do_test shell1-3.13.2 {
catchcmd "test.db" ".mode FOO"
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
@@ -459,9 +459,9 @@ do_test shell1-3.13.10 {
catchcmd "test.db" ".mode tcl"
} {0 {}}
do_test shell1-3.13.11 {
- # too many arguments
+ # extra arguments ignored
catchcmd "test.db" ".mode tcl BAD"
-} {1 {Error: invalid arguments: "BAD". Enter ".help" for help}}
+} {0 {}}
# don't allow partial mode type matches
do_test shell1-3.13.12 {
@@ -472,31 +472,31 @@ do_test shell1-3.13.13 {
} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
do_test shell1-3.13.14 {
catchcmd "test.db" ".mode lin"
-} {1 {Error: mode should be one of: column csv html insert line list tabs tcl}}
+} {0 {}}
# .nullvalue STRING Print STRING in place of NULL values
do_test shell1-3.14.1 {
catchcmd "test.db" ".nullvalue"
-} {1 {Error: unknown command or invalid arguments: "nullvalue". Enter ".help" for help}}
+} {1 {Usage: .nullvalue STRING}}
do_test shell1-3.14.2 {
catchcmd "test.db" ".nullvalue FOO"
} {0 {}}
do_test shell1-3.14.3 {
# too many arguments
catchcmd "test.db" ".nullvalue FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "nullvalue". Enter ".help" for help}}
+} {1 {Usage: .nullvalue STRING}}
# .output FILENAME Send output to FILENAME
do_test shell1-3.15.1 {
catchcmd "test.db" ".output"
-} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}}
+} {0 {}}
do_test shell1-3.15.2 {
catchcmd "test.db" ".output FOO"
} {0 {}}
do_test shell1-3.15.3 {
# too many arguments
catchcmd "test.db" ".output FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}}
+} {1 {Usage: .output FILE}}
# .output stdout Send output to the screen
do_test shell1-3.16.1 {
@@ -505,12 +505,12 @@ do_test shell1-3.16.1 {
do_test shell1-3.16.2 {
# too many arguments
catchcmd "test.db" ".output stdout BAD"
-} {1 {Error: unknown command or invalid arguments: "output". Enter ".help" for help}}
+} {1 {Usage: .output FILE}}
# .prompt MAIN CONTINUE Replace the standard prompts
do_test shell1-3.17.1 {
catchcmd "test.db" ".prompt"
-} {1 {Error: unknown command or invalid arguments: "prompt". Enter ".help" for help}}
+} {0 {}}
do_test shell1-3.17.2 {
catchcmd "test.db" ".prompt FOO"
} {0 {}}
@@ -520,7 +520,7 @@ do_test shell1-3.17.3 {
do_test shell1-3.17.4 {
# too many arguments
catchcmd "test.db" ".prompt FOO BAR BAD"
-} {1 {Error: unknown command or invalid arguments: "prompt". Enter ".help" for help}}
+} {0 {}}
# .quit Exit this program
do_test shell1-3.18.1 {
@@ -529,25 +529,25 @@ do_test shell1-3.18.1 {
do_test shell1-3.18.2 {
# too many arguments
catchcmd "test.db" ".quit BAD"
-} {1 {Error: unknown command or invalid arguments: "quit". Enter ".help" for help}}
+} {0 {}}
# .read FILENAME Execute SQL in FILENAME
do_test shell1-3.19.1 {
catchcmd "test.db" ".read"
-} {1 {Error: unknown command or invalid arguments: "read". Enter ".help" for help}}
+} {1 {Usage: .read FILE}}
do_test shell1-3.19.2 {
- file delete -force FOO
+ forcedelete FOO
catchcmd "test.db" ".read FOO"
} {1 {Error: cannot open "FOO"}}
do_test shell1-3.19.3 {
# too many arguments
catchcmd "test.db" ".read FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "read". Enter ".help" for help}}
+} {1 {Usage: .read FILE}}
# .restore ?DB? FILE Restore content of DB (default "main") from FILE
do_test shell1-3.20.1 {
catchcmd "test.db" ".restore"
-} {1 {Error: unknown command or invalid arguments: "restore". Enter ".help" for help}}
+} {1 {Usage: .restore ?DB? FILE}}
do_test shell1-3.20.2 {
catchcmd "test.db" ".restore FOO"
} {0 {}}
@@ -557,7 +557,7 @@ do_test shell1-3.20.3 {
do_test shell1-3.20.4 {
# too many arguments
catchcmd "test.db" ".restore FOO BAR BAD"
-} {1 {Error: unknown command or invalid arguments: "restore". Enter ".help" for help}}
+} {1 {Usage: .restore ?DB? FILE}}
# .schema ?TABLE? Show the CREATE statements
# If TABLE specified, only show tables matching
@@ -571,7 +571,7 @@ do_test shell1-3.21.2 {
do_test shell1-3.21.3 {
# too many arguments
catchcmd "test.db" ".schema FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "schema". Enter ".help" for help}}
+} {1 {Usage: .schema ?LIKE-PATTERN?}}
do_test shell1-3.21.4 {
catchcmd "test.db" {
@@ -588,14 +588,17 @@ db eval {DROP VIEW v1; DROP VIEW v2; DROP TABLE t1;}
# .separator STRING Change separator used by output mode and .import
do_test shell1-3.22.1 {
catchcmd "test.db" ".separator"
-} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}}
+} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
do_test shell1-3.22.2 {
catchcmd "test.db" ".separator FOO"
} {0 {}}
do_test shell1-3.22.3 {
+ catchcmd "test.db" ".separator ABC XYZ"
+} {0 {}}
+do_test shell1-3.22.4 {
# too many arguments
- catchcmd "test.db" ".separator FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}}
+ catchcmd "test.db" ".separator FOO BAD BAD2"
+} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
# .show Show the current values for various settings
do_test shell1-3.23.1 {
@@ -613,12 +616,12 @@ do_test shell1-3.23.1 {
do_test shell1-3.23.2 {
# too many arguments
catchcmd "test.db" ".show BAD"
-} {1 {Error: unknown command or invalid arguments: "show". Enter ".help" for help}}
+} {1 {Usage: .show}}
# .stats ON|OFF Turn stats on or off
do_test shell1-3.23b.1 {
catchcmd "test.db" ".stats"
-} {1 {Error: unknown command or invalid arguments: "stats". Enter ".help" for help}}
+} {1 {Usage: .stats on|off}}
do_test shell1-3.23b.2 {
catchcmd "test.db" ".stats ON"
} {0 {}}
@@ -628,7 +631,7 @@ do_test shell1-3.23b.3 {
do_test shell1-3.23b.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "stats". Enter ".help" for help}}
+} {1 {Usage: .stats on|off}}
# .tables ?TABLE? List names of tables
# If TABLE specified, only list tables matching
@@ -642,12 +645,12 @@ do_test shell1-3.24.2 {
do_test shell1-3.24.3 {
# too many arguments
catchcmd "test.db" ".tables FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "tables". Enter ".help" for help}}
+} {0 {}}
# .timeout MS Try opening locked tables for MS milliseconds
do_test shell1-3.25.1 {
catchcmd "test.db" ".timeout"
-} {1 {Error: unknown command or invalid arguments: "timeout". Enter ".help" for help}}
+} {0 {}}
do_test shell1-3.25.2 {
catchcmd "test.db" ".timeout zzz"
# this should be treated the same as a '0' timeout
@@ -658,12 +661,12 @@ do_test shell1-3.25.3 {
do_test shell1-3.25.4 {
# too many arguments
catchcmd "test.db" ".timeout 1 BAD"
-} {1 {Error: unknown command or invalid arguments: "timeout". Enter ".help" for help}}
+} {0 {}}
# .width NUM NUM ... Set column widths for "column" mode
do_test shell1-3.26.1 {
catchcmd "test.db" ".width"
-} {1 {Error: unknown command or invalid arguments: "width". Enter ".help" for help}}
+} {0 {}}
do_test shell1-3.26.2 {
catchcmd "test.db" ".width xxx"
# this should be treated the same as a '0' width for col 1
@@ -689,7 +692,7 @@ do_test shell1-3.26.6 {
# .timer ON|OFF Turn the CPU timer measurement on or off
do_test shell1-3.27.1 {
catchcmd "test.db" ".timer"
-} {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}}
+} {1 {Usage: .timer on|off}}
do_test shell1-3.27.2 {
catchcmd "test.db" ".timer ON"
} {0 {}}
@@ -699,7 +702,7 @@ do_test shell1-3.27.3 {
do_test shell1-3.27.4 {
# too many arguments
catchcmd "test.db" ".timer OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "timer". Enter ".help" for help}}
+} {1 {Usage: .timer on|off}}
do_test shell1-3-28.1 {
catchcmd test.db \
@@ -710,10 +713,23 @@ do_test shell1-3-29.1 {
catchcmd "test.db" ".print this is a test"
} {0 {this is a test}}
+# dot-command argument quoting
+do_test shell1-3-30.1 {
+ catchcmd {test.db} {.print "this\"is'a\055test" 'this\"is\\a\055test'}
+} {0 {this"is'a-test this\"is\\a\055test}}
+do_test shell1-3-31.1 {
+ catchcmd {test.db} {.print "this\nis\ta\\test" 'this\nis\ta\\test'}
+} [list 0 "this\nis\ta\\test this\\nis\\ta\\\\test"]
+
+
# Test the output of the ".dump" command
#
do_test shell1-4.1 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
db eval {
+ PRAGMA encoding=UTF16;
CREATE TABLE t1(x);
INSERT INTO t1 VALUES(null), (''), (1), (2.25), ('hello'), (x'807f');
}
@@ -743,6 +759,14 @@ INSERT INTO t1 VALUES(X'807f');}}
# Test the output of ".mode tcl"
#
do_test shell1-4.3 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ PRAGMA encoding=UTF8;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(null), (''), (1), (2.25), ('hello'), (x'807f');
+ }
catchcmd test.db ".mode tcl\nselect * from t1;"
} {0 {""
""
diff --git a/test/shell2.test b/test/shell2.test
index 8260932..def574c 100644
--- a/test/shell2.test
+++ b/test/shell2.test
@@ -42,7 +42,7 @@ sqlite3 db test.db
# Reported on mailing list by Ken Zalewski.
# Ticket [aeff892c57].
do_test shell2-1.1.1 {
- file delete -force foo.db
+ forcedelete foo.db
set rc [ catchcmd "-batch foo.db" "CREATE TABLE t1(a);" ]
set fexist [file exist foo.db]
list $rc $fexist
@@ -81,7 +81,7 @@ do_test shell2-1.3 {
# Test with echo off
# NB. whitespace is important
do_test shell2-1.4.1 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "foo.db" {CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
@@ -90,7 +90,7 @@ SELECT * FROM foo;}
# Test with echo on using command line option
# NB. whitespace is important
do_test shell2-1.4.2 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "-echo foo.db" {CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
SELECT * FROM foo;}
@@ -102,7 +102,7 @@ SELECT * FROM foo;
# Test with echo on using dot command
# NB. whitespace is important
do_test shell2-1.4.3 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "foo.db" {.echo ON
CREATE TABLE foo(a);
INSERT INTO foo(a) VALUES(1);
@@ -116,7 +116,7 @@ SELECT * FROM foo;
# turning off mid- processing.
# NB. whitespace is important
do_test shell2-1.4.4 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "foo.db" {.echo ON
CREATE TABLE foo(a);
.echo OFF
@@ -130,7 +130,7 @@ SELECT * FROM foo;}
# multiple commands per line.
# NB. whitespace is important
do_test shell2-1.4.5 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "foo.db" {.echo ON
CREATE TABLE foo1(a);
INSERT INTO foo1(a) VALUES(1);
@@ -155,13 +155,14 @@ SELECT * FROM foo1;
2
SELECT * FROM foo2;
1
-2}}
+2
+}}
# Test with echo on and headers on using dot command and
# multiple commands per line.
# NB. whitespace is important
do_test shell2-1.4.6 {
- file delete -force foo.db
+ forcedelete foo.db
catchcmd "foo.db" {.echo ON
.headers ON
CREATE TABLE foo1(a);
@@ -192,6 +193,7 @@ a
SELECT * FROM foo2;
b
1
-2}}
+2
+}}
finish_test
diff --git a/test/shell3.test b/test/shell3.test
index d02177b..ce1fd4e 100644
--- a/test/shell3.test
+++ b/test/shell3.test
@@ -40,7 +40,7 @@ sqlite3 db test.db
# Run SQL statement from command line
do_test shell3-1.1 {
- file delete -force foo.db
+ forcedelete foo.db
set rc [ catchcmd "foo.db \"CREATE TABLE t1(a);\"" ]
set fexist [file exist foo.db]
list $rc $fexist
@@ -70,7 +70,7 @@ do_test shell3-1.7 {
# Run SQL file from command line
do_test shell3-2.1 {
- file delete -force foo.db
+ forcedelete foo.db
set rc [ catchcmd "foo.db" "CREATE TABLE t1(a);" ]
set fexist [file exist foo.db]
list $rc $fexist
diff --git a/test/shell4.test b/test/shell4.test
index 5af44c8..c29faf0 100644
--- a/test/shell4.test
+++ b/test/shell4.test
@@ -63,7 +63,7 @@ do_test shell4-1.2.2 {
# .stats ON|OFF Turn stats on or off
do_test shell4-1.3.1 {
catchcmd "test.db" ".stats"
-} {1 {Error: unknown command or invalid arguments: "stats". Enter ".help" for help}}
+} {1 {Usage: .stats on|off}}
do_test shell4-1.3.2 {
catchcmd "test.db" ".stats ON"
} {0 {}}
@@ -73,7 +73,7 @@ do_test shell4-1.3.3 {
do_test shell4-1.3.4 {
# too many arguments
catchcmd "test.db" ".stats OFF BAD"
-} {1 {Error: unknown command or invalid arguments: "stats". Enter ".help" for help}}
+} {1 {Usage: .stats on|off}}
# NB. whitespace is important
do_test shell4-1.4.1 {
diff --git a/test/shell5.test b/test/shell5.test
index d90cedf..8d740cb 100644
--- a/test/shell5.test
+++ b/test/shell5.test
@@ -32,7 +32,6 @@ if {![file executable $CLI]} {
}
db close
forcedelete test.db test.db-journal test.db-wal
-sqlite3 db test.db
#----------------------------------------------------------------------------
# Test cases shell5-1.*: Basic handling of the .import and .separator commands.
@@ -41,29 +40,32 @@ sqlite3 db test.db
# .import FILE TABLE Import data from FILE into TABLE
do_test shell5-1.1.1 {
catchcmd "test.db" ".import"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
+} {1 {Usage: .import FILE TABLE}}
do_test shell5-1.1.2 {
catchcmd "test.db" ".import FOO"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
-do_test shell5-1.1.2 {
- catchcmd "test.db" ".import FOO BAR"
-} {1 {Error: no such table: BAR}}
+} {1 {Usage: .import FILE TABLE}}
+#do_test shell5-1.1.2 {
+# catchcmd "test.db" ".import FOO BAR"
+#} {1 {Error: no such table: BAR}}
do_test shell5-1.1.3 {
# too many arguments
catchcmd "test.db" ".import FOO BAR BAD"
-} {1 {Error: unknown command or invalid arguments: "import". Enter ".help" for help}}
+} {1 {Usage: .import FILE TABLE}}
# .separator STRING Change separator used by output mode and .import
-do_test shell1-1.2.1 {
+do_test shell5-1.2.1 {
catchcmd "test.db" ".separator"
-} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}}
-do_test shell1-1.2.2 {
- catchcmd "test.db" ".separator FOO"
+} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
+do_test shell5-1.2.2 {
+ catchcmd "test.db" ".separator ONE"
} {0 {}}
-do_test shell1-1.2.3 {
+do_test shell5-1.2.3 {
+ catchcmd "test.db" ".separator ONE TWO"
+} {0 {}}
+do_test shell5-1.2.4 {
# too many arguments
- catchcmd "test.db" ".separator FOO BAD"
-} {1 {Error: unknown command or invalid arguments: "separator". Enter ".help" for help}}
+ catchcmd "test.db" ".separator ONE TWO THREE"
+} {1 {Usage: .separator SEPARATOR ?NEWLINE?}}
# separator should default to "|"
do_test shell5-1.3.1 {
@@ -81,14 +83,14 @@ do_test shell5-1.3.2 {
# import file doesn't exist
do_test shell5-1.4.1 {
- file delete -force FOO
+ forcedelete FOO
set res [catchcmd "test.db" {CREATE TABLE t1(a, b);
.import FOO t1}]
} {1 {Error: cannot open "FOO"}}
# empty import file
do_test shell5-1.4.2 {
- file delete -force shell5.csv
+ forcedelete shell5.csv
set in [open shell5.csv w]
close $in
set res [catchcmd "test.db" {.import shell5.csv t1
@@ -101,7 +103,7 @@ do_test shell5-1.4.3 {
puts $in "1"
close $in
set res [catchcmd "test.db" {.import shell5.csv t1}]
-} {1 {Error: shell5.csv line 1: expected 2 columns of data but found 1}}
+} {1 {shell5.csv:1: expected 2 columns but found 1 - filling the rest with NULL}}
# import file with 1 row, 3 columns (expecting 2 cols)
do_test shell5-1.4.4 {
@@ -109,14 +111,15 @@ do_test shell5-1.4.4 {
puts $in "1|2|3"
close $in
set res [catchcmd "test.db" {.import shell5.csv t1}]
-} {1 {Error: shell5.csv line 1: expected 2 columns of data but found 3}}
+} {1 {shell5.csv:1: expected 2 columns but found 3 - extras ignored}}
# import file with 1 row, 2 columns
do_test shell5-1.4.5 {
set in [open shell5.csv w]
puts $in "1|2"
close $in
- set res [catchcmd "test.db" {.import shell5.csv t1
+ set res [catchcmd "test.db" {DELETE FROM t1;
+.import shell5.csv t1
SELECT COUNT(*) FROM t1;}]
} {0 1}
@@ -197,15 +200,15 @@ SELECT length(b) FROM t1 WHERE a='8';}]
# This is limited by SQLITE_MAX_VARIABLE_NUMBER, which defaults to 999.
set cols 999
do_test shell5-1.6.1 {
- set sql {CREATE TABLE t2(}
set data {}
for {set i 1} {$i<$cols} {incr i} {
- append sql "c$i,"
+ append data "c$i|"
+ }
+ append data "c$cols\n";
+ for {set i 1} {$i<$cols} {incr i} {
append data "$i|"
}
- append sql "c$cols);"
append data "$cols"
- catchcmd "test.db" $sql
set in [open shell5.csv w]
puts $in $data
close $in
@@ -214,16 +217,160 @@ SELECT COUNT(*) FROM t2;}]
} {0 1}
# try importing a large number of rows
-set rows 999999
+set rows 9999
do_test shell5-1.7.1 {
set in [open shell5.csv w]
+ puts $in a
for {set i 1} {$i<=$rows} {incr i} {
puts $in $i
}
close $in
- set res [catchcmd "test.db" {CREATE TABLE t3(a);
+ set res [catchcmd "test.db" {.mode csv
.import shell5.csv t3
SELECT COUNT(*) FROM t3;}]
} [list 0 $rows]
+# Inport from a pipe. (Unix only, as it requires "awk")
+if {$tcl_platform(platform)=="unix"} {
+ do_test shell5-1.8 {
+ forcedelete test.db
+ catchcmd test.db {.mode csv
+.import "|awk 'END{print \"x,y\";for(i=1;i<=5;i++){print i \",this is \" i}}'" t1
+SELECT * FROM t1;}
+ } {0 {1,"this is 1"
+2,"this is 2"
+3,"this is 3"
+4,"this is 4"
+5,"this is 5"}}
+}
+
+# Import columns containing quoted strings
+do_test shell5-1.9 {
+ set out [open shell5.csv w]
+ fconfigure $out -translation lf
+ puts $out {1,"",11}
+ puts $out {2,"x",22}
+ puts $out {3,"""",33}
+ puts $out {4,"hello",44}
+ puts $out "5,55,\"\"\r"
+ puts $out {6,66,"x"}
+ puts $out {7,77,""""}
+ puts $out {8,88,"hello"}
+ puts $out {"",9,99}
+ puts $out {"x",10,110}
+ puts $out {"""",11,121}
+ puts $out {"hello",12,132}
+ close $out
+ forcedelete test.db
+ catchcmd test.db {.mode csv
+ CREATE TABLE t1(a,b,c);
+.import shell5.csv t1
+ }
+ sqlite3 db test.db
+ db eval {SELECT *, '|' FROM t1 ORDER BY rowid}
+} {1 {} 11 | 2 x 22 | 3 {"} 33 | 4 hello 44 | 5 55 {} | 6 66 x | 7 77 {"} | 8 88 hello | {} 9 99 | x 10 110 | {"} 11 121 | hello 12 132 |}
+db close
+
+# Import columns containing quoted strings
+do_test shell5-1.10 {
+ set out [open shell5.csv w]
+ fconfigure $out -translation lf
+ puts $out {column1,column2,column3,column4}
+ puts $out "field1,field2,\"x3 \"\"\r\ndata\"\" 3\",field4"
+ puts $out "x1,x2,\"x3 \"\"\ndata\"\" 3\",x4"
+ close $out
+ forcedelete test.db
+ catchcmd test.db {.mode csv
+ CREATE TABLE t1(a,b,c,d);
+.import shell5.csv t1
+ }
+ sqlite3 db test.db
+ db eval {SELECT hex(c) FROM t1 ORDER BY rowid}
+} {636F6C756D6E33 783320220D0A64617461222033 783320220A64617461222033}
+
+# Blank last column with \r\n line endings.
+do_test shell5-1.11 {
+ set out [open shell5.csv w]
+ fconfigure $out -translation binary
+ puts $out "column1,column2,column3\r"
+ puts $out "a,b, \r"
+ puts $out "x,y,\r"
+ puts $out "p,q,r\r"
+ close $out
+ catch {db close}
+ forcedelete test.db
+ catchcmd test.db {.mode csv
+.import shell5.csv t1
+ }
+ sqlite3 db test.db
+ db eval {SELECT *, '|' FROM t1}
+} {a b { } | x y {} | p q r |}
+db close
+
+#----------------------------------------------------------------------------
+#
+reset_db
+sqlite3 db test.db
+do_test shell5-2.1 {
+ set fd [open shell5.csv w]
+ puts $fd ",hello"
+ close $fd
+ catchcmd test.db [string trim {
+.mode csv
+CREATE TABLE t1(a, b);
+.import shell5.csv t1
+ }]
+ db eval { SELECT * FROM t1 }
+} {{} hello}
+
+do_test shell5-2.2 {
+ set fd [open shell5.csv w]
+ puts $fd {"",hello}
+ close $fd
+ catchcmd test.db [string trim {
+.mode csv
+CREATE TABLE t2(a, b);
+.import shell5.csv t2
+ }]
+ db eval { SELECT * FROM t2 }
+} {{} hello}
+
+do_test shell5-2.3 {
+ set fd [open shell5.csv w]
+ puts $fd {"x""y",hello}
+ close $fd
+ catchcmd test.db [string trim {
+.mode csv
+CREATE TABLE t3(a, b);
+.import shell5.csv t3
+ }]
+ db eval { SELECT * FROM t3 }
+} {x\"y hello}
+
+do_test shell5-2.4 {
+ set fd [open shell5.csv w]
+ puts $fd {"xy""",hello}
+ close $fd
+ catchcmd test.db [string trim {
+.mode csv
+CREATE TABLE t4(a, b);
+.import shell5.csv t4
+ }]
+ db eval { SELECT * FROM t4 }
+} {xy\" hello}
+
+do_test shell5-2.5 {
+ set fd [open shell5.csv w]
+ puts $fd {"one","2"}
+ puts $fd {}
+ close $fd
+ catchcmd test.db [string trim {
+.mode csv
+CREATE TABLE t4(a, b);
+.import shell5.csv t4
+ }]
+ db eval { SELECT * FROM t4 }
+} {xy\" hello one 2 {} {}}
+
+
finish_test
diff --git a/test/show_speedtest1_rtree.tcl b/test/show_speedtest1_rtree.tcl
new file mode 100644
index 0000000..3faa168
--- /dev/null
+++ b/test/show_speedtest1_rtree.tcl
@@ -0,0 +1,57 @@
+#!/usr/bin/tclsh
+#
+# This script displays the field of rectangles used by --testset rtree
+# of speedtest1. Run this script as follows:
+#
+# rm test.db
+# ./speedtest1 --testset rtree --size 25 test.db
+# sqlite3 --separator ' ' test.db 'SELECT * FROM rt1' >data.txt
+# wish show_speedtest1_rtree.tcl
+#
+# The filename "data.txt" is hard coded into this script and so that name
+# must be used on lines 3 and 4 above. Elsewhere, different filenames can
+# be used. The --size N parameter can be adjusted as desired.
+#
+package require Tk
+set f [open data.txt rb]
+set data [read $f]
+close $f
+canvas .c
+frame .b
+button .b.b1 -text X-Y -command refill-xy
+button .b.b2 -text X-Z -command refill-xz
+button .b.b3 -text Y-Z -command refill-yz
+pack .b.b1 .b.b2 .b.b3 -side left
+pack .c -side top -fill both -expand 1
+pack .b -side top
+proc resize_canvas_to_fit {} {
+ foreach {x0 y0 x1 y1} [.c bbox all] break
+ set w [expr {$x1-$x0}]
+ set h [expr {$y1-$y0}]
+ .c config -width $w -height $h
+}
+proc refill-xy {} {
+ .c delete all
+ foreach {id x0 x1 y0 y1 z0 z1} $::data {
+ .c create rectangle $x0 $y0 $x1 $y1
+ }
+ .c scale all 0 0 0.05 0.05
+ resize_canvas_to_fit
+}
+proc refill-xz {} {
+ .c delete all
+ foreach {id x0 x1 y0 y1 z0 z1} $::data {
+ .c create rectangle $x0 $z0 $x1 $z1
+ }
+ .c scale all 0 0 0.05 0.05
+ resize_canvas_to_fit
+}
+proc refill-yz {} {
+ .c delete all
+ foreach {id x0 x1 y0 y1 z0 z1} $::data {
+ .c create rectangle $y0 $z0 $y1 $z1
+ }
+ .c scale all 0 0 0.05 0.05
+ resize_canvas_to_fit
+}
+refill-xy
diff --git a/test/skipscan1.test b/test/skipscan1.test
new file mode 100644
index 0000000..8150b01
--- /dev/null
+++ b/test/skipscan1.test
@@ -0,0 +1,250 @@
+# 2013-11-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 tests of the "skip-scan" query strategy.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test skipscan1-1.1 {
+ CREATE TABLE t1(a TEXT, b INT, c INT, d INT);
+ CREATE INDEX t1abc ON t1(a,b,c);
+ INSERT INTO t1 VALUES('abc',123,4,5);
+ INSERT INTO t1 VALUES('abc',234,5,6);
+ INSERT INTO t1 VALUES('abc',234,6,7);
+ INSERT INTO t1 VALUES('abc',345,7,8);
+ INSERT INTO t1 VALUES('def',567,8,9);
+ INSERT INTO t1 VALUES('def',345,9,10);
+ INSERT INTO t1 VALUES('bcd',100,6,11);
+
+ /* Fake the sqlite_stat1 table so that the query planner believes
+ ** the table contains thousands of rows and that the first few
+ ** columns are not selective. */
+ ANALYZE;
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1 VALUES('t1','t1abc','10000 5000 2000 10');
+ ANALYZE sqlite_master;
+} {}
+
+# Simple queries that leave the first one or two columns of the
+# index unconstrainted.
+#
+do_execsql_test skipscan1-1.2 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
+} {abc 345 7 8 | def 345 9 10 |}
+do_execsql_test skipscan1-1.2eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
+} {/* USING INDEX t1abc (ANY(a) AND b=?)*/}
+do_execsql_test skipscan1-1.2sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
+} {~/*ORDER BY*/}
+
+do_execsql_test skipscan1-1.3 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a DESC;
+} {def 345 9 10 | abc 345 7 8 |}
+do_execsql_test skipscan1-1.3eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
+} {/* USING INDEX t1abc (ANY(a) AND b=?)*/}
+do_execsql_test skipscan1-1.3sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b=345 ORDER BY a;
+} {~/*ORDER BY*/}
+
+do_execsql_test skipscan1-1.4 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
+} {abc 234 6 7 | bcd 100 6 11 |}
+do_execsql_test skipscan1-1.4eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
+} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
+do_execsql_test skipscan1-1.4sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c=6 ORDER BY a, b, c;
+} {~/*ORDER BY*/}
+
+do_execsql_test skipscan1-1.5 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
+} {abc 234 6 7 | abc 345 7 8 | bcd 100 6 11 |}
+do_execsql_test skipscan1-1.5eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
+} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
+do_execsql_test skipscan1-1.5sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c IN (6,7) ORDER BY a, b, c;
+} {~/*ORDER BY*/}
+
+do_execsql_test skipscan1-1.6 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
+} {abc 234 6 7 | abc 345 7 8 | bcd 100 6 11 |}
+do_execsql_test skipscan1-1.6eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
+} {/* USING INDEX t1abc (ANY(a) AND ANY(b) AND c>? AND c<?)*/}
+do_execsql_test skipscan1-1.6sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE c BETWEEN 6 AND 7 ORDER BY a, b, c;
+} {~/*ORDER BY*/}
+
+do_execsql_test skipscan1-1.7 {
+ SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
+ ORDER BY a, b;
+} {abc 234 6 7 | abc 345 7 8 |}
+do_execsql_test skipscan1-1.7eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
+ ORDER BY a, b;
+} {/* USING INDEX t1abc (ANY(a) AND b=? AND c>? AND c<?)*/}
+do_execsql_test skipscan1-1.7sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t1 WHERE b IN (234, 345) AND c BETWEEN 6 AND 7
+ ORDER BY a, b;
+} {~/*ORDER BY*/}
+
+
+# Joins
+#
+do_execsql_test skipscan1-1.51 {
+ CREATE TABLE t1j(x TEXT, y INTEGER);
+ INSERT INTO t1j VALUES('one',1),('six',6),('ninty-nine',99);
+ INSERT INTO sqlite_stat1 VALUES('t1j',null,'3');
+ ANALYZE sqlite_master;
+ SELECT x, a, b, c, d, '|' FROM t1j, t1 WHERE c=y ORDER BY +a;
+} {six abc 234 6 7 | six bcd 100 6 11 |}
+do_execsql_test skipscan1-1.51eqp {
+ EXPLAIN QUERY PLAN
+ SELECT x, a, b, c, d, '|' FROM t1j, t1 WHERE c=y ORDER BY +a;
+} {/* INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
+
+do_execsql_test skipscan1-1.52 {
+ SELECT x, a, b, c, d, '|' FROM t1j LEFT JOIN t1 ON c=y ORDER BY +y, +a;
+} {one {} {} {} {} | six abc 234 6 7 | six bcd 100 6 11 | ninty-nine {} {} {} {} |}
+do_execsql_test skipscan1-1.52eqp {
+ EXPLAIN QUERY PLAN
+ SELECT x, a, b, c, d, '|' FROM t1j LEFT JOIN t1 ON c=y ORDER BY +y, +a;
+} {/* INDEX t1abc (ANY(a) AND ANY(b) AND c=?)*/}
+
+do_execsql_test skipscan1-2.1 {
+ CREATE TABLE t2(a TEXT, b INT, c INT, d INT,
+ PRIMARY KEY(a,b,c));
+ INSERT INTO t2 SELECT * FROM t1;
+
+ /* Fake the sqlite_stat1 table so that the query planner believes
+ ** the table contains thousands of rows and that the first few
+ ** columns are not selective. */
+ ANALYZE;
+ UPDATE sqlite_stat1 SET stat='10000 5000 2000 10' WHERE idx NOT NULL;
+ ANALYZE sqlite_master;
+} {}
+
+do_execsql_test skipscan1-2.2 {
+ SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
+} {abc 345 7 8 | def 345 9 10 |}
+do_execsql_test skipscan1-2.2eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
+} {/* USING INDEX sqlite_autoindex_t2_1 (ANY(a) AND b=?)*/}
+do_execsql_test skipscan1-2.2sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t2 WHERE b=345 ORDER BY a;
+} {~/*ORDER BY*/}
+
+
+do_execsql_test skipscan1-3.1 {
+ CREATE TABLE t3(a TEXT, b INT, c INT, d INT,
+ PRIMARY KEY(a,b,c)) WITHOUT ROWID;
+ INSERT INTO t3 SELECT * FROM t1;
+
+ /* Fake the sqlite_stat1 table so that the query planner believes
+ ** the table contains thousands of rows and that the first few
+ ** columns are not selective. */
+ ANALYZE;
+ UPDATE sqlite_stat1 SET stat='10000 5000 2000 10' WHERE idx NOT NULL;
+ ANALYZE sqlite_master;
+} {}
+
+do_execsql_test skipscan1-3.2 {
+ SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
+} {abc 345 7 8 | def 345 9 10 |}
+do_execsql_test skipscan1-3.2eqp {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
+} {/* PRIMARY KEY (ANY(a) AND b=?)*/}
+do_execsql_test skipscan1-3.2sort {
+ EXPLAIN QUERY PLAN
+ SELECT a,b,c,d,'|' FROM t3 WHERE b=345 ORDER BY a;
+} {~/*ORDER BY*/}
+
+# Ticket 520070ec7fbaac: Array overrun in the skip-scan optimization
+# 2013-12-22
+#
+do_execsql_test skipscan1-4.1 {
+ CREATE TABLE t4(a,b,c,d,e,f,g,h,i);
+ CREATE INDEX t4all ON t4(a,b,c,d,e,f,g,h);
+ INSERT INTO t4 VALUES(1,2,3,4,5,6,7,8,9);
+ ANALYZE;
+ DELETE FROM sqlite_stat1;
+ INSERT INTO sqlite_stat1
+ VALUES('t4','t4all','655360 163840 40960 10240 2560 640 160 40 10');
+ ANALYZE sqlite_master;
+ SELECT i FROM t4 WHERE a=1;
+ SELECT i FROM t4 WHERE b=2;
+ SELECT i FROM t4 WHERE c=3;
+ SELECT i FROM t4 WHERE d=4;
+ SELECT i FROM t4 WHERE e=5;
+ SELECT i FROM t4 WHERE f=6;
+ SELECT i FROM t4 WHERE g=7;
+ SELECT i FROM t4 WHERE h=8;
+} {9 9 9 9 9 9 9 9}
+
+# Make sure skip-scan cost computation in the query planner takes into
+# account the fact that the seek must occur multiple times.
+#
+# Prior to 2014-03-10, the costs were computed incorrectly which would
+# cause index t5i2 to be used instead of t5i1 on the skipscan1-5.3.
+#
+do_execsql_test skipscan1-5.1 {
+ CREATE TABLE t5(
+ id INTEGER PRIMARY KEY,
+ loc TEXT,
+ lang INTEGER,
+ utype INTEGER,
+ xa INTEGER,
+ xd INTEGER,
+ xh INTEGER
+ );
+ CREATE INDEX t5i1 on t5(loc, xh, xa, utype, lang);
+ CREATE INDEX t5i2 ON t5(xd,loc,utype,lang);
+ EXPLAIN QUERY PLAN
+ SELECT xh, loc FROM t5 WHERE loc >= 'M' AND loc < 'N';
+} {/.*COVERING INDEX t5i1 .*/}
+do_execsql_test skipscan1-5.2 {
+ ANALYZE;
+ DELETE FROM sqlite_stat1;
+ DROP TABLE IF EXISTS sqlite_stat4;
+ DROP TABLE IF EXISTS sqlite_stat3;
+ INSERT INTO sqlite_stat1 VALUES('t5','t5i1','2702931 3 2 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('t5','t5i2','2702931 686 2 2 2');
+ ANALYZE sqlite_master;
+} {}
+db cache flush
+do_execsql_test skipscan1-5.3 {
+ EXPLAIN QUERY PLAN
+ SELECT xh, loc FROM t5 WHERE loc >= 'M' AND loc < 'N';
+} {/.*COVERING INDEX t5i1 .*/}
+
+
+
+finish_test
diff --git a/test/skipscan2.test b/test/skipscan2.test
new file mode 100644
index 0000000..a42ff2d
--- /dev/null
+++ b/test/skipscan2.test
@@ -0,0 +1,205 @@
+# 2013-11-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 tests of the "skip-scan" query strategy.
+#
+# The test cases in this file are derived from the description of
+# the skip-scan query strategy in the "optoverview.html" document.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test skipscan2-1.1 {
+ CREATE TABLE people(
+ name TEXT PRIMARY KEY,
+ role TEXT NOT NULL,
+ height INT NOT NULL, -- in cm
+ CHECK( role IN ('student','teacher') )
+ );
+ CREATE INDEX people_idx1 ON people(role, height);
+} {}
+do_execsql_test skipscan2-1.2 {
+ INSERT INTO people VALUES('Alice','student',156);
+ INSERT INTO people VALUES('Bob','student',161);
+ INSERT INTO people VALUES('Cindy','student',155);
+ INSERT INTO people VALUES('David','student',181);
+ INSERT INTO people VALUES('Emily','teacher',158);
+ INSERT INTO people VALUES('Fred','student',163);
+ INSERT INTO people VALUES('Ginny','student',169);
+ INSERT INTO people VALUES('Harold','student',172);
+ INSERT INTO people VALUES('Imma','student',179);
+ INSERT INTO people VALUES('Jack','student',181);
+ INSERT INTO people VALUES('Karen','student',163);
+ INSERT INTO people VALUES('Logan','student',177);
+ INSERT INTO people VALUES('Megan','teacher',159);
+ INSERT INTO people VALUES('Nathan','student',163);
+ INSERT INTO people VALUES('Olivia','student',161);
+ INSERT INTO people VALUES('Patrick','teacher',180);
+ INSERT INTO people VALUES('Quiana','student',182);
+ INSERT INTO people VALUES('Robert','student',159);
+ INSERT INTO people VALUES('Sally','student',166);
+ INSERT INTO people VALUES('Tom','student',171);
+ INSERT INTO people VALUES('Ursula','student',170);
+ INSERT INTO people VALUES('Vance','student',179);
+ INSERT INTO people VALUES('Willma','student',175);
+ INSERT INTO people VALUES('Xavier','teacher',185);
+ INSERT INTO people VALUES('Yvonne','student',149);
+ INSERT INTO people VALUES('Zach','student',170);
+}
+
+# Without ANALYZE, a skip-scan is not used
+#
+do_execsql_test skipscan2-1.3 {
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-1.3eqp {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {~/*INDEX people_idx1 */}
+
+# Now do an ANALYZE. A skip-scan can be used after ANALYZE.
+#
+do_execsql_test skipscan2-1.4 {
+ ANALYZE;
+ -- We do not have enough people above to actually force the use
+ -- of a skip-scan. So make a manual adjustment to the stat1 table
+ -- to make it seem like there are many more.
+ UPDATE sqlite_stat1 SET stat='10000 5000 20' WHERE idx='people_idx1';
+ UPDATE sqlite_stat1 SET stat='10000 1' WHERE idx='sqlite_autoindex_people_1';
+ ANALYZE sqlite_master;
+}
+db cache flush
+do_execsql_test skipscan2-1.5 {
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-1.5eqp {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {/*INDEX people_idx1 */}
+
+# Same answer with other formulations of the same query
+#
+do_execsql_test skipscan2-1.6 {
+ SELECT name FROM people
+ WHERE role IN (SELECT DISTINCT role FROM people)
+ AND height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-1.7 {
+ SELECT name FROM people WHERE role='teacher' AND height>=180
+ UNION ALL
+ SELECT name FROM people WHERE role='student' AND height>=180
+ ORDER BY 1;
+} {David Jack Patrick Quiana Xavier}
+
+# Add 8 more people, bringing the total to 34. Then the number of
+# duplicates in the left-column of the index will be 17 and
+# skip-scan should not be used after an (unfudged) ANALYZE.
+#
+do_execsql_test skipscan2-1.8 {
+ INSERT INTO people VALUES('Angie','student',166);
+ INSERT INTO people VALUES('Brad','student',176);
+ INSERT INTO people VALUES('Claire','student',168);
+ INSERT INTO people VALUES('Donald','student',162);
+ INSERT INTO people VALUES('Elaine','student',177);
+ INSERT INTO people VALUES('Frazier','student',159);
+ INSERT INTO people VALUES('Grace','student',179);
+ INSERT INTO people VALUES('Horace','student',166);
+ ANALYZE;
+ SELECT stat FROM sqlite_stat1 WHERE idx='people_idx1';
+} {{34 17 2}}
+db cache flush
+do_execsql_test skipscan2-1.9 {
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-1.9eqp {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {~/*INDEX people_idx1 */}
+
+# Add 2 more people, bringing the total to 36. Then the number of
+# duplicates in the left-column of the index will be 18 and
+# skip-scan will be used after an (unfudged) ANALYZE.
+#
+do_execsql_test skipscan2-1.10 {
+ INSERT INTO people VALUES('Ingrad','student',155);
+ INSERT INTO people VALUES('Jacob','student',179);
+ ANALYZE;
+ SELECT stat FROM sqlite_stat1 WHERE idx='people_idx1';
+} {{36 18 2}}
+db cache flush
+do_execsql_test skipscan2-1.11 {
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-1.11eqp {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM people WHERE height>=180 ORDER BY +name;
+} {/*INDEX people_idx1 */}
+
+
+# Repeat using a WITHOUT ROWID table.
+#
+do_execsql_test skipscan2-2.1 {
+ CREATE TABLE peoplew(
+ name TEXT PRIMARY KEY,
+ role TEXT NOT NULL,
+ height INT NOT NULL, -- in cm
+ CHECK( role IN ('student','teacher') )
+ ) WITHOUT ROWID;
+ CREATE INDEX peoplew_idx1 ON peoplew(role, height);
+ INSERT INTO peoplew(name,role,height)
+ SELECT name, role, height FROM people;
+ ALTER TABLE people RENAME TO old_people;
+ SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-2.2 {
+ SELECT name FROM peoplew
+ WHERE role IN (SELECT DISTINCT role FROM peoplew)
+ AND height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-2.2 {
+ SELECT name FROM peoplew WHERE role='teacher' AND height>=180
+ UNION ALL
+ SELECT name FROM peoplew WHERE role='student' AND height>=180
+ ORDER BY 1;
+} {David Jack Patrick Quiana Xavier}
+
+# Now do an ANALYZE. A skip-scan can be used after ANALYZE.
+#
+do_execsql_test skipscan2-2.4 {
+ ANALYZE;
+}
+db cache flush
+do_execsql_test skipscan2-2.5 {
+ SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
+} {David Jack Patrick Quiana Xavier}
+do_execsql_test skipscan2-2.5eqp {
+ EXPLAIN QUERY PLAN
+ SELECT name FROM peoplew WHERE height>=180 ORDER BY +name;
+} {/*INDEX peoplew_idx1 */}
+
+# A skip-scan on a PK index of a WITHOUT ROWID table.
+#
+do_execsql_test skipscan2-3.1 {
+ CREATE TABLE t3(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID;
+}
+do_test skipscan2-3.2 {
+ for {set i 0} {$i < 1000} {incr i} {
+ execsql { INSERT INTO t3 VALUES($i%2, $i, 'xyz') }
+ }
+ execsql { ANALYZE }
+} {}
+do_eqp_test skipscan2-3.3eqp {
+ SELECT * FROM t3 WHERE b=42;
+} {0 0 0 {SEARCH TABLE t3 USING PRIMARY KEY (ANY(a) AND b=?)}}
+
+
+finish_test
diff --git a/test/skipscan5.test b/test/skipscan5.test
new file mode 100644
index 0000000..5d6d392
--- /dev/null
+++ b/test/skipscan5.test
@@ -0,0 +1,186 @@
+# 2013-11-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 tests of the "skip-scan" query strategy. In
+# particular it tests that stat4 data can be used by a range query
+# that uses the skip-scan approach.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix skipscan5
+
+ifcapable !stat4 {
+ finish_test
+ return
+}
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a INT, b INT, c INT);
+ CREATE INDEX i1 ON t1(a, b);
+} {}
+
+expr srand(4)
+do_test 1.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ set a [expr int(rand()*4.0) + 1]
+ set b [expr int(rand()*20.0) + 1]
+ execsql { INSERT INTO t1 VALUES($a, $b, NULL) }
+ }
+ execsql ANALYZE
+} {}
+
+foreach {tn q res} {
+ 1 "b = 5" {/*ANY(a) AND b=?*/}
+ 2 "b > 12 AND b < 16" {/*ANY(a) AND b>? AND b<?*/}
+ 3 "b > 2 AND b < 16" {/*SCAN TABLE t1*/}
+ 4 "b > 18 AND b < 25" {/*ANY(a) AND b>? AND b<?*/}
+ 5 "b > 15" {/*ANY(a) AND b>?*/}
+ 6 "b > 5" {/*SCAN TABLE t1*/}
+ 7 "b < 15" {/*SCAN TABLE t1*/}
+ 8 "b < 5" {/*ANY(a) AND b<?*/}
+ 9 "5 > b" {/*ANY(a) AND b<?*/}
+ 10 "b = '5'" {/*ANY(a) AND b=?*/}
+ 11 "b > '12' AND b < '16'" {/*ANY(a) AND b>? AND b<?*/}
+ 12 "b > '2' AND b < '16'" {/*SCAN TABLE t1*/}
+ 13 "b > '18' AND b < '25'" {/*ANY(a) AND b>? AND b<?*/}
+ 14 "b > '15'" {/*ANY(a) AND b>?*/}
+ 15 "b > '5'" {/*SCAN TABLE t1*/}
+ 16 "b < '15'" {/*SCAN TABLE t1*/}
+ 17 "b < '5'" {/*ANY(a) AND b<?*/}
+ 18 "'5' > b" {/*ANY(a) AND b<?*/}
+} {
+ set sql "EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE $q"
+ do_execsql_test 1.3.$tn $sql $res
+}
+
+
+#-------------------------------------------------------------------------
+# Test that range-query/skip-scan estimation works with text values.
+# And on UTF-16 databases when there is no UTF-16 collation sequence
+# available.
+#
+
+proc test_collate {enc lhs rhs} {
+ string compare $lhs $rhs
+}
+
+foreach {tn dbenc coll} {
+ 1 UTF-8 { add_test_collate db 0 0 1 }
+ 2 UTF-16 { add_test_collate db 1 0 0 }
+ 3 UTF-8 { add_test_collate db 0 1 0 }
+} {
+ reset_db
+ eval $coll
+
+ do_execsql_test 2.$tn.1 " PRAGMA encoding = '$dbenc' "
+ do_execsql_test 2.$tn.2 {
+ CREATE TABLE t2(a TEXT, b TEXT, c TEXT COLLATE test_collate, d TEXT);
+ CREATE INDEX i2 ON t2(a, b, c);
+ }
+
+ set vocab(d) { :) }
+ set vocab(c) { a b c d e f g h i j k l m n o p q r s t }
+ set vocab(b) { one two three }
+ set vocab(a) { sql }
+
+ do_test 2.$tn.3 {
+ for {set i 0} {$i < 100} {incr i} {
+ foreach var {a b c d} {
+ set $var [lindex $vocab($var) [expr $i % [llength $vocab($var)]]]
+ }
+ execsql { INSERT INTO t2 VALUES($a, $b, $c, $d) }
+ }
+ execsql ANALYZE
+ } {}
+
+ foreach {tn2 q res} {
+ 1 { c BETWEEN 'd' AND 'e' } {/*ANY(a) AND ANY(b) AND c>? AND c<?*/}
+ 2 { c BETWEEN 'b' AND 'r' } {/*SCAN TABLE t2*/}
+ 3 { c > 'q' } {/*ANY(a) AND ANY(b) AND c>?*/}
+ 4 { c > 'e' } {/*SCAN TABLE t2*/}
+ 5 { c < 'q' } {/*SCAN TABLE t2*/}
+ 4 { c < 'e' } {/*ANY(a) AND ANY(b) AND c<?*/}
+ } {
+ set sql "EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE $q"
+ do_execsql_test 2.$tn.$tn2 $sql $res
+ }
+
+}
+
+#-------------------------------------------------------------------------
+# Test that range-query/skip-scan estimation works on columns that contain
+# a variety of types.
+#
+
+reset_db
+do_execsql_test 3.1 {
+ CREATE TABLE t3(a, b, c);
+ CREATE INDEX i3 ON t3(a, b);
+}
+
+set values {
+ NULL NULL NULL
+ NULL -9567 -9240
+ -8725 -8659 -8248.340244520614
+ -8208 -7939 -7746.985758536954
+ -7057 -6550 -5916
+ -5363 -4935.781822975623 -4935.063633571875
+ -3518.4554911770183 -2537 -2026
+ -1511.2603881914456 -1510.4195994839156 -1435
+ -1127.4210136045804 -1045 99
+ 1353 1457 1563.2908193223611
+ 2245 2286 2552
+ 2745.18831295203 2866.279926554429 3075.0468527316334
+ 3447 3867 4237.892420141907
+ 4335 5052.9775000424015 5232.178240656935
+ 5541.784919585003 5749.725576373621 5758
+ 6005 6431 7263.477992854769
+ 7441 7541 8667.279760663994
+ 8857 9199.638673662972 'dl'
+ 'dro' 'h' 'igprfq'
+ 'jnbd' 'k' 'kordee'
+ 'lhwcv' 'mzlb' 'nbjked'
+ 'nufpo' 'nxqkdq' 'shelln'
+ 'tvzn' 'wpnt' 'wylf'
+ 'ydkgu' 'zdb' X''
+ X'0a' X'203f6429f1f33f' X'23858e324545e0362b'
+ X'3f9f8a' X'516f7ddd4b' X'68f1df0930ac6b'
+ X'9ea60d' X'a06f' X'aefd342a39ce36df'
+ X'afaa020fe2' X'be201c' X'c47d97b209601e45'
+}
+
+do_test 3.2 {
+ set c 0
+ foreach v $values {
+ execsql "INSERT INTO t3 VALUES($c % 2, $v, $c)"
+ incr c
+ }
+ execsql ANALYZE
+} {}
+
+foreach {tn q res} {
+ 1 "b BETWEEN -10000 AND -8000" {/*ANY(a) AND b>? AND b<?*/}
+ 2 "b BETWEEN -10000 AND 'qqq'" {/*SCAN TABLE t3*/}
+ 3 "b < X'5555'" {/*SCAN TABLE t3*/}
+ 4 "b > X'5555'" {/*ANY(a) AND b>?*/}
+ 5 "b > 'zzz'" {/*ANY(a) AND b>?*/}
+ 6 "b < 'zzz'" {/*SCAN TABLE t3*/}
+} {
+ set sql "EXPLAIN QUERY PLAN SELECT * FROM t3 WHERE $q"
+ do_execsql_test 3.3.$tn $sql $res
+}
+
+finish_test
+
+
+
+
diff --git a/test/softheap1.test b/test/softheap1.test
index 6855553..522e455 100644
--- a/test/softheap1.test
+++ b/test/softheap1.test
@@ -24,10 +24,27 @@ ifcapable !integrityck {
return
}
-sqlite3_soft_heap_limit -1
-sqlite3_soft_heap_limit 0
-sqlite3_soft_heap_limit 5000
+do_test softheap1-1.0 {
+ execsql {PRAGMA soft_heap_limit}
+} [sqlite3_soft_heap_limit -1]
do_test softheap1-1.1 {
+ execsql {PRAGMA soft_heap_limit=123456; PRAGMA soft_heap_limit;}
+} {123456 123456}
+do_test softheap1-1.2 {
+ sqlite3_soft_heap_limit -1
+} {123456}
+do_test softheap1-1.3 {
+ execsql {PRAGMA soft_heap_limit(-1); PRAGMA soft_heap_limit;}
+} {123456 123456}
+do_test softheap1-1.4 {
+ execsql {PRAGMA soft_heap_limit(0); PRAGMA soft_heap_limit;}
+} {0 0}
+
+sqlite3_soft_heap_limit 5000
+do_test softheap1-2.0 {
+ execsql {PRAGMA soft_heap_limit}
+} {5000}
+do_test softheap1-2.1 {
execsql {
PRAGMA auto_vacuum=1;
CREATE TABLE t1(x);
diff --git a/test/speedtest1.c b/test/speedtest1.c
new file mode 100644
index 0000000..383f580
--- /dev/null
+++ b/test/speedtest1.c
@@ -0,0 +1,1391 @@
+/*
+** A program for performance testing.
+**
+** The available command-line options are described below:
+*/
+static const char zHelp[] =
+ "Usage: %s [--options] DATABASE\n"
+ "Options:\n"
+ " --autovacuum Enable AUTOVACUUM mode\n"
+ " --cachesize N Set the cache size to N\n"
+ " --exclusive Enable locking_mode=EXCLUSIVE\n"
+ " --explain Like --sqlonly but with added EXPLAIN keywords\n"
+ " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n"
+ " --incrvacuum Enable incremenatal vacuum mode\n"
+ " --journalmode M Set the journal_mode to MODE\n"
+ " --key KEY Set the encryption key to KEY\n"
+ " --lookaside N SZ Configure lookaside for N slots of SZ bytes each\n"
+ " --nosync Set PRAGMA synchronous=OFF\n"
+ " --notnull Add NOT NULL constraints to table columns\n"
+ " --pagesize N Set the page size to N\n"
+ " --pcache N SZ Configure N pages of pagecache each of size SZ bytes\n"
+ " --primarykey Use PRIMARY KEY instead of UNIQUE where appropriate\n"
+ " --reprepare Reprepare each statement upon every invocation\n"
+ " --scratch N SZ Configure scratch memory for N slots of SZ bytes each\n"
+ " --sqlonly No-op. Only show the SQL that would have been run.\n"
+ " --size N Relative test size. Default=100\n"
+ " --stats Show statistics at the end\n"
+ " --testset T Run test-set T\n"
+ " --trace Turn on SQL tracing\n"
+ " --utf16be Set text encoding to UTF-16BE\n"
+ " --utf16le Set text encoding to UTF-16LE\n"
+ " --verify Run additional verification steps.\n"
+ " --without-rowid Use WITHOUT ROWID where appropriate\n"
+;
+
+
+#include "sqlite3.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+/* All global state is held in this structure */
+static struct Global {
+ sqlite3 *db; /* The open database connection */
+ sqlite3_stmt *pStmt; /* Current SQL statement */
+ sqlite3_int64 iStart; /* Start-time for the current test */
+ sqlite3_int64 iTotal; /* Total time */
+ int bWithoutRowid; /* True for --without-rowid */
+ int bReprepare; /* True to reprepare the SQL on each rerun */
+ int bSqlOnly; /* True to print the SQL once only */
+ int bExplain; /* Print SQL with EXPLAIN prefix */
+ int bVerify; /* Try to verify that results are correct */
+ int szTest; /* Scale factor for test iterations */
+ const char *zWR; /* Might be WITHOUT ROWID */
+ const char *zNN; /* Might be NOT NULL */
+ const char *zPK; /* Might be UNIQUE or PRIMARY KEY */
+ unsigned int x, y; /* Pseudo-random number generator state */
+ int nResult; /* Size of the current result */
+ char zResult[3000]; /* Text of the current result */
+} g;
+
+
+/* Print an error message and exit */
+static void fatal_error(const char *zMsg, ...){
+ va_list ap;
+ va_start(ap, zMsg);
+ vfprintf(stderr, zMsg, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/*
+** Return the value of a hexadecimal digit. Return -1 if the input
+** is not a hex digit.
+*/
+static int hexDigitValue(char c){
+ if( c>='0' && c<='9' ) return c - '0';
+ if( c>='a' && c<='f' ) return c - 'a' + 10;
+ if( c>='A' && c<='F' ) return c - 'A' + 10;
+ return -1;
+}
+
+/* Provide an alternative to sqlite3_stricmp() in older versions of
+** SQLite */
+#if SQLITE_VERSION_NUMBER<3007011
+# define sqlite3_stricmp strcmp
+#endif
+
+/*
+** Interpret zArg as an integer value, possibly with suffixes.
+*/
+static int integerValue(const char *zArg){
+ sqlite3_int64 v = 0;
+ static const struct { char *zSuffix; int iMult; } aMult[] = {
+ { "KiB", 1024 },
+ { "MiB", 1024*1024 },
+ { "GiB", 1024*1024*1024 },
+ { "KB", 1000 },
+ { "MB", 1000000 },
+ { "GB", 1000000000 },
+ { "K", 1000 },
+ { "M", 1000000 },
+ { "G", 1000000000 },
+ };
+ int i;
+ int isNeg = 0;
+ if( zArg[0]=='-' ){
+ isNeg = 1;
+ zArg++;
+ }else if( zArg[0]=='+' ){
+ zArg++;
+ }
+ if( zArg[0]=='0' && zArg[1]=='x' ){
+ int x;
+ zArg += 2;
+ while( (x = hexDigitValue(zArg[0]))>=0 ){
+ v = (v<<4) + x;
+ zArg++;
+ }
+ }else{
+ while( isdigit(zArg[0]) ){
+ v = v*10 + zArg[0] - '0';
+ zArg++;
+ }
+ }
+ for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
+ if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
+ v *= aMult[i].iMult;
+ break;
+ }
+ }
+ if( v>0x7fffffff ) fatal_error("parameter too large - max 2147483648");
+ return (int)(isNeg? -v : v);
+}
+
+/* Return the current wall-clock time, in milliseconds */
+sqlite3_int64 speedtest1_timestamp(void){
+ static sqlite3_vfs *clockVfs = 0;
+ sqlite3_int64 t;
+ if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
+#if SQLITE_VERSION_NUMBER>=3007000
+ if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
+ clockVfs->xCurrentTimeInt64(clockVfs, &t);
+ }else
+#endif
+ {
+ double r;
+ clockVfs->xCurrentTime(clockVfs, &r);
+ t = (sqlite3_int64)(r*86400000.0);
+ }
+ return t;
+}
+
+/* Return a pseudo-random unsigned integer */
+unsigned int speedtest1_random(void){
+ g.x = (g.x>>1) ^ ((1+~(g.x&1)) & 0xd0000001);
+ g.y = g.y*1103515245 + 12345;
+ return g.x ^ g.y;
+}
+
+/* Map the value in within the range of 1...limit into another
+** number in a way that is chatic and invertable.
+*/
+unsigned swizzle(unsigned in, unsigned limit){
+ unsigned out = 0;
+ while( limit ){
+ out = (out<<1) | (in&1);
+ in >>= 1;
+ limit >>= 1;
+ }
+ return out;
+}
+
+/* Round up a number so that it is a power of two minus one
+*/
+unsigned roundup_allones(unsigned limit){
+ unsigned m = 1;
+ while( m<limit ) m = (m<<1)+1;
+ return m;
+}
+
+/* The speedtest1_numbername procedure below converts its argment (an integer)
+** into a string which is the English-language name for that number.
+** The returned string should be freed with sqlite3_free().
+**
+** Example:
+**
+** speedtest1_numbername(123) -> "one hundred twenty three"
+*/
+int speedtest1_numbername(unsigned int n, char *zOut, int nOut){
+ static const char *ones[] = { "zero", "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "ten", "eleven", "twelve",
+ "thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
+ "eighteen", "nineteen" };
+ static const char *tens[] = { "", "ten", "twenty", "thirty", "forty",
+ "fifty", "sixty", "seventy", "eighty", "ninety" };
+ int i = 0;
+
+ if( n>=1000000000 ){
+ i += speedtest1_numbername(n/1000000000, zOut+i, nOut-i);
+ sqlite3_snprintf(nOut-i, zOut+i, " billion");
+ i += (int)strlen(zOut+i);
+ n = n % 1000000000;
+ }
+ if( n>=1000000 ){
+ if( i && i<nOut-1 ) zOut[i++] = ' ';
+ i += speedtest1_numbername(n/1000000, zOut+i, nOut-i);
+ sqlite3_snprintf(nOut-i, zOut+i, " million");
+ i += (int)strlen(zOut+i);
+ n = n % 1000000;
+ }
+ if( n>=1000 ){
+ if( i && i<nOut-1 ) zOut[i++] = ' ';
+ i += speedtest1_numbername(n/1000, zOut+i, nOut-i);
+ sqlite3_snprintf(nOut-i, zOut+i, " thousand");
+ i += (int)strlen(zOut+i);
+ n = n % 1000;
+ }
+ if( n>=100 ){
+ if( i && i<nOut-1 ) zOut[i++] = ' ';
+ sqlite3_snprintf(nOut-i, zOut+i, "%s hundred", ones[n/100]);
+ i += (int)strlen(zOut+i);
+ n = n % 100;
+ }
+ if( n>=20 ){
+ if( i && i<nOut-1 ) zOut[i++] = ' ';
+ sqlite3_snprintf(nOut-i, zOut+i, "%s", tens[n/10]);
+ i += (int)strlen(zOut+i);
+ n = n % 10;
+ }
+ if( n>0 ){
+ if( i && i<nOut-1 ) zOut[i++] = ' ';
+ sqlite3_snprintf(nOut-i, zOut+i, "%s", ones[n]);
+ i += (int)strlen(zOut+i);
+ }
+ if( i==0 ){
+ sqlite3_snprintf(nOut-i, zOut+i, "zero");
+ i += (int)strlen(zOut+i);
+ }
+ return i;
+}
+
+
+/* Start a new test case */
+#define NAMEWIDTH 60
+static const char zDots[] =
+ ".......................................................................";
+void speedtest1_begin_test(int iTestNum, const char *zTestName, ...){
+ int n = (int)strlen(zTestName);
+ char *zName;
+ va_list ap;
+ va_start(ap, zTestName);
+ zName = sqlite3_vmprintf(zTestName, ap);
+ va_end(ap);
+ n = (int)strlen(zName);
+ if( n>NAMEWIDTH ){
+ zName[NAMEWIDTH] = 0;
+ n = NAMEWIDTH;
+ }
+ if( g.bSqlOnly ){
+ printf("/* %4d - %s%.*s */\n", iTestNum, zName, NAMEWIDTH-n, zDots);
+ }else{
+ printf("%4d - %s%.*s ", iTestNum, zName, NAMEWIDTH-n, zDots);
+ fflush(stdout);
+ }
+ sqlite3_free(zName);
+ g.nResult = 0;
+ g.iStart = speedtest1_timestamp();
+ g.x = 0xad131d0b;
+ g.y = 0x44f9eac8;
+}
+
+/* Complete a test case */
+void speedtest1_end_test(void){
+ sqlite3_int64 iElapseTime = speedtest1_timestamp() - g.iStart;
+ if( !g.bSqlOnly ){
+ g.iTotal += iElapseTime;
+ printf("%4d.%03ds\n", (int)(iElapseTime/1000), (int)(iElapseTime%1000));
+ }
+ if( g.pStmt ){
+ sqlite3_finalize(g.pStmt);
+ g.pStmt = 0;
+ }
+}
+
+/* Report end of testing */
+void speedtest1_final(void){
+ if( !g.bSqlOnly ){
+ printf(" TOTAL%.*s %4d.%03ds\n", NAMEWIDTH-5, zDots,
+ (int)(g.iTotal/1000), (int)(g.iTotal%1000));
+ }
+}
+
+/* Print an SQL statement to standard output */
+static void printSql(const char *zSql){
+ int n = (int)strlen(zSql);
+ while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ){ n--; }
+ if( g.bExplain ) printf("EXPLAIN ");
+ printf("%.*s;\n", n, zSql);
+ if( g.bExplain
+#if SQLITE_VERSION_NUMBER>=3007010
+ && ( sqlite3_strglob("CREATE *", zSql)==0
+ || sqlite3_strglob("DROP *", zSql)==0
+ || sqlite3_strglob("ALTER *", zSql)==0
+ )
+#endif
+ ){
+ printf("%.*s;\n", n, zSql);
+ }
+}
+
+/* Run SQL */
+void speedtest1_exec(const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ if( g.bSqlOnly ){
+ printSql(zSql);
+ }else{
+ char *zErrMsg = 0;
+ int rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
+ if( zErrMsg ) fatal_error("SQL error: %s\n%s\n", zErrMsg, zSql);
+ if( rc!=SQLITE_OK ) fatal_error("exec error: %s\n", sqlite3_errmsg(g.db));
+ }
+ sqlite3_free(zSql);
+}
+
+/* Prepare an SQL statement */
+void speedtest1_prepare(const char *zFormat, ...){
+ va_list ap;
+ char *zSql;
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+ if( g.bSqlOnly ){
+ printSql(zSql);
+ }else{
+ int rc;
+ if( g.pStmt ) sqlite3_finalize(g.pStmt);
+ rc = sqlite3_prepare_v2(g.db, zSql, -1, &g.pStmt, 0);
+ if( rc ){
+ fatal_error("SQL error: %s\n", sqlite3_errmsg(g.db));
+ }
+ }
+ sqlite3_free(zSql);
+}
+
+/* Run an SQL statement previously prepared */
+void speedtest1_run(void){
+ int i, n, len;
+ if( g.bSqlOnly ) return;
+ assert( g.pStmt );
+ g.nResult = 0;
+ while( sqlite3_step(g.pStmt)==SQLITE_ROW ){
+ n = sqlite3_column_count(g.pStmt);
+ for(i=0; i<n; i++){
+ const char *z = (const char*)sqlite3_column_text(g.pStmt, i);
+ if( z==0 ) z = "nil";
+ len = (int)strlen(z);
+ if( g.nResult+len<sizeof(g.zResult)-2 ){
+ if( g.nResult>0 ) g.zResult[g.nResult++] = ' ';
+ memcpy(g.zResult + g.nResult, z, len+1);
+ g.nResult += len;
+ }
+ }
+ }
+ if( g.bReprepare ){
+ sqlite3_stmt *pNew;
+ sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0);
+ sqlite3_finalize(g.pStmt);
+ g.pStmt = pNew;
+ }else{
+ sqlite3_reset(g.pStmt);
+ }
+}
+
+/* The sqlite3_trace() callback function */
+static void traceCallback(void *NotUsed, const char *zSql){
+ int n = (int)strlen(zSql);
+ while( n>0 && (zSql[n-1]==';' || isspace(zSql[n-1])) ) n--;
+ fprintf(stderr,"%.*s;\n", n, zSql);
+}
+
+/* Substitute random() function that gives the same random
+** sequence on each run, for repeatability. */
+static void randomFunc(
+ sqlite3_context *context,
+ int NotUsed,
+ sqlite3_value **NotUsed2
+){
+ sqlite3_result_int64(context, (sqlite3_int64)speedtest1_random());
+}
+
+/* Estimate the square root of an integer */
+static int est_square_root(int x){
+ int y0 = x/2;
+ int y1;
+ int n;
+ for(n=0; y0>0 && n<10; n++){
+ y1 = (y0 + x/y0)/2;
+ if( y1==y0 ) break;
+ y0 = y1;
+ }
+ return y0;
+}
+
+/*
+** The main and default testset
+*/
+void testset_main(void){
+ int i; /* Loop counter */
+ int n; /* iteration count */
+ int sz; /* Size of the tables */
+ int maxb; /* Maximum swizzled value */
+ unsigned x1, x2; /* Parameters */
+ int len; /* Length of the zNum[] string */
+ char zNum[2000]; /* A number name */
+
+ sz = n = g.szTest*500;
+ maxb = roundup_allones(sz);
+ speedtest1_begin_test(100, "%d INSERTs into table with no index", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_exec("CREATE TABLE t1(a INTEGER %s, b INTEGER %s, c TEXT %s);",
+ g.zNN, g.zNN, g.zNN);
+ speedtest1_prepare("INSERT INTO t1 VALUES(?1,?2,?3); -- %d times", n);
+ for(i=1; i<=n; i++){
+ x1 = swizzle(i,maxb);
+ speedtest1_numbername(x1, zNum, sizeof(zNum));
+ sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1);
+ sqlite3_bind_int(g.pStmt, 2, i);
+ sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz;
+ speedtest1_begin_test(110, "%d ordered INSERTS with one index/PK", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_exec("CREATE TABLE t2(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s",
+ g.zNN, g.zPK, g.zNN, g.zNN, g.zWR);
+ speedtest1_prepare("INSERT INTO t2 VALUES(?1,?2,?3); -- %d times", n);
+ for(i=1; i<=n; i++){
+ x1 = swizzle(i,maxb);
+ speedtest1_numbername(x1, zNum, sizeof(zNum));
+ sqlite3_bind_int(g.pStmt, 1, i);
+ sqlite3_bind_int64(g.pStmt, 2, (sqlite3_int64)x1);
+ sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz;
+ speedtest1_begin_test(120, "%d unordered INSERTS with one index/PK", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_exec("CREATE TABLE t3(a INTEGER %s %s, b INTEGER %s, c TEXT %s) %s",
+ g.zNN, g.zPK, g.zNN, g.zNN, g.zWR);
+ speedtest1_prepare("INSERT INTO t3 VALUES(?1,?2,?3); -- %d times", n);
+ for(i=1; i<=n; i++){
+ x1 = swizzle(i,maxb);
+ speedtest1_numbername(x1, zNum, sizeof(zNum));
+ sqlite3_bind_int(g.pStmt, 2, i);
+ sqlite3_bind_int64(g.pStmt, 1, (sqlite3_int64)x1);
+ sqlite3_bind_text(g.pStmt, 3, zNum, -1, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = 25;
+ speedtest1_begin_test(130, "%d SELECTS, numeric BETWEEN, unindexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT count(*), avg(b), sum(length(c)) FROM t1\n"
+ " WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ x2 = speedtest1_random()%10 + sz/5000 + x1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = 10;
+ speedtest1_begin_test(140, "%d SELECTS, LIKE, unindexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT count(*), avg(b), sum(length(c)) FROM t1\n"
+ " WHERE c LIKE ?1; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ zNum[0] = '%';
+ len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2);
+ zNum[len] = '%';
+ zNum[len+1] = 0;
+ sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = 10;
+ speedtest1_begin_test(142, "%d SELECTS w/ORDER BY, unindexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT a, b, c FROM t1 WHERE c LIKE ?1\n"
+ " ORDER BY a; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ zNum[0] = '%';
+ len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2);
+ zNum[len] = '%';
+ zNum[len+1] = 0;
+ sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ n = 10; //g.szTest/5;
+ speedtest1_begin_test(145, "%d SELECTS w/ORDER BY and LIMIT, unindexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT a, b, c FROM t1 WHERE c LIKE ?1\n"
+ " ORDER BY a LIMIT 10; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ zNum[0] = '%';
+ len = speedtest1_numbername(i, zNum+1, sizeof(zNum)-2);
+ zNum[len] = '%';
+ zNum[len+1] = 0;
+ sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(150, "CREATE INDEX five times");
+ speedtest1_exec("BEGIN;");
+ speedtest1_exec("CREATE UNIQUE INDEX t1b ON t1(b);");
+ speedtest1_exec("CREATE INDEX t1c ON t1(c);");
+ speedtest1_exec("CREATE UNIQUE INDEX t2b ON t2(b);");
+ speedtest1_exec("CREATE INDEX t2c ON t2(c DESC);");
+ speedtest1_exec("CREATE INDEX t3bc ON t3(b,c);");
+ speedtest1_exec("COMMIT;");
+ speedtest1_end_test();
+
+
+ n = sz/5;
+ speedtest1_begin_test(160, "%d SELECTS, numeric BETWEEN, indexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT count(*), avg(b), sum(length(c)) FROM t1\n"
+ " WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ x2 = speedtest1_random()%10 + sz/5000 + x1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz/5;
+ speedtest1_begin_test(161, "%d SELECTS, numeric BETWEEN, PK", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT count(*), avg(b), sum(length(c)) FROM t2\n"
+ " WHERE a BETWEEN ?1 AND ?2; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ x2 = speedtest1_random()%10 + sz/5000 + x1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz/5;
+ speedtest1_begin_test(170, "%d SELECTS, text BETWEEN, indexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT count(*), avg(b), sum(length(c)) FROM t1\n"
+ " WHERE c BETWEEN ?1 AND (?1||'~'); -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = swizzle(i, maxb);
+ len = speedtest1_numbername(x1, zNum, sizeof(zNum)-1);
+ sqlite3_bind_text(g.pStmt, 1, zNum, len, SQLITE_STATIC);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ n = sz;
+ speedtest1_begin_test(180, "%d INSERTS with three indexes", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_exec(
+ "CREATE TABLE t4(\n"
+ " a INTEGER %s %s,\n"
+ " b INTEGER %s,\n"
+ " c TEXT %s\n"
+ ") %s",
+ g.zNN, g.zPK, g.zNN, g.zNN, g.zWR);
+ speedtest1_exec("CREATE INDEX t4b ON t4(b)");
+ speedtest1_exec("CREATE INDEX t4c ON t4(c)");
+ speedtest1_exec("INSERT INTO t4 SELECT * FROM t1");
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ n = sz;
+ speedtest1_begin_test(190, "DELETE and REFILL one table", n);
+ speedtest1_exec("DELETE FROM t2;");
+ speedtest1_exec("INSERT INTO t2 SELECT * FROM t1;");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(200, "VACUUM");
+ speedtest1_exec("VACUUM");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(210, "ALTER TABLE ADD COLUMN, and query");
+ speedtest1_exec("ALTER TABLE t2 ADD COLUMN d DEFAULT 123");
+ speedtest1_exec("SELECT sum(d) FROM t2");
+ speedtest1_end_test();
+
+
+ n = sz/5;
+ speedtest1_begin_test(230, "%d UPDATES, numeric BETWEEN, indexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "UPDATE t2 SET d=b*2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb;
+ x2 = speedtest1_random()%10 + sz/5000 + x1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz;
+ speedtest1_begin_test(240, "%d UPDATES of individual rows", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "UPDATE t2 SET d=b*3 WHERE a=?1; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%sz + 1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ speedtest1_begin_test(250, "One big UPDATE of the whole %d-row table", sz);
+ speedtest1_exec("UPDATE t2 SET d=b*4");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(260, "Query added column after filling");
+ speedtest1_exec("SELECT sum(d) FROM t2");
+ speedtest1_end_test();
+
+
+
+ n = sz/5;
+ speedtest1_begin_test(270, "%d DELETEs, numeric BETWEEN, indexed", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "DELETE FROM t2 WHERE b BETWEEN ?1 AND ?2; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%maxb + 1;
+ x2 = speedtest1_random()%10 + sz/5000 + x1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ n = sz;
+ speedtest1_begin_test(280, "%d DELETEs of individual rows", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "DELETE FROM t3 WHERE a=?1; -- %d times", n
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%sz + 1;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(290, "Refill two %d-row tables using REPLACE", sz);
+ speedtest1_exec("REPLACE INTO t2(a,b,c) SELECT a,b,c FROM t1");
+ speedtest1_exec("REPLACE INTO t3(a,b,c) SELECT a,b,c FROM t1");
+ speedtest1_end_test();
+
+ speedtest1_begin_test(300, "Refill a %d-row table using (b&1)==(a&1)", sz);
+ speedtest1_exec("DELETE FROM t2;");
+ speedtest1_exec("INSERT INTO t2(a,b,c)\n"
+ " SELECT a,b,c FROM t1 WHERE (b&1)==(a&1);");
+ speedtest1_exec("INSERT INTO t2(a,b,c)\n"
+ " SELECT a,b,c FROM t1 WHERE (b&1)<>(a&1);");
+ speedtest1_end_test();
+
+
+ n = sz/5;
+ speedtest1_begin_test(310, "%d four-ways joins", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_prepare(
+ "SELECT t1.c FROM t1, t2, t3, t4\n"
+ " WHERE t4.a BETWEEN ?1 AND ?2\n"
+ " AND t3.a=t4.b\n"
+ " AND t2.a=t3.b\n"
+ " AND t1.c=t2.c"
+ );
+ for(i=1; i<=n; i++){
+ x1 = speedtest1_random()%sz + 1;
+ x2 = speedtest1_random()%10 + x1 + 4;
+ sqlite3_bind_int(g.pStmt, 1, x1);
+ sqlite3_bind_int(g.pStmt, 2, x2);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ speedtest1_begin_test(320, "subquery in result set", n);
+ speedtest1_prepare(
+ "SELECT sum(a), max(c),\n"
+ " avg((SELECT a FROM t2 WHERE 5+t2.b=t1.b) AND rowid<?1), max(c)\n"
+ " FROM t1 WHERE rowid<?1;"
+ );
+ sqlite3_bind_int(g.pStmt, 1, est_square_root(g.szTest)*50);
+ speedtest1_run();
+ speedtest1_end_test();
+
+ speedtest1_begin_test(980, "PRAGMA integrity_check");
+ speedtest1_exec("PRAGMA integrity_check");
+ speedtest1_end_test();
+
+
+ speedtest1_begin_test(990, "ANALYZE");
+ speedtest1_exec("ANALYZE");
+ speedtest1_end_test();
+}
+
+/*
+** A testset for common table expressions. This exercises code
+** for views, subqueries, co-routines, etc.
+*/
+void testset_cte(void){
+ static const char *azPuzzle[] = {
+ /* Easy */
+ "534...9.."
+ "67.195..."
+ ".98....6."
+ "8...6...3"
+ "4..8.3..1"
+ "....2...6"
+ ".6....28."
+ "...419..5"
+ "...28..79",
+
+ /* Medium */
+ "53....9.."
+ "6..195..."
+ ".98....6."
+ "8...6...3"
+ "4..8.3..1"
+ "....2...6"
+ ".6....28."
+ "...419..5"
+ "....8..79",
+
+ /* Hard */
+ "53......."
+ "6..195..."
+ ".98....6."
+ "8...6...3"
+ "4..8.3..1"
+ "....2...6"
+ ".6....28."
+ "...419..5"
+ "....8..79",
+ };
+ const char *zPuz;
+ double rSpacing;
+ int nElem;
+
+ if( g.szTest<25 ){
+ zPuz = azPuzzle[0];
+ }else if( g.szTest<70 ){
+ zPuz = azPuzzle[1];
+ }else{
+ zPuz = azPuzzle[2];
+ }
+ speedtest1_begin_test(100, "Sudoku with recursive 'digits'");
+ speedtest1_prepare(
+ "WITH RECURSIVE\n"
+ " input(sud) AS (VALUES(?1)),\n"
+ " digits(z,lp) AS (\n"
+ " VALUES('1', 1)\n"
+ " UNION ALL\n"
+ " SELECT CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9\n"
+ " ),\n"
+ " x(s, ind) AS (\n"
+ " SELECT sud, instr(sud, '.') FROM input\n"
+ " UNION ALL\n"
+ " SELECT\n"
+ " substr(s, 1, ind-1) || z || substr(s, ind+1),\n"
+ " instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )\n"
+ " FROM x, digits AS z\n"
+ " WHERE ind>0\n"
+ " AND NOT EXISTS (\n"
+ " SELECT 1\n"
+ " FROM digits AS lp\n"
+ " WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)\n"
+ " OR z.z = substr(s, ((ind-1)%%9) + (lp-1)*9 + 1, 1)\n"
+ " OR z.z = substr(s, (((ind-1)/3) %% 3) * 3\n"
+ " + ((ind-1)/27) * 27 + lp\n"
+ " + ((lp-1) / 3) * 6, 1)\n"
+ " )\n"
+ " )\n"
+ "SELECT s FROM x WHERE ind=0;"
+ );
+ sqlite3_bind_text(g.pStmt, 1, zPuz, -1, SQLITE_STATIC);
+ speedtest1_run();
+ speedtest1_end_test();
+
+ speedtest1_begin_test(200, "Sudoku with VALUES 'digits'");
+ speedtest1_prepare(
+ "WITH RECURSIVE\n"
+ " input(sud) AS (VALUES(?1)),\n"
+ " digits(z,lp) AS (VALUES('1',1),('2',2),('3',3),('4',4),('5',5),\n"
+ " ('6',6),('7',7),('8',8),('9',9)),\n"
+ " x(s, ind) AS (\n"
+ " SELECT sud, instr(sud, '.') FROM input\n"
+ " UNION ALL\n"
+ " SELECT\n"
+ " substr(s, 1, ind-1) || z || substr(s, ind+1),\n"
+ " instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )\n"
+ " FROM x, digits AS z\n"
+ " WHERE ind>0\n"
+ " AND NOT EXISTS (\n"
+ " SELECT 1\n"
+ " FROM digits AS lp\n"
+ " WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)\n"
+ " OR z.z = substr(s, ((ind-1)%%9) + (lp-1)*9 + 1, 1)\n"
+ " OR z.z = substr(s, (((ind-1)/3) %% 3) * 3\n"
+ " + ((ind-1)/27) * 27 + lp\n"
+ " + ((lp-1) / 3) * 6, 1)\n"
+ " )\n"
+ " )\n"
+ "SELECT s FROM x WHERE ind=0;"
+ );
+ sqlite3_bind_text(g.pStmt, 1, zPuz, -1, SQLITE_STATIC);
+ speedtest1_run();
+ speedtest1_end_test();
+
+ rSpacing = 5.0/g.szTest;
+ speedtest1_begin_test(300, "Mandelbrot Set with spacing=%f", rSpacing);
+ speedtest1_prepare(
+ "WITH RECURSIVE \n"
+ " xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+?1 FROM xaxis WHERE x<1.2),\n"
+ " yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+?2 FROM yaxis WHERE y<1.0),\n"
+ " m(iter, cx, cy, x, y) AS (\n"
+ " SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis\n"
+ " UNION ALL\n"
+ " SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m \n"
+ " WHERE (x*x + y*y) < 4.0 AND iter<28\n"
+ " ),\n"
+ " m2(iter, cx, cy) AS (\n"
+ " SELECT max(iter), cx, cy FROM m GROUP BY cx, cy\n"
+ " ),\n"
+ " a(t) AS (\n"
+ " SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '') \n"
+ " FROM m2 GROUP BY cy\n"
+ " )\n"
+ "SELECT group_concat(rtrim(t),x'0a') FROM a;"
+ );
+ sqlite3_bind_double(g.pStmt, 1, rSpacing*.05);
+ sqlite3_bind_double(g.pStmt, 2, rSpacing);
+ speedtest1_run();
+ speedtest1_end_test();
+
+ nElem = 10000*g.szTest;
+ speedtest1_begin_test(400, "EXCEPT operator on %d-element tables", nElem);
+ speedtest1_prepare(
+ "WITH RECURSIVE \n"
+ " t1(x) AS (VALUES(2) UNION ALL SELECT x+2 FROM t1 WHERE x<%d),\n"
+ " t2(y) AS (VALUES(3) UNION ALL SELECT y+3 FROM t2 WHERE y<%d)\n"
+ "SELECT count(x), avg(x) FROM (\n"
+ " SELECT x FROM t1 EXCEPT SELECT y FROM t2 ORDER BY 1\n"
+ ");",
+ nElem, nElem
+ );
+ speedtest1_run();
+ speedtest1_end_test();
+
+}
+
+/* Generate two numbers between 1 and mx. The first number is less than
+** the second. Usually the numbers are near each other but can sometimes
+** be far apart.
+*/
+static void twoCoords(
+ int p1, int p2, /* Parameters adjusting sizes */
+ unsigned mx, /* Range of 1..mx */
+ unsigned *pX0, unsigned *pX1 /* OUT: write results here */
+){
+ unsigned d, x0, x1, span;
+
+ span = mx/100 + 1;
+ if( speedtest1_random()%3==0 ) span *= p1;
+ if( speedtest1_random()%p2==0 ) span = mx/2;
+ d = speedtest1_random()%span + 1;
+ x0 = speedtest1_random()%(mx-d) + 1;
+ x1 = x0 + d;
+ *pX0 = x0;
+ *pX1 = x1;
+}
+
+/* The following routine is an R-Tree geometry callback. It returns
+** true if the object overlaps a slice on the Y coordinate between the
+** two values given as arguments. In other words
+**
+** SELECT count(*) FROM rt1 WHERE id MATCH xslice(10,20);
+**
+** Is the same as saying:
+**
+** SELECT count(*) FROM rt1 WHERE y1>=10 AND y0<=20;
+*/
+static int xsliceGeometryCallback(
+ sqlite3_rtree_geometry *p,
+ int nCoord,
+ double *aCoord,
+ int *pRes
+){
+ *pRes = aCoord[3]>=p->aParam[0] && aCoord[2]<=p->aParam[1];
+ return SQLITE_OK;
+}
+
+/*
+** A testset for the R-Tree virtual table
+*/
+void testset_rtree(int p1, int p2){
+ unsigned i, n;
+ unsigned mxCoord;
+ unsigned x0, x1, y0, y1, z0, z1;
+ unsigned iStep;
+ int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*100 );
+
+ mxCoord = 15000;
+ n = g.szTest*100;
+ speedtest1_begin_test(100, "%d INSERTs into an r-tree", n);
+ speedtest1_exec("BEGIN");
+ speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)");
+ speedtest1_prepare("INSERT INTO rt1(id,x0,x1,y0,y1,z0,z1)"
+ "VALUES(?1,?2,?3,?4,?5,?6,?7)");
+ for(i=1; i<=n; i++){
+ twoCoords(p1, p2, mxCoord, &x0, &x1);
+ twoCoords(p1, p2, mxCoord, &y0, &y1);
+ twoCoords(p1, p2, mxCoord, &z0, &z1);
+ sqlite3_bind_int(g.pStmt, 1, i);
+ sqlite3_bind_int(g.pStmt, 2, x0);
+ sqlite3_bind_int(g.pStmt, 3, x1);
+ sqlite3_bind_int(g.pStmt, 4, y0);
+ sqlite3_bind_int(g.pStmt, 5, y1);
+ sqlite3_bind_int(g.pStmt, 6, z0);
+ sqlite3_bind_int(g.pStmt, 7, z1);
+ speedtest1_run();
+ }
+ speedtest1_exec("COMMIT");
+ speedtest1_end_test();
+
+ speedtest1_begin_test(101, "Copy from rtree to a regular table");
+ speedtest1_exec("CREATE TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
+ speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1");
+ speedtest1_end_test();
+
+ n = g.szTest*20;
+ speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n);
+ speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ aCheck[i] = atoi(g.zResult);
+ }
+ speedtest1_end_test();
+
+ if( g.bVerify ){
+ n = g.szTest*20;
+ speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries");
+ speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ if( aCheck[i]!=atoi(g.zResult) ){
+ fatal_error("Count disagree step %d: %d..%d. %d vs %d",
+ i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
+ }
+ }
+ speedtest1_end_test();
+ }
+
+ n = g.szTest*20;
+ speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n);
+ speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ aCheck[i] = atoi(g.zResult);
+ }
+ speedtest1_end_test();
+
+ if( g.bVerify ){
+ n = g.szTest*20;
+ speedtest1_begin_test(121, "Verify result from 1-D overlap slice queries");
+ speedtest1_prepare("SELECT count(*) FROM t1 WHERE y1>=?1 AND y0<=?2");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ if( aCheck[i]!=atoi(g.zResult) ){
+ fatal_error("Count disagree step %d: %d..%d. %d vs %d",
+ i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
+ }
+ }
+ speedtest1_end_test();
+ }
+
+
+ n = g.szTest*20;
+ speedtest1_begin_test(125, "%d custom geometry callback queries", n);
+ sqlite3_rtree_geometry_callback(g.db, "xslice", xsliceGeometryCallback, 0);
+ speedtest1_prepare("SELECT count(*) FROM rt1 WHERE id MATCH xslice(?1,?2)");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ if( aCheck[i]!=atoi(g.zResult) ){
+ fatal_error("Count disagree step %d: %d..%d. %d vs %d",
+ i, i*iStep, (i+1)*iStep, aCheck[i], atoi(g.zResult));
+ }
+ }
+ speedtest1_end_test();
+
+ n = g.szTest*80;
+ speedtest1_begin_test(130, "%d three-dimensional intersect box queries", n);
+ speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x1>=?1 AND x0<=?2"
+ " AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2");
+ iStep = mxCoord/n;
+ for(i=0; i<n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i*iStep);
+ sqlite3_bind_int(g.pStmt, 2, (i+1)*iStep);
+ speedtest1_run();
+ aCheck[i] = atoi(g.zResult);
+ }
+ speedtest1_end_test();
+
+ n = g.szTest*100;
+ speedtest1_begin_test(140, "%d rowid queries", n);
+ speedtest1_prepare("SELECT * FROM rt1 WHERE id=?1");
+ for(i=1; i<=n; i++){
+ sqlite3_bind_int(g.pStmt, 1, i);
+ speedtest1_run();
+ }
+ speedtest1_end_test();
+}
+
+/*
+** A testset used for debugging speedtest1 itself.
+*/
+void testset_debug1(void){
+ unsigned i, n;
+ unsigned x1, x2;
+ char zNum[2000]; /* A number name */
+
+ n = g.szTest;
+ for(i=1; i<=n; i++){
+ x1 = swizzle(i, n);
+ x2 = swizzle(x1, n);
+ speedtest1_numbername(x1, zNum, sizeof(zNum));
+ printf("%5d %5d %5d %s\n", i, x1, x2, zNum);
+ }
+}
+
+int main(int argc, char **argv){
+ int doAutovac = 0; /* True for --autovacuum */
+ int cacheSize = 0; /* Desired cache size. 0 means default */
+ int doExclusive = 0; /* True for --exclusive */
+ int nHeap = 0, mnHeap = 0; /* Heap size from --heap */
+ int doIncrvac = 0; /* True for --incrvacuum */
+ const char *zJMode = 0; /* Journal mode */
+ const char *zKey = 0; /* Encryption key */
+ int nLook = 0, szLook = 0; /* --lookaside configuration */
+ int noSync = 0; /* True for --nosync */
+ int pageSize = 0; /* Desired page size. 0 means default */
+ int nPCache = 0, szPCache = 0;/* --pcache configuration */
+ int nScratch = 0, szScratch=0;/* --scratch configuration */
+ int showStats = 0; /* True for --stats */
+ const char *zTSet = "main"; /* Which --testset torun */
+ int doTrace = 0; /* True for --trace */
+ const char *zEncoding = 0; /* --utf16be or --utf16le */
+ const char *zDbName = 0; /* Name of the test database */
+
+ void *pHeap = 0; /* Allocated heap space */
+ void *pLook = 0; /* Allocated lookaside space */
+ void *pPCache = 0; /* Allocated storage for pcache */
+ void *pScratch = 0; /* Allocated storage for scratch */
+ int iCur, iHi; /* Stats values, current and "highwater" */
+ int i; /* Loop counter */
+ int rc; /* API return code */
+
+ /* Process command-line arguments */
+ g.zWR = "";
+ g.zNN = "";
+ g.zPK = "UNIQUE";
+ g.szTest = 100;
+ for(i=1; i<argc; i++){
+ const char *z = argv[i];
+ if( z[0]=='-' ){
+ do{ z++; }while( z[0]=='-' );
+ if( strcmp(z,"autovacuum")==0 ){
+ doAutovac = 1;
+ }else if( strcmp(z,"cachesize")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ i++;
+ cacheSize = integerValue(argv[i]);
+ }else if( strcmp(z,"exclusive")==0 ){
+ doExclusive = 1;
+ }else if( strcmp(z,"explain")==0 ){
+ g.bSqlOnly = 1;
+ g.bExplain = 1;
+ }else if( strcmp(z,"heap")==0 ){
+ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]);
+ nHeap = integerValue(argv[i+1]);
+ mnHeap = integerValue(argv[i+2]);
+ i += 2;
+ }else if( strcmp(z,"incrvacuum")==0 ){
+ doIncrvac = 1;
+ }else if( strcmp(z,"journal")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ zJMode = argv[++i];
+ }else if( strcmp(z,"key")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ zKey = argv[++i];
+ }else if( strcmp(z,"lookaside")==0 ){
+ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]);
+ nLook = integerValue(argv[i+1]);
+ szLook = integerValue(argv[i+2]);
+ i += 2;
+ }else if( strcmp(z,"nosync")==0 ){
+ noSync = 1;
+ }else if( strcmp(z,"notnull")==0 ){
+ g.zNN = "NOT NULL";
+ }else if( strcmp(z,"pagesize")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ pageSize = integerValue(argv[++i]);
+ }else if( strcmp(z,"pcache")==0 ){
+ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]);
+ nPCache = integerValue(argv[i+1]);
+ szPCache = integerValue(argv[i+2]);
+ i += 2;
+ }else if( strcmp(z,"primarykey")==0 ){
+ g.zPK = "PRIMARY KEY";
+ }else if( strcmp(z,"reprepare")==0 ){
+ g.bReprepare = 1;
+ }else if( strcmp(z,"scratch")==0 ){
+ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]);
+ nScratch = integerValue(argv[i+1]);
+ szScratch = integerValue(argv[i+2]);
+ i += 2;
+ }else if( strcmp(z,"sqlonly")==0 ){
+ g.bSqlOnly = 1;
+ }else if( strcmp(z,"size")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ g.szTest = integerValue(argv[++i]);
+ }else if( strcmp(z,"stats")==0 ){
+ showStats = 1;
+ }else if( strcmp(z,"testset")==0 ){
+ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
+ zTSet = argv[++i];
+ }else if( strcmp(z,"trace")==0 ){
+ doTrace = 1;
+ }else if( strcmp(z,"utf16le")==0 ){
+ zEncoding = "utf16le";
+ }else if( strcmp(z,"utf16be")==0 ){
+ zEncoding = "utf16be";
+ }else if( strcmp(z,"verify")==0 ){
+ g.bVerify = 1;
+ }else if( strcmp(z,"without-rowid")==0 ){
+ g.zWR = "WITHOUT ROWID";
+ g.zPK = "PRIMARY KEY";
+ }else if( strcmp(z, "help")==0 || strcmp(z,"?")==0 ){
+ printf(zHelp, argv[0]);
+ exit(0);
+ }else{
+ fatal_error("unknown option: %s\nUse \"%s -?\" for help\n",
+ argv[i], argv[0]);
+ }
+ }else if( zDbName==0 ){
+ zDbName = argv[i];
+ }else{
+ fatal_error("surplus argument: %s\nUse \"%s -?\" for help\n",
+ argv[i], argv[0]);
+ }
+ }
+#if 0
+ if( zDbName==0 ){
+ fatal_error(zHelp, argv[0]);
+ }
+#endif
+ if( nHeap>0 ){
+ pHeap = malloc( nHeap );
+ if( pHeap==0 ) fatal_error("cannot allocate %d-byte heap\n", nHeap);
+ rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, mnHeap);
+ if( rc ) fatal_error("heap configuration failed: %d\n", rc);
+ }
+ if( nPCache>0 && szPCache>0 ){
+ pPCache = malloc( nPCache*(sqlite3_int64)szPCache );
+ if( pPCache==0 ) fatal_error("cannot allocate %lld-byte pcache\n",
+ nPCache*(sqlite3_int64)szPCache);
+ rc = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pPCache, szPCache, nPCache);
+ if( rc ) fatal_error("pcache configuration failed: %d\n", rc);
+ }
+ if( nScratch>0 && szScratch>0 ){
+ pScratch = malloc( nScratch*(sqlite3_int64)szScratch );
+ if( pScratch==0 ) fatal_error("cannot allocate %lld-byte scratch\n",
+ nScratch*(sqlite3_int64)szScratch);
+ rc = sqlite3_config(SQLITE_CONFIG_SCRATCH, pScratch, szScratch, nScratch);
+ if( rc ) fatal_error("scratch configuration failed: %d\n", rc);
+ }
+ if( nLook>0 ){
+ sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0);
+ }
+
+ /* Open the database and the input file */
+ if( sqlite3_open(zDbName, &g.db) ){
+ fatal_error("Cannot open database file: %s\n", zDbName);
+ }
+ if( nLook>0 && szLook>0 ){
+ pLook = malloc( nLook*szLook );
+ rc = sqlite3_db_config(g.db, SQLITE_DBCONFIG_LOOKASIDE, pLook, szLook,nLook);
+ if( rc ) fatal_error("lookaside configuration failed: %d\n", rc);
+ }
+
+ /* Set database connection options */
+ sqlite3_create_function(g.db, "random", 0, SQLITE_UTF8, 0, randomFunc, 0, 0);
+ if( doTrace ) sqlite3_trace(g.db, traceCallback, 0);
+ if( zKey ){
+ speedtest1_exec("PRAGMA key('%s')", zKey);
+ }
+ if( zEncoding ){
+ speedtest1_exec("PRAGMA encoding=%s", zEncoding);
+ }
+ if( doAutovac ){
+ speedtest1_exec("PRAGMA auto_vacuum=FULL");
+ }else if( doIncrvac ){
+ speedtest1_exec("PRAGMA auto_vacuum=INCREMENTAL");
+ }
+ if( pageSize ){
+ speedtest1_exec("PRAGMA page_size=%d", pageSize);
+ }
+ if( cacheSize ){
+ speedtest1_exec("PRAGMA cache_size=%d", cacheSize);
+ }
+ if( noSync ) speedtest1_exec("PRAGMA synchronous=OFF");
+ if( doExclusive ){
+ speedtest1_exec("PRAGMA locking_mode=EXCLUSIVE");
+ }
+ if( zJMode ){
+ speedtest1_exec("PRAGMA journal_mode=%s", zJMode);
+ }
+
+ if( g.bExplain ) printf(".explain\n.echo on\n");
+ if( strcmp(zTSet,"main")==0 ){
+ testset_main();
+ }else if( strcmp(zTSet,"debug1")==0 ){
+ testset_debug1();
+ }else if( strcmp(zTSet,"cte")==0 ){
+ testset_cte();
+ }else if( strcmp(zTSet,"rtree")==0 ){
+ testset_rtree(6, 147);
+ }else{
+ fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n",
+ zTSet);
+ }
+ speedtest1_final();
+
+ /* Database connection statistics printed after both prepared statements
+ ** have been finalized */
+#if SQLITE_VERSION_NUMBER>=3007009
+ if( showStats ){
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHi, 0);
+ printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHi);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHi, 0);
+ printf("-- Successful lookasides: %d\n", iHi);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHi,0);
+ printf("-- Lookaside size faults: %d\n", iHi);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHi,0);
+ printf("-- Lookaside OOM faults: %d\n", iHi);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHi, 0);
+ printf("-- Pager Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHi, 1);
+ printf("-- Page cache hits: %d\n", iCur);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHi, 1);
+ printf("-- Page cache misses: %d\n", iCur);
+#if SQLITE_VERSION_NUMBER>=3007012
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHi, 1);
+ printf("-- Page cache writes: %d\n", iCur);
+#endif
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHi, 0);
+ printf("-- Schema Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(g.db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHi, 0);
+ printf("-- Statement Heap Usage: %d bytes\n", iCur);
+ }
+#endif
+
+ sqlite3_close(g.db);
+
+ /* Global memory usage statistics printed after the database connection
+ ** has closed. Memory usage should be zero at this point. */
+ if( showStats ){
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHi, 0);
+ printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHi);
+#if SQLITE_VERSION_NUMBER>=3007000
+ sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHi, 0);
+ printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHi);
+#endif
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHi, 0);
+ printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHi, 0);
+ printf("-- Scratch Overflow Bytes: %d (max %d)\n", iCur,iHi);
+ sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Allocation: %d bytes\n",iHi);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Pcache Allocation: %d bytes\n",iHi);
+ sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHi, 0);
+ printf("-- Largest Scratch Allocation: %d bytes\n", iHi);
+ }
+
+ /* Release memory */
+ free( pLook );
+ free( pPCache );
+ free( pScratch );
+ free( pHeap );
+ return 0;
+}
diff --git a/test/spellfix.test b/test/spellfix.test
index dfa487a..21383c2 100644
--- a/test/spellfix.test
+++ b/test/spellfix.test
@@ -95,6 +95,9 @@ do_test 1.10 {
do_execsql_test 1.11 {
SELECT next_char('re','vocab','w');
} {a}
+do_execsql_test 1.11sub {
+ SELECT next_char('re','(SELECT w AS x FROM vocab)','x');
+} {a}
do_execsql_test 1.12 {
SELECT next_char('r','vocab','w');
} {ae}
@@ -105,6 +108,22 @@ do_test 1.14 {
catchsql {SELECT next_char('','xyzzy','a')}
} {1 {no such table: xyzzy}}
+do_execsql_test 1.20 {
+ CREATE TABLE vocab2(w TEXT);
+ CREATE INDEX vocab2w ON vocab2(w COLLATE nocase);
+ INSERT INTO vocab2 VALUES('abc'), ('ABD'), ('aBe'), ('AbF');
+ SELECT next_char('ab', 'vocab2', 'w', null, 'nocase');
+} {cDeF}
+do_execsql_test 1.21 {
+ SELECT next_char('ab','vocab2','w',null,null);
+} {c}
+do_execsql_test 1.22 {
+ SELECT next_char('AB','vocab2','w',null,'NOCASE');
+} {cDeF}
+do_execsql_test 1.23 {
+ SELECT next_char('ab','vocab2','w',null,'binary');
+} {c}
+
do_execsql_test 2.1 {
CREATE VIRTUAL TABLE t2 USING spellfix1;
INSERT INTO t2 (word, soundslike) VALUES('school', 'skuul');
@@ -202,6 +221,53 @@ foreach {tn word res} {
} $res
}
+#-------------------------------------------------------------------------
+# Try some queries by rowid.
+#
+do_execsql_test 6.1.1 {
+ SELECT word FROM t3 WHERE rowid = 10;
+} {keener}
+do_execsql_test 6.1.2 {
+ SELECT word, distance FROM t3 WHERE rowid = 10;
+} {keener {}}
+do_execsql_test 6.1.3 {
+ SELECT word, distance FROM t3 WHERE rowid = 10 AND word MATCH 'kiiner';
+} {keener 300}
+
+ifcapable trace {
+ proc trace_callback {sql} {
+ if {[string range $sql 0 2] == "-- "} {
+ lappend ::trace [string range $sql 3 end]
+ }
+ }
+
+ proc do_tracesql_test {tn sql {res {}}} {
+ set ::trace [list]
+ uplevel [list do_test $tn [subst -nocommands {
+ set vals [execsql {$sql}]
+ concat [set vals] [set ::trace]
+ }] [list {*}$res]]
+ }
+
+ db trace trace_callback
+ do_tracesql_test 6.2.1 {
+ SELECT word FROM t3 WHERE rowid = 10;
+ } {keener
+ {SELECT word, rank, NULL, langid, id FROM "main"."t3_vocab" WHERE rowid=?}
+ }
+ do_tracesql_test 6.2.2 {
+ SELECT word, distance FROM t3 WHERE rowid = 10;
+ } {keener {}
+ {SELECT word, rank, NULL, langid, id FROM "main"."t3_vocab" WHERE rowid=?}
+ }
+ do_tracesql_test 6.2.3 {
+ SELECT word, distance FROM t3 WHERE rowid = 10 AND word MATCH 'kiiner';
+ } {keener 300
+ {SELECT id, word, rank, k1 FROM "main"."t3_vocab" WHERE langid=0 AND k2>=?1 AND k2<?2}
+ }
+}
+
+
finish_test
diff --git a/test/stat.test b/test/stat.test
index ac88f7a..f0447e4 100644
--- a/test/stat.test
+++ b/test/stat.test
@@ -112,7 +112,17 @@ do_execsql_test stat-2.1 {
t3 /00e/ 22 leaf 2 730 276 366 \
t3 /00f/ 23 leaf 2 738 268 370 \
]
-do_execsql_test stat-2.2 { DROP TABLE t3 } {}
+
+# With every index entry overflowing, make sure no pages are missed
+# (other than the locking page which is 64 in this test build.)
+#
+do_execsql_test stat-2.2 {
+ UPDATE t3 SET a=a||hex(randomblob(700));
+ VACUUM;
+ SELECT pageno FROM stat EXCEPT SELECT pageno-1 FROM stat;
+} {64 136}
+
+do_execsql_test stat-2.3 { DROP TABLE t3; VACUUM; } {}
do_execsql_test stat-3.1 {
CREATE TABLE t4(x);
@@ -122,22 +132,22 @@ do_execsql_test stat-3.1 {
FROM stat WHERE name != 'sqlite_master';
} [list \
i4 / 3 leaf 1 103 905 7782 \
- i4 /000+000000 9 overflow 0 1020 0 0 \
- i4 /000+000001 10 overflow 0 1020 0 0 \
- i4 /000+000002 11 overflow 0 1020 0 0 \
- i4 /000+000003 12 overflow 0 1020 0 0 \
- i4 /000+000004 13 overflow 0 1020 0 0 \
- i4 /000+000005 14 overflow 0 1020 0 0 \
- i4 /000+000006 15 overflow 0 1020 0 0 \
- i4 /000+000007 16 overflow 0 539 481 0 \
+ i4 /000+000000 4 overflow 0 1020 0 0 \
+ i4 /000+000001 5 overflow 0 1020 0 0 \
+ i4 /000+000002 6 overflow 0 1020 0 0 \
+ i4 /000+000003 7 overflow 0 1020 0 0 \
+ i4 /000+000004 8 overflow 0 1020 0 0 \
+ i4 /000+000005 9 overflow 0 1020 0 0 \
+ i4 /000+000006 10 overflow 0 1020 0 0 \
+ i4 /000+000007 11 overflow 0 539 481 0 \
t4 / 2 leaf 1 640 367 7780 \
- t4 /000+000000 22 overflow 0 1020 0 0 \
- t4 /000+000001 23 overflow 0 1020 0 0 \
- t4 /000+000002 21 overflow 0 1020 0 0 \
- t4 /000+000003 20 overflow 0 1020 0 0 \
- t4 /000+000004 19 overflow 0 1020 0 0 \
- t4 /000+000005 18 overflow 0 1020 0 0 \
- t4 /000+000006 17 overflow 0 1020 0 0 \
+ t4 /000+000000 12 overflow 0 1020 0 0 \
+ t4 /000+000001 13 overflow 0 1020 0 0 \
+ t4 /000+000002 14 overflow 0 1020 0 0 \
+ t4 /000+000003 15 overflow 0 1020 0 0 \
+ t4 /000+000004 16 overflow 0 1020 0 0 \
+ t4 /000+000005 17 overflow 0 1020 0 0 \
+ t4 /000+000006 18 overflow 0 1020 0 0 \
]
do_execsql_test stat-4.1 {
@@ -146,15 +156,14 @@ do_execsql_test stat-4.1 {
SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
FROM stat WHERE name = 't5' OR name = 'i5';
} [list \
- i5 / 5 leaf 0 0 1016 0 \
- t5 / 4 leaf 0 0 1016 0 \
+ i5 / 20 leaf 0 0 1016 0 \
+ t5 / 19 leaf 0 0 1016 0 \
]
db close
forcedelete test.db
sqlite3 db test.db
register_dbstat_vtab db
-breakpoint
do_execsql_test stat-5.1 {
PRAGMA auto_vacuum = OFF;
CREATE VIRTUAL TABLE temp.stat USING dbstat;
diff --git a/test/subquery.test b/test/subquery.test
index f601d3f..93c3f28 100644
--- a/test/subquery.test
+++ b/test/subquery.test
@@ -241,8 +241,11 @@ do_test subquery-2.5.3.1 {
} {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 {}}
+ execsql {
+ EXPLAIN QUERY PLAN
+ SELECT * FROM t4 WHERE x IN (SELECT a FROM t3);
+ }
+} {~/t4i/}
do_test subquery-2.5.4 {
execsql {
DROP TABLE t3;
diff --git a/test/syscall.test b/test/syscall.test
index 5bf1225..c2d9979 100644
--- a/test/syscall.test
+++ b/test/syscall.test
@@ -61,6 +61,7 @@ foreach s {
fcntl read pread write pwrite fchmod fallocate
pread64 pwrite64 unlink openDirectory mkdir rmdir
statvfs fchown umask mmap munmap mremap
+ getpagesize
} {
if {[test_syscall exists $s]} {lappend syscall_list $s}
}
diff --git a/test/table.test b/test/table.test
index 4826cb9..656884c 100644
--- a/test/table.test
+++ b/test/table.test
@@ -268,6 +268,7 @@ do_test table-5.2.1 {
DROP TABLE IF EXISTS sqlite_stat1;
DROP TABLE IF EXISTS sqlite_stat2;
DROP TABLE IF EXISTS sqlite_stat3;
+ DROP TABLE IF EXISTS sqlite_stat4;
SELECT name FROM sqlite_master WHERE name GLOB 'sqlite_stat*';
}
} {}
@@ -463,7 +464,7 @@ do_test table-10.1 {
CREATE TABLE t6(a REFERENCES t4(a) NOT NULL);
INSERT INTO t6 VALUES(NULL);
}
-} {1 {t6.a may not be NULL}}
+} {1 {NOT NULL constraint failed: t6.a}}
do_test table-10.2 {
catchsql {
DROP TABLE t6;
@@ -725,4 +726,51 @@ do_test table-15.2 {
execsql {COMMIT}
} {}
+# Ticket 3a88d85f36704eebe134f7f48aebf00cd6438c1a (2014-08-05)
+# The following SQL script segfaults while running the INSERT statement:
+#
+# CREATE TABLE t1(x DEFAULT(max(1)));
+# INSERT INTO t1(rowid) VALUES(1);
+#
+# The problem appears to be the use of an aggregate function as part of
+# the default value for a column. This problem has been in the code since
+# at least 2006-01-01 and probably before that. This problem was detected
+# and reported on the sqlite-users@sqlite.org mailing list by Zsbán Ambrus.
+#
+do_execsql_test table-16.1 {
+ CREATE TABLE t16(x DEFAULT(max(1)));
+ INSERT INTO t16(x) VALUES(123);
+ SELECT rowid, x FROM t16;
+} {1 123}
+do_catchsql_test table-16.2 {
+ INSERT INTO t16(rowid) VALUES(4);
+} {1 {unknown function: max()}}
+do_execsql_test table-16.3 {
+ DROP TABLE t16;
+ CREATE TABLE t16(x DEFAULT(abs(1)));
+ INSERT INTO t16(rowid) VALUES(4);
+ SELECT rowid, x FROM t16;
+} {4 1}
+do_catchsql_test table-16.4 {
+ DROP TABLE t16;
+ CREATE TABLE t16(x DEFAULT(avg(1)));
+ INSERT INTO t16(rowid) VALUES(123);
+ SELECT rowid, x FROM t16;
+} {1 {unknown function: avg()}}
+do_catchsql_test table-16.5 {
+ DROP TABLE t16;
+ CREATE TABLE t16(x DEFAULT(count()));
+ INSERT INTO t16(rowid) VALUES(123);
+ SELECT rowid, x FROM t16;
+} {1 {unknown function: count()}}
+do_catchsql_test table-16.6 {
+ DROP TABLE t16;
+ CREATE TABLE t16(x DEFAULT(group_concat('x',',')));
+ INSERT INTO t16(rowid) VALUES(123);
+ SELECT rowid, x FROM t16;
+} {1 {unknown function: group_concat()}}
+do_catchsql_test table-16.7 {
+ INSERT INTO t16 DEFAULT VALUES;
+} {1 {unknown function: group_concat()}}
+
finish_test
diff --git a/test/tableopts.test b/test/tableopts.test
new file mode 100644
index 0000000..0b2457e
--- /dev/null
+++ b/test/tableopts.test
@@ -0,0 +1,76 @@
+# 2013-10-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.
+#
+#***********************************************************************
+#
+# Test the operation of table-options in the WITH clause of the
+# CREATE TABLE statement.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test tableopt-1.1 {
+ catchsql {
+ CREATE TABLE t1(a,b) WITHOUT rowid;
+ }
+} {1 {PRIMARY KEY missing on table t1}}
+do_test tableopt-1.1b {
+ catchsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT,b) WITHOUT rowid;
+ }
+} {1 {AUTOINCREMENT not allowed on WITHOUT ROWID tables}}
+do_test tableopt-1.2 {
+ catchsql {
+ CREATE TABLE t1(a,b) WITHOUT unknown2;
+ }
+} {1 {unknown table option: unknown2}}
+
+do_execsql_test tableopt-2.1 {
+ CREATE TABLE t1(a, b, c, PRIMARY KEY(a,b)) WITHOUT rowid;
+ INSERT INTO t1 VALUES(1,2,3),(2,3,4);
+ SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;
+} {3 4}
+do_test tableopt-2.1.1 {
+ catchsql {
+ SELECT rowid, * FROM t1;
+ }
+} {1 {no such column: rowid}}
+do_test tableopt-2.1.2 {
+ catchsql {
+ SELECT _rowid_, * FROM t1;
+ }
+} {1 {no such column: _rowid_}}
+do_test tableopt-2.1.3 {
+ catchsql {
+ SELECT oid, * FROM t1;
+ }
+} {1 {no such column: oid}}
+do_execsql_test tableopt-2.2 {
+ VACUUM;
+ SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;
+} {3 4}
+do_test tableopt-2.3 {
+ sqlite3 db2 test.db
+ db2 eval {SELECT c FROM t1 WHERE a IN (1,2) ORDER BY b;}
+} {3 4}
+db2 close
+
+# Make sure the "without" keyword is still usable as a table or
+# column name.
+#
+do_execsql_test tableopt-3.1 {
+ CREATE TABLE without(x INTEGER PRIMARY KEY, without TEXT);
+ INSERT INTO without VALUES(1, 'xyzzy'), (2, 'fizzle');
+ SELECT * FROM without WHERE without='xyzzy';
+} {1 xyzzy}
+
+
+finish_test
diff --git a/test/temptrigger.test b/test/temptrigger.test
index ed1efb9..551c620 100644
--- a/test/temptrigger.test
+++ b/test/temptrigger.test
@@ -13,6 +13,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix temptrigger
ifcapable {!trigger || !shared_cache} { finish_test ; return }
@@ -201,4 +202,78 @@ do_test temptrigger-3.4 {
catch { db close }
catch { db2 close }
+
+#-------------------------------------------------------------------------
+# Test that creating a temp table after a temp trigger on the same name
+# has been created is an error.
+#
+reset_db
+do_execsql_test 4.0 {
+ CREATE TABLE t1(x);
+ CREATE TEMP TRIGGER tr1 BEFORE INSERT ON t1 BEGIN
+ SELECT 1,2,3;
+ END;
+}
+
+do_execsql_test 4.1 {
+ CREATE TEMP TABLE t1(x);
+}
+
+#-------------------------------------------------------------------------
+# Test that no harm is done if the table a temp trigger is attached to is
+# deleted by an external connection.
+#
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t1(x);
+ CREATE TEMP TRIGGER tr1 BEFORE INSERT ON t1 BEGIN SELECT 1,2,3; END;
+}
+
+do_test 5.1 {
+ sqlite3 db2 test.db
+ execsql { DROP TABLE t1 } db2
+} {}
+
+do_execsql_test 5.2 {
+ SELECT * FROM sqlite_master;
+ SELECT * FROM sqlite_temp_master;
+} {
+ trigger tr1 t1 0
+ {CREATE TRIGGER tr1 BEFORE INSERT ON t1 BEGIN SELECT 1,2,3; END}
+}
+db2 close
+
+#-------------------------------------------------------------------------
+# Check that if a second connection creates a table in an attached database
+# with the same name as a table in the main database that has a temp
+# trigger attached to it nothing goes awry.
+#
+reset_db
+forcedelete test.db2
+
+do_execsql_test 6.0 {
+ CREATE TABLE t1(x);
+ CREATE TEMP TRIGGER tr1 BEFORE INSERT ON t1 BEGIN
+ SELECT raise(ABORT, 'error');
+ END;
+ ATTACH 'test.db2' AS aux;
+}
+
+do_test 6.1 {
+ sqlite3 db2 test.db2
+ execsql { CREATE TABLE t1(a, b, c); } db2
+} {}
+
+do_execsql_test 6.2 {
+ SELECT type,name,tbl_name,sql FROM aux.sqlite_master;
+ INSERT INTO aux.t1 VALUES(1,2,3);
+} {
+ table t1 t1 {CREATE TABLE t1(a, b, c)}
+}
+
+do_catchsql_test 6.3 {
+ INSERT INTO main.t1 VALUES(1);
+} {1 error}
+db2 close
+
finish_test
diff --git a/test/tester.tcl b/test/tester.tcl
index 761a36e..3bf92f2 100644
--- a/test/tester.tcl
+++ b/test/tester.tcl
@@ -14,7 +14,7 @@
# $Id: tester.tcl,v 1.143 2009/04/09 01:23:49 drh Exp $
#-------------------------------------------------------------------------
-# The commands provided by the code in this file to help with creating
+# The commands provided by the code in this file to help with creating
# test cases are as follows:
#
# Commands to manipulate the db and the file-system at a high level:
@@ -42,6 +42,7 @@
#
# Commands to execute/explain SQL statements:
#
+# memdbsql SQL
# stepsql DB SQL
# execsql2 SQL
# explain_no_trace SQL
@@ -58,6 +59,7 @@
# do_test TESTNAME SCRIPT EXPECTED
# do_execsql_test TESTNAME SQL EXPECTED
# do_catchsql_test TESTNAME SQL EXPECTED
+# do_timed_execsql_test TESTNAME SQL EXPECTED
#
# Commands providing a lower level interface to the global test counters:
#
@@ -80,7 +82,7 @@
# presql
#
-# Set the precision of FP arithmatic used by the interpreter. And
+# Set the precision of FP arithmatic used by the interpreter. And
# configure SQLite to take database file locks on the page that begins
# 64KB into the database file instead of the one 1GB in. This means
# the code that handles that special case can be tested without creating
@@ -90,7 +92,7 @@ set tcl_precision 15
sqlite3_test_control_pending_byte 0x0010000
-# If the pager codec is available, create a wrapper for the [sqlite3]
+# If the pager codec is available, create a wrapper for the [sqlite3]
# command that appends "-key {xyzzy}" to the command line. i.e. this:
#
# sqlite3 db test.db
@@ -122,7 +124,7 @@ if {[info command sqlite_orig]==""} {
}
set res
} else {
- # This command is not opening a new database connection. Pass the
+ # This command is not opening a new database connection. Pass the
# arguments through to the C implementation as the are.
#
uplevel 1 sqlite_orig $args
@@ -291,6 +293,66 @@ proc do_delete_file {force args} {
}
}
+if {$::tcl_platform(platform) eq "windows"} {
+ proc do_remove_win32_dir {args} {
+ set nRetry [getFileRetries] ;# Maximum number of retries.
+ set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
+
+ foreach dirName $args {
+ # On windows, sometimes even a [remove_win32_dir] can fail just after
+ # a directory is emptied. The cause is usually "tag-alongs" - programs
+ # like anti-virus software, automatic backup tools and various explorer
+ # extensions that keep a file open a little longer than we expect,
+ # causing the delete to fail.
+ #
+ # The solution is to wait a short amount of time before retrying the
+ # removal.
+ #
+ if {$nRetry > 0} {
+ for {set i 0} {$i < $nRetry} {incr i} {
+ set rc [catch {
+ remove_win32_dir $dirName
+ } msg]
+ if {$rc == 0} break
+ if {$nDelay > 0} { after $nDelay }
+ }
+ if {$rc} { error $msg }
+ } else {
+ remove_win32_dir $dirName
+ }
+ }
+ }
+
+ proc do_delete_win32_file {args} {
+ set nRetry [getFileRetries] ;# Maximum number of retries.
+ set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
+
+ foreach fileName $args {
+ # On windows, sometimes even a [delete_win32_file] can fail just after
+ # a file is closed. The cause is usually "tag-alongs" - programs like
+ # anti-virus software, automatic backup tools and various explorer
+ # extensions that keep a file open a little longer than we expect,
+ # causing the delete to fail.
+ #
+ # The solution is to wait a short amount of time before retrying the
+ # delete.
+ #
+ if {$nRetry > 0} {
+ for {set i 0} {$i < $nRetry} {incr i} {
+ set rc [catch {
+ delete_win32_file $fileName
+ } msg]
+ if {$rc == 0} break
+ if {$nDelay > 0} { after $nDelay }
+ }
+ if {$rc} { error $msg }
+ } else {
+ delete_win32_file $fileName
+ }
+ }
+ }
+}
+
proc execpresql {handle args} {
trace remove execution $handle enter [list execpresql $handle]
if {[info exists ::G(perm:presql)]} {
@@ -312,8 +374,8 @@ proc do_not_use_codec {} {
#
if {[info exists cmdlinearg]==0} {
- # Parse any options specified in the $argv array. This script accepts the
- # following options:
+ # Parse any options specified in the $argv array. This script accepts the
+ # following options:
#
# --pause
# --soft-heap-limit=NN
@@ -342,7 +404,7 @@ if {[info exists cmdlinearg]==0} {
foreach a $argv {
switch -regexp -- $a {
{^-+pause$} {
- # Wait for user input before continuing. This is to give the user an
+ # Wait for user input before continuing. This is to give the user an
# opportunity to connect profiling tools to the process.
puts -nonewline "Press RETURN to begin..."
flush stdout
@@ -405,8 +467,8 @@ if {[info exists cmdlinearg]==0} {
# Install the malloc layer used to inject OOM errors. And the 'automatic'
# extensions. This only needs to be done once for the process.
#
- sqlite3_shutdown
- install_malloc_faultsim 1
+ sqlite3_shutdown
+ install_malloc_faultsim 1
sqlite3_initialize
autoinstall_test_functions
@@ -516,7 +578,7 @@ proc incr_ntest {} {
}
-# Invoke the do_test procedure to run a single test
+# Invoke the do_test procedure to run a single test
#
proc do_test {name cmd expected} {
global argv cmdlinearg
@@ -525,7 +587,7 @@ proc do_test {name cmd expected} {
sqlite3_memdebug_settitle $name
-# if {[llength $argv]==0} {
+# if {[llength $argv]==0} {
# set go 1
# } else {
# set go 0
@@ -551,12 +613,38 @@ proc do_test {name cmd expected} {
fail_test $name
} else {
if {[regexp {^~?/.*/$} $expected]} {
+ # "expected" is of the form "/PATTERN/" then the result if correct if
+ # regular expression PATTERN matches the result. "~/PATTERN/" means
+ # the regular expression must not match.
if {[string index $expected 0]=="~"} {
- set re [string map {# {[-0-9.]+}} [string range $expected 2 end-1]]
- set ok [expr {![regexp $re $result]}]
+ set re [string range $expected 2 end-1]
+ if {[string index $re 0]=="*"} {
+ # If the regular expression begins with * then treat it as a glob instead
+ set ok [string match $re $result]
+ } else {
+ set re [string map {# {[-0-9.]+}} $re]
+ set ok [regexp $re $result]
+ }
+ set ok [expr {!$ok}]
} else {
- set re [string map {# {[-0-9.]+}} [string range $expected 1 end-1]]
- set ok [regexp $re $result]
+ set re [string range $expected 1 end-1]
+ if {[string index $re 0]=="*"} {
+ # If the regular expression begins with * then treat it as a glob instead
+ set ok [string match $re $result]
+ } else {
+ set re [string map {# {[-0-9.]+}} $re]
+ set ok [regexp $re $result]
+ }
+ }
+ } elseif {[regexp {^~?\*.*\*$} $expected]} {
+ # "expected" is of the form "*GLOB*" then the result if correct if
+ # glob pattern GLOB matches the result. "~/GLOB/" means
+ # the glob must not match.
+ if {[string index $expected 0]=="~"} {
+ set e [string range $expected 1 end]
+ set ok [expr {![string match $e $result]}]
+ } else {
+ set ok [string match $expected $result]
}
} else {
set ok [expr {[string compare $result $expected]==0}]
@@ -615,13 +703,13 @@ proc do_realnum_test {name cmd expected} {
proc fix_testname {varname} {
upvar $varname testname
- if {[info exists ::testprefix]
+ if {[info exists ::testprefix]
&& [string is digit [string range $testname 0 0]]
} {
set testname "${::testprefix}-$testname"
}
}
-
+
proc do_execsql_test {testname sql {result {}}} {
fix_testname testname
uplevel do_test [list $testname] [list "execsql {$sql}"] [list [list {*}$result]]
@@ -630,6 +718,11 @@ proc do_catchsql_test {testname sql result} {
fix_testname testname
uplevel do_test [list $testname] [list "catchsql {$sql}"] [list $result]
}
+proc do_timed_execsql_test {testname sql {result {}}} {
+ fix_testname testname
+ uplevel do_test [list $testname] [list "execsql_timed {$sql}"]\
+ [list [list {*}$result]]
+}
proc do_eqp_test {name sql res} {
uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
}
@@ -707,7 +800,7 @@ proc delete_all_data {} {
}
}
-# Run an SQL script.
+# Run an SQL script.
# Return the number of microseconds per statement.
#
proc speed_trial {name numstmt units sql} {
@@ -770,6 +863,7 @@ proc speed_trial_summary {name} {
#
proc finish_test {} {
catch {db close}
+ catch {db1 close}
catch {db2 close}
catch {db3 close}
if {0==[info exists ::SLAVE]} { finalize_testing }
@@ -793,9 +887,28 @@ proc finalize_testing {} {
set nTest [incr_ntest]
set nErr [set_test_counter errors]
- puts "$nErr errors out of $nTest tests"
- if {$nErr>0} {
- puts "Failures on these tests: [set_test_counter fail_list]"
+ set nKnown 0
+ if {[file readable known-problems.txt]} {
+ set fd [open known-problems.txt]
+ set content [read $fd]
+ close $fd
+ foreach x $content {set known_error($x) 1}
+ foreach x [set_test_counter fail_list] {
+ if {[info exists known_error($x)]} {incr nKnown}
+ }
+ }
+ if {$nKnown>0} {
+ puts "[expr {$nErr-$nKnown}] new errors and $nKnown known errors\
+ out of $nTest tests"
+ } else {
+ puts "$nErr errors out of $nTest tests"
+ }
+ if {$nErr>$nKnown} {
+ puts -nonewline "Failures on these tests:"
+ foreach x [set_test_counter fail_list] {
+ if {![info exists known_error($x)]} {puts -nonewline " $x"}
+ }
+ puts ""
}
foreach warning [set_test_counter warn_list] {
puts "Warning: $warning"
@@ -907,6 +1020,14 @@ proc execsql {sql {db db}} {
# puts "SQL = $sql"
uplevel [list $db eval $sql]
}
+proc execsql_timed {sql {db db}} {
+ set tm [time {
+ set x [uplevel [list $db eval $sql]]
+ } 1]
+ set tm [lindex $tm 0]
+ puts -nonewline " ([expr {$tm*0.001}]ms) "
+ set x
+}
# Execute SQL and catch exceptions.
#
@@ -930,6 +1051,87 @@ proc explain {sql {db db}} {
}
}
+proc explain_i {sql {db db}} {
+ puts ""
+ puts "addr opcode p1 p2 p3 p4 p5 #"
+ puts "---- ------------ ------ ------ ------ ---------------- -- -"
+
+
+ # Set up colors for the different opcodes. Scheme is as follows:
+ #
+ # Red: Opcodes that write to a b-tree.
+ # Blue: Opcodes that reposition or seek a cursor.
+ # Green: The ResultRow opcode.
+ #
+ if { [catch {fconfigure stdout -mode}]==0 } {
+ set R "\033\[31;1m" ;# Red fg
+ set G "\033\[32;1m" ;# Green fg
+ set B "\033\[34;1m" ;# Red fg
+ set D "\033\[39;0m" ;# Default fg
+ } else {
+ set R ""
+ set G ""
+ set B ""
+ set D ""
+ }
+ foreach opcode {
+ Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
+ NoConflict Next Prev VNext VPrev VFilter
+ } {
+ set color($opcode) $B
+ }
+ foreach opcode {ResultRow} {
+ set color($opcode) $G
+ }
+ foreach opcode {IdxInsert Insert Delete IdxDelete} {
+ set color($opcode) $R
+ }
+
+ set bSeenGoto 0
+ $db eval "explain $sql" {} {
+ set x($addr) 0
+ set op($addr) $opcode
+
+ if {$opcode == "Goto" && ($bSeenGoto==0 || ($p2 > $addr+10))} {
+ set linebreak($p2) 1
+ set bSeenGoto 1
+ }
+
+ if {$opcode=="Next" || $opcode=="Prev"
+ || $opcode=="VNext" || $opcode=="VPrev"
+ } {
+ for {set i $p2} {$i<$addr} {incr i} {
+ incr x($i) 2
+ }
+ }
+
+ if {$opcode == "Goto" && $p2<$addr && $op($p2)=="Yield"} {
+ for {set i [expr $p2+1]} {$i<$addr} {incr i} {
+ incr x($i) 2
+ }
+ }
+
+ if {$opcode == "Halt" && $comment == "End of coroutine"} {
+ set linebreak([expr $addr+1]) 1
+ }
+ }
+
+ $db eval "explain $sql" {} {
+ if {[info exists linebreak($addr)]} {
+ puts ""
+ }
+ set I [string repeat " " $x($addr)]
+
+ set col ""
+ catch { set col $color($opcode) }
+
+ puts [format {%-4d %s%s%-12.12s%s %-6d %-6d %-6d % -17s %s %s} \
+ $addr $I $col $opcode $D $p1 $p2 $p3 $p4 $p5 $comment
+ ]
+ }
+ puts "---- ------------ ------ ------ ------ ---------------- -- -"
+}
+
# 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.
@@ -952,6 +1154,15 @@ proc execsql2 {sql} {
return $result
}
+# Use a temporary in-memory database to execute SQL statements
+#
+proc memdbsql {sql} {
+ sqlite3 memdb :memory:
+ set result [memdb eval $sql]
+ memdb close
+ return $result
+}
+
# Use the non-callback API to execute multiple SQL statements
#
proc stepsql {dbptr sql} {
@@ -1062,17 +1273,19 @@ proc crashsql {args} {
set blocksize ""
set crashdelay 1
set prngseed 0
+ set opendb { sqlite3 db test.db -vfs crash }
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 -opendb]==0} {set opendb $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} \
@@ -1085,7 +1298,7 @@ proc crashsql {args} {
error "Compulsory option -file missing"
}
- # $crashfile gets compared to the native filename in
+ # $crashfile gets compared to the native filename in
# cfSync(), which can be different then what TCL uses by
# default, so here we force it to the "nativename" format.
set cfile [string map {\\ \\\\} [file nativename [file join [get_pwd] $crashfile]]]
@@ -1094,7 +1307,7 @@ proc crashsql {args} {
puts $f "sqlite3_crash_enable 1"
puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
- puts $f "sqlite3 db test.db -vfs crash"
+ puts $f $opendb
# This block sets the cache size of the main database to 10
# pages. This is done in case the build is configured to omit
@@ -1102,6 +1315,7 @@ proc crashsql {args} {
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
@@ -1120,7 +1334,7 @@ proc crashsql {args} {
set r [catch {
exec [info nameofexec] crash.tcl >@stdout
} msg]
-
+
# Windows/ActiveState TCL returns a slightly different
# error message. We map that to the expected message
# so that we don't have to change all of the test
@@ -1130,7 +1344,7 @@ proc crashsql {args} {
set msg "child process exited abnormally"
}
}
-
+
lappend r $msg
}
@@ -1156,7 +1370,7 @@ proc run_ioerr_prep {} {
# 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
+# 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.
@@ -1185,7 +1399,7 @@ proc do_ioerr_test {testname 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
-
+
# Create a single TCL script from the TCL and SQL specified
# as the body of the test.
set ::ioerrorbody {}
@@ -1209,7 +1423,7 @@ proc do_ioerr_test {testname args} {
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
@@ -1218,7 +1432,7 @@ proc do_ioerr_test {testname args} {
restore_prng_state
}
- # Delete the files test.db and test2.db, then execute the TCL and
+ # 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 {
run_ioerr_prep
@@ -1236,7 +1450,7 @@ proc do_ioerr_test {testname args} {
}] $n
# Execute the TCL script created for the body of this test. If
- # at least N IO operations performed by SQLite as a result of
+ # 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
@@ -1290,12 +1504,12 @@ proc do_ioerr_test {testname args} {
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,
+ # 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
+ # 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].
@@ -1311,7 +1525,7 @@ proc do_ioerr_test {testname args} {
} {1}
}
- # If there is an open database handle and no open transaction,
+ # 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
@@ -1415,7 +1629,7 @@ proc allcksum {{db db}} {
}
# Generate a checksum based on the contents of a single database with
-# a database connection. The name of the database is $dbname.
+# a database connection. The name of the database is $dbname.
# Examples of $dbname are "temp" or "main".
#
proc dbcksum {db dbname} {
@@ -1509,8 +1723,8 @@ proc drop_all_tables {{db db}} {
#-------------------------------------------------------------------------
# If a test script is executed with global variable $::G(perm:name) set to
-# "wal", then the tests are run in WAL mode. Otherwise, they should be run
-# in rollback mode. The following Tcl procs are used to make this less
+# "wal", then the tests are run in WAL mode. Otherwise, they should be run
+# in rollback mode. The following Tcl procs are used to make this less
# intrusive:
#
# wal_set_journal_mode ?DB?
@@ -1525,9 +1739,9 @@ proc drop_all_tables {{db db}} {
# Otherwise (if not running a WAL permutation) this is a no-op.
#
# wal_is_wal_mode
-#
+#
# Returns true if this test should be run in WAL mode. False otherwise.
-#
+#
proc wal_is_wal_mode {} {
expr {[permutation] eq "wal"}
}
@@ -1628,10 +1842,10 @@ proc slave_test_file {zFile} {
}
set ::sqlite_open_file_count 0
- # Test that the global "shared-cache" setting was not altered by
+ # Test that the global "shared-cache" setting was not altered by
# the test script.
#
- ifcapable shared_cache {
+ ifcapable shared_cache {
set res [expr {[sqlite3_enable_shared_cache] == $scs}]
do_test ${tail}-sharedcachesetting [list set {} $res] 1
}
@@ -1697,5 +1911,11 @@ set AUTOVACUUM $sqlite_options(default_autovacuum)
# Make sure the FTS enhanced query syntax is disabled.
set sqlite_fts3_enable_parentheses 0
+# During testing, assume that all database files are well-formed. The
+# few test cases that deliberately corrupt database files should rescind
+# this setting by invoking "database_can_be_corrupt"
+#
+database_never_corrupt
+
source $testdir/thread_common.tcl
source $testdir/malloc_common.tcl
diff --git a/test/tkt-2a5629202f.test b/test/tkt-2a5629202f.test
index 037f100..8f09c31 100644
--- a/test/tkt-2a5629202f.test
+++ b/test/tkt-2a5629202f.test
@@ -46,6 +46,12 @@ do_execsql_test 1.3 {
SELECT coalesce(b, 'null') || '/' || c FROM t8 x ORDER BY x.b, x.c
} {null/four null/three a/one b/two}
+do_execsql_test 1.4 {
+ DROP INDEX i1;
+ CREATE UNIQUE INDEX i1 ON t8(b, c);
+ SELECT coalesce(b, 'null') || '/' || c FROM t8 x ORDER BY x.b, x.c
+} {null/four null/three a/one b/two}
+
#-------------------------------------------------------------------------
#
@@ -68,4 +74,3 @@ do_test 2.4 {
} {sort}
finish_test
-
diff --git a/test/tkt-385a5b56b9.test b/test/tkt-385a5b56b9.test
index 8184864..1338435 100644
--- a/test/tkt-385a5b56b9.test
+++ b/test/tkt-385a5b56b9.test
@@ -35,19 +35,19 @@ do_execsql_test 2.0 {
}
do_eqp_test 2.1 { SELECT DISTINCT x FROM t2 } {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2x (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2x}
}
do_eqp_test 2.2 { SELECT DISTINCT y FROM t2 } {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t2 USING COVERING INDEX t2y}
}
do_eqp_test 2.3 { SELECT DISTINCT x, y FROM t2 WHERE y=10 } {
- 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?)}
}
do_eqp_test 2.4 { SELECT DISTINCT x, y FROM t2 WHERE x=10 } {
- 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x=?)}
}
finish_test
diff --git a/test/tkt-3a77c9714e.test b/test/tkt-3a77c9714e.test
index 6eaec16..b7d366f 100644
--- a/test/tkt-3a77c9714e.test
+++ b/test/tkt-3a77c9714e.test
@@ -70,4 +70,3 @@ do_execsql_test 2.2 {
finish_test
-
diff --git a/test/tkt-3fe897352e.test b/test/tkt-3fe897352e.test
index deafe48..bc2b203 100644
--- a/test/tkt-3fe897352e.test
+++ b/test/tkt-3fe897352e.test
@@ -16,9 +16,9 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-
-# The following tests use hex_to_utf16be() and hex_to_utf16le() which
-# which are only available if SQLite is built with UTF16 support.
+
+# The following tests use hex_to_utf16be() and hex_to_utf16le() which
+# which are only available if SQLite is built with UTF16 support.
ifcapable {!utf16} {
finish_test
return
diff --git a/test/tkt-4a03edc4c8.test b/test/tkt-4a03edc4c8.test
index 649f27d..1908bcd 100644
--- a/test/tkt-4a03edc4c8.test
+++ b/test/tkt-4a03edc4c8.test
@@ -31,7 +31,7 @@ do_test tkt-4a03ed-1.1 {
INSERT INTO t1 VALUES(1, 2);
COMMIT;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t1.b}}
do_test tkt-4a03ed-1.2 {
db eval {
PRAGMA integrity_check;
diff --git a/test/tkt-4c86b126f2.test b/test/tkt-4c86b126f2.test
new file mode 100644
index 0000000..3c5177e
--- /dev/null
+++ b/test/tkt-4c86b126f2.test
@@ -0,0 +1,49 @@
+# 2014-02-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. Specifically,
+# it tests that ticket [4c86b126f22ad548fee0125337bdc9366912d9ac].
+#
+# When SQLite is compiled using SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4,
+# it gets the wrong answer...
+#
+# The problem was introduced in SQLite 3.8.1.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test tkt-4c86b126f2-1.1 {
+ CREATE TABLE nodes(
+ local_relpath TEXT PRIMARY KEY,
+ moved_to TEXT
+ );
+ INSERT INTO nodes VALUES('A',NULL);
+ INSERT INTO nodes VALUES('A/B',NULL);
+ INSERT INTO nodes VALUES('',NULL);
+ INSERT INTO nodes VALUES('A/B/C-move',NULL);
+ INSERT INTO nodes VALUES('A/B/C','A/B/C-move');
+ INSERT INTO nodes VALUES('A/B-move',NULL);
+ INSERT INTO nodes VALUES('A/B-move/C-move',NULL);
+ INSERT INTO nodes VALUES('A/B-move/C','x');
+ SELECT local_relpath, moved_to
+ FROM nodes
+ WHERE (local_relpath = 'A/B' OR
+ ((local_relpath > 'A/B/') AND (local_relpath < 'A/B0')))
+ AND moved_to IS NOT NULL;
+} {A/B/C A/B/C-move}
+
+do_execsql_test tkt-4c86b126f2-2.1 {
+ CREATE TABLE t1(x TEXT UNIQUE, y TEXT UNIQUE, z);
+ INSERT INTO t1 VALUES('ghi','jkl','y');
+ SELECT * FROM t1 WHERE (x='ghi' OR y='jkl') AND z IS NOT NULL;
+} {ghi jkl y}
+
+
+finish_test
diff --git a/test/tkt-4ef7e3cfca.test b/test/tkt-4ef7e3cfca.test
new file mode 100644
index 0000000..fc1efde
--- /dev/null
+++ b/test/tkt-4ef7e3cfca.test
@@ -0,0 +1,68 @@
+# 2014-03-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 tests to verify that ticket [4ef7e3cfca] has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-4ef7e3cfca
+
+do_catchsql_test 1.1 {
+ CREATE TABLE x(a);
+ CREATE TRIGGER t AFTER INSERT ON x BEGIN
+ SELECT * FROM x WHERE abc.a = 1;
+ END;
+ INSERT INTO x VALUES('assert');
+} {1 {no such column: abc.a}}
+
+reset_db
+do_execsql_test 2.1 {
+ CREATE TABLE w(a);
+ CREATE TABLE x(a);
+ CREATE TABLE y(a);
+ CREATE TABLE z(a);
+
+ INSERT INTO x(a) VALUES(5);
+ INSERT INTO y(a) VALUES(10);
+
+ CREATE TRIGGER t AFTER INSERT ON w BEGIN
+ INSERT INTO z
+ SELECT (SELECT x.a + y.a FROM y) FROM x;
+ END;
+ INSERT INTO w VALUES('incorrect');
+}
+do_execsql_test 2.2 {
+ SELECT * FROM z;
+} {15}
+
+reset_db
+do_execsql_test 3.1 {
+ CREATE TABLE w(a);
+ CREATE TABLE x(b);
+ CREATE TABLE y(a);
+ CREATE TABLE z(a);
+
+ INSERT INTO x(b) VALUES(5);
+ INSERT INTO y(a) VALUES(10);
+
+ CREATE TRIGGER t AFTER INSERT ON w BEGIN
+ INSERT INTO z
+ SELECT (SELECT x.b + y.a FROM y) FROM x;
+ END;
+ INSERT INTO w VALUES('assert');
+}
+do_execsql_test 3.2 {
+ SELECT * FROM z;
+} {15}
+
+finish_test
diff --git a/test/tkt-78e04e52ea.test b/test/tkt-78e04e52ea.test
index a664ceb..975e5b3 100644
--- a/test/tkt-78e04e52ea.test
+++ b/test/tkt-78e04e52ea.test
@@ -18,23 +18,23 @@ source $testdir/tester.tcl
do_test tkt-78e04-1.0 {
execsql {
- CREATE TABLE ""("" UNIQUE);
+ CREATE TABLE ""("" UNIQUE, x CHAR(100));
CREATE TABLE t2(x);
- INSERT INTO "" VALUES(1);
+ INSERT INTO ""("") VALUES(1);
INSERT INTO t2 VALUES(2);
SELECT * FROM "", t2;
}
-} {1 2}
+} {1 {} 2}
do_test tkt-78e04-1.1 {
catchsql {
- INSERT INTO "" VALUES(1);
+ INSERT INTO ""("") VALUES(1);
}
-} {1 {column is not unique}}
+} {1 {UNIQUE constraint failed: .}}
do_test tkt-78e04-1.2 {
execsql {
PRAGMA table_info("");
}
-} {0 {} {} 0 {} 0}
+} {0 {} {} 0 {} 0 1 x CHAR(100) 0 {} 0}
do_test tkt-78e04-1.3 {
execsql {
CREATE INDEX i1 ON ""("" COLLATE nocase);
@@ -42,9 +42,9 @@ do_test tkt-78e04-1.3 {
} {}
do_test tkt-78e04-1.4 {
execsql {
- EXPLAIN QUERY PLAN SELECT * FROM "" WHERE "" LIKE 'abc%';
+ EXPLAIN QUERY PLAN SELECT "" FROM "" WHERE "" LIKE 'abc%';
}
-} {0 0 0 {SCAN TABLE USING COVERING INDEX i1 (~500000 rows)}}
+} {0 0 0 {SCAN TABLE USING COVERING INDEX i1}}
do_test tkt-78e04-1.5 {
execsql {
DROP TABLE "";
@@ -57,12 +57,12 @@ do_test tkt-78e04-2.1 {
CREATE INDEX "" ON t2(x);
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=5;
}
-} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?) (~10 rows)}}
+} {0 0 0 {SEARCH TABLE t2 USING COVERING INDEX (x=?)}}
do_test tkt-78e04-2.2 {
execsql {
DROP INDEX "";
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE x=2;
}
-} {0 0 0 {SCAN TABLE t2 (~100000 rows)}}
+} {0 0 0 {SCAN TABLE t2}}
finish_test
diff --git a/test/tkt-7a31705a7e6.test b/test/tkt-7a31705a7e6.test
index 6470122..e3e4029 100644
--- a/test/tkt-7a31705a7e6.test
+++ b/test/tkt-7a31705a7e6.test
@@ -23,4 +23,3 @@ do_execsql_test tkt-7a31705a7e6-1.1 {
CREATE TABLE t2x (b INTEGER PRIMARY KEY);
SELECT t1.a FROM ((t1 JOIN t2 ON t1.a=t2.a) AS x JOIN t2x ON x.b=t2x.b) as y;
} {}
-
diff --git a/test/tkt-7bbfb7d442.test b/test/tkt-7bbfb7d442.test
index dcb9b16..56d4cae 100644
--- a/test/tkt-7bbfb7d442.test
+++ b/test/tkt-7bbfb7d442.test
@@ -152,5 +152,3 @@ do_execsql_test 2.3 {
finish_test
-
-
diff --git a/test/tkt-80e031a00f.test b/test/tkt-80e031a00f.test
index 95372ab..af1d636 100644
--- a/test/tkt-80e031a00f.test
+++ b/test/tkt-80e031a00f.test
@@ -160,6 +160,10 @@ do_execsql_test tkt-80e031a00f.322 {SELECT 'b' IN t8} 1
do_execsql_test tkt-80e031a00f.323 {SELECT 'c' NOT IN t8} 0
do_execsql_test tkt-80e031a00f.324 {SELECT 'c' IN t8n} 1
do_execsql_test tkt-80e031a00f.325 {SELECT 'd' NOT IN t8n} 0
+do_execsql_test tkt-80e031a00f.326 {SELECT 'a' IN (NULL,'a')} 1
+do_execsql_test tkt-80e031a00f.327 {SELECT 'a' IN (NULL,'b')} {{}}
+do_execsql_test tkt-80e031a00f.328 {SELECT 'a' NOT IN (NULL,'a')} 0
+do_execsql_test tkt-80e031a00f.329 {SELECT 'a' NOT IN (NULL,'b')} {{}}
#
# Row 4:
do_execsql_test tkt-80e031a00f.400 {SELECT 1 IN (2,3,4,null)} {{}}
diff --git a/test/tkt-868145d012.test b/test/tkt-868145d012.test
new file mode 100644
index 0000000..145d159
--- /dev/null
+++ b/test/tkt-868145d012.test
@@ -0,0 +1,64 @@
+# 2013 March 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. Specifically,
+# it tests that ticket [868145d012a1] is fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test tkt-868145d012.100 {
+ CREATE TABLE p (
+ id INTEGER PRIMARY KEY,
+ uid VARCHAR(36),
+ t INTEGER
+ );
+
+ CREATE TABLE pa (
+ id INTEGER PRIMARY KEY,
+ a_uid VARCHAR(36)
+ );
+
+ CREATE TABLE a (
+ id INTEGER PRIMARY KEY,
+ uid VARCHAR(36),
+ t INTEGER
+ );
+
+ INSERT INTO pa VALUES(1,'1234');
+ INSERT INTO pa VALUES(2,'2345');
+ INSERT INTO p VALUES(3,'1234',97);
+ INSERT INTO p VALUES(4,'1234',98);
+ INSERT INTO a VALUES(5,'1234',98);
+ INSERT INTO a VALUES(6,'1234',99);
+} {}
+do_execsql_test tkt-868145d012.110 {
+ SELECT DISTINCT pa.id, p.id, a.id
+ FROM
+ pa
+ LEFT JOIN p ON p.uid='1234'
+ LEFT JOIN a ON a.uid=pa.a_uid
+ WHERE
+ a.t=p.t
+ ;
+} {1 4 5}
+do_execsql_test tkt-868145d012.120 {
+ SELECT DISTINCT pa.id, p.id, a.id
+ FROM
+ pa
+ LEFT JOIN p ON p.uid='1234'
+ LEFT JOIN a ON a.uid=pa.a_uid AND a.t=p.t
+ ORDER BY 1, 2, 3
+ ;
+} {1 3 {} 1 4 5 2 3 {} 2 4 {}}
+
+
+finish_test
diff --git a/test/tkt-8c63ff0ec.test b/test/tkt-8c63ff0ec.test
new file mode 100644
index 0000000..d4aaefd
--- /dev/null
+++ b/test/tkt-8c63ff0ec.test
@@ -0,0 +1,48 @@
+# 2014-02-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.
+#
+#***********************************************************************
+#
+# Test cases to show that ticket [8c63ff0eca81a9132d8d67b31cd6ae9712a2cc6f]
+# "Incorrect query result on a UNION ALL" which was caused by using the same
+# temporary register in concurrent co-routines, as been fixed.
+#
+
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix tkt-8c63ff0ec
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e);
+ INSERT INTO t1 VALUES(1,20,30,40,50),(3,60,70,80,90);
+ CREATE TABLE t2(x INTEGER PRIMARY KEY);
+ INSERT INTO t2 VALUES(2);
+ CREATE TABLE t3(z);
+ INSERT INTO t3 VALUES(2),(2),(2),(2);
+
+ SELECT a, b+c FROM t1
+ UNION ALL
+ SELECT x, 5 FROM t2 JOIN t3 ON z=x WHERE x=2
+ ORDER BY a;
+} {1 50 2 5 2 5 2 5 2 5 3 130}
+do_execsql_test 1.2 {
+ SELECT a, b+c+d FROM t1
+ UNION ALL
+ SELECT x, 5 FROM t2 JOIN t3 ON z=x WHERE x=2
+ ORDER BY a;
+} {1 90 2 5 2 5 2 5 2 5 3 210}
+do_execsql_test 1.3 {
+ SELECT a, b+c+d+e FROM t1
+ UNION ALL
+ SELECT x, 5 FROM t2 JOIN t3 ON z=x WHERE x=2
+ ORDER BY a;
+} {1 140 2 5 2 5 2 5 2 5 3 300}
+
+finish_test
diff --git a/test/tkt-94c04eaadb.test b/test/tkt-94c04eaadb.test
index 0063de6..9de8aea 100644
--- a/test/tkt-94c04eaadb.test
+++ b/test/tkt-94c04eaadb.test
@@ -44,7 +44,6 @@ do_test tkt-94c94-2.1 {
execsql { CREATE TABLE t2(x, y) } db
} {}
do_test tkt-94c94-2.2 {
-breakpoint
execsql { INSERT INTO t2 VALUES(1, 2) } db2
} {}
do_test tkt-94c94-2.3 {
diff --git a/test/tkt-9a8b09f8e6.test b/test/tkt-9a8b09f8e6.test
new file mode 100644
index 0000000..d6b22ef
--- /dev/null
+++ b/test/tkt-9a8b09f8e6.test
@@ -0,0 +1,323 @@
+# 2014 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.
+#
+# This file implements tests to verify that ticket [9a8b09f8e6] has been
+# fixed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-9a8b09f8e6
+
+do_test 1.1 {
+ execsql {
+ CREATE TABLE t1(x TEXT);
+ INSERT INTO t1 VALUES('1');
+ }
+} {}
+
+do_test 1.2 {
+ execsql {
+ CREATE TABLE t2(x INTEGER);
+ INSERT INTO t2 VALUES(1);
+ }
+} {}
+
+do_test 1.3 {
+ execsql {
+ CREATE TABLE t3(x REAL);
+ INSERT INTO t3 VALUES(1.0);
+ }
+} {}
+
+do_test 1.4 {
+ execsql {
+ CREATE TABLE t4(x REAL);
+ INSERT INTO t4 VALUES(1.11);
+ }
+} {}
+
+do_test 1.5 {
+ execsql {
+ CREATE TABLE t5(x, y);
+ INSERT INTO t5 VALUES('1', 'one');
+ INSERT INTO t5 VALUES(1, 'two');
+ INSERT INTO t5 VALUES('1.0', 'three');
+ INSERT INTO t5 VALUES(1.0, 'four');
+ }
+} {}
+
+do_test 2.1 {
+ execsql {
+ SELECT x FROM t1 WHERE x IN (1);
+ }
+} {1}
+
+do_test 2.2 {
+ execsql {
+ SELECT x FROM t1 WHERE x IN (1.0);
+ }
+} {}
+
+do_test 2.3 {
+ execsql {
+ SELECT x FROM t1 WHERE x IN ('1');
+ }
+} {1}
+
+do_test 2.4 {
+ execsql {
+ SELECT x FROM t1 WHERE x IN ('1.0');
+ }
+} {}
+
+do_test 2.5 {
+ execsql {
+ SELECT x FROM t1 WHERE 1 IN (x);
+ }
+} {}
+
+do_test 2.6 {
+ execsql {
+ SELECT x FROM t1 WHERE 1.0 IN (x);
+ }
+} {}
+
+do_test 2.7 {
+ execsql {
+ SELECT x FROM t1 WHERE '1' IN (x);
+ }
+} {1}
+
+do_test 2.8 {
+ execsql {
+ SELECT x FROM t1 WHERE '1.0' IN (x);
+ }
+} {}
+
+do_test 3.1 {
+ execsql {
+ SELECT x FROM t2 WHERE x IN (1);
+ }
+} {1}
+
+do_test 3.2 {
+ execsql {
+ SELECT x FROM t2 WHERE x IN (1.0);
+ }
+} {1}
+
+do_test 3.3 {
+ execsql {
+ SELECT x FROM t2 WHERE x IN ('1');
+ }
+} {1}
+
+do_test 3.4 {
+ execsql {
+ SELECT x FROM t2 WHERE x IN ('1.0');
+ }
+} {1}
+
+do_test 3.5 {
+ execsql {
+ SELECT x FROM t2 WHERE 1 IN (x);
+ }
+} {1}
+
+do_test 3.6 {
+ execsql {
+ SELECT x FROM t2 WHERE 1.0 IN (x);
+ }
+} {1}
+
+do_test 3.7 {
+ execsql {
+ SELECT x FROM t2 WHERE '1' IN (x);
+ }
+} {}
+
+do_test 3.8 {
+ execsql {
+ SELECT x FROM t2 WHERE '1.0' IN (x);
+ }
+} {}
+
+do_test 4.1 {
+ execsql {
+ SELECT x FROM t3 WHERE x IN (1);
+ }
+} {1.0}
+
+do_test 4.2 {
+ execsql {
+ SELECT x FROM t3 WHERE x IN (1.0);
+ }
+} {1.0}
+
+do_test 4.3 {
+ execsql {
+ SELECT x FROM t3 WHERE x IN ('1');
+ }
+} {1.0}
+
+do_test 4.4 {
+ execsql {
+ SELECT x FROM t3 WHERE x IN ('1.0');
+ }
+} {1.0}
+
+do_test 4.5 {
+ execsql {
+ SELECT x FROM t3 WHERE 1 IN (x);
+ }
+} {1.0}
+
+do_test 4.6 {
+ execsql {
+ SELECT x FROM t3 WHERE 1.0 IN (x);
+ }
+} {1.0}
+
+do_test 4.7 {
+ execsql {
+ SELECT x FROM t3 WHERE '1' IN (x);
+ }
+} {}
+
+do_test 4.8 {
+ execsql {
+ SELECT x FROM t3 WHERE '1.0' IN (x);
+ }
+} {}
+
+do_test 5.1 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN (1);
+ }
+} {}
+
+do_test 5.2 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN (1.0);
+ }
+} {}
+
+do_test 5.3 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN ('1');
+ }
+} {}
+
+do_test 5.4 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN ('1.0');
+ }
+} {}
+
+do_test 5.5 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN (1.11);
+ }
+} {1.11}
+
+do_test 5.6 {
+ execsql {
+ SELECT x FROM t4 WHERE x IN ('1.11');
+ }
+} {1.11}
+
+do_test 5.7 {
+ execsql {
+ SELECT x FROM t4 WHERE 1 IN (x);
+ }
+} {}
+
+do_test 5.8 {
+ execsql {
+ SELECT x FROM t4 WHERE 1.0 IN (x);
+ }
+} {}
+
+do_test 5.9 {
+ execsql {
+ SELECT x FROM t4 WHERE '1' IN (x);
+ }
+} {}
+
+do_test 5.10 {
+ execsql {
+ SELECT x FROM t4 WHERE '1.0' IN (x);
+ }
+} {}
+
+do_test 5.11 {
+ execsql {
+ SELECT x FROM t4 WHERE 1.11 IN (x);
+ }
+} {1.11}
+
+do_test 5.12 {
+ execsql {
+ SELECT x FROM t4 WHERE '1.11' IN (x);
+ }
+} {}
+
+do_test 6.1 {
+ execsql {
+ SELECT x, y FROM t5 WHERE x IN (1);
+ }
+} {1 two 1.0 four}
+
+do_test 6.2 {
+ execsql {
+ SELECT x, y FROM t5 WHERE x IN (1.0);
+ }
+} {1 two 1.0 four}
+
+do_test 6.3 {
+ execsql {
+ SELECT x, y FROM t5 WHERE x IN ('1');
+ }
+} {1 one}
+
+do_test 6.4 {
+ execsql {
+ SELECT x, y FROM t5 WHERE x IN ('1.0');
+ }
+} {1.0 three}
+
+do_test 6.5 {
+ execsql {
+ SELECT x, y FROM t5 WHERE 1 IN (x);
+ }
+} {1 two 1.0 four}
+
+do_test 6.6 {
+ execsql {
+ SELECT x, y FROM t5 WHERE 1.0 IN (x);
+ }
+} {1 two 1.0 four}
+
+do_test 6.7 {
+ execsql {
+ SELECT x, y FROM t5 WHERE '1' IN (x);
+ }
+} {1 one}
+
+do_test 6.8 {
+ execsql {
+ SELECT x, y FROM t5 WHERE '1.0' IN (x);
+ }
+} {1.0 three}
+
+finish_test
diff --git a/test/tkt-9f2eb3abac.test b/test/tkt-9f2eb3abac.test
new file mode 100644
index 0000000..5b93733
--- /dev/null
+++ b/test/tkt-9f2eb3abac.test
@@ -0,0 +1,79 @@
+
+# 2013 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.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set ::testprefix tkt-9f2eb3abac
+
+do_execsql_test 1.1 {
+ CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e));
+ SELECT * FROM t1 WHERE a=? AND b=? AND c=? AND d=? AND e=?;
+} {}
+
+do_execsql_test 1.2 {
+ CREATE TABLE "a" (
+ "b" integer NOT NULL,
+ "c" integer NOT NULL,
+ PRIMARY KEY ("b", "c")
+ );
+
+ CREATE TABLE "d" (
+ "e" integer NOT NULL,
+ "g" integer NOT NULL,
+ "f" integer NOT NULL,
+ "h" integer NOT NULL,
+ "i" character(10) NOT NULL,
+ "j" int,
+ PRIMARY KEY ("e", "g", "f", "h")
+ );
+
+ CREATE TABLE "d_to_a" (
+ "f_e" integer NOT NULL,
+ "f_g" integer NOT NULL,
+ "f_f" integer NOT NULL,
+ "f_h" integer NOT NULL,
+ "t_b" integer NOT NULL,
+ "t_c" integer NOT NULL,
+ "r" character NOT NULL,
+ "s" integer,
+ PRIMARY KEY ("f_e", "f_g", "f_f", "f_h", "t_b", "t_c")
+ );
+
+ INSERT INTO d (g, e, h, f, j, i) VALUES ( 1, 1, 1, 1, 1, 1 );
+ INSERT INTO a (b, c) VALUES ( 1, 1 );
+ INSERT INTO d_to_a VALUES (1, 1, 1, 1, 1, 1, 1, 1);
+
+ DELETE FROM d_to_a
+ WHERE f_g = 1 AND f_e = 1 AND f_h = 1 AND f_f = 1 AND t_b = 1 AND t_c = 1;
+
+ SELECT * FROM d_to_a;
+} {}
+
+faultsim_delete_and_reopen
+do_execsql_test 2.0 { CREATE TABLE t1(a,b,c,d,e, PRIMARY KEY(a,b,c,d,e)) }
+do_execsql_test 2.1 { CREATE TABLE t2(x) }
+faultsim_save_and_close
+
+do_faultsim_test 3 -faults oom* -prep {
+ faultsim_restore_and_reopen
+ execsql { SELECT 1 FROM sqlite_master }
+} -body {
+ execsql { SELECT * FROM t1,t2 WHERE a=? AND b=? AND c=? AND d=? AND e=? }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+finish_test
+
diff --git a/test/tkt-a8a0d2996a.test b/test/tkt-a8a0d2996a.test
new file mode 100644
index 0000000..6b15e41
--- /dev/null
+++ b/test/tkt-a8a0d2996a.test
@@ -0,0 +1,93 @@
+# 2014-03-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.
+#
+#***********************************************************************
+#
+# Tests to verify that arithmetic operators do not change the type of
+# input operands. Ticket [a8a0d2996a]
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-a8a0d2996a
+
+do_execsql_test 1.0 {
+ CREATE TABLE t(x,y);
+ INSERT INTO t VALUES('1','1');
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
+} {text text}
+do_execsql_test 1.1 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
+} {text text}
+do_execsql_test 1.2 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
+} {text text}
+do_execsql_test 1.3 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
+} {text text}
+do_execsql_test 1.4 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x%4 AND y=='1';
+} {text text}
+
+do_execsql_test 2.0 {
+ UPDATE t SET x='1xyzzy';
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
+} {text text}
+do_execsql_test 2.1 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
+} {text text}
+do_execsql_test 2.2 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
+} {text text}
+do_execsql_test 2.3 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
+} {text text}
+do_execsql_test 2.4 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x%4 AND y=='1';
+} {text text}
+
+
+do_execsql_test 3.0 {
+ UPDATE t SET x='1.0';
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x+0 AND y=='1';
+} {text text}
+do_execsql_test 3.1 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x-0 AND y=='1';
+} {text text}
+do_execsql_test 3.2 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x*1 AND y=='1';
+} {text text}
+do_execsql_test 3.3 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x/1 AND y=='1';
+} {text text}
+do_execsql_test 3.4 {
+ SELECT typeof(x), typeof(y) FROM t WHERE 1=x%4 AND y=='1';
+} {text text}
+
+do_execsql_test 4.0 {
+ SELECT 1+1.;
+} {2.0}
+do_execsql_test 4.1 {
+ SELECT '1.23e64'/'1.0000e+62';
+} {123.0}
+do_execsql_test 4.2 {
+ SELECT '100x'+'-2y';
+} {98}
+do_execsql_test 4.3 {
+ SELECT '100x'+'4.5y';
+} {104.5}
+do_execsql_test 4.4 {
+ SELECT '-9223372036854775807x'-'1x';
+} {-9.22337203685478e+18}
+do_execsql_test 4.5 {
+ SELECT '9223372036854775806x'+'1x';
+} {9.22337203685478e+18}
+do_execsql_test 4.6 {
+ SELECT '1234x'/'10y';
+} {123.4}
diff --git a/test/tkt-b1d3a2e531.test b/test/tkt-b1d3a2e531.test
index 54534b6..745bbe8 100644
--- a/test/tkt-b1d3a2e531.test
+++ b/test/tkt-b1d3a2e531.test
@@ -98,7 +98,7 @@ do_catchsql_test 3.2 {
DROP TABLE pp1;
DROP TABLE cc1;
COMMIT;
-} {1 {foreign key constraint failed}}
+} {1 {FOREIGN KEY constraint failed}}
do_catchsql_test 3.3 {
DROP TABLE cc2;
COMMIT;
diff --git a/test/tkt-b75a9ca6b0.test b/test/tkt-b75a9ca6b0.test
new file mode 100644
index 0000000..0c81a53
--- /dev/null
+++ b/test/tkt-b75a9ca6b0.test
@@ -0,0 +1,77 @@
+# 2014-04-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.
+#
+#*************************************************************************
+#
+# Test that ticket [b75a9ca6b0] has been fixed.
+#
+# Ticket [b75a9ca6b0] concerns queries that have both a GROUP BY
+# and an ORDER BY. This code verifies that SQLite is able to
+# optimize out the ORDER BY in some circumstances, but retains the
+# ORDER BY when necessary.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-b75a9ca6b0
+
+do_execsql_test 1 {
+ CREATE TABLE t1 (x, y);
+ INSERT INTO t1 VALUES (1, 3);
+ INSERT INTO t1 VALUES (2, 2);
+ INSERT INTO t1 VALUES (3, 1);
+}
+
+do_execsql_test 1.1 {
+ CREATE INDEX i1 ON t1(x, y);
+}
+
+set idxscan {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}}
+set tblscan {0 0 0 {SCAN TABLE t1}}
+set grpsort {0 0 0 {USE TEMP B-TREE FOR GROUP BY}}
+set sort {0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
+
+foreach {tn q res eqp} [subst -nocommands {
+ 1 "SELECT * FROM t1 GROUP BY x, y ORDER BY x,y"
+ {1 3 2 2 3 1} {$idxscan}
+
+ 2 "SELECT * FROM t1 GROUP BY x, y ORDER BY x"
+ {1 3 2 2 3 1} {$idxscan $sort}
+
+ 3 "SELECT * FROM t1 GROUP BY y, x ORDER BY y, x"
+ {3 1 2 2 1 3} {$idxscan $sort}
+
+ 4 "SELECT * FROM t1 GROUP BY x ORDER BY x"
+ {1 3 2 2 3 1} {$idxscan}
+
+ 5 "SELECT * FROM t1 GROUP BY y ORDER BY y"
+ {3 1 2 2 1 3} {$tblscan $grpsort}
+
+ 6 "SELECT * FROM t1 GROUP BY y ORDER BY x"
+ {1 3 2 2 3 1} {$tblscan $grpsort $sort}
+
+ 7 "SELECT * FROM t1 GROUP BY x, y ORDER BY x, y DESC"
+ {1 3 2 2 3 1} {$idxscan $sort}
+
+ 8 "SELECT * FROM t1 GROUP BY x, y ORDER BY x DESC, y DESC"
+ {3 1 2 2 1 3} {$idxscan $sort}
+
+ 9 "SELECT * FROM t1 GROUP BY x, y ORDER BY x ASC, y ASC"
+ {1 3 2 2 3 1} {$idxscan}
+
+ 10 "SELECT * FROM t1 GROUP BY x, y ORDER BY x COLLATE nocase, y"
+ {1 3 2 2 3 1} {$idxscan $sort}
+
+}] {
+ do_execsql_test 1.$tn.1 $q $res
+ do_eqp_test 1.$tn.2 $q $eqp
+}
+
+
+finish_test
diff --git a/test/tkt-c48d99d690.test b/test/tkt-c48d99d690.test
index 9b40579..6d1b9db 100644
--- a/test/tkt-c48d99d690.test
+++ b/test/tkt-c48d99d690.test
@@ -23,4 +23,3 @@ do_test 1.1 {
do_test 1.2 { execsql VACUUM } {}
finish_test
-
diff --git a/test/tkt-cbd054fa6b.test b/test/tkt-cbd054fa6b.test
index 51e0199..2951233 100644
--- a/test/tkt-cbd054fa6b.test
+++ b/test/tkt-cbd054fa6b.test
@@ -16,11 +16,26 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-ifcapable !stat3 {
+ifcapable !stat4&&!stat3 {
finish_test
return
}
+proc s {blob} {
+ set ret ""
+ binary scan $blob c* bytes
+ foreach b $bytes {
+ set t [binary format c $b]
+ if {[string is print $t]} {
+ append ret $t
+ } else {
+ append ret .
+ }
+ }
+ return $ret
+}
+db function s s
+
do_test tkt-cbd05-1.1 {
db eval {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT UNIQUE NOT NULL);
@@ -39,18 +54,30 @@ do_test tkt-cbd05-1.1 {
}
} {10}
do_test tkt-cbd05-1.2 {
- db eval {
- ANALYZE;
+ db eval { ANALYZE; }
+ ifcapable stat4 {
+ db eval {
+ PRAGMA writable_schema = 1;
+ CREATE VIEW vvv AS
+ SELECT tbl,idx,neq,nlt,ndlt,test_extract(sample,0) AS sample
+ FROM sqlite_stat4;
+ PRAGMA writable_schema = 0;
+ }
+ } else {
+ db eval {
+ CREATE VIEW vvv AS
+ SELECT tbl,idx,neq,nlt,ndlt,sample FROM sqlite_stat3;
+ }
}
} {}
do_test tkt-cbd05-1.3 {
execsql {
- SELECT tbl,idx,group_concat(sample,' ')
- FROM sqlite_stat3
+ SELECT tbl,idx,group_concat(s(sample),' ')
+ FROM vvv
WHERE idx = 't1_x'
GROUP BY tbl,idx
}
-} {/t1 t1_x .[ ABCDEFGHI]{10}./}
+} {t1 t1_x { A B C D E F G H I}}
do_test tkt-cbd05-2.1 {
db eval {
@@ -77,11 +104,11 @@ do_test tkt-cbd05-2.2 {
} {}
do_test tkt-cbd05-2.3 {
execsql {
- SELECT tbl,idx,group_concat(sample,' ')
- FROM sqlite_stat3
+ SELECT tbl,idx,group_concat(s(sample),' ')
+ FROM vvv
WHERE idx = 't1_x'
GROUP BY tbl,idx
}
-} {/t1 t1_x .[ ABCDEFGHI]{10}./}
+} {t1 t1_x { A B C D E F G H I}}
finish_test
diff --git a/test/tkt-d11f09d36e.test b/test/tkt-d11f09d36e.test
index 7065770..ffd3d21 100644
--- a/test/tkt-d11f09d36e.test
+++ b/test/tkt-d11f09d36e.test
@@ -59,4 +59,3 @@ do_test tkt-d11f09d36e.5 {
} {ok}
finish_test
-
diff --git a/test/tkt-f3e5abed55.test b/test/tkt-f3e5abed55.test
index b3f5d56..3c793d4 100644
--- a/test/tkt-f3e5abed55.test
+++ b/test/tkt-f3e5abed55.test
@@ -114,4 +114,3 @@ if {[permutation]!="inmemory_journal"} {
finish_test
-
diff --git a/test/tkt-f67b41381a.test b/test/tkt-f67b41381a.test
new file mode 100644
index 0000000..1ddec98
--- /dev/null
+++ b/test/tkt-f67b41381a.test
@@ -0,0 +1,53 @@
+# 2014 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.
+#
+#***********************************************************************
+# Test that ticket f67b41381a has been resolved.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tkt-f67b41381a
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ ALTER TABLE t1 ADD COLUMN b DEFAULT 2;
+ CREATE TABLE t2(a, b);
+ INSERT INTO t2 SELECT * FROM t1;
+ SELECT * FROM t2;
+} {1 2}
+
+db cache size 0
+foreach {tn tbls xfer} {
+ 1 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b) } 1
+ 2 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b) } 0
+ 3 { CREATE TABLE t1(a, b DEFAULT 'x'); CREATE TABLE t2(a, b DEFAULT 'x') } 1
+ 4 { CREATE TABLE t1(a, b DEFAULT NULL); CREATE TABLE t2(a, b) } 0
+ 5 { CREATE TABLE t1(a DEFAULT 2, b); CREATE TABLE t2(a DEFAULT 1, b) } 1
+ 6 { CREATE TABLE t1(a DEFAULT 1, b); CREATE TABLE t2(a DEFAULT 1, b) } 1
+ 7 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1);
+ CREATE TABLE t2(a DEFAULT 3, b DEFAULT 1) } 1
+ 8 { CREATE TABLE t1(a DEFAULT 1, b DEFAULT 1);
+ CREATE TABLE t2(a DEFAULT 3, b DEFAULT 3) } 0
+
+} {
+
+ execsql { DROP TABLE t1; DROP TABLE t2 }
+ execsql $tbls
+
+ set res 1
+ db eval { EXPLAIN INSERT INTO t1 SELECT * FROM t2 } {
+ if {$opcode == "Column"} { set res 0 }
+ }
+
+ do_test 2.$tn [list set res] $xfer
+}
+
+finish_test
diff --git a/test/tkt-f973c7ac31.test b/test/tkt-f973c7ac31.test
index 882e86a..4543090 100644
--- a/test/tkt-f973c7ac31.test
+++ b/test/tkt-f973c7ac31.test
@@ -84,4 +84,3 @@ foreach {tn sql} {
finish_test
-
diff --git a/test/tkt1567.test b/test/tkt1567.test
index 6c4548a..cb1a6be 100644
--- a/test/tkt1567.test
+++ b/test/tkt1567.test
@@ -40,7 +40,7 @@ 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}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test tkt1567-1.5 {
execsql {
COMMIT;
@@ -48,4 +48,35 @@ do_test tkt1567-1.5 {
} {}
integrity_check tkt1567-1.6
+do_test tkt1567-2.1 {
+ execsql {
+ CREATE TABLE t2(a TEXT PRIMARY KEY, rowid INT) WITHOUT rowid;
+ }
+ set bigstr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
+ for {set i 0} {$i<100} {incr i} {
+ set x [format %5d [expr $i*2]]
+ set sql "INSERT INTO t2 VALUES('$x-$bigstr', $i+1)"
+ execsql $sql
+ }
+} {}
+integrity_check tkt1567-2.2
+
+do_test tkt1567-2.3 {
+ execsql {
+ BEGIN;
+ UPDATE t2 SET a = a||'x' WHERE rowid%2==0;
+ }
+} {}
+do_test tkt1567-2.4 {
+ catchsql {
+ UPDATE t2 SET a = CASE WHEN rowid<90 THEN substr(a,1,10) ELSE '9999' END;
+ }
+} {1 {UNIQUE constraint failed: t2.a}}
+do_test tkt1567-2.5 {
+ execsql {
+ COMMIT;
+ }
+} {}
+integrity_check tkt1567-2.6
+
finish_test
diff --git a/test/tkt2822.test b/test/tkt2822.test
index d3512d3..d0b1633 100644
--- a/test/tkt2822.test
+++ b/test/tkt2822.test
@@ -208,15 +208,12 @@ do_test tkt2822-5.4 {
# In "ORDER BY +b" the term is now an expression rather than
# a label. It therefore matches by rule (3) instead of rule (2).
-#
-# 2013-04-13: This is busted. Changed to conform to PostgreSQL and
-# MySQL and Oracle behavior.
#
do_test tkt2822-5.5 {
execsql {
SELECT a AS b FROM t3 ORDER BY +b;
}
-} {1 9}
+} {9 1}
# Tests for rule 2 in compound queries
#
diff --git a/test/tkt3442.test b/test/tkt3442.test
index ae03949..ee9169c 100644
--- a/test/tkt3442.test
+++ b/test/tkt3442.test
@@ -49,10 +49,10 @@ proc EQP {sql} {
ifcapable explain {
do_test tkt3442-1.2 {
EQP { SELECT node FROM listhash WHERE id='5000' LIMIT 1; }
- } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}}
+ } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}}
do_test tkt3442-1.3 {
EQP { SELECT node FROM listhash WHERE id="5000" LIMIT 1; }
- } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}}
+ } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}}
}
@@ -61,7 +61,7 @@ ifcapable explain {
ifcapable explain {
do_test tkt3442-1.4 {
EQP { SELECT node FROM listhash WHERE id=5000 LIMIT 1; }
- } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?) (~1 rows)}}
+ } {0 0 0 {SEARCH TABLE listhash USING INDEX ididx (id=?)}}
}
do_test tkt3442-1.5 {
catchsql {
diff --git a/test/tkt35xx.test b/test/tkt35xx.test
index f9d10c3..3b911c1 100644
--- a/test/tkt35xx.test
+++ b/test/tkt35xx.test
@@ -74,7 +74,7 @@ do_test tkt35xx-1.2.2 {
DROP TABLE t5;
INSERT INTO t3(a, b) SELECT c, d FROM t4;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t3.a}}
do_test tkt35xx-1.2.3 {
# Show that the transaction has not been rolled back.
catchsql BEGIN
diff --git a/test/tkt3918.test b/test/tkt3918.test
index c46ad8f..e20ee15 100644
--- a/test/tkt3918.test
+++ b/test/tkt3918.test
@@ -57,4 +57,3 @@ do_test tkt3918.5 {
} {}
finish_test
-
diff --git a/test/tkt3929.test b/test/tkt3929.test
index 3ed4d28..db02bb8 100644
--- a/test/tkt3929.test
+++ b/test/tkt3929.test
@@ -50,4 +50,3 @@ do_test tkt3930-1.2 {
integrity_check tkt3930-1.3
finish_test
-
diff --git a/test/tpch01.test b/test/tpch01.test
new file mode 100644
index 0000000..ce48f8e
--- /dev/null
+++ b/test/tpch01.test
@@ -0,0 +1,192 @@
+# 2013-09-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.
+#
+#***********************************************************************
+#
+# TPC-H test queries.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix tpch01
+
+do_execsql_test tpch01-1.0 {
+ CREATE TABLE NATION ( N_NATIONKEY INTEGER NOT NULL,
+ N_NAME CHAR(25) NOT NULL,
+ N_REGIONKEY INTEGER NOT NULL,
+ N_COMMENT VARCHAR(152));
+ CREATE TABLE REGION ( R_REGIONKEY INTEGER NOT NULL,
+ R_NAME CHAR(25) NOT NULL,
+ R_COMMENT VARCHAR(152));
+ CREATE TABLE PART ( P_PARTKEY INTEGER NOT NULL,
+ P_NAME VARCHAR(55) NOT NULL,
+ P_MFGR CHAR(25) NOT NULL,
+ P_BRAND CHAR(10) NOT NULL,
+ P_TYPE VARCHAR(25) NOT NULL,
+ P_SIZE INTEGER NOT NULL,
+ P_CONTAINER CHAR(10) NOT NULL,
+ P_RETAILPRICE DECIMAL(15,2) NOT NULL,
+ P_COMMENT VARCHAR(23) NOT NULL );
+ CREATE TABLE SUPPLIER ( S_SUPPKEY INTEGER NOT NULL,
+ S_NAME CHAR(25) NOT NULL,
+ S_ADDRESS VARCHAR(40) NOT NULL,
+ S_NATIONKEY INTEGER NOT NULL,
+ S_PHONE CHAR(15) NOT NULL,
+ S_ACCTBAL DECIMAL(15,2) NOT NULL,
+ S_COMMENT VARCHAR(101) NOT NULL);
+ CREATE TABLE PARTSUPP ( PS_PARTKEY INTEGER NOT NULL,
+ PS_SUPPKEY INTEGER NOT NULL,
+ PS_AVAILQTY INTEGER NOT NULL,
+ PS_SUPPLYCOST DECIMAL(15,2) NOT NULL,
+ PS_COMMENT VARCHAR(199) NOT NULL );
+ CREATE TABLE CUSTOMER ( C_CUSTKEY INTEGER NOT NULL,
+ C_NAME VARCHAR(25) NOT NULL,
+ C_ADDRESS VARCHAR(40) NOT NULL,
+ C_NATIONKEY INTEGER NOT NULL,
+ C_PHONE CHAR(15) NOT NULL,
+ C_ACCTBAL DECIMAL(15,2) NOT NULL,
+ C_MKTSEGMENT CHAR(10) NOT NULL,
+ C_COMMENT VARCHAR(117) NOT NULL);
+ CREATE TABLE ORDERS ( O_ORDERKEY INTEGER NOT NULL,
+ O_CUSTKEY INTEGER NOT NULL,
+ O_ORDERSTATUS CHAR(1) NOT NULL,
+ O_TOTALPRICE DECIMAL(15,2) NOT NULL,
+ O_ORDERDATE DATE NOT NULL,
+ O_ORDERPRIORITY CHAR(15) NOT NULL,
+ O_CLERK CHAR(15) NOT NULL,
+ O_SHIPPRIORITY INTEGER NOT NULL,
+ O_COMMENT VARCHAR(79) NOT NULL);
+ CREATE TABLE LINEITEM ( L_ORDERKEY INTEGER NOT NULL,
+ L_PARTKEY INTEGER NOT NULL,
+ L_SUPPKEY INTEGER NOT NULL,
+ L_LINENUMBER INTEGER NOT NULL,
+ L_QUANTITY DECIMAL(15,2) NOT NULL,
+ L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL,
+ L_DISCOUNT DECIMAL(15,2) NOT NULL,
+ L_TAX DECIMAL(15,2) NOT NULL,
+ L_RETURNFLAG CHAR(1) NOT NULL,
+ L_LINESTATUS CHAR(1) NOT NULL,
+ L_SHIPDATE DATE NOT NULL,
+ L_COMMITDATE DATE NOT NULL,
+ L_RECEIPTDATE DATE NOT NULL,
+ L_SHIPINSTRUCT CHAR(25) NOT NULL,
+ L_SHIPMODE CHAR(10) NOT NULL,
+ L_COMMENT VARCHAR(44) NOT NULL);
+ CREATE INDEX npki on nation(N_NATIONKEY);
+ CREATE INDEX rpki on region(R_REGIONKEY);
+ CREATE INDEX ppki on part(P_PARTKEY);
+ CREATE INDEX spki on supplier(S_SUPPKEY);
+ CREATE INDEX pspki on partsupp(PS_PARTKEY, PS_SUPPKEY);
+ CREATE INDEX cpki on customer(C_CUSTKEY);
+ CREATE INDEX opki on orders(O_ORDERKEY);
+ CREATE INDEX lpki on lineitem(L_ORDERKEY, L_LINENUMBER);
+ CREATE INDEX nrki on nation(n_regionkey);
+ CREATE INDEX snki on supplier(s_nationkey);
+ CREATE INDEX cnki on customer(c_nationkey);
+ CREATE INDEX ocki on orders(O_CUSTKEY);
+ CREATE INDEX odi on orders(O_ORDERDATE);
+ CREATE INDEX lpki2 on lineitem(L_PARTKEY);
+ CREATE INDEX lski on lineitem(L_SUPPKEY);
+ CREATE INDEX lsdi on lineitem(L_SHIPDATE);
+ CREATE INDEX lcdi on lineitem(L_COMMITDATE);
+ CREATE INDEX lrdi on lineitem(L_RECEIPTDATE);
+ CREATE INDEX bootleg_nni on nation(N_NAME);
+ CREATE INDEX bootleg_psi on part(p_size);
+ CREATE INDEX bootleg_pti on part(p_type);
+ ANALYZE sqlite_master;
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lrdi','600572 236');
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lcdi','600572 244');
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lsdi','600572 238');
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lski','600572 601');
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lpki2','600572 31');
+ INSERT INTO sqlite_stat1 VALUES('LINEITEM','lpki','600572 5 1');
+ INSERT INTO sqlite_stat1 VALUES('ORDERS','odi','150000 63');
+ INSERT INTO sqlite_stat1 VALUES('ORDERS','ocki','150000 15');
+ INSERT INTO sqlite_stat1 VALUES('ORDERS','opki','150000 1');
+ INSERT INTO sqlite_stat1 VALUES('CUSTOMER','cnki','15000 600');
+ INSERT INTO sqlite_stat1 VALUES('CUSTOMER','cpki','15000 1');
+ INSERT INTO sqlite_stat1 VALUES('PARTSUPP','pspki','80000 4 1');
+ INSERT INTO sqlite_stat1 VALUES('SUPPLIER','snki','1000 40');
+ INSERT INTO sqlite_stat1 VALUES('SUPPLIER','spki','1000 1');
+ INSERT INTO sqlite_stat1 VALUES('PART','bootleg_pti','20000 134');
+ INSERT INTO sqlite_stat1 VALUES('PART','bootleg_psi','20000 400');
+ INSERT INTO sqlite_stat1 VALUES('PART','ppki','20000 1');
+ INSERT INTO sqlite_stat1 VALUES('REGION','rpki','5 1');
+ INSERT INTO sqlite_stat1 VALUES('NATION','bootleg_nni','25 1');
+ INSERT INTO sqlite_stat1 VALUES('NATION','nrki','25 5');
+ INSERT INTO sqlite_stat1 VALUES('NATION','npki','25 1');
+ ANALYZE sqlite_master;
+} {}
+
+do_test tpch01-1.1 {
+ unset -nocomplain ::eqpres
+ set ::eqpres [db eval {EXPLAIN QUERY PLAN
+ select
+ o_year,
+ sum(case
+ when nation = 'EGYPT' then volume
+ else 0
+ end) / sum(volume) as mkt_share
+ from
+ (
+ select
+ strftime('%Y', o_orderdate) as o_year,
+ l_extendedprice * (1 - l_discount) as volume,
+ n2.n_name as nation
+ from
+ part,
+ supplier,
+ lineitem,
+ orders,
+ customer,
+ nation n1,
+ nation n2,
+ region
+ where
+ p_partkey = l_partkey
+ and s_suppkey = l_suppkey
+ and l_orderkey = o_orderkey
+ and o_custkey = c_custkey
+ and c_nationkey = n1.n_nationkey
+ and n1.n_regionkey = r_regionkey
+ and r_name = 'MIDDLE EAST'
+ and s_nationkey = n2.n_nationkey
+ and o_orderdate between '1995-01-01' and '1996-12-31'
+ and p_type = 'LARGE PLATED STEEL'
+ ) as all_nations
+ group by
+ o_year
+ order by
+ o_year;}]
+ set ::eqpres
+} {/0 0 0 {SEARCH TABLE part USING INDEX bootleg_pti .P_TYPE=..} 0 1 2 {SEARCH TABLE lineitem USING INDEX lpki2 .L_PARTKEY=..}.*/}
+do_test tpch01-1.1b {
+ set ::eqpres
+} {/.* customer .* nation AS n1 .*/}
+do_test tpch01-1.1c {
+ set ::eqpres
+} {/.* supplier .* nation AS n2 .*/}
+
+do_eqp_test tpch01-1.2 {
+select
+ c_custkey, c_name, sum(l_extendedprice * (1 - l_discount)) as revenue,
+ c_acctbal, n_name, c_address, c_phone, c_comment
+from
+ customer, orders, lineitem, nation
+where
+ c_custkey = o_custkey and l_orderkey = o_orderkey
+ and o_orderdate >= '1994-08-01' and o_orderdate < date('1994-08-01', '+3 month')
+ and l_returnflag = 'R' and c_nationkey = n_nationkey
+group by
+ c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment
+order by
+ revenue desc;
+} {0 0 1 {SEARCH TABLE orders USING INDEX odi (O_ORDERDATE>? AND O_ORDERDATE<?)} 0 1 0 {SEARCH TABLE customer USING INDEX cpki (C_CUSTKEY=?)} 0 2 3 {SEARCH TABLE nation USING INDEX npki (N_NATIONKEY=?)} 0 3 2 {SEARCH TABLE lineitem USING INDEX lpki (L_ORDERKEY=?)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
+
+finish_test
diff --git a/test/trace2.test b/test/trace2.test
index 2f7ae7d..562c70c 100644
--- a/test/trace2.test
+++ b/test/trace2.test
@@ -128,6 +128,7 @@ ifcapable fts3 {
INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');
} {
"INSERT INTO x1 VALUES('North northwest wind between 8 and 14 mph');"
+ "-- DELETE FROM 'main'.'x1_segdir' WHERE level = ?"
"-- INSERT INTO 'main'.'x1_content' VALUES(?,(?))"
"-- REPLACE INTO 'main'.'x1_docsize' VALUES(?,?)"
"-- SELECT value FROM 'main'.'x1_stat' WHERE id=?"
@@ -135,6 +136,7 @@ ifcapable fts3 {
"-- SELECT (SELECT max(idx) FROM 'main'.'x1_segdir' WHERE level = ?) + 1"
"-- SELECT coalesce((SELECT max(blockid) FROM 'main'.'x1_segments') + 1, 1)"
"-- REPLACE INTO 'main'.'x1_segdir' VALUES(?,?,?,?,?,?)"
+ "-- SELECT level, idx, end_block FROM 'main'.'x1_segdir' WHERE level BETWEEN ? AND ? ORDER BY level DESC, idx ASC"
}
do_trace_test 2.3 {
diff --git a/test/trans2.test b/test/trans2.test
index cfd3e67..36a2f99 100644
--- a/test/trans2.test
+++ b/test/trans2.test
@@ -161,7 +161,7 @@ for {set i 2} {$i<=30} {incr i} {
z = CASE WHEN id<$max_rowid
THEN zeroblob((random()&65535)%5000 + 1000) END;
}
- } {1 {t1.z may not be NULL}}
+ } {1 {NOT NULL constraint failed: t1.z}}
do_test trans2-$i.11 {
db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id}
} $newres
@@ -185,7 +185,7 @@ for {set i 2} {$i<=30} {incr i} {
z = CASE WHEN id<$max1
THEN zeroblob((random()&65535)%5000 + 1000) END;
}
- } {1 {t1.z may not be NULL}}
+ } {1 {NOT NULL constraint failed: t1.z}}
do_test trans2-$i.31 {
db eval {SELECT md5sum(u1), md5sum(u2) FROM t1 ORDER BY id}
} $origres
diff --git a/test/transitive1.test b/test/transitive1.test
index ff81af6..200dc61 100644
--- a/test/transitive1.test
+++ b/test/transitive1.test
@@ -47,4 +47,238 @@ do_execsql_test transitive1-220 {
SELECT * FROM t2 WHERE a=b AND c=b AND c<=20 ORDER BY +a;
} {20 20 20 100 100 100}
+# Test cases for ticket [[d805526eae253103] 2013-07-08
+# "Incorrect join result or assertion fault due to transitive constraints"
+#
+do_execsql_test transitive1-300 {
+ CREATE TABLE t301(w INTEGER PRIMARY KEY, x);
+ CREATE TABLE t302(y INTEGER UNIQUE, z);
+ INSERT INTO t301 VALUES(1,2),(3,4),(5,6);
+ INSERT INTO t302 VALUES(1,3),(3,6),(5,7);
+ SELECT *
+ FROM t301 CROSS JOIN t302
+ WHERE w=y AND y IS NOT NULL
+ ORDER BY +w;
+} {1 2 1 3 3 4 3 6 5 6 5 7}
+do_execsql_test transitive1-301 {
+ SELECT *
+ FROM t301 CROSS JOIN t302
+ WHERE w=y AND y IS NOT NULL
+ ORDER BY w;
+} {1 2 1 3 3 4 3 6 5 6 5 7}
+do_execsql_test transitive1-310 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y>1
+ ORDER BY +w
+} {3 4 3 6 5 6 5 7}
+do_execsql_test transitive1-311 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y>1
+ ORDER BY w
+} {3 4 3 6 5 6 5 7}
+do_execsql_test transitive1-312 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y>1
+ ORDER BY w DESC
+} {5 6 5 7 3 4 3 6}
+do_execsql_test transitive1-320 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y BETWEEN 2 AND 4;
+} {3 4 3 6}
+do_execsql_test transitive1-331 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y BETWEEN 1 AND 4
+ ORDER BY w;
+} {1 2 1 3 3 4 3 6}
+do_execsql_test transitive1-332 {
+ SELECT *
+ FROM t301 CROSS JOIN t302 ON w=y
+ WHERE y BETWEEN 1 AND 4
+ ORDER BY w DESC;
+} {3 4 3 6 1 2 1 3}
+
+# Ticket [c620261b5b5dc] circa 2013-10-28.
+# Make sureconstraints are not used with LEFT JOINs.
+#
+# The next case is from the ticket report. It outputs no rows in 3.8.1
+# prior to the bug-fix.
+#
+do_execsql_test transitive1-400 {
+ CREATE TABLE t401(a);
+ CREATE TABLE t402(b);
+ CREATE TABLE t403(c INTEGER PRIMARY KEY);
+ INSERT INTO t401 VALUES(1);
+ INSERT INTO t403 VALUES(1);
+ SELECT '1-row' FROM t401 LEFT JOIN t402 ON b=a JOIN t403 ON c=a;
+} {1-row}
+
+# The following is a script distilled from the XBMC project where the
+# bug was originally encountered. The correct answer is a single row
+# of output. Before the bug was fixed, zero rows were generated.
+#
+do_execsql_test transitive1-410 {
+ CREATE TABLE bookmark ( idBookmark integer primary key, idFile integer, timeInSeconds double, totalTimeInSeconds double, thumbNailImage text, player text, playerState text, type integer);
+ CREATE TABLE path ( idPath integer primary key, strPath text, strContent text, strScraper text, strHash text, scanRecursive integer, useFolderNames bool, strSettings text, noUpdate bool, exclude bool, dateAdded text);
+ INSERT INTO "path" VALUES(1,'/tmp/tvshows/','tvshows','metadata.tvdb.com','989B1CE5680A14F5F86123F751169B49',0,0,'<settings><setting id="absolutenumber" value="false" /><setting id="dvdorder" value="false" /><setting id="fanart" value="true" /><setting id="language" value="en" /></settings>',0,0,NULL);
+ INSERT INTO "path" VALUES(2,'/tmp/tvshows/The.Big.Bang.Theory/','','','85E1DAAB2F5FF6EAE8AEDF1B5C882D1E',NULL,NULL,NULL,NULL,NULL,'2013-10-23 18:58:43');
+ CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text);
+ INSERT INTO "files" VALUES(1,2,'The.Big.Bang.Theory.S01E01.WEB-DL.AAC2.0.H264.mkv',NULL,NULL,'2013-10-23 18:57:36');
+ CREATE TABLE tvshow ( idShow integer primary key,c00 text,c01 text,c02 text,c03 text,c04 text,c05 text,c06 text,c07 text,c08 text,c09 text,c10 text,c11 text,c12 text,c13 text,c14 text,c15 text,c16 text,c17 text,c18 text,c19 text,c20 text,c21 text,c22 text,c23 text);
+ INSERT INTO "tvshow" VALUES(1,'The Big Bang Theory','Leonard Hofstadter and Sheldon Cooper are brilliant physicists, the kind of "beautiful minds" that understand how the universe works. But none of that genius helps them interact with people, especially women. All this begins to change when a free-spirited beauty named Penny moves in next door. Sheldon, Leonard''s roommate, is quite content spending his nights playing Klingon Boggle with their socially dysfunctional friends, fellow CalTech scientists Howard Wolowitz and Raj Koothrappali. However, Leonard sees in Penny a whole new universe of possibilities... including love.','','','9.200000','2007-09-24','<thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g13.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g23.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g18.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g17.jpg</thumb><thumb aspect="banner">http://
+ thetvdb.com/banners/graphical/80379-g6.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g5.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g2.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g11.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g12.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g19.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g3.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g4.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g15.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g22.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g7.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g10.jpg</thumb><thumb
+ aspect="banner">http://thetvdb.com/banners/graphical/80379-g24.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g8.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g9.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g14.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g16.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/graphical/80379-g21.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/text/80379-4.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/text/80379-2.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/text/80379-3.jpg</thumb><thumb aspect="banner">http://thetvdb.com/banners/text/80379-5.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-8.jpg</thumb><thumb aspect="poster" type="season" season="0">http://thetvdb.com/banners/seasons/80379-0-4.jpg</thumb><thumb aspect="poster" type="season"
+ season="1">http://thetvdb.com/banners/seasons/80379-1-12.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-9.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-11.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-9.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-8.jpg</thumb><thumb aspect="poster" type="season" season="7">http://thetvdb.com/banners/seasons/80379-7-3.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-4.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-5.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-9.jpg</thumb><thumb aspect="poster" type="season" season="0">http://thetvdb.com/banners/seasons/80379-0-2.jpg</thumb><thumb aspect="
+ poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-6.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-4.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-2.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-9.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-4.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-2.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-7.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-10.jpg</
+ thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-5.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-5.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-4.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-3.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-6.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2.jpg</thumb><thumb aspect="poster" type="season" season="7">http://thetvdb.com/banners/seasons/80379-7.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-
+ 1-7.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-2.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-3.jpg</thumb><thumb aspect="poster" type="season" season="7">http://thetvdb.com/banners/seasons/80379-7-2.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-2.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-5.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-3.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-5.jpg</thumb><thumb aspect="poster" type="season" season="0">http://thetvdb.com/banners/seasons/80379-0.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-5.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/
+ seasons/80379-1-6.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-3.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-8.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6-7.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-8.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-7.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-6.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-8.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-11.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-10.jpg</thumb><thumb aspect="poster" type="season" season="1">http://
+ thetvdb.com/banners/seasons/80379-1-8.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-7.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-4.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-3.jpg</thumb><thumb aspect="poster" type="season" season="1">http://thetvdb.com/banners/seasons/80379-1-4.jpg</thumb><thumb aspect="poster" type="season" season="3">http://thetvdb.com/banners/seasons/80379-3-3.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-7.jpg</thumb><thumb aspect="poster" type="season" season="6">http://thetvdb.com/banners/seasons/80379-6.jpg</thumb><thumb aspect="poster" type="season" season="2">http://thetvdb.com/banners/seasons/80379-2-2.jpg</thumb><thumb aspect="poster" type="season" season="5">http://thetvdb.com/banners/seasons/80379-5-6.jpg</thumb><thumb aspect="poster" type="season"
+ season="3">http://thetvdb.com/banners/seasons/80379-3-2.jpg</thumb><thumb aspect="poster" type="season" season="4">http://thetvdb.com/banners/seasons/80379-4-6.jpg</thumb><thumb aspect="banner" type="season" season="5">http://thetvdb.com/banners/seasonswide/80379-5.jpg</thumb><thumb aspect="banner" type="season" season="3">http://thetvdb.com/banners/seasonswide/80379-3-2.jpg</thumb><thumb aspect="banner" type="season" season="1">http://thetvdb.com/banners/seasonswide/80379-1-2.jpg</thumb><thumb aspect="banner" type="season" season="2">http://thetvdb.com/banners/seasonswide/80379-2-2.jpg</thumb><thumb aspect="banner" type="season" season="4">http://thetvdb.com/banners/seasonswide/80379-4-2.jpg</thumb><thumb aspect="banner" type="season" season="0">http://thetvdb.com/banners/seasonswide/80379-0.jpg</thumb><thumb aspect="banner" type="season" season="0">http://thetvdb.com/banners/seasonswide/80379-0-2.jpg</thumb><thumb aspect="banner" type="season" season="1">http://thetvdb.com/banners/seasonswide/80379-1.jpg</
+ thumb><thumb aspect="banner" type="season" season="2">http://thetvdb.com/banners/seasonswide/80379-2.jpg</thumb><thumb aspect="banner" type="season" season="4">http://thetvdb.com/banners/seasonswide/80379-4.jpg</thumb><thumb aspect="banner" type="season" season="3">http://thetvdb.com/banners/seasonswide/80379-3.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-22.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-18.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-13.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-10.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-16.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-1.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-9.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-2.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-19.jpg</
+ thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-8.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-4.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-20.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-23.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-7.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-3.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-12.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-11.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-15.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-21.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-14.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-17.jpg</thumb><thumb aspect="poster">http://thetvdb.com/banners/posters/80379-6.jpg</thumb><thumb
+ aspect="poster">http://thetvdb.com/banners/posters/80379-5.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-22.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-18.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-13.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-10.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-16.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-1.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-9.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-2.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-19.jpg</thumb><thumb aspect="
+ poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-8.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-4.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-20.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-23.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-7.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-3.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-12.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-11.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-15.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-21.jpg</
+ thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-14.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-17.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-6.jpg</thumb><thumb aspect="poster" type="season" season="-1">http://thetvdb.com/banners/posters/80379-5.jpg</thumb>','','Comedy','','<episodeguide><url cache="80379-en.xml">http://thetvdb.com/api/1D62F2F90030C444/series/80379/all/en.zip</url></episodeguide>','<fanart url="http://thetvdb.com/banners/"><thumb dim="1920x1080" colors="|192,185,169|19,20,25|57,70,89|" preview="_cache/fanart/original/80379-2.jpg">fanart/original/80379-2.jpg</thumb><thumb dim="1920x1080" colors="|94,28,16|194,18,38|0,0,8|" preview="_cache/fanart/original/80379-34.jpg">fanart/original/80379-34.jpg</thumb><thumb dim="1280x720" colors="|254,157,210|11,12,7|191,152,111|" preview="_cache/fanart/original/80379-4.jpg">fanart/original/80379-
+ 4.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-42.jpg">fanart/original/80379-42.jpg</thumb><thumb dim="1920x1080" colors="|236,187,155|136,136,128|254,254,252|" preview="_cache/fanart/original/80379-37.jpg">fanart/original/80379-37.jpg</thumb><thumb dim="1920x1080" colors="|112,102,152|116,109,116|235,152,146|" preview="_cache/fanart/original/80379-14.jpg">fanart/original/80379-14.jpg</thumb><thumb dim="1920x1080" colors="|150,158,161|174,75,121|150,98,58|" preview="_cache/fanart/original/80379-16.jpg">fanart/original/80379-16.jpg</thumb><thumb dim="1280x720" colors="|224,200,176|11,1,28|164,96,0|" preview="_cache/fanart/original/80379-1.jpg">fanart/original/80379-1.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-35.jpg">fanart/original/80379-35.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-40.jpg">fanart/original/80379-40.jpg</thumb><thumb dim="1920x1080" colors="|255,255,255|30,19,13|155,112,70|"
+ preview="_cache/fanart/original/80379-31.jpg">fanart/original/80379-31.jpg</thumb><thumb dim="1920x1080" colors="|241,195,172|84,54,106|254,221,206|" preview="_cache/fanart/original/80379-29.jpg">fanart/original/80379-29.jpg</thumb><thumb dim="1280x720" colors="|197,167,175|219,29,39|244,208,192|" preview="_cache/fanart/original/80379-11.jpg">fanart/original/80379-11.jpg</thumb><thumb dim="1280x720" colors="|195,129,97|244,192,168|219,148,118|" preview="_cache/fanart/original/80379-24.jpg">fanart/original/80379-24.jpg</thumb><thumb dim="1920x1080" colors="|14,10,11|255,255,255|175,167,164|" preview="_cache/fanart/original/80379-30.jpg">fanart/original/80379-30.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-19.jpg">fanart/original/80379-19.jpg</thumb><thumb dim="1920x1080" colors="|246,199,69|98,55,38|161,127,82|" preview="_cache/fanart/original/80379-9.jpg">fanart/original/80379-9.jpg</thumb><thumb dim="1280x720" colors="|129,22,14|48,50,39|223,182,64|" preview="_cache/
+ fanart/original/80379-13.jpg">fanart/original/80379-13.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-45.jpg">fanart/original/80379-45.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-33.jpg">fanart/original/80379-33.jpg</thumb><thumb dim="1280x720" colors="|103,77,60|224,180,153|129,100,84|" preview="_cache/fanart/original/80379-10.jpg">fanart/original/80379-10.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-23.jpg">fanart/original/80379-23.jpg</thumb><thumb dim="1280x720" colors="|219,29,39|0,4,10|88,117,135|" preview="_cache/fanart/original/80379-12.jpg">fanart/original/80379-12.jpg</thumb><thumb dim="1920x1080" colors="|226,209,165|51,18,9|89,54,24|" preview="_cache/fanart/original/80379-5.jpg">fanart/original/80379-5.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-26.jpg">fanart/original/80379-26.jpg</thumb><thumb dim="1280x720" colors="|249,251,229|126,47,53|251,226,
+ 107|" preview="_cache/fanart/original/80379-27.jpg">fanart/original/80379-27.jpg</thumb><thumb dim="1920x1080" colors="|233,218,65|30,27,46|173,53,18|" preview="_cache/fanart/original/80379-32.jpg">fanart/original/80379-32.jpg</thumb><thumb dim="1280x720" colors="|248,248,248|64,54,78|188,193,196|" preview="_cache/fanart/original/80379-3.jpg">fanart/original/80379-3.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-25.jpg">fanart/original/80379-25.jpg</thumb><thumb dim="1280x720" colors="|159,150,133|59,39,32|168,147,104|" preview="_cache/fanart/original/80379-7.jpg">fanart/original/80379-7.jpg</thumb><thumb dim="1920x1080" colors="|221,191,157|11,7,6|237,146,102|" preview="_cache/fanart/original/80379-21.jpg">fanart/original/80379-21.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-28.jpg">fanart/original/80379-28.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-36.jpg">fanart/original/80379-36.jpg</thumb><thumb
+ dim="1920x1080" colors="|253,237,186|33,25,22|245,144,38|" preview="_cache/fanart/original/80379-38.jpg">fanart/original/80379-38.jpg</thumb><thumb dim="1920x1080" colors="|174,111,68|243,115,50|252,226,45|" preview="_cache/fanart/original/80379-20.jpg">fanart/original/80379-20.jpg</thumb><thumb dim="1920x1080" colors="|63,56,123|87,59,47|63,56,123|" preview="_cache/fanart/original/80379-17.jpg">fanart/original/80379-17.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-43.jpg">fanart/original/80379-43.jpg</thumb><thumb dim="1280x720" colors="|69,68,161|142,118,142|222,191,137|" preview="_cache/fanart/original/80379-22.jpg">fanart/original/80379-22.jpg</thumb><thumb dim="1280x720" colors="|1,108,206|242,209,192|250,197,163|" preview="_cache/fanart/original/80379-15.jpg">fanart/original/80379-15.jpg</thumb><thumb dim="1280x720" colors="|239,229,237|0,0,0|167,136,115|" preview="_cache/fanart/original/80379-18.jpg">fanart/original/80379-18.jpg</thumb><thumb dim="1280x720" colors=""
+ preview="_cache/fanart/original/80379-6.jpg">fanart/original/80379-6.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-8.jpg">fanart/original/80379-8.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-41.jpg">fanart/original/80379-41.jpg</thumb><thumb dim="1920x1080" colors="" preview="_cache/fanart/original/80379-44.jpg">fanart/original/80379-44.jpg</thumb><thumb dim="1280x720" colors="" preview="_cache/fanart/original/80379-39.jpg">fanart/original/80379-39.jpg</thumb></fanart>','80379','TV-PG','CBS','','/tmp/tvshows/The.Big.Bang.Theory/','1',NULL,NULL,NULL,NULL,NULL,NULL);
+ CREATE TABLE episode ( idEpisode integer primary key, idFile integer,c00 text,c01 text,c02 text,c03 text,c04 text,c05 text,c06 text,c07 text,c08 text,c09 text,c10 text,c11 text,c12 varchar(24),c13 varchar(24),c14 text,c15 text,c16 text,c17 varchar(24),c18 text,c19 text,c20 text,c21 text,c22 text,c23 text, idShow integer);
+ INSERT INTO "episode" VALUES(1,1,'Pilot','Brilliant physicist roommates Leonard and Sheldon meet their new neighbor Penny, who begins showing them that as much as they know about science, they know little about actual living.','','7.700000','Chuck Lorre / Bill Prady','2007-09-24','<thumb>http://thetvdb.com/banners/episodes/80379/332484.jpg</thumb>','',NULL,'1800','James Burrows','','1','1','','-1','-1','-1','/tmp/tvshows/The.Big.Bang.Theory/The.Big.Bang.Theory.S01E01.WEB-DL.AAC2.0.H264.mkv','2','332484',NULL,NULL,NULL,1);
+ CREATE TABLE tvshowlinkpath (idShow integer, idPath integer);
+ INSERT INTO "tvshowlinkpath" VALUES(1,2);
+ CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer);
+ INSERT INTO "seasons" VALUES(1,1,-1);
+ INSERT INTO "seasons" VALUES(2,1,0);
+ INSERT INTO "seasons" VALUES(3,1,1);
+ INSERT INTO "seasons" VALUES(4,1,2);
+ INSERT INTO "seasons" VALUES(5,1,3);
+ INSERT INTO "seasons" VALUES(6,1,4);
+ INSERT INTO "seasons" VALUES(7,1,5);
+ INSERT INTO "seasons" VALUES(8,1,6);
+ INSERT INTO "seasons" VALUES(9,1,7);
+ CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT);
+ INSERT INTO "art" VALUES(1,1,'actor','thumb','http://thetvdb.com/banners/actors/73597.jpg');
+ INSERT INTO "art" VALUES(2,2,'actor','thumb','http://thetvdb.com/banners/actors/73596.jpg');
+ INSERT INTO "art" VALUES(3,3,'actor','thumb','http://thetvdb.com/banners/actors/73595.jpg');
+ INSERT INTO "art" VALUES(4,4,'actor','thumb','http://thetvdb.com/banners/actors/73599.jpg');
+ INSERT INTO "art" VALUES(5,5,'actor','thumb','http://thetvdb.com/banners/actors/73598.jpg');
+ INSERT INTO "art" VALUES(6,6,'actor','thumb','http://thetvdb.com/banners/actors/283158.jpg');
+ INSERT INTO "art" VALUES(7,7,'actor','thumb','http://thetvdb.com/banners/actors/283157.jpg');
+ INSERT INTO "art" VALUES(8,8,'actor','thumb','http://thetvdb.com/banners/actors/91271.jpg');
+ INSERT INTO "art" VALUES(9,9,'actor','thumb','http://thetvdb.com/banners/actors/294178.jpg');
+ INSERT INTO "art" VALUES(10,10,'actor','thumb','http://thetvdb.com/banners/actors/283159.jpg');
+ INSERT INTO "art" VALUES(11,1,'tvshow','banner','http://thetvdb.com/banners/graphical/80379-g13.jpg');
+ INSERT INTO "art" VALUES(12,1,'tvshow','fanart','http://thetvdb.com/banners/fanart/original/80379-2.jpg');
+ INSERT INTO "art" VALUES(13,1,'tvshow','poster','http://thetvdb.com/banners/posters/80379-22.jpg');
+ INSERT INTO "art" VALUES(14,1,'season','poster','http://thetvdb.com/banners/posters/80379-22.jpg');
+ INSERT INTO "art" VALUES(15,2,'season','banner','http://thetvdb.com/banners/seasonswide/80379-0.jpg');
+ INSERT INTO "art" VALUES(16,2,'season','poster','http://thetvdb.com/banners/seasons/80379-0-4.jpg');
+ INSERT INTO "art" VALUES(17,3,'season','banner','http://thetvdb.com/banners/seasonswide/80379-1-2.jpg');
+ INSERT INTO "art" VALUES(18,3,'season','poster','http://thetvdb.com/banners/seasons/80379-1-12.jpg');
+ INSERT INTO "art" VALUES(19,4,'season','banner','http://thetvdb.com/banners/seasonswide/80379-2-2.jpg');
+ INSERT INTO "art" VALUES(20,4,'season','poster','http://thetvdb.com/banners/seasons/80379-2-11.jpg');
+ INSERT INTO "art" VALUES(21,5,'season','banner','http://thetvdb.com/banners/seasonswide/80379-3-2.jpg');
+ INSERT INTO "art" VALUES(22,5,'season','poster','http://thetvdb.com/banners/seasons/80379-3-9.jpg');
+ INSERT INTO "art" VALUES(23,6,'season','banner','http://thetvdb.com/banners/seasonswide/80379-4-2.jpg');
+ INSERT INTO "art" VALUES(24,6,'season','poster','http://thetvdb.com/banners/seasons/80379-4-8.jpg');
+ INSERT INTO "art" VALUES(25,7,'season','banner','http://thetvdb.com/banners/seasonswide/80379-5.jpg');
+ INSERT INTO "art" VALUES(26,7,'season','poster','http://thetvdb.com/banners/seasons/80379-5-9.jpg');
+ INSERT INTO "art" VALUES(27,8,'season','poster','http://thetvdb.com/banners/seasons/80379-6-8.jpg');
+ INSERT INTO "art" VALUES(28,9,'season','poster','http://thetvdb.com/banners/seasons/80379-7-3.jpg');
+ INSERT INTO "art" VALUES(29,1,'episode','thumb','http://thetvdb.com/banners/episodes/80379/332484.jpg');
+ CREATE INDEX ix_bookmark ON bookmark (idFile, type);
+ CREATE INDEX ix_path ON path ( strPath );
+ CREATE INDEX ix_files ON files ( idPath, strFilename );
+ CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile);
+ CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode);
+ CREATE INDEX ix_episode_season_episode on episode (c12, c13);
+ CREATE INDEX ix_episode_bookmark on episode (c17);
+ CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow);
+ CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode);
+ CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath );
+ CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow );
+ CREATE INDEX ixEpisodeBasePath ON episode ( c19 );
+ CREATE INDEX ixTVShowBasePath on tvshow ( c17 );
+ CREATE INDEX ix_seasons ON seasons (idShow, season);
+ CREATE INDEX ix_art ON art(media_id, media_type, type);
+ CREATE VIEW episodeview
+ AS
+ SELECT episode.*,
+ files.strfilename AS strFileName,
+ path.strpath AS strPath,
+ files.playcount AS playCount,
+ files.lastplayed AS lastPlayed,
+ files.dateadded AS dateAdded,
+ tvshow.c00 AS strTitle,
+ tvshow.c14 AS strStudio,
+ tvshow.c05 AS premiered,
+ tvshow.c13 AS mpaa,
+ tvshow.c16 AS strShowPath,
+ bookmark.timeinseconds AS resumeTimeInSeconds,
+ bookmark.totaltimeinseconds AS totalTimeInSeconds,
+ seasons.idseason AS idSeason
+ FROM episode
+ JOIN files
+ ON files.idfile = episode.idfile
+ JOIN tvshow
+ ON tvshow.idshow = episode.idshow
+ LEFT JOIN seasons
+ ON seasons.idshow = episode.idshow
+ AND seasons.season = episode.c12
+ JOIN path
+ ON files.idpath = path.idpath
+ LEFT JOIN bookmark
+ ON bookmark.idfile = episode.idfile
+ AND bookmark.type = 1;
+ CREATE VIEW tvshowview
+ AS
+ SELECT tvshow.*,
+ path.strpath AS strPath,
+ path.dateadded AS dateAdded,
+ Max(files.lastplayed) AS lastPlayed,
+ NULLIF(Count(episode.c12), 0) AS totalCount,
+ Count(files.playcount) AS watchedcount,
+ NULLIF(Count(DISTINCT( episode.c12 )), 0) AS totalSeasons
+ FROM tvshow
+ LEFT JOIN tvshowlinkpath
+ ON tvshowlinkpath.idshow = tvshow.idshow
+ LEFT JOIN path
+ ON path.idpath = tvshowlinkpath.idpath
+ LEFT JOIN episode
+ ON episode.idshow = tvshow.idshow
+ LEFT JOIN files
+ ON files.idfile = episode.idfile
+ GROUP BY tvshow.idshow;
+ SELECT
+ episodeview.c12,
+ path.strPath,
+ tvshowview.c00,
+ tvshowview.c01,
+ tvshowview.c05,
+ tvshowview.c08,
+ tvshowview.c14,
+ tvshowview.c13,
+ seasons.idSeason,
+ count(1),
+ count(files.playCount)
+ FROM episodeview
+ JOIN tvshowview ON tvshowview.idShow = episodeview.idShow
+ JOIN seasons ON (seasons.idShow = tvshowview.idShow
+ AND seasons.season = episodeview.c12)
+ JOIN files ON files.idFile = episodeview.idFile
+ JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow
+ JOIN path ON path.idPath = tvshowlinkpath.idPath
+ WHERE tvshowview.idShow = 1
+ GROUP BY episodeview.c12;
+} {1 /tmp/tvshows/The.Big.Bang.Theory/ {The Big Bang Theory} {Leonard Hofstadter and Sheldon Cooper are brilliant physicists, the kind of "beautiful minds" that understand how the universe works. But none of that genius helps them interact with people, especially women. All this begins to change when a free-spirited beauty named Penny moves in next door. Sheldon, Leonard's roommate, is quite content spending his nights playing Klingon Boggle with their socially dysfunctional friends, fellow CalTech scientists Howard Wolowitz and Raj Koothrappali. However, Leonard sees in Penny a whole new universe of possibilities... including love.} 2007-09-24 Comedy CBS TV-PG 3 1 0}
+
+
finish_test
diff --git a/test/trigger2.test b/test/trigger2.test
index b4b922a..7b939bd 100644
--- a/test/trigger2.test
+++ b/test/trigger2.test
@@ -497,7 +497,7 @@ ifcapable conflict {
catchsql {
INSERT OR ABORT INTO tbl values (2, 2, 3);
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.1c {
execsql {
SELECT * from tbl;
@@ -507,7 +507,7 @@ ifcapable conflict {
catchsql {
INSERT OR FAIL INTO tbl values (2, 2, 3);
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.1e {
execsql {
SELECT * from tbl;
@@ -523,7 +523,7 @@ ifcapable conflict {
catchsql {
INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.1h {
execsql {
SELECT * from tbl;
@@ -551,7 +551,7 @@ ifcapable conflict {
catchsql {
UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.2c {
execsql {
SELECT * from tbl;
@@ -561,7 +561,7 @@ ifcapable conflict {
catchsql {
UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.2e {
execsql {
SELECT * from tbl;
@@ -583,7 +583,7 @@ ifcapable conflict {
catchsql {
UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
}
- } {1 {column a is not unique}}
+ } {1 {UNIQUE constraint failed: tbl.a}}
do_test trigger2-6.2h {
execsql {
SELECT * from tbl;
diff --git a/test/triggerC.test b/test/triggerC.test
index 8d98487..14cc0f0 100644
--- a/test/triggerC.test
+++ b/test/triggerC.test
@@ -157,7 +157,7 @@ do_test triggerC-1.14 {
} {}
do_test triggerC-1.15 {
catchsql { UPDATE OR ROLLBACK t1 SET a=100 }
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
#-------------------------------------------------------------------------
diff --git a/test/triggerE.test b/test/triggerE.test
new file mode 100644
index 0000000..e2727bb
--- /dev/null
+++ b/test/triggerE.test
@@ -0,0 +1,112 @@
+# 2009 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 tests the effects of SQL variable references embedded in
+# triggers. If the user attempts to create such a trigger, it is an
+# error. However, if an existing trigger definition is read from
+# the sqlite_master table, the variable reference always evaluates
+# to NULL.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+ifcapable {!trigger} {
+ finish_test
+ return
+}
+set testprefix triggerE
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d);
+ CREATE TABLE t3(e, f);
+}
+
+# forcedelete test.db2
+# do_execsql_test 1.1 {
+# ATTACH 'test.db2' AS aux;
+# CREATE TABLE aux.t4(x);
+# INSERT INTO aux.t4 VALUES(5);
+#
+# CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN new.a IN (SELECT x FROM aux.t4)
+# BEGIN
+# SELECT 1;
+# END;
+# }
+# do_execsql_test 1.2 { INSERT INTO t1 VALUES(1,1); }
+# do_execsql_test 1.3 { INSERT INTO t1 VALUES(5,5); }
+
+#-------------------------------------------------------------------------
+# Attempt to create various triggers that use bound variables.
+#
+set errmsg "trigger cannot use variables"
+foreach {tn defn} {
+ 1 { AFTER INSERT ON t1 WHEN new.a = ? BEGIN SELECT 1; END; }
+ 2 { BEFORE DELETE ON t1 BEGIN SELECT ?; END; }
+ 3 { BEFORE DELETE ON t1 BEGIN SELECT * FROM (SELECT * FROM (SELECT ?)); END; }
+ 5 { BEFORE DELETE ON t1 BEGIN SELECT * FROM t2 GROUP BY ?; END; }
+ 6 { BEFORE DELETE ON t1 BEGIN SELECT * FROM t2 LIMIT ?; END; }
+ 7 { BEFORE DELETE ON t1 BEGIN SELECT * FROM t2 ORDER BY ?; END; }
+ 8 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = ?; END; }
+ 9 { BEFORE UPDATE ON t1 BEGIN UPDATE t2 SET c = 1 WHERE d = ?; END; }
+} {
+ catchsql {drop trigger tr1}
+ do_catchsql_test 1.1.$tn "CREATE TRIGGER tr1 $defn" [list 1 $errmsg]
+ do_catchsql_test 1.2.$tn "CREATE TEMP TRIGGER tr1 $defn" [list 1 $errmsg]
+}
+
+#-------------------------------------------------------------------------
+# Test that variable references within trigger definitions loaded from
+# the sqlite_master table are automatically converted to NULL.
+#
+do_execsql_test 2.1 {
+ PRAGMA writable_schema = 1;
+ INSERT INTO sqlite_master VALUES('trigger', 'tr1', 't1', 0,
+ 'CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN
+ INSERT INTO t2 VALUES(?1, ?2);
+ END'
+ );
+
+ INSERT INTO sqlite_master VALUES('trigger', 'tr2', 't3', 0,
+ 'CREATE TRIGGER tr2 AFTER INSERT ON t3 WHEN ?1 IS NULL BEGIN
+ UPDATE t2 SET c=d WHERE c IS ?2;
+ END'
+ );
+}
+db close
+sqlite3 db test.db
+
+do_execsql_test 2.2.1 {
+ INSERT INTO t1 VALUES(1, 2);
+ SELECT * FROM t2;
+} {{} {}}
+do_test 2.2.2 {
+ set one 3
+ execsql {
+ DELETE FROM t2;
+ INSERT INTO t1 VALUES($one, ?1);
+ SELECT * FROM t2;
+ }
+} {{} {}}
+do_execsql_test 2.2.3 { SELECT * FROM t1 } {1 2 3 3}
+
+do_execsql_test 2.3 {
+ DELETE FROM t2;
+ INSERT INTO t2 VALUES('x', 'y');
+ INSERT INTO t2 VALUES(NULL, 'z');
+ INSERT INTO t3 VALUES(1, 2);
+ SELECT * FROM t3;
+ SELECT * FROM t2;
+} {1 2 x y z z}
+
+finish_test
+
+
diff --git a/test/unique.test b/test/unique.test
index eac19b5..04581c2 100644
--- a/test/unique.test
+++ b/test/unique.test
@@ -47,8 +47,8 @@ do_test unique-1.3 {
catchsql {
INSERT INTO t1(a,b,c) VALUES(1,3,4)
}
-} {1 {column a is not unique}}
-verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_UNIQUE
+} {1 {UNIQUE constraint failed: t1.a}}
+verify_ex_errcode unique-1.3b SQLITE_CONSTRAINT_PRIMARYKEY
do_test unique-1.4 {
execsql {
SELECT * FROM t1 ORDER BY a;
@@ -58,7 +58,7 @@ do_test unique-1.5 {
catchsql {
INSERT INTO t1(a,b,c) VALUES(3,2,4)
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t1.b}}
verify_ex_errcode unique-1.5b SQLITE_CONSTRAINT_UNIQUE
do_test unique-1.6 {
execsql {
@@ -100,7 +100,7 @@ do_test unique-2.3 {
catchsql {
INSERT INTO t2 VALUES(1,5);
}
-} {1 {column a is not unique}}
+} {1 {UNIQUE constraint failed: t2.a}}
verify_ex_errcode unique-2.3b SQLITE_CONSTRAINT_UNIQUE
do_test unique-2.4 {
catchsql {
@@ -127,7 +127,7 @@ do_test unique-2.8 {
catchsql {
CREATE UNIQUE INDEX i2 ON t2(a);
}
-} {1 {indexed columns are not unique}}
+} {1 {UNIQUE constraint failed: t2.a}}
verify_ex_errcode unique-2.8b SQLITE_CONSTRAINT_UNIQUE
do_test unique-2.9 {
catchsql {
@@ -166,7 +166,7 @@ do_test unique-3.4 {
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}}
+} {1 {UNIQUE constraint failed: t3.a, t3.c, t3.d}}
verify_ex_errcode unique-3.4b SQLITE_CONSTRAINT_UNIQUE
integrity_check unique-3.5
@@ -221,7 +221,7 @@ do_test unique-4.9 {
} {0 {}}
do_test unique-4.10 {
catchsql {CREATE UNIQUE INDEX i4c ON t4(b)}
-} {1 {indexed columns are not unique}}
+} {1 {UNIQUE constraint failed: t4.b}}
verify_ex_errcode unique-4.10b SQLITE_CONSTRAINT_UNIQUE
integrity_check unique-4.99
@@ -254,7 +254,7 @@ 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, sixth_column_with_long_name are not unique}}
+} {1 {UNIQUE constraint failed: t5.first_column_with_long_name, t5.second_column_with_long_name, t5.third_column_with_long_name, t5.fourth_column_with_long_name, t5.fifth_column_with_long_name, t5.sixth_column_with_long_name}}
verify_ex_errcode unique-5.2b SQLITE_CONSTRAINT_UNIQUE
diff --git a/test/unique2.test b/test/unique2.test
new file mode 100644
index 0000000..6a320d4
--- /dev/null
+++ b/test/unique2.test
@@ -0,0 +1,78 @@
+# 2014-07-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 testing the CREATE UNIQUE INDEX statement
+# to verify that ticket 9a6daf340df99ba93c53bcf8fa83d9f28040d2a8
+# has been fixed:
+#
+# drh added on 2014-07-30 12:33:04:
+#
+# The CREATE UNIQUE INDEX on the third line below does not fail even
+# though the x column values are not all unique.
+#
+# CREATE TABLE t1(x NOT NULL);
+# INSERT INTO t1 VALUES(1),(2),(2),(3);
+# CREATE UNIQUE INDEX t1x ON t1(x);
+#
+# If the index is created before the INSERT, then uniqueness is enforced
+# at the point of the INSERT. Note that the NOT NULL on the indexed column
+# seems to be required in order to exhibit this bug.
+#
+# "PRAGMA integrity_check" does not detect the resulting malformed database.
+# That might be considered a separate issue.
+#
+# Bisecting shows that this problem was introduced by the addition of
+# WITHOUT ROWID support in version 3.8.2, specifically in check-in
+# [c80e229dd9c1230] on 2013-11-07. This problem was reported on the mailing
+# list by Pavel Pimenov. and primary keys, and the UNIQUE constraint
+# on table columns
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+foreach {id sql} {
+ 1 {CREATE TABLE t1(x TEXT PRIMARY KEY, y NOT NULL) WITHOUT ROWID}
+ 2 {CREATE TABLE t1(x TEXT PRIMARY KEY, y NOT NULL)}
+ 3 {CREATE TABLE t1(x TEXT PRIMARY KEY, y) WITHOUT ROWID}
+ 4 {CREATE TABLE t1(x TEXT PRIMARY KEY, y)}
+} {
+ do_test $id.1 {
+ db eval {DROP TABLE IF EXISTS t1}
+ db eval $sql
+ db eval {INSERT INTO t1(x,y) VALUES(1,1),(2,2),(3,2),(4,3)}
+ } {}
+ do_test $id.2 {
+ catchsql {CREATE UNIQUE INDEX t1y ON t1(y)}
+ } {1 {UNIQUE constraint failed: t1.y}}
+}
+
+foreach {id sql} {
+ 5 {CREATE TABLE t1(w,x,y NOT NULL,z NOT NULL,PRIMARY KEY(w,x)) WITHOUT ROWID}
+ 6 {CREATE TABLE t1(w,x,y NOT NULL,z NOT NULL,PRIMARY KEY(w,x))}
+ 7 {CREATE TABLE t1(w,x,y NOT NULL,z,PRIMARY KEY(w,x)) WITHOUT ROWID}
+ 8 {CREATE TABLE t1(w,x,y NOT NULL,z,PRIMARY KEY(w,x))}
+ 9 {CREATE TABLE t1(w,x,y,z NOT NULL,PRIMARY KEY(w,x)) WITHOUT ROWID}
+ 10 {CREATE TABLE t1(w,x,y,z NOT NULL,PRIMARY KEY(w,x))}
+ 11 {CREATE TABLE t1(w,x,y,z,PRIMARY KEY(w,x)) WITHOUT ROWID}
+ 12 {CREATE TABLE t1(w,x,y,z,PRIMARY KEY(w,x))}
+} {
+ do_test $id.1 {
+ db eval {DROP TABLE IF EXISTS t1}
+ db eval $sql
+ db eval {INSERT INTO t1(w,x,y,z) VALUES(1,2,3,4),(2,3,3,4)}
+ } {}
+ do_test $id.2 {
+ catchsql {CREATE UNIQUE INDEX t1yz ON t1(y,z)}
+ } {1 {UNIQUE constraint failed: t1.y, t1.z}}
+}
+
+finish_test
diff --git a/test/unixexcl.test b/test/unixexcl.test
index 0147e6b..d676217 100644
--- a/test/unixexcl.test
+++ b/test/unixexcl.test
@@ -109,7 +109,7 @@ do_multiclient_test tn {
} {1 2}
do_test unixexcl-3.$tn.3 {
sql1 { PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(3, 4); }
- } {0 3 3}
+ } {0 5 5}
do_test unixexcl-3.$tn.4 {
sql2 { SELECT * FROM t1; }
} {1 2}
@@ -121,7 +121,7 @@ do_multiclient_test tn {
} {1 2 3 4}
do_test unixexcl-3.$tn.7 {
sql1 { PRAGMA wal_checkpoint; }
- } {0 4 4}
+ } {0 7 7}
}
}
diff --git a/test/unordered.test b/test/unordered.test
index 4aa8310..147e91f 100644
--- a/test/unordered.test
+++ b/test/unordered.test
@@ -40,28 +40,28 @@ foreach idxmode {ordered unordered} {
sqlite3 db test.db
foreach {tn sql r(ordered) r(unordered)} {
1 "SELECT * FROM t1 ORDER BY a"
- {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}}
- {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
- 2 "SELECT * FROM t1 WHERE a >?"
- {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?) (~32 rows)}}
- {0 0 0 {SCAN TABLE t1 (~42 rows)}}
+ {0 0 0 {SCAN TABLE t1 USING INDEX i1}}
+ {0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
+ 2 "SELECT * FROM t1 WHERE a > 100"
+ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
+ {0 0 0 {SCAN TABLE t1}}
3 "SELECT * FROM t1 WHERE a = ? ORDER BY rowid"
- {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}}
- {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}
+ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
+ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}}
4 "SELECT max(a) FROM t1"
- {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}}
- {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1 (~1 rows)}}
+ {0 0 0 {SEARCH TABLE t1 USING COVERING INDEX i1}}
+ {0 0 0 {SEARCH TABLE t1}}
5 "SELECT group_concat(b) FROM t1 GROUP BY a"
- {0 0 0 {SCAN TABLE t1 USING INDEX i1 (~128 rows)}}
- {0 0 0 {SCAN TABLE t1 (~128 rows)} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}}
+ {0 0 0 {SCAN TABLE t1 USING INDEX i1}}
+ {0 0 0 {SCAN TABLE t1} 0 0 0 {USE TEMP B-TREE FOR GROUP BY}}
6 "SELECT * FROM t1 WHERE a = ?"
- {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}}
- {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?) (~1 rows)}}
+ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
+ {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
7 "SELECT count(*) FROM t1"
- {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1(~128 rows)}}
- {0 0 0 {SCAN TABLE t1 (~128 rows)}}
+ {0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}}
+ {0 0 0 {SCAN TABLE t1}}
} {
do_eqp_test 1.$idxmode.$tn $sql $r($idxmode)
}
diff --git a/test/update.test b/test/update.test
index a6fef7d..e67b0ef 100644
--- a/test/update.test
+++ b/test/update.test
@@ -452,7 +452,7 @@ do_test update-10.3 {
UPDATE t1 SET a=1, e=10 WHERE f=7;
SELECT * FROM t1;
}
-} {1 {PRIMARY KEY must be unique}}
+} {1 {UNIQUE constraint failed: t1.a}}
do_test update-10.4 {
catchsql {
SELECT * FROM t1;
@@ -469,7 +469,7 @@ do_test update-10.6 {
UPDATE t1 SET b=2, e=12 WHERE f=7;
SELECT * FROM t1;
}
-} {1 {column b is not unique}}
+} {1 {UNIQUE constraint failed: t1.b}}
do_test update-10.7 {
catchsql {
SELECT * FROM t1;
@@ -486,7 +486,7 @@ do_test update-10.9 {
UPDATE t1 SET c=3, d=4, e=14 WHERE f=7;
SELECT * FROM t1;
}
-} {1 {columns c, d are not unique}}
+} {1 {UNIQUE constraint failed: t1.c, t1.d}}
do_test update-10.10 {
catchsql {
SELECT * FROM t1;
diff --git a/test/uri.test b/test/uri.test
index af1ad67..dd78783 100644
--- a/test/uri.test
+++ b/test/uri.test
@@ -238,12 +238,14 @@ ifcapable wal {
tvfs1 script tvfs1_callback
proc tvfs1_callback {method filename args} {
set ::T1([file tail $filename]) 1
+ return SQLITE_OK
}
testvfs tvfs2
tvfs2 filter {xOpen xDelete xAccess xFullPathname}
tvfs2 script tvfs2_callback
proc tvfs2_callback {method filename args} {
set ::T2([file tail $filename]) 1
+ return SQLITE_OK
}
catch {db close}
diff --git a/test/veryquick.test b/test/veryquick.test
index ca82b22..c8d6ce8 100644
--- a/test/veryquick.test
+++ b/test/veryquick.test
@@ -16,4 +16,3 @@ source $testdir/permutations.test
run_test_suite veryquick
finish_test
-
diff --git a/test/view.test b/test/view.test
index 779f77b..3ba6c0b 100644
--- a/test/view.test
+++ b/test/view.test
@@ -611,4 +611,17 @@ ifcapable progress {
} {1 interrupted}
}
+db close
+sqlite3 db :memory:
+do_execsql_test view-22.1 {
+ CREATE VIEW x1 AS SELECT 123 AS '', 234 AS '', 345 AS '';
+ SELECT * FROM x1;
+} {123 234 345}
+do_test view-22.2 {
+ unset -nocomplain x
+ db eval {SELECT * FROM x1} x break
+ lsort [array names x]
+} {{} * :1 :2}
+
+
finish_test
diff --git a/test/vtab1.test b/test/vtab1.test
index 1f17e53..0542ee6 100644
--- a/test/vtab1.test
+++ b/test/vtab1.test
@@ -618,8 +618,9 @@ do_test vtab1-5-6 {
do_test vtab1-5-7 {
filter $::echo_module
} [list \
- xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \
xFilter {SELECT rowid, * FROM 't1'} \
+ xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \
+ xFilter {SELECT rowid, * FROM 't2' WHERE d = ?} \
]
execsql {
@@ -1039,10 +1040,10 @@ do_test vtab1.12-1 {
# 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}}
+} {1 {echo-vtab-error: UNIQUE constraint failed: c.a}}
do_test vtab1.12-2.1 {
sqlite3_errmsg db
-} {echo-vtab-error: column a is not unique}
+} {echo-vtab-error: UNIQUE constraint failed: c.a}
do_test vtab1.12-3 {
execsql { SELECT * FROM c }
} {3 G H}
@@ -1051,7 +1052,7 @@ do_test vtab1.12-3 {
do_test vtab1.12-4 {
execsql {BEGIN}
catchsql { INSERT INTO echo_c SELECT * FROM b; }
-} {1 {echo-vtab-error: column a is not unique}}
+} {1 {echo-vtab-error: UNIQUE constraint failed: c.a}}
do_test vtab1.12-5 {
execsql { SELECT * FROM c }
} {3 G H}
@@ -1133,12 +1134,12 @@ do_test vtab1-14.015 {
-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
-} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c' WHERE rowid = .} 1/}
+#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
+#} {/.*xBestIndex {SELECT rowid, . FROM 'c' WHERE rowid = .} xFilter {SELECT rowid, . FROM 'c'} 1/}
do_test vtab1-14.2 {
set echo_module ""
@@ -1152,11 +1153,11 @@ do_test vtab1-14.3 {
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
-} {/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
+#} {/xBestIndex {SELECT rowid, . FROM 'c' WHERE a = .} xFilter {SELECT rowid, . FROM 'c' WHERE a = .} 1/}
do_test vtab1-15.1 {
execsql {
@@ -1375,4 +1376,23 @@ do_execsql_test 20.4 {
ORDER BY 1, 2;
} {5 5 6 6 11 11 12 12}
+#-------------------------------------------------------------------------
+#
+do_execsql_test 21.1 {
+ CREATE TABLE t9(a,b,c);
+ CREATE VIRTUAL TABLE t9v USING echo(t9);
+
+ INSERT INTO t9 VALUES(1,2,3);
+ INSERT INTO t9 VALUES(3,2,1);
+ INSERT INTO t9 VALUES(2,2,2);
+}
+
+do_execsql_test 21.2 {
+ SELECT * FROM t9v WHERE a<b;
+} {1 2 3}
+
+do_execsql_test 21.3 {
+ SELECT * FROM t9v WHERE a=b;
+} {2 2 2}
+
finish_test
diff --git a/test/vtab6.test b/test/vtab6.test
index 96e45bf..10bf286 100644
--- a/test/vtab6.test
+++ b/test/vtab6.test
@@ -561,12 +561,12 @@ do_test vtab6-11.4.1 {
catchsql {
SELECT a, b, c FROM ab NATURAL JOIN bc;
}
-} {1 {table bc: xBestIndex returned an invalid plan}}
+} {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}}
+} {1 {table bc: xBestIndex returned an invalid plan}}
unset ::echo_module_ignore_usable
diff --git a/test/vtab_shared.test b/test/vtab_shared.test
index 6a76e27..3473992 100644
--- a/test/vtab_shared.test
+++ b/test/vtab_shared.test
@@ -15,6 +15,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix vtab_shared
ifcapable !vtab||!shared_cache {
finish_test
@@ -116,7 +117,6 @@ do_test vtab_shared-1.10 {
} {1 {database table is locked: sqlite_master}}
do_test vtab_shared-1.11 {
-breakpoint
execsql {
CREATE VIRTUAL TABLE t2 USING echo(t0);
CREATE VIRTUAL TABLE t3 USING echo(t0);
@@ -229,5 +229,51 @@ do_test vtab_shared_1.15.3 {
db close
db2 close
+
+#---------------------------------------------------------------
+# Test calling sqlite3_close() with vtabs on the disconnect list.
+#
+ifcapable rtree {
+ reset_db
+ do_test 2.1.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+
+ # Create a virtual table using [db].
+ execsql {
+ CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
+ INSERT INTO rt VALUES(1, 2 ,3);
+ SELECT * FROM rt;
+ }
+
+ # Drop the virtual table using [db2]. The sqlite3_vtab object belonging
+ # to [db] is moved to the sqlite3.pDisconnect list.
+ execsql { DROP TABLE rt } db2
+
+ # Immediately close [db]. At one point this would fail due to the
+ # unfinalized statements held by the un-xDisconnect()ed sqlite3_vtab.
+ db close
+ } {}
+ db2 close
+}
+
+ifcapable fts3 {
+ # Same test as above, except using fts3 instead of rtree.
+ reset_db
+ do_test 2.2.1 {
+ sqlite3 db test.db
+ sqlite3 db2 test.db
+ execsql {
+ CREATE VIRTUAL TABLE ft USING fts3;
+ INSERT INTO ft VALUES('hello world');
+ SELECT * FROM ft;
+ }
+ execsql { DROP TABLE ft } db2
+ db close
+ } {}
+ db2 close
+}
+
sqlite3_enable_shared_cache 0
finish_test
+
diff --git a/test/wal.test b/test/wal.test
index c8078a1..675be73 100644
--- a/test/wal.test
+++ b/test/wal.test
@@ -322,7 +322,7 @@ do_test wal-5.4 {
INSERT INTO t3 VALUES('abc');
}
catchsql { INSERT INTO t3 VALUES('abc') }
-} {1 {column x is not unique}}
+} {1 {UNIQUE constraint failed: t3.x}}
do_test wal-5.5 {
execsql {
COMMIT;
@@ -852,7 +852,6 @@ do_test wal-13.1.2 {
sqlite3 db test.db
execsql { SELECT * FROM t2 }
} {B 2}
-breakpoint
do_test wal-13.1.3 {
db close
file exists test.db-wal
@@ -1575,4 +1574,17 @@ sqlite3_shutdown
test_sqlite3_log
sqlite3_initialize
+# Make sure PRAGMA journal_mode=WAL works with ATTACHED databases in
+# all journal modes.
+#
+foreach mode {OFF MEMORY PERSIST DELETE TRUNCATE WAL} {
+ delete_file test.db test2.db
+ sqlite3 db test.db
+ do_test wal-25.$mode {
+ db eval "PRAGMA journal_mode=$mode"
+ db eval {ATTACH 'test2.db' AS t2; PRAGMA journal_mode=WAL;}
+ } {wal}
+ db close
+}
+
finish_test
diff --git a/test/wal2.test b/test/wal2.test
index 4371e98..9d45444 100644
--- a/test/wal2.test
+++ b/test/wal2.test
@@ -811,7 +811,13 @@ do_test wal2-7.1.1 {
do_test wal2-7.1.2 {
forcecopy test.db test2.db
forcecopy test.db-wal test2.db-wal
- hexio_write test2.db-wal 48 FF
+ # The first 32 bytes of the WAL file contain the WAL header. Offset 48
+ # is the first byte of the checksum for the first frame in the WAL.
+ # The following three lines replaces the contents of that byte with
+ # a different value.
+ set newval FF
+ if {$newval == [hexio_read test2.db-wal 48 1]} { set newval 00 }
+ hexio_write test2.db-wal 48 $newval
} {1}
do_test wal2-7.1.3 {
sqlite3 db2 test2.db
@@ -1279,7 +1285,6 @@ foreach {tn settings restart_sync commit_sync ckpt_sync} {
PRAGMA synchronous = [lindex $settings 2];
" {0 wal}
-if { $tn==2} breakpoint
do_test 15.$tn.2 {
set sync(normal) 0
set sync(full) 0
diff --git a/test/wal6.test b/test/wal6.test
index ec31bb8..2498907 100644
--- a/test/wal6.test
+++ b/test/wal6.test
@@ -14,6 +14,7 @@
#
set testdir [file dirname $argv0]
+set testprefix wal6
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
@@ -29,18 +30,18 @@ forcedelete test.db
set all_journal_modes {delete persist truncate memory off}
foreach jmode $all_journal_modes {
- do_test wal6-1.0.$jmode {
+ do_test wal6-1.0.$jmode {
sqlite3 db test.db
execsql "PRAGMA journal_mode = $jmode;"
- } $jmode
+ } $jmode
- do_test wal6-1.1.$jmode {
- execsql {
- CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
- INSERT INTO t1 VALUES(1,2);
- SELECT * FROM t1;
- }
- } {1 2}
+ do_test wal6-1.1.$jmode {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1,2);
+ SELECT * FROM t1;
+ }
+ } {1 2}
# Under Windows, you'll get an error trying to delete
# a file this is already opened. Close the first connection
@@ -51,31 +52,146 @@ if {$tcl_platform(platform)=="windows"} {
}
}
- do_test wal6-1.2.$jmode {
- sqlite3 db2 test.db
- execsql {
- PRAGMA journal_mode=WAL;
- INSERT INTO t1 VALUES(3,4);
- SELECT * FROM t1 ORDER BY a;
- } db2
- } {wal 1 2 3 4}
+ do_test wal6-1.2.$jmode {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA journal_mode=WAL;
+ INSERT INTO t1 VALUES(3,4);
+ SELECT * FROM t1 ORDER BY a;
+ } db2
+ } {wal 1 2 3 4}
if {$tcl_platform(platform)=="windows"} {
if {$jmode=="persist" || $jmode=="truncate"} {
- sqlite3 db test.db
+ sqlite3 db test.db
}
}
- do_test wal6-1.3.$jmode {
- execsql {
- SELECT * FROM t1 ORDER BY a;
- }
- } {1 2 3 4}
+ do_test wal6-1.3.$jmode {
+ execsql {
+ SELECT * FROM t1 ORDER BY a;
+ }
+ } {1 2 3 4}
- db close
- db2 close
+ db close
+ db2 close
forcedelete test.db
}
+#-------------------------------------------------------------------------
+# Test that SQLITE_BUSY_SNAPSHOT is returned as expected.
+#
+reset_db
+sqlite3 db2 test.db
+
+do_execsql_test 2.1 {
+ PRAGMA journal_mode = WAL;
+ CREATE TABLE t1(a PRIMARY KEY, b TEXT);
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ BEGIN;
+ SELECT * FROM t1;
+} {wal 1 one 2 two}
+
+do_test 2.2 {
+ execsql {
+ SELECT * FROM t1;
+ INSERT INTO t1 VALUES(3, 'three');
+ } db2
+} {1 one 2 two}
+
+do_catchsql_test 2.3 {
+ INSERT INTO t1 VALUES('x', 'x')
+} {1 {database is locked}}
+
+do_test 2.4 {
+ list [sqlite3_errcode db] [sqlite3_extended_errcode db]
+} {SQLITE_BUSY SQLITE_BUSY_SNAPSHOT}
+
+do_execsql_test 2.5 {
+ SELECT * FROM t1;
+ COMMIT;
+ INSERT INTO t1 VALUES('x', 'x')
+} {1 one 2 two}
+
+proc test3 {prefix} {
+ do_test $prefix.1 {
+ execsql { SELECT count(*) FROM t1 }
+ } {0}
+ do_test $prefix.2 {
+ execsql { INSERT INTO t1 VALUES('x', 'x') } db2
+ } {}
+ do_test $prefix.3 {
+ execsql { INSERT INTO t1 VALUES('y', 'y') }
+ } {}
+ do_test $prefix.4 {
+ execsql { SELECT count(*) FROM t1 }
+ } {2}
+}
+
+do_execsql_test 2.6.1 { DELETE FROM t1 }
+test3 2.6.2
+
+db func test3 test3
+do_execsql_test 2.6.3 { DELETE FROM t1 }
+db eval {SELECT test3('2.6.4')}
+
+do_test 2.x {
+ db2 close
+} {}
+
+#-------------------------------------------------------------------------
+# Check that if BEGIN IMMEDIATE fails, it does not leave the user with
+# an open read-transaction (unless one was already open before the BEGIN
+# IMMEDIATE). Even if there are other active VMs.
+#
+
+proc test4 {prefix} {
+ do_test $prefix.1 {
+ catchsql { BEGIN IMMEDIATE }
+ } {1 {database is locked}}
+
+ do_test $prefix.2 {
+ execsql { COMMIT } db2
+ } {}
+
+ do_test $prefix.3 {
+ execsql { BEGIN IMMEDIATE }
+ } {}
+ do_test $prefix.4 {
+ execsql { COMMIT }
+ } {}
+}
+
+reset_db
+sqlite3 db2 test.db
+do_execsql_test 3.1 {
+ PRAGMA journal_mode = WAL;
+ CREATE TABLE ab(a PRIMARY KEY, b);
+} {wal}
+
+do_test 3.2.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO ab VALUES(1, 2);
+ } db2
+} {}
+test4 3.2.2
+
+db func test4 test4
+do_test 3.3.1 {
+ execsql {
+ BEGIN;
+ INSERT INTO ab VALUES(3, 4);
+ } db2
+} {}
+
+db eval {SELECT test4('3.3.2')}
+
+do_test 3.x {
+ db2 close
+} {}
+
finish_test
+
diff --git a/test/wal64k.test b/test/wal64k.test
new file mode 100644
index 0000000..e962da1
--- /dev/null
+++ b/test/wal64k.test
@@ -0,0 +1,51 @@
+# 2010 April 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 operation of the library in
+# "PRAGMA journal_mode=WAL" mode.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix wal64k
+
+ifcapable !wal {finish_test ; return }
+
+if {$tcl_platform(platform) != "unix"} {
+ finish_test
+ return
+}
+
+db close
+test_syscall pagesize 65536
+sqlite3 db test.db
+
+do_execsql_test 1.0 {
+ PRAGMA journal_mode = WAL;
+ CREATE TABLE t1(x);
+ CREATE INDEX i1 ON t1(x);
+} {wal}
+do_test 1.1 { file size test.db-shm } {65536}
+
+do_test 1.2 {
+ execsql BEGIN
+ while {[file size test.db-shm]==65536} {
+ execsql { INSERT INTO t1 VALUES( randstr(900,1100) ) }
+ }
+ execsql COMMIT
+ file size test.db-shm
+} {131072}
+
+integrity_check 1.3
+
+db close
+test_syscall pagesize -1
+finish_test
diff --git a/test/wal8.test b/test/wal8.test
index 3399538..0682fce 100644
--- a/test/wal8.test
+++ b/test/wal8.test
@@ -88,4 +88,3 @@ do_execsql_test 3.1 {
} {t1}
finish_test
-
diff --git a/test/walcksum.test b/test/walcksum.test
index 08278dd..3005a75 100644
--- a/test/walcksum.test
+++ b/test/walcksum.test
@@ -390,4 +390,3 @@ foreach incr {1 2 3 20 40 60 80 100 120 140 160 180 200 220 240 253 254 255} {
}
finish_test
-
diff --git a/test/walcrash.test b/test/walcrash.test
index adc4841..2a64718 100644
--- a/test/walcrash.test
+++ b/test/walcrash.test
@@ -293,4 +293,3 @@ for {set i 1} {$i < $REPEATS} {incr i} {
}
finish_test
-
diff --git a/test/walcrash2.test b/test/walcrash2.test
index 7116281..9c93bcd 100644
--- a/test/walcrash2.test
+++ b/test/walcrash2.test
@@ -96,4 +96,3 @@ do_test walcrash2-1.3 {
catch { db2 close }
finish_test
-
diff --git a/test/walcrash3.test b/test/walcrash3.test
index c2c9a6d..71dfb45 100644
--- a/test/walcrash3.test
+++ b/test/walcrash3.test
@@ -126,4 +126,3 @@ for {set i 2} {$i<10000 && [set_test_counter errors]==$nInitialErr} {incr i} {
}
finish_test
-
diff --git a/test/walfault.test b/test/walfault.test
index 4a9d98a..4e7064d 100644
--- a/test/walfault.test
+++ b/test/walfault.test
@@ -567,7 +567,6 @@ do_test walfault-14-pre {
} {}
do_faultsim_test walfault-14 -prep {
faultsim_restore_and_reopen
- breakpoint
execsql {
SELECT count(*) FROM abc;
PRAGMA locking_mode = exclusive;
diff --git a/test/walro.test b/test/walro.test
index 465ce83..6d920b1 100644
--- a/test/walro.test
+++ b/test/walro.test
@@ -32,9 +32,6 @@ ifcapable !wal {
}
do_multiclient_test tn {
- # Do not run tests with the connections in the same process.
- #
- if {$tn==2} continue
# Close all connections and delete the database.
#
@@ -43,6 +40,10 @@ do_multiclient_test tn {
code3 { db3 close }
forcedelete test.db
forcedelete walro
+
+ # Do not run tests with the connections in the same process.
+ #
+ if {$tn==2} continue
foreach c {code1 code2 code3} {
$c {
@@ -232,9 +233,6 @@ forcedelete test.db
# database file while a checkpoint operation is ongoing.
#
do_multiclient_test tn {
- # Do not run tests with the connections in the same process.
- #
- if {$tn==2} continue
# Close all connections and delete the database.
#
@@ -243,6 +241,10 @@ do_multiclient_test tn {
code3 { db3 close }
forcedelete test.db
forcedelete walro
+
+ # Do not run tests with the connections in the same process.
+ #
+ if {$tn==2} continue
foreach c {code1 code2 code3} {
$c {
@@ -291,5 +293,3 @@ do_multiclient_test tn {
}
finish_test
-
-
diff --git a/test/walshared.test b/test/walshared.test
index 73a3fb8..fbbdeb4 100644
--- a/test/walshared.test
+++ b/test/walshared.test
@@ -60,4 +60,3 @@ do_test walshared-1.4 {
sqlite3_enable_shared_cache $::enable_shared_cache
finish_test
-
diff --git a/test/where.test b/test/where.test
index 2dbc283..f560708 100644
--- a/test/where.test
+++ b/test/where.test
@@ -65,9 +65,9 @@ proc count sql {
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_eqp_test where-1.1.2 {
+ SELECT x, y, w FROM t1 WHERE w=10
+} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*}
do_test where-1.1.3 {
db status step
} {0}
@@ -77,15 +77,15 @@ do_test where-1.1.4 {
do_test where-1.1.5 {
db status step
} {99}
-do_test where-1.1.6 {
- set sqlite_query_plan
-} {t1 {}}
+do_eqp_test where-1.1.6 {
+ SELECT x, y, w FROM t1 WHERE +w=10
+} {*SCAN TABLE t1*}
do_test where-1.1.7 {
count {SELECT x, y, w AS abc FROM t1 WHERE abc=10}
} {3 121 10 3}
-do_test where-1.1.8 {
- set sqlite_query_plan
-} {t1 i1w}
+do_eqp_test where-1.1.8 {
+ SELECT x, y, w AS abc FROM t1 WHERE abc=10
+} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*}
do_test where-1.1.9 {
db status step
} {0}
@@ -104,21 +104,21 @@ do_test where-1.3.2 {
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_eqp_test where-1.4.2 {
+ SELECT w, x, y FROM t1 WHERE 11=w AND x>2
+} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*}
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_eqp_test where-1.4.4 {
+ SELECT w AS a, x AS b, y FROM t1 WHERE 11=a AND b>2
+} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*}
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_eqp_test where-1.5.2 {
+ SELECT x, y FROM t1 WHERE y<200 AND w=11 AND x>2
+} {*SEARCH TABLE t1 USING INDEX i1w (w=?)*}
do_test where-1.6 {
count {SELECT x, y FROM t1 WHERE y<200 AND x>2 AND w=11}
} {3 144 3}
@@ -128,13 +128,12 @@ do_test where-1.7 {
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_eqp_test where-1.8.2 {
+ SELECT x, y FROM t1 WHERE w>10 AND y=144 AND x=3
+} {*SEARCH TABLE t1 USING INDEX i1xy (x=? AND y=?)*}
+do_eqp_test where-1.8.3 {
+ SELECT x, y FROM t1 WHERE y=144 AND x=3
+} {*SEARCH TABLE t1 USING COVERING INDEX i1xy (x=? AND y=?)*}
do_test where-1.9 {
count {SELECT x, y FROM t1 WHERE y=144 AND w>10 AND x=3}
} {3 144 3}
@@ -238,10 +237,10 @@ do_test where-1.34 {
} {97 99}
do_test where-1.35 {
count {SELECT w FROM t1 WHERE w<3}
-} {1 2 2}
+} {1 2 3}
do_test where-1.36 {
count {SELECT w FROM t1 WHERE w<=3}
-} {1 2 3 3}
+} {1 2 3 4}
do_test where-1.37 {
count {SELECT w FROM t1 WHERE w+1<=4 ORDER BY w}
} {1 2 3 99}
@@ -605,7 +604,7 @@ 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}
+} {1 100 4 nosort}
do_test where-6.9.8 {
cksort {
SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c ASC LIMIT 3
@@ -1126,9 +1125,9 @@ do_test where-13.12 {
if {[permutation] != "no_optimization"} {
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');
+ CREATE TABLE t8(a INTEGER PRIMARY KEY, b TEXT UNIQUE, c CHAR(100));
+ INSERT INTO t8(a,b) VALUES(1,'one');
+ INSERT INTO t8(a,b) VALUES(4,'four');
}
cksort {
SELECT x.a || '/' || y.a FROM t8 x, t8 y ORDER BY x.a, y.b
@@ -1305,4 +1304,33 @@ do_test where-17.5 {
}
} {42 1 43 1}
+# Ticket [be84e357c035d068135f20bcfe82761bbf95006b] 2013-09-03
+# Segfault during query involving LEFT JOIN column in the ORDER BY clause.
+#
+do_execsql_test where-18.1 {
+ CREATE TABLE t181(a);
+ CREATE TABLE t182(b,c);
+ INSERT INTO t181 VALUES(1);
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL;
+} {1}
+do_execsql_test where-18.2 {
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c;
+} {1}
+do_execsql_test where-18.3 {
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c;
+} {1}
+do_execsql_test where-18.4 {
+ INSERT INTO t181 VALUES(1),(1),(1),(1);
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +c;
+} {1}
+do_execsql_test where-18.5 {
+ INSERT INTO t181 VALUES(2);
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY c IS NULL, +a;
+} {1 2}
+do_execsql_test where-18.6 {
+ INSERT INTO t181 VALUES(2);
+ SELECT DISTINCT a FROM t181 LEFT JOIN t182 ON a=b ORDER BY +a, +c IS NULL;
+} {1 2}
+
+
finish_test
diff --git a/test/where2.test b/test/where2.test
index e8c2f36..367eb0d 100644
--- a/test/where2.test
+++ b/test/where2.test
@@ -66,14 +66,24 @@ proc cksort {sql} {
# 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.
+# it appends the name of the table and index used.
#
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]
+ set eqp [execsql "EXPLAIN QUERY PLAN $sql"]
+ # puts eqp=$eqp
+ foreach {a b c x} $eqp {
+ if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \
+ $x all as tab idx]} {
+ lappend data $tab $idx
+ } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} {
+ lappend data $tab *
+ }
+ }
+ return $data
}
@@ -111,6 +121,42 @@ do_test where2-2.3 {
}
} {85 6 7396 7402 nosort t1 *}
+# Ticket [65bdeb9739605cc22966f49208452996ff29a640] 2014-02-26
+# Make sure "ORDER BY random" does not gets optimized out.
+#
+do_test where2-2.4 {
+ db eval {
+ CREATE TABLE x1(a INTEGER PRIMARY KEY, b DEFAULT 1);
+ WITH RECURSIVE
+ cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<50)
+ INSERT INTO x1 SELECT x, 1 FROM cnt;
+ CREATE TABLE x2(x INTEGER PRIMARY KEY);
+ INSERT INTO x2 VALUES(1);
+ }
+ set sql {SELECT * FROM x1, x2 WHERE x=1 ORDER BY random()}
+ set out1 [db eval $sql]
+ set out2 [db eval $sql]
+ set out3 [db eval $sql]
+ expr {$out1!=$out2 && $out2!=$out3}
+} {1}
+do_execsql_test where2-2.5 {
+ -- random() is not optimized out
+ EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY random();
+} {/ random/}
+do_execsql_test where2-2.5b {
+ -- random() is not optimized out
+ EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY random();
+} {/ SorterOpen /}
+do_execsql_test where2-2.6 {
+ -- other constant functions are optimized out
+ EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY abs(5);
+} {~/ abs/}
+do_execsql_test where2-2.6b {
+ -- other constant functions are optimized out
+ EXPLAIN SELECT * FROM x1, x2 WHERE x=1 ORDER BY abs(5);
+} {~/ SorterOpen /}
+
+
# Efficient handling of forward and reverse table scans.
#
@@ -273,12 +319,12 @@ 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 {}}
+} {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 {}}
+} {6 2 49 51 99 6 10000 10006 100 6 10201 10207 sort t1 *}
set ::idx {}
ifcapable subquery {set ::idx i1zyx}
@@ -297,14 +343,16 @@ do_test where2-6.6 {
}
} [list 1 0 4 4 2 1 9 10 sort a i1w b $::idx]
+if {[permutation] != "no_optimization"} {
+
# 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 t2249a(a TEXT UNIQUE, x CHAR(100));
CREATE TABLE t2249b(b INTEGER);
- INSERT INTO t2249a VALUES('0123');
+ INSERT INTO t2249a(a) VALUES('0123');
INSERT INTO t2249b VALUES(123);
}
queryplan {
@@ -312,56 +360,56 @@ do_test where2-6.7 {
-- will attempt to convert to NUMERIC before the comparison.
-- They will thus compare equal.
--
- SELECT * FROM t2249b CROSS JOIN t2249a WHERE a=b;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=b;
}
-} {123 0123 nosort t2249b {} t2249a {}}
+} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
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;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=+b;
}
-} {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+} {nosort t2249b * t2249a 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
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE +b=a
}
-} {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+} {nosort t2249b * t2249a 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;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE +a=+b;
}
-} {nosort t2249b {} t2249a {}}
+} {nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
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';
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=b OR a='hello';
}
-} {123 0123 nosort t2249b {} t2249a {}}
+} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
do_test where2-6.11.2 {
# Permutations of the expression terms.
queryplan {
- SELECT * FROM t2249b CROSS JOIN t2249a WHERE b=a OR a='hello';
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE b=a OR a='hello';
}
-} {123 0123 nosort t2249b {} t2249a {}}
+} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
do_test where2-6.11.3 {
# Permutations of the expression terms.
queryplan {
- SELECT * FROM t2249b CROSS JOIN t2249a WHERE 'hello'=a OR b=a;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE 'hello'=a OR b=a;
}
-} {123 0123 nosort t2249b {} t2249a {}}
+} {123 0123 nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
do_test where2-6.11.4 {
# Permutations of the expression terms.
queryplan {
- SELECT * FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR b=a;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR b=a;
}
-} {123 0123 nosort t2249b {} t2249a {}}
+} {123 0123 nosort t2249b * t2249a 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
@@ -373,41 +421,41 @@ ifcapable explain&&subquery {
# 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';
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=+b OR a='hello';
}
- } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ } {nosort t2249b * t2249a 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;
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a='hello' OR +b=a;
}
- } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ } {nosort t2249b * t2249a 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';
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE +b=a OR a='hello';
}
- } {nosort t2249b {} {} sqlite_autoindex_t2249a_1}
+ } {nosort t2249b * t2249a 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';
+ SELECT b,a FROM t2249b CROSS JOIN t2249a WHERE a=+b OR +a='hello';
}
- } {nosort t2249b {} t2249a {}}
+ } {nosort t2249b * t2249a sqlite_autoindex_t2249a_1}
}
# 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
+ SELECT x.a, y.a FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a
}
-} {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+} {0123 0123 nosort x sqlite_autoindex_t2249a_1 y 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
@@ -416,19 +464,22 @@ ifcapable explain&&subquery {
#
do_test where2-6.21 {
queryplan {
- SELECT * FROM t2249a x CROSS JOIN t2249a y WHERE x.a=y.a OR y.a='hello'
+ SELECT x.a,y.a FROM t2249a x CROSS JOIN t2249a y
+ WHERE x.a=y.a OR y.a='hello'
}
- } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y 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'
+ SELECT x.a,y.a FROM t2249a x CROSS JOIN t2249a y
+ WHERE y.a=x.a OR y.a='hello'
}
- } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y 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
+ SELECT x.a,y.a FROM t2249a x CROSS JOIN t2249a y
+ WHERE y.a='hello' OR x.a=y.a
}
- } {0123 0123 nosort x {} {} sqlite_autoindex_t2249a_1}
+ } {0123 0123 nosort x sqlite_autoindex_t2249a_1 y sqlite_autoindex_t2249a_1}
}
# Unique queries (queries that are guaranteed to return only a single
@@ -464,6 +515,8 @@ do_test where2-7.4 {
}
} {1 2 3 2 3 nosort}
+} ;# if {[permutation] != "no_optimization"}
+
# Ticket #1807. Using IN constrains on multiple columns of
# a multi-column index.
#
@@ -685,5 +738,26 @@ do_test where2-11.4 {
}
} {4 8 10}
+# Verify that the OR clause is used in an outer loop even when
+# the OR clause scores slightly better on an inner loop.
+if {[permutation] != "no_optimization"} {
+do_execsql_test where2-12.1 {
+ CREATE TABLE t12(x INTEGER PRIMARY KEY, y INT, z CHAR(100));
+ CREATE INDEX t12y ON t12(y);
+ EXPLAIN QUERY PLAN
+ SELECT a.x, b.x
+ FROM t12 AS a JOIN t12 AS b ON a.y=b.x
+ WHERE (b.x=$abc OR b.y=$abc);
+} {/.*SEARCH TABLE t12 AS b .*SEARCH TABLE t12 AS b .*/}
+}
+
+# Verify that all necessary OP_OpenRead opcodes occur in the OR optimization.
+#
+do_execsql_test where2-13.1 {
+ CREATE TABLE t13(a,b);
+ CREATE INDEX t13a ON t13(a);
+ INSERT INTO t13 VALUES(4,5);
+ SELECT * FROM t13 WHERE (1=2 AND a=3) OR a=4;
+} {4 5}
finish_test
diff --git a/test/where3.test b/test/where3.test
index e08f905..c2804b5 100644
--- a/test/where3.test
+++ b/test/where3.test
@@ -103,12 +103,22 @@ ifcapable explain {
}
# This procedure executes the SQL. Then it appends
-# the ::sqlite_query_plan variable.
+# the names of the table and index used
#
proc queryplan {sql} {
set ::sqlite_sort_count 0
set data [execsql $sql]
- return [concat $data $::sqlite_query_plan]
+ set eqp [execsql "EXPLAIN QUERY PLAN $sql"]
+ # puts eqp=$eqp
+ foreach {a b c x} $eqp {
+ if {[regexp { TABLE (\w+ AS )?(\w+) USING.* INDEX (\w+)\y} \
+ $x all as tab idx]} {
+ lappend data $tab $idx
+ } elseif {[regexp { TABLE (\w+ AS )?(\w+)\y} $x all as tab]} {
+ lappend data $tab *
+ }
+ }
+ return $data
}
@@ -144,73 +154,73 @@ do_test where3-2.1 {
SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
WHERE cpk=bx AND bpk=ax
}
-} {tA {} tB * tC * tD *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {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 *}
+} {tA * tC * tB * tD *}
do_test where3-2.6 {
queryplan {
SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
WHERE bpk=cx AND apk=bx
}
-} {tC {} tB * tA * tD *}
+} {tC * tB * tA * tD *}
do_test where3-2.7 {
queryplan {
SELECT * FROM tA, tB, tC LEFT JOIN tD ON dpk=cx
WHERE cpk=bx AND apk=cx
}
-} {tB {} tC * tA * tD *}
+} {tB * tC * tA * tD *}
# Ticket [13f033c865f878953]
# If the outer loop must be a full table scan, do not let ANALYZE trick
@@ -221,22 +231,30 @@ do_execsql_test where3-3.0 {
CREATE TABLE t301(a INTEGER PRIMARY KEY,b,c);
CREATE INDEX t301c ON t301(c);
INSERT INTO t301 VALUES(1,2,3);
+ INSERT INTO t301 VALUES(2,2,3);
CREATE TABLE t302(x, y);
INSERT INTO t302 VALUES(4,5);
ANALYZE;
explain query plan SELECT * FROM t302, t301 WHERE t302.x=5 AND t301.a=t302.y;
} {
- 0 0 0 {SCAN TABLE t302 (~1 rows)}
- 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SCAN TABLE t302}
+ 0 1 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)}
}
do_execsql_test where3-3.1 {
explain query plan
SELECT * FROM t301, t302 WHERE t302.x=5 AND t301.a=t302.y;
} {
- 0 0 1 {SCAN TABLE t302 (~1 rows)}
- 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 1 {SCAN TABLE t302}
+ 0 1 0 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)}
}
+do_execsql_test where3-3.2 {
+ SELECT * FROM t301 WHERE c=3 AND a IS NULL;
+} {}
+do_execsql_test where3-3.3 {
+ SELECT * FROM t301 WHERE c=3 AND a IS NOT NULL;
+} {1 2 3 2 2 3}
+if 0 { # Query planner no longer does this
# Verify that when there are multiple tables in a join which must be
# full table scans that the query planner attempts put the table with
# the fewest number of output rows as the outer loop.
@@ -248,26 +266,27 @@ do_execsql_test where3-4.0 {
EXPLAIN QUERY PLAN
SELECT * FROM t400, t401, t402 WHERE t402.z GLOB 'abc*';
} {
- 0 0 2 {SCAN TABLE t402 (~500000 rows)}
- 0 1 0 {SCAN TABLE t400 (~1000000 rows)}
- 0 2 1 {SCAN TABLE t401 (~1000000 rows)}
+ 0 0 2 {SCAN TABLE t402}
+ 0 1 0 {SCAN TABLE t400}
+ 0 2 1 {SCAN TABLE t401}
}
do_execsql_test where3-4.1 {
EXPLAIN QUERY PLAN
SELECT * FROM t400, t401, t402 WHERE t401.r GLOB 'abc*';
} {
- 0 0 1 {SCAN TABLE t401 (~500000 rows)}
- 0 1 0 {SCAN TABLE t400 (~1000000 rows)}
- 0 2 2 {SCAN TABLE t402 (~1000000 rows)}
+ 0 0 1 {SCAN TABLE t401}
+ 0 1 0 {SCAN TABLE t400}
+ 0 2 2 {SCAN TABLE t402}
}
do_execsql_test where3-4.2 {
EXPLAIN QUERY PLAN
SELECT * FROM t400, t401, t402 WHERE t400.c GLOB 'abc*';
} {
- 0 0 0 {SCAN TABLE t400 (~500000 rows)}
- 0 1 1 {SCAN TABLE t401 (~1000000 rows)}
- 0 2 2 {SCAN TABLE t402 (~1000000 rows)}
+ 0 0 0 {SCAN TABLE t400}
+ 0 1 1 {SCAN TABLE t401}
+ 0 2 2 {SCAN TABLE t402}
}
+} ;# endif
# Verify that a performance regression encountered by firefox
# has been fixed.
@@ -298,8 +317,8 @@ do_execsql_test where3-5.0 {
AND bbb.parent = 4
ORDER BY bbb.title COLLATE NOCASE ASC;
} {
- 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)}
- 0 1 1 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)}
+ 0 1 1 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
do_execsql_test where3-5.1 {
@@ -311,8 +330,8 @@ do_execsql_test where3-5.1 {
AND bbb.parent = 4
ORDER BY bbb.title COLLATE NOCASE ASC;
} {
- 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)}
- 0 1 1 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 0 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)}
+ 0 1 1 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
do_execsql_test where3-5.2 {
@@ -324,8 +343,8 @@ do_execsql_test where3-5.2 {
AND bbb.parent = 4
ORDER BY bbb.title COLLATE NOCASE ASC;
} {
- 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)}
- 0 1 0 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)}
+ 0 1 0 {SEARCH TABLE bbb USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
do_execsql_test where3-5.3 {
@@ -337,8 +356,8 @@ do_execsql_test where3-5.3 {
AND bbb.parent = 4
ORDER BY bbb.title COLLATE NOCASE ASC;
} {
- 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?) (~10 rows)}
- 0 1 0 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
+ 0 0 1 {SEARCH TABLE aaa USING INDEX aaa_333 (fk=?)}
+ 0 1 0 {SEARCH TABLE aaa AS bbb USING INTEGER PRIMARY KEY (rowid=?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
@@ -424,5 +443,48 @@ foreach predicate {
} {1 w-one x-one y-one z-one 9 w-nine x-nine y-nine z-nine}
}
+do_execsql_test where3-7-setup {
+ CREATE TABLE t71(x1 INTEGER PRIMARY KEY, y1);
+ CREATE TABLE t72(x2 INTEGER PRIMARY KEY, y2);
+ CREATE TABLE t73(x3, y3);
+ CREATE TABLE t74(x4, y4);
+ INSERT INTO t71 VALUES(123,234);
+ INSERT INTO t72 VALUES(234,345);
+ INSERT INTO t73 VALUES(123,234);
+ INSERT INTO t74 VALUES(234,345);
+ INSERT INTO t74 VALUES(234,678);
+} {}
+foreach disabled_opt {none omit-noop-join all} {
+ optimization_control db all 1
+ optimization_control db $disabled_opt 0
+ do_execsql_test where3-7.$disabled_opt.1 {
+ SELECT x1 FROM t71 LEFT JOIN t72 ON x2=y1;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.2 {
+ SELECT x1 FROM t71 LEFT JOIN t72 ON x2=y1 WHERE y2 IS NULL;
+ } {}
+ do_execsql_test where3-7.$disabled_opt.3 {
+ SELECT x1 FROM t71 LEFT JOIN t72 ON x2=y1 WHERE y2 IS NOT NULL;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.4 {
+ SELECT x1 FROM t71 LEFT JOIN t72 ON x2=y1 AND y2 IS NULL;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.5 {
+ SELECT x1 FROM t71 LEFT JOIN t72 ON x2=y1 AND y2 IS NOT NULL;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.6 {
+ SELECT x3 FROM t73 LEFT JOIN t72 ON x2=y3;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.7 {
+ SELECT DISTINCT x3 FROM t73 LEFT JOIN t72 ON x2=y3;
+ } {123}
+ do_execsql_test where3-7.$disabled_opt.8 {
+ SELECT x3 FROM t73 LEFT JOIN t74 ON x4=y3;
+ } {123 123}
+ do_execsql_test where3-7.$disabled_opt.9 {
+ SELECT DISTINCT x3 FROM t73 LEFT JOIN t74 ON x4=y3;
+ } {123}
+}
+
finish_test
diff --git a/test/where4.test b/test/where4.test
index 280eb5f..a26e9ad 100644
--- a/test/where4.test
+++ b/test/where4.test
@@ -71,7 +71,7 @@ do_test where4-1.5 {
} {1 2}
do_test where4-1.6 {
count {SELECT rowid FROM t1 WHERE w=1 AND x<9}
-} {1 3}
+} {1 2}
do_test where4-1.7 {
count {SELECT rowid FROM t1 WHERE w=1 AND x IS NULL AND y=3}
} {2 2}
@@ -98,7 +98,7 @@ do_test where4-1.14 {
} {7 2}
do_test where4-1.15 {
count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL AND y<0}
-} {2}
+} {1}
do_test where4-1.16 {
count {SELECT rowid FROM t1 WHERE w IS NULL AND x IS NULL AND y>=0}
} {1}
diff --git a/test/where7.test b/test/where7.test
index b6cd7cc..5032c69 100644
--- a/test/where7.test
+++ b/test/where7.test
@@ -23303,7 +23303,7 @@ do_test where7-2.1001.2 {
#
# The test case that follows is code from an actual
# application with identifiers change and unused columns
-# remove.
+# removed.
#
do_execsql_test where7-3.1 {
CREATE TABLE t301 (
@@ -23332,16 +23332,16 @@ do_execsql_test where7-3.1 {
EXPLAIN QUERY PLAN
SELECT t302.c1
- FROM t302 JOIN t301 ON t302.c8 = t301.c8
+ FROM t302 JOIN t301 ON t302.c8 = +t301.c8
WHERE t302.c2 = 19571
AND t302.c3 > 1287603136
AND (t301.c4 = 1407449685622784
OR t301.c8 = 1407424651264000)
ORDER BY t302.c5 LIMIT 200;
} {
- 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?) (~10 rows)}
- 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
- 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?) (~2 rows)}
+ 0 0 1 {SEARCH TABLE t301 USING COVERING INDEX t301_c4 (c4=?)}
+ 0 0 1 {SEARCH TABLE t301 USING INTEGER PRIMARY KEY (rowid=?)}
+ 0 1 0 {SEARCH TABLE t302 USING INDEX t302_c8_c3 (c8=? AND c3>?)}
0 0 0 {USE TEMP B-TREE FOR ORDER BY}
}
diff --git a/test/where8.test b/test/where8.test
index 9b6014e..139251a 100644
--- a/test/where8.test
+++ b/test/where8.test
@@ -12,7 +12,6 @@
# is testing of where.c. More specifically, the focus is the optimization
# of WHERE clauses that feature the OR operator.
#
-# $Id: where8.test,v 1.9 2009/07/31 06:14:52 danielk1977 Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -88,13 +87,13 @@ do_test where8-1.8 {
do_test where8-1.9 {
execsql_status2 { SELECT c FROM t1 WHERE a >= 9 OR b <= 'eight' }
-} {IX X VIII 0 0 6}
+} {IX X VIII 0 0 7}
do_test where8-1.10 {
execsql_status2 {
SELECT c FROM t1 WHERE (a >= 9 AND c != 'X') OR b <= 'eight'
}
-} {IX VIII 0 0 6}
+} {IX VIII 0 0 7}
do_test where8-1.11 {
execsql_status2 {
@@ -211,9 +210,10 @@ do_test where8-3.4 {
do_test where8-3.5 {
execsql_status {
- SELECT a, d FROM t1, t2 WHERE (a = 2 OR a = 3) AND (d = a OR e = 'sixteen')
+ SELECT a, d FROM t1, t2 WHERE (a = 2 OR a = 3) AND (d = +a OR e = 'sixteen')
+ ORDER BY +a, +d;
}
-} {2 2 2 4 3 3 3 4 0 0}
+} {2 2 2 4 3 3 3 4 0 1}
do_test where8-3.6 {
# The first part of the WHERE clause in this query, (a=2 OR a=3) is
@@ -222,7 +222,7 @@ do_test where8-3.6 {
execsql_status {
SELECT a, d
FROM t1, t2
- WHERE (a = 2 OR a = 3) AND (d = a OR e = 'sixteen')
+ WHERE (a = 2 OR a = 3) AND (d = +a OR e = 'sixteen')
ORDER BY t1.rowid
}
} {2 2 2 4 3 3 3 4 0 1}
@@ -233,7 +233,7 @@ do_test where8-3.7 {
WHERE a = 2 AND (d = a OR e = 'sixteen')
ORDER BY t1.rowid
}
-} {2 2 2 4 0 0}
+} {/2 2 2 4 0 [01]/}
do_test where8-3.8 {
execsql_status {
SELECT a, d
@@ -268,7 +268,7 @@ do_test where8-3.12 {
execsql_status {
SELECT a, d FROM t1, t2 WHERE (a=d OR b=e) AND +a<5 ORDER BY a
}
-} {1 1 2 2 3 3 4 2 4 4 0 0}
+} {1 1 2 2 3 3 4 2 4 4 9 0}
do_test where8-3.13 {
execsql_status {
SELECT a, d FROM t1, t2 WHERE (a=d OR b=e) AND +a<5
@@ -748,4 +748,13 @@ do_test where8-5.3 {
}
} {1 {}}
+# The OR optimization and WITHOUT ROWID
+#
+do_execsql_test where8-6.1 {
+ CREATE TABLE t600(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE INDEX t600b ON t600(b);
+ INSERT INTO t600 VALUES('state','screen'),('exact','dolphin'),('green','mercury');
+ SELECT a, b, '|' FROM t600 WHERE a=='state' OR b='mercury' ORDER BY +a;
+} {green mercury | state screen |}
+
finish_test
diff --git a/test/where9.test b/test/where9.test
index 1e94fdf..d073074 100644
--- a/test/where9.test
+++ b/test/where9.test
@@ -362,9 +362,9 @@ ifcapable explain {
SELECT t2.a FROM t1, t2
WHERE t1.a=80 AND ((t1.c=t2.c AND t1.d=t2.d) OR t1.f=t2.f)
} {
- 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
- 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)}
+ 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?)}
+ 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?)}
}
do_execsql_test where9-3.2 {
EXPLAIN QUERY PLAN
@@ -372,9 +372,9 @@ ifcapable explain {
FROM t1 LEFT JOIN t2 ON (t1.c+1=t2.c AND t1.d=t2.d) OR (t1.f||'x')=t2.f
WHERE t1.a=80
} {
- 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?) (~1 rows)}
- 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?) (~2 rows)}
- 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?) (~10 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)}
+ 0 1 1 {SEARCH TABLE t2 USING INDEX t2d (d=?)}
+ 0 1 1 {SEARCH TABLE t2 USING COVERING INDEX t2f (f=?)}
}
}
@@ -420,7 +420,7 @@ do_test where9-4.5 {
AND (c=31031 OR d IS NULL)
ORDER BY +a
}
-} {1 {cannot use index: t1b}}
+} {1 {no query solution}}
do_test where9-4.6 {
count_steps {
SELECT a FROM t1 NOT INDEXED
@@ -436,7 +436,7 @@ do_test where9-4.7 {
AND (c=31031 OR d IS NULL)
ORDER BY +a
}
-} {1 {cannot use index: t1c}}
+} {1 {no query solution}}
do_test where9-4.8 {
catchsql {
SELECT a FROM t1 INDEXED BY t1d
@@ -444,7 +444,7 @@ do_test where9-4.8 {
AND (c=31031 OR d IS NULL)
ORDER BY +a
}
-} {1 {cannot use index: t1d}}
+} {1 {no query solution}}
ifcapable explain {
# The (c=31031 OR d IS NULL) clause is preferred over b>1000 because
@@ -453,8 +453,8 @@ ifcapable explain {
do_execsql_test where9-5.1 {
EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c=31031 OR d IS NULL)
} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?) (~3 rows)}
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?) (~3 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c=?)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1d (d=?)}
}
# In contrast, b=1000 is preferred over any OR-clause.
@@ -462,7 +462,7 @@ ifcapable explain {
do_execsql_test where9-5.2 {
EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b=1000 AND (c=31031 OR d IS NULL)
} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~5 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?)}
}
# Likewise, inequalities in an AND are preferred over inequalities in
@@ -471,7 +471,7 @@ ifcapable explain {
do_execsql_test where9-5.3 {
EXPLAIN QUERY PLAN SELECT a FROM t1 WHERE b>1000 AND (c>=31031 OR d IS NULL)
} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?) (~125000 rows)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>?)}
}
}
@@ -768,20 +768,62 @@ do_test where9-6.7.4 {
do_test where9-6.8.1 {
catchsql {
DELETE FROM t1 INDEXED BY t1b
- WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL)
OR (b NOT NULL AND c IS NULL AND d NOT NULL)
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
}
-} {1 {cannot use index: t1b}}
+} {1 {no query solution}}
do_test where9-6.8.2 {
catchsql {
UPDATE t1 INDEXED BY t1b SET a=a+100
- WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ WHERE (+b IS NULL AND c NOT NULL AND d NOT NULL)
OR (b NOT NULL AND c IS NULL AND d NOT NULL)
OR (b NOT NULL AND c NOT NULL AND d IS NULL)
}
-} {1 {cannot use index: t1b}}
+} {1 {no query solution}}
+set solution_possible 0
+ifcapable stat4||stat3 {
+ if {[permutation] != "no_optimization"} { set solution_possible 1 }
+}
+if $solution_possible {
+ # When STAT3 is enabled, the "b NOT NULL" terms get translated
+ # into b>NULL, which can be satified by the index t1b. It is a very
+ # expensive way to do the query, but it works, and so a solution is possible.
+ do_test where9-6.8.3-stat4 {
+ catchsql {
+ UPDATE t1 INDEXED BY t1b SET a=a+100
+ WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ OR (b NOT NULL AND c IS NULL AND d NOT NULL)
+ OR (b NOT NULL AND c NOT NULL AND d IS NULL)
+ }
+ } {0 {}}
+ do_test where9-6.8.4-stat4 {
+ catchsql {
+ DELETE FROM t1 INDEXED BY t1b
+ WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ OR (b NOT NULL AND c IS NULL AND d NOT NULL)
+ OR (b NOT NULL AND c NOT NULL AND d IS NULL)
+ }
+ } {0 {}}
+} else {
+ do_test where9-6.8.3 {
+ catchsql {
+ UPDATE t1 INDEXED BY t1b SET a=a+100
+ WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ OR (b NOT NULL AND c IS NULL AND d NOT NULL)
+ OR (b NOT NULL AND c NOT NULL AND d IS NULL)
+ }
+ } {1 {no query solution}}
+ do_test where9-6.8.4 {
+ catchsql {
+ DELETE FROM t1 INDEXED BY t1b
+ WHERE (b IS NULL AND c NOT NULL AND d NOT NULL)
+ OR (b NOT NULL AND c IS NULL AND d NOT NULL)
+ OR (b NOT NULL AND c NOT NULL AND d IS NULL)
+ }
+ } {1 {no query solution}}
+}
############################################################################
# Test cases where terms inside an OR series are combined with AND terms
# external to the OR clause. In other words, cases where
@@ -814,6 +856,11 @@ do_test where9-7.0 {
INSERT INTO t6 SELECT * FROM t5;
ANALYZE t5;
}
+ ifcapable stat3 {
+ sqlite3 db2 test.db
+ db2 eval { DROP TABLE IF EXISTS sqlite_stat3 }
+ db2 close
+ }
} {}
do_test where9-7.1.1 {
count_steps {
@@ -914,4 +961,28 @@ do_test where9-9.1 {
}
} {1 2 3 4 8 9}
+# Fix for ticket [bc878246eafe0f52c519e29049b2fe4a99491b27]
+# Incorrect result when OR is used in a join to the right of a LEFT JOIN
+#
+do_test where9-10.1 {
+ db eval {
+ CREATE TABLE t101 (id INTEGER PRIMARY KEY);
+ INSERT INTO t101 VALUES (1);
+ SELECT * FROM t101 AS t0
+ LEFT JOIN t101 AS t1 ON t1.id BETWEEN 10 AND 20
+ JOIN t101 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id));
+ }
+} {1 {} 1}
+do_test where9-10.2 {
+ db eval {
+ CREATE TABLE t102 (id TEXT UNIQUE NOT NULL);
+ INSERT INTO t102 VALUES ('1');
+ SELECT * FROM t102 AS t0
+ LEFT JOIN t102 AS t1 ON t1.id GLOB 'abc%'
+ JOIN t102 AS t2 ON (t2.id = t0.id OR (t2.id<>555 AND t2.id=t1.id));
+ }
+} {1 {} 1}
+
+
+
finish_test
diff --git a/test/whereA.test b/test/whereA.test
index 5d0aca5..78826b1 100644
--- a/test/whereA.test
+++ b/test/whereA.test
@@ -68,6 +68,12 @@ do_test whereA-1.7 {
SELECT * FROM t1;
}
} {3 4.53 {} 2 hello world 1 2 3}
+do_execsql_test whereA-1.8 {
+ SELECT * FROM t1 WHERE b=2 AND a IS NULL;
+} {}
+do_execsql_test whereA-1.9 {
+ SELECT * FROM t1 WHERE b=2 AND a IS NOT NULL;
+} {1 2 3}
do_test whereA-2.1 {
db eval {
diff --git a/test/whereC.test b/test/whereC.test
index 9fa1bba..bd7fe9a 100644
--- a/test/whereC.test
+++ b/test/whereC.test
@@ -67,4 +67,3 @@ foreach {tn sql res} {
finish_test
-
diff --git a/test/whereD.test b/test/whereD.test
index 9ac5a68..db99304 100644
--- a/test/whereD.test
+++ b/test/whereD.test
@@ -186,4 +186,90 @@ do_test 4.3 {
}
} {1 2 3 3 6 9 4 5 6 {} {} {}}
+# Ticket [bc1aea7b725f276177]
+# Incorrect result on LEFT JOIN with OR constraints and an ORDER BY clause.
+#
+do_execsql_test 4.4 {
+ CREATE TABLE t44(a INTEGER, b INTEGER);
+ INSERT INTO t44 VALUES(1,2);
+ INSERT INTO t44 VALUES(3,4);
+ SELECT *
+ FROM t44 AS x
+ LEFT JOIN (SELECT a AS c, b AS d FROM t44) AS y ON a=c
+ WHERE d=4 OR d IS NULL;
+} {3 4 3 4}
+do_execsql_test 4.5 {
+ SELECT *
+ FROM t44 AS x
+ LEFT JOIN (SELECT a AS c, b AS d FROM t44) AS y ON a=c
+ WHERE d=4 OR d IS NULL
+ ORDER BY a;
+} {3 4 3 4}
+do_execsql_test 4.6 {
+ CREATE TABLE t46(c INTEGER, d INTEGER);
+ INSERT INTO t46 SELECT a, b FROM t44;
+ SELECT * FROM t44 LEFT JOIN t46 ON a=c
+ WHERE d=4 OR d IS NULL;
+} {3 4 3 4}
+do_execsql_test 4.7 {
+ SELECT * FROM t44 LEFT JOIN t46 ON a=c
+ WHERE d=4 OR d IS NULL
+ ORDER BY a;
+} {3 4 3 4}
+
+# Verify fix of a bug reported on the mailing list by Peter Reid
+#
+do_execsql_test 5.1 {
+ DROP TABLE IF EXISTS t;
+ CREATE TABLE t(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17);
+ CREATE INDEX tc0 ON t(c0);
+ CREATE INDEX tc1 ON t(c1);
+ CREATE INDEX tc2 ON t(c2);
+ CREATE INDEX tc3 ON t(c3);
+ CREATE INDEX tc4 ON t(c4);
+ CREATE INDEX tc5 ON t(c5);
+ CREATE INDEX tc6 ON t(c6);
+ CREATE INDEX tc7 ON t(c7);
+ CREATE INDEX tc8 ON t(c8);
+ CREATE INDEX tc9 ON t(c9);
+ CREATE INDEX tc10 ON t(c10);
+ CREATE INDEX tc11 ON t(c11);
+ CREATE INDEX tc12 ON t(c12);
+ CREATE INDEX tc13 ON t(c13);
+ CREATE INDEX tc14 ON t(c14);
+ CREATE INDEX tc15 ON t(c15);
+ CREATE INDEX tc16 ON t(c16);
+ CREATE INDEX tc17 ON t(c17);
+
+ INSERT INTO t(c0, c16) VALUES (1,1);
+
+ SELECT * FROM t WHERE
+ c0=1 or c1=1 or c2=1 or c3=1 or
+ c4=1 or c5=1 or c6=1 or c7=1 or
+ c8=1 or c9=1 or c10=1 or c11=1 or
+ c12=1 or c13=1 or c14=1 or c15=1 or
+ c16=1 or c17=1;
+} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {}}
+do_execsql_test 5.2 {
+ DELETE FROM t;
+ INSERT INTO t(c0,c17) VALUES(1,1);
+ SELECT * FROM t WHERE
+ c0=1 or c1=1 or c2=1 or c3=1 or
+ c4=1 or c5=1 or c6=1 or c7=1 or
+ c8=1 or c9=1 or c10=1 or c11=1 or
+ c12=1 or c13=1 or c14=1 or c15=1 or
+ c16=1 or c17=1;
+} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1}
+do_execsql_test 5.3 {
+ DELETE FROM t;
+ INSERT INTO t(c0,c15) VALUES(1,1);
+ SELECT * FROM t WHERE
+ c0=1 or c1=1 or c2=1 or c3=1 or
+ c4=1 or c5=1 or c6=1 or c7=1 or
+ c8=1 or c9=1 or c10=1 or c11=1 or
+ c12=1 or c13=1 or c14=1 or c15=1 or
+ c16=1 or c17=1;
+} {1 {} {} {} {} {} {} {} {} {} {} {} {} {} {} 1 {} {}}
+
+
finish_test
diff --git a/test/whereE.test b/test/whereE.test
index e686a46..a6b8f48 100644
--- a/test/whereE.test
+++ b/test/whereE.test
@@ -47,16 +47,16 @@ do_execsql_test 1.1 {
CREATE UNIQUE INDEX t2zx ON t2(z,x);
EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x;
-} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/}
+} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/}
do_execsql_test 1.2 {
EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x;
-} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/}
+} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/}
do_execsql_test 1.3 {
ANALYZE;
EXPLAIN QUERY PLAN SELECT x FROM t1, t2 WHERE a=z AND c=x;
-} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/}
+} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/}
do_execsql_test 1.4 {
EXPLAIN QUERY PLAN SELECT x FROM t2, t1 WHERE a=z AND c=x;
-} {/.*SCAN TABLE t1 .*SEARCH TABLE t2 .*/}
+} {/.*SCAN TABLE t1.*SEARCH TABLE t2.*/}
finish_test
diff --git a/test/whereF.test b/test/whereF.test
index 57bdbee..b9580bb 100644
--- a/test/whereF.test
+++ b/test/whereF.test
@@ -46,7 +46,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-set testprefix x
+set testprefix whereF
do_execsql_test 1.0 {
PRAGMA automatic_index = 0;
@@ -63,7 +63,7 @@ foreach {tn sql} {
} {
do_test 1.$tn {
db eval "EXPLAIN QUERY PLAN $sql"
- } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/}
+ } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/}
}
do_execsql_test 2.0 {
@@ -84,7 +84,7 @@ foreach {tn sql} {
} {
do_test 2.$tn {
db eval "EXPLAIN QUERY PLAN $sql"
- } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/}
+ } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/}
}
do_execsql_test 3.0 {
@@ -109,7 +109,14 @@ foreach {tn sql} {
} {
do_test 3.$tn {
db eval "EXPLAIN QUERY PLAN $sql"
- } {/.*SCAN TABLE t2 .*SEARCH TABLE t1 .*/}
+ } {/.*SCAN TABLE t2\y.*SEARCH TABLE t1\y.*/}
}
+do_execsql_test 4.0 {
+ CREATE TABLE t4(a,b,c,d,e, PRIMARY KEY(a,b,c));
+ CREATE INDEX t4adc ON t4(a,d,c);
+ CREATE UNIQUE INDEX t4aebc ON t4(a,e,b,c);
+ EXPLAIN QUERY PLAN SELECT rowid FROM t4 WHERE a=? AND b=?;
+} {/a=. AND b=./}
+
finish_test
diff --git a/test/whereG.test b/test/whereG.test
new file mode 100644
index 0000000..c2c54db
--- /dev/null
+++ b/test/whereG.test
@@ -0,0 +1,233 @@
+# 2013-09-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.
+#
+#***********************************************************************
+#
+# Test cases for query planning decisions and the likely(), unlikely(), and
+# likelihood() functions.
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix whereG
+
+do_execsql_test whereG-1.0 {
+ CREATE TABLE composer(
+ cid INTEGER PRIMARY KEY,
+ cname TEXT
+ );
+ CREATE TABLE album(
+ aid INTEGER PRIMARY KEY,
+ aname TEXT
+ );
+ CREATE TABLE track(
+ tid INTEGER PRIMARY KEY,
+ cid INTEGER REFERENCES composer,
+ aid INTEGER REFERENCES album,
+ title TEXT
+ );
+ CREATE INDEX track_i1 ON track(cid);
+ CREATE INDEX track_i2 ON track(aid);
+ INSERT INTO composer VALUES(1, 'W. A. Mozart');
+ INSERT INTO composer VALUES(2, 'Beethoven');
+ INSERT INTO composer VALUES(3, 'Thomas Tallis');
+ INSERT INTO composer VALUES(4, 'Joseph Hayden');
+ INSERT INTO composer VALUES(5, 'Thomas Weelkes');
+ INSERT INTO composer VALUES(6, 'J. S. Bach');
+ INSERT INTO composer VALUES(7, 'Orlando Gibbons');
+ INSERT INTO composer VALUES(8, 'Josquin des Prés');
+ INSERT INTO composer VALUES(9, 'Byrd');
+ INSERT INTO composer VALUES(10, 'Francis Poulenc');
+ INSERT INTO composer VALUES(11, 'Mendelsshon');
+ INSERT INTO composer VALUES(12, 'Zoltán Kodály');
+ INSERT INTO composer VALUES(13, 'Handel');
+ INSERT INTO album VALUES(100, 'Kodály: Missa Brevis');
+ INSERT INTO album VALUES(101, 'Messiah');
+ INSERT INTO album VALUES(102, 'Missa Brevis in D-, K.65');
+ INSERT INTO album VALUES(103, 'The complete English anthems');
+ INSERT INTO album VALUES(104, 'Mass in B Minor, BWV 232');
+ INSERT INTO track VALUES(10005, 12, 100, 'Sanctus');
+ INSERT INTO track VALUES(10007, 12, 100, 'Agnus Dei');
+ INSERT INTO track VALUES(10115, 13, 101, 'Surely He Hath Borne Our Griefs');
+ INSERT INTO track VALUES(10129, 13, 101, 'Since By Man Came Death');
+ INSERT INTO track VALUES(10206, 1, 102, 'Agnus Dei');
+ INSERT INTO track VALUES(10301, 3, 103, 'If Ye Love Me');
+ INSERT INTO track VALUES(10402, 6, 104, 'Domine Deus');
+ INSERT INTO track VALUES(10403, 6, 104, 'Qui tollis');
+} {}
+do_eqp_test whereG-1.1 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE unlikely(cname LIKE '%bach%')
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {/.*composer.*track.*album.*/}
+do_execsql_test whereG-1.2 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE unlikely(cname LIKE '%bach%')
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {{Mass in B Minor, BWV 232}}
+
+do_eqp_test whereG-1.3 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE likelihood(cname LIKE '%bach%', 0.5)
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {/.*track.*composer.*album.*/}
+do_execsql_test whereG-1.4 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE likelihood(cname LIKE '%bach%', 0.5)
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {{Mass in B Minor, BWV 232}}
+
+do_eqp_test whereG-1.5 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE cname LIKE '%bach%'
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {/.*track.*(composer.*album|album.*composer).*/}
+do_execsql_test whereG-1.6 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE cname LIKE '%bach%'
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+} {{Mass in B Minor, BWV 232}}
+
+do_eqp_test whereG-1.7 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE cname LIKE '%bach%'
+ AND unlikely(composer.cid=track.cid)
+ AND unlikely(album.aid=track.aid);
+} {/.*track.*(composer.*album|album.*composer).*/}
+do_execsql_test whereG-1.8 {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE cname LIKE '%bach%'
+ AND unlikely(composer.cid=track.cid)
+ AND unlikely(album.aid=track.aid);
+} {{Mass in B Minor, BWV 232}}
+
+do_test whereG-2.1 {
+ catchsql {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE likelihood(cname LIKE '%bach%', -0.01)
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+do_test whereG-2.2 {
+ catchsql {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE likelihood(cname LIKE '%bach%', 1.01)
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+do_test whereG-2.3 {
+ catchsql {
+ SELECT DISTINCT aname
+ FROM album, composer, track
+ WHERE likelihood(cname LIKE '%bach%', track.cid)
+ AND composer.cid=track.cid
+ AND album.aid=track.aid;
+ }
+} {1 {second argument to likelihood() must be a constant between 0.0 and 1.0}}
+
+# Commuting a term of the WHERE clause should not change the query plan
+#
+do_execsql_test whereG-3.0 {
+ CREATE TABLE a(a1 PRIMARY KEY, a2);
+ CREATE TABLE b(b1 PRIMARY KEY, b2);
+} {}
+do_eqp_test whereG-3.1 {
+ SELECT * FROM a, b WHERE b1=a1 AND a2=5;
+} {/.*SCAN TABLE a.*SEARCH TABLE b USING INDEX .*b_1 .b1=..*/}
+do_eqp_test whereG-3.2 {
+ SELECT * FROM a, b WHERE a1=b1 AND a2=5;
+} {/.*SCAN TABLE a.*SEARCH TABLE b USING INDEX .*b_1 .b1=..*/}
+do_eqp_test whereG-3.3 {
+ SELECT * FROM a, b WHERE a2=5 AND b1=a1;
+} {/.*SCAN TABLE a.*SEARCH TABLE b USING INDEX .*b_1 .b1=..*/}
+do_eqp_test whereG-3.4 {
+ SELECT * FROM a, b WHERE a2=5 AND a1=b1;
+} {/.*SCAN TABLE a.*SEARCH TABLE b USING INDEX .*b_1 .b1=..*/}
+
+# Ticket [1e64dd782a126f48d78c43a664844a41d0e6334e]:
+# Incorrect result in a nested GROUP BY/DISTINCT due to the use of an OP_SCopy
+# where an OP_Copy was needed.
+#
+do_execsql_test whereG-4.0 {
+ CREATE TABLE t4(x);
+ INSERT INTO t4 VALUES('right'),('wrong');
+ SELECT DISTINCT x
+ FROM (SELECT x FROM t4 GROUP BY x)
+ WHERE x='right'
+ ORDER BY x;
+} {right}
+
+#-------------------------------------------------------------------------
+# Test that likelihood() specifications on indexed terms are taken into
+# account by various forms of loops.
+#
+# 5.1.*: open ended range scans
+# 5.2.*: skip-scans
+#
+reset_db
+
+do_execsql_test 5.1 {
+ CREATE TABLE t1(a, b, c);
+ CREATE INDEX i1 ON t1(a, b);
+}
+do_eqp_test 5.1.2 {
+ SELECT * FROM t1 WHERE a>?
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a>?)}}
+do_eqp_test 5.1.3 {
+ SELECT * FROM t1 WHERE likelihood(a>?, 0.9)
+} {0 0 0 {SCAN TABLE t1}}
+do_eqp_test 5.1.4 {
+ SELECT * FROM t1 WHERE likely(a>?)
+} {0 0 0 {SCAN TABLE t1}}
+
+do_test 5.2 {
+ for {set i 0} {$i < 100} {incr i} {
+ execsql { INSERT INTO t1 VALUES('abc', $i, $i); }
+ }
+ execsql { INSERT INTO t1 SELECT 'def', b, c FROM t1; }
+ execsql { ANALYZE }
+} {}
+do_eqp_test 5.2.2 {
+ SELECT * FROM t1 WHERE likelihood(b>?, 0.01)
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (ANY(a) AND b>?)}}
+do_eqp_test 5.2.3 {
+ SELECT * FROM t1 WHERE likelihood(b>?, 0.9)
+} {0 0 0 {SCAN TABLE t1}}
+do_eqp_test 5.2.4 {
+ SELECT * FROM t1 WHERE likely(b>?)
+} {0 0 0 {SCAN TABLE t1}}
+
+do_eqp_test 5.3.1 {
+ SELECT * FROM t1 WHERE a=?
+} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (a=?)}}
+do_eqp_test 5.3.2 {
+ SELECT * FROM t1 WHERE likelihood(a=?, 0.9)
+} {0 0 0 {SCAN TABLE t1}}
+do_eqp_test 5.3.3 {
+ SELECT * FROM t1 WHERE likely(a=?)
+} {0 0 0 {SCAN TABLE t1}}
+
+finish_test
diff --git a/test/whereH.test b/test/whereH.test
new file mode 100644
index 0000000..c252fe1
--- /dev/null
+++ b/test/whereH.test
@@ -0,0 +1,139 @@
+# 2014-03-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.
+#
+#***********************************************************************
+#
+# Test cases for query planning decisions where one candidate index
+# covers a proper superset of the WHERE clause terms of another
+# candidate index.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_execsql_test whereH-1.1 {
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX t1abc ON t1(a,b,c);
+ CREATE INDEX t1bc ON t1(b,c);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c;
+} {/INDEX t1abc /}
+do_execsql_test whereH-1.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-2.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d);
+ CREATE INDEX t1bc ON t1(b,c);
+ CREATE INDEX t1abc ON t1(a,b,c);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c;
+} {/INDEX t1abc /}
+do_execsql_test whereH-2.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c>=? ORDER BY c;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-3.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1cd ON t1(c,d);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-3.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-4.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1cd ON t1(c,d);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-4.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-5.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+ CREATE INDEX t1cd ON t1(c,d);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-5.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-6.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+ CREATE INDEX t1cd ON t1(c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-6.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-7.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+ CREATE INDEX t1cd ON t1(c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-7.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+do_execsql_test whereH-8.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e);
+ CREATE INDEX t1abcd ON t1(a,b,c,d);
+ CREATE INDEX t1cd ON t1(c,d);
+ CREATE INDEX t1bcd ON t1(b,c,d);
+
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {/INDEX t1abcd /}
+do_execsql_test whereH-8.2 {
+ EXPLAIN QUERY PLAN
+ SELECT d FROM t1 WHERE a=? AND b=? AND c=? AND d>=? ORDER BY d;
+} {~/TEMP B-TREE FOR ORDER BY/}
+
+
+
+finish_test
diff --git a/test/whereI.test b/test/whereI.test
new file mode 100644
index 0000000..452e7f8
--- /dev/null
+++ b/test/whereI.test
@@ -0,0 +1,92 @@
+# 2014-03-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.
+#
+#***********************************************************************
+# The focus of this file is testing the OR optimization on WITHOUT ROWID
+# tables.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix whereI
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1, 'a', 'z');
+ INSERT INTO t1 VALUES(2, 'b', 'y');
+ INSERT INTO t1 VALUES(3, 'c', 'x');
+ INSERT INTO t1 VALUES(4, 'd', 'w');
+ CREATE INDEX i1 ON t1(b);
+ CREATE INDEX i2 ON t1(c);
+}
+
+do_eqp_test 1.1 {
+ SELECT a FROM t1 WHERE b='b' OR c='x'
+} {
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (b=?)}
+ 0 0 0 {SEARCH TABLE t1 USING INDEX i2 (c=?)}
+}
+
+do_execsql_test 1.2 {
+ SELECT a FROM t1 WHERE b='b' OR c='x'
+} {2 3}
+
+do_execsql_test 1.3 {
+ SELECT a FROM t1 WHERE b='a' OR c='z'
+} {1}
+
+#----------------------------------------------------------------------
+# Try that again, this time with non integer PRIMARY KEY values.
+#
+do_execsql_test 2.0 {
+ CREATE TABLE t2(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
+ INSERT INTO t2 VALUES('i', 'a', 'z');
+ INSERT INTO t2 VALUES('ii', 'b', 'y');
+ INSERT INTO t2 VALUES('iii', 'c', 'x');
+ INSERT INTO t2 VALUES('iv', 'd', 'w');
+ CREATE INDEX i3 ON t2(b);
+ CREATE INDEX i4 ON t2(c);
+}
+
+do_eqp_test 2.1 {
+ SELECT a FROM t2 WHERE b='b' OR c='x'
+} {
+ 0 0 0 {SEARCH TABLE t2 USING INDEX i3 (b=?)}
+ 0 0 0 {SEARCH TABLE t2 USING INDEX i4 (c=?)}
+}
+
+do_execsql_test 2.2 {
+ SELECT a FROM t2 WHERE b='b' OR c='x'
+} {ii iii}
+
+do_execsql_test 2.3 {
+ SELECT a FROM t2 WHERE b='a' OR c='z'
+} {i}
+
+#----------------------------------------------------------------------
+# On a table with a multi-column PK.
+#
+do_execsql_test 3.0 {
+ CREATE TABLE t3(a, b, c, d, PRIMARY KEY(c, b)) WITHOUT ROWID;
+
+ INSERT INTO t3 VALUES('f', 1, 1, 'o');
+ INSERT INTO t3 VALUES('o', 2, 1, 't');
+ INSERT INTO t3 VALUES('t', 1, 2, 't');
+ INSERT INTO t3 VALUES('t', 2, 2, 'f');
+
+ CREATE INDEX t3i1 ON t3(d);
+ CREATE INDEX t3i2 ON t3(a);
+
+ SELECT c||'.'||b FROM t3 WHERE a='t' OR d='t'
+} {
+ 2.1 2.2 1.2
+}
+
+finish_test
+
diff --git a/test/whereJ.test b/test/whereJ.test
new file mode 100644
index 0000000..5209f16
--- /dev/null
+++ b/test/whereJ.test
@@ -0,0 +1,375 @@
+# 2014-06-06
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# This file implements regression tests for a complex
+# query planning case.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix whereJ
+
+ifcapable !stat4 {
+ finish_test
+ return
+}
+
+do_execsql_test whereJ-1.0 {
+ CREATE TABLE tx1 (
+ est,
+ cid,
+ sid,
+ fid,
+ aid,
+ edate,
+ rstat,
+ ftype,
+ cx,
+ fyear,
+ fp,
+ acode,
+ a1,
+ curx,
+ tdate,
+ gstat,
+ trgtpx,
+ effdate,
+ adate,
+ ytime,
+ mstat
+ );
+ CREATE INDEX ix0 on tx1(a1,curx,aid,cid,sid,ftype,fp,fyear DESC,edate DESC,fid);
+ CREATE INDEX ix1 on tx1(a1,curx,aid,ftype,fp,fyear DESC,fid,edate DESC,cid,sid);
+ CREATE INDEX ix2 on tx1(a1,curx,cid,sid,ftype,fp,fyear DESC,edate DESC,aid,fid);
+ CREATE INDEX ix3 on tx1(a1,curx,fid,ftype,fp,fyear DESC,cid,sid,aid,edate DESC);
+ CREATE INDEX ix4 on tx1(a1,curx,ftype,cid,sid,aid,edate DESC,fid,fp,fyear DESC);
+ CREATE INDEX ix5 on tx1(a1,curx,ftype,aid,fid,cid,sid,edate DESC,fp,fyear DESC);
+ CREATE INDEX ix6 on tx1(ftype,fp,fyear DESC,cid,sid,edate DESC,a1,fid,aid,curx,est,rstat,cx,acode,tdate,gstat,trgtpx,effdate,adate,ytime,mstat);
+ CREATE INDEX ix7 on tx1(cid,a1,curx,sid,ftype,est,fid,aid,edate,rstat,cx,fyear,fp,acode,tdate,gstat,trgtpx,effdate,adate,ytime,mstat);
+ CREATE INDEX ix8 on tx1(cid,sid,edate DESC,aid,est);
+ CREATE INDEX ix9 on tx1(aid,edate DESC,a1,curx);
+} {}
+do_execsql_test whereJ-1.1 {
+ ANALYZE sqlite_master;
+ DELETE FROM sqlite_stat1;
+ DELETE FROM sqlite_stat4;
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix9','11680827 289 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix8','11680827 286 250 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix7','11680827 286 194 98 88 83 18 7 6 2 2 2 2 2 2 2 2 2 2 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix6','11680827 5840414 5840414 5840414 240 212 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix5','11680827 5840414 2920207 1668690 114 90 8 8 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix4','11680827 5840414 2920207 1668690 92 83 9 2 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix3','11680827 5840414 2920207 2048 1835 1835 1835 12 11 8 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix2','11680827 5840414 2920207 98 88 83 83 83 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix1','11680827 5840414 2920207 117 114 114 114 90 2 2 2');
+ INSERT INTO sqlite_stat1 VALUES('tx1','ix0','11680827 5840414 2920207 117 9 9 9 9 9 2 2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','6736 21 21 21 1','29210 29404 29404 29404 29424','44 12184 13020 13079 29424',X'06030409080416C1150133512800B01FCA');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','6658 24 21 21 1','452220 453273 453276 453276 453296','622 226258 235279 236774 453296',X'06030409080416F34501332ADC00AA1BD3');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','820 2 2 2 1','1297771 1297869 1297869 1297869 1297869','1964 681724 711020 715822 1297869',X'06030409080317875501332C6C55AF4D');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','26985 2216 2216 2216 1','1797607 1797782 1797782 1797782 1799997','3162 970307 1008879 1016089 1799997',X'0603040809041A08040132401A0099A334');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','10434 19 17 17 1','2118117 2120403 2120405 2120405 2120421','3815 1136110 1181459 1190207 2120421',X'0603040908041AD36901332CD000861A2F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','437 1 1 1 1','2595414 2595739 2595739 2595739 2595739','5005 1409452 1464066 1475163 2595739',X'0603040808031CE7FD01317FD46BFBCC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','12619 38 38 38 1','2595957 2600212 2600212 2600212 2600249','5007 1410347 1464961 1476068 2600249',X'0603040808041CE87E01328F61008CE96A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','7534 23 18 18 1','3329985 3334890 3334895 3334895 3334912','6901 1834013 1902216 1917268 3334912',X'060304090804244E1901328F59008CAA39');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','5693 1 1 1 1','3891665 3893609 3893609 3893609 3893609','8357 2164400 2245393 2263185 3893609',X'0603040808043063B70132B66800A28A43');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','44405 2588 2223 1527 1','4220255 4221633 4221998 4222694 4224220','9221 2354858 2441973 2461511 4224220',X'0603040909043377630133517A00B0DE4F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','6883 32 28 28 1','4423918 4429926 4429930 4429930 4429957','9690 2452276 2543443 2563995 4429957',X'06030409080434F46801328F5C008CC3DA');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','6974 27 26 26 1','5048404 5051129 5051130 5051130 5051155','11703 2817010 2920184 2944013 5051155',X'0603040908043C1C5C0132DEA5009F7473');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','476 2 2 2 1','5191322 5191479 5191479 5191479 5191479','12242 2901130 3006663 3031222 5191479',X'0603040908033DC6080132DEA478849A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','627 4 4 4 1','6488823 6489349 6489349 6489349 6489349','16423 3644815 3778857 3809866 6489349',X'0603040808035AA00E0131F4AE342150');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','145 2 2 1 1','7787091 7787218 7787218 7787219 7787219','20223 4343720 4510110 4547961 7787219',X'0603040809037254890132189C703706');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','20 1 1 1 1','9085074 9085089 9085089 9085089 9085089','25315 5033102 5230788 5275692 9085089',X'06040408080300EAE6CA01326657620652');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','229621 6135 5934 5934 1','9507353 9572696 9572696 9572696 9576801','27189 5255584 5463962 5511784 9576801',X'06040408080300F2FA440132DF1A7D1A60');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','6376 24 22 22 1','10381524 10382938 10382940 10382940 10382959','30519 5581705 5804515 5856651 10382959',X'06040409080400F9DBF3013305AC00A688A4');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','7761 45 9 9 1','10569039 10572476 10572512 10572512 10572520','31455 5661599 5888691 5941811 10572520',X'06040409080400FB31560132DDD800A05DF5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','8382 37 37 37 1','10866664 10867565 10867565 10867565 10867601','33475 5809193 6042611 6097741 10867601',X'06040409080400FFA4A701332A0E00A93957');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','76136 4099 3018 3018 1','11283107 11308143 11309224 11309224 11312241','37001 6022861 6264510 6322923 11312241',X'060404090804010B0A5C0133517200B0E8E0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','23472 2188 2188 2181 1','11365285 11380281 11380281 11380288 11382468','37055 6026680 6268909 6327509 11382468',X'060404080904010B3C6701332B2E00AA4374');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','59591 4087 4073 4 1','11415316 11448759 11448773 11452842 11452845','37350 6040743 6283483 6342389 11452845',X'060404090904010BFA810133512800B010AE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix9','43891 3029 3021 4 1','11598477 11622881 11622889 11625906 11625909','39110 6107644 6353109 6413914 11625909',X'0604040909040113B9960133512800B01235');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7340 4977 19 1 1 1','206533 206533 208739 208757 208757 208757','125 164 111403 207397 207399 208757',X'070308040407030187840132B54B0101A0D1401C0000000000004C87E5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','8877 8788 45 1 1 1','1221375 1221375 1224509 1224553 1224553 1224553','931 1117 679933 1216705 1216722 1224553',X'07030804040703018D3F0133023D010B9B67401C0000000000007A99EF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7204 7204 39 1 1 1','1240162 1240162 1242572 1242610 1242610 1242610','942 1131 688420 1234655 1234672 1242610',X'07030804040703018D4F0132DB820105D324401C0000000000007EC569');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','9608 9608 44 1 1 1','1264939 1264939 1266529 1266572 1266572 1266572','952 1145 699518 1258423 1258440 1266572',X'07030804040704018D61013305B9010D3CEB406E8000000000000081E17A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','6636 6636 1 1 1 1','1294580 1294580 1297869 1297869 1297869 1297869','964 1159 713121 1289522 1289540 1297869',X'07030804030704018D7801328F693482A2403400000000000000A26728');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7822 6629 26 1 1 1','2375708 2375708 2381333 2381358 2381358 2381358','3423 3833 1371902 2366527 2366559 2381358',X'0703080403070301B1F501317F16403B7B403F00000000000060D67A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','1403 1303 5 1 1 1','2594767 2594767 2595737 2595739 2595739 2595739','3914 4427 1512042 2580073 2580114 2595739',X'0703080403070301B6480131CC18558082407120000000000029CC12');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7901 6067 26 1 1 1','3424107 3424107 3425939 3425964 3425964 3425964','5872 6630 2032411 3406550 3406594 3425964',X'0703080404070401C3F90132B7A100FDCC04403E00000000000000A014CE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7483 6161 22 1 1 1','3549446 3549446 3555223 3555244 3555244 3555244','5932 6752 2099309 3535259 3535304 3555244',X'0703080403070301C4490131573F4104F8403400000000000067FD1E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','12076 8263 37 1 1 1','3558079 3558079 3560036 3560072 3560072 3560072','5935 6758 2101989 3540078 3540123 3560072',X'0703080404070301C44E0132DD0901076DA9404200000000000076F994');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','1123 1113 1 1 1 1','3892913 3892913 3893609 3893609 3893609 3893609','6594 7611 2305483 3871711 3871770 3893609',X'0703080403070301CA280131CA1C215083401C00000000000071F6B2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','9344 7315 41 1 1 1','4213510 4213510 4219434 4219474 4219474 4219474','7200 8390 2503024 4196141 4196204 4219474',X'0703080404070301CE8C01317DE800FE4E8B4034000000000000458317');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','8062 3291 1 1 1 1','5037060 5037060 5040350 5040350 5040350 5040350','10201 11915 3045602 5012912 5012997 5040350',X'070308040307030213B20130B83A16DF86403600000000000028F8CD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','9125 2926 1 1 1 1','5046490 5052665 5055590 5055590 5055590 5055590','10203 11926 3055524 5028097 5028182 5055590',X'070302040307030213B5232A013107F01745AF40330000000000002B57DE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','591 591 1 1 1 1','5190991 5190991 5191479 5191479 5191479 5191479','10649 12426 3145181 5163206 5163296 5191479',X'070308040307030244AD0131315217C1CD401C00000000000003BC32');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7381 4689 1 1 1 1','6112248 6112248 6116936 6116936 6116936 6116936','13780 16308 3748958 6083681 6083797 6116936',X'0703080403070402A9D1013108B531A21C401C0000000000000092F5C6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7569 7381 28 1 1 1','6280084 6280084 6281842 6281869 6281869 6281869','14559 17217 3856803 6247722 6247841 6281869',X'0703080404070302C14C0132DBF101044CC7401C00000000000074FB16');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','8289 7047 28 1 1 1','6290764 6290764 6296854 6296881 6296881 6296881','14569 17229 3863206 6262658 6262777 6296881',X'0703080403070302C16401317CC348B670401C0000000000006824BB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','2209 2209 1 1 1 1','6489075 6489075 6489349 6489349 6489349 6489349','15377 18147 3986912 6454194 6454318 6489349',X'0703080403070402EA5901332A656C6F5E401C00000000000000AE7C03');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7381 6799 1 1 1 1','7314420 7314420 7321218 7321218 7321218 7321218','18403 21722 4532963 7281695 7281847 7321218',X'07030804030703049EE501310667176DC940438000000000005ED2B5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','7163 7001 31 1 1 1','7652849 7652849 7658600 7658630 7658630 7658630','19462 22956 4750159 7617449 7617608 7658630',X'070308040407030503EB01317DEF010ADD3F402800000000000061C3EF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','1433 1433 2 1 1 1','7785842 7785842 7787219 7787219 7787219 7787219','20001 23575 4834605 7745315 7745477 7787219',X'07030804030703055010013156D81B11AC404380000000000004A313');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','2247 2247 1 1 1 1','9083272 9083272 9085089 9085089 9085089 9085089','24940 29143 5668423 9036693 9036887 9085089',X'070308040307031A620A01323EEB5CE39C406FE000000000006F8177');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix8','107 107 5 1 1 1','10382912 10382912 10382957 10382959 10382959 10382959','31251 36297 6541362 10329764 10330008 10382959',X'0704080403070400955501013350516AA9D0406060000000000000884648');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7340 4770 4527 3331 3331 970 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','206533 206533 206533 206533 206533 206596 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565 207565','125 202 402 512 560 6209 20696 25885 206260 206260 206260 206260 206260 206260 206260 206260 206260 206260 206260 206260 206260 207565',X'1703080808080704030408030808010308070808080803018784401C000000000000024FD353493F8801317E4700FFFF0F00FFFFC0F869E0000000002642C5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','8877 4669 71 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','1221375 1225583 1230181 1230249 1230249 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251 1230251','931 1504 2992 3495 3751 39629 120561 147424 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1222711 1230251',X'1703090902080704030408090808010108070404040804018D3F03E8405680000000000000822B981EB823013351F00F0C4086E00000000000013351F0013351F053870D7500B22A8C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7204 4801 193 193 4 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','1240162 1240162 1244770 1244770 1244959 1244961 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962 1244962','942 1520 3024 3531 3791 40098 122274 149540 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1237296 1244962',X'1703080908010703030408030808010308070408040803018D4F07406FE000000000000602CF16DE05013303C300FFFF0300FFFFC0F869E000000000013303C3008000004BD756');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','9608 6098 5910 5910 5910 2149 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','1264939 1264939 1264939 1264939 1264939 1265217 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365 1267365','952 1536 3055 3576 3841 40616 124132 151876 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1259547 1267365',X'1703080808080704030408090808010101070404040803018D61401C0000000000000131374D1726AB0132DD780F0C0540390000000000000132DD780132DD784E2789C6190CA9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','6636 4411 4237 4237 4237 33 8 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1','1294580 1294580 1294580 1294580 1294580 1297842 1297862 1297862 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869','964 1557 3097 3622 3888 41246 126538 154868 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1289863 1297869',X'1703080808080703030408090808010308070808080803018D7840420000000000007332F1227B5901321AF80800FFFF40260000000000006D0CEB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7822 4817 260 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','2375708 2375708 2380265 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524 2380524','3423 5117 10120 11184 11775 86237 242937 294878 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2366404 2380524',X'170308090308070304040803080801030807040408080401B1F501831C405C40000000000077646800EAD44C0132697000FFFF0300FFFFC0F869E0000000000132696F0132696F0094935E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','1403 1022 82 74 74 55 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','2594767 2594767 2595707 2595707 2595707 2595708 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739','3914 5817 11518 12879 13613 98100 278304 342850 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2580971 2595739',X'170308090808070303040803080801030807080808080301B648405C400000000000038EAC243F770131A6F700FFFF0E00FFFFC0F869E0000000005C8664');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7901 5298 291 55 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','3424107 3424107 3429114 3429350 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404 3429404','5872 8944 17727 19769 20846 145206 401995 498360 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3411279 3429404',X'170308090201070404040803080801030807040808080401C3F9232807405C40000000000000FF559400F2FA440133294600FFFF0300FFFFC0F869E000000000013329460083157B');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7483 5407 230 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','3549446 3549446 3554623 3554845 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852 3554852','5932 9041 17921 20139 21264 149060 417718 520064 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3536268 3554852',X'170308090201070404040803080801030807040808080401C449232807405C40000000000000FF559400F2FA440133294600FFFF0300FFFFC0F869E000000000013329460083162A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','12076 8365 7981 5546 5546 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','3558079 3558079 3558079 3558079 3558079 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624 3563624','5935 9046 17930 20154 21279 149235 418443 521109 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3544970 3563624',X'170308080808070303040803080801030107040408080301C44E4074300000000000029EB13BB98E01328EFE00FFFF0E00FFFF0AC0F869E00000000001328F0001328F0071D802');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','1123 916 875 875 875 93 22 22 1 1 1 1 1 1 1 1 1 1 1 1 1 1','3892913 3892913 3892913 3892913 3892913 3893531 3893602 3893602 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609','6594 10057 19942 22653 23964 165533 468498 587524 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3873325 3893609',X'170308080808070403040802080801030107040404080301CA28403F00000000000000832DC71933330132DB8800FC0F00FFFF054050C000000000000132DB870132DB8A4D5E35A2107205');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','9344 6359 6022 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','4213510 4213510 4213510 4219523 4219523 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531 4219531','7200 11025 21872 25036 26543 181203 517345 652858 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4198057 4219531',X'170308080308070303040803080801030807080808080301CE8C0186A0403F00000000000002C2142F19870131554100FFFF0F00FFFFC0F869E0000000000687F7');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','8062 4993 241 53 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','5037060 5037060 5041812 5042000 5042051 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052 5042052','10201 15484 30736 35196 37326 231854 654010 829510 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5017193 5042052',X'17030809020107030404080308080103080704080808030213B2245407406FE0000000000002C0AA00F6ABA101332A7E00FFFF0300FFFFC0F869E00000000001332A7E5287C0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','9125 5761 264 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','5046490 5046490 5051987 5052246 5052246 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250 5052250','10203 15488 30744 35230 37365 232178 654964 830718 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5027358 5052250',X'17030809030807030304080308080103080708080808030213B501831F406FE00000000000029E627136F901328DD500FFFF0300FFFFC0F869E000000000290DD6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','591 591 566 566 566 27 18 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1','5190991 5190991 5190991 5190991 5190991 5191461 5191464 5191474 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479','10649 16115 31973 36593 38793 239550 673441 853755 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5165855 5191479',X'17030808080807030404080108080101080708080808040244AD4045800000000000029E6200EAE8740132690D0A0F0C402E00000000000000941EAF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7381 3728 83 16 16 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','6112248 6115901 6119546 6119613 6119613 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628 6119628','13780 20707 41067 47538 50438 296473 834203 1056686 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6089878 6119628',X'170309090208070304040809080801010807040404080402A9D12328407360000000000002172300EABCFF0133517F0F0C404AA6665E02EA960133517F01335239538BC5C1008B5EC9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7569 5785 274 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','6280084 6280084 6285595 6285867 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868 6285868','14559 21811 43240 50020 53057 307264 862871 1092992 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6255378 6285868',X'170308090301070404040803080801030807040808080302C14C01832707405C40000000000000FF559400F2FA440133294600FFFF0300FFFFC0F869E000000000013329461E5109');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','8289 5734 266 33 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','6290764 6290764 6296232 6296465 6296495 6296495 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497 6296497','14569 21827 43272 50056 53096 307563 864043 1094585 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6265950 6296497',X'170308090201070404040803080801030807040808080402C164232807405C40000000000000FF559400F2FA440133294600FFFF0300FFFFC0F869E0000000000133294600A90415');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','2209 1450 1369 1369 1369 597 13 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1','6489075 6489075 6489075 6489075 6489075 6489139 6489344 6489344 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349','15377 22998 45584 52641 55837 319564 895653 1134252 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6458046 6489349',X'170308080808070303040802080801030807040404080302EA59401C0000000000000D4B201AD8B30132DF0400FC0600FFFF40180000000000000132DF040132DF054EBA338714FBBA');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7381 5446 171 11 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','7314420 7314420 7319695 7319855 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865 7319865','18403 27319 54152 62541 66337 369851 1032180 1306261 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7284923 7319865',X'1703080902010703030408030808010308070408080804049EE503E907405C400000000000029EC32E97BE0133061F00FFFF0300FFFFC0F869E0000000000133061F00A9ECEF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','7163 4782 209 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','7652849 7652849 7657422 7657626 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630 7657630','19462 28830 57147 65962 69945 388539 1084446 1372423 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7621247 7657630',X'17030809020107040404080308080103080704080808040503EB03EA07405C40000000000000FF559400F2FA440133294600FFFF0300FFFFC0F869E0000000000133294600831554');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','1433 582 571 571 571 15 15 15 1 1 1 1 1 1 1 1 1 1 1 1 1 1','7785842 7786693 7786693 7786693 7786693 7787208 7787208 7787208 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219','20001 29582 58632 67649 71736 396840 1107204 1400830 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7750264 7787219',X'1703090808080703040408090808010108070404040804055010406E80000000000001C7A600FC429201332D890F0C402C00000000000001332D890133505352D7248A008775D8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','2247 682 657 657 657 117 45 45 1 1 1 1 1 1 1 1 1 1 1 1 1 1','9083272 9084837 9084837 9084837 9084837 9085088 9085088 9085088 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089','24940 36384 72113 82665 87632 474987 1318830 1664527 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9042708 9085089',X'17030908080807030304080208080103010704040408031A620A403E00000000000001C3D332CF850132DE9D00FC0200FFFF054050E000000000000132DE9D0132DE9D4E8C092E790553');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix7','107 77 69 69 69 11 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1','10382912 10382942 10382942 10382942 10382942 10382952 10382956 10382956 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959','31251 45639 90416 102896 109225 567280 1544395 1943661 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10337289 10382959',X'170409080808070303040809080801010807040404080300955501403E000000000000029E686E250B01332D220F0C402E00000000000001332D220133504E52CFCF4F21CD7F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7337 4975 19 19 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 206462 206462 210789 210789 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807 210807','0 0 0 125 164 112407 128579 207456 209489 209490 209490 209490 209490 209490 209490 209490 209490 209490 209490 209490 209490 210807',X'170808080308040804030807080301030807080808080301878401317F26024FD3531E2DAF404200000000000000FFFF0200FFFFC0F869E0000000002642C4');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 8874 8785 45 25 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 1221012 1221012 1224145 1224165 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189 1224189','0 0 0 931 1117 679896 773861 1207582 1216653 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1216673 1224189',X'1708080803080409040408070809010308070404040803018D3F0133023D01940C420106B14D40420000000000000F00FFFF4070E000000000000133023D0133023D4F2053654AE525');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7200 7200 39 15 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 1239793 1239793 1242199 1242223 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237 1242237','0 0 0 942 1131 688381 783660 1225441 1234602 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1234622 1242237',X'1708080803080409040308070809010301070404040804018D4F0132DB820113FAB92F19C5401C0000000000000F00FFFF0F403A0000000000000132DB820132DB824D542A4F009B5C72');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 9606 9606 44 25 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 1264560 1264560 1266149 1266168 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192 1266192','0 0 0 952 1145 699479 796008 1249062 1258369 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1258391 1266192',X'1708080803080409040308070809010101070404040803018D61013305B9017CDF4916F8B140280000000000000F0C0A403600000000000001330557013305B9507EA8DD212E79');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 6635 6635 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 1294199 1294199 1297868 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869 1297869','0 0 0 964 1159 713241 812408 1280435 1289849 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1289871 1297869',X'1708080803080409030308070803010308070808080803018D78013267DC0EF747387106403F00000000000000FFFF0F00FFFFC0F869E00000000006B968');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7815 6624 26 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 2374939 2374939 2380559 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584 2380584','0 0 0 3423 3833 1371803 1539928 2350493 2366415 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2366460 2380584',X'170808080308040903030807080301030807080808080301B1F501317F160DDEA51C46AB405280000000000000FFFF0F00FFFFC0F869E0000000006ADDA9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 3595 3459 10 7 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 2595336 2595336 2595734 2595737 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739 2595739','0 0 0 3918 4433 1512586 1693312 2557025 2580917 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2580967 2595739',X'170808080308040903030807080101010107040404080301B64F01332ACC029E833D19084028000000000000320F0C054074A0000000000001332A0901332ACC518A316F1F30D6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7894 6061 26 10 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 3422592 3422592 3424418 3424434 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443 3424443','0 0 0 5872 6630 2032185 2267240 3366727 3406294 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3406363 3424443',X'170808080308040904030807080101030807040404080401C3F90132B7A100D6FD7F6CE97F4070500000000000100F00FFFF40560000000000000132B7310132DB2D4D3F72D9009AE8EE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7479 6158 22 22 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 3547845 3547845 3553619 3553619 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640 3553640','0 0 0 5932 6752 2099067 2343370 3491679 3534981 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3535053 3553640',X'170808080308040804030807080301030807080808080301C4490131573F008E7AC03077E0403E00000000000000FFFF0F00FFFFC0F869E00000000065B45A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 12068 8257 37 17 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 3556474 3556474 3558426 3558446 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462 3558462','0 0 0 5935 6758 2101744 2346448 3496404 3539797 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3539869 3558462',X'170808080308040904030807080201010107040404080301C44E0132DD09016891C11788EB404200000000000000FC0F0C0F400199999999999A0132DD090132DD094DE5DCE3763299');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 1510 1497 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 3892149 3892149 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609 3893609','0 0 0 6596 7614 2306358 2572095 3823194 3873232 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3873319 3893609',X'170808080308040803030807080301030807080808080301CA2E01312E3A038EE6257265400000000000000000FFFF0600FFFFC0F869E00000000032518F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 9341 7313 41 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 4211282 4211282 4217204 4217243 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244 4217244','0 0 0 7200 8390 2502650 2792131 4139620 4195704 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4195798 4217244',X'170808080308040903030807080301030807080808080301CE8C01317DE8029E9D3B6D23403E00000000000000FFFF0F00FFFFC0F869E00000000008EF7D');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 8055 3290 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 5033936 5033936 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225 5037225','0 0 0 10201 11915 3045026 3383481 4941323 5012256 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5012369 5037225',X'17080808030804090303080708030803080708080808030213B20130B83A018A7516DF86403600000000000000FFFF00FFFFC0F869E00000000028F8CD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 9117 2923 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 5043358 5049528 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450 5052450','0 0 0 10203 11926 3054947 3394812 4956408 5027439 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5027552 5052450',X'17080808030204080303080708030103080708080808030213B5232A013107F00217231745AF403300000000000000FFFF0200FFFFC0F869E0000000002B57DE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 333 333 4 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 5191271 5191271 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479 5191479','0 0 0 10672 12452 3147059 3494842 5093162 5165710 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5165827 5191479',X'17080808030804080303080708030103080708080808030244F00131CB4201C1C917188E403F00000000000000FFFF0800FFFFC0F869E00000000001C5CB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7374 4684 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 6107894 6107894 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577 6112577','0 0 0 13780 16308 3748117 4153600 5995776 6082692 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6082834 6112577',X'170808080308040803030807080301030807080808080402A9D1013108B5029E6831A21C401C00000000000000FFFF0200FFFFC0F869E0000000000092F5C6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7562 7376 28 11 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 6275521 6275521 6277274 6277291 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301 6277301','0 0 0 14559 17217 3855913 4271255 6156622 6246677 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6246823 6277301',X'170808080308040904030807080201030107040404080302C14C0132DBF100832DC739FF53403F00000000000000FC0F00FFFF05402C0000000000000132DBF10132DBF34D89B7AA1114CB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 8282 7043 28 28 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 6286191 6286191 6292277 6292277 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304 6292304','0 0 0 14569 17229 3862314 4278722 6171137 6261611 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6261757 6292304',X'170808080308040804030807080301030807080808080302C16401317CC301B5AA4A1AB270405680000000000000FFFF0F00FFFFC0F869E00000000048FD4C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 3101 3101 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 6486495 6486495 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349 6489349','0 0 0 15379 18149 3988504 4417216 6363796 6457879 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6458036 6489349',X'170808080308040803030807080301030807080808080302EA6601317F7301C3EB17C4CE401C00000000000000FFFF0F00FFFFC0F869E000000000398198');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 7379 6798 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 7308682 7308682 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479 7315479','0 0 0 18403 21722 4531809 5008068 7172304 7280343 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7280536 7315479',X'1708080803080408040308070803010308070808080803049EE501310667008390D0176DC9404380000000000000FFFF0500FFFFC0F869E0000000005ED2B5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 211 211 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 7787150 7787150 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219','0 0 0 20027 23602 4837427 5341824 7634169 7750020 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7750224 7787219',X'17080808030804080303080708090103080704040808030550B30132B73501A79C17C3D840340000000000000F00FFFF40140000000000000132B6D00132B7400D1DB9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 26 26 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 9085083 9085083 9085088 9085088 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089 9085089','0 0 0 24964 29171 5671673 6246216 8904392 9042441 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9042684 9085089',X'17080808030804080303080708030103080708080808031AA25C0131CA197A6D831B1D84401C00000000000000FFFF0F00FFFFC0F869E00000000071E307');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','11668540 11668540 11668540 1261 1261 9 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','0 0 0 10381872 10381872 10382956 10382956 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959 10382959','0 0 0 31297 36344 6545140 7198586 10180602 10336983 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10337266 10382959',X'17080808040804080303080708010101080708080808030095A4C401321BB603010141073C401C000000000000080F0C40308000000000007E55BE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix6','12287 12287 12287 17 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1','11668540 11668540 11668540 11672111 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127 11672127','1 1 1 43128 49243 7431284 8170740 11452355 11624787 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11625093 11672127',X'1701080803020408040409070803010308070408040804070267C127130133041E00AB540900E64074405C40000000000000FFFF0300FFFFC0F869E0000000000133041E0080000000A3EE16');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 24157 24157 29 29 1 1 1 1','0 0 0 1236590 1236590 1251799 1251799 1251827 1251827 1251827 1251827','0 0 0 3120 4771 71527 72755 1238782 1238782 1238782 1251827',X'0C08080803040308040808031A080400FF5594029BB30131CD372B1368');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 1209 1209 129 129 1 1 1 1','0 0 0 1296873 1296873 1297744 1297744 1297869 1297869 1297869 1297869','0 0 0 3203 4902 74577 75831 1284645 1284645 1284645 1297869',X'0C08080803030308040808041A215F04C9D901B0A30131066F0087162A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 12129 9261 428 428 1 1 1 1','0 0 0 1790076 1790076 1790076 1790076 1790503 1790503 1790503 1790503','0 0 0 4938 7561 108783 110860 1774402 1774402 1774402 1790503',X'0C08080803030308040808031CE87E029E6B018700013219C1373BA8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 273 273 5 5 1 1 1 1','0 0 0 2595625 2595625 2595738 2595738 2595739 2595739 2595739 2595739','0 0 0 7863 12132 166423 169877 2575172 2575172 2575172 2595739',X'0C08080803030308040808042F020502DA0E02C73A0133049700A711F8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 7735 7735 29 29 1 1 1 1','0 0 0 2924165 2924165 2929754 2929754 2929782 2929782 2929782 2929782','0 0 0 9123 14045 191316 195293 2907742 2907742 2907742 2929782',X'0C080808030403080408080333776300FF559475928D013329435A824F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 360 229 27 27 1 1 1 1','0 0 0 3893374 3893505 3893605 3893605 3893609 3893609 3893609 3893609','0 0 0 13912 21124 273265 278784 3866425 3866425 3866425 3893609',X'0C08080803030308040808044717710E070B01E88F0131A5B80098D0A5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 683 671 32 32 1 1 1 1','0 0 0 5191447 5191459 5191477 5191477 5191479 5191479 5191479 5191479','0 0 0 20361 30776 381171 389104 5158962 5158962 5158962 5191479',X'0C08080803030308040808037370D10F00290188450132B537354C5E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 213354 213354 113 113 1 1 1 1','0 0 0 6102233 6102233 6155183 6155183 6155295 6155295 6155295 6155295','0 0 0 26812 39489 472780 483115 6119281 6119281 6119281 6155295',X'0C080808040403080408080300F2FA4400FF5594019B6D01324067714AE8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 3675 3675 241 241 1 1 1 1','0 0 0 6486701 6486701 6489287 6489287 6489349 6489349 6489349 6489349','0 0 0 28380 41383 502486 513750 6452691 6452691 6452691 6489349',X'0C080808040304080408080300F6ABAC6C967D009F84B60132DBDD155A2A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 24063 24063 38 38 1 1 1 1','0 0 0 7080911 7080911 7102380 7102380 7102417 7102417 7102417 7102417','0 0 0 35724 49844 582928 595393 7065320 7065320 7065320 7102417',X'0C0808080404040804080803010B0A5C00FF559400CCD3EE0133294559DDCD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 7173953 7173950 20284 20284 38 38 1 1 1 1','0 0 0 7107237 7107237 7126606 7126606 7126643 7126643 7126643 7126643','0 0 0 35764 49885 586473 599030 7089546 7089546 7089546 7126643',X'0C0808080404040804080803010B3C6700FF55940114CE73013329432225CC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 630326 618063 11855 11855 3 1 1 1 1 1','0 7173953 7173953 7665258 7665258 7672559 7672561 7672561 7672561 7672561 7672561','0 1 2 64320 91213 1077048 1100563 7630436 7630436 7630436 7672561',X'0C080908040403020408080400F2FA4400FF559405DD0A23280132DD75009F3468');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 630326 618063 4 4 1 1 1 1 1 1','0 7173953 7173953 7787218 7787218 7787219 7787219 7787219 7787219 7787219 7787219','0 1 2 74034 102405 1189484 1215034 7745001 7745001 7745001 7787219',X'0C0809080403040804080803010DDABE0368B700FEBFF60133068F22D1E5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','7804279 630326 12263 1433 1433 1 1 1 1 1 1','0 7173953 7792016 7797877 7797877 7799309 7799309 7799309 7799309 7799309 7799309','0 1 3 76213 104626 1201397 1227113 7757087 7757087 7757087 7799309',X'0C08090104040408040808030700F2FA4400FF559401C3969E013329461E5052');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 8657 8657 605 605 1 1 1 1','7804279 7804279 7804279 8360485 8360485 8364643 8364643 8365247 8365247 8365247 8365247','1 2 4 77865 106290 1217527 1243460 8321843 8321843 8321843 8365247',X'0C09080803030308040808031AD369021FFC0DC36E01328E2F3D9128');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 33421 33421 57 26 1 1 1 1','7804279 7804279 7804279 8873934 8873934 8891008 8891039 8891064 8891064 8891064 8891064','1 2 4 78914 107340 1231432 1257622 8846644 8846644 8846644 8891064',X'0C090808030403020408080433776300FF55940CE166232801332A1800836DFB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 2182 2182 94 94 1 1 1 1','7804279 7804279 7804279 9083187 9083187 9085006 9085006 9085089 9085089 9085089 9085089','1 2 4 79307 107733 1237174 1263453 9040316 9040316 9040316 9085089',X'0C09080803030308040808033AC92C021FFC04AF670132B5FC38DBD0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 6023 6023 50 50 1 1 1 1','7804279 7804279 7804279 9393480 9393480 9395815 9395815 9395864 9395864 9395864 9395864','1 2 4 79973 108402 1245294 1271679 9350536 9350536 9350536 9395864',X'0C09080803030308040808034CDD9A7E1C2301C30D0132908703CC11');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 79 79 6 6 1 1 1 1','7804279 7804279 7804279 10382946 10382946 10382956 10382956 10382959 10382959 10382959 10382959','1 2 4 82501 110947 1276304 1303221 10336296 10336296 10336296 10382959',X'0C090808040303080408080400E9A10A0CC31602A47201332C6C00ABCD60');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 8251 8251 280 280 1 1 1 1','7804279 7804279 7804279 11067468 11067468 11072690 11072690 11072969 11072969 11072969 11072969','1 2 4 85529 113989 1307763 1335183 11025935 11025935 11025935 11072969',X'0C090808040404080408080300FFA4A7008A66AD0089DBDD0132B5FF6AE2B8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 44772 44772 58 58 1 1 1 1','7804279 7804279 7804279 11268036 11268036 11269261 11269261 11269318 11269318 11269318 11269318','1 2 4 87132 115597 1321101 1348736 11222284 11222284 11222284 11269318',X'0C0908080404030804080804010B0A5C00FF5594018ACD0133294300B0E291');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 53775 53775 37 19 1 1 1 1','7804279 7804279 7804279 11330929 11330929 11355027 11355045 11355063 11355063 11355063 11355063','1 2 4 87335 115800 1327560 1355328 11308029 11308029 11308029 11355063',X'0C0908080404030204080803010BFA8100FF5594026C24232801332B2E1F8BB5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 3718791 3718791 39430 39430 39 39 1 1 1 1','7804279 7804279 7804279 11458789 11458789 11476757 11476757 11476795 11476795 11476795 11476795','1 2 4 88502 116970 1340484 1368500 11429761 11429761 11429761 11476795',X'0C09080804040308040808030113B99600FF559402F6AF01332B2E1F8A54');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix5','3876548 157757 157736 5317 5317 1 1 1 1 1 1','7804279 11523070 11523070 11652823 11652823 11658139 11658139 11658139 11658139 11658139 11658139','1 3 5 100045 128561 1480184 1510648 11611105 11611105 11611105 11658139',X'0C0909080404040204080803010BFA8100FF559402594CCB2328013351E85B0F5D');
+} {}
+do_execsql_test whereJ-1.3 {
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 4748 4629 308 1 1 1 1 1','0 0 0 634880 634880 639119 639426 639426 639426 639426 639426','0 0 0 779 907 26900 633919 633920 633920 633920 639426',X'0C0808080308040403080803018C4A00F450A80132671F029EAC271DA5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 4608 4608 246 1 1 1 1 1','0 0 0 752580 752580 755578 755823 755823 755823 755823 755823','0 0 0 936 1089 31876 749384 749385 749385 749385 755823',X'0C0808080308030403080804018D4F347FD20131547D018A7500936FE2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 4776 4776 240 1 1 1 1 1','0 0 0 762070 762070 764545 764784 764784 764784 764784 764784','0 0 0 940 1097 32187 758274 758275 758275 758275 764784',X'0C0808080308030403080804018D562E0EAB0131A68F029E9300994D36');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5910 5910 257 1 1 1 1 1','0 0 0 771353 771353 772646 772902 772902 772902 772902 772902','0 0 0 946 1103 32460 766333 766334 766334 766334 772902',X'0C0808080308030404080803018D611726F40130E13C00822B980302A0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 1834 1834 46 1 1 1 1 1','0 0 0 1296866 1296866 1297832 1297869 1297869 1297869 1297869 1297869','0 0 0 2621 2842 56932 1287063 1287064 1287064 1287064 1297869',X'0C080808030803040308080301A6E33010BC01317F2005516E3932DB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5007 3959 317 1 1 1 1 1','0 0 0 2152400 2152400 2153781 2154097 2154097 2154097 2154097 2154097','0 0 0 5683 6304 111431 2139450 2139457 2139457 2139457 2154097',X'0C080808030803040308080301C3F91E114001315549029F74635F36');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 4768 4609 397 1 1 1 1 1','0 0 0 2200466 2200466 2200700 2201096 2201096 2201096 2201096 2201096','0 0 0 5714 6370 114266 2186220 2186227 2186227 2186227 2201096',X'0C080808030803040308080401C42217887E01312D72036D5B009AF3F8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5177 4669 264 1 1 1 1 1','0 0 0 2234078 2234078 2235865 2236128 2236128 2236128 2236128 2236128','0 0 0 5743 6422 116499 2221108 2221115 2221115 2221115 2236128',X'0C080808030803040308080401C4491EF49D013155450D4B200091BFB3');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 7981 5546 272 1 1 1 1 1','0 0 0 2239923 2239923 2242583 2242854 2242854 2242854 2242854 2242854','0 0 0 5746 6428 116800 2227775 2227782 2227782 2227782 2242854',X'0C080808030803040308080301C44E2EFF35013109E67764687E36D6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 351 336 15 1 1 1 1 1','0 0 0 2595725 2595725 2595725 2595739 2595739 2595739 2595739 2595739','0 0 0 6747 7717 144364 2578697 2578708 2578708 2578708 2595739',X'0C080808030803040308080401CCC91782C20131CC1902D7C0008E7A4F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 6022 4942 304 1 1 1 1 1','0 0 0 2648510 2648510 2648849 2649152 2649152 2649152 2649152 2649152','0 0 0 6934 7959 149030 2631886 2631900 2631900 2631900 2649152',X'0C080808030803040308080301CE8C17887E01312D6E036D5B424939');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 4752 1965 5 1 1 1 1 1','0 0 0 3161780 3161780 3163740 3163744 3163744 3163744 3163744 3163744','0 0 0 9707 11219 194477 3143873 3143896 3143896 3143896 3163744',X'0C08080803080404030808030213B20116290C01332B9D0270102494C2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5497 1880 3 1 1 1 1 1','0 0 0 3167643 3167643 3169520 3169522 3169522 3169522 3169522 3169522','0 0 0 9709 11226 194760 3149632 3149655 3149655 3149655 3169522',X'0C08080803080404040808030213B5010B0A5C0133294500FF55941DCA72');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 2185 2126 134 1 1 1 1 1','0 0 0 3891669 3891669 3893607 3893609 3893609 3893609 3893609 3893609','0 0 0 13596 15952 257364 3869701 3869733 3869733 3869733 3893609',X'0C080808030804040308080302BD5F01010F13013350517B8BF356D913');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5511 5355 345 1 1 1 1 1','0 0 0 3931849 3931849 3931954 3932298 3932298 3932298 3932298 3932298','0 0 0 13793 16188 260160 3908172 3908206 3908206 3908206 3932298',X'0C080808030803040308080402C14C17887E01312D75036D5B009AF4B1');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5468 5115 310 1 1 1 1 1','0 0 0 3939090 3939090 3939894 3940203 3940203 3940203 3940203 3940203','0 0 0 13803 16200 260571 3916034 3916068 3916068 3916068 3940203',X'0C080808030803040308080302C164191E7F0131A4FC0606F30CE25A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 5275 4792 252 1 1 1 1 1','0 0 0 4590216 4590216 4591340 4591591 4591591 4591591 4591591 4591591','0 0 0 17456 20455 315887 4563939 4563981 4563981 4563981 4591591',X'0C0808080308030403080804049EE51EB67B01317C5204C9D900AE08CD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 339 339 20 1 1 1 1 1','0 0 0 5191257 5191257 5191475 5191479 5191479 5191479 5191479 5191479','0 0 0 20754 24230 366257 5160652 5160701 5160701 5160701 5191479',X'0C08080803080404030808030CEE9300E50D8D013242BE018A755F6BC3');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 7173953 7173950 13 13 1 1 1 1 1 1','0 0 0 6489337 6489337 6489349 6489349 6489349 6489349 6489349 6489349','0 0 0 29449 33987 481632 6453309 6453378 6453378 6453378 6489349',X'0C08080804080304030808030091B3AB600E9801323F54029F7403EF0C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 630326 618063 10 9 1 1 1 1 1 1','0 7173953 7173953 7787217 7787217 7787219 7787219 7787219 7787219 7787219 7787219','0 1 2 74978 85458 1107555 7744920 7744997 7744997 7744997 7787219',X'0C0809080408030403080804014F459B48960801332ADC01C7A600AA1BA3');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','7804279 630326 12263 17 1 1 1 1 1 1 1','0 7173953 7792016 7795570 7795586 7795586 7795586 7795586 7795586 7795586 7795586','0 1 3 78329 89057 1115864 7753287 7753364 7753364 7753364 7795586',X'0C0809010302040404080804070267C1271300E640740133041E00AB540900A3EE16');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','3876548 3718791 3718791 397 397 46 1 1 1 1 1','7804279 7804279 7804279 9084892 9084892 9085088 9085089 9085089 9085089 9085089 9085089','1 2 4 87584 99143 1159195 9040539 9040616 9040616 9040616 9085089',X'0C090808030804040308080301CBAF00E5C288013351810C390F26671E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','3876548 3718791 3718791 141 141 20 1 1 1 1 1','7804279 7804279 7804279 10382921 10382921 10382951 10382959 10382959 10382959 10382959 10382959','1 2 4 95054 107437 1210217 10336699 10336776 10336776 10336776 10382959',X'0C09080803080304030808030DB5C14114E301332A7E018A751F17C1');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix4','3876548 157757 157736 83 16 1 1 1 1 1 1','7804279 11523070 11523070 11587733 11587800 11587815 11587815 11587815 11587815 11587815 11587815','1 3 5 112412 126326 1332792 11540704 11540781 11540781 11540781 11587815',X'0C090908030204040308080402A9D12328010B92880133517F029E9D008B5B23');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 213279 213279 213279 213279 376 212 101 1 1','0 0 12394 12394 12394 12394 18421 18421 18421 18424 18424','0 0 3 3 3 3 810 1027 1949 18324 18424',X'0C0808030808080308030403018A75018788178574013265FC611E2A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 86515 86515 86515 86515 377 377 377 1 1','0 0 271240 271240 271240 271240 274521 274521 274521 274780 274780','0 0 8 8 8 8 9766 10762 19602 273178 274780',X'0C080803080808030803040401A79C01886F174AEF0132678B00A4AEFF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 193482 193482 193482 193482 444 444 444 1 1','0 0 542802 542802 542802 542802 575453 575453 575453 575505 575505','0 0 27 27 27 27 24310 25597 43165 571774 575505',X'0C080803080808030803040401C3EB018CCE55312F013305B700AF487C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 126108 126108 126108 126108 200 200 28 1 1','0 0 747801 747801 747801 747801 762121 762121 762293 762320 762320','0 0 33 33 33 33 30770 32410 56629 757315 762320',X'0C080803080808030803040301C7A6018A9D6AB1100131F17D04364A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 149712 149712 149712 149712 216 216 5 1 1','0 0 1107747 1107747 1107747 1107747 1147090 1147090 1147301 1147305 1147305','0 0 98 98 98 98 51603 53785 94043 1139967 1147305',X'0C080803080808030803040302172301B84D2E828501317B95651924');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 87289 87289 87289 87289 27 27 12 1 1','0 0 1257751 1257751 1257751 1257751 1297850 1297850 1297865 1297869 1297869','0 0 100 100 100 100 57625 60123 107069 1289807 1297869',X'0C0808030808080308040403021FFC02554D00E643C7013242692A9BBC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 192597 192597 192597 192597 291 291 3 1 1','0 0 1464077 1464077 1464077 1464077 1512946 1512946 1513234 1513236 1513236','0 0 134 134 134 134 67476 70262 123260 1502886 1513236',X'0C0808030808080308040404029E6201B94000F8341701330419008B74DA');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 196917 196917 196917 196917 326 326 12 1 1','0 0 1677739 1677739 1677739 1677739 1797333 1797333 1797647 1797658 1797658','0 0 136 136 136 136 76717 80026 143308 1785522 1797658',X'0C0808030808080308040404029E6802B7DC0108ACBA0132DD7B0087795F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 140297 140297 140297 140297 206 188 90 1 1','0 0 1951850 1951850 1951850 1951850 2020506 2020524 2020622 2020711 2020711','0 0 146 146 146 146 85439 89181 160816 2006993 2020711',X'0C0808030808080302040403029E8102171603E800FA82B20132B5AD384ECD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 132041 132041 132041 132041 173 173 20 1 1','0 0 2415706 2415706 2415706 2415706 2428962 2428962 2429115 2429134 2429134','0 0 163 163 163 163 100984 105182 186071 2412732 2429134',X'0C0808030808080308030403029EB1018BF55020D60131A3D23BB156');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 6830 6830 6830 6830 35 35 35 1 1','0 0 2595444 2595444 2595444 2595444 2595711 2595711 2595711 2595739 2595739','0 0 169 169 169 169 108584 113037 199947 2578183 2595739',X'0C0808030808080308030403029EC001878C17A7520131A43A6E513C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 101615 101615 101615 101615 244 33 33 1 1','0 0 2999185 2999185 2999185 2999185 3019255 3019466 3019466 3019498 3019498','0 0 213 213 213 213 131035 136293 237357 2999333 3019498',X'0C080803080808030203040302D7C001B93E03F041A96D013303017C72B6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 26914 26914 26914 26914 87 87 73 1 1','0 0 3868553 3868553 3868553 3868553 3893568 3893568 3893582 3893609 3893609','0 0 387 387 387 387 172456 180121 311050 3869040 3893609',X'0C08080308080803080304040498C86B7EF347CC5A0132B678008A4646');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 97492 97492 97492 97492 410 410 44 1 1','0 0 4948831 4948831 4948831 4948831 5009456 5009456 5009822 5009865 5009865','0 0 751 751 751 751 226745 236274 399260 4980988 5009865',X'0C08080308080803080404030E565F03686C00F89CD001332A115479DC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 1782 1782 1782 1782 52 52 52 1 1','0 0 5190938 5190938 5190938 5190938 5191434 5191434 5191434 5191479 5191479','0 0 800 800 800 800 236362 246237 414618 5161779 5191479',X'0C08080308080803080304040EBD0F0268E064A6DB013242CB00875BAF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 4645 4645 4645 4645 17 17 17 1 1','0 0 6485214 6485214 6485214 6485214 6489339 6489339 6489339 6489349 6489349','0 0 1396 1396 1396 1396 317293 329836 538486 6453712 6489349',X'0C080804080808040803040300ABD9B000A69D33737CBB01328F6B0F6298');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 7173953 290196 290196 290196 290196 138 138 26 1 1','0 0 6734628 6734628 6734628 6734628 6744872 6744872 6744984 6745009 6745009','0 0 1641 1641 1641 1641 338986 352099 569319 6708651 6745009',X'0C080804080808030804040400FF5594018819010B3C670133294300B1674C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','7804279 630326 24310 1435 1435 1435 1 1 1 1 1','0 7173953 7763625 7786500 7786500 7786500 7787219 7787219 7787219 7787219 7787219','0 1 3518 4119 4119 4119 726018 753845 1215054 7745002 7787219',X'0C080904010808030204040400FF5594070357AB232800F2FA440133294600A90441');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 3718791 114188 114188 114188 114188 463 61 61 1 1','7804279 7804279 7809377 7809377 7809377 7809377 7864755 7865157 7865157 7865217 7865217','1 2 3751 4413 4413 4413 740133 768607 1233927 7822883 7865217',X'0C0908030808080302030403018A750213B2245416DF860131CB3A2879CC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 3718791 86108 86108 86108 86108 605 605 605 1 1','7804279 7804279 8403815 8403815 8403815 8403815 8463845 8463845 8463845 8464449 8464449','1 2 3805 4467 4467 4467 760796 789718 1255047 8421249 8464449',X'0C0908030808080308030403021FFC0DC36E1AD36901328E2F3D9128');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 3718791 60490 60490 60490 60490 44 44 44 1 1','7804279 7804279 9062901 9062901 9062901 9062901 9085056 9085056 9085056 9085089 9085089','1 2 3845 4507 4507 4507 778931 808217 1273550 9040805 9085089',X'0C0908030808080308040403029EB101C30B00E889140132697F092505');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 3718791 57304 57304 57304 57304 2 2 2 1 1','7804279 7804279 10328144 10328144 10328144 10328144 10382958 10382958 10382958 10382959 10382959','1 2 4106 4768 4768 4768 823966 853934 1319290 10336860 10382959',X'0C09080308080804080404040E565F00CE8C3100E7F68D0133517900B0B4CE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 3718791 174377 174377 174377 174377 81 81 66 1 1','7804279 7804279 11246252 11246252 11246252 11246252 11358749 11358749 11358764 11358829 11358829','1 2 4517 5179 5179 5179 867595 898252 1363807 11311829 11358829',X'0C090804080808030804040300FF55940E584300F2FA44013240670E2E89');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix3','3876548 157757 15066 15066 15066 15066 1 1 1 1 1','7804279 11523070 11657249 11657249 11657249 11657249 11672314 11672314 11672314 11672314 11672314','1 3 5503 6167 6167 6167 1025351 1059076 1524822 11625280 11672314',X'0C090904080808040203040300FF5594025D19032328337763013351F2277A3E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 4748 4629 4629 4629 4629 17 1 1 1','0 0 634880 634880 634880 634880 634880 639175 639191 639191 639191','0 0 779 907 907 907 907 415429 633684 633685 639191',X'0C0808030808080804030303018C4A013157983CE7FE05E54B6BB7FB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 4608 4608 4608 4608 4608 33 1 1 1','0 0 752580 752580 752580 752580 752580 755506 755538 755538 755538','0 0 936 1089 1089 1089 1089 491324 749126 749127 755538',X'0C0808030808080804030303018D4F0131A682512ECF029E6A70A9E9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 4776 4776 4776 4776 4776 27 1 1 1','0 0 762070 762070 762070 762070 762070 763357 763383 763383 763383','0 0 940 1097 1097 1097 1097 495628 756890 756891 763383',X'0C0808030808080804040303018D560132DB8800F3416D029EA654D9AC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5910 5910 5910 5910 5910 37 2 2 1','0 0 771353 771353 771353 771353 771353 775562 775597 775597 775598','0 0 946 1103 1103 1103 1103 501111 769013 769014 775598',X'0C0808030808080804030303018D610131CADE4C6E2D01D5860141A9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 1834 1834 1834 1834 1834 6 1 1 1','0 0 1296866 1296866 1296866 1296866 1296866 1297867 1297869 1297869 1297869','0 0 2621 2842 2842 2842 2842 857867 1287063 1287064 1297869',X'0C080803080808080403030301A6E30132419A1E1B7F0567EE2880C0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5007 3959 3959 3959 3959 25 1 1 1','0 0 2152400 2152400 2152400 2152400 2152400 2155551 2155575 2155575 2155575','0 0 5683 6304 6304 6304 6304 1442790 2140930 2140937 2155575',X'0C080803080808080404030301C3F901317CC701044CC7029E6834F7E2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 4768 4609 4609 4609 4609 26 1 1 1','0 0 2200466 2200466 2200466 2200466 2200466 2204364 2204389 2204389 2204389','0 0 5714 6370 6370 6370 6370 1470393 2189496 2189503 2204389',X'0C080803080808080403030301C4220131586B3EB735018A7567CE9E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5177 4669 4669 4669 4669 22 1 1 1','0 0 2234078 2234078 2234078 2234078 2234078 2238366 2238387 2238387 2238387','0 0 5743 6422 6422 6422 6422 1490145 2223355 2223362 2238387',X'0C080803080808080403030301C4490131573F4104F802D9CA67FD1E');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 7981 5546 5546 5546 5546 32 1 1 1','0 0 2239923 2239923 2239923 2239923 2239923 2243388 2243419 2243419 2243419','0 0 5746 6428 6428 6428 6428 1492784 2228358 2228365 2243419',X'0C080803080808080404030301C44E0131A4FE00FDCC04029E8175076B');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 351 336 336 336 336 1 1 1 1','0 0 2595725 2595725 2595725 2595725 2595725 2595739 2595739 2595739 2595739','0 0 6747 7717 7717 7717 7717 1732397 2578697 2578708 2595739',X'0C080803080808080404030301CCC901332A7700F17E5D0E5D6F523B01');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 6022 4942 4942 4942 4942 38 1 1 1','0 0 2648510 2648510 2648510 2648510 2648510 2652126 2652163 2652163 2652163','0 0 6934 7959 7959 7959 7959 1770411 2634884 2634898 2652163',X'0C080803080808080404030301CE8C01317DE800FE4E8B029E81458317');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 4752 1965 1965 1965 1965 1 1 1 1','0 0 3161780 3161780 3161780 3161780 3161780 3163744 3163744 3163744 3163744','0 0 9707 11219 11219 11219 11219 2143797 3143873 3143896 3163744',X'0C08080308080808040303030213B20130BC9216F939029E815B86EE');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5497 1880 1880 1880 1880 1 1 1 1','0 0 3167643 3167643 3167643 3167643 3167643 3169522 3169522 3169522 3169522','0 0 9709 11226 11226 11226 11226 2148088 3149632 3149655 3169522',X'0C08080308080808040303030213B50130BBC616FC6501C7A62A1BB2');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 2185 2126 2126 2126 2126 4 1 1 1','0 0 3891670 3891670 3891670 3891670 3891670 3893609 3893609 3893609 3893609','0 0 13596 15952 15953 15953 15953 2677069 3869700 3869732 3893609',X'0C080803080808080403030302BD5F01317CC81782F704C9D95EFF0C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5511 5355 5355 5355 5355 21 1 1 1','0 0 3931850 3931850 3931850 3931850 3931850 3936206 3936226 3936226 3936226','0 0 13793 16188 16189 16189 16189 2705655 3912091 3912125 3936226',X'0C080803080808080403030302C14C01317C514010FA01B64235E688');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5468 5115 5115 5115 5115 27 1 1 1','0 0 3939091 3939091 3939091 3939091 3939091 3943255 3943281 3943281 3943281','0 0 13803 16200 16201 16201 16201 2709082 3919099 3919133 3943281',X'0C080803080808080403030302C16401317CC348B67001C3EB6824BB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 5275 4792 4792 4792 4792 31 1 1 1','0 0 4590217 4590217 4590217 4590217 4590217 4591639 4591669 4591669 4591669','0 0 17456 20455 20456 20456 20456 3175906 4564022 4564064 4591669',X'0C0808030808080804040403049EE50132915200FA2F9300EA0DC50554E0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 339 339 339 339 339 2 1 1 1','0 0 5191258 5191258 5191258 5191258 5191258 5191478 5191479 5191479 5191479','0 0 20754 24230 24231 24231 24231 3601696 5160652 5160701 5191479',X'0C08080308080808040303030CEE930132671A1B8AFF01B6426386D0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 7173953 13 13 13 13 13 1 1 1 1','0 0 6489339 6489339 6489339 6489339 6489339 6489349 6489349 6489349 6489349','0 0 29449 33987 33989 33989 33989 4540005 6453309 6453378 6489349',X'0C08080408080808040303030091B3AB01321B5A4F26F2018A750B6686');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','7804279 630326 19 19 18 18 18 1 1 1 1','0 7173953 7787203 7787203 7787203 7787203 7787203 7787219 7787219 7787219 7787219','0 1 73522 83967 91688 91688 91688 5643606 7744920 7744997 7787219',X'0C080904080808080404030300FF69210132DB2900FB0715029EB573EB9F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','3876548 3718791 4598 4512 4512 4512 4512 38 1 1 1','7804279 7804279 8237877 8237877 8237877 8237877 8237877 8238986 8239023 8239023 8239023','1 2 76726 87349 95641 95641 95641 5923145 8195880 8195957 8239023',X'0C0908030808080804040404018D3F01332A780111068001FB6F840083BEF9');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','3876548 3718791 397 397 397 397 397 2 1 1 1','7804279 7804279 9084892 9084892 9084892 9084892 9084892 9085089 9085089 9085089 9085089','1 2 79826 90851 99143 99143 99143 6505397 9040540 9040617 9085089',X'0C090803080808080403030301CBAF0133028A4E35BE715A2A49098C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','3876548 3718791 141 141 141 141 141 2 1 1 1','7804279 7804279 10382921 10382921 10382921 10382921 10382921 10382958 10382959 10382959 10382959','1 2 87296 99145 107437 107437 107437 7432077 10336699 10336776 10382959',X'0C09080308080808040403030DB5C101332BF7010E37310EB97420BFF6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix2','3876548 157757 83 16 16 16 16 1 1 1 1','7804279 11523070 11587750 11587817 11587817 11587817 11587817 11587832 11587832 11587832 11587832','1 3 104655 118035 126343 126343 126343 8311754 11540721 11540798 11587832',X'0C090903020808080404030402A9D1232801332CC300EB7D35029E83008581B1');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 24157 24157 24157 24157 24157 133 1 1 1','0 0 1236590 1236590 1236590 1236590 1236590 1241150 1241282 1241282 1241282','0 0 3120 3120 3120 3120 4771 741878 1223262 1228239 1241282',X'0C08080308080804040408031A080400FF559401321BC20099C49F3BE84C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 1209 1209 1209 1209 1209 2 1 1 1','0 0 1296873 1296873 1296873 1296873 1296873 1297868 1297869 1297869 1297869','0 0 3203 3203 3203 3203 4902 763298 1279554 1284645 1297869',X'0C08080308080803040308031A215F04C9D901317D1501B0A355A290');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 12129 12129 12129 12129 9261 38 1 1 1','0 0 1790076 1790076 1790076 1790076 1790076 1791737 1791774 1791774 1791774','0 0 4938 4938 4938 4938 7561 1075153 1767990 1775629 1791774',X'0C08080308080803040408031CE87E029E6B01328F6100B473FD5BE552');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 273 273 273 273 273 1 1 1 1','0 0 2595625 2595625 2595625 2595625 2595625 2595739 2595739 2595739 2595739','0 0 7863 7863 7863 7863 12132 1580849 2562484 2575172 2595739',X'0C08080308080803040308032F020502DA0E013241A16B73DE01E021');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 7735 7735 7735 7735 7735 503 1 1 1','0 0 2924165 2924165 2924165 2924165 2924165 2931394 2931896 2931896 2931896','0 0 9123 9123 9123 9123 14045 1794002 2895440 2909856 2931896',X'0C080803080808040404080433776300FF55940133294301C56631008BCB5A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 360 360 360 360 229 1 1 1 1','0 0 3893374 3893374 3893374 3893374 3893505 3893609 3893609 3893609 3893609','0 0 13912 13912 13912 13912 21124 2420346 3846830 3866425 3893609',X'0C08080308080803040308034717710E070B01317F7102C73A396CE1');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 683 683 683 683 671 2 1 1 1','0 0 5191447 5191447 5191447 5191447 5191459 5191479 5191479 5191479 5191479','0 0 20361 20361 20361 20361 30776 3226670 5127525 5158962 5191479',X'0C08080308080803040308037370D10F00290132B59A018F7A694C9B');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 213354 213354 213354 213354 213354 5934 1 1 1','0 0 6102233 6102233 6102233 6102233 6102233 6154276 6155650 6155650 6155650','0 0 26812 26812 26812 26812 39489 3770219 6079715 6119855 6155650',X'0C080804080808040403080300F2FA4400FF55940132DF1A01C0C115D4D4');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 3675 3675 3675 3675 3675 14 1 1 1','0 0 6486701 6486701 6486701 6486701 6486701 6489347 6489349 6489349 6489349','0 0 28380 28380 28380 28380 41383 3868787 6409451 6452691 6489349',X'0C080804080808030403020300F6ABAC6C967D0132B672026C8D03E8155844');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 24063 24063 24063 24063 24063 1799 1 1 1','0 0 7080914 7080914 7080914 7080914 7080914 7103178 7104976 7104976 7104976','0 0 35724 35725 35725 35725 49845 4189282 7019641 7067879 7104976',X'0C0808040808080404040804010B0A5C00FF55940133294301D2C35700A877B5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 7173953 20284 20284 20284 20284 20284 1518 1 1 1','0 0 7107240 7107240 7107240 7107240 7107240 7124584 7126101 7126101 7126101','0 0 35764 35765 35765 35765 49886 4191089 7040717 7089004 7126101',X'0C0808040808080404040803010B3C6700FF55940133294501F0A829511573');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 630326 13288 11855 11855 11855 11855 5449 1 1 1','0 7173953 7671119 7671119 7671119 7671119 7671119 7676514 7676526 7676526 7676526','0 1 64320 65543 65543 65543 92453 4431581 7580039 7634401 7676526',X'0C080904080808040403080400F2FA4400FF5594013329430186CB00A866E4');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','7804279 630326 61 61 61 61 61 14 1 1 1','0 7173953 7787172 7787172 7787172 7787172 7787172 7787217 7787219 7787219 7787219','0 1 73272 75274 75274 75274 103642 4468650 7689369 7745002 7787219',X'0C0809040808080304030803010ABA910DD2420133068F05DE6223042F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 8657 8657 8657 8657 8657 18 1 1 1','7804279 7804279 8360485 8360485 8360485 8360485 8360485 8360977 8360994 8360994 8360994','1 2 75785 77865 77865 77865 106290 4756698 8258979 8317590 8360994',X'0C09080308080803040408031AD369021FFC0133517F01B017F259F6C3');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 33421 33421 33421 33421 33421 2221 1 1 1','7804279 7804279 8873934 8873934 8873934 8873934 8873934 8874868 8877088 8877088 8877088','1 2 76834 78914 78914 78914 107340 5041847 8770182 8832668 8877088',X'0C090803080808040404020333776300FF5594013351790241FEE4232825FB3D');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 2182 2182 2182 2182 2182 20 1 1 1','7804279 7804279 9083187 9083187 9083187 9083187 9083187 9085087 9085089 9085089 9085089','1 2 77227 79307 79307 79307 107733 5141706 8974619 9040316 9085089',X'0C09080308080803040308043AC92C021FFC0132B668018BAB00964778');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 6008 6008 6008 6008 6008 26 1 1 1','7804279 7804279 9124662 9124662 9124662 9124662 9124662 9128286 9128311 9128311 9128311','1 2 77300 79380 79380 79380 107806 5160088 9016921 9083451 9128311',X'0C09080308080804040408033C1C5C00F024880132DB2100B88E8A420C1B');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 6023 6023 6023 6023 6023 91 1 1 1','7804279 7804279 9393480 9393480 9393480 9393480 9393480 9396519 9396609 9396609 9396609','1 2 77893 79973 79973 79973 108402 5311949 9282988 9351281 9396609',X'0C09080308080803040408044CDD9A7E1C230132DE4200D105C800A087F7');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 79 79 79 79 79 1 1 1 1','7804279 7804279 10382946 10382946 10382946 10382946 10382946 10382959 10382959 10382959 10382959','1 2 80421 82501 82501 82501 110947 5821380 10258569 10336296 10382959',X'0C090804080808030403080300E9A10A0CC3160133505F0D25365733C6');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 8251 8251 8251 8251 8251 37 1 1 1','7804279 7804279 11067468 11067468 11067468 11067468 11067468 11068084 11068120 11068120 11068120','1 2 83449 85529 85529 85529 113989 6177561 10936213 11021086 11068120',X'0C090804080808040404080400FFA4A7008A66AD01332AD10170A8B10084054A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 44772 44772 44772 44772 44772 3018 1 1 1','7804279 7804279 11268036 11268036 11268036 11268036 11268036 11282829 11285846 11285846 11285846','1 2 85052 87132 87132 87132 115597 6281979 11151347 11238812 11285846',X'0C0908040808080404040804010B0A5C00FF5594013351720249087400B0523C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 53775 53775 53775 53775 53775 4069 1 1 1','7804279 7804279 11330929 11330929 11330929 11330929 11330929 11359035 11363103 11363103 11363103','1 2 85255 87335 87335 87335 115800 6292285 11227544 11316069 11363103',X'0C0908040808080404040804010BFA8100FF5594013351280252F8A000B01818');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 3718791 39430 39430 39430 39430 39430 3018 1 1 1','7804279 7804279 11458789 11458789 11458789 11458789 11458789 11468188 11471205 11471205 11471205','1 2 86422 88502 88502 88502 116970 6333877 11333316 11424171 11471205',X'0C09080408080804040408040113B99600FF559401335174024530C4008AC82C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix1','3876548 157757 5317 5317 5317 5317 5317 12 1 1 1','7804279 11523070 11652844 11652844 11652844 11652844 11652844 11658149 11658160 11658160 11658160','1 3 97969 100051 100051 100051 128567 6429914 11517744 11611126 11658160',X'0C0909040808080404040804010BFA8100FF559401332B2E01521E0B008447F4');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 24157 29 29 29 29 29 1 1 1','0 0 1236590 1251799 1251799 1251799 1251799 1251799 1251827 1251827 1251827','0 0 3120 62451 63798 63798 63798 63798 1238772 1238782 1251827',X'0C08080303080808080404031A0804029BB30131CD3700FF55942B1368');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 1209 129 129 129 129 129 1 1 1','0 0 1296873 1297744 1297744 1297744 1297744 1297744 1297869 1297869 1297869','0 0 3203 65280 66657 66657 66657 66657 1284635 1284645 1297869',X'0C08080303080808080403041A215F01B0A30131066F04C9D90087162A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 12129 604 604 604 604 604 1 1 1','0 0 1790076 1790076 1790076 1790076 1790076 1790076 1790679 1790679 1790679','0 0 4938 95334 97612 97612 97612 97612 1774566 1774578 1790679',X'0C08080303080808080403031CE87E01870001317C4D0CED622FE2E8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 273 5 5 5 5 5 1 1 1','0 0 2595625 2595738 2595738 2595738 2595738 2595738 2595739 2595739 2595739','0 0 7863 145612 149427 149427 149427 149427 2575145 2575172 2595739',X'0C08080303080808080403042F020502C73A0133049702DA0E00A711F8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 7735 29 29 29 29 29 1 1 1','0 0 2924165 2929754 2929754 2929754 2929754 2929754 2929782 2929782 2929782','0 0 9123 167852 172269 172269 172269 172269 2907715 2907742 2929782',X'0C080803030808080804040333776375928D0133294300FF55945A824F');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 360 6 6 6 6 6 1 1 1','0 0 3893374 3893607 3893607 3893607 3893607 3893607 3893609 3893609 3893609','0 0 13912 240908 247109 247109 247109 247109 3866384 3866425 3893609',X'0C080803030808080804030347177104AF8701317D220E070B687905');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 683 32 32 32 32 32 2 2 1','0 0 5191447 5191465 5191465 5191465 5191465 5191465 5191479 5191479 5191479','0 0 20361 336764 345696 345696 345696 345696 5158907 5158962 5191479',X'0C08080303080808080403037370D101884501328F690F002900A69C');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 213354 113 113 113 113 113 1 1 1','0 0 6102233 6155183 6155183 6155183 6155183 6155183 6155295 6155295 6155295','0 0 26812 421693 433202 433202 433202 433202 6119212 6119281 6155295',X'0C080804030808080804040300F2FA44019B6D0132406700FF5594714AE8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 3675 241 241 241 241 241 1 1 1','0 0 6486701 6489287 6489287 6489287 6489287 6489287 6489349 6489349 6489349','0 0 28380 450494 462953 462953 462953 462953 6452616 6452691 6489349',X'0C080804040808080804030300F6ABAC009F84B60132DBDD6C967D155A2A');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 24063 38 38 38 38 38 1 1 1','0 0 7080914 7102383 7102383 7102383 7102383 7102383 7102420 7102420 7102420','0 0 35724 528136 541855 541858 541858 541858 7065247 7065323 7102420',X'0C0808040408080808040403010B0A5C00CCD3EE0133294500FF559459DDCD');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 7173953 20284 38 38 38 38 38 1 1 1','0 0 7107240 7126609 7126609 7126609 7126609 7126609 7126646 7126646 7126646','0 0 35764 531673 545484 545487 545487 545487 7089473 7089549 7126646',X'0C0808040408080808040403010B3C670114CE730133294300FF55942225CC');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 630326 13288 5 1 1 1 1 1 1 1','0 7173953 7671119 7675167 7675171 7675171 7675171 7675171 7675171 7675171 7675171','0 1 64320 967583 993386 999414 999414 999414 7632969 7633046 7675171',X'0C080904030308080804040400F2FA4401D16F0183350132DEFE00FF5594009FA501');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','7804279 630326 61 1 1 1 1 1 1 1 1','0 7173953 7787172 7787219 7787219 7787219 7787219 7787219 7787219 7787219 7787219','0 1 73272 1068631 1096442 1107608 1107608 1107608 7744925 7745002 7787219',X'0C0809040408080808040304010ABA9100AA4C1F013351DB0DD242008BFFEF');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 8657 605 605 605 605 605 1 1 1','7804279 7804279 8360485 8364643 8364643 8364643 8364643 8364643 8365247 8365247 8365247','1 2 75785 1096159 1124446 1135923 1135923 1135923 8321766 8321843 8365247',X'0C09080303080808080403031AD3690DC36E01328E2F021FFC3D9128');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 33421 57 26 26 26 26 1 1 1','7804279 7804279 8873934 8891008 8891039 8891039 8891039 8891039 8891064 8891064 8891064','1 2 76834 1110063 1138607 1150084 1150084 1150084 8846567 8846644 8891064',X'0C09080303020808080404043377630CE166232801332A1800FF559400836DFB');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 2182 94 94 94 94 94 1 1 1','7804279 7804279 9083187 9085006 9085006 9085006 9085006 9085006 9085089 9085089 9085089','1 2 77227 1115805 1144438 1155915 1155915 1155915 9040239 9040316 9085089',X'0C09080303080808080403033AC92C04AF670132B5FC021FFC38DBD0');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 6008 277 277 277 277 277 1 1 1','7804279 7804279 9124662 9124662 9124662 9124662 9124662 9124662 9124938 9124938 9124938','1 2 77300 1116652 1145304 1156781 1156781 1156781 9080000 9080077 9124938',X'0C09080303080808080404033C1C5C01B49901328DDA00F024883D6052');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 6023 50 50 50 50 50 1 1 1','7804279 7804279 9393480 9395815 9395815 9395815 9395815 9395815 9395864 9395864 9395864','1 2 77893 1123924 1152663 1164140 1164140 1164140 9350459 9350536 9395864',X'0C09080303080808080403034CDD9A01C30D013290877E1C2303CC11');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 79 6 6 6 6 6 1 1 1','7804279 7804279 10382946 10382956 10382956 10382956 10382956 10382956 10382959 10382959 10382959','1 2 80421 1154920 1184197 1195674 1195674 1195674 10336219 10336296 10382959',X'0C090804030808080804030400E9A10A02A47201332C6C0CC31600ABCD60');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 8251 280 280 280 280 280 1 1 1','7804279 7804279 11067468 11072690 11072690 11072690 11072690 11072690 11072969 11072969 11072969','1 2 83449 1186373 1216159 1227636 1227636 1227636 11025858 11025935 11072969',X'0C090804040808080804040300FFA4A70089DBDD0132B5FF008A66AD6AE2B8');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 44772 58 58 58 58 58 1 1 1','7804279 7804279 11268036 11269261 11269261 11269261 11269261 11269261 11269318 11269318 11269318','1 2 85052 1199706 1229712 1241189 1241189 1241189 11222207 11222284 11269318',X'0C0908040308080808040404010B0A5C018ACD0133294300FF559400B0E291');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 53775 37 19 19 19 19 1 1 1','7804279 7804279 11330929 11355027 11355045 11355045 11355045 11355045 11355063 11355063 11355063','1 2 85255 1206165 1236304 1247781 1247781 1247781 11307952 11308029 11355063',X'0C0908040302080808040403010BFA81026C24232801332B2E00FF55941F8BB5');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 3718791 39430 39 39 39 39 39 1 1 1','7804279 7804279 11458789 11476757 11476757 11476757 11476757 11476757 11476795 11476795 11476795','1 2 86422 1219089 1249476 1260953 1260953 1260953 11429684 11429761 11476795',X'0C09080403080808080404030113B99602F6AF01332B2E00FF55941F8A54');
+ INSERT INTO sqlite_stat4 VALUES('tx1','ix0','3876548 157757 5317 1 1 1 1 1 1 1 1','7804279 11523070 11652844 11658160 11658160 11658160 11658160 11658160 11658160 11658160 11658160','1 3 97969 1358780 1391643 1403122 1403122 1403122 11611049 11611126 11658160',X'0C0909040402080808040403010BFA8102594CCB2328013351E800FF55945B0F5D');
+ ANALYZE sqlite_master;
+} {}
+
+# Ensure that the query planner implements the GROUP BY using a separate sort
+#
+do_execsql_test whereJ-1.4 {
+ EXPLAIN QUERY PLAN
+ SELECT aid, sid, MAX(edate) edate
+ FROM tx1
+ WHERE cid = 115790
+ AND sid = 9100
+ AND edate <= 20140430 AND edate >= 20120429
+ GROUP BY aid;
+} {/B-TREE/}
+
+############################################################################
+# Ensure that the sorting cost does not swamp the loop costs and cause
+# distinctions between individual loop costs to get lost, and hence for
+# sub-optimal loops to be chosen.
+#
+do_execsql_test whereJ-2.1 {
+ CREATE TABLE tab(
+ id INTEGER PRIMARY KEY,
+ minChild INTEGER REFERENCES t1,
+ maxChild INTEGER REFERENCES t1,
+ x INTEGER
+ );
+ EXPLAIN QUERY PLAN
+ SELECT t4.x
+ FROM tab AS t0, tab AS t1, tab AS t2, tab AS t3, tab AS t4
+ WHERE t0.id=0
+ AND t1.id BETWEEN t0.minChild AND t0.maxChild
+ AND t2.id BETWEEN t1.minChild AND t1.maxChild
+ AND t3.id BETWEEN t2.minChild AND t2.maxChild
+ AND t4.id BETWEEN t3.minChild AND t3.maxChild
+ ORDER BY t4.x;
+} {~/SCAN/}
+do_execsql_test whereJ-2.2 {
+ EXPLAIN QUERY PLAN
+ SELECT t4.x
+ FROM tab AS t0a, tab AS t0b,
+ tab AS t1a, tab AS t1b,
+ tab AS t2a, tab AS t2b,
+ tab AS t3a, tab AS t3b,
+ tab AS t4
+ WHERE 1
+ AND t0a.id=1
+ AND t1a.id BETWEEN t0a.minChild AND t0a.maxChild
+ AND t2a.id BETWEEN t1a.minChild AND t1a.maxChild
+ AND t3a.id BETWEEN t2a.minChild AND t2a.maxChild
+ AND t0b.id=2
+ AND t1b.id BETWEEN t0b.minChild AND t0b.maxChild
+ AND t2b.id BETWEEN t1b.minChild AND t1b.maxChild
+ AND t3b.id BETWEEN t2b.minChild AND t2b.maxChild
+ AND t4.id BETWEEN t3a.minChild AND t3b.maxChild
+ ORDER BY t4.x;
+} {~/SCAN/}
+
+
+finish_test
diff --git a/test/wild001.test b/test/wild001.test
new file mode 100644
index 0000000..7fe1404
--- /dev/null
+++ b/test/wild001.test
@@ -0,0 +1,311 @@
+# 2013-07-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 is a test case from content taken "from the wild". In this
+# particular instance, the query was provided with permission by
+# Elan Feingold on 2013-06-27. His message on the SQLite mailing list
+# on that date reads:
+#
+#------------------------------------------------------------------------------
+# > Can you send (1) the schema (2) the query that is giving problems, and (3)
+# > the content of the sqlite_stat1 table after you have run ANALYZE? If you
+# > can combine all of the above into a script, that would be great!
+# >
+# > If you send (1..3) above and you give us written permission to include the
+# > query in our test suite, that would be off-the-chain terrific.
+#
+# Please find items 1..3 in this file: http://www.plexapp.com/elan/sqlite_bug.txt
+#
+# You have our permission to include the query in your test suite.
+#
+# Thanks for an amazing product.
+#-----------------------------------------------------------------------------
+#
+# This test case merely creates the schema and populates SQLITE_STAT1 and
+# SQLITE_STAT3 then runs an EXPLAIN QUERY PLAN to ensure that the right plan
+# is discovered. This test case may need to be adjusted for future revisions
+# of the query planner manage to select a better query plan. The query plan
+# shown here is known to be very fast with the original data.
+#
+# This test should work the same with and without SQLITE_ENABLE_STAT3
+#
+###############################################################################
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !stat3 {
+ finish_test
+ return
+}
+
+do_execsql_test wild001.01 {
+ CREATE TABLE "items" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "secid" integer, "parent_id" integer, "metadata_type" integer, "guid" varchar(255), "media_item_count" integer, "title" varchar(255), "title_sort" varchar(255) COLLATE NOCASE, "original_title" varchar(255), "studio" varchar(255), "rating" float, "rating_count" integer, "tagline" varchar(255), "summary" text, "trivia" text, "quotes" text, "content_rating" varchar(255), "content_rating_age" integer, "index" integer, "absolute_index" integer, "duration" integer, "user_thumb_url" varchar(255), "user_art_url" varchar(255), "user_banner_url" varchar(255), "user_music_url" varchar(255), "user_fields" varchar(255), "tags_genre" varchar(255), "tags_collection" varchar(255), "tags_director" varchar(255), "tags_writer" varchar(255), "tags_star" varchar(255), "originally_available_at" datetime, "available_at" datetime, "expires_at" datetime, "refreshed_at" datetime, "year" integer, "added_at" datetime, "created_at" datetime, "updated_at" datetime, "deleted_at" datetime, "tags_country" varchar(255), "extra_data" varchar(255), "hash" varchar(255));
+ CREATE INDEX "i_secid" ON "items" ("secid" );
+ CREATE INDEX "i_parent_id" ON "items" ("parent_id" );
+ CREATE INDEX "i_created_at" ON "items" ("created_at" );
+ CREATE INDEX "i_index" ON "items" ("index" );
+ CREATE INDEX "i_title" ON "items" ("title" );
+ CREATE INDEX "i_title_sort" ON "items" ("title_sort" );
+ CREATE INDEX "i_guid" ON "items" ("guid" );
+ CREATE INDEX "i_metadata_type" ON "items" ("metadata_type" );
+ CREATE INDEX "i_deleted_at" ON "items" ("deleted_at" );
+ CREATE INDEX "i_secid_ex1" ON "items" ("secid", "metadata_type", "added_at" );
+ CREATE INDEX "i_hash" ON "items" ("hash" );
+ CREATE TABLE "settings" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "account_id" integer, "guid" varchar(255), "rating" float, "view_offset" integer, "view_count" integer, "last_viewed_at" datetime, "created_at" datetime, "updated_at" datetime);
+ CREATE INDEX "s_account_id" ON "settings" ("account_id" );
+ CREATE INDEX "s_guid" ON "settings" ("guid" );
+ ANALYZE;
+ INSERT INTO sqlite_stat1 VALUES('settings','s_guid','4740 1');
+ INSERT INTO sqlite_stat1 VALUES('settings','s_account_id','4740 4740');
+ INSERT INTO sqlite_stat1 VALUES('items','i_hash','27316 2');
+ INSERT INTO sqlite_stat1 VALUES('items','i_secid_ex1','27316 6829 4553 3');
+ INSERT INTO sqlite_stat1 VALUES('items','i_deleted_at','27316 27316');
+ INSERT INTO sqlite_stat1 VALUES('items','i_metadata_type','27316 6829');
+ INSERT INTO sqlite_stat1 VALUES('items','i_guid','27316 2');
+ INSERT INTO sqlite_stat1 VALUES('items','i_title_sort','27316 2');
+ INSERT INTO sqlite_stat1 VALUES('items','i_title','27316 2');
+ INSERT INTO sqlite_stat1 VALUES('items','i_index','27316 144');
+ INSERT INTO sqlite_stat1 VALUES('items','i_created_at','27316 2');
+ INSERT INTO sqlite_stat1 VALUES('items','i_parent_id','27316 15');
+ INSERT INTO sqlite_stat1 VALUES('items','i_secid','27316 6829');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,150,150,'com.plexapp.agents.thetvdb://153021/2/9?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,198,198,'com.plexapp.agents.thetvdb://194031/1/10?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,526,526,'com.plexapp.agents.thetvdb://71256/12/92?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,923,923,'com.plexapp.agents.thetvdb://71256/15/16?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1008,1008,'com.plexapp.agents.thetvdb://71256/15/93?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1053,1053,'com.plexapp.agents.thetvdb://71256/16/21?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1068,1068,'com.plexapp.agents.thetvdb://71256/16/35?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1235,1235,'com.plexapp.agents.thetvdb://71256/17/44?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1255,1255,'com.plexapp.agents.thetvdb://71256/17/62?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1573,1573,'com.plexapp.agents.thetvdb://71663/20/9?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,1580,1580,'com.plexapp.agents.thetvdb://71663/21/16?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2000,2000,'com.plexapp.agents.thetvdb://73141/9/8?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2107,2107,'com.plexapp.agents.thetvdb://73244/6/17?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2256,2256,'com.plexapp.agents.thetvdb://74845/4/7?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2408,2408,'com.plexapp.agents.thetvdb://75978/2/21?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2634,2634,'com.plexapp.agents.thetvdb://79126/1/1?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,2962,2962,'com.plexapp.agents.thetvdb://79274/3/94?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,3160,3160,'com.plexapp.agents.thetvdb://79274/5/129?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,3161,3161,'com.plexapp.agents.thetvdb://79274/5/12?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,3688,3688,'com.plexapp.agents.thetvdb://79274/8/62?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,3714,3714,'com.plexapp.agents.thetvdb://79274/8/86?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,4002,4002,'com.plexapp.agents.thetvdb://79590/13/17?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,4215,4215,'com.plexapp.agents.thetvdb://80727/3/6?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_guid',1,4381,4381,'com.plexapp.agents.thetvdb://83462/3/24?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('settings','s_account_id',4740,0,0,1);
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,1879,1879,'1113f632ccd52ec8b8d7ca3d6d56da4701e48018');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,2721,2721,'1936154b97bb5567163edaebc2806830ae419ccf');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,3035,3035,'1c122331d4b7bfa0dc2c003ab5fb4f7152b9987a');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,3393,3393,'1f81bdbc9acc3321dc592b1a109ca075731b549a');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,6071,6070,'393cf7713efb4519c7a3d1d5403f0d945d15a16a');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,7462,7461,'4677dd37011f8bd9ae7fbbdd3af6dcd8a5b4ab2d');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,8435,8434,'4ffa339485334e81a5e12e03a63b6508d76401cf');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,8716,8714,'52a093852e6599dd5004857b7ff5b5b82c7cdb25');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,9107,9104,'561183e39f866d97ec728e9ff16ac4ad01466111');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,10942,10939,'66e99b72e29610f49499ae09ee04a376210d1f08');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,12143,12139,'71f0602427e173dc2c551535f73fdb6885fe4302');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,14962,14958,'8ca8e4dfba696019830c19ab8a32c7ece9d8534b');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,15179,15174,'8ebf1a5cf33f8ada1fc5853ac06ac4d7e074f825');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,15375,15370,'908bc211bebdf21c79d2d2b54ebaa442ac1f5cae');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,18215,18210,'ab29e4e18ec5a14fef95aa713d69e31c045a22c1');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,18615,18610,'ae84c008cc0c338bf4f28d798a88575746452f6d');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,18649,18644,'aec7c901353e115aa5307e94018ba7507bec3a45');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,19517,19512,'b75025fbf2e9c504e3c1197ff1b69250402a31f8');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,21251,21245,'c7d32f0e3a8f3a0a3dbd00833833d2ccee62f0fd');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,23616,23610,'dd5ff61479a9bd4100de802515d9dcf72d46f07a');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,24287,24280,'e3db00034301b7555419d4ef6f64769298d5845e');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,24949,24942,'ea336abd197ecd7013854a25a4f4eb9dea7927c6');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',1,25574,25567,'f018ea5182ec3f32768ca1c3cefbf3ad160ec20b');
+ INSERT INTO sqlite_stat3 VALUES('items','i_hash',2,26139,26132,'f53709a8d81c12cb0f4f8d58004a25dd063de67c');
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid_ex1',25167,0,0,2);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid_ex1',736,25167,1,3);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid_ex1',15,25903,2,4);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid_ex1',1398,25918,3,5);
+ INSERT INTO sqlite_stat3 VALUES('items','i_deleted_at',27316,0,0,NULL);
+ INSERT INTO sqlite_stat3 VALUES('items','i_metadata_type',2149,0,0,1);
+ INSERT INTO sqlite_stat3 VALUES('items','i_metadata_type',411,2149,1,2);
+ INSERT INTO sqlite_stat3 VALUES('items','i_metadata_type',1440,2560,2,3);
+ INSERT INTO sqlite_stat3 VALUES('items','i_metadata_type',23316,4000,3,4);
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,215,215,'com.plexapp.agents.imdb://tt0065702?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,711,711,'com.plexapp.agents.imdb://tt0198781?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,987,986,'com.plexapp.agents.imdb://tt0454876?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1004,1002,'com.plexapp.agents.imdb://tt0464154?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1056,1053,'com.plexapp.agents.imdb://tt0499549?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1120,1116,'com.plexapp.agents.imdb://tt0903624?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1250,1245,'com.plexapp.agents.imdb://tt1268799?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1270,1264,'com.plexapp.agents.imdb://tt1320261?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',2,1376,1369,'com.plexapp.agents.imdb://tt1772341?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,3035,3027,'com.plexapp.agents.thetvdb://153021/3/14?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,6071,6063,'com.plexapp.agents.thetvdb://71173/1/18?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,6342,6334,'com.plexapp.agents.thetvdb://71256/13/4?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,9107,9099,'com.plexapp.agents.thetvdb://72389/2/19?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,11740,11732,'com.plexapp.agents.thetvdb://73893/2/13?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,12143,12135,'com.plexapp.agents.thetvdb://73976/4/23?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,15179,15171,'com.plexapp.agents.thetvdb://75897/16/12?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,17408,17400,'com.plexapp.agents.thetvdb://76808/2/16?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,17984,17976,'com.plexapp.agents.thetvdb://77068/1/16?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,18215,18207,'com.plexapp.agents.thetvdb://77259/1/1?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,21251,21243,'com.plexapp.agents.thetvdb://78957/8/2?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,24287,24279,'com.plexapp.agents.thetvdb://80337/5/8?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,25513,25505,'com.plexapp.agents.thetvdb://82226/6?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,25548,25540,'com.plexapp.agents.thetvdb://82339/2/10?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_guid',1,26770,26762,'com.plexapp.agents.thetvdb://86901/1/3?lang=en');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',1524,0,0,'');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',2,3034,1391,'Attack of the Giant Squid');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',51,4742,2895,'Brad Sherwood');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',11,4912,2996,'Brian Williams');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',39,5847,3857,'Chip Esten');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',1,6071,4015,'Chuck Versus the DeLorean');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',12,7625,5436,'Denny Siegel');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',30,8924,6618,'Episode 1');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',29,9015,6629,'Episode 2');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',32,9082,6643,'Episode 3');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',28,9135,6654,'Episode 4');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',26,9183,6665,'Episode 5');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',27,9229,6677,'Episode 6');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',22,9266,6688,'Episode 7');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',20,9298,6699,'Episode 8');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',55,11750,8817,'Greg Proops');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',1,12143,9120,'Hardware Jungle');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',33,14712,11435,'Kathy Greenwood');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',3,15179,11840,'Last Call');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',1,18215,14601,'Nature or Nurture?');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',12,18241,14623,'Neil DeGrasse Tyson');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',68,19918,16144,'Pilot');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',7,21251,17298,'Reza Aslan');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title_sort',1,24287,20035,'Technoviking');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1524,0,0,'');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,3035,1429,'Anderson Can''t Dance');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',51,4782,2991,'Brad Sherwood');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',11,4936,3079,'Brian Williams');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',39,5694,3783,'Chip Esten');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,6071,4100,'Clive Warren');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',12,7144,5078,'Denny Siegel');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',30,8249,6097,'Episode 1');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',29,8340,6108,'Episode 2');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',32,8407,6122,'Episode 3');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',28,8460,6133,'Episode 4');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',26,8508,6144,'Episode 5');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',27,8554,6156,'Episode 6');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',22,8591,6167,'Episode 7');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',20,8623,6178,'Episode 8');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,9107,6537,'Fat Albert and the Cosby Kids');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',55,10539,7843,'Greg Proops');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,12143,9276,'Iron Age Remains');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',33,13118,10143,'Kathy Greenwood');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,15179,11972,'Mink');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',68,17411,14035,'Pilot');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',2,18214,14727,'Reflections');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',4,21250,17481,'The Apartment');
+ INSERT INTO sqlite_stat3 VALUES('items','i_title',1,24287,20283,'The Simpsons Already Did It');
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',4315,95,2,1);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1553,4410,3,2);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1485,5963,4,3);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1414,7448,5,4);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1367,8862,6,5);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1328,10229,7,6);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1161,11557,8,7);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1108,12718,9,8);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1033,13826,10,9);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',1014,14859,11,10);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',929,15873,12,11);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',906,16802,13,12);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',844,17708,14,13);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',690,18552,15,14);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',655,19242,16,15);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',625,19897,17,16);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',579,20522,18,17);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',555,21101,19,18);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',526,21656,20,19);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',501,22182,21,20);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',459,22683,22,21);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',439,23142,23,22);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',315,23581,24,23);
+ INSERT INTO sqlite_stat3 VALUES('items','i_index',192,24177,26,25);
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1851,0,0,NULL);
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',373,1857,2,'2011-10-22 14:54:39');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',595,2230,3,'2011-10-22 14:54:41');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',337,2825,4,'2011-10-22 14:54:43');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',361,3378,8,'2011-10-22 14:54:54');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',160,3739,9,'2011-10-22 14:54:56');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',315,4000,11,'2011-10-22 14:54:59');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',321,4334,13,'2011-10-22 14:55:02');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1292,4723,16,'2011-10-22 14:55:06');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',161,6015,17,'2011-10-22 14:55:07');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1,9107,2677,'2012-09-04 18:07:50');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',313,9717,3270,'2012-10-18 16:50:21');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',450,10030,3271,'2012-10-18 16:50:22');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',389,10668,3275,'2012-10-18 16:50:26');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',796,11057,3276,'2012-10-18 16:51:06');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',161,12041,3280,'2012-10-19 19:52:37');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',135,13281,4186,'2013-02-19 00:56:10');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1063,13416,4187,'2013-02-19 00:56:11');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',797,14479,4188,'2013-02-19 00:56:13');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',147,15276,4189,'2013-02-19 00:56:15');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',346,15423,4190,'2013-02-19 00:56:16');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1,18215,6436,'2013-05-05 14:09:54');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',2,21251,8122,'2013-05-24 15:25:45');
+ INSERT INTO sqlite_stat3 VALUES('items','i_created_at',1,24287,11116,'2013-05-26 14:17:39');
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',2560,0,0,NULL);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',18,3022,31,2350);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',10,6068,285,8150);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',158,6346,315,8949);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',34,9094,562,18831);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',20,12139,794,22838);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',134,14033,886,24739);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',159,14167,887,24740);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',161,14326,888,24741);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',161,14487,889,24742);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',124,14648,890,24743);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',157,14772,891,24744);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',126,15043,894,24747);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',40,15169,895,24748);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',161,15243,898,24753);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',138,15404,899,24754);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',160,15542,900,24755);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',161,15702,901,24756);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',161,15863,902,24757);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',124,16024,903,24758);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',155,16148,904,24759);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',26,18208,1043,29704);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',2,21251,1282,32952);
+ INSERT INTO sqlite_stat3 VALUES('items','i_parent_id',13,24279,1583,36068);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid',25167,0,0,2);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid',736,25167,1,3);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid',15,25903,2,4);
+ INSERT INTO sqlite_stat3 VALUES('items','i_secid',1398,25918,3,5);
+ ANALYZE sqlite_master;
+
+ explain query plan
+ select items.title
+ from items
+ join items as child on child.parent_id=items.id
+ join items as grandchild on grandchild.parent_id=child.id
+ join settings
+ on settings.guid=grandchild.guid
+ and settings.account_id=1
+ where items.metadata_type=2
+ and items.secid=2
+ and settings.last_viewed_at is not null
+ group by items.id
+ order by settings.last_viewed_at desc
+ limit 10;
+} [list \
+ 0 0 3 {SEARCH TABLE settings USING INDEX s_account_id (account_id=?)} \
+ 0 1 2 {SEARCH TABLE items AS grandchild USING INDEX i_guid (guid=?)} \
+ 0 2 1 {SEARCH TABLE items AS child USING INTEGER PRIMARY KEY (rowid=?)} \
+ 0 3 0 {SEARCH TABLE items USING INTEGER PRIMARY KEY (rowid=?)} \
+ 0 0 0 {USE TEMP B-TREE FOR GROUP BY} \
+ 0 0 0 {USE TEMP B-TREE FOR ORDER BY}]
+
+
+finish_test
diff --git a/test/win32heap.test b/test/win32heap.test
new file mode 100644
index 0000000..b92f804
--- /dev/null
+++ b/test/win32heap.test
@@ -0,0 +1,74 @@
+# 2013 November 22
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is recovery from transient manditory locks
+# that sometimes appear on database files due to anti-virus software.
+#
+
+if {$tcl_platform(platform)!="windows"} return
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !win32malloc {
+ finish_test
+ return
+}
+
+set testprefix win32heap
+
+do_test 1.1 {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_heap_size 1048576
+ sqlite3_initialize
+} {SQLITE_OK}
+
+do_test 1.2 {
+ sqlite3 db test.db
+ catchsql {
+ CREATE TABLE t1(x);
+ }
+} {0 {}}
+
+do_test 1.3 {
+ catchsql {
+ INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576));
+ }
+} {1 {out of memory}}
+
+do_test 1.4 {
+ catchsql {
+ SELECT COUNT(*) FROM t1;
+ }
+} {0 0}
+
+do_test 1.5 {
+ catch {db close}
+ sqlite3_shutdown
+ sqlite3_config_heap_size 0
+ sqlite3_initialize
+} {SQLITE_OK}
+
+do_test 1.6 {
+ sqlite3 db test.db
+ catchsql {
+ INSERT INTO t1 (x) VALUES(RANDOMBLOB(1048576));
+ }
+} {0 {}}
+
+do_test 1.7 {
+ catchsql {
+ SELECT COUNT(*) FROM t1;
+ }
+} {0 1}
+
+finish_test
diff --git a/test/win32lock.test b/test/win32lock.test
index 7241720..cff1ed3 100644
--- a/test/win32lock.test
+++ b/test/win32lock.test
@@ -24,7 +24,7 @@ db close
sqlite3_shutdown
test_sqlite3_log xLog
proc xLog {error_code msg} {
- lappend ::log $msg
+ lappend ::log $msg
}
sqlite3 db test.db
db eval {PRAGMA mmap_size=0}
@@ -128,7 +128,53 @@ while {1} {
file_control_win32_av_retry db 10 25
sqlite3_test_control_pending_byte $old_pending_byte
db close
+forcedelete test.db
+
+sqlite3 db test.db
+sqlite3 db2 test.db
+
+do_test win32lock-3.0 {
+ db eval {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ }
+} {}
+
+do_test win32lock-3.1 {
+ db eval {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(4);
+ }
+} {}
+
+do_test win32lock-3.2 {
+ catchsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(5);
+ COMMIT;
+ } db2
+} {1 {database is locked}}
+
+do_test win32lock-3.3 {
+ db eval {
+ COMMIT;
+ }
+} {}
+
+do_test win32lock-3.4 {
+ set handle [lindex [file_control_win32_set_handle db 0] end]
+ list [catchsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(6);
+ COMMIT;
+ }] [file_control_win32_set_handle db $handle] [sqlite3_extended_errcode db]
+} {{1 {disk I/O error}} {0 0} SQLITE_IOERR_LOCK}
+
+db2 close
+db close
sqlite3_shutdown
-test_sqlite3_log
+test_sqlite3_log
sqlite3_initialize
finish_test
diff --git a/test/win32longpath.test b/test/win32longpath.test
new file mode 100644
index 0000000..9e9ed35
--- /dev/null
+++ b/test/win32longpath.test
@@ -0,0 +1,110 @@
+# 2013 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing the file name handling provided
+# by the "win32-longpath" VFS.
+#
+
+if {$tcl_platform(platform)!="windows"} return
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix win32longpath
+
+do_test 1.0 {
+ file_control_vfsname db
+} win32
+
+db close
+set path [file nativename [get_pwd]]
+sqlite3 db [file join $path test.db] -vfs win32-longpath
+
+do_test 1.1 {
+ file_control_vfsname db
+} win32-longpath
+
+do_test 1.2 {
+ db eval {
+ BEGIN EXCLUSIVE;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {1 2 3 4}
+
+set longPath(1) \\\\?\\$path\\[pid]
+make_win32_dir $longPath(1)
+
+set longPath(2) $longPath(1)\\[string repeat X 255]
+make_win32_dir $longPath(2)
+
+set longPath(3) $longPath(2)\\[string repeat Y 255]
+make_win32_dir $longPath(3)
+
+set fileName $longPath(3)\\test.db
+
+do_test 1.3 {
+ list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
+} {1 {unable to open database file}}
+
+sqlite3 db3 $fileName -vfs win32-longpath
+
+do_test 1.4 {
+ db3 eval {
+ BEGIN EXCLUSIVE;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(5);
+ INSERT INTO t1 VALUES(6);
+ INSERT INTO t1 VALUES(7);
+ INSERT INTO t1 VALUES(8);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {5 6 7 8}
+
+db3 close
+# puts " Database exists \{[exists_win32_path $fileName]\}"
+
+sqlite3 db3 $fileName -vfs win32-longpath
+
+do_test 1.5 {
+ db3 eval {
+ PRAGMA journal_mode = WAL;
+ }
+} {wal}
+
+do_test 1.6 {
+ db3 eval {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(10);
+ INSERT INTO t1 VALUES(11);
+ INSERT INTO t1 VALUES(12);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {5 6 7 8 9 10 11 12}
+
+db3 close
+# puts " Database exists \{[exists_win32_path $fileName]\}"
+
+do_delete_win32_file $fileName
+# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
+
+do_remove_win32_dir $longPath(3)
+do_remove_win32_dir $longPath(2)
+do_remove_win32_dir $longPath(1)
+
+finish_test
diff --git a/test/with1.test b/test/with1.test
new file mode 100644
index 0000000..42d2277
--- /dev/null
+++ b/test/with1.test
@@ -0,0 +1,831 @@
+# 2014 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 WITH clause.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix with1
+
+ifcapable {!cte} {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x INTEGER, y INTEGER);
+ WITH x(a) AS ( SELECT * FROM t1) SELECT 10
+} {10}
+
+do_execsql_test 1.1 {
+ SELECT * FROM ( WITH x AS ( SELECT * FROM t1) SELECT 10 );
+} {10}
+
+do_execsql_test 1.2 {
+ WITH x(a) AS ( SELECT * FROM t1) INSERT INTO t1 VALUES(1,2);
+} {}
+
+do_execsql_test 1.3 {
+ WITH x(a) AS ( SELECT * FROM t1) DELETE FROM t1;
+} {}
+
+do_execsql_test 1.4 {
+ WITH x(a) AS ( SELECT * FROM t1) UPDATE t1 SET x = y;
+} {}
+
+#--------------------------------------------------------------------------
+
+do_execsql_test 2.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ WITH tmp AS ( SELECT * FROM t1 ) SELECT x FROM tmp;
+} {1 2}
+
+do_execsql_test 2.2 {
+ WITH tmp(a) AS ( SELECT * FROM t1 ) SELECT a FROM tmp;
+} {1 2}
+
+do_execsql_test 2.3 {
+ SELECT * FROM (
+ WITH tmp(a) AS ( SELECT * FROM t1 ) SELECT a FROM tmp
+ );
+} {1 2}
+
+do_execsql_test 2.4 {
+ WITH tmp1(a) AS ( SELECT * FROM t1 ),
+ tmp2(x) AS ( SELECT * FROM tmp1)
+ SELECT * FROM tmp2;
+} {1 2}
+
+do_execsql_test 2.5 {
+ WITH tmp2(x) AS ( SELECT * FROM tmp1),
+ tmp1(a) AS ( SELECT * FROM t1 )
+ SELECT * FROM tmp2;
+} {1 2}
+
+#-------------------------------------------------------------------------
+do_catchsql_test 3.1 {
+ WITH tmp2(x) AS ( SELECT * FROM tmp1 ),
+ tmp1(a) AS ( SELECT * FROM tmp2 )
+ SELECT * FROM tmp1;
+} {1 {circular reference: tmp1}}
+
+do_catchsql_test 3.2 {
+ CREATE TABLE t2(x INTEGER);
+ WITH tmp(a) AS (SELECT * FROM t1),
+ tmp(a) AS (SELECT * FROM t1)
+ SELECT * FROM tmp;
+} {1 {duplicate WITH table name: tmp}}
+
+do_execsql_test 3.3 {
+ CREATE TABLE t3(x);
+ CREATE TABLE t4(x);
+
+ INSERT INTO t3 VALUES('T3');
+ INSERT INTO t4 VALUES('T4');
+
+ WITH t3(a) AS (SELECT * FROM t4)
+ SELECT * FROM t3;
+} {T4}
+
+do_execsql_test 3.4 {
+ WITH tmp AS ( SELECT * FROM t3 ),
+ tmp2 AS ( WITH tmp AS ( SELECT * FROM t4 ) SELECT * FROM tmp )
+ SELECT * FROM tmp2;
+} {T4}
+
+do_execsql_test 3.5 {
+ WITH tmp AS ( SELECT * FROM t3 ),
+ tmp2 AS ( WITH xxxx AS ( SELECT * FROM t4 ) SELECT * FROM tmp )
+ SELECT * FROM tmp2;
+} {T3}
+
+do_catchsql_test 3.6 {
+ WITH tmp AS ( SELECT * FROM t3 ),
+ SELECT * FROM tmp;
+} {1 {near "SELECT": syntax error}}
+
+#-------------------------------------------------------------------------
+do_execsql_test 4.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+
+ WITH dset AS ( SELECT 2 UNION ALL SELECT 4 )
+ DELETE FROM t1 WHERE x IN dset;
+ SELECT * FROM t1;
+} {1 3}
+
+do_execsql_test 4.2 {
+ WITH iset AS ( SELECT 2 UNION ALL SELECT 4 )
+ INSERT INTO t1 SELECT * FROM iset;
+ SELECT * FROM t1;
+} {1 3 2 4}
+
+do_execsql_test 4.3 {
+ WITH uset(a, b) AS ( SELECT 2, 8 UNION ALL SELECT 4, 9 )
+ UPDATE t1 SET x = COALESCE( (SELECT b FROM uset WHERE a=x), x );
+ SELECT * FROM t1;
+} {1 3 8 9}
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 5.1 {
+ WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i)
+ SELECT x FROM i LIMIT 10;
+} {1 2 3 4 5 6 7 8 9 10}
+
+do_catchsql_test 5.2 {
+ WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i ORDER BY 1)
+ SELECT x FROM i LIMIT 10;
+} {0 {1 2 3 4 5 6 7 8 9 10}}
+
+do_execsql_test 5.2.1 {
+ CREATE TABLE edge(xfrom, xto, seq, PRIMARY KEY(xfrom, xto)) WITHOUT ROWID;
+ INSERT INTO edge VALUES(0, 1, 10);
+ INSERT INTO edge VALUES(1, 2, 20);
+ INSERT INTO edge VALUES(0, 3, 30);
+ INSERT INTO edge VALUES(2, 4, 40);
+ INSERT INTO edge VALUES(3, 4, 40);
+ INSERT INTO edge VALUES(2, 5, 50);
+ INSERT INTO edge VALUES(3, 6, 60);
+ INSERT INTO edge VALUES(5, 7, 70);
+ INSERT INTO edge VALUES(3, 7, 70);
+ INSERT INTO edge VALUES(4, 8, 80);
+ INSERT INTO edge VALUES(7, 8, 80);
+ INSERT INTO edge VALUES(8, 9, 90);
+
+ WITH RECURSIVE
+ ancest(id, mtime) AS
+ (VALUES(0, 0)
+ UNION
+ SELECT edge.xto, edge.seq FROM edge, ancest
+ WHERE edge.xfrom=ancest.id
+ ORDER BY 2
+ )
+ SELECT * FROM ancest;
+} {0 0 1 10 2 20 3 30 4 40 5 50 6 60 7 70 8 80 9 90}
+do_execsql_test 5.2.2 {
+ WITH RECURSIVE
+ ancest(id, mtime) AS
+ (VALUES(0, 0)
+ UNION ALL
+ SELECT edge.xto, edge.seq FROM edge, ancest
+ WHERE edge.xfrom=ancest.id
+ ORDER BY 2
+ )
+ SELECT * FROM ancest;
+} {0 0 1 10 2 20 3 30 4 40 4 40 5 50 6 60 7 70 7 70 8 80 8 80 8 80 8 80 9 90 9 90 9 90 9 90}
+do_execsql_test 5.2.3 {
+ WITH RECURSIVE
+ ancest(id, mtime) AS
+ (VALUES(0, 0)
+ UNION ALL
+ SELECT edge.xto, edge.seq FROM edge, ancest
+ WHERE edge.xfrom=ancest.id
+ ORDER BY 2 LIMIT 4 OFFSET 2
+ )
+ SELECT * FROM ancest;
+} {2 20 3 30 4 40 4 40}
+
+do_catchsql_test 5.3 {
+ WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 5)
+ SELECT x FROM i;
+} {0 {1 2 3 4 5}}
+
+do_execsql_test 5.4 {
+ WITH i(x) AS ( VALUES(1) UNION ALL SELECT (x+1)%10 FROM i)
+ SELECT x FROM i LIMIT 20;
+} {1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0}
+
+do_execsql_test 5.5 {
+ WITH i(x) AS ( VALUES(1) UNION SELECT (x+1)%10 FROM i)
+ SELECT x FROM i LIMIT 20;
+} {1 2 3 4 5 6 7 8 9 0}
+
+do_catchsql_test 5.6.1 {
+ WITH i(x, y) AS ( VALUES(1) )
+ SELECT * FROM i;
+} {1 {table i has 1 values for 2 columns}}
+
+do_catchsql_test 5.6.2 {
+ WITH i(x) AS ( VALUES(1,2) )
+ SELECT * FROM i;
+} {1 {table i has 2 values for 1 columns}}
+
+do_catchsql_test 5.6.3 {
+ CREATE TABLE t5(a, b);
+ WITH i(x) AS ( SELECT * FROM t5 )
+ SELECT * FROM i;
+} {1 {table i has 2 values for 1 columns}}
+
+do_catchsql_test 5.6.4 {
+ WITH i(x) AS ( SELECT 1, 2 UNION ALL SELECT 1 )
+ SELECT * FROM i;
+} {1 {table i has 2 values for 1 columns}}
+
+do_catchsql_test 5.6.5 {
+ WITH i(x) AS ( SELECT 1 UNION ALL SELECT 1, 2 )
+ SELECT * FROM i;
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+
+do_catchsql_test 5.6.6 {
+ WITH i(x) AS ( SELECT 1 UNION ALL SELECT x+1, x*2 FROM i )
+ SELECT * FROM i;
+} {1 {SELECTs to the left and right of UNION ALL do not have the same number of result columns}}
+
+do_catchsql_test 5.6.7 {
+ WITH i(x) AS ( SELECT 1, 2 UNION SELECT x+1 FROM i )
+ SELECT * FROM i;
+} {1 {table i has 2 values for 1 columns}}
+
+#-------------------------------------------------------------------------
+#
+do_execsql_test 6.1 {
+ CREATE TABLE f(
+ id INTEGER PRIMARY KEY, parentid REFERENCES f, name TEXT
+ );
+
+ INSERT INTO f VALUES(0, NULL, '');
+ INSERT INTO f VALUES(1, 0, 'bin');
+ INSERT INTO f VALUES(2, 1, 'true');
+ INSERT INTO f VALUES(3, 1, 'false');
+ INSERT INTO f VALUES(4, 1, 'ls');
+ INSERT INTO f VALUES(5, 1, 'grep');
+ INSERT INTO f VALUES(6, 0, 'etc');
+ INSERT INTO f VALUES(7, 6, 'rc.d');
+ INSERT INTO f VALUES(8, 7, 'rc.apache');
+ INSERT INTO f VALUES(9, 7, 'rc.samba');
+ INSERT INTO f VALUES(10, 0, 'home');
+ INSERT INTO f VALUES(11, 10, 'dan');
+ INSERT INTO f VALUES(12, 11, 'public_html');
+ INSERT INTO f VALUES(13, 12, 'index.html');
+ INSERT INTO f VALUES(14, 13, 'logo.gif');
+}
+
+do_execsql_test 6.2 {
+ WITH flat(fid, fpath) AS (
+ SELECT id, '' FROM f WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, fpath || '/' || name FROM f, flat WHERE parentid=fid
+ )
+ SELECT fpath FROM flat WHERE fpath!='' ORDER BY 1;
+} {
+ /bin
+ /bin/false /bin/grep /bin/ls /bin/true
+ /etc
+ /etc/rc.d
+ /etc/rc.d/rc.apache /etc/rc.d/rc.samba
+ /home
+ /home/dan
+ /home/dan/public_html
+ /home/dan/public_html/index.html
+ /home/dan/public_html/index.html/logo.gif
+}
+
+do_execsql_test 6.3 {
+ WITH flat(fid, fpath) AS (
+ SELECT id, '' FROM f WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, fpath || '/' || name FROM f, flat WHERE parentid=fid
+ )
+ SELECT count(*) FROM flat;
+} {15}
+
+do_execsql_test 6.4 {
+ WITH x(i) AS (
+ SELECT 1
+ UNION ALL
+ SELECT i+1 FROM x WHERE i<10
+ )
+ SELECT count(*) FROM x
+} {10}
+
+
+#-------------------------------------------------------------------------
+
+do_execsql_test 7.1 {
+ CREATE TABLE tree(i, p);
+ INSERT INTO tree VALUES(1, NULL);
+ INSERT INTO tree VALUES(2, 1);
+ INSERT INTO tree VALUES(3, 1);
+ INSERT INTO tree VALUES(4, 2);
+ INSERT INTO tree VALUES(5, 4);
+}
+
+do_execsql_test 7.2 {
+ WITH t(id, path) AS (
+ SELECT i, '' FROM tree WHERE p IS NULL
+ UNION ALL
+ SELECT i, path || '/' || i FROM tree, t WHERE p = id
+ )
+ SELECT path FROM t;
+} {{} /2 /3 /2/4 /2/4/5}
+
+do_execsql_test 7.3 {
+ WITH t(id) AS (
+ VALUES(2)
+ UNION ALL
+ SELECT i FROM tree, t WHERE p = id
+ )
+ SELECT id FROM t;
+} {2 4 5}
+
+do_catchsql_test 7.4 {
+ WITH t(id) AS (
+ VALUES(2)
+ UNION ALL
+ SELECT i FROM tree WHERE p IN (SELECT id FROM t)
+ )
+ SELECT id FROM t;
+} {1 {recursive reference in a subquery: t}}
+
+do_catchsql_test 7.5 {
+ WITH t(id) AS (
+ VALUES(2)
+ UNION ALL
+ SELECT i FROM tree, t WHERE p = id AND p IN (SELECT id FROM t)
+ )
+ SELECT id FROM t;
+} {1 {multiple recursive references: t}}
+
+do_catchsql_test 7.6 {
+ WITH t(id) AS (
+ SELECT i FROM tree WHERE 2 IN (SELECT id FROM t)
+ UNION ALL
+ SELECT i FROM tree, t WHERE p = id
+ )
+ SELECT id FROM t;
+} {1 {circular reference: t}}
+
+# Compute the mandelbrot set using a recursive query
+#
+do_execsql_test 8.1-mandelbrot {
+ WITH RECURSIVE
+ xaxis(x) AS (VALUES(-2.0) UNION ALL SELECT x+0.05 FROM xaxis WHERE x<1.2),
+ yaxis(y) AS (VALUES(-1.0) UNION ALL SELECT y+0.1 FROM yaxis WHERE y<1.0),
+ m(iter, cx, cy, x, y) AS (
+ SELECT 0, x, y, 0.0, 0.0 FROM xaxis, yaxis
+ UNION ALL
+ SELECT iter+1, cx, cy, x*x-y*y + cx, 2.0*x*y + cy FROM m
+ WHERE (x*x + y*y) < 4.0 AND iter<28
+ ),
+ m2(iter, cx, cy) AS (
+ SELECT max(iter), cx, cy FROM m GROUP BY cx, cy
+ ),
+ a(t) AS (
+ SELECT group_concat( substr(' .+*#', 1+min(iter/7,4), 1), '')
+ FROM m2 GROUP BY cy
+ )
+ SELECT group_concat(rtrim(t),x'0a') FROM a;
+} {{ ....#
+ ..#*..
+ ..+####+.
+ .......+####.... +
+ ..##+*##########+.++++
+ .+.##################+.
+ .............+###################+.+
+ ..++..#.....*#####################+.
+ ...+#######++#######################.
+ ....+*################################.
+ #############################################...
+ ....+*################################.
+ ...+#######++#######################.
+ ..++..#.....*#####################+.
+ .............+###################+.+
+ .+.##################+.
+ ..##+*##########+.++++
+ .......+####.... +
+ ..+####+.
+ ..#*..
+ ....#
+ +.}}
+
+# Solve a sudoku puzzle using a recursive query
+#
+do_execsql_test 8.2-soduko {
+ WITH RECURSIVE
+ input(sud) AS (
+ VALUES('53..7....6..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79')
+ ),
+
+ /* A table filled with digits 1..9, inclusive. */
+ digits(z, lp) AS (
+ VALUES('1', 1)
+ UNION ALL SELECT
+ CAST(lp+1 AS TEXT), lp+1 FROM digits WHERE lp<9
+ ),
+
+ /* The tricky bit. */
+ x(s, ind) AS (
+ SELECT sud, instr(sud, '.') FROM input
+ UNION ALL
+ SELECT
+ substr(s, 1, ind-1) || z || substr(s, ind+1),
+ instr( substr(s, 1, ind-1) || z || substr(s, ind+1), '.' )
+ FROM x, digits AS z
+ WHERE ind>0
+ AND NOT EXISTS (
+ SELECT 1
+ FROM digits AS lp
+ WHERE z.z = substr(s, ((ind-1)/9)*9 + lp, 1)
+ OR z.z = substr(s, ((ind-1)%9) + (lp-1)*9 + 1, 1)
+ OR z.z = substr(s, (((ind-1)/3) % 3) * 3
+ + ((ind-1)/27) * 27 + lp
+ + ((lp-1) / 3) * 6, 1)
+ )
+ )
+ SELECT s FROM x WHERE ind=0;
+} {534678912672195348198342567859761423426853791713924856961537284287419635345286179}
+
+#--------------------------------------------------------------------------
+# Some tests that use LIMIT and OFFSET in the definition of recursive CTEs.
+#
+set I [list 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
+proc limit_test {tn iLimit iOffset} {
+ if {$iOffset < 0} { set iOffset 0 }
+ if {$iLimit < 0 } {
+ set result [lrange $::I $iOffset end]
+ } else {
+ set result [lrange $::I $iOffset [expr $iLimit+$iOffset-1]]
+ }
+ uplevel [list do_execsql_test $tn [subst -nocommands {
+ WITH ii(a) AS (
+ VALUES(1)
+ UNION ALL
+ SELECT a+1 FROM ii WHERE a<20
+ LIMIT $iLimit OFFSET $iOffset
+ )
+ SELECT * FROM ii
+ }] $result]
+}
+
+limit_test 9.1 20 0
+limit_test 9.2 0 0
+limit_test 9.3 19 1
+limit_test 9.4 20 -1
+limit_test 9.5 5 5
+limit_test 9.6 0 -1
+limit_test 9.7 40 -1
+limit_test 9.8 -1 -1
+limit_test 9.9 -1 -1
+
+#--------------------------------------------------------------------------
+# Test the ORDER BY clause on recursive tables.
+#
+
+do_execsql_test 10.1 {
+ DROP TABLE IF EXISTS tree;
+ CREATE TABLE tree(id INTEGER PRIMARY KEY, parentid, payload);
+}
+
+proc insert_into_tree {L} {
+ db eval { DELETE FROM tree }
+ foreach key $L {
+ unset -nocomplain parentid
+ foreach seg [split $key /] {
+ if {$seg==""} continue
+ set id [db one {
+ SELECT id FROM tree WHERE parentid IS $parentid AND payload=$seg
+ }]
+ if {$id==""} {
+ db eval { INSERT INTO tree VALUES(NULL, $parentid, $seg) }
+ set parentid [db last_insert_rowid]
+ } else {
+ set parentid $id
+ }
+ }
+ }
+}
+
+insert_into_tree {
+ /a/a/a
+ /a/b/c
+ /a/b/c/d
+ /a/b/d
+}
+do_execsql_test 10.2 {
+ WITH flat(fid, p) AS (
+ SELECT id, '/' || payload FROM tree WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, p || '/' || payload FROM flat, tree WHERE parentid=fid
+ )
+ SELECT p FROM flat ORDER BY p;
+} {
+ /a /a/a /a/a/a
+ /a/b /a/b/c /a/b/c/d
+ /a/b/d
+}
+
+# Scan the tree-structure currently stored in table tree. Return a list
+# of nodes visited.
+#
+proc scan_tree {bDepthFirst bReverse} {
+
+ set order "ORDER BY "
+ if {$bDepthFirst==0} { append order "2 ASC," }
+ if {$bReverse==0} {
+ append order " 3 ASC"
+ } else {
+ append order " 3 DESC"
+ }
+
+ db eval "
+ WITH flat(fid, depth, p) AS (
+ SELECT id, 1, '/' || payload FROM tree WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, depth+1, p||'/'||payload FROM flat, tree WHERE parentid=fid
+ $order
+ )
+ SELECT p FROM flat;
+ "
+}
+
+insert_into_tree {
+ /a/b
+ /a/b/c
+ /a/d
+ /a/d/e
+ /a/d/f
+ /g/h
+}
+
+# Breadth first, siblings in ascending order.
+#
+do_test 10.3 {
+ scan_tree 0 0
+} [list {*}{
+ /a /g
+ /a/b /a/d /g/h
+ /a/b/c /a/d/e /a/d/f
+}]
+
+# Depth first, siblings in ascending order.
+#
+do_test 10.4 {
+ scan_tree 1 0
+} [list {*}{
+ /a /a/b /a/b/c
+ /a/d /a/d/e
+ /a/d/f
+ /g /g/h
+}]
+
+# Breadth first, siblings in descending order.
+#
+do_test 10.5 {
+ scan_tree 0 1
+} [list {*}{
+ /g /a
+ /g/h /a/d /a/b
+ /a/d/f /a/d/e /a/b/c
+}]
+
+# Depth first, siblings in ascending order.
+#
+do_test 10.6 {
+ scan_tree 1 1
+} [list {*}{
+ /g /g/h
+ /a /a/d /a/d/f
+ /a/d/e
+ /a/b /a/b/c
+}]
+
+
+# Test name resolution in ORDER BY clauses.
+#
+do_catchsql_test 10.7.1 {
+ WITH t(a) AS (
+ SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY a
+ )
+ SELECT * FROM t
+} {1 {1st ORDER BY term does not match any column in the result set}}
+do_execsql_test 10.7.2 {
+ WITH t(a) AS (
+ SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY b
+ )
+ SELECT * FROM t
+} {1 2 3 4 5}
+do_execsql_test 10.7.3 {
+ WITH t(a) AS (
+ SELECT 1 AS b UNION ALL SELECT a+1 AS c FROM t WHERE a<5 ORDER BY c
+ )
+ SELECT * FROM t
+} {1 2 3 4 5}
+
+# Test COLLATE clauses attached to ORDER BY.
+#
+insert_into_tree {
+ /a/b
+ /a/C
+ /a/d
+ /B/e
+ /B/F
+ /B/g
+ /c/h
+ /c/I
+ /c/j
+}
+
+do_execsql_test 10.8.1 {
+ WITH flat(fid, depth, p) AS (
+ SELECT id, 1, '/' || payload FROM tree WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, depth+1, p||'/'||payload FROM flat, tree WHERE parentid=fid
+ ORDER BY 2, 3 COLLATE nocase
+ )
+ SELECT p FROM flat;
+} {
+ /a /B /c
+ /a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
+}
+do_execsql_test 10.8.2 {
+ WITH flat(fid, depth, p) AS (
+ SELECT id, 1, ('/' || payload) COLLATE nocase
+ FROM tree WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, depth+1, (p||'/'||payload)
+ FROM flat, tree WHERE parentid=fid
+ ORDER BY 2, 3
+ )
+ SELECT p FROM flat;
+} {
+ /a /B /c
+ /a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
+}
+
+do_execsql_test 10.8.3 {
+ WITH flat(fid, depth, p) AS (
+ SELECT id, 1, ('/' || payload)
+ FROM tree WHERE parentid IS NULL
+ UNION ALL
+ SELECT id, depth+1, (p||'/'||payload) COLLATE nocase
+ FROM flat, tree WHERE parentid=fid
+ ORDER BY 2, 3
+ )
+ SELECT p FROM flat;
+} {
+ /a /B /c
+ /a/b /a/C /a/d /B/e /B/F /B/g /c/h /c/I /c/j
+}
+
+do_execsql_test 10.8.4.1 {
+ CREATE TABLE tst(a,b);
+ INSERT INTO tst VALUES('a', 'A');
+ INSERT INTO tst VALUES('b', 'B');
+ INSERT INTO tst VALUES('c', 'C');
+ SELECT a COLLATE nocase FROM tst UNION ALL SELECT b FROM tst ORDER BY 1;
+} {a A b B c C}
+do_execsql_test 10.8.4.2 {
+ SELECT a FROM tst UNION ALL SELECT b COLLATE nocase FROM tst ORDER BY 1;
+} {A B C a b c}
+do_execsql_test 10.8.4.3 {
+ SELECT a||'' FROM tst UNION ALL SELECT b COLLATE nocase FROM tst ORDER BY 1;
+} {a A b B c C}
+
+# Test cases to illustrate on the ORDER BY clause on a recursive query can be
+# used to control depth-first versus breath-first search in a tree.
+#
+do_execsql_test 11.1 {
+ CREATE TABLE org(
+ name TEXT PRIMARY KEY,
+ boss TEXT REFERENCES org
+ ) WITHOUT ROWID;
+ INSERT INTO org VALUES('Alice',NULL);
+ INSERT INTO org VALUES('Bob','Alice');
+ INSERT INTO org VALUES('Cindy','Alice');
+ INSERT INTO org VALUES('Dave','Bob');
+ INSERT INTO org VALUES('Emma','Bob');
+ INSERT INTO org VALUES('Fred','Cindy');
+ INSERT INTO org VALUES('Gail','Cindy');
+ INSERT INTO org VALUES('Harry','Dave');
+ INSERT INTO org VALUES('Ingrid','Dave');
+ INSERT INTO org VALUES('Jim','Emma');
+ INSERT INTO org VALUES('Kate','Emma');
+ INSERT INTO org VALUES('Lanny','Fred');
+ INSERT INTO org VALUES('Mary','Fred');
+ INSERT INTO org VALUES('Noland','Gail');
+ INSERT INTO org VALUES('Olivia','Gail');
+ -- The above are all under Alice. Add a few more records for people
+ -- not in Alice's group, just to prove that they won't be selected.
+ INSERT INTO org VALUES('Xaviar',NULL);
+ INSERT INTO org VALUES('Xia','Xaviar');
+ INSERT INTO org VALUES('Xerxes','Xaviar');
+ INSERT INTO org VALUES('Xena','Xia');
+ -- Find all members of Alice's group, breath-first order
+ WITH RECURSIVE
+ under_alice(name,level) AS (
+ VALUES('Alice','0')
+ UNION ALL
+ SELECT org.name, under_alice.level+1
+ FROM org, under_alice
+ WHERE org.boss=under_alice.name
+ ORDER BY 2
+ )
+ SELECT group_concat(substr('...............',1,level*3) || name,x'0a')
+ FROM under_alice;
+} {{Alice
+...Bob
+...Cindy
+......Dave
+......Emma
+......Fred
+......Gail
+.........Harry
+.........Ingrid
+.........Jim
+.........Kate
+.........Lanny
+.........Mary
+.........Noland
+.........Olivia}}
+
+# The previous query used "ORDER BY level" to yield a breath-first search.
+# Change that to "ORDER BY level DESC" for a depth-first search.
+#
+do_execsql_test 11.2 {
+ WITH RECURSIVE
+ under_alice(name,level) AS (
+ VALUES('Alice','0')
+ UNION ALL
+ SELECT org.name, under_alice.level+1
+ FROM org, under_alice
+ WHERE org.boss=under_alice.name
+ ORDER BY 2 DESC
+ )
+ SELECT group_concat(substr('...............',1,level*3) || name,x'0a')
+ FROM under_alice;
+} {{Alice
+...Bob
+......Dave
+.........Harry
+.........Ingrid
+......Emma
+.........Jim
+.........Kate
+...Cindy
+......Fred
+.........Lanny
+.........Mary
+......Gail
+.........Noland
+.........Olivia}}
+
+# Without an ORDER BY clause, the recursive query should use a FIFO,
+# resulting in a breath-first search.
+#
+do_execsql_test 11.3 {
+ WITH RECURSIVE
+ under_alice(name,level) AS (
+ VALUES('Alice','0')
+ UNION ALL
+ SELECT org.name, under_alice.level+1
+ FROM org, under_alice
+ WHERE org.boss=under_alice.name
+ )
+ SELECT group_concat(substr('...............',1,level*3) || name,x'0a')
+ FROM under_alice;
+} {{Alice
+...Bob
+...Cindy
+......Dave
+......Emma
+......Fred
+......Gail
+.........Harry
+.........Ingrid
+.........Jim
+.........Kate
+.........Lanny
+.........Mary
+.........Noland
+.........Olivia}}
+
+#--------------------------------------------------------------------------
+# Ticket [31a19d11b97088296ac104aaff113a9790394927] (2014-02-09)
+# Name resolution issue with compound SELECTs and Common Table Expressions
+#
+do_execsql_test 12.1 {
+WITH RECURSIVE
+ t1(x) AS (VALUES(2) UNION ALL SELECT x+2 FROM t1 WHERE x<20),
+ t2(y) AS (VALUES(3) UNION ALL SELECT y+3 FROM t2 WHERE y<20)
+SELECT x FROM t1 EXCEPT SELECT y FROM t2 ORDER BY 1;
+} {2 4 8 10 14 16 20}
+
+
+finish_test
diff --git a/test/with2.test b/test/with2.test
new file mode 100644
index 0000000..eb06147
--- /dev/null
+++ b/test/with2.test
@@ -0,0 +1,418 @@
+# 2014 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 WITH clause.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix with2
+
+ifcapable {!cte} {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+}
+
+do_execsql_test 1.1 {
+ WITH x1 AS (SELECT * FROM t1)
+ SELECT sum(a) FROM x1;
+} {3}
+
+do_execsql_test 1.2 {
+ WITH x1 AS (SELECT * FROM t1)
+ SELECT (SELECT sum(a) FROM x1);
+} {3}
+
+do_execsql_test 1.3 {
+ WITH x1 AS (SELECT * FROM t1)
+ SELECT (SELECT sum(a) FROM x1);
+} {3}
+
+do_execsql_test 1.4 {
+ CREATE TABLE t2(i);
+ INSERT INTO t2 VALUES(2);
+ INSERT INTO t2 VALUES(3);
+ INSERT INTO t2 VALUES(5);
+
+ WITH x1 AS (SELECT i FROM t2),
+ i(a) AS (
+ SELECT min(i)-1 FROM x1 UNION SELECT a+1 FROM i WHERE a<10
+ )
+ SELECT a FROM i WHERE a NOT IN x1
+} {1 4 6 7 8 9 10}
+
+do_execsql_test 1.5 {
+ WITH x1 AS (SELECT a FROM t1),
+ x2 AS (SELECT i FROM t2),
+ x3 AS (SELECT * FROM x1, x2 WHERE x1.a IN x2 AND x2.i IN x1)
+ SELECT * FROM x3
+} {2 2}
+
+do_execsql_test 1.6 {
+ CREATE TABLE t3 AS SELECT 3 AS x;
+ CREATE TABLE t4 AS SELECT 4 AS x;
+
+ WITH x1 AS (SELECT * FROM t3),
+ x2 AS (
+ WITH t3 AS (SELECT * FROM t4)
+ SELECT * FROM x1
+ )
+ SELECT * FROM x2;
+} {3}
+
+do_execsql_test 1.7 {
+ WITH x2 AS (
+ WITH t3 AS (SELECT * FROM t4)
+ SELECT * FROM t3
+ )
+ SELECT * FROM x2;
+} {4}
+
+do_execsql_test 1.8 {
+ WITH x2 AS (
+ WITH t3 AS (SELECT * FROM t4)
+ SELECT * FROM main.t3
+ )
+ SELECT * FROM x2;
+} {3}
+
+do_execsql_test 1.9 {
+ WITH x1 AS (SELECT * FROM t1)
+ SELECT (SELECT sum(a) FROM x1), (SELECT max(a) FROM x1);
+} {3 2}
+
+do_execsql_test 1.10 {
+ WITH x1 AS (SELECT * FROM t1)
+ SELECT (SELECT sum(a) FROM x1), (SELECT max(a) FROM x1), a FROM x1;
+} {3 2 1 3 2 2}
+
+do_execsql_test 1.11 {
+ WITH
+ i(x) AS (
+ WITH
+ j(x) AS ( SELECT * FROM i ),
+ i(x) AS ( SELECT * FROM t1 )
+ SELECT * FROM j
+ )
+ SELECT * FROM i;
+} {1 2}
+
+do_execsql_test 1.12 {
+ WITH r(i) AS (
+ VALUES('.')
+ UNION ALL
+ SELECT i || '.' FROM r, (
+ SELECT x FROM x INTERSECT SELECT y FROM y
+ ) WHERE length(i) < 10
+ ),
+ x(x) AS ( VALUES(1) UNION ALL VALUES(2) UNION ALL VALUES(3) ),
+ y(y) AS ( VALUES(2) UNION ALL VALUES(4) UNION ALL VALUES(6) )
+
+ SELECT * FROM r;
+} {. .. ... .... ..... ...... ....... ........ ......... ..........}
+
+do_execsql_test 1.13 {
+ WITH r(i) AS (
+ VALUES('.')
+ UNION ALL
+ SELECT i || '.' FROM r, ( SELECT x FROM x WHERE x=2 ) WHERE length(i) < 10
+ ),
+ x(x) AS ( VALUES(1) UNION ALL VALUES(2) UNION ALL VALUES(3) )
+
+ SELECT * FROM r ORDER BY length(i) DESC;
+} {.......... ......... ........ ....... ...... ..... .... ... .. .}
+
+do_execsql_test 1.14 {
+ WITH
+ t4(x) AS (
+ VALUES(4)
+ UNION ALL
+ SELECT x+1 FROM t4 WHERE x<10
+ )
+ SELECT * FROM t4;
+} {4 5 6 7 8 9 10}
+
+do_execsql_test 1.15 {
+ WITH
+ t4(x) AS (
+ VALUES(4)
+ UNION ALL
+ SELECT x+1 FROM main.t4 WHERE x<10
+ )
+ SELECT * FROM t4;
+} {4 5}
+
+do_catchsql_test 1.16 {
+ WITH
+ t4(x) AS (
+ VALUES(4)
+ UNION ALL
+ SELECT x+1 FROM t4, main.t4, t4 WHERE x<10
+ )
+ SELECT * FROM t4;
+} {1 {multiple references to recursive table: t4}}
+
+
+#---------------------------------------------------------------------------
+# Check that variables can be used in CTEs.
+#
+set ::min [expr 3]
+set ::max [expr 9]
+do_execsql_test 2.1 {
+ WITH i(x) AS (
+ VALUES($min) UNION ALL SELECT x+1 FROM i WHERE x < $max
+ )
+ SELECT * FROM i;
+} {3 4 5 6 7 8 9}
+
+do_execsql_test 2.2 {
+ WITH i(x) AS (
+ VALUES($min) UNION ALL SELECT x+1 FROM i WHERE x < $max
+ )
+ SELECT x FROM i JOIN i AS j USING (x);
+} {3 4 5 6 7 8 9}
+
+#---------------------------------------------------------------------------
+# Check that circular references are rejected.
+#
+do_catchsql_test 3.1 {
+ WITH i(x, y) AS ( VALUES(1, (SELECT x FROM i)) )
+ SELECT * FROM i;
+} {1 {circular reference: i}}
+
+do_catchsql_test 3.2 {
+ WITH
+ i(x) AS ( SELECT * FROM j ),
+ j(x) AS ( SELECT * FROM k ),
+ k(x) AS ( SELECT * FROM i )
+ SELECT * FROM i;
+} {1 {circular reference: i}}
+
+do_catchsql_test 3.3 {
+ WITH
+ i(x) AS ( SELECT * FROM (SELECT * FROM j) ),
+ j(x) AS ( SELECT * FROM (SELECT * FROM i) )
+ SELECT * FROM i;
+} {1 {circular reference: i}}
+
+do_catchsql_test 3.4 {
+ WITH
+ i(x) AS ( SELECT * FROM (SELECT * FROM j) ),
+ j(x) AS ( SELECT * FROM (SELECT * FROM i) )
+ SELECT * FROM j;
+} {1 {circular reference: j}}
+
+do_catchsql_test 3.5 {
+ WITH
+ i(x) AS (
+ WITH j(x) AS ( SELECT * FROM i )
+ SELECT * FROM j
+ )
+ SELECT * FROM i;
+} {1 {circular reference: i}}
+
+#---------------------------------------------------------------------------
+# Try empty and very long column lists.
+#
+do_catchsql_test 4.1 {
+ WITH x() AS ( SELECT 1,2,3 )
+ SELECT * FROM x;
+} {1 {near ")": syntax error}}
+
+proc genstmt {n} {
+ for {set i 1} {$i<=$n} {incr i} {
+ lappend cols "c$i"
+ lappend vals $i
+ }
+ return "
+ WITH x([join $cols ,]) AS (SELECT [join $vals ,])
+ SELECT (c$n == $n) FROM x
+ "
+}
+
+do_execsql_test 4.2 [genstmt 10] 1
+do_execsql_test 4.3 [genstmt 100] 1
+do_execsql_test 4.4 [genstmt 255] 1
+set nLimit [sqlite3_limit db SQLITE_LIMIT_COLUMN -1]
+do_execsql_test 4.5 [genstmt [expr $nLimit-1]] 1
+do_execsql_test 4.6 [genstmt $nLimit] 1
+do_catchsql_test 4.7 [genstmt [expr $nLimit+1]] {1 {too many columns in index}}
+
+#---------------------------------------------------------------------------
+# Check that adding a WITH clause to an INSERT disables the xfer
+# optimization.
+#
+proc do_xfer_test {tn bXfer sql {res {}}} {
+ set ::sqlite3_xferopt_count 0
+ uplevel [list do_test $tn [subst -nocommands {
+ set dres [db eval {$sql}]
+ list [set ::sqlite3_xferopt_count] [set dres]
+ }] [list $bXfer $res]]
+}
+
+do_execsql_test 5.1 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+}
+
+do_xfer_test 5.2 1 { INSERT INTO t1 SELECT * FROM t2 }
+do_xfer_test 5.3 0 { INSERT INTO t1 SELECT a, b FROM t2 }
+do_xfer_test 5.4 0 { INSERT INTO t1 SELECT b, a FROM t2 }
+do_xfer_test 5.5 0 {
+ WITH x AS (SELECT a, b FROM t2) INSERT INTO t1 SELECT * FROM x
+}
+do_xfer_test 5.6 0 {
+ WITH x AS (SELECT a, b FROM t2) INSERT INTO t1 SELECT * FROM t2
+}
+do_xfer_test 5.7 0 {
+ INSERT INTO t1 WITH x AS ( SELECT * FROM t2 ) SELECT * FROM x
+}
+do_xfer_test 5.8 0 {
+ INSERT INTO t1 WITH x(a,b) AS ( SELECT * FROM t2 ) SELECT * FROM x
+}
+
+#---------------------------------------------------------------------------
+# Check that syntax (and other) errors in statements with WITH clauses
+# attached to them do not cause problems (e.g. memory leaks).
+#
+do_execsql_test 6.1 {
+ DROP TABLE IF EXISTS t1;
+ DROP TABLE IF EXISTS t2;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(a, b);
+}
+
+do_catchsql_test 6.2 {
+ WITH x AS (SELECT * FROM t1)
+ INSERT INTO t2 VALUES(1, 2,);
+} {1 {near ")": syntax error}}
+
+do_catchsql_test 6.3 {
+ WITH x AS (SELECT * FROM t1)
+ INSERT INTO t2 SELECT a, b, FROM t1;
+} {1 {near "FROM": syntax error}}
+
+do_catchsql_test 6.3 {
+ WITH x AS (SELECT * FROM t1)
+ INSERT INTO t2 SELECT a, b FROM abc;
+} {1 {no such table: abc}}
+
+do_catchsql_test 6.4 {
+ WITH x AS (SELECT * FROM t1)
+ INSERT INTO t2 SELECT a, b, FROM t1 a a a;
+} {1 {near "FROM": syntax error}}
+
+do_catchsql_test 6.5 {
+ WITH x AS (SELECT * FROM t1)
+ DELETE FROM t2 WHERE;
+} {1 {near ";": syntax error}}
+
+do_catchsql_test 6.6 {
+ WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHERE
+} {/1 {near .* syntax error}/}
+
+do_catchsql_test 6.7 {
+ WITH x AS (SELECT * FROM t1) DELETE FROM t2 WHRE 1;
+} {/1 {near .* syntax error}/}
+
+do_catchsql_test 6.8 {
+ WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = ;
+} {/1 {near .* syntax error}/}
+
+do_catchsql_test 6.9 {
+ WITH x AS (SELECT * FROM t1) UPDATE t2 SET a = 10, b = 1 WHERE a===b;
+} {/1 {near .* syntax error}/}
+
+do_catchsql_test 6.10 {
+ WITH x(a,b) AS (
+ SELECT 1, 1
+ UNION ALL
+ SELECT a*b,a+b FROM x WHERE c=2
+ )
+ SELECT * FROM x
+} {1 {no such column: c}}
+
+#-------------------------------------------------------------------------
+# Recursive queries in IN(...) expressions.
+#
+do_execsql_test 7.1 {
+ CREATE TABLE t5(x INTEGER);
+ CREATE TABLE t6(y INTEGER);
+
+ WITH s(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM s WHERE x<49 )
+ INSERT INTO t5
+ SELECT * FROM s;
+
+ INSERT INTO t6
+ WITH s(x) AS ( VALUES(2) UNION ALL SELECT x+2 FROM s WHERE x<49 )
+ SELECT * FROM s;
+}
+
+do_execsql_test 7.2 {
+ SELECT * FROM t6 WHERE y IN (SELECT x FROM t5)
+} {14 28 42}
+
+do_execsql_test 7.3 {
+ WITH ss AS (SELECT x FROM t5)
+ SELECT * FROM t6 WHERE y IN (SELECT x FROM ss)
+} {14 28 42}
+
+do_execsql_test 7.4 {
+ WITH ss(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM ss WHERE x<49 )
+ SELECT * FROM t6 WHERE y IN (SELECT x FROM ss)
+} {14 28 42}
+
+do_execsql_test 7.5 {
+ SELECT * FROM t6 WHERE y IN (
+ WITH ss(x) AS ( VALUES(7) UNION ALL SELECT x+7 FROM ss WHERE x<49 )
+ SELECT x FROM ss
+ )
+} {14 28 42}
+
+#-------------------------------------------------------------------------
+# At one point the following was causing an assertion failure and a
+# memory leak.
+#
+do_execsql_test 8.1 {
+ CREATE TABLE t7(y);
+ INSERT INTO t7 VALUES(NULL);
+ CREATE VIEW v AS SELECT * FROM t7 ORDER BY y;
+}
+
+do_execsql_test 8.2 {
+ WITH q(a) AS (
+ SELECT 1
+ UNION
+ SELECT a+1 FROM q, v WHERE a<5
+ )
+ SELECT * FROM q;
+} {1 2 3 4 5}
+
+do_execsql_test 8.3 {
+ WITH q(a) AS (
+ SELECT 1
+ UNION ALL
+ SELECT a+1 FROM q, v WHERE a<5
+ )
+ SELECT * FROM q;
+} {1 2 3 4 5}
+
+
+finish_test
+
diff --git a/test/withM.test b/test/withM.test
new file mode 100644
index 0000000..c1650d9
--- /dev/null
+++ b/test/withM.test
@@ -0,0 +1,77 @@
+# 2014 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 WITH clause.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/malloc_common.tcl
+set ::testprefix withM
+
+ifcapable {!cte} {
+ finish_test
+ return
+}
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(x INTEGER, y INTEGER);
+ INSERT INTO t1 VALUES(123, 456);
+}
+
+do_faultsim_test withM-1.1 -prep {
+ sqlite3 db test.db
+} -body {
+ execsql {
+ WITH tmp AS ( SELECT * FROM t1 )
+ SELECT * FROM tmp;
+ }
+} -test {
+ faultsim_test_result {0 {123 456}}
+ db close
+}
+
+do_faultsim_test withM-1.2 -prep {
+ sqlite3 db test.db
+} -body {
+ execsql {
+ WITH w1 AS ( SELECT * FROM t1 ),
+ w2 AS (
+ WITH w3 AS ( SELECT * FROM w1 )
+ SELECT * FROM w3
+ )
+ SELECT * FROM w2;
+ }
+} -test {
+ faultsim_test_result {0 {123 456}}
+ db close
+}
+
+do_faultsim_test withM-1.3 -prep {
+ sqlite3 db test.db
+} -body {
+ execsql {
+ WITH w1(a,b) AS (
+ SELECT 1, 1
+ UNION ALL
+ SELECT a+1, b + 2*a + 1 FROM w1
+ )
+ SELECT * FROM w1 LIMIT 5;
+ }
+} -test {
+ faultsim_test_result {0 {1 1 2 4 3 9 4 16 5 25}}
+ db close
+}
+
+finish_test
+
+
+
diff --git a/test/without_rowid1.test b/test/without_rowid1.test
new file mode 100644
index 0000000..9d7a643
--- /dev/null
+++ b/test/without_rowid1.test
@@ -0,0 +1,281 @@
+# 2013-10-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 testing WITHOUT ROWID tables.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix without_rowid1
+
+# Create and query a WITHOUT ROWID table.
+#
+do_execsql_test without_rowid1-1.0 {
+ CREATE TABLE t1(a,b,c,d, PRIMARY KEY(c,a)) WITHOUT ROWID;
+ CREATE INDEX t1bd ON t1(b, d);
+ INSERT INTO t1 VALUES('journal','sherman','ammonia','helena');
+ INSERT INTO t1 VALUES('dynamic','juliet','flipper','command');
+ INSERT INTO t1 VALUES('journal','sherman','gamma','patriot');
+ INSERT INTO t1 VALUES('arctic','sleep','ammonia','helena');
+ SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
+
+integrity_check without_rowid1-1.0ic
+
+do_execsql_test without_rowid1-1.1 {
+ SELECT *, '|' FROM t1 ORDER BY +c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
+
+do_execsql_test without_rowid1-1.2 {
+ SELECT *, '|' FROM t1 ORDER BY c DESC, a DESC;
+} {journal sherman gamma patriot | dynamic juliet flipper command | journal sherman ammonia helena | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.11 {
+ SELECT *, '|' FROM t1 ORDER BY b, d;
+} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.12 {
+ SELECT *, '|' FROM t1 ORDER BY +b, d;
+} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+# Trying to insert a duplicate PRIMARY KEY fails.
+#
+do_test without_rowid1-1.21 {
+ catchsql {
+ INSERT INTO t1 VALUES('dynamic','phone','flipper','harvard');
+ }
+} {1 {UNIQUE constraint failed: t1.c, t1.a}}
+
+# REPLACE INTO works, however.
+#
+do_execsql_test without_rowid1-1.22 {
+ REPLACE INTO t1 VALUES('dynamic','phone','flipper','harvard');
+ SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic phone flipper harvard | journal sherman gamma patriot |}
+
+do_execsql_test without_rowid1-1.23 {
+ SELECT *, '|' FROM t1 ORDER BY b, d;
+} {dynamic phone flipper harvard | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+# UPDATE statements.
+#
+do_execsql_test without_rowid1-1.31 {
+ UPDATE t1 SET d=3.1415926 WHERE a='journal';
+ SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | dynamic phone flipper harvard | journal sherman gamma 3.1415926 |}
+do_execsql_test without_rowid1-1.32 {
+ SELECT *, '|' FROM t1 ORDER BY b, d;
+} {dynamic phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.35 {
+ UPDATE t1 SET a=1250 WHERE b='phone';
+ SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | 1250 phone flipper harvard | journal sherman gamma 3.1415926 |}
+integrity_check without_rowid1-1.36
+
+do_execsql_test without_rowid1-1.37 {
+ SELECT *, '|' FROM t1 ORDER BY b, d;
+} {1250 phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.40 {
+ VACUUM;
+ SELECT *, '|' FROM t1 ORDER BY b, d;
+} {1250 phone flipper harvard | journal sherman ammonia 3.1415926 | journal sherman gamma 3.1415926 | arctic sleep ammonia helena |}
+integrity_check without_rowid1-1.41
+
+# Verify that ANALYZE works
+#
+do_execsql_test without_rowid1-1.50 {
+ ANALYZE;
+ SELECT * FROM sqlite_stat1 ORDER BY idx;
+} {t1 t1 {4 2 1} t1 t1bd {4 2 2}}
+ifcapable stat3 {
+ do_execsql_test without_rowid1-1.51 {
+ SELECT DISTINCT tbl, idx FROM sqlite_stat3 ORDER BY idx;
+ } {t1 t1 t1 t1bd}
+}
+ifcapable stat4 {
+ do_execsql_test without_rowid1-1.52 {
+ SELECT DISTINCT tbl, idx FROM sqlite_stat4 ORDER BY idx;
+ } {t1 t1 t1 t1bd}
+}
+
+#----------
+
+do_execsql_test 2.1.1 {
+ CREATE TABLE t4 (a COLLATE nocase PRIMARY KEY, b) WITHOUT ROWID;
+ INSERT INTO t4 VALUES('abc', 'def');
+ SELECT * FROM t4;
+} {abc def}
+do_execsql_test 2.1.2 {
+ UPDATE t4 SET a = 'ABC';
+ SELECT * FROM t4;
+} {ABC def}
+
+do_execsql_test 2.2.1 {
+ DROP TABLE t4;
+ CREATE TABLE t4 (b, a COLLATE nocase PRIMARY KEY) WITHOUT ROWID;
+ INSERT INTO t4(a, b) VALUES('abc', 'def');
+ SELECT * FROM t4;
+} {def abc}
+
+do_execsql_test 2.2.2 {
+ UPDATE t4 SET a = 'ABC', b = 'xyz';
+ SELECT * FROM t4;
+} {xyz ABC}
+
+do_execsql_test 2.3.1 {
+ CREATE TABLE t5 (a, b, PRIMARY KEY(b, a)) WITHOUT ROWID;
+ INSERT INTO t5(a, b) VALUES('abc', 'def');
+ UPDATE t5 SET a='abc', b='def';
+} {}
+
+do_execsql_test 2.4.1 {
+ CREATE TABLE t6 (
+ a COLLATE nocase, b, c UNIQUE, PRIMARY KEY(b, a)
+ ) WITHOUT ROWID;
+
+ INSERT INTO t6(a, b, c) VALUES('abc', 'def', 'ghi');
+ UPDATE t6 SET a='ABC', c='ghi';
+} {}
+
+do_execsql_test 2.4.2 {
+ SELECT * FROM t6 ORDER BY b, a;
+ SELECT * FROM t6 ORDER BY c;
+} {ABC def ghi ABC def ghi}
+
+#-------------------------------------------------------------------------
+# Unless the destination table is completely empty, the xfer optimization
+# is disabled for WITHOUT ROWID tables. The following tests check for
+# some problems that might occur if this were not the case.
+#
+reset_db
+do_execsql_test 3.1.1 {
+ CREATE TABLE t1(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
+ CREATE UNIQUE INDEX i1 ON t1(b);
+
+ CREATE TABLE t2(a, b, PRIMARY KEY(a)) WITHOUT ROWID;
+ CREATE UNIQUE INDEX i2 ON t2(b);
+
+ INSERT INTO t1 VALUES('one', 'two');
+ INSERT INTO t2 VALUES('three', 'two');
+}
+
+do_execsql_test 3.1.2 {
+ INSERT OR REPLACE INTO t1 SELECT * FROM t2;
+ SELECT * FROM t1;
+} {three two}
+
+do_execsql_test 3.1.3 {
+ DELETE FROM t1;
+ INSERT INTO t1 SELECT * FROM t2;
+ SELECT * FROM t1;
+} {three two}
+
+do_catchsql_test 3.1.4 {
+ INSERT INTO t2 VALUES('four', 'four');
+ INSERT INTO t2 VALUES('six', 'two');
+ INSERT INTO t1 SELECT * FROM t2;
+} {1 {UNIQUE constraint failed: t2.b}}
+
+do_execsql_test 3.1.5 {
+ CREATE TABLE t3(a PRIMARY KEY);
+ CREATE TABLE t4(a PRIMARY KEY);
+
+ INSERT INTO t4 VALUES('i');
+ INSERT INTO t4 VALUES('ii');
+ INSERT INTO t4 VALUES('iii');
+
+ INSERT INTO t3 SELECT * FROM t4;
+ SELECT * FROM t3;
+} {i ii iii}
+
+############################################################################
+# Ticket [c34d0557f740c450709d6e33df72d4f3f651a3cc]
+# Name resolution issue with WITHOUT ROWID
+#
+do_execsql_test 4.1 {
+ CREATE TABLE t41(a PRIMARY KEY) WITHOUT ROWID;
+ INSERT INTO t41 VALUES('abc');
+ CREATE TABLE t42(x);
+ INSERT INTO t42 VALUES('xyz');
+ SELECT t42.rowid FROM t41, t42;
+} {1}
+do_execsql_test 4.2 {
+ SELECT t42.rowid FROM t42, t41;
+} {1}
+
+
+#--------------------------------------------------------------------------
+# The following tests verify that the trailing PK fields added to each
+# entry in an index on a WITHOUT ROWID table are used correctly.
+#
+do_execsql_test 5.0 {
+ CREATE TABLE t45(a PRIMARY KEY, b, c) WITHOUT ROWID;
+ CREATE INDEX i45 ON t45(b);
+
+ INSERT INTO t45 VALUES(2, 'one', 'x');
+ INSERT INTO t45 VALUES(4, 'one', 'x');
+ INSERT INTO t45 VALUES(6, 'one', 'x');
+ INSERT INTO t45 VALUES(8, 'one', 'x');
+ INSERT INTO t45 VALUES(10, 'one', 'x');
+
+ INSERT INTO t45 VALUES(1, 'two', 'x');
+ INSERT INTO t45 VALUES(3, 'two', 'x');
+ INSERT INTO t45 VALUES(5, 'two', 'x');
+ INSERT INTO t45 VALUES(7, 'two', 'x');
+ INSERT INTO t45 VALUES(9, 'two', 'x');
+}
+
+do_eqp_test 5.1 {
+ SELECT * FROM t45 WHERE b=? AND a>?
+} {/*USING INDEX i45 (b=? AND a>?)*/}
+
+do_execsql_test 5.2 {
+ SELECT * FROM t45 WHERE b='two' AND a>4
+} {5 two x 7 two x 9 two x}
+
+do_execsql_test 5.3 {
+ SELECT * FROM t45 WHERE b='one' AND a<8
+} { 2 one x 4 one x 6 one x }
+
+do_execsql_test 5.4 {
+ CREATE TABLE t46(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
+ WITH r(x) AS (
+ SELECT 1 UNION ALL SELECT x+1 FROM r WHERE x<100
+ )
+ INSERT INTO t46 SELECT x / 20, x % 20, x % 10, x FROM r;
+}
+
+set queries {
+ 1 2 "c = 5 AND a = 1" {/*i46 (c=? AND a=?)*/}
+ 2 6 "c = 4 AND a < 3" {/*i46 (c=? AND a<?)*/}
+ 3 4 "c = 2 AND a >= 3" {/*i46 (c=? AND a>?)*/}
+ 4 1 "c = 2 AND a = 1 AND b<10" {/*i46 (c=? AND a=? AND b<?)*/}
+ 5 1 "c = 0 AND a = 0 AND b>5" {/*i46 (c=? AND a=? AND b>?)*/}
+}
+
+foreach {tn cnt where eqp} $queries {
+ do_execsql_test 5.5.$tn.1 "SELECT count(*) FROM t46 WHERE $where" $cnt
+}
+
+do_execsql_test 5.6 {
+ CREATE INDEX i46 ON t46(c);
+}
+
+foreach {tn cnt where eqp} $queries {
+ do_execsql_test 5.7.$tn.1 "SELECT count(*) FROM t46 WHERE $where" $cnt
+ do_eqp_test 5.7.$tn.2 "SELECT count(*) FROM t46 WHERE $where" $eqp
+}
+
+
+finish_test
diff --git a/test/without_rowid2.test b/test/without_rowid2.test
new file mode 100644
index 0000000..5ba1a23
--- /dev/null
+++ b/test/without_rowid2.test
@@ -0,0 +1,125 @@
+# 2013-11-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 WITHOUT ROWID tables, and especially
+# FOREIGN KEY constraints.
+#
+
+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 without_rowid2-1.0 {
+ execsql {
+ CREATE TABLE t1(
+ a INT PRIMARY KEY,
+ b INT
+ REFERENCES t1 ON DELETE CASCADE
+ REFERENCES t2,
+ c TEXT,
+ FOREIGN KEY (b,c) REFERENCES t2(x,y) ON UPDATE CASCADE
+ ) WITHOUT rowid;
+ }
+} {}
+do_test without_rowid2-1.1 {
+ execsql {
+ CREATE TABLE t2(
+ x INT PRIMARY KEY,
+ y TEXT
+ ) WITHOUT rowid;
+ }
+} {}
+do_test without_rowid2-1.2 {
+ execsql {
+ CREATE TABLE t3(
+ a INT REFERENCES t2,
+ b INT REFERENCES t1,
+ FOREIGN KEY (a,b) REFERENCES t2(x,y)
+ );
+ }
+} {}
+
+do_test without_rowid2-2.1 {
+ execsql {
+ CREATE TABLE t4(a int primary key) WITHOUT rowid;
+ 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;
+ }
+} {}
+
+do_test without_rowid2-3.1 {
+ execsql {
+ CREATE TABLE t5(a PRIMARY KEY, b, c) WITHOUT rowid;
+ CREATE TABLE t6(
+ d REFERENCES t5,
+ e REFERENCES t5(c)
+ );
+ PRAGMA foreign_key_list(t6);
+ }
+} [concat \
+ {0 0 t5 e c {NO ACTION} {NO ACTION} NONE} \
+ {1 0 t5 d {} {NO ACTION} {NO ACTION} NONE} \
+]
+do_test without_rowid2-3.2 {
+ execsql {
+ CREATE TABLE t7(d, e, f,
+ FOREIGN KEY (d, e) REFERENCES t5(a, b)
+ );
+ PRAGMA foreign_key_list(t7);
+ }
+} [concat \
+ {0 0 t5 d a {NO ACTION} {NO ACTION} NONE} \
+ {0 1 t5 e b {NO ACTION} {NO ACTION} NONE} \
+]
+do_test without_rowid2-3.3 {
+ execsql {
+ CREATE TABLE t8(d, e, f,
+ FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET NULL
+ );
+ PRAGMA foreign_key_list(t8);
+ }
+} [concat \
+ {0 0 t5 d {} {SET NULL} CASCADE NONE} \
+ {0 1 t5 e {} {SET NULL} CASCADE NONE} \
+]
+do_test without_rowid2-3.4 {
+ execsql {
+ CREATE TABLE t9(d, e, f,
+ FOREIGN KEY (d, e) REFERENCES t5 ON DELETE CASCADE ON UPDATE SET DEFAULT
+ );
+ PRAGMA foreign_key_list(t9);
+ }
+} [concat \
+ {0 0 t5 d {} {SET DEFAULT} CASCADE NONE} \
+ {0 1 t5 e {} {SET DEFAULT} CASCADE NONE} \
+]
+do_test without_rowid2-3.5 {
+ sqlite3_db_status db DBSTATUS_DEFERRED_FKS 0
+} {0 0 0}
+
+finish_test
diff --git a/test/without_rowid3.test b/test/without_rowid3.test
new file mode 100644
index 0000000..c4c2d6f
--- /dev/null
+++ b/test/without_rowid3.test
@@ -0,0 +1,2084 @@
+# 2013-11-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.
+#
+# This file implements tests for foreign keys on WITHOUT ROWID
+# tables.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable {!foreignkey||!trigger} {
+ finish_test
+ return
+}
+
+#-------------------------------------------------------------------------
+# Test structure:
+#
+# without_rowid3-1.*: Simple tests to check that immediate and deferred foreign key
+# constraints work when not inside a transaction.
+#
+# without_rowid3-2.*: Tests to verify that deferred foreign keys work inside
+# explicit transactions (i.e that processing really is deferred).
+#
+# without_rowid3-3.*: Tests that a statement transaction is rolled back if an
+# immediate foreign key constraint is violated.
+#
+# without_rowid3-4.*: Test that FK actions may recurse even when recursive triggers
+# are disabled.
+#
+# without_rowid3-5.*: Check that if foreign-keys are enabled, it is not possible
+# to write to an FK column using the incremental blob API.
+#
+# without_rowid3-6.*: Test that FK processing is automatically disabled when
+# running VACUUM.
+#
+# without_rowid3-7.*: Test using an IPK as the key in the child (referencing) table.
+#
+# without_rowid3-8.*: Test that enabling/disabling foreign key support while a
+# transaction is active is not possible.
+#
+# without_rowid3-9.*: Test SET DEFAULT actions.
+#
+# without_rowid3-10.*: Test errors.
+#
+# without_rowid3-11.*: Test CASCADE actions.
+#
+# without_rowid3-12.*: Test RESTRICT actions.
+#
+# without_rowid3-13.*: Test that FK processing is performed when a row is REPLACED by
+# an UPDATE or INSERT statement.
+#
+# without_rowid3-14.*: Test the ALTER TABLE and DROP TABLE commands.
+#
+# without_rowid3-15.*: Test that if there are no (known) outstanding foreign key
+# constraint violations in the database, inserting into a parent
+# table or deleting from a child table does not cause SQLite
+# to check if this has repaired an outstanding violation.
+#
+# without_rowid3-16.*: Test that rows that refer to themselves may be inserted,
+# updated and deleted.
+#
+# without_rowid3-17.*: Test that the "count_changes" pragma does not interfere with
+# FK constraint processing.
+#
+# without_rowid3-18.*: Test that the authorization callback is invoked when processing
+# FK constraints.
+#
+# without_rowid3-20.*: Test that ON CONFLICT clauses specified as part of statements
+# do not affect the operation of FK constraints.
+#
+# without_rowid3-genfkey.*: Tests that were used with the shell tool .genfkey
+# command. Recycled to test the built-in implementation.
+#
+# without_rowid3-dd08e5.*: Tests to verify that ticket dd08e5a988d00decc4a543daa8d
+# has been fixed.
+#
+
+
+execsql { PRAGMA foreign_keys = on }
+
+set FkeySimpleSchema {
+ PRAGMA foreign_keys = on;
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(c REFERENCES t1(a) /D/ , d);
+
+ CREATE TABLE t3(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t4(c REFERENCES t3 /D/, d);
+
+ CREATE TABLE t7(a, b INT PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE t8(c REFERENCES t7 /D/, d);
+
+ CREATE TABLE t9(a REFERENCES nosuchtable, b);
+ CREATE TABLE t10(a REFERENCES t9(c) /D/, b);
+}
+
+
+set FkeySimpleTests {
+ 1.1 "INSERT INTO t2 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
+ 1.2 "INSERT INTO t1 VALUES(1, 2)" {0 {}}
+ 1.3 "INSERT INTO t2 VALUES(1, 3)" {0 {}}
+ 1.4 "INSERT INTO t2 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
+ 1.5 "INSERT INTO t2 VALUES(NULL, 4)" {0 {}}
+ 1.6 "UPDATE t2 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
+ 1.7 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
+ 1.9 "UPDATE t2 SET c=1 WHERE d=4" {0 {}}
+ 1.10 "UPDATE t2 SET c=NULL WHERE d=4" {0 {}}
+ 1.11 "DELETE FROM t1 WHERE a=1" {1 {FOREIGN KEY constraint failed}}
+ 1.12 "UPDATE t1 SET a = 2" {1 {FOREIGN KEY constraint failed}}
+ 1.13 "UPDATE t1 SET a = 1" {0 {}}
+
+ 2.1 "INSERT INTO t4 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
+ 2.2 "INSERT INTO t3 VALUES(1, 2)" {0 {}}
+ 2.3 "INSERT INTO t4 VALUES(1, 3)" {0 {}}
+
+ 4.1 "INSERT INTO t8 VALUES(1, 3)" {1 {FOREIGN KEY constraint failed}}
+ 4.2 "INSERT INTO t7 VALUES(2, 1)" {0 {}}
+ 4.3 "INSERT INTO t8 VALUES(1, 3)" {0 {}}
+ 4.4 "INSERT INTO t8 VALUES(2, 4)" {1 {FOREIGN KEY constraint failed}}
+ 4.5 "INSERT INTO t8 VALUES(NULL, 4)" {0 {}}
+ 4.6 "UPDATE t8 SET c=2 WHERE d=4" {1 {FOREIGN KEY constraint failed}}
+ 4.7 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
+ 4.9 "UPDATE t8 SET c=1 WHERE d=4" {0 {}}
+ 4.10 "UPDATE t8 SET c=NULL WHERE d=4" {0 {}}
+ 4.11 "DELETE FROM t7 WHERE b=1" {1 {FOREIGN KEY constraint failed}}
+ 4.12 "UPDATE t7 SET b = 2" {1 {FOREIGN KEY constraint failed}}
+ 4.13 "UPDATE t7 SET b = 1" {0 {}}
+ 4.14 "INSERT INTO t8 VALUES('a', 'b')" {1 {FOREIGN KEY constraint failed}}
+ 4.15 "UPDATE t7 SET b = 5" {1 {FOREIGN KEY constraint failed}}
+ 4.17 "UPDATE t7 SET a = 10" {0 {}}
+
+ 5.1 "INSERT INTO t9 VALUES(1, 3)" {1 {no such table: main.nosuchtable}}
+ 5.2 "INSERT INTO t10 VALUES(1, 3)"
+ {1 {foreign key mismatch - "t10" referencing "t9"}}
+}
+
+do_test without_rowid3-1.1.0 {
+ execsql [string map {/D/ {}} $FkeySimpleSchema]
+} {}
+foreach {tn zSql res} $FkeySimpleTests {
+ do_test without_rowid3-1.1.$tn.1 { catchsql $zSql } $res
+ do_test without_rowid3-1.1.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
+ do_test without_rowid3-1.1.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
+ do_test without_rowid3-1.1.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
+ do_test without_rowid3-1.1.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
+ do_test without_rowid3-1.1.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
+ do_test without_rowid3-1.1.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
+}
+drop_all_tables
+
+do_test without_rowid3-1.2.0 {
+ execsql [string map {/D/ {DEFERRABLE INITIALLY DEFERRED}} $FkeySimpleSchema]
+} {}
+foreach {tn zSql res} $FkeySimpleTests {
+ do_test without_rowid3-1.2.$tn { catchsql $zSql } $res
+ do_test without_rowid3-1.2.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
+ do_test without_rowid3-1.2.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
+ do_test without_rowid3-1.2.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
+ do_test without_rowid3-1.2.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
+ do_test without_rowid3-1.2.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
+ do_test without_rowid3-1.2.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
+}
+drop_all_tables
+
+do_test without_rowid3-1.3.0 {
+ execsql [string map {/D/ {}} $FkeySimpleSchema]
+ execsql { PRAGMA count_changes = 1 }
+} {}
+foreach {tn zSql res} $FkeySimpleTests {
+ if {$res == "0 {}"} { set res {0 1} }
+ do_test without_rowid3-1.3.$tn { catchsql $zSql } $res
+ do_test without_rowid3-1.3.$tn.2 { execsql {PRAGMA foreign_key_check(t1)} } {}
+ do_test without_rowid3-1.3.$tn.3 { execsql {PRAGMA foreign_key_check(t2)} } {}
+ do_test without_rowid3-1.3.$tn.4 { execsql {PRAGMA foreign_key_check(t3)} } {}
+ do_test without_rowid3-1.3.$tn.5 { execsql {PRAGMA foreign_key_check(t4)} } {}
+ do_test without_rowid3-1.3.$tn.6 { execsql {PRAGMA foreign_key_check(t7)} } {}
+ do_test without_rowid3-1.3.$tn.7 { execsql {PRAGMA foreign_key_check(t8)} } {}
+}
+execsql { PRAGMA count_changes = 0 }
+drop_all_tables
+
+do_test without_rowid3-1.4.0 {
+ execsql [string map {/D/ {}} $FkeySimpleSchema]
+ execsql { PRAGMA count_changes = 1 }
+} {}
+foreach {tn zSql res} $FkeySimpleTests {
+ if {$res == "0 {}"} { set res {0 1} }
+ execsql BEGIN
+ do_test without_rowid3-1.4.$tn { catchsql $zSql } $res
+ execsql COMMIT
+}
+execsql { PRAGMA count_changes = 0 }
+drop_all_tables
+
+# Special test: When the parent key is an IPK, make sure the affinity of
+# the IPK is not applied to the child key value before it is inserted
+# into the child table.
+do_test without_rowid3-1.5.1 {
+ execsql {
+ CREATE TABLE i(i INT PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE j(j REFERENCES i);
+ INSERT INTO i VALUES(35);
+ INSERT INTO j VALUES('35.0');
+ SELECT j, typeof(j) FROM j;
+ }
+} {35.0 text}
+do_test without_rowid3-1.5.2 {
+ catchsql { DELETE FROM i }
+} {1 {FOREIGN KEY constraint failed}}
+
+# Same test using a regular primary key with integer affinity.
+drop_all_tables
+do_test without_rowid3-1.6.1 {
+ execsql {
+ CREATE TABLE i(i INT UNIQUE);
+ CREATE TABLE j(j REFERENCES i(i));
+ INSERT INTO i VALUES('35.0');
+ INSERT INTO j VALUES('35.0');
+ SELECT j, typeof(j) FROM j;
+ SELECT i, typeof(i) FROM i;
+ }
+} {35.0 text 35 integer}
+do_test without_rowid3-1.6.2 {
+ catchsql { DELETE FROM i }
+} {1 {FOREIGN KEY constraint failed}}
+
+# Use a collation sequence on the parent key.
+drop_all_tables
+do_test without_rowid3-1.7.1 {
+ execsql {
+ CREATE TABLE i(i TEXT COLLATE nocase PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE j(j TEXT COLLATE binary REFERENCES i(i));
+ INSERT INTO i VALUES('SQLite');
+ INSERT INTO j VALUES('sqlite');
+ }
+ catchsql { DELETE FROM i }
+} {1 {FOREIGN KEY constraint failed}}
+
+# Use the parent key collation even if it is default and the child key
+# has an explicit value.
+drop_all_tables
+do_test without_rowid3-1.7.2 {
+ execsql {
+ CREATE TABLE i(i TEXT PRIMARY KEY) WITHOUT rowid; -- Colseq is "BINARY"
+ CREATE TABLE j(j TEXT COLLATE nocase REFERENCES i(i));
+ INSERT INTO i VALUES('SQLite');
+ }
+ catchsql { INSERT INTO j VALUES('sqlite') }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-1.7.3 {
+ execsql {
+ INSERT INTO i VALUES('sqlite');
+ INSERT INTO j VALUES('sqlite');
+ DELETE FROM i WHERE i = 'SQLite';
+ }
+ catchsql { DELETE FROM i WHERE i = 'sqlite' }
+} {1 {FOREIGN KEY constraint failed}}
+
+#-------------------------------------------------------------------------
+# This section (test cases without_rowid3-2.*) contains tests to check that the
+# deferred foreign key constraint logic works.
+#
+proc without_rowid3-2-test {tn nocommit sql {res {}}} {
+ if {$res eq "FKV"} {
+ set expected {1 {FOREIGN KEY constraint failed}}
+ } else {
+ set expected [list 0 $res]
+ }
+ do_test without_rowid3-2.$tn [list catchsql $sql] $expected
+ if {$nocommit} {
+ do_test without_rowid3-2.${tn}c {
+ catchsql COMMIT
+ } {1 {FOREIGN KEY constraint failed}}
+ }
+}
+
+without_rowid3-2-test 1 0 {
+ CREATE TABLE node(
+ nodeid PRIMARY KEY,
+ parent REFERENCES node DEFERRABLE INITIALLY DEFERRED
+ ) WITHOUT rowid;
+ CREATE TABLE leaf(
+ cellid PRIMARY KEY,
+ parent REFERENCES node DEFERRABLE INITIALLY DEFERRED
+ ) WITHOUT rowid;
+}
+
+without_rowid3-2-test 1 0 "INSERT INTO node VALUES(1, 0)" FKV
+without_rowid3-2-test 2 0 "BEGIN"
+without_rowid3-2-test 3 1 "INSERT INTO node VALUES(1, 0)"
+without_rowid3-2-test 4 0 "UPDATE node SET parent = NULL"
+without_rowid3-2-test 5 0 "COMMIT"
+without_rowid3-2-test 6 0 "SELECT * FROM node" {1 {}}
+
+without_rowid3-2-test 7 0 "BEGIN"
+without_rowid3-2-test 8 1 "INSERT INTO leaf VALUES('a', 2)"
+without_rowid3-2-test 9 1 "INSERT INTO node VALUES(2, 0)"
+without_rowid3-2-test 10 0 "UPDATE node SET parent = 1 WHERE nodeid = 2"
+without_rowid3-2-test 11 0 "COMMIT"
+without_rowid3-2-test 12 0 "SELECT * FROM node" {1 {} 2 1}
+without_rowid3-2-test 13 0 "SELECT * FROM leaf" {a 2}
+
+without_rowid3-2-test 14 0 "BEGIN"
+without_rowid3-2-test 15 1 "DELETE FROM node WHERE nodeid = 2"
+without_rowid3-2-test 16 0 "INSERT INTO node VALUES(2, NULL)"
+without_rowid3-2-test 17 0 "COMMIT"
+without_rowid3-2-test 18 0 "SELECT * FROM node" {1 {} 2 {}}
+without_rowid3-2-test 19 0 "SELECT * FROM leaf" {a 2}
+
+without_rowid3-2-test 20 0 "BEGIN"
+without_rowid3-2-test 21 0 "INSERT INTO leaf VALUES('b', 1)"
+without_rowid3-2-test 22 0 "SAVEPOINT save"
+without_rowid3-2-test 23 0 "DELETE FROM node WHERE nodeid = 1"
+without_rowid3-2-test 24 0 "ROLLBACK TO save"
+without_rowid3-2-test 25 0 "COMMIT"
+without_rowid3-2-test 26 0 "SELECT * FROM node" {1 {} 2 {}}
+without_rowid3-2-test 27 0 "SELECT * FROM leaf" {a 2 b 1}
+
+without_rowid3-2-test 28 0 "BEGIN"
+without_rowid3-2-test 29 0 "INSERT INTO leaf VALUES('c', 1)"
+without_rowid3-2-test 30 0 "SAVEPOINT save"
+without_rowid3-2-test 31 0 "DELETE FROM node WHERE nodeid = 1"
+without_rowid3-2-test 32 1 "RELEASE save"
+without_rowid3-2-test 33 1 "DELETE FROM leaf WHERE cellid = 'b'"
+without_rowid3-2-test 34 0 "DELETE FROM leaf WHERE cellid = 'c'"
+without_rowid3-2-test 35 0 "COMMIT"
+without_rowid3-2-test 36 0 "SELECT * FROM node" {2 {}}
+without_rowid3-2-test 37 0 "SELECT * FROM leaf" {a 2}
+
+without_rowid3-2-test 38 0 "SAVEPOINT outer"
+without_rowid3-2-test 39 1 "INSERT INTO leaf VALUES('d', 3)"
+without_rowid3-2-test 40 1 "RELEASE outer" FKV
+without_rowid3-2-test 41 1 "INSERT INTO leaf VALUES('e', 3)"
+without_rowid3-2-test 42 0 "INSERT INTO node VALUES(3, 2)"
+without_rowid3-2-test 43 0 "RELEASE outer"
+
+without_rowid3-2-test 44 0 "SAVEPOINT outer"
+without_rowid3-2-test 45 1 "DELETE FROM node WHERE nodeid=3"
+without_rowid3-2-test 47 0 "INSERT INTO node VALUES(3, 2)"
+without_rowid3-2-test 48 0 "ROLLBACK TO outer"
+without_rowid3-2-test 49 0 "RELEASE outer"
+
+without_rowid3-2-test 50 0 "SAVEPOINT outer"
+without_rowid3-2-test 51 1 "INSERT INTO leaf VALUES('f', 4)"
+without_rowid3-2-test 52 1 "SAVEPOINT inner"
+without_rowid3-2-test 53 1 "INSERT INTO leaf VALUES('g', 4)"
+without_rowid3-2-test 54 1 "RELEASE outer" FKV
+without_rowid3-2-test 55 1 "ROLLBACK TO inner"
+without_rowid3-2-test 56 0 "COMMIT" FKV
+without_rowid3-2-test 57 0 "INSERT INTO node VALUES(4, NULL)"
+without_rowid3-2-test 58 0 "RELEASE outer"
+without_rowid3-2-test 59 0 "SELECT * FROM node" {2 {} 3 2 4 {}}
+without_rowid3-2-test 60 0 "SELECT * FROM leaf" {a 2 d 3 e 3 f 4}
+
+# The following set of tests check that if a statement that affects
+# multiple rows violates some foreign key constraints, then strikes a
+# constraint that causes the statement-transaction to be rolled back,
+# the deferred constraint counter is correctly reset to the value it
+# had before the statement-transaction was opened.
+#
+without_rowid3-2-test 61 0 "BEGIN"
+without_rowid3-2-test 62 0 "DELETE FROM leaf"
+without_rowid3-2-test 63 0 "DELETE FROM node"
+without_rowid3-2-test 64 1 "INSERT INTO leaf VALUES('a', 1)"
+without_rowid3-2-test 65 1 "INSERT INTO leaf VALUES('b', 2)"
+without_rowid3-2-test 66 1 "INSERT INTO leaf VALUES('c', 1)"
+do_test without_rowid3-2-test-67 {
+ catchsql "INSERT INTO node SELECT parent, 3 FROM leaf"
+} {1 {UNIQUE constraint failed: node.nodeid}}
+without_rowid3-2-test 68 0 "COMMIT" FKV
+without_rowid3-2-test 69 1 "INSERT INTO node VALUES(1, NULL)"
+without_rowid3-2-test 70 0 "INSERT INTO node VALUES(2, NULL)"
+without_rowid3-2-test 71 0 "COMMIT"
+
+without_rowid3-2-test 72 0 "BEGIN"
+without_rowid3-2-test 73 1 "DELETE FROM node"
+without_rowid3-2-test 74 0 "INSERT INTO node(nodeid) SELECT DISTINCT parent FROM leaf"
+without_rowid3-2-test 75 0 "COMMIT"
+
+#-------------------------------------------------------------------------
+# Test cases without_rowid3-3.* test that a program that executes foreign key
+# actions (CASCADE, SET DEFAULT, SET NULL etc.) or tests FK constraints
+# opens a statement transaction if required.
+#
+# without_rowid3-3.1.*: Test UPDATE statements.
+# without_rowid3-3.2.*: Test DELETE statements.
+#
+drop_all_tables
+do_test without_rowid3-3.1.1 {
+ execsql {
+ CREATE TABLE ab(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE cd(
+ c PRIMARY KEY REFERENCES ab ON UPDATE CASCADE ON DELETE CASCADE,
+ d
+ ) WITHOUT rowid;
+ CREATE TABLE ef(
+ e REFERENCES cd ON UPDATE CASCADE,
+ f, CHECK (e!=5)
+ );
+ }
+} {}
+do_test without_rowid3-3.1.2 {
+ execsql {
+ INSERT INTO ab VALUES(1, 'b');
+ INSERT INTO cd VALUES(1, 'd');
+ INSERT INTO ef VALUES(1, 'e');
+ }
+} {}
+do_test without_rowid3-3.1.3 {
+ catchsql { UPDATE ab SET a = 5 }
+} {1 {CHECK constraint failed: ef}}
+do_test without_rowid3-3.1.4 {
+ execsql { SELECT * FROM ab }
+} {1 b}
+do_test without_rowid3-3.1.4 {
+ execsql BEGIN;
+ catchsql { UPDATE ab SET a = 5 }
+} {1 {CHECK constraint failed: ef}}
+do_test without_rowid3-3.1.5 {
+ execsql COMMIT;
+ execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
+} {1 b 1 d 1 e}
+
+do_test without_rowid3-3.2.1 {
+ execsql BEGIN;
+ catchsql { DELETE FROM ab }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-3.2.2 {
+ execsql COMMIT
+ execsql { SELECT * FROM ab; SELECT * FROM cd; SELECT * FROM ef }
+} {1 b 1 d 1 e}
+
+#-------------------------------------------------------------------------
+# Test cases without_rowid3-4.* test that recursive foreign key actions
+# (i.e. CASCADE) are allowed even if recursive triggers are disabled.
+#
+drop_all_tables
+do_test without_rowid3-4.1 {
+ execsql {
+ CREATE TABLE t1(
+ node PRIMARY KEY,
+ parent REFERENCES t1 ON DELETE CASCADE
+ ) WITHOUT rowid;
+ CREATE TABLE t2(node PRIMARY KEY, parent) WITHOUT rowid;
+ CREATE TRIGGER t2t AFTER DELETE ON t2 BEGIN
+ DELETE FROM t2 WHERE parent = old.node;
+ END;
+ INSERT INTO t1 VALUES(1, NULL);
+ INSERT INTO t1 VALUES(2, 1);
+ INSERT INTO t1 VALUES(3, 1);
+ INSERT INTO t1 VALUES(4, 2);
+ INSERT INTO t1 VALUES(5, 2);
+ INSERT INTO t1 VALUES(6, 3);
+ INSERT INTO t1 VALUES(7, 3);
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+} {}
+do_test without_rowid3-4.2 {
+ execsql { PRAGMA recursive_triggers = off }
+ execsql {
+ BEGIN;
+ DELETE FROM t1 WHERE node = 1;
+ SELECT node FROM t1;
+ }
+} {}
+do_test without_rowid3-4.3 {
+ execsql {
+ DELETE FROM t2 WHERE node = 1;
+ SELECT node FROM t2;
+ ROLLBACK;
+ }
+} {4 5 6 7}
+do_test without_rowid3-4.4 {
+ execsql { PRAGMA recursive_triggers = on }
+ execsql {
+ BEGIN;
+ DELETE FROM t1 WHERE node = 1;
+ SELECT node FROM t1;
+ }
+} {}
+do_test without_rowid3-4.3 {
+ execsql {
+ DELETE FROM t2 WHERE node = 1;
+ SELECT node FROM t2;
+ ROLLBACK;
+ }
+} {}
+
+#-------------------------------------------------------------------------
+# Test cases without_rowid3-5.* verify that the incremental blob API may not
+# write to a foreign key column while foreign-keys are enabled.
+#
+drop_all_tables
+ifcapable incrblob {
+ do_test without_rowid3-5.1 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1(a)) WITHOUT rowid;
+ INSERT INTO t1 VALUES('hello', 'world');
+ INSERT INTO t2 VALUES('key', 'hello');
+ }
+ } {}
+ do_test without_rowid3-5.2 {
+ set rc [catch { set fd [db incrblob t2 b 1] } msg]
+ list $rc $msg
+ } {1 {cannot open table without rowid: t2}}
+ do_test without_rowid3-5.5 {
+ execsql { PRAGMA foreign_keys = on }
+ } {}
+}
+
+drop_all_tables
+ifcapable vacuum {
+ do_test without_rowid3-6.1 {
+ execsql {
+ CREATE TABLE t1(a REFERENCES t2(c), b);
+ CREATE TABLE t2(c UNIQUE, b);
+ INSERT INTO t2 VALUES(1, 2);
+ INSERT INTO t1 VALUES(1, 2);
+ VACUUM;
+ }
+ } {}
+}
+
+#-------------------------------------------------------------------------
+# Test that it is possible to use an INT PRIMARY KEY as the child key
+# of a foreign constraint.
+#
+drop_all_tables
+do_test without_rowid3-7.1 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(c INT PRIMARY KEY REFERENCES t1, b) WITHOUT rowid;
+ }
+} {}
+do_test without_rowid3-7.2 {
+ catchsql { INSERT INTO t2 VALUES(1, 'A'); }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-7.3 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(2, 3);
+ INSERT INTO t2 VALUES(1, 'A');
+ }
+} {}
+do_test without_rowid3-7.4 {
+ execsql { UPDATE t2 SET c = 2 }
+} {}
+do_test without_rowid3-7.5 {
+ catchsql { UPDATE t2 SET c = 3 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-7.6 {
+ catchsql { DELETE FROM t1 WHERE a = 2 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-7.7 {
+ execsql { DELETE FROM t1 WHERE a = 1 }
+} {}
+do_test without_rowid3-7.8 {
+ catchsql { UPDATE t1 SET a = 3 }
+} {1 {FOREIGN KEY constraint failed}}
+
+#-------------------------------------------------------------------------
+# Test that it is not possible to enable/disable FK support while a
+# transaction is open.
+#
+drop_all_tables
+proc without_rowid3-8-test {tn zSql value} {
+ do_test without_rowid3-2.8.$tn.1 [list execsql $zSql] {}
+ do_test without_rowid3-2.8.$tn.2 { execsql "PRAGMA foreign_keys" } $value
+}
+without_rowid3-8-test 1 { PRAGMA foreign_keys = 0 } 0
+without_rowid3-8-test 2 { PRAGMA foreign_keys = 1 } 1
+without_rowid3-8-test 3 { BEGIN } 1
+without_rowid3-8-test 4 { PRAGMA foreign_keys = 0 } 1
+without_rowid3-8-test 5 { COMMIT } 1
+without_rowid3-8-test 6 { PRAGMA foreign_keys = 0 } 0
+without_rowid3-8-test 7 { BEGIN } 0
+without_rowid3-8-test 8 { PRAGMA foreign_keys = 1 } 0
+without_rowid3-8-test 9 { COMMIT } 0
+without_rowid3-8-test 10 { PRAGMA foreign_keys = 1 } 1
+without_rowid3-8-test 11 { PRAGMA foreign_keys = off } 0
+without_rowid3-8-test 12 { PRAGMA foreign_keys = on } 1
+without_rowid3-8-test 13 { PRAGMA foreign_keys = no } 0
+without_rowid3-8-test 14 { PRAGMA foreign_keys = yes } 1
+without_rowid3-8-test 15 { PRAGMA foreign_keys = false } 0
+without_rowid3-8-test 16 { PRAGMA foreign_keys = true } 1
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-9.*, test SET DEFAULT actions.
+#
+drop_all_tables
+do_test without_rowid3-9.1.1 {
+ execsql {
+ CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(
+ c INT PRIMARY KEY,
+ d INTEGER DEFAULT 1 REFERENCES t1 ON DELETE SET DEFAULT
+ ) WITHOUT rowid;
+ DELETE FROM t1;
+ }
+} {}
+do_test without_rowid3-9.1.2 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t2 VALUES(1, 2);
+ SELECT * FROM t2;
+ DELETE FROM t1 WHERE a = 2;
+ SELECT * FROM t2;
+ }
+} {1 2 1 1}
+do_test without_rowid3-9.1.3 {
+ execsql {
+ INSERT INTO t1 VALUES(2, 'two');
+ UPDATE t2 SET d = 2;
+ DELETE FROM t1 WHERE a = 1;
+ SELECT * FROM t2;
+ }
+} {1 2}
+do_test without_rowid3-9.1.4 {
+ execsql { SELECT * FROM t1 }
+} {2 two}
+do_test without_rowid3-9.1.5 {
+ catchsql { DELETE FROM t1 }
+} {1 {FOREIGN KEY constraint failed}}
+
+do_test without_rowid3-9.2.1 {
+ execsql {
+ CREATE TABLE pp(a, b, c, PRIMARY KEY(b, c)) WITHOUT rowid;
+ CREATE TABLE cc(d DEFAULT 3, e DEFAULT 1, f DEFAULT 2,
+ FOREIGN KEY(f, d) REFERENCES pp
+ ON UPDATE SET DEFAULT
+ ON DELETE SET NULL
+ );
+ INSERT INTO pp VALUES(1, 2, 3);
+ INSERT INTO pp VALUES(4, 5, 6);
+ INSERT INTO pp VALUES(7, 8, 9);
+ }
+} {}
+do_test without_rowid3-9.2.2 {
+ execsql {
+ INSERT INTO cc VALUES(6, 'A', 5);
+ INSERT INTO cc VALUES(6, 'B', 5);
+ INSERT INTO cc VALUES(9, 'A', 8);
+ INSERT INTO cc VALUES(9, 'B', 8);
+ UPDATE pp SET b = 1 WHERE a = 7;
+ SELECT * FROM cc;
+ }
+} {6 A 5 6 B 5 3 A 2 3 B 2}
+do_test without_rowid3-9.2.3 {
+ execsql {
+ DELETE FROM pp WHERE a = 4;
+ SELECT * FROM cc;
+ }
+} {{} A {} {} B {} 3 A 2 3 B 2}
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-10.*, test "foreign key mismatch" and
+# other errors.
+#
+set tn 0
+foreach zSql [list {
+ CREATE TABLE p(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE c(x REFERENCES p(c));
+} {
+ CREATE TABLE c(x REFERENCES v(y));
+ CREATE VIEW v AS SELECT x AS y FROM c;
+} {
+ CREATE TABLE p(a, b, PRIMARY KEY(a, b)) WITHOUT rowid;
+ CREATE TABLE c(x REFERENCES p);
+} {
+ CREATE TABLE p(a COLLATE binary, b);
+ CREATE UNIQUE INDEX i ON p(a COLLATE nocase);
+ CREATE TABLE c(x REFERENCES p(a));
+}] {
+ drop_all_tables
+ do_test without_rowid3-10.1.[incr tn] {
+ execsql $zSql
+ catchsql { INSERT INTO c DEFAULT VALUES }
+ } {/1 {foreign key mismatch - "c" referencing "."}/}
+}
+
+# "rowid" cannot be used as part of a child or parent key definition
+# unless it happens to be the name of an explicitly declared column.
+#
+do_test without_rowid3-10.2.1 {
+ drop_all_tables
+ catchsql {
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(c, d, FOREIGN KEY(rowid) REFERENCES t1(a));
+ }
+} {1 {unknown column "rowid" in foreign key definition}}
+do_test without_rowid3-10.2.2 {
+ drop_all_tables
+ catchsql {
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(rowid, d, FOREIGN KEY(rowid) REFERENCES t1(a));
+ }
+} {0 {}}
+do_test without_rowid3-10.2.1 {
+ drop_all_tables
+ catchsql {
+ CREATE TABLE t1(a, b);
+ CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(rowid));
+ INSERT INTO t1(rowid, a, b) VALUES(1, 1, 1);
+ INSERT INTO t2 VALUES(1, 1);
+ }
+} {1 {foreign key mismatch - "t2" referencing "t1"}}
+do_test without_rowid3-10.2.2 {
+ drop_all_tables
+ catchsql {
+ CREATE TABLE t1(rowid PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(rowid));
+ INSERT INTO t1(rowid, b) VALUES(1, 1);
+ INSERT INTO t2 VALUES(1, 1);
+ }
+} {0 {}}
+
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-11.*, test CASCADE actions.
+#
+drop_all_tables
+do_test without_rowid3-11.1.1 {
+ execsql {
+ CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE t2(c, d, FOREIGN KEY(c) REFERENCES t1(a) ON UPDATE CASCADE);
+
+ INSERT INTO t1 VALUES(10, 100);
+ INSERT INTO t2 VALUES(10, 100);
+ UPDATE t1 SET a = 15;
+ SELECT * FROM t2;
+ }
+} {15 100}
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-12.*, test RESTRICT actions.
+#
+drop_all_tables
+do_test without_rowid3-12.1.1 {
+ execsql {
+ CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE t2(
+ x REFERENCES t1 ON UPDATE RESTRICT DEFERRABLE INITIALLY DEFERRED
+ );
+ INSERT INTO t1 VALUES(1, 'one');
+ INSERT INTO t1 VALUES(2, 'two');
+ INSERT INTO t1 VALUES(3, 'three');
+ }
+} {}
+do_test without_rowid3-12.1.2 {
+ execsql "BEGIN"
+ execsql "INSERT INTO t2 VALUES('two')"
+} {}
+do_test without_rowid3-12.1.3 {
+ execsql "UPDATE t1 SET b = 'four' WHERE b = 'one'"
+} {}
+do_test without_rowid3-12.1.4 {
+ catchsql "UPDATE t1 SET b = 'five' WHERE b = 'two'"
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-12.1.5 {
+ execsql "DELETE FROM t1 WHERE b = 'two'"
+} {}
+do_test without_rowid3-12.1.6 {
+ catchsql "COMMIT"
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-12.1.7 {
+ execsql {
+ INSERT INTO t1 VALUES(2, 'two');
+ COMMIT;
+ }
+} {}
+
+drop_all_tables
+do_test without_rowid3-12.2.1 {
+ execsql {
+ CREATE TABLE t1(x COLLATE NOCASE PRIMARY KEY) WITHOUT rowid;
+ CREATE TRIGGER tt1 AFTER DELETE ON t1
+ WHEN EXISTS ( SELECT 1 FROM t2 WHERE old.x = y )
+ BEGIN
+ INSERT INTO t1 VALUES(old.x);
+ END;
+ CREATE TABLE t2(y REFERENCES t1);
+ INSERT INTO t1 VALUES('A');
+ INSERT INTO t1 VALUES('B');
+ INSERT INTO t2 VALUES('a');
+ INSERT INTO t2 VALUES('b');
+
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+} {A B a b}
+do_test without_rowid3-12.2.2 {
+ execsql { DELETE FROM t1 }
+ execsql {
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+} {A B a b}
+do_test without_rowid3-12.2.3 {
+ execsql {
+ DROP TABLE t2;
+ CREATE TABLE t2(y REFERENCES t1 ON DELETE RESTRICT);
+ INSERT INTO t2 VALUES('a');
+ INSERT INTO t2 VALUES('b');
+ }
+ catchsql { DELETE FROM t1 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-12.2.4 {
+ execsql {
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+} {A B a b}
+
+drop_all_tables
+do_test without_rowid3-12.3.1 {
+ execsql {
+ CREATE TABLE up(
+ c00, c01, c02, c03, c04, c05, c06, c07, c08, c09,
+ c10, c11, c12, c13, c14, c15, c16, c17, c18, c19,
+ c20, c21, c22, c23, c24, c25, c26, c27, c28, c29,
+ c30, c31, c32, c33, c34, c35, c36, c37, c38, c39,
+ PRIMARY KEY(c34, c35)
+ ) WITHOUT rowid;
+ CREATE TABLE down(
+ c00, c01, c02, c03, c04, c05, c06, c07, c08, c09,
+ c10, c11, c12, c13, c14, c15, c16, c17, c18, c19,
+ c20, c21, c22, c23, c24, c25, c26, c27, c28, c29,
+ c30, c31, c32, c33, c34, c35, c36, c37, c38, c39,
+ FOREIGN KEY(c39, c38) REFERENCES up ON UPDATE CASCADE
+ );
+ }
+} {}
+do_test without_rowid3-12.3.2 {
+ execsql {
+ INSERT INTO up(c34, c35) VALUES('yes', 'no');
+ INSERT INTO down(c39, c38) VALUES('yes', 'no');
+ UPDATE up SET c34 = 'possibly';
+ SELECT c38, c39 FROM down;
+ DELETE FROM down;
+ }
+} {no possibly}
+do_test without_rowid3-12.3.3 {
+ catchsql { INSERT INTO down(c39, c38) VALUES('yes', 'no') }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-12.3.4 {
+ execsql {
+ INSERT INTO up(c34, c35) VALUES('yes', 'no');
+ INSERT INTO down(c39, c38) VALUES('yes', 'no');
+ }
+ catchsql { DELETE FROM up WHERE c34 = 'yes' }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-12.3.5 {
+ execsql {
+ DELETE FROM up WHERE c34 = 'possibly';
+ SELECT c34, c35 FROM up;
+ SELECT c39, c38 FROM down;
+ }
+} {yes no yes no}
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-13.*, test that FK processing is performed
+# when rows are REPLACEd.
+#
+drop_all_tables
+do_test without_rowid3-13.1.1 {
+ execsql {
+ CREATE TABLE pp(a UNIQUE, b, c, PRIMARY KEY(b, c)) WITHOUT rowid;
+ CREATE TABLE cc(d, e, f UNIQUE, FOREIGN KEY(d, e) REFERENCES pp);
+ INSERT INTO pp VALUES(1, 2, 3);
+ INSERT INTO cc VALUES(2, 3, 1);
+ }
+} {}
+foreach {tn stmt} {
+ 1 "REPLACE INTO pp VALUES(1, 4, 5)"
+} {
+ do_test without_rowid3-13.1.$tn.1 {
+ catchsql $stmt
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-13.1.$tn.2 {
+ execsql {
+ SELECT * FROM pp;
+ SELECT * FROM cc;
+ }
+ } {1 2 3 2 3 1}
+ do_test without_rowid3-13.1.$tn.3 {
+ execsql BEGIN;
+ catchsql $stmt
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-13.1.$tn.4 {
+ execsql {
+ COMMIT;
+ SELECT * FROM pp;
+ SELECT * FROM cc;
+ }
+ } {1 2 3 2 3 1}
+}
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-14.*, test that the "DROP TABLE" and "ALTER
+# TABLE" commands work as expected wrt foreign key constraints.
+#
+# without_rowid3-14.1*: ALTER TABLE ADD COLUMN
+# without_rowid3-14.2*: ALTER TABLE RENAME TABLE
+# without_rowid3-14.3*: DROP TABLE
+#
+drop_all_tables
+ifcapable altertable {
+ do_test without_rowid3-14.1.1 {
+ # Adding a column with a REFERENCES clause is not supported.
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE t2(a, b);
+ }
+ catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1.2 {
+ catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1.3 {
+ catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL}
+ } {0 {}}
+ do_test without_rowid3-14.1.4 {
+ catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'}
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1.5 {
+ catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 }
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1.6 {
+ execsql {
+ PRAGMA foreign_keys = off;
+ ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1;
+ PRAGMA foreign_keys = on;
+ SELECT sql FROM sqlite_master WHERE name='t2';
+ }
+ } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}}
+
+
+ # Test the sqlite_rename_parent() function directly.
+ #
+ proc test_rename_parent {zCreate zOld zNew} {
+ db eval {SELECT sqlite_rename_parent($zCreate, $zOld, $zNew)}
+ }
+ do_test without_rowid3-14.2.1.1 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+ do_test without_rowid3-14.2.1.2 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3
+ } {{CREATE TABLE t1(a REFERENCES t2)}}
+ do_test without_rowid3-14.2.1.3 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+
+ # Test ALTER TABLE RENAME TABLE a bit.
+ #
+ do_test without_rowid3-14.2.2.1 {
+ drop_all_tables
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid;
+ CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid;
+ CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1);
+ }
+ execsql { SELECT sql FROM sqlite_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \
+ ]
+ do_test without_rowid3-14.2.2.2 {
+ execsql { ALTER TABLE t1 RENAME TO t4 }
+ execsql { SELECT sql FROM sqlite_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \
+ ]
+ do_test without_rowid3-14.2.2.3 {
+ catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2.2.4 {
+ execsql { INSERT INTO t4 VALUES(1, NULL) }
+ } {}
+ do_test without_rowid3-14.2.2.5 {
+ catchsql { UPDATE t4 SET b = 5 }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2.2.6 {
+ catchsql { UPDATE t4 SET b = 1 }
+ } {0 {}}
+ do_test without_rowid3-14.2.2.7 {
+ execsql { INSERT INTO t3 VALUES(1, NULL, 1) }
+ } {}
+
+ # Repeat for TEMP tables
+ #
+ drop_all_tables
+ do_test without_rowid3-14.1tmp.1 {
+ # Adding a column with a REFERENCES clause is not supported.
+ execsql {
+ CREATE TEMP TABLE t1(a PRIMARY KEY) WITHOUT rowid;
+ CREATE TEMP TABLE t2(a, b);
+ }
+ catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1tmp.2 {
+ catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1tmp.3 {
+ catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL}
+ } {0 {}}
+ do_test without_rowid3-14.1tmp.4 {
+ catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'}
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1tmp.5 {
+ catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 }
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1tmp.6 {
+ execsql {
+ PRAGMA foreign_keys = off;
+ ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1;
+ PRAGMA foreign_keys = on;
+ SELECT sql FROM sqlite_temp_master WHERE name='t2';
+ }
+ } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}}
+
+ do_test without_rowid3-14.2tmp.1.1 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+ do_test without_rowid3-14.2tmp.1.2 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3
+ } {{CREATE TABLE t1(a REFERENCES t2)}}
+ do_test without_rowid3-14.2tmp.1.3 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+
+ # Test ALTER TABLE RENAME TABLE a bit.
+ #
+ do_test without_rowid3-14.2tmp.2.1 {
+ drop_all_tables
+ execsql {
+ CREATE TEMP TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid;
+ CREATE TEMP TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid;
+ CREATE TEMP TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1);
+ }
+ execsql { SELECT sql FROM sqlite_temp_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \
+ ]
+ do_test without_rowid3-14.2tmp.2.2 {
+ execsql { ALTER TABLE t1 RENAME TO t4 }
+ execsql { SELECT sql FROM sqlite_temp_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \
+ ]
+ do_test without_rowid3-14.2tmp.2.3 {
+ catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2tmp.2.4 {
+ execsql { INSERT INTO t4 VALUES(1, NULL) }
+ } {}
+ do_test without_rowid3-14.2tmp.2.5 {
+ catchsql { UPDATE t4 SET b = 5 }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2tmp.2.6 {
+ catchsql { UPDATE t4 SET b = 1 }
+ } {0 {}}
+ do_test without_rowid3-14.2tmp.2.7 {
+ execsql { INSERT INTO t3 VALUES(1, NULL, 1) }
+ } {}
+
+ # Repeat for ATTACH-ed tables
+ #
+ drop_all_tables
+ do_test without_rowid3-14.1aux.1 {
+ # Adding a column with a REFERENCES clause is not supported.
+ execsql {
+ ATTACH ':memory:' AS aux;
+ CREATE TABLE aux.t1(a PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE aux.t2(a, b);
+ }
+ catchsql { ALTER TABLE t2 ADD COLUMN c REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1aux.2 {
+ catchsql { ALTER TABLE t2 ADD COLUMN d DEFAULT NULL REFERENCES t1 }
+ } {0 {}}
+ do_test without_rowid3-14.1aux.3 {
+ catchsql { ALTER TABLE t2 ADD COLUMN e REFERENCES t1 DEFAULT NULL}
+ } {0 {}}
+ do_test without_rowid3-14.1aux.4 {
+ catchsql { ALTER TABLE t2 ADD COLUMN f REFERENCES t1 DEFAULT 'text'}
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1aux.5 {
+ catchsql { ALTER TABLE t2 ADD COLUMN g DEFAULT CURRENT_TIME REFERENCES t1 }
+ } {1 {Cannot add a REFERENCES column with non-NULL default value}}
+ do_test without_rowid3-14.1aux.6 {
+ execsql {
+ PRAGMA foreign_keys = off;
+ ALTER TABLE t2 ADD COLUMN h DEFAULT 'text' REFERENCES t1;
+ PRAGMA foreign_keys = on;
+ SELECT sql FROM aux.sqlite_master WHERE name='t2';
+ }
+ } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}}
+
+ do_test without_rowid3-14.2aux.1.1 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+ do_test without_rowid3-14.2aux.1.2 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3
+ } {{CREATE TABLE t1(a REFERENCES t2)}}
+ do_test without_rowid3-14.2aux.1.3 {
+ test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3
+ } {{CREATE TABLE t1(a REFERENCES "t3")}}
+
+ # Test ALTER TABLE RENAME TABLE a bit.
+ #
+ do_test without_rowid3-14.2aux.2.1 {
+ drop_all_tables
+ execsql {
+ CREATE TABLE aux.t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid;
+ CREATE TABLE aux.t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid;
+ CREATE TABLE aux.t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1);
+ }
+ execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1) WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES t1, c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES t1, b REFERENCES t2, c REFERENCES t1)} \
+ ]
+ do_test without_rowid3-14.2aux.2.2 {
+ execsql { ALTER TABLE t1 RENAME TO t4 }
+ execsql { SELECT sql FROM aux.sqlite_master WHERE type = 'table'}
+ } [list \
+ {CREATE TABLE "t4"(a PRIMARY KEY, b REFERENCES "t4") WITHOUT rowid} \
+ {CREATE TABLE t2(a PRIMARY KEY, b REFERENCES "t4", c REFERENCES t2)
+ WITHOUT rowid} \
+ {CREATE TABLE t3(a REFERENCES "t4", b REFERENCES t2, c REFERENCES "t4")} \
+ ]
+ do_test without_rowid3-14.2aux.2.3 {
+ catchsql { INSERT INTO t3 VALUES(1, 2, 3) }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2aux.2.4 {
+ execsql { INSERT INTO t4 VALUES(1, NULL) }
+ } {}
+ do_test without_rowid3-14.2aux.2.5 {
+ catchsql { UPDATE t4 SET b = 5 }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-14.2aux.2.6 {
+ catchsql { UPDATE t4 SET b = 1 }
+ } {0 {}}
+ do_test without_rowid3-14.2aux.2.7 {
+ execsql { INSERT INTO t3 VALUES(1, NULL, 1) }
+ } {}
+}
+
+do_test without_rowid3-2.14.3.1 {
+ drop_all_tables
+ execsql {
+ CREATE TABLE t1(a, b REFERENCES nosuchtable);
+ DROP TABLE t1;
+ }
+} {}
+do_test without_rowid3-2.14.3.2 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT rowid;
+ INSERT INTO t1 VALUES('a', 1);
+ CREATE TABLE t2(x REFERENCES t1);
+ INSERT INTO t2 VALUES('a');
+ }
+} {}
+do_test without_rowid3-2.14.3.3 {
+ catchsql { DROP TABLE t1 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-2.14.3.4 {
+ execsql {
+ DELETE FROM t2;
+ DROP TABLE t1;
+ }
+} {}
+do_test without_rowid3-2.14.3.4 {
+ catchsql { INSERT INTO t2 VALUES('x') }
+} {1 {no such table: main.t1}}
+do_test without_rowid3-2.14.3.5 {
+ execsql {
+ CREATE TABLE t1(x PRIMARY KEY) WITHOUT rowid;
+ INSERT INTO t1 VALUES('x');
+ }
+ execsql { INSERT INTO t2 VALUES('x') }
+} {}
+do_test without_rowid3-2.14.3.6 {
+ catchsql { DROP TABLE t1 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-2.14.3.7 {
+ execsql {
+ DROP TABLE t2;
+ DROP TABLE t1;
+ }
+} {}
+do_test without_rowid3-2.14.3.8 {
+ execsql {
+ CREATE TABLE pp(x, y, PRIMARY KEY(x, y)) WITHOUT ROWID;
+ CREATE TABLE cc(a, b, FOREIGN KEY(a, b) REFERENCES pp(x, z));
+ }
+ catchsql { INSERT INTO cc VALUES(1, 2) }
+} {1 {foreign key mismatch - "cc" referencing "pp"}}
+do_test without_rowid3-2.14.3.9 {
+ execsql { DROP TABLE cc }
+} {}
+do_test without_rowid3-2.14.3.10 {
+ execsql {
+ CREATE TABLE cc(a, b,
+ FOREIGN KEY(a, b) REFERENCES pp DEFERRABLE INITIALLY DEFERRED
+ );
+ }
+ execsql {
+ INSERT INTO pp VALUES('a', 'b');
+ INSERT INTO cc VALUES('a', 'b');
+ BEGIN;
+ DROP TABLE pp;
+ CREATE TABLE pp(a, b, c, PRIMARY KEY(b, c)) WITHOUT rowid;
+ INSERT INTO pp VALUES(1, 'a', 'b');
+ COMMIT;
+ }
+} {}
+do_test without_rowid3-2.14.3.11 {
+ execsql {
+ BEGIN;
+ DROP TABLE cc;
+ DROP TABLE pp;
+ COMMIT;
+ }
+} {}
+do_test without_rowid3-2.14.3.12 {
+ execsql {
+ CREATE TABLE b1(a, b);
+ CREATE TABLE b2(a, b REFERENCES b1);
+ DROP TABLE b1;
+ }
+} {}
+do_test without_rowid3-2.14.3.13 {
+ execsql {
+ CREATE TABLE b3(a, b REFERENCES b2 DEFERRABLE INITIALLY DEFERRED);
+ DROP TABLE b2;
+ }
+} {}
+
+# Test that nothing goes wrong when dropping a table that refers to a view.
+# Or dropping a view that an existing FK (incorrectly) refers to. Or either
+# of the above scenarios with a virtual table.
+drop_all_tables
+do_test without_rowid3-2.14.4.1 {
+ execsql {
+ CREATE TABLE t1(x REFERENCES v);
+ CREATE VIEW v AS SELECT * FROM t1;
+ }
+} {}
+do_test without_rowid3-2.14.4.2 {
+ execsql {
+ DROP VIEW v;
+ }
+} {}
+ifcapable vtab {
+ register_echo_module db
+ do_test without_rowid3-2.14.4.3 {
+ execsql { CREATE VIRTUAL TABLE v USING echo(t1) }
+ } {}
+ do_test without_rowid3-2.14.4.2 {
+ execsql {
+ DROP TABLE v;
+ }
+ } {}
+}
+
+#-------------------------------------------------------------------------
+# The following tests, without_rowid3-15.*, test that unnecessary FK related scans
+# and lookups are avoided when the constraint counters are zero.
+#
+drop_all_tables
+proc execsqlS {zSql} {
+ set ::sqlite_search_count 0
+ set ::sqlite_found_count 0
+ set res [uplevel [list execsql $zSql]]
+ concat [expr $::sqlite_found_count + $::sqlite_search_count] $res
+}
+do_test without_rowid3-15.1.1 {
+ execsql {
+ CREATE TABLE pp(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE cc(x, y REFERENCES pp DEFERRABLE INITIALLY DEFERRED);
+ INSERT INTO pp VALUES(1, 'one');
+ INSERT INTO pp VALUES(2, 'two');
+ INSERT INTO cc VALUES('neung', 1);
+ INSERT INTO cc VALUES('song', 2);
+ }
+} {}
+do_test without_rowid3-15.1.2 {
+ execsqlS { INSERT INTO pp VALUES(3, 'three') }
+} {0}
+do_test without_rowid3-15.1.3 {
+ execsql {
+ BEGIN;
+ INSERT INTO cc VALUES('see', 4); -- Violates deferred constraint
+ }
+ execsqlS { INSERT INTO pp VALUES(5, 'five') }
+} {2}
+do_test without_rowid3-15.1.4 {
+ execsql { DELETE FROM cc WHERE x = 'see' }
+ execsqlS { INSERT INTO pp VALUES(6, 'six') }
+} {0}
+do_test without_rowid3-15.1.5 {
+ execsql COMMIT
+} {}
+do_test without_rowid3-15.1.6 {
+ execsql BEGIN
+ execsqlS {
+ DELETE FROM cc WHERE x = 'neung';
+ ROLLBACK;
+ }
+} {1}
+do_test without_rowid3-15.1.7 {
+ execsql {
+ BEGIN;
+ DELETE FROM pp WHERE a = 2;
+ }
+ execsqlS {
+ DELETE FROM cc WHERE x = 'neung';
+ ROLLBACK;
+ }
+} {2}
+
+#-------------------------------------------------------------------------
+# This next block of tests, without_rowid3-16.*, test that rows that refer to
+# themselves may be inserted and deleted.
+#
+foreach {tn zSchema} {
+ 1 { CREATE TABLE self(a INTEGER PRIMARY KEY, b REFERENCES self(a))
+ WITHOUT rowid }
+ 2 { CREATE TABLE self(a PRIMARY KEY, b REFERENCES self(a)) WITHOUT rowid }
+ 3 { CREATE TABLE self(a UNIQUE, b INT PRIMARY KEY REFERENCES self(a))
+ WITHOUT rowid }
+} {
+ drop_all_tables
+ do_test without_rowid3-16.1.$tn.1 {
+ execsql $zSchema
+ execsql { INSERT INTO self VALUES(13, 13) }
+ } {}
+ do_test without_rowid3-16.1.$tn.2 {
+ execsql { UPDATE self SET a = 14, b = 14 }
+ } {}
+
+ do_test without_rowid3-16.1.$tn.3 {
+ catchsql { UPDATE self SET b = 15 }
+ } {1 {FOREIGN KEY constraint failed}}
+
+ do_test without_rowid3-16.1.$tn.4 {
+ catchsql { UPDATE self SET a = 15 }
+ } {1 {FOREIGN KEY constraint failed}}
+
+ do_test without_rowid3-16.1.$tn.5 {
+ catchsql { UPDATE self SET a = 15, b = 16 }
+ } {1 {FOREIGN KEY constraint failed}}
+
+ do_test without_rowid3-16.1.$tn.6 {
+ catchsql { UPDATE self SET a = 17, b = 17 }
+ } {0 {}}
+
+ do_test without_rowid3-16.1.$tn.7 {
+ execsql { DELETE FROM self }
+ } {}
+ do_test without_rowid3-16.1.$tn.8 {
+ catchsql { INSERT INTO self VALUES(20, 21) }
+ } {1 {FOREIGN KEY constraint failed}}
+}
+
+# Additional tests cases using multi-column self-referential
+# FOREIGN KEY constraints.
+#
+drop_all_tables
+do_execsql_test without_rowid3-16.4.1.1 {
+ PRAGMA foreign_keys=ON;
+ CREATE TABLE t1(a,b,c,d,e,f,
+ UNIQUE (a,b),
+ PRIMARY KEY (e,c),
+ FOREIGN KEY (d,f) REFERENCES t1(e,c)
+ ) WITHOUT rowid;
+ INSERT INTO t1 VALUES(1,2,3,5,5,3);
+ INSERT INTO t1 VALUES(2,3,4,6,6,4);
+ INSERT INTO t1 VALUES('x','y',1.5,'fizzle','fizzle',1.5);
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 3 5 5 3 | 2 3 4 6 6 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_execsql_test without_rowid3-16.4.1.2 {
+ UPDATE t1 SET c=99, f=99 WHERE a=1;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 99 5 5 99 | 2 3 4 6 6 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_execsql_test without_rowid3-16.4.1.3 {
+ UPDATE t1 SET e=876, d=876 WHERE a=2;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 99 5 5 99 | 2 3 4 876 876 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_test without_rowid3-16.4.1.4 {
+ catchsql {
+ UPDATE t1 SET c=11, e=22 WHERE a=1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+do_test without_rowid3-16.4.1.5 {
+ catchsql {
+ UPDATE t1 SET d=11, f=22 WHERE a=1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+do_execsql_test without_rowid3-16.4.1.6 {
+ DELETE FROM t1 WHERE a=1;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {2 3 4 876 876 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_execsql_test without_rowid3-16.4.2.1 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a,b,c,d,e,f,
+ PRIMARY KEY (a,b),
+ UNIQUE (e,c),
+ FOREIGN KEY (d,f) REFERENCES t1(e,c)
+ ) WITHOUT rowid;
+ INSERT INTO t1 VALUES(1,2,3,5,5,3);
+ INSERT INTO t1 VALUES(2,3,4,6,6,4);
+ INSERT INTO t1 VALUES('x','y',1.5,'fizzle','fizzle',1.5);
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 3 5 5 3 | 2 3 4 6 6 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_execsql_test without_rowid3-16.4.2.2 {
+ UPDATE t1 SET c=99, f=99 WHERE a=1;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 99 5 5 99 | 2 3 4 6 6 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_execsql_test without_rowid3-16.4.2.3 {
+ UPDATE t1 SET e=876, d=876 WHERE a=2;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {1 2 99 5 5 99 | 2 3 4 876 876 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+do_test without_rowid3-16.4.2.4 {
+ catchsql {
+ UPDATE t1 SET c=11, e=22 WHERE a=1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+do_test without_rowid3-16.4.2.5 {
+ catchsql {
+ UPDATE t1 SET d=11, f=22 WHERE a=1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+do_execsql_test without_rowid3-16.4.2.6 {
+ DELETE FROM t1 WHERE a=1;
+ SELECT *, '|' FROM t1 ORDER BY a, b;
+} {2 3 4 876 876 4 | x y 1.5 fizzle fizzle 1.5 |}
+
+
+#-------------------------------------------------------------------------
+# This next block of tests, without_rowid3-17.*, tests that if "PRAGMA count_changes"
+# is turned on statements that violate immediate FK constraints return
+# SQLITE_CONSTRAINT immediately, not after returning a number of rows.
+# Whereas statements that violate deferred FK constraints return the number
+# of rows before failing.
+#
+# Also test that rows modified by FK actions are not counted in either the
+# returned row count or the values returned by sqlite3_changes(). Like
+# trigger related changes, they are included in sqlite3_total_changes() though.
+#
+drop_all_tables
+do_test without_rowid3-17.1.1 {
+ execsql { PRAGMA count_changes = 1 }
+ execsql {
+ CREATE TABLE one(a, b, c, UNIQUE(b, c));
+ CREATE TABLE two(d, e, f, FOREIGN KEY(e, f) REFERENCES one(b, c));
+ INSERT INTO one VALUES(1, 2, 3);
+ }
+} {1}
+do_test without_rowid3-17.1.2 {
+ set STMT [sqlite3_prepare_v2 db "INSERT INTO two VALUES(4, 5, 6)" -1 dummy]
+ sqlite3_step $STMT
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-17.1.2b SQLITE_CONSTRAINT_FOREIGNKEY
+ifcapable autoreset {
+ do_test without_rowid3-17.1.3 {
+ sqlite3_step $STMT
+ } {SQLITE_CONSTRAINT}
+ verify_ex_errcode without_rowid3-17.1.3b SQLITE_CONSTRAINT_FOREIGNKEY
+} else {
+ do_test without_rowid3-17.1.3 {
+ sqlite3_step $STMT
+ } {SQLITE_MISUSE}
+}
+do_test without_rowid3-17.1.4 {
+ sqlite3_finalize $STMT
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-17.1.4b SQLITE_CONSTRAINT_FOREIGNKEY
+do_test without_rowid3-17.1.5 {
+ execsql {
+ INSERT INTO one VALUES(2, 3, 4);
+ INSERT INTO one VALUES(3, 4, 5);
+ INSERT INTO two VALUES(1, 2, 3);
+ INSERT INTO two VALUES(2, 3, 4);
+ INSERT INTO two VALUES(3, 4, 5);
+ }
+} {1 1 1 1 1}
+do_test without_rowid3-17.1.6 {
+ catchsql {
+ BEGIN;
+ INSERT INTO one VALUES(0, 0, 0);
+ UPDATE two SET e=e+1, f=f+1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-17.1.7 {
+ execsql { SELECT * FROM one }
+} {1 2 3 2 3 4 3 4 5 0 0 0}
+do_test without_rowid3-17.1.8 {
+ execsql { SELECT * FROM two }
+} {1 2 3 2 3 4 3 4 5}
+do_test without_rowid3-17.1.9 {
+ execsql COMMIT
+} {}
+do_test without_rowid3-17.1.10 {
+ execsql {
+ CREATE TABLE three(
+ g, h, i,
+ FOREIGN KEY(h, i) REFERENCES one(b, c) DEFERRABLE INITIALLY DEFERRED
+ );
+ }
+} {}
+do_test without_rowid3-17.1.11 {
+ set STMT [sqlite3_prepare_v2 db "INSERT INTO three VALUES(7, 8, 9)" -1 dummy]
+ sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test without_rowid3-17.1.12 {
+ sqlite3_column_text $STMT 0
+} {1}
+do_test without_rowid3-17.1.13 {
+ sqlite3_step $STMT
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-17.1.13b SQLITE_CONSTRAINT_FOREIGNKEY
+do_test without_rowid3-17.1.14 {
+ sqlite3_finalize $STMT
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-17.1.14b SQLITE_CONSTRAINT_FOREIGNKEY
+
+drop_all_tables
+do_test without_rowid3-17.2.1 {
+ execsql {
+ CREATE TABLE high("a'b!" PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE low(
+ c,
+ "d&6" REFERENCES high ON UPDATE CASCADE ON DELETE CASCADE
+ );
+ }
+} {}
+do_test without_rowid3-17.2.2 {
+ execsql {
+ INSERT INTO high VALUES('a', 'b');
+ INSERT INTO low VALUES('b', 'a');
+ }
+ db changes
+} {1}
+set nTotal [db total_changes]
+do_test without_rowid3-17.2.3 {
+ execsql { UPDATE high SET "a'b!" = 'c' }
+} {1}
+do_test without_rowid3-17.2.4 {
+ db changes
+} {1}
+do_test without_rowid3-17.2.5 {
+ expr [db total_changes] - $nTotal
+} {2}
+do_test without_rowid3-17.2.6 {
+ execsql { SELECT * FROM high ; SELECT * FROM low }
+} {c b b c}
+do_test without_rowid3-17.2.7 {
+ execsql { DELETE FROM high }
+} {1}
+do_test without_rowid3-17.2.8 {
+ db changes
+} {1}
+do_test without_rowid3-17.2.9 {
+ expr [db total_changes] - $nTotal
+} {4}
+do_test without_rowid3-17.2.10 {
+ execsql { SELECT * FROM high ; SELECT * FROM low }
+} {}
+execsql { PRAGMA count_changes = 0 }
+
+#-------------------------------------------------------------------------
+# Test that the authorization callback works.
+#
+
+ifcapable auth {
+ do_test without_rowid3-18.1 {
+ execsql {
+ CREATE TABLE long(a, b PRIMARY KEY, c) WITHOUT rowid;
+ CREATE TABLE short(d, e, f REFERENCES long);
+ CREATE TABLE mid(g, h, i REFERENCES long DEFERRABLE INITIALLY DEFERRED);
+ }
+ } {}
+
+ proc auth {args} {eval lappend ::authargs $args ; return SQLITE_OK}
+ db auth auth
+
+ # An insert on the parent table must read the child key of any deferred
+ # foreign key constraints. But not the child key of immediate constraints.
+ set authargs {}
+ do_test without_rowid3-18.2 {
+ execsql { INSERT INTO long VALUES(1, 2, 3) }
+ set authargs
+ } {SQLITE_INSERT long {} main {} SQLITE_READ mid i main {}}
+
+ # An insert on the child table of an immediate constraint must read the
+ # parent key columns (to see if it is a violation or not).
+ set authargs {}
+ do_test without_rowid3-18.3 {
+ execsql { INSERT INTO short VALUES(1, 3, 2) }
+ set authargs
+ } {SQLITE_INSERT short {} main {} SQLITE_READ long b main {}}
+
+ # As must an insert on the child table of a deferred constraint.
+ set authargs {}
+ do_test without_rowid3-18.4 {
+ execsql { INSERT INTO mid VALUES(1, 3, 2) }
+ set authargs
+ } {SQLITE_INSERT mid {} main {} SQLITE_READ long b main {}}
+
+ do_test without_rowid3-18.5 {
+ execsql {
+ CREATE TABLE nought(a, b PRIMARY KEY, c) WITHOUT rowid;
+ CREATE TABLE cross(d, e, f,
+ FOREIGN KEY(e) REFERENCES nought(b) ON UPDATE CASCADE
+ );
+ }
+ execsql { INSERT INTO nought VALUES(2, 1, 2) }
+ execsql { INSERT INTO cross VALUES(0, 1, 0) }
+ set authargs [list]
+ execsql { UPDATE nought SET b = 5 }
+ set authargs
+ } {SQLITE_UPDATE nought b main {} SQLITE_READ cross e main {} SQLITE_READ cross e main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {} SQLITE_UPDATE cross e main {} SQLITE_READ nought b main {} SQLITE_READ cross e main {} SQLITE_READ nought b main {} SQLITE_READ nought b main {}}
+
+ do_test without_rowid3-18.6 {
+ execsql {SELECT * FROM cross}
+ } {0 5 0}
+
+ do_test without_rowid3-18.7 {
+ execsql {
+ CREATE TABLE one(a INT PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE two(b, c REFERENCES one);
+ INSERT INTO one VALUES(101, 102);
+ }
+ set authargs [list]
+ execsql { INSERT INTO two VALUES(100, 101); }
+ set authargs
+ } {SQLITE_INSERT two {} main {} SQLITE_READ one a main {}}
+
+ # Return SQLITE_IGNORE to requests to read from the parent table. This
+ # causes inserts of non-NULL keys into the child table to fail.
+ #
+ rename auth {}
+ proc auth {args} {
+ if {[lindex $args 1] == "long"} {return SQLITE_IGNORE}
+ return SQLITE_OK
+ }
+ do_test without_rowid3-18.8 {
+ catchsql { INSERT INTO short VALUES(1, 3, 2) }
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-18.9 {
+ execsql { INSERT INTO short VALUES(1, 3, NULL) }
+ } {}
+ do_test without_rowid3-18.10 {
+ execsql { SELECT * FROM short }
+ } {1 3 2 1 3 {}}
+ do_test without_rowid3-18.11 {
+ catchsql { UPDATE short SET f = 2 WHERE f IS NULL }
+ } {1 {FOREIGN KEY constraint failed}}
+
+ db auth {}
+ unset authargs
+}
+
+
+do_test without_rowid3-19.1 {
+ execsql {
+ CREATE TABLE main(id INT PRIMARY KEY) WITHOUT rowid;
+ CREATE TABLE sub(id INT REFERENCES main(id));
+ INSERT INTO main VALUES(1);
+ INSERT INTO main VALUES(2);
+ INSERT INTO sub VALUES(2);
+ }
+} {}
+do_test without_rowid3-19.2 {
+ set S [sqlite3_prepare_v2 db "DELETE FROM main WHERE id = ?" -1 dummy]
+ sqlite3_bind_int $S 1 2
+ sqlite3_step $S
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-19.2b SQLITE_CONSTRAINT_FOREIGNKEY
+do_test without_rowid3-19.3 {
+ sqlite3_reset $S
+} {SQLITE_CONSTRAINT}
+verify_ex_errcode without_rowid3-19.3b SQLITE_CONSTRAINT_FOREIGNKEY
+do_test without_rowid3-19.4 {
+ sqlite3_bind_int $S 1 1
+ sqlite3_step $S
+} {SQLITE_DONE}
+do_test without_rowid3-19.4 {
+ sqlite3_finalize $S
+} {SQLITE_OK}
+
+drop_all_tables
+do_test without_rowid3-20.1 {
+ execsql {
+ CREATE TABLE pp(a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE TABLE cc(c PRIMARY KEY, d REFERENCES pp) WITHOUT rowid;
+ }
+} {}
+
+foreach {tn insert} {
+ 1 "INSERT"
+ 2 "INSERT OR IGNORE"
+ 3 "INSERT OR ABORT"
+ 4 "INSERT OR ROLLBACK"
+ 5 "INSERT OR REPLACE"
+ 6 "INSERT OR FAIL"
+} {
+ do_test without_rowid3-20.2.$tn.1 {
+ catchsql "$insert INTO cc VALUES(1, 2)"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.2.$tn.2 {
+ execsql { SELECT * FROM cc }
+ } {}
+ do_test without_rowid3-20.2.$tn.3 {
+ execsql {
+ BEGIN;
+ INSERT INTO pp VALUES(2, 'two');
+ INSERT INTO cc VALUES(1, 2);
+ }
+ catchsql "$insert INTO cc VALUES(3, 4)"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.2.$tn.4 {
+ execsql { COMMIT ; SELECT * FROM cc }
+ } {1 2}
+ do_test without_rowid3-20.2.$tn.5 {
+ execsql { DELETE FROM cc ; DELETE FROM pp }
+ } {}
+}
+
+foreach {tn update} {
+ 1 "UPDATE"
+ 2 "UPDATE OR IGNORE"
+ 3 "UPDATE OR ABORT"
+ 4 "UPDATE OR ROLLBACK"
+ 5 "UPDATE OR REPLACE"
+ 6 "UPDATE OR FAIL"
+} {
+ do_test without_rowid3-20.3.$tn.1 {
+ execsql {
+ INSERT INTO pp VALUES(2, 'two');
+ INSERT INTO cc VALUES(1, 2);
+ }
+ } {}
+ do_test without_rowid3-20.3.$tn.2 {
+ catchsql "$update pp SET a = 1"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.3.$tn.3 {
+ execsql { SELECT * FROM pp }
+ } {2 two}
+ do_test without_rowid3-20.3.$tn.4 {
+ catchsql "$update cc SET d = 1"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.3.$tn.5 {
+ execsql { SELECT * FROM cc }
+ } {1 2}
+ do_test without_rowid3-20.3.$tn.6 {
+ execsql {
+ BEGIN;
+ INSERT INTO pp VALUES(3, 'three');
+ }
+ catchsql "$update pp SET a = 1 WHERE a = 2"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.3.$tn.7 {
+ execsql { COMMIT ; SELECT * FROM pp }
+ } {2 two 3 three}
+ do_test without_rowid3-20.3.$tn.8 {
+ execsql {
+ BEGIN;
+ INSERT INTO cc VALUES(2, 2);
+ }
+ catchsql "$update cc SET d = 1 WHERE c = 1"
+ } {1 {FOREIGN KEY constraint failed}}
+ do_test without_rowid3-20.3.$tn.9 {
+ execsql { COMMIT ; SELECT * FROM cc }
+ } {1 2 2 2}
+ do_test without_rowid3-20.3.$tn.10 {
+ execsql { DELETE FROM cc ; DELETE FROM pp }
+ } {}
+}
+
+#-------------------------------------------------------------------------
+# The following block of tests, those prefixed with "without_rowid3-genfkey.",
+# are the same tests that were used to test the ".genfkey" command provided
+# by the shell tool. So these tests show that the built-in foreign key
+# implementation is more or less compatible with the triggers generated
+# by genfkey.
+#
+drop_all_tables
+do_test without_rowid3-genfkey.1.1 {
+ execsql {
+ CREATE TABLE t1(a INT PRIMARY KEY, b, c, UNIQUE(b, c)) WITHOUT rowid;
+ CREATE TABLE t2(e REFERENCES t1, f);
+ CREATE TABLE t3(g, h, i, FOREIGN KEY (h, i) REFERENCES t1(b, c));
+ }
+} {}
+do_test without_rowid3-genfkey.1.2 {
+ catchsql { INSERT INTO t2 VALUES(1, 2) }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.3 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t2 VALUES(1, 2);
+ }
+} {}
+do_test without_rowid3-genfkey.1.4 {
+ execsql { INSERT INTO t2 VALUES(NULL, 3) }
+} {}
+do_test without_rowid3-genfkey.1.5 {
+ catchsql { UPDATE t2 SET e = 5 WHERE e IS NULL }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.6 {
+ execsql { UPDATE t2 SET e = 1 WHERE e IS NULL }
+} {}
+do_test without_rowid3-genfkey.1.7 {
+ execsql { UPDATE t2 SET e = NULL WHERE f = 3 }
+} {}
+do_test without_rowid3-genfkey.1.8 {
+ catchsql { UPDATE t1 SET a = 10 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.9 {
+ catchsql { UPDATE t1 SET a = NULL }
+} {1 {NOT NULL constraint failed: t1.a}}
+do_test without_rowid3-genfkey.1.10 {
+ catchsql { DELETE FROM t1 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.11 {
+ execsql { UPDATE t2 SET e = NULL }
+} {}
+do_test without_rowid3-genfkey.1.12 {
+ execsql {
+ UPDATE t1 SET a = 10;
+ DELETE FROM t1;
+ DELETE FROM t2;
+ }
+} {}
+do_test without_rowid3-genfkey.1.13 {
+ execsql {
+ INSERT INTO t3 VALUES(1, NULL, NULL);
+ INSERT INTO t3 VALUES(1, 2, NULL);
+ INSERT INTO t3 VALUES(1, NULL, 3);
+ }
+} {}
+do_test without_rowid3-genfkey.1.14 {
+ catchsql { INSERT INTO t3 VALUES(3, 1, 4) }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.15 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 1, 4);
+ INSERT INTO t3 VALUES(3, 1, 4);
+ }
+} {}
+do_test without_rowid3-genfkey.1.16 {
+ catchsql { DELETE FROM t1 }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.17 {
+ catchsql { UPDATE t1 SET b = 10}
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-genfkey.1.18 {
+ execsql { UPDATE t1 SET a = 10}
+} {}
+do_test without_rowid3-genfkey.1.19 {
+ catchsql { UPDATE t3 SET h = 'hello' WHERE i = 3}
+} {1 {FOREIGN KEY constraint failed}}
+
+drop_all_tables
+do_test without_rowid3-genfkey.2.1 {
+ execsql {
+ CREATE TABLE t1(a INT PRIMARY KEY, b, c, UNIQUE(b, c)) WITHOUT rowid;
+ CREATE TABLE t2(e REFERENCES t1 ON UPDATE CASCADE ON DELETE CASCADE, f);
+ CREATE TABLE t3(g, h, i,
+ FOREIGN KEY (h, i)
+ REFERENCES t1(b, c) ON UPDATE CASCADE ON DELETE CASCADE
+ );
+ }
+} {}
+do_test without_rowid3-genfkey.2.2 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES(4, 5, 6);
+ INSERT INTO t2 VALUES(1, 'one');
+ INSERT INTO t2 VALUES(4, 'four');
+ }
+} {}
+do_test without_rowid3-genfkey.2.3 {
+ execsql {
+ UPDATE t1 SET a = 2 WHERE a = 1;
+ SELECT * FROM t2;
+ }
+} {2 one 4 four}
+do_test without_rowid3-genfkey.2.4 {
+ execsql {
+ DELETE FROM t1 WHERE a = 4;
+ SELECT * FROM t2;
+ }
+} {2 one}
+
+do_test without_rowid3-genfkey.2.5 {
+ execsql {
+ INSERT INTO t3 VALUES('hello', 2, 3);
+ UPDATE t1 SET c = 2;
+ SELECT * FROM t3;
+ }
+} {hello 2 2}
+do_test without_rowid3-genfkey.2.6 {
+ execsql {
+ DELETE FROM t1;
+ SELECT * FROM t3;
+ }
+} {}
+
+drop_all_tables
+do_test without_rowid3-genfkey.3.1 {
+ execsql {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, UNIQUE(c, b)) WITHOUT rowid;
+ CREATE TABLE t2(e REFERENCES t1 ON UPDATE SET NULL ON DELETE SET NULL, f);
+ CREATE TABLE t3(g, h, i,
+ FOREIGN KEY (h, i)
+ REFERENCES t1(b, c) ON UPDATE SET NULL ON DELETE SET NULL
+ );
+ }
+} {}
+do_test without_rowid3-genfkey.3.2 {
+ execsql {
+ INSERT INTO t1 VALUES(1, 2, 3);
+ INSERT INTO t1 VALUES(4, 5, 6);
+ INSERT INTO t2 VALUES(1, 'one');
+ INSERT INTO t2 VALUES(4, 'four');
+ }
+} {}
+do_test without_rowid3-genfkey.3.3 {
+ execsql {
+ UPDATE t1 SET a = 2 WHERE a = 1;
+ SELECT * FROM t2;
+ }
+} {{} one 4 four}
+do_test without_rowid3-genfkey.3.4 {
+ execsql {
+ DELETE FROM t1 WHERE a = 4;
+ SELECT * FROM t2;
+ }
+} {{} one {} four}
+do_test without_rowid3-genfkey.3.5 {
+ execsql {
+ INSERT INTO t3 VALUES('hello', 2, 3);
+ UPDATE t1 SET c = 2;
+ SELECT * FROM t3;
+ }
+} {hello {} {}}
+do_test without_rowid3-genfkey.3.6 {
+ execsql {
+ UPDATE t3 SET h = 2, i = 2;
+ DELETE FROM t1;
+ SELECT * FROM t3;
+ }
+} {hello {} {}}
+
+#-------------------------------------------------------------------------
+# Verify that ticket dd08e5a988d00decc4a543daa8dbbfab9c577ad8 has been
+# fixed.
+#
+do_test without_rowid3-dd08e5.1.1 {
+ execsql {
+ PRAGMA foreign_keys=ON;
+ CREATE TABLE tdd08(a INTEGER PRIMARY KEY, b) WITHOUT rowid;
+ CREATE UNIQUE INDEX idd08 ON tdd08(a,b);
+ INSERT INTO tdd08 VALUES(200,300);
+
+ CREATE TABLE tdd08_b(w,x,y, FOREIGN KEY(x,y) REFERENCES tdd08(a,b));
+ INSERT INTO tdd08_b VALUES(100,200,300);
+ }
+} {}
+do_test without_rowid3-dd08e5.1.2 {
+ catchsql {
+ DELETE FROM tdd08;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-dd08e5.1.3 {
+ execsql {
+ SELECT * FROM tdd08;
+ }
+} {200 300}
+do_test without_rowid3-dd08e5.1.4 {
+ catchsql {
+ INSERT INTO tdd08_b VALUES(400,500,300);
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-dd08e5.1.5 {
+ catchsql {
+ UPDATE tdd08_b SET x=x+1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-dd08e5.1.6 {
+ catchsql {
+ UPDATE tdd08 SET a=a+1;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+#-------------------------------------------------------------------------
+# Verify that ticket ce7c133ea6cc9ccdc1a60d80441f80b6180f5eba
+# fixed.
+#
+do_test without_rowid3-ce7c13.1.1 {
+ execsql {
+ CREATE TABLE tce71(a INTEGER PRIMARY KEY, b) WITHOUT rowid;
+ CREATE UNIQUE INDEX ice71 ON tce71(a,b);
+ INSERT INTO tce71 VALUES(100,200);
+ CREATE TABLE tce72(w, x, y, FOREIGN KEY(x,y) REFERENCES tce71(a,b));
+ INSERT INTO tce72 VALUES(300,100,200);
+ UPDATE tce71 set b = 200 where a = 100;
+ SELECT * FROM tce71, tce72;
+ }
+} {100 200 300 100 200}
+do_test without_rowid3-ce7c13.1.2 {
+ catchsql {
+ UPDATE tce71 set b = 201 where a = 100;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-ce7c13.1.3 {
+ catchsql {
+ UPDATE tce71 set a = 101 where a = 100;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-ce7c13.1.4 {
+ execsql {
+ CREATE TABLE tce73(a INTEGER PRIMARY KEY, b, UNIQUE(a,b)) WITHOUT rowid;
+ INSERT INTO tce73 VALUES(100,200);
+ CREATE TABLE tce74(w, x, y, FOREIGN KEY(x,y) REFERENCES tce73(a,b));
+ INSERT INTO tce74 VALUES(300,100,200);
+ UPDATE tce73 set b = 200 where a = 100;
+ SELECT * FROM tce73, tce74;
+ }
+} {100 200 300 100 200}
+do_test without_rowid3-ce7c13.1.5 {
+ catchsql {
+ UPDATE tce73 set b = 201 where a = 100;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+do_test without_rowid3-ce7c13.1.6 {
+ catchsql {
+ UPDATE tce73 set a = 101 where a = 100;
+ }
+} {1 {FOREIGN KEY constraint failed}}
+
+finish_test
diff --git a/test/without_rowid4.test b/test/without_rowid4.test
new file mode 100644
index 0000000..d8c2d69
--- /dev/null
+++ b/test/without_rowid4.test
@@ -0,0 +1,764 @@
+# 2013-11-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.
+#
+#***********************************************************************
+#
+# Regression testing of FOR EACH ROW table triggers on WITHOUT ROWID
+# tables.
+#
+# 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.
+#
+# without_rowid4-1.1.*: ON UPDATE trigger execution model.
+# without_rowid4-1.2.*: DELETE trigger execution model.
+# without_rowid4-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.
+#
+# without_rowid4-3.1: UPDATE OF triggers
+# without_rowid4-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.
+#
+# without_rowid4-4.1: Trivial cascading trigger
+# without_rowid4-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
+# without_rowid4-6.1[a-f]: INSERT statements
+# without_rowid4-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
+}
+
+# The tests in this file were written before SQLite supported recursive
+# trigger invocation, and some tests depend on that to pass. So disable
+# recursive triggers for this file.
+catchsql { pragma recursive_triggers = off }
+
+# 1.
+ifcapable subquery {
+ set ii 0
+ set tbl_definitions [list \
+ {CREATE TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;} \
+ {CREATE TABLE tbl (a, b PRIMARY KEY) WITHOUT rowid;} \
+ {CREATE TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE INDEX tbl_idx ON tbl(b);} \
+ ]
+ ifcapable tempdb {
+ lappend tbl_definitions \
+ {CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid;
+ CREATE INDEX tbl_idx ON tbl(b);}
+ lappend tbl_definitions \
+ {CREATE TEMP TABLE tbl (a PRIMARY KEY, b) WITHOUT rowid}
+ lappend tbl_definitions \
+ {CREATE TEMPORARY TABLE tbl (a INTEGER PRIMARY KEY, b) WITHOUT rowid;}
+ }
+ 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 without_rowid4-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 without_rowid4-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 without_rowid4-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 without_rowid4-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) WITHOUT rowid;
+ 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 without_rowid4-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 without_rowid4-2.$ii-after "execsql {$statement $query}" $after_data
+ execsql "DROP TRIGGER the_trigger;"
+
+ integrity_check without_rowid4-2.$ii-integrity
+ }
+}
+catchsql {
+ DROP TABLE tbl;
+ DROP TABLE log;
+}
+
+# 3.
+
+# without_rowid4-3.1: UPDATE OF triggers
+execsql {
+ CREATE TABLE tbl (a, b, c, d, PRIMARY KEY(a,b,c,d)) WITHOUT rowid;
+ 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 without_rowid4-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;
+}
+
+# without_rowid4-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 without_rowid4-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 without_rowid4-3.3
+
+# Simple cascaded trigger
+execsql {
+ CREATE TABLE tblA(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+ CREATE TABLE tblB(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+ CREATE TABLE tblC(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+
+ 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 without_rowid4-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, PRIMARY KEY(c,a,b)) WITHOUT rowid;
+ CREATE TRIGGER tbl_trig BEFORE INSERT ON tbl
+ BEGIN
+ INSERT INTO tbl VALUES (new.a, new.b, new.c+1);
+ END;
+}
+do_test without_rowid4-4.2 {
+ execsql {
+ INSERT INTO tbl VALUES (1, 2, 3);
+ select * from tbl;
+ }
+} {1 2 3 1 2 4}
+execsql {
+ DROP TABLE tbl;
+}
+
+# 5.
+execsql {
+ CREATE TABLE tbl(a, b, c, PRIMARY KEY(c,a,b)) WITHOUT rowid;
+ 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 without_rowid4-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) WITHOUT rowid;
+ CREATE TRIGGER ai_tbl AFTER INSERT ON tbl BEGIN
+ INSERT OR IGNORE INTO tbl values (new.a, 0, 0);
+ END;
+ }
+ do_test without_rowid4-6.1a {
+ execsql {
+ BEGIN;
+ INSERT INTO tbl values (1, 2, 3);
+ SELECT * from tbl;
+ }
+ } {1 2 3}
+ do_test without_rowid4-6.1b {
+ catchsql {
+ INSERT OR ABORT INTO tbl values (2, 2, 3);
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-6.1c {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 3}
+ do_test without_rowid4-6.1d {
+ catchsql {
+ INSERT OR FAIL INTO tbl values (2, 2, 3);
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-6.1e {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 3 2 2 3}
+ do_test without_rowid4-6.1f {
+ execsql {
+ INSERT OR REPLACE INTO tbl values (2, 2, 3);
+ SELECT * from tbl;
+ }
+ } {1 2 3 2 0 0}
+ do_test without_rowid4-6.1g {
+ catchsql {
+ INSERT OR ROLLBACK INTO tbl values (3, 2, 3);
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-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 without_rowid4-6.2a {
+ execsql {
+ BEGIN;
+ UPDATE tbl SET a = 1 WHERE a = 4;
+ SELECT * from tbl;
+ }
+ } {1 2 10 6 3 4}
+ do_test without_rowid4-6.2b {
+ catchsql {
+ UPDATE OR ABORT tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-6.2c {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {1 2 10 6 3 4}
+ do_test without_rowid4-6.2d {
+ catchsql {
+ UPDATE OR FAIL tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-6.2e {
+ execsql {
+ SELECT * from tbl;
+ }
+ } {4 2 10 6 3 4}
+ do_test without_rowid4-6.2f.1 {
+ execsql {
+ UPDATE OR REPLACE tbl SET a = 1 WHERE a = 4;
+ SELECT * from tbl;
+ }
+ } {1 3 10}
+ do_test without_rowid4-6.2f.2 {
+ execsql {
+ INSERT INTO tbl VALUES (2, 3, 4);
+ SELECT * FROM tbl;
+ }
+ } {1 3 10 2 3 4}
+ do_test without_rowid4-6.2g {
+ catchsql {
+ UPDATE OR ROLLBACK tbl SET a = 4 WHERE a = 1;
+ }
+ } {1 {UNIQUE constraint failed: tbl.a}}
+ do_test without_rowid4-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 without_rowid4-7.1 {
+ execsql {
+ CREATE TABLE ab(a, b, PRIMARY KEY(a,b)) WITHOUT rowid;
+ CREATE TABLE cd(c, d, PRIMARY KEY(c,d)) WITHOUT rowid;
+ 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 without_rowid4-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 without_rowid4-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 without_rowid4-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 without_rowid4-8.1 {
+ execsql {
+ CREATE TABLE t1(a,b,c, PRIMARY KEY(a,b,c)) WITHOUT rowid;
+ 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 without_rowid4-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 without_rowid4-8.3 {
+ execsql {
+ DELETE FROM v1 WHERE x=3;
+ SELECT * FROM v1log;
+ }
+} {3 {} 5 {} 4 {}}
+do_test without_rowid4-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 without_rowid4-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 without_rowid4-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}
+
+# At one point the following was causing a segfault.
+do_test without_rowid4-9.1 {
+ execsql {
+ CREATE TABLE t3(a TEXT, b TEXT);
+ CREATE VIEW v3 AS SELECT t3.a FROM t3;
+ CREATE TRIGGER trig1 INSTEAD OF DELETE ON v3 BEGIN
+ SELECT 1;
+ END;
+ DELETE FROM v3 WHERE a = 1;
+ }
+} {}
+
+} ;# ifcapable view
+
+integrity_check without_rowid4-9.9
+
+finish_test
diff --git a/test/without_rowid5.test b/test/without_rowid5.test
new file mode 100644
index 0000000..45e047b
--- /dev/null
+++ b/test/without_rowid5.test
@@ -0,0 +1,201 @@
+# 2013-11-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.
+#
+#***********************************************************************
+#
+# Requirements testing for WITHOUT ROWID tables.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+
+# EVIDENCE-OF: R-36924-43758 By default, every row in SQLite has a
+# special column, usually called the "rowid", that uniquely identifies
+# that row within the table.
+#
+# EVIDENCE-OF: R-32341-39358 However if the phrase "WITHOUT ROWID" is
+# added to the end of a CREATE TABLE statement, then the special "rowid"
+# column is omitted.
+#
+do_execsql_test without_rowid5-1.1 {
+ CREATE TABLE t1(a PRIMARY KEY,b,c);
+ CREATE TABLE t1w(a PRIMARY KEY,b,c) WITHOUT ROWID;
+ INSERT INTO t1 VALUES(1565,681,1148),(1429,1190,1619),(425,358,1306);
+ INSERT INTO t1w SELECT a,b,c FROM t1;
+ SELECT rowid, _rowid_, oid FROM t1 ORDER BY a DESC;
+} {1 1 1 2 2 2 3 3 3}
+do_catchsql_test without_rowid5-1.2 {
+ SELECT rowid FROM t1w;
+} {1 {no such column: rowid}}
+do_catchsql_test without_rowid5-1.3 {
+ SELECT _rowid_ FROM t1w;
+} {1 {no such column: _rowid_}}
+do_catchsql_test without_rowid5-1.4 {
+ SELECT oid FROM t1w;
+} {1 {no such column: oid}}
+
+# EVIDENCE-OF: R-00217-01605 To create a WITHOUT ROWID table, simply add
+# the keywords "WITHOUT ROWID" to the end of the CREATE TABLE statement.
+# For example: CREATE TABLE IF NOT EXISTS wordcount( word TEXT PRIMARY
+# KEY, cnt INTEGER ) WITHOUT ROWID;
+#
+do_execsql_test without_rowid5-2.1 {
+ CREATE TABLE IF NOT EXISTS wordcount(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) WITHOUT ROWID;
+ INSERT INTO wordcount VALUES('one',1);
+} {}
+do_catchsql_test without_rowid5-2.2 {
+ SELECT rowid FROM wordcount;
+} {1 {no such column: rowid}}
+
+# EVIDENCE-OF: R-24770-17719 As with all SQL syntax, the case of the
+# keywords does not matter. One can write "WITHOUT rowid" or "without
+# rowid" or "WiThOuT rOwId" and it will mean the same thing.
+#
+do_execsql_test without_rowid5-2.3 {
+ CREATE TABLE IF NOT EXISTS wordcount_b(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) WITHOUT rowid;
+ INSERT INTO wordcount_b VALUES('one',1);
+} {}
+do_catchsql_test without_rowid5-2.4 {
+ SELECT rowid FROM wordcount_b;
+} {1 {no such column: rowid}}
+do_execsql_test without_rowid5-2.5 {
+ CREATE TABLE IF NOT EXISTS wordcount_c(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) without rowid;
+ INSERT INTO wordcount_c VALUES('one',1);
+} {}
+do_catchsql_test without_rowid5-2.6 {
+ SELECT rowid FROM wordcount_c;
+} {1 {no such column: rowid}}
+do_execsql_test without_rowid5-2.7 {
+ CREATE TABLE IF NOT EXISTS wordcount_d(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) WITHOUT rowid;
+ INSERT INTO wordcount_d VALUES('one',1);
+} {}
+do_catchsql_test without_rowid5-2.8 {
+ SELECT rowid FROM wordcount_d;
+} {1 {no such column: rowid}}
+
+# EVIDENCE-OF: R-01418-51310 However, only "rowid" works as the keyword
+# in the CREATE TABLE statement.
+#
+do_catchsql_test without_rowid5-3.1 {
+ CREATE TABLE IF NOT EXISTS error1(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) WITHOUT _rowid_;
+} {1 {unknown table option: _rowid_}}
+do_catchsql_test without_rowid5-3.2 {
+ CREATE TABLE IF NOT EXISTS error2(
+ word TEXT PRIMARY KEY,
+ cnt INTEGER
+ ) WITHOUT oid;
+} {1 {unknown table option: oid}}
+
+# EVIDENCE-OF: R-58033-17334 An error is raised if a CREATE TABLE
+# statement with the WITHOUT ROWID clause lacks a PRIMARY KEY.
+#
+# EVIDENCE-OF: R-63443-09418 Every WITHOUT ROWID table must have a
+# PRIMARY KEY.
+#
+# EVIDENCE-OF: R-27966-31616 An attempt to create a WITHOUT ROWID table
+# without a PRIMARY KEY results in an error.
+#
+do_catchsql_test without_rowid5-4.1 {
+ CREATE TABLE IF NOT EXISTS error3(
+ word TEXT UNIQUE,
+ cnt INTEGER
+ ) WITHOUT ROWID;
+} {1 {PRIMARY KEY missing on table error3}}
+
+# EVIDENCE-OF: R-48230-36247 The special behaviors associated "INTEGER
+# PRIMARY KEY" do not apply on WITHOUT ROWID tables.
+#
+do_execsql_test without_rowid5-5.1 {
+ CREATE TABLE ipk(key INTEGER PRIMARY KEY, val TEXT) WITHOUT ROWID;
+ INSERT INTO ipk VALUES('rival','bonus'); -- ok to insert non-integer key
+ SELECT * FROM ipk;
+} {rival bonus}
+do_catchsql_test without_rowid5-5.2 {
+ INSERT INTO ipk VALUES(NULL,'sample'); -- no automatic generation of keys
+} {1 {NOT NULL constraint failed: ipk.key}}
+
+# EVIDENCE-OF: R-33142-02092 AUTOINCREMENT does not work on WITHOUT
+# ROWID tables.
+#
+# EVIDENCE-OF: R-53084-07740 An error is raised if the "AUTOINCREMENT"
+# keyword is used in the CREATE TABLE statement for a WITHOUT ROWID
+# table.
+#
+do_catchsql_test without_rowid5-5.3 {
+ CREATE TABLE ipk2(key INTEGER PRIMARY KEY AUTOINCREMENT, val TEXT)WITHOUT ROWID;
+} {1 {AUTOINCREMENT not allowed on WITHOUT ROWID tables}}
+
+# EVIDENCE-OF: R-27831-00579 NOT NULL is enforced on every column of the
+# PRIMARY KEY in a WITHOUT ROWID table.
+#
+# EVIDENCE-OF: R-29781-51289 So, ordinary rowid tables in SQLite violate
+# the SQL standard and allow NULL values in PRIMARY KEY fields.
+#
+# EVIDENCE-OF: R-27472-62612 But WITHOUT ROWID tables do follow the
+# standard and will throw an error on any attempt to insert a NULL into
+# a PRIMARY KEY column.
+#
+do_execsql_test without_rowid5-5.4 {
+ CREATE TABLE nn(a, b, c, d, e, PRIMARY KEY(c,a,e));
+ CREATE TABLE nnw(a, b, c, d, e, PRIMARY KEY(c,a,e)) WITHOUT ROWID;
+ INSERT INTO nn VALUES(1,2,3,4,5);
+ INSERT INTO nnw VALUES(1,2,3,4,5);
+} {}
+do_execsql_test without_rowid5-5.5 {
+ INSERT INTO nn VALUES(NULL, 3,4,5,6);
+ INSERT INTO nn VALUES(3,4,NULL,7,8);
+ INSERT INTO nn VALUES(4,5,6,7,NULL);
+ SELECT count(*) FROM nn;
+} {4}
+do_catchsql_test without_rowid5-5.6 {
+ INSERT INTO nnw VALUES(NULL, 3,4,5,6);
+} {1 {NOT NULL constraint failed: nnw.a}}
+do_catchsql_test without_rowid5-5.7 {
+ INSERT INTO nnw VALUES(3,4,NULL,7,8)
+} {1 {NOT NULL constraint failed: nnw.c}}
+do_catchsql_test without_rowid5-5.8 {
+ INSERT INTO nnw VALUES(4,5,6,7,NULL)
+} {1 {NOT NULL constraint failed: nnw.e}}
+do_execsql_test without_rowid5-5.9 {
+ SELECT count(*) FROM nnw;
+} {1}
+
+# EVIDENCE-OF: R-12643-30541 The incremental blob I/O mechanism does not
+# work for WITHOUT ROWID tables.
+#
+# EVIDENCE-OF: R-25760-33257 The sqlite3_blob_open() interface will fail
+# for a WITHOUT ROWID table.
+#
+do_execsql_test without_rowid5-6.1 {
+ CREATE TABLE b1(a INTEGER PRIMARY KEY, b BLOB) WITHOUT ROWID;
+ INSERT INTO b1 VALUES(1,x'0102030405060708090a0b0c0d0e0f');
+} {}
+do_test without_rowid5-6.2 {
+ set rc [catch {db incrblob b1 b 1} msg]
+ lappend rc $msg
+} {1 {cannot open table without rowid: b1}}
+
+
+finish_test
diff --git a/test/wordcount.c b/test/wordcount.c
new file mode 100644
index 0000000..cf63e98
--- /dev/null
+++ b/test/wordcount.c
@@ -0,0 +1,545 @@
+/*
+** This C program extracts all "words" from an input document and adds them
+** to an SQLite database. A "word" is any contiguous sequence of alphabetic
+** characters. All digits, punctuation, and whitespace characters are
+** word separators. The database stores a single entry for each distinct
+** word together with a count of the number of occurrences of that word.
+** A fresh database is created automatically on each run.
+**
+** wordcount DATABASE INPUTFILE
+**
+** The INPUTFILE name can be omitted, in which case input it taken from
+** standard input.
+**
+** Option:
+**
+** --without-rowid Use a WITHOUT ROWID table to store the words.
+** --insert Use INSERT mode (the default)
+** --replace Use REPLACE mode
+** --select Use SELECT mode
+** --update Use UPDATE mode
+** --delete Use DELETE mode
+** --query Use QUERY mode
+** --nocase Add the NOCASE collating sequence to the words.
+** --trace Enable sqlite3_trace() output.
+** --summary Show summary information on the collected data.
+** --stats Show sqlite3_status() results at the end.
+** --pagesize NNN Use a page size of NNN
+** --cachesize NNN Use a cache size of NNN
+** --commit NNN Commit after every NNN operations
+** --nosync Use PRAGMA synchronous=OFF
+** --journal MMMM Use PRAGMA journal_mode=MMMM
+** --timer Time the operation of this program
+**
+** Modes:
+**
+** Insert mode means:
+** (1) INSERT OR IGNORE INTO wordcount VALUES($new,1)
+** (2) UPDATE wordcount SET cnt=cnt+1 WHERE word=$new -- if (1) is a noop
+**
+** Update mode means:
+** (1) INSERT OR IGNORE INTO wordcount VALUES($new,0)
+** (2) UPDATE wordcount SET cnt=cnt+1 WHERE word=$new
+**
+** Replace mode means:
+** (1) REPLACE INTO wordcount
+** VALUES($new,ifnull((SELECT cnt FROM wordcount WHERE word=$new),0)+1);
+**
+** Select mode means:
+** (1) SELECT 1 FROM wordcount WHERE word=$new
+** (2) INSERT INTO wordcount VALUES($new,1) -- if (1) returns nothing
+** (3) UPDATE wordcount SET cnt=cnt+1 WHERE word=$new --if (1) return TRUE
+**
+** Delete mode means:
+** (1) DELETE FROM wordcount WHERE word=$new
+**
+** Query mode means:
+** (1) SELECT cnt FROM wordcount WHERE word=$new
+**
+** Note that delete mode and query mode are only useful for preexisting
+** databases. The wordcount table is created using IF NOT EXISTS so this
+** utility can be run multiple times on the same database file. The
+** --without-rowid, --nocase, and --pagesize parameters are only effective
+** when creating a new database and are harmless no-ops on preexisting
+** databases.
+**
+******************************************************************************
+**
+** Compile as follows:
+**
+** gcc -I. wordcount.c sqlite3.c -ldl -lpthreads
+**
+** Or:
+**
+** gcc -I. -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
+** wordcount.c sqlite3.c
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "sqlite3.h"
+
+/* Return the current wall-clock time */
+static sqlite3_int64 realTime(void){
+ static sqlite3_vfs *clockVfs = 0;
+ sqlite3_int64 t;
+ if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
+ if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){
+ clockVfs->xCurrentTimeInt64(clockVfs, &t);
+ }else{
+ double r;
+ clockVfs->xCurrentTime(clockVfs, &r);
+ t = (sqlite3_int64)(r*86400000.0);
+ }
+ return t;
+}
+
+/* Print an error message and exit */
+static void fatal_error(const char *zMsg, ...){
+ va_list ap;
+ va_start(ap, zMsg);
+ vfprintf(stderr, zMsg, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/* The sqlite3_trace() callback function */
+static void traceCallback(void *NotUsed, const char *zSql){
+ printf("%s;\n", zSql);
+}
+
+/* An sqlite3_exec() callback that prints results on standard output,
+** each column separated by a single space. */
+static int printResult(void *NotUsed, int nArg, char **azArg, char **azNm){
+ int i;
+ printf("--");
+ for(i=0; i<nArg; i++){
+ printf(" %s", azArg[i] ? azArg[i] : "(null)");
+ }
+ printf("\n");
+ return 0;
+}
+
+
+/*
+** Add one character to a hash
+*/
+static void addCharToHash(unsigned int *a, unsigned char x){
+ if( a[0]<4 ){
+ a[1] = (a[1]<<8) | x;
+ a[0]++;
+ }else{
+ a[2] = (a[2]<<8) | x;
+ a[0]++;
+ if( a[0]==8 ){
+ a[3] += a[1] + a[4];
+ a[4] += a[2] + a[3];
+ a[0] = a[1] = a[2] = 0;
+ }
+ }
+}
+
+/*
+** Compute the final hash value.
+*/
+static void finalHash(unsigned int *a, char *z){
+ a[3] += a[1] + a[4] + a[0];
+ a[4] += a[2] + a[3];
+ sqlite3_snprintf(17, z, "%08x%08x", a[3], a[4]);
+}
+
+
+/*
+** Implementation of a checksum() aggregate SQL function
+*/
+static void checksumStep(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *zVal;
+ int nVal, i, j;
+ unsigned int *a;
+ a = (unsigned*)sqlite3_aggregate_context(context, sizeof(unsigned int)*5);
+
+ if( a ){
+ for(i=0; i<argc; i++){
+ nVal = sqlite3_value_bytes(argv[i]);
+ zVal = (const unsigned char*)sqlite3_value_text(argv[i]);
+ if( zVal ) for(j=0; j<nVal; j++) addCharToHash(a, zVal[j]);
+ addCharToHash(a, '|');
+ }
+ addCharToHash(a, '\n');
+ }
+}
+static void checksumFinalize(sqlite3_context *context){
+ unsigned int *a;
+ char zResult[24];
+ a = sqlite3_aggregate_context(context, 0);
+ if( a ){
+ finalHash(a, zResult);
+ sqlite3_result_text(context, zResult, -1, SQLITE_TRANSIENT);
+ }
+}
+
+
+/* Define operating modes */
+#define MODE_INSERT 0
+#define MODE_REPLACE 1
+#define MODE_SELECT 2
+#define MODE_UPDATE 3
+#define MODE_DELETE 4
+#define MODE_QUERY 5
+
+int main(int argc, char **argv){
+ const char *zFileToRead = 0; /* Input file. NULL for stdin */
+ const char *zDbName = 0; /* Name of the database file to create */
+ int useWithoutRowid = 0; /* True for --without-rowid */
+ int iMode = MODE_INSERT; /* One of MODE_xxxxx */
+ int useNocase = 0; /* True for --nocase */
+ int doTrace = 0; /* True for --trace */
+ int showStats = 0; /* True for --stats */
+ int showSummary = 0; /* True for --summary */
+ int showTimer = 0; /* True for --timer */
+ int cacheSize = 0; /* Desired cache size. 0 means default */
+ int pageSize = 0; /* Desired page size. 0 means default */
+ int commitInterval = 0; /* How often to commit. 0 means never */
+ int noSync = 0; /* True for --nosync */
+ const char *zJMode = 0; /* Journal mode */
+ int nOp = 0; /* Operation counter */
+ int i, j; /* Loop counters */
+ sqlite3 *db; /* The SQLite database connection */
+ char *zSql; /* Constructed SQL statement */
+ sqlite3_stmt *pInsert = 0; /* The INSERT statement */
+ sqlite3_stmt *pUpdate = 0; /* The UPDATE statement */
+ sqlite3_stmt *pSelect = 0; /* The SELECT statement */
+ sqlite3_stmt *pDelete = 0; /* The DELETE statement */
+ FILE *in; /* The open input file */
+ int rc; /* Return code from an SQLite interface */
+ int iCur, iHiwtr; /* Statistics values, current and "highwater" */
+ sqlite3_int64 sumCnt = 0; /* Sum in QUERY mode */
+ sqlite3_int64 startTime;
+ char zInput[2000]; /* A single line of input */
+
+ /* Process command-line arguments */
+ for(i=1; i<argc; i++){
+ const char *z = argv[i];
+ if( z[0]=='-' ){
+ do{ z++; }while( z[0]=='-' );
+ if( strcmp(z,"without-rowid")==0 ){
+ useWithoutRowid = 1;
+ }else if( strcmp(z,"replace")==0 ){
+ iMode = MODE_REPLACE;
+ }else if( strcmp(z,"select")==0 ){
+ iMode = MODE_SELECT;
+ }else if( strcmp(z,"insert")==0 ){
+ iMode = MODE_INSERT;
+ }else if( strcmp(z,"update")==0 ){
+ iMode = MODE_UPDATE;
+ }else if( strcmp(z,"delete")==0 ){
+ iMode = MODE_DELETE;
+ }else if( strcmp(z,"query")==0 ){
+ iMode = MODE_QUERY;
+ }else if( strcmp(z,"nocase")==0 ){
+ useNocase = 1;
+ }else if( strcmp(z,"trace")==0 ){
+ doTrace = 1;
+ }else if( strcmp(z,"nosync")==0 ){
+ noSync = 1;
+ }else if( strcmp(z,"stats")==0 ){
+ showStats = 1;
+ }else if( strcmp(z,"summary")==0 ){
+ showSummary = 1;
+ }else if( strcmp(z,"timer")==0 ){
+ showTimer = i;
+ }else if( strcmp(z,"cachesize")==0 && i<argc-1 ){
+ i++;
+ cacheSize = atoi(argv[i]);
+ }else if( strcmp(z,"pagesize")==0 && i<argc-1 ){
+ i++;
+ pageSize = atoi(argv[i]);
+ }else if( strcmp(z,"commit")==0 && i<argc-1 ){
+ i++;
+ commitInterval = atoi(argv[i]);
+ }else if( strcmp(z,"journal")==0 && i<argc-1 ){
+ zJMode = argv[++i];
+ }else{
+ fatal_error("unknown option: %s\n", argv[i]);
+ }
+ }else if( zDbName==0 ){
+ zDbName = argv[i];
+ }else if( zFileToRead==0 ){
+ zFileToRead = argv[i];
+ }else{
+ fatal_error("surplus argument: %s\n", argv[i]);
+ }
+ }
+ if( zDbName==0 ){
+ fatal_error("Usage: %s [--options] DATABASE [INPUTFILE]\n", argv[0]);
+ }
+ startTime = realTime();
+
+ /* Open the database and the input file */
+ if( sqlite3_open(zDbName, &db) ){
+ fatal_error("Cannot open database file: %s\n", zDbName);
+ }
+ if( zFileToRead ){
+ in = fopen(zFileToRead, "rb");
+ if( in==0 ){
+ fatal_error("Could not open input file \"%s\"\n", zFileToRead);
+ }
+ }else{
+ in = stdin;
+ }
+
+ /* Set database connection options */
+ if( doTrace ) sqlite3_trace(db, traceCallback, 0);
+ if( pageSize ){
+ zSql = sqlite3_mprintf("PRAGMA page_size=%d", pageSize);
+ sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+ if( cacheSize ){
+ zSql = sqlite3_mprintf("PRAGMA cache_size=%d", cacheSize);
+ sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+ if( noSync ) sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
+ if( zJMode ){
+ zSql = sqlite3_mprintf("PRAGMA journal_mode=%s", zJMode);
+ sqlite3_exec(db, zSql, 0, 0, 0);
+ sqlite3_free(zSql);
+ }
+
+
+ /* Construct the "wordcount" table into which to put the words */
+ if( sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, 0) ){
+ fatal_error("Could not start a transaction\n");
+ }
+ zSql = sqlite3_mprintf(
+ "CREATE TABLE IF NOT EXISTS wordcount(\n"
+ " word TEXT PRIMARY KEY COLLATE %s,\n"
+ " cnt INTEGER\n"
+ ")%s",
+ useNocase ? "nocase" : "binary",
+ useWithoutRowid ? " WITHOUT ROWID" : ""
+ );
+ if( zSql==0 ) fatal_error("out of memory\n");
+ rc = sqlite3_exec(db, zSql, 0, 0, 0);
+ if( rc ) fatal_error("Could not create the wordcount table: %s.\n",
+ sqlite3_errmsg(db));
+ sqlite3_free(zSql);
+
+ /* Prepare SQL statements that will be needed */
+ if( iMode==MODE_QUERY ){
+ rc = sqlite3_prepare_v2(db,
+ "SELECT cnt FROM wordcount WHERE word=?1",
+ -1, &pSelect, 0);
+ if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_SELECT ){
+ rc = sqlite3_prepare_v2(db,
+ "SELECT 1 FROM wordcount WHERE word=?1",
+ -1, &pSelect, 0);
+ if( rc ) fatal_error("Could not prepare the SELECT statement: %s\n",
+ sqlite3_errmsg(db));
+ rc = sqlite3_prepare_v2(db,
+ "INSERT INTO wordcount(word,cnt) VALUES(?1,1)",
+ -1, &pInsert, 0);
+ if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_SELECT || iMode==MODE_UPDATE || iMode==MODE_INSERT ){
+ rc = sqlite3_prepare_v2(db,
+ "UPDATE wordcount SET cnt=cnt+1 WHERE word=?1",
+ -1, &pUpdate, 0);
+ if( rc ) fatal_error("Could not prepare the UPDATE statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_INSERT ){
+ rc = sqlite3_prepare_v2(db,
+ "INSERT OR IGNORE INTO wordcount(word,cnt) VALUES(?1,1)",
+ -1, &pInsert, 0);
+ if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_UPDATE ){
+ rc = sqlite3_prepare_v2(db,
+ "INSERT OR IGNORE INTO wordcount(word,cnt) VALUES(?1,0)",
+ -1, &pInsert, 0);
+ if( rc ) fatal_error("Could not prepare the INSERT statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_REPLACE ){
+ rc = sqlite3_prepare_v2(db,
+ "REPLACE INTO wordcount(word,cnt)"
+ "VALUES(?1,coalesce((SELECT cnt FROM wordcount WHERE word=?1),0)+1)",
+ -1, &pInsert, 0);
+ if( rc ) fatal_error("Could not prepare the REPLACE statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+ if( iMode==MODE_DELETE ){
+ rc = sqlite3_prepare_v2(db,
+ "DELETE FROM wordcount WHERE word=?1",
+ -1, &pDelete, 0);
+ if( rc ) fatal_error("Could not prepare the DELETE statement: %s\n",
+ sqlite3_errmsg(db));
+ }
+
+ /* Process the input file */
+ while( fgets(zInput, sizeof(zInput), in) ){
+ for(i=0; zInput[i]; i++){
+ if( !isalpha(zInput[i]) ) continue;
+ for(j=i+1; isalpha(zInput[j]); j++){}
+
+ /* Found a new word at zInput[i] that is j-i bytes long.
+ ** Process it into the wordcount table. */
+ if( iMode==MODE_DELETE ){
+ sqlite3_bind_text(pDelete, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pDelete)!=SQLITE_DONE ){
+ fatal_error("DELETE failed: %s\n", sqlite3_errmsg(db));
+ }
+ sqlite3_reset(pDelete);
+ }else if( iMode==MODE_SELECT ){
+ sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC);
+ rc = sqlite3_step(pSelect);
+ sqlite3_reset(pSelect);
+ if( rc==SQLITE_ROW ){
+ sqlite3_bind_text(pUpdate, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pUpdate)!=SQLITE_DONE ){
+ fatal_error("UPDATE failed: %s\n", sqlite3_errmsg(db));
+ }
+ sqlite3_reset(pUpdate);
+ }else if( rc==SQLITE_DONE ){
+ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pInsert)!=SQLITE_DONE ){
+ fatal_error("Insert failed: %s\n", sqlite3_errmsg(db));
+ }
+ sqlite3_reset(pInsert);
+ }else{
+ fatal_error("SELECT failed: %s\n", sqlite3_errmsg(db));
+ }
+ }else if( iMode==MODE_QUERY ){
+ sqlite3_bind_text(pSelect, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pSelect)==SQLITE_ROW ){
+ sumCnt += sqlite3_column_int64(pSelect, 0);
+ }
+ sqlite3_reset(pSelect);
+ }else{
+ sqlite3_bind_text(pInsert, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pInsert)!=SQLITE_DONE ){
+ fatal_error("INSERT failed: %s\n", sqlite3_errmsg(db));
+ }
+ sqlite3_reset(pInsert);
+ if( iMode==MODE_UPDATE
+ || (iMode==MODE_INSERT && sqlite3_changes(db)==0)
+ ){
+ sqlite3_bind_text(pUpdate, 1, zInput+i, j-i, SQLITE_STATIC);
+ if( sqlite3_step(pUpdate)!=SQLITE_DONE ){
+ fatal_error("UPDATE failed: %s\n", sqlite3_errmsg(db));
+ }
+ sqlite3_reset(pUpdate);
+ }
+ }
+ i = j-1;
+
+ /* Increment the operation counter. Do a COMMIT if it is time. */
+ nOp++;
+ if( commitInterval>0 && (nOp%commitInterval)==0 ){
+ sqlite3_exec(db, "COMMIT; BEGIN IMMEDIATE", 0, 0, 0);
+ }
+ }
+ }
+ sqlite3_exec(db, "COMMIT", 0, 0, 0);
+ if( zFileToRead ) fclose(in);
+ sqlite3_finalize(pInsert);
+ sqlite3_finalize(pUpdate);
+ sqlite3_finalize(pSelect);
+ sqlite3_finalize(pDelete);
+
+ if( iMode==MODE_QUERY ){
+ printf("sum of cnt: %lld\n", sumCnt);
+ rc = sqlite3_prepare_v2(db,"SELECT sum(cnt*cnt) FROM wordcount", -1,
+ &pSelect, 0);
+ if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){
+ printf("double-check: %lld\n", sqlite3_column_int64(pSelect, 0));
+ }
+ sqlite3_finalize(pSelect);
+ }
+
+
+ if( showTimer ){
+ sqlite3_int64 elapseTime = realTime() - startTime;
+ fprintf(stderr, "%3d.%03d wordcount", (int)(elapseTime/1000),
+ (int)(elapseTime%1000));
+ for(i=1; i<argc; i++) if( i!=showTimer ) fprintf(stderr, " %s", argv[i]);
+ fprintf(stderr, "\n");
+ }
+
+ if( showSummary ){
+ sqlite3_create_function(db, "checksum", -1, SQLITE_UTF8, 0,
+ 0, checksumStep, checksumFinalize);
+ sqlite3_exec(db,
+ "SELECT 'count(*): ', count(*) FROM wordcount;\n"
+ "SELECT 'sum(cnt): ', sum(cnt) FROM wordcount;\n"
+ "SELECT 'max(cnt): ', max(cnt) FROM wordcount;\n"
+ "SELECT 'avg(cnt): ', avg(cnt) FROM wordcount;\n"
+ "SELECT 'sum(cnt=1):', sum(cnt=1) FROM wordcount;\n"
+ "SELECT 'top 10: ', group_concat(word, ', ') FROM "
+ "(SELECT word FROM wordcount ORDER BY cnt DESC, word LIMIT 10);\n"
+ "SELECT 'checksum: ', checksum(word, cnt) FROM "
+ "(SELECT word, cnt FROM wordcount ORDER BY word);\n"
+ "PRAGMA integrity_check;\n",
+ printResult, 0, 0);
+ }
+
+ /* Database connection statistics printed after both prepared statements
+ ** have been finalized */
+ if( showStats ){
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, 0);
+ printf("-- Lookaside Slots Used: %d (max %d)\n", iCur,iHiwtr);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, 0);
+ printf("-- Successful lookasides: %d\n", iHiwtr);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur,&iHiwtr,0);
+ printf("-- Lookaside size faults: %d\n", iHiwtr);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur,&iHiwtr,0);
+ printf("-- Lookaside OOM faults: %d\n", iHiwtr);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, 0);
+ printf("-- Pager Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
+ printf("-- Page cache hits: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
+ printf("-- Page cache misses: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
+ printf("-- Page cache writes: %d\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, 0);
+ printf("-- Schema Heap Usage: %d bytes\n", iCur);
+ sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, 0);
+ printf("-- Statement Heap Usage: %d bytes\n", iCur);
+ }
+
+ sqlite3_close(db);
+
+ /* Global memory usage statistics printed after the database connection
+ ** has closed. Memory usage should be zero at this point. */
+ if( showStats ){
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, 0);
+ printf("-- Memory Used (bytes): %d (max %d)\n", iCur,iHiwtr);
+ sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, 0);
+ printf("-- Outstanding Allocations: %d (max %d)\n", iCur,iHiwtr);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, 0);
+ printf("-- Pcache Overflow Bytes: %d (max %d)\n", iCur,iHiwtr);
+ sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, 0);
+ printf("-- Scratch Overflow Bytes: %d (max %d)\n", iCur,iHiwtr);
+ sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, 0);
+ printf("-- Largest Allocation: %d bytes\n",iHiwtr);
+ sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, 0);
+ printf("-- Largest Pcache Allocation: %d bytes\n",iHiwtr);
+ sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, 0);
+ printf("-- Largest Scratch Allocation: %d bytes\n", iHiwtr);
+ }
+ return 0;
+}
diff --git a/test/zerodamage.test b/test/zerodamage.test
index de5088b..dccaba8 100644
--- a/test/zerodamage.test
+++ b/test/zerodamage.test
@@ -115,7 +115,7 @@ ifcapable wal {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
file size test.db-wal
- } {8416}
+ } {16800}
}
finish_test
diff --git a/tool/build-all-msvc.bat b/tool/build-all-msvc.bat
index 758036f..1fb61d4 100755
--- a/tool/build-all-msvc.bat
+++ b/tool/build-all-msvc.bat
@@ -147,6 +147,17 @@ IF NOT DEFINED CONFIGURATIONS (
%_VECHO% Configurations = '%CONFIGURATIONS%'
REM
+REM NOTE: If the command used to invoke NMAKE is not already set, use the
+REM default.
+REM
+IF NOT DEFINED NMAKE_CMD (
+ SET NMAKE_CMD=nmake -B -f Makefile.msc
+)
+
+%_VECHO% NmakeCmd = '%NMAKE_CMD%'
+%_VECHO% NmakeArgs = '%NMAKE_ARGS%'
+
+REM
REM NOTE: Setup environment variables to translate between the MSVC platform
REM names and the names to be used for the platform-specific binary
REM directories.
@@ -203,8 +214,8 @@ SET TOOLPATH=%gawk.exe_PATH%;%tclsh85.exe_PATH%
%_VECHO% ToolPath = '%TOOLPATH%'
REM
-REM NOTE: Check for MSVC 2012 because the Windows SDK directory handling is
-REM slightly different for that version.
+REM NOTE: Check for MSVC 2012/2013 because the Windows SDK directory handling
+REM is slightly different for those versions.
REM
IF "%VisualStudioVersion%" == "11.0" (
REM
@@ -214,6 +225,14 @@ IF "%VisualStudioVersion%" == "11.0" (
IF NOT DEFINED NSDKLIBPATH (
SET SET_NSDKLIBPATH=1
)
+) ELSE IF "%VisualStudioVersion%" == "12.0" (
+ REM
+ REM NOTE: If the Windows SDK library path has already been set, do not set
+ REM it to something else later on.
+ REM
+ IF NOT DEFINED NSDKLIBPATH (
+ SET SET_NSDKLIBPATH=1
+ )
) ELSE (
CALL :fn_UnsetVariable SET_NSDKLIBPATH
)
@@ -230,6 +249,7 @@ GOTO set_vcvarsall_done
:set_vcvarsall_phone
SET VCVARSALL=%VCINSTALLDIR%\WPSDK\WP80\vcvarsphoneall.bat
:set_vcvarsall_done
+SET VCVARSALL=%VCVARSALL:\\=\%
REM
REM NOTE: This is the outer loop. There should be exactly one iteration per
@@ -244,7 +264,7 @@ FOR %%P IN (%PLATFORMS%) DO (
CALL :fn_CopyVariable %%P_NAME PLATFORMNAME
REM
- REM NOTE: This is the inner loop. There should be exactly one iteration.
+ REM NOTE: This is the second loop. There should be exactly one iteration.
REM This loop is necessary because the PlatformName environment
REM variable was set above and that value is needed by some of the
REM commands contained in the inner loop. If these commands were
@@ -257,9 +277,11 @@ FOR %%P IN (%PLATFORMS%) DO (
REM and/or Visual Studio. This block may need to be updated in the
REM future to account for additional environment variables.
REM
+ CALL :fn_UnsetVariable CommandPromptType
CALL :fn_UnsetVariable DevEnvDir
CALL :fn_UnsetVariable ExtensionSdkDir
CALL :fn_UnsetVariable Framework35Version
+ CALL :fn_UnsetVariable Framework40Version
CALL :fn_UnsetVariable FrameworkDir
CALL :fn_UnsetVariable FrameworkDir32
CALL :fn_UnsetVariable FrameworkVersion
@@ -275,18 +297,26 @@ FOR %%P IN (%PLATFORMS%) DO (
CALL :fn_UnsetVariable WindowsSdkDir
CALL :fn_UnsetVariable WindowsSdkDir_35
CALL :fn_UnsetVariable WindowsSdkDir_old
+ CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x86
+ CALL :fn_UnsetVariable WindowsSDK_ExecutablePath_x64
REM
REM NOTE: Reset the PATH here to the absolute bare minimum required.
REM
SET PATH=%TOOLPATH%;%SystemRoot%\System32;%SystemRoot%
+ REM
+ REM NOTE: This is the inner loop. There are normally two iterations, one
+ REM for each supported build configuration, e.g. Debug or Retail.
+ REM
FOR %%B IN (%CONFIGURATIONS%) DO (
REM
REM NOTE: When preparing the debug build, set the DEBUG and MEMDEBUG
REM environment variables to be picked up by the MSVC makefile
REM itself.
REM
+ %_AECHO% Building the %%B configuration for platform %%P with name %%D...
+
IF /I "%%B" == "Debug" (
SET DEBUG=2
SET MEMDEBUG=1
@@ -310,6 +340,10 @@ FOR %%P IN (%PLATFORMS%) DO (
REM platform to the platform-specific directory beneath the
REM binary directory.
REM
+ REM 5. Unless prevented from doing so, copy the "sqlite3.pdb"
+ REM symbols file for this platform to the platform-specific
+ REM directory beneath the binary directory.
+ REM
"%ComSpec%" /C (
REM
REM NOTE: Attempt to setup the MSVC environment for this platform.
@@ -337,21 +371,36 @@ FOR %%P IN (%PLATFORMS%) DO (
)
REM
- REM NOTE: When using MSVC 2012, the native SDK path cannot simply use
- REM the "lib" sub-directory beneath the location specified in the
- REM WindowsSdkDir environment variable because that location does
- REM not actually contain the necessary library files for x86.
- REM This must be done for each iteration because it relies upon
- REM the WindowsSdkDir environment variable being set by the batch
- REM file used to setup the MSVC environment.
+ REM NOTE: When using MSVC 2012 and/or 2013, the native SDK path cannot
+ REM simply use the "lib" sub-directory beneath the location
+ REM specified in the WindowsSdkDir environment variable because
+ REM that location does not actually contain the necessary library
+ REM files for x86. This must be done for each iteration because
+ REM it relies upon the WindowsSdkDir environment variable being
+ REM set by the batch file used to setup the MSVC environment.
REM
IF DEFINED SET_NSDKLIBPATH (
+ REM
+ REM NOTE: The Windows Phone SDK has a slightly different directory
+ REM structure and must be handled specially here.
+ REM
IF DEFINED WindowsPhoneKitDir (
CALL :fn_CopyVariable WindowsPhoneKitDir NSDKLIBPATH
CALL :fn_AppendVariable NSDKLIBPATH \lib\x86
) ELSE IF DEFINED WindowsSdkDir (
CALL :fn_CopyVariable WindowsSdkDir NSDKLIBPATH
- CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86
+
+ REM
+ REM NOTE: The Windows 8.1 SDK has a slightly different directory
+ REM naming convention.
+ REM
+ IF DEFINED USE_WINV63_NSDKLIBPATH (
+ CALL :fn_AppendVariable NSDKLIBPATH \lib\winv6.3\um\x86
+ ) ELSE IF "%VisualStudioVersion%" == "12.0" (
+ CALL :fn_AppendVariable NSDKLIBPATH \..\8.0\lib\win8\um\x86
+ ) ELSE (
+ CALL :fn_AppendVariable NSDKLIBPATH \lib\win8\um\x86
+ )
)
)
@@ -362,7 +411,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM file, etc.
REM
IF NOT DEFINED NOCLEAN (
- %__ECHO% nmake -f Makefile.msc clean
+ %__ECHO% %NMAKE_CMD% clean
IF ERRORLEVEL 1 (
ECHO Failed to clean for platform %%P.
@@ -374,7 +423,8 @@ FOR %%P IN (%PLATFORMS%) DO (
REM need to remove the build output for the files we are
REM specifically wanting to build for each platform.
REM
- %__ECHO% DEL /Q sqlite3.dll sqlite3.lib sqlite3.pdb
+ %_AECHO% Cleaning final output files only...
+ %__ECHO% DEL /Q *.lo sqlite3.dll sqlite3.lib sqlite3.pdb
)
REM
@@ -384,7 +434,7 @@ FOR %%P IN (%PLATFORMS%) DO (
REM Also, disable looking for and/or linking to the native Tcl
REM runtime library.
REM
- %__ECHO% nmake -f Makefile.msc sqlite3.dll XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS%
+ %__ECHO% %NMAKE_CMD% sqlite3.dll XCOMPILE=1 USE_NATIVE_LIBPATHS=1 NO_TCL=1 %NMAKE_ARGS%
IF ERRORLEVEL 1 (
ECHO Failed to build %%B "sqlite3.dll" for platform %%P.
@@ -462,9 +512,9 @@ GOTO no_errors
GOTO :EOF
:fn_CopyVariable
- SETLOCAL
IF NOT DEFINED %1 GOTO :EOF
IF "%2" == "" GOTO :EOF
+ SETLOCAL
SET __ECHO_CMD=ECHO %%%1%%
FOR /F "delims=" %%V IN ('%__ECHO_CMD%') DO (
SET VALUE=%%V
diff --git a/tool/fast_vacuum.c b/tool/fast_vacuum.c
new file mode 100644
index 0000000..6a50dcc
--- /dev/null
+++ b/tool/fast_vacuum.c
@@ -0,0 +1,234 @@
+/*
+** 2013-10-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 program implements a high-speed version of the VACUUM command.
+** It repacks an SQLite database to remove as much unused space as
+** possible and to relocate content sequentially in the file.
+**
+** This program runs faster and uses less temporary disk space than the
+** built-in VACUUM command. On the other hand, this program has a number
+** of important restrictions relative to the built-in VACUUM command.
+**
+** (1) The caller must ensure that no other processes are accessing the
+** database file while the vacuum is taking place. The usual SQLite
+** file locking is insufficient for this. The caller must use
+** external means to make sure only this one routine is reading and
+** writing the database.
+**
+** (2) Database reconfiguration such as page size or auto_vacuum changes
+** are not supported by this utility.
+**
+** (3) The database file might be renamed if a power loss or crash
+** occurs at just the wrong moment. Recovery must be prepared to
+** to deal with the possibly changed filename.
+**
+** This program is intended as a *Demonstration Only*. The intent of this
+** program is to provide example code that application developers can use
+** when creating similar functionality in their applications.
+**
+** To compile this program:
+**
+** cc fast_vacuum.c sqlite3.c
+**
+** Add whatever linker options are required. (Example: "-ldl -lpthread").
+** Then to run the program:
+**
+** ./a.out file-to-vacuum
+**
+*/
+#include "sqlite3.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+** Finalize a prepared statement. If an error has occurred, print the
+** error message and exit.
+*/
+static void vacuumFinalize(sqlite3_stmt *pStmt){
+ sqlite3 *db = sqlite3_db_handle(pStmt);
+ int rc = sqlite3_finalize(pStmt);
+ if( rc ){
+ fprintf(stderr, "finalize error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+}
+
+/*
+** Execute zSql on database db. The SQL text is printed to standard
+** output. If an error occurs, print an error message and exit the
+** process.
+*/
+static void execSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ if( !zSql ){
+ fprintf(stderr, "out of memory!\n");
+ exit(1);
+ }
+ printf("%s;\n", zSql);
+ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+ sqlite3_step(pStmt);
+ vacuumFinalize(pStmt);
+}
+
+/*
+** Execute zSql on database db. The zSql statement returns exactly
+** one column. Execute this return value as SQL on the same database.
+**
+** The zSql statement is printed on standard output prior to being
+** run. If any errors occur, an error is printed and the process
+** exits.
+*/
+static void execExecSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ printf("%s;\n", zSql);
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ exit(1);
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ execSql(db, (char*)sqlite3_column_text(pStmt, 0));
+ }
+ vacuumFinalize(pStmt);
+}
+
+
+int main(int argc, char **argv){
+ sqlite3 *db; /* Connection to the database file */
+ int rc; /* Return code from SQLite interface calls */
+ sqlite3_uint64 r; /* A random number */
+ const char *zDbToVacuum; /* Database to be vacuumed */
+ char *zBackupDb; /* Backup copy of the original database */
+ char *zTempDb; /* Temporary database */
+ char *zSql; /* An SQL statement */
+
+ if( argc!=2 ){
+ fprintf(stderr, "Usage: %s DATABASE\n", argv[0]);
+ return 1;
+ }
+
+ /* Identify the database file to be vacuumed and open it.
+ */
+ zDbToVacuum = argv[1];
+ printf("-- open database file \"%s\"\n", zDbToVacuum);
+ rc = sqlite3_open(zDbToVacuum, &db);
+ if( rc ){
+ fprintf(stderr, "%s: %s\n", zDbToVacuum, sqlite3_errstr(rc));
+ return 1;
+ }
+
+ /* Create names for two other files. zTempDb will be a new database
+ ** into which we construct a vacuumed copy of zDbToVacuum. zBackupDb
+ ** will be a new name for zDbToVacuum after it is vacuumed.
+ */
+ sqlite3_randomness(sizeof(r), &r);
+ zTempDb = sqlite3_mprintf("%s-vacuum-%016llx", zDbToVacuum, r);
+ zBackupDb = sqlite3_mprintf("%s-backup-%016llx", zDbToVacuum, r);
+
+ /* Attach the zTempDb database to the database connection.
+ */
+ zSql = sqlite3_mprintf("ATTACH '%q' AS vacuum_db;", zTempDb);
+ execSql(db, zSql);
+ sqlite3_free(zSql);
+
+ /* TODO:
+ ** Set the page_size and auto_vacuum mode for zTempDb here, if desired.
+ */
+
+ /* The vacuum will occur inside of a transaction. Set writable_schema
+ ** to ON so that we can directly update the sqlite_master table in the
+ ** zTempDb database.
+ */
+ execSql(db, "PRAGMA writable_schema=ON");
+ execSql(db, "BEGIN");
+
+
+ /* Query the schema of the main database. Create a mirror schema
+ ** in the temporary database.
+ */
+ execExecSql(db,
+ "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
+ " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
+ " AND rootpage>0"
+ );
+ execExecSql(db,
+ "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
+ " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %'"
+ );
+ execExecSql(db,
+ "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
+ " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"
+ );
+
+ /* Loop through the tables in the main database. For each, do
+ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
+ ** the contents to the temporary database.
+ */
+ execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) "
+ "FROM main.sqlite_master "
+ "WHERE type = 'table' AND name!='sqlite_sequence' "
+ " AND rootpage>0"
+ );
+
+ /* Copy over the sequence table
+ */
+ execExecSql(db,
+ "SELECT 'DELETE FROM vacuum_db.' || quote(name) "
+ "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence'"
+ );
+ execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
+ "|| ' SELECT * FROM main.' || quote(name) "
+ "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence'"
+ );
+
+ /* Copy the triggers, views, and virtual tables from the main database
+ ** over to the temporary database. None of these objects has any
+ ** associated storage, so all we have to do is copy their entries
+ ** from the SQLITE_MASTER table.
+ */
+ execSql(db,
+ "INSERT INTO vacuum_db.sqlite_master "
+ " SELECT type, name, tbl_name, rootpage, sql"
+ " FROM main.sqlite_master"
+ " WHERE type='view' OR type='trigger'"
+ " OR (type='table' AND rootpage=0)"
+ );
+
+ /* Commit the transaction and close the database
+ */
+ execSql(db, "COMMIT");
+ printf("-- close database\n");
+ sqlite3_close(db);
+
+
+ /* At this point, zDbToVacuum is unchanged. zTempDb contains a
+ ** vacuumed copy of zDbToVacuum. Rearrange filenames so that
+ ** zTempDb becomes thenew zDbToVacuum.
+ */
+ printf("-- rename \"%s\" to \"%s\"\n", zDbToVacuum, zBackupDb);
+ rename(zDbToVacuum, zBackupDb);
+ printf("-- rename \"%s\" to \"%s\"\n", zTempDb, zDbToVacuum);
+ rename(zTempDb, zDbToVacuum);
+
+ /* Release allocated memory */
+ sqlite3_free(zTempDb);
+ sqlite3_free(zBackupDb);
+ return 0;
+}
diff --git a/tool/lemon.c b/tool/lemon.c
index f63e76f..85e94f7 100644
--- a/tool/lemon.c
+++ b/tool/lemon.c
@@ -50,6 +50,107 @@ static char *msort(char*,char**,int(*)(const char*,const char*));
*/
#define lemonStrlen(X) ((int)strlen(X))
+/*
+** Compilers are starting to complain about the use of sprintf() and strcpy(),
+** saying they are unsafe. So we define our own versions of those routines too.
+**
+** There are three routines here: lemon_sprintf(), lemon_vsprintf(), and
+** lemon_addtext(). The first two are replacements for sprintf() and vsprintf().
+** The third is a helper routine for vsnprintf() that adds texts to the end of a
+** buffer, making sure the buffer is always zero-terminated.
+**
+** The string formatter is a minimal subset of stdlib sprintf() supporting only
+** a few simply conversions:
+**
+** %d
+** %s
+** %.*s
+**
+*/
+static void lemon_addtext(
+ char *zBuf, /* The buffer to which text is added */
+ int *pnUsed, /* Slots of the buffer used so far */
+ const char *zIn, /* Text to add */
+ int nIn, /* Bytes of text to add. -1 to use strlen() */
+ int iWidth /* Field width. Negative to left justify */
+){
+ if( nIn<0 ) for(nIn=0; zIn[nIn]; nIn++){}
+ while( iWidth>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth--; }
+ if( nIn==0 ) return;
+ memcpy(&zBuf[*pnUsed], zIn, nIn);
+ *pnUsed += nIn;
+ while( (-iWidth)>nIn ){ zBuf[(*pnUsed)++] = ' '; iWidth++; }
+ zBuf[*pnUsed] = 0;
+}
+static int lemon_vsprintf(char *str, const char *zFormat, va_list ap){
+ int i, j, k, c;
+ int nUsed = 0;
+ const char *z;
+ char zTemp[50];
+ str[0] = 0;
+ for(i=j=0; (c = zFormat[i])!=0; i++){
+ if( c=='%' ){
+ int iWidth = 0;
+ lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0);
+ c = zFormat[++i];
+ if( isdigit(c) || (c=='-' && isdigit(zFormat[i+1])) ){
+ if( c=='-' ) i++;
+ while( isdigit(zFormat[i]) ) iWidth = iWidth*10 + zFormat[i++] - '0';
+ if( c=='-' ) iWidth = -iWidth;
+ c = zFormat[i];
+ }
+ if( c=='d' ){
+ int v = va_arg(ap, int);
+ if( v<0 ){
+ lemon_addtext(str, &nUsed, "-", 1, iWidth);
+ v = -v;
+ }else if( v==0 ){
+ lemon_addtext(str, &nUsed, "0", 1, iWidth);
+ }
+ k = 0;
+ while( v>0 ){
+ k++;
+ zTemp[sizeof(zTemp)-k] = (v%10) + '0';
+ v /= 10;
+ }
+ lemon_addtext(str, &nUsed, &zTemp[sizeof(zTemp)-k], k, iWidth);
+ }else if( c=='s' ){
+ z = va_arg(ap, const char*);
+ lemon_addtext(str, &nUsed, z, -1, iWidth);
+ }else if( c=='.' && memcmp(&zFormat[i], ".*s", 3)==0 ){
+ i += 2;
+ k = va_arg(ap, int);
+ z = va_arg(ap, const char*);
+ lemon_addtext(str, &nUsed, z, k, iWidth);
+ }else if( c=='%' ){
+ lemon_addtext(str, &nUsed, "%", 1, 0);
+ }else{
+ fprintf(stderr, "illegal format\n");
+ exit(1);
+ }
+ j = i+1;
+ }
+ }
+ lemon_addtext(str, &nUsed, &zFormat[j], i-j, 0);
+ return nUsed;
+}
+static int lemon_sprintf(char *str, const char *format, ...){
+ va_list ap;
+ int rc;
+ va_start(ap, format);
+ rc = lemon_vsprintf(str, format, ap);
+ va_end(ap);
+ return rc;
+}
+static void lemon_strcpy(char *dest, const char *src){
+ while( (*(dest++) = *(src++))!=0 ){}
+}
+static void lemon_strcat(char *dest, const char *src){
+ while( *dest ) dest++;
+ lemon_strcpy(dest, src);
+}
+
+
/* a few forward declarations... */
struct rule;
struct lemon;
@@ -1082,8 +1183,7 @@ static int resolve_conflict(
apx->type = SH_RESOLVED;
}else{
assert( spx->prec==spy->prec && spx->assoc==NONE );
- apy->type = SRCONFLICT;
- errcnt++;
+ apx->type = ERROR;
}
}else if( apx->type==REDUCE && apy->type==REDUCE ){
spx = apx->x.rp->precsym;
@@ -1367,7 +1467,7 @@ static void handle_D_option(char *z){
fprintf(stderr,"out of memory\n");
exit(1);
}
- strcpy(*paz, z);
+ lemon_strcpy(*paz, z);
for(z=*paz; *z && *z!='='; z++){}
*z = 0;
}
@@ -1378,7 +1478,7 @@ static void handle_T_option(char *z){
if( user_templatename==0 ){
memory_error();
}
- strcpy(user_templatename, z);
+ lemon_strcpy(user_templatename, z);
}
/* The main program. Parse the command line and do it... */
@@ -1447,12 +1547,15 @@ int main(int argc, char **argv)
}
/* Count and index the symbols of the grammar */
- lem.nsymbol = Symbol_count();
Symbol_new("{default}");
+ lem.nsymbol = Symbol_count();
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*), Symbolcmpp);
- for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i;
+ for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i;
+ qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp);
+ for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i;
+ while( lem.symbols[i-1]->type==MULTITERMINAL ){ i--; }
+ assert( strcmp(lem.symbols[i-1]->name,"{default}")==0 );
+ lem.nsymbol = i - 1;
for(i=1; isupper(lem.symbols[i]->name[0]); i++);
lem.nterminal = i;
@@ -1940,7 +2043,9 @@ enum e_state {
WAITING_FOR_DESTRUCTOR_SYMBOL,
WAITING_FOR_DATATYPE_SYMBOL,
WAITING_FOR_FALLBACK_ID,
- WAITING_FOR_WILDCARD_ID
+ WAITING_FOR_WILDCARD_ID,
+ WAITING_FOR_CLASS_ID,
+ WAITING_FOR_CLASS_TOKEN
};
struct pstate {
char *filename; /* Name of the input file */
@@ -1950,6 +2055,7 @@ struct pstate {
struct lemon *gp; /* Global state vector */
enum e_state state; /* The state of the parser */
struct symbol *fallback; /* The fallback token */
+ struct symbol *tkclass; /* Token class symbol */
struct symbol *lhs; /* Left-hand side of current rule */
const char *lhsalias; /* Alias for the LHS */
int nrhs; /* Number of right-hand side symbols seen */
@@ -2254,6 +2360,8 @@ to follow the previous rule.");
psp->state = WAITING_FOR_FALLBACK_ID;
}else if( strcmp(x,"wildcard")==0 ){
psp->state = WAITING_FOR_WILDCARD_ID;
+ }else if( strcmp(x,"token_class")==0 ){
+ psp->state = WAITING_FOR_CLASS_ID;
}else{
ErrorMsg(psp->filename,psp->tokenlineno,
"Unknown declaration keyword: \"%%%s\".",x);
@@ -2347,7 +2455,7 @@ to follow the previous rule.");
for(z=psp->filename, nBack=0; *z; z++){
if( *z=='\\' ) nBack++;
}
- sprintf(zLine, "#line %d ", psp->tokenlineno);
+ lemon_sprintf(zLine, "#line %d ", psp->tokenlineno);
nLine = lemonStrlen(zLine);
n += nLine + lemonStrlen(psp->filename) + nBack;
}
@@ -2422,6 +2530,40 @@ to follow the previous rule.");
}
}
break;
+ case WAITING_FOR_CLASS_ID:
+ if( !islower(x[0]) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%token_class must be followed by an identifier: ", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else if( Symbol_find(x) ){
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "Symbol \"%s\" already used", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }else{
+ psp->tkclass = Symbol_new(x);
+ psp->tkclass->type = MULTITERMINAL;
+ psp->state = WAITING_FOR_CLASS_TOKEN;
+ }
+ break;
+ case WAITING_FOR_CLASS_TOKEN:
+ if( x[0]=='.' ){
+ psp->state = WAITING_FOR_DECL_OR_RULE;
+ }else if( isupper(x[0]) || ((x[0]=='|' || x[0]=='/') && isupper(x[1])) ){
+ struct symbol *msp = psp->tkclass;
+ msp->nsubsym++;
+ msp->subsym = (struct symbol **) realloc(msp->subsym,
+ sizeof(struct symbol*)*msp->nsubsym);
+ if( !isupper(x[0]) ) x++;
+ msp->subsym[msp->nsubsym-1] = Symbol_new(x);
+ }else{
+ ErrorMsg(psp->filename, psp->tokenlineno,
+ "%%token_class argument \"%s\" should be a token", x);
+ psp->errorcnt++;
+ psp->state = RESYNC_AFTER_DECL_ERROR;
+ }
+ break;
case RESYNC_AFTER_RULE_ERROR:
/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE;
** break; */
@@ -2516,9 +2658,8 @@ void Parse(struct lemon *gp)
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);
+ if( filesize>100000000 || filebuf==0 ){
+ ErrorMsg(ps.filename,0,"Input file too large.");
gp->errorcnt++;
fclose(fp);
return;
@@ -2716,10 +2857,10 @@ PRIVATE char *file_makename(struct lemon *lemp, const char *suffix)
fprintf(stderr,"Can't allocate space for a filename.\n");
exit(1);
}
- strcpy(name,lemp->filename);
+ lemon_strcpy(name,lemp->filename);
cp = strrchr(name,'.');
if( cp ) *cp = 0;
- strcat(name,suffix);
+ lemon_strcat(name,suffix);
return name;
}
@@ -2776,11 +2917,13 @@ void Reprint(struct lemon *lemp)
printf(" ::=");
for(i=0; i<rp->nrhs; i++){
sp = rp->rhs[i];
- printf(" %s", sp->name);
if( sp->type==MULTITERMINAL ){
+ printf(" %s", sp->subsym[0]->name);
for(j=1; j<sp->nsubsym; j++){
printf("|%s", sp->subsym[j]->name);
}
+ }else{
+ printf(" %s", sp->name);
}
/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */
}
@@ -2802,11 +2945,13 @@ void ConfigPrint(FILE *fp, struct config *cfp)
if( i==cfp->dot ) fprintf(fp," *");
if( i==rp->nrhs ) break;
sp = rp->rhs[i];
- fprintf(fp," %s", sp->name);
if( sp->type==MULTITERMINAL ){
+ fprintf(fp," %s", sp->subsym[0]->name);
for(j=1; j<sp->nsubsym; j++){
fprintf(fp,"|%s",sp->subsym[j]->name);
}
+ }else{
+ fprintf(fp," %s", sp->name);
}
}
}
@@ -2916,7 +3061,7 @@ void ReportOutput(struct lemon *lemp)
while( cfp ){
char buf[20];
if( cfp->dot==cfp->rp->nrhs ){
- sprintf(buf,"(%d)",cfp->rp->index);
+ lemon_sprintf(buf,"(%d)",cfp->rp->index);
fprintf(fp," %5s ",buf);
}else{
fprintf(fp," ");
@@ -2981,7 +3126,7 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
c = *cp;
*cp = 0;
path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 );
- if( path ) sprintf(path,"%s/%s",argv0,name);
+ if( path ) lemon_sprintf(path,"%s/%s",argv0,name);
*cp = c;
}else{
pathlist = getenv("PATH");
@@ -2990,13 +3135,13 @@ PRIVATE char *pathsearch(char *argv0, char *name, int modemask)
path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 );
if( (pathbuf != 0) && (path!=0) ){
pathbufptr = pathbuf;
- strcpy(pathbuf, pathlist);
+ lemon_strcpy(pathbuf, pathlist);
while( *pathbuf ){
cp = strchr(pathbuf,':');
if( cp==0 ) cp = &pathbuf[lemonStrlen(pathbuf)];
c = *cp;
*cp = 0;
- sprintf(path,"%s/%s",pathbuf,name);
+ lemon_sprintf(path,"%s/%s",pathbuf,name);
*cp = c;
if( c==0 ) pathbuf[0] = 0;
else pathbuf = &cp[1];
@@ -3087,9 +3232,9 @@ PRIVATE FILE *tplt_open(struct lemon *lemp)
cp = strrchr(lemp->filename,'.');
if( cp ){
- sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
+ lemon_sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename);
}else{
- sprintf(buf,"%s.lt",lemp->filename);
+ lemon_sprintf(buf,"%s.lt",lemp->filename);
}
if( access(buf,004)==0 ){
tpltname = buf;
@@ -3240,9 +3385,9 @@ PRIVATE char *append_str(const char *zText, int n, int p1, int p2){
while( n-- > 0 ){
c = *(zText++);
if( c=='%' && n>0 && zText[0]=='d' ){
- sprintf(zInt, "%d", p1);
+ lemon_sprintf(zInt, "%d", p1);
p1 = p2;
- strcpy(&z[used], zInt);
+ lemon_strcpy(&z[used], zInt);
used += lemonStrlen(&z[used]);
zText++;
n--;
@@ -3391,7 +3536,7 @@ void print_stack_union(
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 */
+ unsigned hash; /* For hashing the name of a type */
const char *name; /* Name of the parser */
/* Allocate and initialize types[] and allocate stddt[] */
@@ -3458,7 +3603,7 @@ void print_stack_union(
break;
}
hash++;
- if( hash>=arraysize ) hash = 0;
+ if( hash>=(unsigned)arraysize ) hash = 0;
}
if( types[hash]==0 ){
sp->dtnum = hash + 1;
@@ -3467,7 +3612,7 @@ void print_stack_union(
fprintf(stderr,"Out of memory.\n");
exit(1);
}
- strcpy(types[hash],stddt);
+ lemon_strcpy(types[hash],stddt);
}
}
@@ -3553,9 +3698,11 @@ static void writeRuleText(FILE *out, struct rule *rp){
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 ){
+ if( sp->type!=MULTITERMINAL ){
+ fprintf(out," %s", sp->name);
+ }else{
int k;
+ fprintf(out," %s", sp->subsym[0]->name);
for(k=1; k<sp->nsubsym; k++){
fprintf(out,"|%s",sp->subsym[k]->name);
}
@@ -3856,7 +4003,7 @@ void ReportTable(
/* Generate a table containing the symbolic name of every symbol
*/
for(i=0; i<lemp->nsymbol; i++){
- sprintf(line,"\"%s\",",lemp->symbols[i]->name);
+ lemon_sprintf(line,"\"%s\",",lemp->symbols[i]->name);
fprintf(out," %-15s",line);
if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; }
}
@@ -4023,7 +4170,8 @@ void ReportHeader(struct lemon *lemp)
if( in ){
int nextChar;
for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){
- sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i);
+ lemon_sprintf(pattern,"#define %s%-30s %3d\n",
+ prefix,lemp->symbols[i]->name,i);
if( strcmp(line,pattern) ) break;
}
nextChar = fgetc(in);
@@ -4036,7 +4184,7 @@ void ReportHeader(struct lemon *lemp)
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);
+ fprintf(out,"#define %s%-30s %3d\n",prefix,lemp->symbols[i]->name,i);
}
fclose(out);
}
@@ -4234,10 +4382,10 @@ int SetUnion(char *s1, char *s2)
** Code for processing tables in the LEMON parser generator.
*/
-PRIVATE int strhash(const char *x)
+PRIVATE unsigned strhash(const char *x)
{
- int h = 0;
- while( *x) h = h*13 + *(x++);
+ unsigned h = 0;
+ while( *x ) h = h*13 + *(x++);
return h;
}
@@ -4253,7 +4401,7 @@ const char *Strsafe(const char *y)
if( y==0 ) return 0;
z = Strsafe_find(y);
if( z==0 && (cpy=(char *)malloc( lemonStrlen(y)+1 ))!=0 ){
- strcpy(cpy,y);
+ lemon_strcpy(cpy,y);
z = cpy;
Strsafe_insert(z);
}
@@ -4292,8 +4440,7 @@ void Strsafe_init(){
if( x1a ){
x1a->size = 1024;
x1a->count = 0;
- x1a->tbl = (x1node*)malloc(
- (sizeof(x1node) + sizeof(x1node*))*1024 );
+ x1a->tbl = (x1node*)calloc(1024, sizeof(x1node) + sizeof(x1node*));
if( x1a->tbl==0 ){
free(x1a);
x1a = 0;
@@ -4309,8 +4456,8 @@ void Strsafe_init(){
int Strsafe_insert(const char *data)
{
x1node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x1a==0 ) return 0;
ph = strhash(data);
@@ -4330,8 +4477,7 @@ int Strsafe_insert(const char *data)
struct s_x1 array;
array.size = size = x1a->size*2;
array.count = x1a->count;
- array.tbl = (x1node*)malloc(
- (sizeof(x1node) + sizeof(x1node*))*size );
+ array.tbl = (x1node*)calloc(size, sizeof(x1node) + sizeof(x1node*));
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;
@@ -4364,7 +4510,7 @@ int Strsafe_insert(const char *data)
** if no such key. */
const char *Strsafe_find(const char *key)
{
- int h;
+ unsigned h;
x1node *np;
if( x1a==0 ) return 0;
@@ -4406,11 +4552,15 @@ struct symbol *Symbol_new(const char *x)
return sp;
}
-/* Compare two symbols for working purposes
+/* Compare two symbols for sorting purposes. Return negative,
+** zero, or positive if a is less then, equal to, or greater
+** than b.
**
** 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.
+** (non-terminals). And MULTITERMINAL symbols (created using the
+** %token_class directive) must sort at the very end. 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
@@ -4418,12 +4568,11 @@ struct symbol *Symbol_new(const char *x)
*/
int Symbolcmpp(const void *_a, const void *_b)
{
- const struct symbol **a = (const struct symbol **) _a;
- const struct symbol **b = (const struct symbol **) _b;
- int i1 = (**a).index + 10000000*((**a).name[0]>'Z');
- int i2 = (**b).index + 10000000*((**b).name[0]>'Z');
- assert( i1!=i2 || strcmp((**a).name,(**b).name)==0 );
- return i1-i2;
+ const struct symbol *a = *(const struct symbol **) _a;
+ const struct symbol *b = *(const struct symbol **) _b;
+ int i1 = a->type==MULTITERMINAL ? 3 : a->name[0]>'Z' ? 2 : 1;
+ int i2 = b->type==MULTITERMINAL ? 3 : b->name[0]>'Z' ? 2 : 1;
+ return i1==i2 ? a->index - b->index : i1 - i2;
}
/* There is one instance of the following structure for each
@@ -4458,8 +4607,7 @@ void Symbol_init(){
if( x2a ){
x2a->size = 128;
x2a->count = 0;
- x2a->tbl = (x2node*)malloc(
- (sizeof(x2node) + sizeof(x2node*))*128 );
+ x2a->tbl = (x2node*)calloc(128, sizeof(x2node) + sizeof(x2node*));
if( x2a->tbl==0 ){
free(x2a);
x2a = 0;
@@ -4475,8 +4623,8 @@ void Symbol_init(){
int Symbol_insert(struct symbol *data, const char *key)
{
x2node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x2a==0 ) return 0;
ph = strhash(key);
@@ -4496,8 +4644,7 @@ int Symbol_insert(struct symbol *data, const char *key)
struct s_x2 array;
array.size = size = x2a->size*2;
array.count = x2a->count;
- array.tbl = (x2node*)malloc(
- (sizeof(x2node) + sizeof(x2node*))*size );
+ array.tbl = (x2node*)calloc(size, sizeof(x2node) + sizeof(x2node*));
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;
@@ -4532,7 +4679,7 @@ int Symbol_insert(struct symbol *data, const char *key)
** if no such key. */
struct symbol *Symbol_find(const char *key)
{
- int h;
+ unsigned h;
x2node *np;
if( x2a==0 ) return 0;
@@ -4606,9 +4753,9 @@ PRIVATE int statecmp(struct config *a, struct config *b)
}
/* Hash a state */
-PRIVATE int statehash(struct config *a)
+PRIVATE unsigned statehash(struct config *a)
{
- int h=0;
+ unsigned h=0;
while( a ){
h = h*571 + a->rp->index*37 + a->dot;
a = a->bp;
@@ -4657,8 +4804,7 @@ void State_init(){
if( x3a ){
x3a->size = 128;
x3a->count = 0;
- x3a->tbl = (x3node*)malloc(
- (sizeof(x3node) + sizeof(x3node*))*128 );
+ x3a->tbl = (x3node*)calloc(128, sizeof(x3node) + sizeof(x3node*));
if( x3a->tbl==0 ){
free(x3a);
x3a = 0;
@@ -4674,8 +4820,8 @@ void State_init(){
int State_insert(struct state *data, struct config *key)
{
x3node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x3a==0 ) return 0;
ph = statehash(key);
@@ -4695,8 +4841,7 @@ int State_insert(struct state *data, struct config *key)
struct s_x3 array;
array.size = size = x3a->size*2;
array.count = x3a->count;
- array.tbl = (x3node*)malloc(
- (sizeof(x3node) + sizeof(x3node*))*size );
+ array.tbl = (x3node*)calloc(size, sizeof(x3node) + sizeof(x3node*));
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;
@@ -4731,7 +4876,7 @@ int State_insert(struct state *data, struct config *key)
** if no such key. */
struct state *State_find(struct config *key)
{
- int h;
+ unsigned h;
x3node *np;
if( x3a==0 ) return 0;
@@ -4753,7 +4898,7 @@ struct state **State_arrayof()
int i,size;
if( x3a==0 ) return 0;
size = x3a->count;
- array = (struct state **)malloc( sizeof(struct state *)*size );
+ array = (struct state **)calloc(size, sizeof(struct state *));
if( array ){
for(i=0; i<size; i++) array[i] = x3a->tbl[i].data;
}
@@ -4761,9 +4906,9 @@ struct state **State_arrayof()
}
/* Hash a configuration */
-PRIVATE int confighash(struct config *a)
+PRIVATE unsigned confighash(struct config *a)
{
- int h=0;
+ unsigned h=0;
h = h*571 + a->rp->index*37 + a->dot;
return h;
}
@@ -4799,8 +4944,7 @@ void Configtable_init(){
if( x4a ){
x4a->size = 64;
x4a->count = 0;
- x4a->tbl = (x4node*)malloc(
- (sizeof(x4node) + sizeof(x4node*))*64 );
+ x4a->tbl = (x4node*)calloc(64, sizeof(x4node) + sizeof(x4node*));
if( x4a->tbl==0 ){
free(x4a);
x4a = 0;
@@ -4816,8 +4960,8 @@ void Configtable_init(){
int Configtable_insert(struct config *data)
{
x4node *np;
- int h;
- int ph;
+ unsigned h;
+ unsigned ph;
if( x4a==0 ) return 0;
ph = confighash(data);
@@ -4837,8 +4981,7 @@ int Configtable_insert(struct config *data)
struct s_x4 array;
array.size = size = x4a->size*2;
array.count = x4a->count;
- array.tbl = (x4node*)malloc(
- (sizeof(x4node) + sizeof(x4node*))*size );
+ array.tbl = (x4node*)calloc(size, sizeof(x4node) + sizeof(x4node*));
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;
diff --git a/tool/logest.c b/tool/logest.c
new file mode 100644
index 0000000..347fa68
--- /dev/null
+++ b/tool/logest.c
@@ -0,0 +1,170 @@
+/*
+** 2013-06-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 a simple command-line utility for converting from
+** integers and LogEst values and back again and for doing simple
+** arithmetic operations (multiple and add) on LogEst values.
+**
+** Usage:
+**
+** ./LogEst ARGS
+**
+** See the showHelp() routine for a description of valid arguments.
+** Examples:
+**
+** To convert 123 from LogEst to integer:
+**
+** ./LogEst ^123
+**
+** To convert 123456 from integer to LogEst:
+**
+** ./LogEst 123456
+**
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <assert.h>
+#include <string.h>
+#include "sqlite3.h"
+
+typedef short int LogEst; /* 10 times log2() */
+
+LogEst logEstMultiply(LogEst a, LogEst b){ return a+b; }
+LogEst logEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a<b ){ LogEst t = a; a = b; b = t; }
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+}
+LogEst logEstFromInteger(sqlite3_uint64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; }
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+static sqlite3_uint64 logEstToInt(LogEst x){
+ sqlite3_uint64 n;
+ if( x<10 ) return 1;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+ if( x>=3 ) return (n+8)<<(x-3);
+ return (n+8)>>(3-x);
+}
+static LogEst logEstFromDouble(double x){
+ sqlite3_uint64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=0.0 ) return -32768;
+ if( x<0.01 ) return -logEstFromDouble(1.0/x);
+ if( x<1.0 ) return logEstFromDouble(100.0*x) - 66;
+ if( x<1024.0 ) return logEstFromInteger((sqlite3_uint64)(1024.0*x)) - 100;
+ if( x<=2000000000.0 ) return logEstFromInteger((sqlite3_uint64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+
+int isInteger(const char *z){
+ while( z[0]>='0' && z[0]<='9' ) z++;
+ return z[0]==0;
+}
+
+int isFloat(const char *z){
+ char c;
+ while( ((c=z[0])>='0' && c<='9') || c=='.' || c=='E' || c=='e'
+ || c=='+' || c=='-' ) z++;
+ return z[0]==0;
+}
+
+static void showHelp(const char *zArgv0){
+ printf("Usage: %s ARGS...\n", zArgv0);
+ printf("Arguments:\n"
+ " NUM Convert NUM from integer to LogEst and push onto the stack\n"
+ " ^NUM Interpret NUM as a LogEst and push onto stack\n"
+ " x Multiple the top two elements of the stack\n"
+ " + Add the top two elements of the stack\n"
+ " dup Dupliate the top element on the stack\n"
+ " inv Take the reciprocal of the top of stack. N = 1/N.\n"
+ " log Find the LogEst of the number on top of stack\n"
+ " nlogn Compute NlogN where N is the top of stack\n"
+ );
+ exit(1);
+}
+
+int main(int argc, char **argv){
+ int i;
+ int n = 0;
+ LogEst a[100];
+ for(i=1; i<argc; i++){
+ const char *z = argv[i];
+ if( strcmp(z,"+")==0 ){
+ if( n>=2 ){
+ a[n-2] = logEstAdd(a[n-2],a[n-1]);
+ n--;
+ }
+ }else if( strcmp(z,"x")==0 ){
+ if( n>=2 ){
+ a[n-2] = logEstMultiply(a[n-2],a[n-1]);
+ n--;
+ }
+ }else if( strcmp(z,"dup")==0 ){
+ if( n>0 ){
+ a[n] = a[n-1];
+ n++;
+ }
+ }else if( strcmp(z,"log")==0 ){
+ if( n>0 ) a[n-1] = logEstFromInteger(a[n-1]) - 33;
+ }else if( strcmp(z,"nlogn")==0 ){
+ if( n>0 ) a[n-1] += logEstFromInteger(a[n-1]) - 33;
+ }else if( strcmp(z,"inv")==0 ){
+ if( n>0 ) a[n-1] = -a[n-1];
+ }else if( z[0]=='^' ){
+ a[n++] = atoi(z+1);
+ }else if( isInteger(z) ){
+ a[n++] = logEstFromInteger(atoi(z));
+ }else if( isFloat(z) && z[0]!='-' ){
+ a[n++] = logEstFromDouble(atof(z));
+ }else{
+ showHelp(argv[0]);
+ }
+ }
+ for(i=n-1; i>=0; i--){
+ if( a[i]<-40 ){
+ printf("%5d (%f)\n", a[i], 1.0/(double)logEstToInt(-a[i]));
+ }else if( a[i]<10 ){
+ printf("%5d (%f)\n", a[i], logEstToInt(a[i]+100)/1024.0);
+ }else{
+ sqlite3_uint64 x = logEstToInt(a[i]+100)*100/1024;
+ printf("%5d (%lld.%02lld)\n", a[i], x/100, x%100);
+ }
+ }
+ return 0;
+}
diff --git a/tool/mkautoconfamal.sh b/tool/mkautoconfamal.sh
new file mode 100644
index 0000000..c13f7c9
--- /dev/null
+++ b/tool/mkautoconfamal.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+# This script is used to build the amalgamation autoconf package.
+# It assumes the following:
+#
+# 1. The files "sqlite3.c", "sqlite3.h" and "sqlite3ext.h"
+# are available in the current directory.
+#
+# 2. Variable $TOP is set to the full path of the root directory
+# of the SQLite source tree.
+#
+# 3. There is nothing of value in the ./mkpkg_tmp_dir directory.
+# This is important, as the script executes "rm -rf ./mkpkg_tmp_dir".
+#
+
+
+# Bail out of the script if any command returns a non-zero exit
+# status. Or if the script tries to use an unset variable. These
+# may fail for old /bin/sh interpreters.
+#
+set -e
+set -u
+
+TMPSPACE=./mkpkg_tmp_dir
+VERSION=`cat $TOP/VERSION`
+
+# Set global variable $ARTIFACT to the "3xxyyzz" string incorporated
+# into artifact filenames. And $VERSION2 to the "3.x.y[.z]" form.
+xx=`echo $VERSION|sed 's/3\.\([0-9]*\)\..*/\1/'`
+yy=`echo $VERSION|sed 's/3\.[^.]*\.\([0-9]*\).*/\1/'`
+zz=0
+set +e
+ zz=`echo $VERSION|sed 's/3\.[^.]*\.[^.]*\.\([0-9]*\).*/\1/'|grep -v '\.'`
+set -e
+ARTIFACT=`printf "3%.2d%.2d%.2d" $xx $yy $zz`
+
+rm -rf $TMPSPACE
+cp -R $TOP/autoconf $TMPSPACE
+
+cp sqlite3.c $TMPSPACE
+cp sqlite3.h $TMPSPACE
+cp sqlite3ext.h $TMPSPACE
+cp $TOP/sqlite3.1 $TMPSPACE
+cp $TOP/sqlite3.pc.in $TMPSPACE
+cp $TOP/src/shell.c $TMPSPACE
+
+chmod 755 $TMPSPACE/install-sh
+chmod 755 $TMPSPACE/missing
+chmod 755 $TMPSPACE/depcomp
+chmod 755 $TMPSPACE/config.sub
+chmod 755 $TMPSPACE/config.guess
+
+cat $TMPSPACE/configure.ac |
+sed "s/AC_INIT(sqlite, .*, http:\/\/www.sqlite.org)/AC_INIT(sqlite, $VERSION, http:\/\/www.sqlite.org)/" > $TMPSPACE/tmp
+mv $TMPSPACE/tmp $TMPSPACE/configure.ac
+
+cd $TMPSPACE
+aclocal
+autoconf
+automake
+
+mkdir -p tea/generic
+echo "#ifdef USE_SYSTEM_SQLITE" > tea/generic/tclsqlite3.c
+echo "# include <sqlite3.h>" >> tea/generic/tclsqlite3.c
+echo "#else" >> tea/generic/tclsqlite3.c
+echo "#include \"../../sqlite3.c\"" >> tea/generic/tclsqlite3.c
+echo "#endif" >> tea/generic/tclsqlite3.c
+cat $TOP/src/tclsqlite.c >> tea/generic/tclsqlite3.c
+
+cat tea/configure.in |
+ sed "s/AC_INIT(\[sqlite\], .*)/AC_INIT([sqlite], [$VERSION])/" > tmp
+mv tmp tea/configure.in
+
+cd tea
+autoconf
+rm -rf autom4te.cache
+
+cd ../
+./configure && make dist
+tar -xzf sqlite-$VERSION.tar.gz
+mv sqlite-$VERSION sqlite-autoconf-$ARTIFACT
+tar -czf sqlite-autoconf-$ARTIFACT.tar.gz sqlite-autoconf-$ARTIFACT
+mv sqlite-autoconf-$ARTIFACT.tar.gz ..
+
diff --git a/tool/mkkeywordhash.c b/tool/mkkeywordhash.c
index 4e5ba8f..721611f 100644
--- a/tool/mkkeywordhash.c
+++ b/tool/mkkeywordhash.c
@@ -138,6 +138,11 @@ struct Keyword {
#else
# define AUTOVACUUM 0x00020000
#endif
+#ifdef SQLITE_OMIT_CTE
+# define CTE 0
+#else
+# define CTE 0x00040000
+#endif
/*
** These are the keywords
@@ -234,6 +239,7 @@ static Keyword aKeywordTable[] = {
{ "PRIMARY", "TK_PRIMARY", ALWAYS },
{ "QUERY", "TK_QUERY", EXPLAIN },
{ "RAISE", "TK_RAISE", TRIGGER },
+ { "RECURSIVE", "TK_RECURSIVE", CTE },
{ "REFERENCES", "TK_REFERENCES", FKEY },
{ "REGEXP", "TK_LIKE_KW", ALWAYS },
{ "REINDEX", "TK_REINDEX", REINDEX },
@@ -262,6 +268,8 @@ static Keyword aKeywordTable[] = {
{ "VALUES", "TK_VALUES", ALWAYS },
{ "VIEW", "TK_VIEW", VIEW },
{ "VIRTUAL", "TK_VIRTUAL", VTAB },
+ { "WITH", "TK_WITH", CTE },
+ { "WITHOUT", "TK_WITHOUT", ALWAYS },
{ "WHEN", "TK_WHEN", ALWAYS },
{ "WHERE", "TK_WHERE", ALWAYS },
};
@@ -362,7 +370,7 @@ int main(int argc, char **argv){
Keyword *p = &aKeywordTable[i];
p->len = (int)strlen(p->zName);
assert( p->len<sizeof(p->zOrigName) );
- strcpy(p->zOrigName, p->zName);
+ memcpy(p->zOrigName, p->zName, p->len+1);
totalLen += p->len;
p->hash = (UpperToLower[(int)p->zName[0]]*4) ^
(UpperToLower[(int)p->zName[p->len-1]]*3) ^ p->len;
diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl
new file mode 100644
index 0000000..28a1e46
--- /dev/null
+++ b/tool/mkpragmatab.tcl
@@ -0,0 +1,434 @@
+#!/usr/bin/tclsh
+#
+# Run this script to generate the pragma name lookup table C code.
+#
+# To add new pragmas, first add the name and other relevant attributes
+# of the pragma to the "pragma_def" object below. Then run this script
+# to generate the C-code for the lookup table and copy/paste the output
+# of this script into the appropriate spot in the pragma.c source file.
+# Then add the extra "case PragTyp_XXXXX:" and subsequent code for the
+# new pragma.
+#
+
+set pragma_def {
+ NAME: full_column_names
+ TYPE: FLAG
+ ARG: SQLITE_FullColNames
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: short_column_names
+ TYPE: FLAG
+ ARG: SQLITE_ShortColNames
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: count_changes
+ TYPE: FLAG
+ ARG: SQLITE_CountRows
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: empty_result_callbacks
+ TYPE: FLAG
+ ARG: SQLITE_NullCallback
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: legacy_file_format
+ TYPE: FLAG
+ ARG: SQLITE_LegacyFileFmt
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: fullfsync
+ TYPE: FLAG
+ ARG: SQLITE_FullFSync
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: checkpoint_fullfsync
+ TYPE: FLAG
+ ARG: SQLITE_CkptFullFSync
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: cache_spill
+ TYPE: FLAG
+ ARG: SQLITE_CacheSpill
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: reverse_unordered_selects
+ TYPE: FLAG
+ ARG: SQLITE_ReverseOrder
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: query_only
+ TYPE: FLAG
+ ARG: SQLITE_QueryOnly
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: automatic_index
+ TYPE: FLAG
+ ARG: SQLITE_AutoIndex
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_AUTOMATIC_INDEX)
+
+ NAME: sql_trace
+ TYPE: FLAG
+ ARG: SQLITE_SqlTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_listing
+ TYPE: FLAG
+ ARG: SQLITE_VdbeListing
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_trace
+ TYPE: FLAG
+ ARG: SQLITE_VdbeTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_addoptrace
+ TYPE: FLAG
+ ARG: SQLITE_VdbeAddopTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_debug
+ TYPE: FLAG
+ ARG: SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: vdbe_eqp
+ TYPE: FLAG
+ ARG: SQLITE_VdbeEQP
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: ignore_check_constraints
+ TYPE: FLAG
+ ARG: SQLITE_IgnoreChecks
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_CHECK)
+
+ NAME: writable_schema
+ TYPE: FLAG
+ ARG: SQLITE_WriteSchema|SQLITE_RecoveryMode
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: read_uncommitted
+ TYPE: FLAG
+ ARG: SQLITE_ReadUncommitted
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: recursive_triggers
+ TYPE: FLAG
+ ARG: SQLITE_RecTriggers
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+
+ NAME: foreign_keys
+ TYPE: FLAG
+ ARG: SQLITE_ForeignKeys
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: defer_foreign_keys
+ TYPE: FLAG
+ ARG: SQLITE_DeferFKs
+ IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: default_cache_size
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+
+ NAME: page_size
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: secure_delete
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: page_count
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: max_page_count
+ TYPE: PAGE_COUNT
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: locking_mode
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: journal_mode
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: journal_size_limit
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: cache_size
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: mmap_size
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: auto_vacuum
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_AUTOVACUUM)
+
+ NAME: incremental_vacuum
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_AUTOVACUUM)
+
+ NAME: temp_store
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: temp_store_directory
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: data_store_directory
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
+
+ NAME: lock_proxy_file
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
+
+ NAME: synchronous
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+
+ NAME: table_info
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: stats
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: index_info
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: index_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: database_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: collation_list
+ IF: !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+
+ NAME: foreign_key_list
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY)
+
+ NAME: foreign_key_check
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+
+ NAME: parser_trace
+ IF: defined(SQLITE_DEBUG)
+
+ NAME: case_sensitive_like
+
+ NAME: integrity_check
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+
+ NAME: quick_check
+ TYPE: INTEGRITY_CHECK
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+
+ NAME: encoding
+ IF: !defined(SQLITE_OMIT_UTF16)
+
+ NAME: schema_version
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: user_version
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: freelist_count
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: application_id
+ TYPE: HEADER_VALUE
+ IF: !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+
+ NAME: compile_options
+ IF: !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
+
+ NAME: wal_checkpoint
+ FLAG: NeedSchema
+ IF: !defined(SQLITE_OMIT_WAL)
+
+ NAME: wal_autocheckpoint
+ IF: !defined(SQLITE_OMIT_WAL)
+
+ NAME: shrink_memory
+
+ NAME: busy_timeout
+
+ NAME: lock_status
+ IF: defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+
+ NAME: key
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: rekey
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: hexkey
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: hexrekey
+ TYPE: HEXKEY
+ IF: defined(SQLITE_HAS_CODEC)
+
+ NAME: activate_extensions
+ IF: defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+
+ NAME: soft_heap_limit
+}
+fconfigure stdout -translation lf
+set name {}
+set type {}
+set if {}
+set flags {}
+set arg 0
+proc record_one {} {
+ global name type if arg allbyname typebyif flags
+ if {$name==""} return
+ set allbyname($name) [list $type $arg $if $flags]
+ set name {}
+ set type {}
+ set if {}
+ set flags {}
+ set arg 0
+}
+foreach line [split $pragma_def \n] {
+ set line [string trim $line]
+ if {$line==""} continue
+ foreach {id val} [split $line :] break
+ set val [string trim $val]
+ if {$id=="NAME"} {
+ record_one
+ set name $val
+ set type [string toupper $val]
+ } elseif {$id=="TYPE"} {
+ set type $val
+ } elseif {$id=="ARG"} {
+ set arg $val
+ } elseif {$id=="IF"} {
+ lappend if $val
+ } elseif {$id=="FLAG"} {
+ foreach term [split $val] {
+ lappend flags $term
+ set allflags($term) 1
+ }
+ } else {
+ error "bad pragma_def line: $line"
+ }
+}
+record_one
+set allnames [lsort [array names allbyname]]
+
+# Generate #defines for all pragma type names. Group the pragmas that are
+# omit in default builds (defined(SQLITE_DEBUG) and defined(SQLITE_HAS_CODEC))
+# at the end.
+#
+set pnum 0
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set if [lindex $allbyname($name) 2]
+ if {[regexp SQLITE_DEBUG $if] || [regexp SQLITE_HAS_CODEC $if]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set if [lindex $allbyname($name) 2]
+ if {[regexp SQLITE_DEBUG $if]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+foreach name $allnames {
+ set type [lindex $allbyname($name) 0]
+ if {[info exists seentype($type)]} continue
+ set seentype($type) 1
+ puts [format {#define %-35s %4d} PragTyp_$type $pnum]
+ incr pnum
+}
+
+# Generate #defines for flags
+#
+set fv 1
+foreach f [lsort [array names allflags]] {
+ puts [format {#define PragFlag_%-20s 0x%02x} $f $fv]
+ set fv [expr {$fv*2}]
+}
+
+# Generate the lookup table
+#
+puts "static const struct sPragmaNames \173"
+puts " const char *const zName; /* Name of pragma */"
+puts " u8 ePragTyp; /* PragTyp_XXX value */"
+puts " u8 mPragFlag; /* Zero or more PragFlag_XXX values */"
+puts " u32 iArg; /* Extra argument */"
+puts "\175 aPragmaNames\[\] = \173"
+
+set current_if {}
+set spacer [format { %26s } {}]
+foreach name $allnames {
+ foreach {type arg if flag} $allbyname($name) break
+ if {$if!=$current_if} {
+ if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#endif"
+ }
+ }
+ set current_if $if
+ if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#if $this_if"
+ }
+ }
+ }
+ set typex [format PragTyp_%-23s $type,]
+ if {$flag==""} {
+ set flagx "0"
+ } else {
+ set flagx PragFlag_[join $flag {|PragFlag_}]
+ }
+ puts " \173 /* zName: */ \"$name\","
+ puts " /* ePragTyp: */ PragTyp_$type,"
+ puts " /* ePragFlag: */ $flagx,"
+ puts " /* iArg: */ $arg \175,"
+}
+if {$current_if!=""} {
+ foreach this_if $current_if {
+ puts "#endif"
+ }
+}
+puts "\175;"
+
+# count the number of pragmas, for information purposes
+#
+set allcnt 0
+set dfltcnt 0
+foreach name $allnames {
+ incr allcnt
+ set if [lindex $allbyname($name) 2]
+ if {[regexp {^defined} $if] || [regexp {[^!]defined} $if]} continue
+ incr dfltcnt
+}
+puts "/* Number of pragmas: $dfltcnt on by default, $allcnt total. */"
diff --git a/tool/mksqlite3c-noext.tcl b/tool/mksqlite3c-noext.tcl
index 017ad62..ecb9cb0 100644
--- a/tool/mksqlite3c-noext.tcl
+++ b/tool/mksqlite3c-noext.tcl
@@ -99,6 +99,8 @@ foreach hdr {
mutex.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl
index 07c01ef..c14be20 100644
--- a/tool/mksqlite3c.tcl
+++ b/tool/mksqlite3c.tcl
@@ -105,6 +105,8 @@ foreach hdr {
mutex.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
@@ -118,6 +120,7 @@ foreach hdr {
vdbe.h
vdbeInt.h
wal.h
+ whereInt.h
} {
set available_hdr($hdr) 1
}
@@ -139,7 +142,7 @@ proc section_comment {text} {
# Read the source file named $filename and write it into the
# sqlite3.c output file. If any #include statements are seen,
-# process them approprately.
+# process them appropriately.
#
proc copy_file {filename} {
global seen_hdr available_hdr out addstatic linemacros
@@ -169,10 +172,18 @@ proc copy_file {filename} {
if {$linemacros} {puts $out "#line [expr {$ln+1}] \"$filename\""}
}
} elseif {![info exists seen_hdr($hdr)]} {
- set seen_hdr($hdr) 1
+ if {![regexp {/\*\s+amalgamator:\s+dontcache\s+\*/} $line]} {
+ set seen_hdr($hdr) 1
+ }
+ puts $out $line
+ } elseif {[regexp {/\*\s+amalgamator:\s+keep\s+\*/} $line]} {
+ # This include file must be kept because there was a "keep"
+ # directive inside of a line comment.
puts $out $line
} else {
- puts $out "/* $line */"
+ # Comment out the entire line, replacing any nested comment
+ # begin/end markers with the harmless substring "**".
+ puts $out "/* [string map [list /* ** */ **] $line] */"
}
} elseif {[regexp {^#ifdef __cplusplus} $line]} {
puts $out "#if 0"
diff --git a/tool/mksqlite3internalh.tcl b/tool/mksqlite3internalh.tcl
index 406ef5c..7e92b3a 100644
--- a/tool/mksqlite3internalh.tcl
+++ b/tool/mksqlite3internalh.tcl
@@ -60,6 +60,8 @@ foreach hdr {
keywordhash.h
opcodes.h
os_common.h
+ os_setup.h
+ os_win.h
os.h
pager.h
parse.h
diff --git a/tool/mkvsix.tcl b/tool/mkvsix.tcl
index e9f1f81..208ce2b 100644
--- a/tool/mkvsix.tcl
+++ b/tool/mkvsix.tcl
@@ -65,12 +65,16 @@
# argument is optional and if present must contain the name of the directory
# containing the root of the source tree for SQLite. The third argument is
# optional and if present must contain the flavor the VSIX package to build.
-# Currently, the only supported package flavors are "WinRT" and "WP80". The
-# fourth argument is optional and if present must be a string containing a list
-# of platforms to include in the VSIX package. The format of the platform list
-# string is "platform1,platform2,platform3". Typically, when on Windows, this
-# script is executed using commands similar to the following from a normal
-# Windows command prompt:
+# Currently, the only supported package flavors are "WinRT", "WinRT81", "WP80",
+# "WP81", and "Win32". The fourth argument is optional and if present must be
+# a string containing a list of platforms to include in the VSIX package. The
+# platform list is "platform1,platform2,platform3". The fifth argument is
+# optional and if present must contain the version of Visual Studio required by
+# the package. Currently, the only supported versions are "2012" and "2013".
+# The package flavors "WinRT81" and "WP81" are only supported when the Visual
+# Studio version is "2013". Typically, when on Windows, this script is
+# executed using commands similar to the following from a normal Windows
+# command prompt:
#
# CD /D C:\dev\sqlite\core
# tclsh85 tool\mkvsix.tcl C:\Temp
@@ -100,7 +104,7 @@ proc fail { {error ""} {usage false} } {
puts stdout "usage:\
[file tail [info nameofexecutable]]\
[file tail [info script]] <binaryDirectory> \[sourceDirectory\]\
-\[packageFlavor\] \[platformNames\]"
+\[packageFlavor\] \[platformNames\] \[vsVersion\]"
exit 1
}
@@ -170,13 +174,81 @@ proc writeFile { fileName data } {
return ""
}
-proc substFile { fileName } {
+proc getMinVsVersionXmlChunk { vsVersion } {
+ switch -exact $vsVersion {
+ 2012 {
+ return [appendArgs \
+ "\r\n " {MinVSVersion="11.0"}]
+ }
+ 2013 {
+ return [appendArgs \
+ "\r\n " {MinVSVersion="12.0"}]
+ }
+ default {
+ return ""
+ }
+ }
+}
+
+proc getMaxPlatformVersionXmlChunk { packageFlavor vsVersion } {
#
- # NOTE: Performs all Tcl command, variable, and backslash substitutions in
- # the specified file and then rewrites the contents of that same file
- # with the substituted data.
+ # NOTE: Only Visual Studio 2013 supports this SDK manifest attribute.
#
- return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]]
+ if {![string equal $vsVersion 2013]} then {
+ return ""
+ }
+
+ switch -exact $packageFlavor {
+ WinRT {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.0"}]
+ }
+ WinRT81 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.1"}]
+ }
+ WP80 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.0"}]
+ }
+ WP81 {
+ return [appendArgs \
+ "\r\n " {MaxPlatformVersion="8.1"}]
+ }
+ default {
+ return ""
+ }
+ }
+}
+
+proc getExtraFileListXmlChunk { packageFlavor vsVersion } {
+ #
+ # NOTE: Windows Phone 8.0 does not require any extra attributes in its VSIX
+ # package SDK manifests; however, it appears that Windows Phone 8.1
+ # does.
+ #
+ if {[string equal $packageFlavor WP80]} then {
+ return ""
+ }
+
+ set appliesTo [expr {[string equal $packageFlavor Win32] ? \
+ "VisualC" : "WindowsAppContainer"}]
+
+ switch -exact $vsVersion {
+ 2012 {
+ return [appendArgs \
+ "\r\n " AppliesTo=\" $appliesTo \" \
+ "\r\n " {DependsOn="Microsoft.VCLibs, version=11.0"}]
+ }
+ 2013 {
+ return [appendArgs \
+ "\r\n " AppliesTo=\" $appliesTo \" \
+ "\r\n " {DependsOn="Microsoft.VCLibs, version=12.0"}]
+ }
+ default {
+ return ""
+ }
+ }
}
proc replaceFileNameTokens { fileName name buildName platformName } {
@@ -188,6 +260,15 @@ proc replaceFileNameTokens { fileName name buildName platformName } {
<name> $name] $fileName]
}
+proc substFile { fileName } {
+ #
+ # NOTE: Performs all Tcl command, variable, and backslash substitutions in
+ # the specified file and then rewrites the contents of that same file
+ # with the substituted data.
+ #
+ return [writeFile $fileName [uplevel 1 [list subst [readFile $fileName]]]]
+}
+
#
# NOTE: This is the entry point for this script.
#
@@ -206,7 +287,7 @@ set rootName [file rootname [file tail $script]]
# NOTE: Process and verify all the command line arguments.
#
set argc [llength $argv]
-if {$argc < 1 || $argc > 4} then {fail}
+if {$argc < 1 || $argc > 5} then {fail}
set binaryDirectory [lindex $argv 0]
@@ -251,34 +332,123 @@ if {[string length $packageFlavor] == 0} then {
fail "invalid package flavor"
}
-if {[string equal -nocase $packageFlavor WinRT]} then {
- set shortName SQLite.WinRT
- set displayName "SQLite for Windows Runtime"
- set targetPlatformIdentifier Windows
- set extraSdkPath ""
- set extraFileListAttributes [appendArgs \
- "\r\n " {AppliesTo="WindowsAppContainer"} \
- "\r\n " {DependsOn="Microsoft.VCLibs, version=11.0"}]
-} elseif {[string equal -nocase $packageFlavor WP80]} then {
- set shortName SQLite.WP80
- set displayName "SQLite for Windows Phone"
- set targetPlatformIdentifier "Windows Phone"
- set extraSdkPath "\\..\\$targetPlatformIdentifier"
- set extraFileListAttributes ""
-} else {
- fail "unsupported package flavor, must be \"WinRT\" or \"WP80\""
-}
-
if {$argc >= 4} then {
set platformNames [list]
foreach platformName [split [lindex $argv 3] ", "] {
+ set platformName [string trim $platformName]
+
if {[string length $platformName] > 0} then {
lappend platformNames $platformName
}
}
}
+if {$argc >= 5} then {
+ set vsVersion [lindex $argv 4]
+} else {
+ set vsVersion 2012
+}
+
+if {[string length $vsVersion] == 0} then {
+ fail "invalid Visual Studio version"
+}
+
+if {![string equal $vsVersion 2012] && ![string equal $vsVersion 2013]} then {
+ fail [appendArgs \
+ "unsupported Visual Studio version, must be one of: " \
+ [list 2012 2013]]
+}
+
+set shortNames(WinRT,2012) SQLite.WinRT
+set shortNames(WinRT,2013) SQLite.WinRT.2013
+set shortNames(WinRT81,2013) SQLite.WinRT81
+set shortNames(WP80,2012) SQLite.WP80
+set shortNames(WP80,2013) SQLite.WP80.2013
+set shortNames(WP81,2013) SQLite.WP81
+set shortNames(Win32,2012) SQLite.Win32
+set shortNames(Win32,2013) SQLite.Win32.2013
+
+set displayNames(WinRT,2012) "SQLite for Windows Runtime"
+set displayNames(WinRT,2013) "SQLite for Windows Runtime"
+set displayNames(WinRT81,2013) "SQLite for Windows Runtime (Windows 8.1)"
+set displayNames(WP80,2012) "SQLite for Windows Phone"
+set displayNames(WP80,2013) "SQLite for Windows Phone"
+set displayNames(WP81,2013) "SQLite for Windows Phone 8.1"
+set displayNames(Win32,2012) "SQLite for Windows"
+set displayNames(Win32,2013) "SQLite for Windows"
+
+if {[string equal $packageFlavor WinRT]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WinRT81]} then {
+ if {$vsVersion ne "2013"} then {
+ fail [appendArgs \
+ "unsupported combination, package flavor " $packageFlavor \
+ " is only supported with Visual Studio 2013"]
+ }
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.1
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WP80]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier "Windows Phone"
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath "\\..\\$targetPlatformIdentifier"
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor WP81]} then {
+ if {$vsVersion ne "2013"} then {
+ fail [appendArgs \
+ "unsupported combination, package flavor " $packageFlavor \
+ " is only supported with Visual Studio 2013"]
+ }
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier WindowsPhoneApp
+ set targetPlatformVersion v8.1
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath "\\..\\$targetPlatformIdentifier"
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} elseif {[string equal $packageFlavor Win32]} then {
+ set shortName $shortNames($packageFlavor,$vsVersion)
+ set displayName $displayNames($packageFlavor,$vsVersion)
+ set targetPlatformIdentifier Windows
+ set targetPlatformVersion v8.0
+ set minVsVersion [getMinVsVersionXmlChunk $vsVersion]
+ set maxPlatformVersion \
+ [getMaxPlatformVersionXmlChunk $packageFlavor $vsVersion]
+ set extraSdkPath ""
+ set extraFileListAttributes \
+ [getExtraFileListXmlChunk $packageFlavor $vsVersion]
+} else {
+ fail [appendArgs \
+ "unsupported package flavor, must be one of: " \
+ [list WinRT WinRT81 WP80 WP81 Win32]]
+}
+
###############################################################################
#
@@ -466,7 +636,7 @@ if {![info exists buildNames]} then {
# overridden via the command line or the user-specific customizations
# file.
#
-if {![info exists platformNames]} then {
+if {![info exists platformNames] || [llength $platformNames] == 0} then {
set platformNames [list x86 x64 ARM]
}
diff --git a/tool/omittest.tcl b/tool/omittest.tcl
index 3351b96..5437f2e 100644
--- a/tool/omittest.tcl
+++ b/tool/omittest.tcl
@@ -190,6 +190,7 @@ proc main {argv} {
SQLITE_OMIT_COMPILEOPTION_DIAGS \
SQLITE_OMIT_COMPLETE \
SQLITE_OMIT_COMPOUND_SELECT \
+ SQLITE_OMIT_CTE \
SQLITE_OMIT_DATETIME_FUNCS \
SQLITE_OMIT_DECLTYPE \
SQLITE_OMIT_DEPRECATED \
diff --git a/tool/pagesig.c b/tool/pagesig.c
new file mode 100644
index 0000000..540c9d7
--- /dev/null
+++ b/tool/pagesig.c
@@ -0,0 +1,92 @@
+/*
+** 2013-10-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.
+**
+******************************************************************************
+**
+** Compute hash signatures for every page of a database file. This utility
+** program is useful for analyzing the output logs generated by the
+** ext/misc/vfslog.c extension.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+/*
+** Compute signature for a block of content.
+**
+** For blocks of 16 or fewer bytes, the signature is just a hex dump of
+** the entire block.
+**
+** For blocks of more than 16 bytes, the signature is a hex dump of the
+** first 8 bytes followed by a 64-bit has of the entire block.
+*/
+static void vlogSignature(unsigned char *p, int n, char *zCksum){
+ unsigned int s0 = 0, s1 = 0;
+ unsigned int *pI;
+ int i;
+ if( n<=16 ){
+ for(i=0; i<n; i++) sprintf(zCksum+i*2, "%02x", p[i]);
+ }else{
+ pI = (unsigned int*)p;
+ for(i=0; i<n-7; i+=8){
+ s0 += pI[0] + s1;
+ s1 += pI[1] + s0;
+ pI += 2;
+ }
+ for(i=0; i<8; i++) sprintf(zCksum+i*2, "%02x", p[i]);
+ sprintf(zCksum+i*2, "-%08x%08x", s0, s1);
+ }
+}
+
+/*
+** Open a file. Find its page size. Read each page, and compute and
+** display the page signature.
+*/
+static void computeSigs(const char *zFilename){
+ FILE *in = fopen(zFilename, "rb");
+ unsigned pgsz;
+ size_t got;
+ unsigned n;
+ unsigned char aBuf[50];
+ unsigned char aPage[65536];
+
+ if( in==0 ){
+ fprintf(stderr, "cannot open \"%s\"\n", zFilename);
+ return;
+ }
+ got = fread(aBuf, 1, sizeof(aBuf), in);
+ if( got!=sizeof(aBuf) ){
+ goto endComputeSigs;
+ }
+ pgsz = aBuf[16]*256 + aBuf[17];
+ if( pgsz==1 ) pgsz = 65536;
+ if( (pgsz & (pgsz-1))!=0 ){
+ fprintf(stderr, "invalid page size: %02x%02x\n", aBuf[16], aBuf[17]);
+ goto endComputeSigs;
+ }
+ rewind(in);
+ for(n=1; (got=fread(aPage, 1, pgsz, in))==pgsz; n++){
+ vlogSignature(aPage, pgsz, aBuf);
+ printf("%4d: %s\n", n, aBuf);
+ }
+
+endComputeSigs:
+ fclose(in);
+}
+
+/*
+** Find page signatures for all named files.
+*/
+int main(int argc, char **argv){
+ int i;
+ for(i=1; i<argc; i++) computeSigs(argv[i]);
+ return 0;
+}
diff --git a/tool/showdb.c b/tool/showdb.c
index 27424e0..1a51e9d 100644
--- a/tool/showdb.c
+++ b/tool/showdb.c
@@ -9,6 +9,8 @@
#if !defined(_MSC_VER)
#include <unistd.h>
+#else
+#include <io.h>
#endif
#include <stdlib.h>
@@ -66,7 +68,7 @@ static unsigned char *getContent(int ofst, int nByte){
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
lseek(db, ofst, SEEK_SET);
- read(db, aData, nByte);
+ if( read(db, aData, nByte)<nByte ) memset(aData, 0, nByte);
return aData;
}
@@ -119,7 +121,7 @@ static unsigned char *print_byte_range(
/*
** Print an entire page of content as hex
*/
-static print_page(int iPg){
+static void print_page(int iPg){
int iStart;
unsigned char *aData;
iStart = (iPg-1)*pagesize;
@@ -129,9 +131,10 @@ static print_page(int iPg){
free(aData);
}
+
/* Print a line of decode output showing a 4-byte integer.
*/
-static print_decode_line(
+static void print_decode_line(
unsigned char *aData, /* Content being decoded */
int ofst, int nByte, /* Start and size of decode */
const char *zMsg /* Message to append */
@@ -140,7 +143,7 @@ static print_decode_line(
int val = aData[ofst];
char zBuf[100];
sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
- i = strlen(zBuf);
+ i = (int)strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
sprintf(&zBuf[i], " ");
@@ -148,7 +151,7 @@ static print_decode_line(
sprintf(&zBuf[i], " %02x", aData[ofst+j]);
val = val*256 + aData[ofst+j];
}
- i += strlen(&zBuf[i]);
+ i += (int)strlen(&zBuf[i]);
}
sprintf(&zBuf[i], " %9d", val);
printf("%s %s\n", zBuf, zMsg);
@@ -189,14 +192,14 @@ static void print_db_header(void){
/*
** Describe cell content.
*/
-static int describeContent(
+static i64 describeContent(
unsigned char *a, /* Cell content */
- int nLocal, /* Bytes in a[] */
+ i64 nLocal, /* Bytes in a[] */
char *zDesc /* Write description here */
){
- int nDesc = 0;
- int n, i, j;
- i64 x, v;
+ i64 nDesc = 0;
+ int n, j;
+ i64 i, x, v;
const unsigned char *pData;
const unsigned char *pLimit;
char sep = ' ';
@@ -236,15 +239,15 @@ static int describeContent(
}else if( x==9 ){
sprintf(zDesc, "1");
}else if( x>=12 ){
- int size = (x-12)/2;
+ i64 size = (x-12)/2;
if( (x&1)==0 ){
- sprintf(zDesc, "blob(%d)", size);
+ sprintf(zDesc, "blob(%lld)", size);
}else{
- sprintf(zDesc, "txt(%d)", size);
+ sprintf(zDesc, "txt(%lld)", size);
}
pData += size;
}
- j = strlen(zDesc);
+ j = (int)strlen(zDesc);
zDesc += j;
nDesc += j;
}
@@ -255,11 +258,11 @@ static int describeContent(
** Compute the local payload size given the total payload size and
** the page size.
*/
-static int localPayload(i64 nPayload, char cType){
- int maxLocal;
- int minLocal;
- int surplus;
- int nLocal;
+static i64 localPayload(i64 nPayload, char cType){
+ i64 maxLocal;
+ i64 minLocal;
+ i64 surplus;
+ i64 nLocal;
if( cType==13 ){
/* Table leaf */
maxLocal = pagesize-35;
@@ -287,19 +290,19 @@ static int localPayload(i64 nPayload, char cType){
**
** The return value is the local cell size.
*/
-static int describeCell(
+static i64 describeCell(
unsigned char cType, /* Page type */
unsigned char *a, /* Cell content */
int showCellContent, /* Show cell content if true */
char **pzDesc /* Store description here */
){
int i;
- int nDesc = 0;
+ i64 nDesc = 0;
int n = 0;
int leftChild;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
static char zDesc[1000];
i = 0;
if( cType<=5 ){
@@ -341,6 +344,180 @@ static int describeCell(
return nLocal+n;
}
+/* Print an offset followed by nByte bytes. Add extra white-space
+** at the end so that subsequent text is aligned.
+*/
+static void printBytes(
+ unsigned char *aData, /* Content being decoded */
+ unsigned char *aStart, /* Start of content to be printed */
+ int nByte /* Number of bytes to print */
+){
+ int j;
+ printf(" %03x: ", (int)(aStart-aData));
+ for(j=0; j<9; j++){
+ if( j>=nByte ){
+ printf(" ");
+ }else{
+ printf("%02x ", aStart[j]);
+ }
+ }
+}
+
+
+/*
+** Write a full decode on stdout for the cell at a[ofst].
+** Assume the page contains a header of size szPgHdr bytes.
+*/
+static void decodeCell(
+ unsigned char *a, /* Page content (without the page-1 header) */
+ unsigned pgno, /* Page number */
+ int iCell, /* Cell index */
+ int szPgHdr, /* Size of the page header. 0 or 100 */
+ int ofst /* Cell begins at a[ofst] */
+){
+ int i, j;
+ int leftChild;
+ i64 k;
+ i64 nPayload;
+ i64 rowid;
+ i64 nHdr;
+ i64 iType;
+ i64 nLocal;
+ unsigned char *x = a + ofst;
+ unsigned char *end;
+ unsigned char cType = a[0];
+ int nCol = 0;
+ int szCol[2000];
+ int ofstCol[2000];
+ int typeCol[2000];
+
+ printf("Cell[%d]:\n", iCell);
+ if( cType<=5 ){
+ leftChild = ((x[0]*256 + x[1])*256 + x[2])*256 + x[3];
+ printBytes(a, x, 4);
+ printf("left child page:: %d\n", leftChild);
+ x += 4;
+ }
+ if( cType!=5 ){
+ i = decodeVarint(x, &nPayload);
+ printBytes(a, x, i);
+ nLocal = localPayload(nPayload, cType);
+ if( nLocal==nPayload ){
+ printf("payload-size: %lld\n", nPayload);
+ }else{
+ printf("payload-size: %lld (%lld local, %lld overflow)\n",
+ nPayload, nLocal, nPayload-nLocal);
+ }
+ x += i;
+ }else{
+ nPayload = nLocal = 0;
+ }
+ end = x + nLocal;
+ if( cType==5 || cType==13 ){
+ i = decodeVarint(x, &rowid);
+ printBytes(a, x, i);
+ printf("rowid: %lld\n", rowid);
+ x += i;
+ }
+ if( nLocal>0 ){
+ i = decodeVarint(x, &nHdr);
+ printBytes(a, x, i);
+ printf("record-header-size: %d\n", (int)nHdr);
+ j = i;
+ nCol = 0;
+ k = nHdr;
+ while( x+j<end && j<nHdr ){
+ const char *zTypeName;
+ int sz = 0;
+ char zNm[30];
+ i = decodeVarint(x+j, &iType);
+ printBytes(a, x+j, i);
+ printf("typecode[%d]: %d - ", nCol, (int)iType);
+ switch( iType ){
+ case 0: zTypeName = "NULL"; sz = 0; break;
+ case 1: zTypeName = "int8"; sz = 1; break;
+ case 2: zTypeName = "int16"; sz = 2; break;
+ case 3: zTypeName = "int24"; sz = 3; break;
+ case 4: zTypeName = "int32"; sz = 4; break;
+ case 5: zTypeName = "int48"; sz = 6; break;
+ case 6: zTypeName = "int64"; sz = 8; break;
+ case 7: zTypeName = "double"; sz = 8; break;
+ case 8: zTypeName = "zero"; sz = 0; break;
+ case 9: zTypeName = "one"; sz = 0; break;
+ case 10:
+ case 11: zTypeName = "error"; sz = 0; break;
+ default: {
+ sz = (int)(iType-12)/2;
+ sprintf(zNm, (iType&1)==0 ? "blob(%d)" : "text(%d)", sz);
+ zTypeName = zNm;
+ break;
+ }
+ }
+ printf("%s\n", zTypeName);
+ szCol[nCol] = sz;
+ ofstCol[nCol] = (int)k;
+ typeCol[nCol] = (int)iType;
+ k += sz;
+ nCol++;
+ j += i;
+ }
+ for(i=0; i<nCol && ofstCol[i]+szCol[i]<=nLocal; i++){
+ int s = ofstCol[i];
+ i64 v;
+ const unsigned char *pData;
+ if( szCol[i]==0 ) continue;
+ printBytes(a, x+s, szCol[i]);
+ printf("data[%d]: ", i);
+ pData = x+s;
+ if( typeCol[i]<=7 ){
+ v = (signed char)pData[0];
+ for(k=1; k<szCol[i]; k++){
+ v = (v<<8) + pData[k];
+ }
+ if( typeCol[i]==7 ){
+ double r;
+ memcpy(&r, &v, sizeof(r));
+ printf("%#g\n", r);
+ }else{
+ printf("%lld\n", v);
+ }
+ }else{
+ int ii, jj;
+ char zConst[32];
+ if( (typeCol[i]&1)==0 ){
+ zConst[0] = 'x';
+ zConst[1] = '\'';
+ for(ii=2, jj=0; jj<szCol[i] && ii<24; jj++, ii+=2){
+ sprintf(zConst+ii, "%02x", pData[jj]);
+ }
+ }else{
+ zConst[0] = '\'';
+ for(ii=1, jj=0; jj<szCol[i] && ii<24; jj++, ii++){
+ zConst[ii] = isprint(pData[jj]) ? pData[jj] : '.';
+ }
+ zConst[ii] = 0;
+ }
+ if( jj<szCol[i] ){
+ memcpy(zConst+ii, "...'", 5);
+ }else{
+ memcpy(zConst+ii, "'", 2);
+ }
+ printf("%s\n", zConst);
+ }
+ j = ofstCol[i] + szCol[i];
+ }
+ }
+ if( j<nLocal ){
+ printBytes(a, x+j, 0);
+ printf("... %lld bytes of content ...\n", nLocal-j);
+ }
+ if( nLocal<nPayload ){
+ printBytes(a, x+nLocal, 4);
+ printf("overflow-page: %d\n", decodeInt32(x+nLocal));
+ }
+}
+
+
/*
** Decode a btree page
*/
@@ -356,6 +533,7 @@ static void decode_btree_page(
int iCellPtr;
int showCellContent = 0;
int showMap = 0;
+ int cellToDecode = -2;
char *zMap = 0;
switch( a[0] ){
case 2: zType = "index interior node"; break;
@@ -367,23 +545,37 @@ static void decode_btree_page(
switch( zArgs[0] ){
case 'c': showCellContent = 1; break;
case 'm': showMap = 1; break;
+ case 'd': {
+ if( !isdigit(zArgs[1]) ){
+ cellToDecode = -1;
+ }else{
+ cellToDecode = 0;
+ while( isdigit(zArgs[1]) ){
+ zArgs++;
+ cellToDecode = cellToDecode*10 + zArgs[0] - '0';
+ }
+ }
+ break;
+ }
}
zArgs++;
}
- printf("Decode of btree page %d:\n", pgno);
+ nCell = a[3]*256 + a[4];
+ iCellPtr = (a[0]==2 || a[0]==5) ? 12 : 8;
+ if( cellToDecode>=nCell ){
+ printf("Page %d has only %d cells\n", pgno, nCell);
+ return;
+ }
+ printf("Header on btree page %d:\n", pgno);
print_decode_line(a, 0, 1, zType);
print_decode_line(a, 1, 2, "Offset to first freeblock");
print_decode_line(a, 3, 2, "Number of cells on this page");
- nCell = a[3]*256 + a[4];
print_decode_line(a, 5, 2, "Offset to cell content area");
print_decode_line(a, 7, 1, "Fragmented byte count");
if( a[0]==2 || a[0]==5 ){
print_decode_line(a, 8, 4, "Right child");
- iCellPtr = 12;
- }else{
- iCellPtr = 8;
}
- if( nCell>0 ){
+ if( cellToDecode==(-2) && nCell>0 ){
printf(" key: lx=left-child n=payload-size r=rowid\n");
}
if( showMap ){
@@ -396,27 +588,32 @@ static void decode_btree_page(
for(i=0; i<nCell; i++){
int cofst = iCellPtr + i*2;
char *zDesc;
- int n;
+ i64 n;
cofst = a[cofst]*256 + a[cofst+1];
n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
if( showMap ){
char zBuf[30];
- memset(&zMap[cofst], '*', n);
+ memset(&zMap[cofst], '*', (size_t)n);
zMap[cofst] = '[';
zMap[cofst+n-1] = ']';
sprintf(zBuf, "%d", i);
- j = strlen(zBuf);
+ j = (int)strlen(zBuf);
if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
}
- printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
+ if( cellToDecode==(-2) ){
+ printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
+ }else if( cellToDecode==(-1) || cellToDecode==i ){
+ decodeCell(a, pgno, i, hdrSize, cofst-hdrSize);
+ }
}
if( showMap ){
+ printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n");
for(i=0; i<pagesize; i+=64){
printf(" %03x: %.64s\n", i, &zMap[i]);
}
free(zMap);
- }
+ }
}
/*
@@ -428,7 +625,7 @@ static void decode_trunk_page(
int detail, /* Show leaf pages if true */
int recursive /* Follow the trunk change if true */
){
- int n, i, k;
+ int n, i;
unsigned char *a;
while( pgno>0 ){
a = getContent((pgno-1)*pagesize, pagesize);
@@ -495,11 +692,10 @@ static void page_usage_cell(
int cellno /* Index of the cell on the page */
){
int i;
- int nDesc = 0;
int n = 0;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
i = 0;
if( cType<=5 ){
a += 4;
@@ -677,7 +873,7 @@ static void page_usage_report(const char *zDbName){
if( rc==SQLITE_OK ){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
int pgno = sqlite3_column_int(pStmt, 2);
- page_usage_btree(pgno, 0, 0, sqlite3_column_text(pStmt, 1));
+ page_usage_btree(pgno, 0, 0, (const char*)sqlite3_column_text(pStmt,1));
}
}else{
printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db));
@@ -700,12 +896,12 @@ static void page_usage_report(const char *zDbName){
** Try to figure out how every page in the database file is being used.
*/
static void ptrmap_coverage_report(const char *zDbName){
- unsigned int pgno;
+ int pgno;
unsigned char *aHdr;
unsigned char *a;
int usable;
int perPage;
- unsigned int i;
+ int i;
/* Avoid the pathological case */
if( mxPage<1 ){
@@ -758,6 +954,7 @@ static void usage(const char *argv0){
" NNNb Decode btree page NNN\n"
" NNNbc Decode btree page NNN and show content\n"
" NNNbm Decode btree page NNN and show a layout map\n"
+ " NNNbdCCC Decode cell CCC on btree page NNN\n"
" NNNt Decode freelist trunk page NNN\n"
" NNNtd Show leaf freelist pages on the decode\n"
" NNNtr Recurisvely decode freelist starting at NNN\n"
@@ -779,7 +976,7 @@ int main(int argc, char **argv){
zPgSz[0] = 0;
zPgSz[1] = 0;
lseek(db, 16, SEEK_SET);
- read(db, zPgSz, 2);
+ if( read(db, zPgSz, 2)<2 ) memset(zPgSz, 0, 2);
pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
if( pagesize==0 ) pagesize = 1024;
printf("Pagesize: %d\n", pagesize);
@@ -835,7 +1032,6 @@ int main(int argc, char **argv){
free(a);
continue;
}else if( zLeft && zLeft[0]=='t' ){
- unsigned char *a;
int detail = 0;
int recursive = 0;
int i;
@@ -861,4 +1057,5 @@ int main(int argc, char **argv){
}
}
close(db);
+ return 0;
}
diff --git a/tool/showjournal.c b/tool/showjournal.c
index 5724f52..19220f5 100644
--- a/tool/showjournal.c
+++ b/tool/showjournal.c
@@ -12,7 +12,6 @@
static int pageSize = 1024;
static int sectorSize = 512;
static FILE *db = 0;
-static int showPageContent = 0;
static int fileSize = 0;
static unsigned cksumNonce = 0;
@@ -26,9 +25,9 @@ static void out_of_memory(void){
** Read N bytes of memory starting at iOfst into space obtained
** from malloc().
*/
-static char *read_content(int N, int iOfst){
+static unsigned char *read_content(int N, int iOfst){
int got;
- char *pBuf = malloc(N);
+ unsigned char *pBuf = malloc(N);
if( pBuf==0 ) out_of_memory();
fseek(db, iOfst, SEEK_SET);
got = fread(pBuf, 1, N, db);
@@ -46,14 +45,14 @@ static char *read_content(int N, int iOfst){
/* Print a line of decode output showing a 4-byte integer.
*/
static unsigned print_decode_line(
- unsigned char *aData, /* Content being decoded */
- int ofst, int nByte, /* Start and size of decode */
- const char *zMsg /* Message to append */
+ const unsigned char *aData, /* Content being decoded */
+ int ofst, int nByte, /* Start and size of decode */
+ const char *zMsg /* Message to append */
){
int i, j;
unsigned val = aData[ofst];
char zBuf[100];
- sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
+ sprintf(zBuf, " %05x: %02x", ofst, aData[ofst]);
i = strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
@@ -74,7 +73,7 @@ static unsigned print_decode_line(
** in global variables.
*/
static unsigned decode_journal_header(int iOfst){
- char *pHdr = read_content(64, iOfst);
+ unsigned char *pHdr = read_content(64, iOfst);
unsigned nPage;
printf("Header at offset %d:\n", iOfst);
print_decode_line(pHdr, 0, 4, "Header part 1 (3654616569)");
@@ -101,12 +100,11 @@ static void print_page(int iOfst){
char zTitle[50];
aData = read_content(pageSize+8, iOfst);
sprintf(zTitle, "page number for page at offset %d", iOfst);
- print_decode_line(aData, 0, 4, zTitle);
+ print_decode_line(aData-iOfst, iOfst, 4, zTitle);
free(aData);
}
int main(int argc, char **argv){
- int rc;
int nPage, cnt;
int iOfst;
if( argc!=2 ){
@@ -136,4 +134,5 @@ int main(int argc, char **argv){
iOfst = (iOfst/sectorSize + 1)*sectorSize;
}
fclose(db);
+ return 0;
}
diff --git a/tool/showstat4.c b/tool/showstat4.c
new file mode 100644
index 0000000..668d210
--- /dev/null
+++ b/tool/showstat4.c
@@ -0,0 +1,157 @@
+/*
+** This utility program decodes and displays the content of the
+** sqlite_stat4 table in the database file named on the command
+** line.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "sqlite3.h"
+
+typedef sqlite3_int64 i64; /* 64-bit signed integer type */
+
+
+/*
+** Convert the var-int format into i64. Return the number of bytes
+** in the var-int. Write the var-int value into *pVal.
+*/
+static int decodeVarint(const unsigned char *z, i64 *pVal){
+ i64 v = 0;
+ int i;
+ for(i=0; i<8; i++){
+ v = (v<<7) + (z[i]&0x7f);
+ if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
+ }
+ v = (v<<8) + (z[i]&0xff);
+ *pVal = v;
+ return 9;
+}
+
+
+
+int main(int argc, char **argv){
+ sqlite3 *db;
+ sqlite3_stmt *pStmt;
+ char *zIdx = 0;
+ int rc, j, x, y, mxHdr;
+ const unsigned char *aSample;
+ int nSample;
+ i64 iVal;
+ const char *zSep;
+
+ if( argc!=2 ){
+ fprintf(stderr, "Usage: %s DATABASE-FILE\n", argv[0]);
+ exit(1);
+ }
+ rc = sqlite3_open(argv[1], &db);
+ if( rc!=SQLITE_OK || db==0 ){
+ fprintf(stderr, "Cannot open database file [%s]\n", argv[1]);
+ exit(1);
+ }
+ rc = sqlite3_prepare_v2(db,
+ "SELECT tbl||'.'||idx, nEq, nLT, nDLt, sample "
+ "FROM sqlite_stat4 ORDER BY 1", -1,
+ &pStmt, 0);
+ if( rc!=SQLITE_OK || pStmt==0 ){
+ fprintf(stderr, "%s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ exit(1);
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ if( zIdx==0 || strcmp(zIdx, (const char*)sqlite3_column_text(pStmt,0))!=0 ){
+ if( zIdx ) printf("\n");
+ sqlite3_free(zIdx);
+ zIdx = sqlite3_mprintf("%s", sqlite3_column_text(pStmt,0));
+ printf("%s:\n", zIdx);
+ }else{
+ printf(" -----------------------------------------------------------\n");
+ }
+ printf(" nEq = %s\n", sqlite3_column_text(pStmt,1));
+ printf(" nLt = %s\n", sqlite3_column_text(pStmt,2));
+ printf(" nDLt = %s\n", sqlite3_column_text(pStmt,3));
+ printf(" sample = x'");
+ aSample = sqlite3_column_blob(pStmt,4);
+ nSample = sqlite3_column_bytes(pStmt,4);
+ for(j=0; j<nSample; j++) printf("%02x", aSample[j]);
+ printf("'\n ");
+ zSep = " ";
+ x = decodeVarint(aSample, &iVal);
+ if( iVal<x || iVal>nSample ){
+ printf(" <error>\n");
+ continue;
+ }
+ y = mxHdr = (int)iVal;
+ while( x<mxHdr ){
+ int sz;
+ i64 v;
+ x += decodeVarint(aSample+x, &iVal);
+ if( x>mxHdr ) break;
+ if( iVal<0 ) break;
+ switch( iVal ){
+ case 0: sz = 0; break;
+ case 1: sz = 1; break;
+ case 2: sz = 2; break;
+ case 3: sz = 3; break;
+ case 4: sz = 4; break;
+ case 5: sz = 6; break;
+ case 6: sz = 8; break;
+ case 7: sz = 8; break;
+ case 8: sz = 0; break;
+ case 9: sz = 0; break;
+ case 10:
+ case 11: sz = 0; break;
+ default: sz = (int)(iVal-12)/2; break;
+ }
+ if( y+sz>nSample ) break;
+ if( iVal==0 ){
+ printf("%sNULL", zSep);
+ }else if( iVal==8 || iVal==9 ){
+ printf("%s%d", zSep, ((int)iVal)-8);
+ }else if( iVal<=7 ){
+ v = (signed char)aSample[y];
+ for(j=1; j<sz; j++){
+ v = (v<<8) + aSample[y+j];
+ }
+ if( iVal==7 ){
+ double r;
+ memcpy(&r, &v, sizeof(r));
+ printf("%s%#g", zSep, r);
+ }else{
+ printf("%s%lld", zSep, v);
+ }
+ }else if( (iVal&1)==0 ){
+ printf("%sx'", zSep);
+ for(j=0; j<sz; j++){
+ printf("%02x", aSample[y+j]);
+ }
+ printf("'");
+ }else{
+ printf("%s\"", zSep);
+ for(j=0; j<sz; j++){
+ char c = (char)aSample[y+j];
+ if( isprint(c) ){
+ if( c=='"' || c=='\\' ) putchar('\\');
+ putchar(c);
+ }else if( c=='\n' ){
+ printf("\\n");
+ }else if( c=='\t' ){
+ printf("\\t");
+ }else if( c=='\r' ){
+ printf("\\r");
+ }else{
+ printf("\\%03o", c);
+ }
+ }
+ printf("\"");
+ }
+ zSep = ",";
+ y += sz;
+ }
+ printf("\n");
+ }
+ sqlite3_free(zIdx);
+ sqlite3_finalize(pStmt);
+ sqlite3_close(db);
+ return 0;
+}
diff --git a/tool/showwal.c b/tool/showwal.c
index 2888c10..6dc1de1 100644
--- a/tool/showwal.c
+++ b/tool/showwal.c
@@ -6,7 +6,13 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+
+#if !defined(_MSC_VER)
#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
@@ -172,7 +178,7 @@ static void print_decode_line(
int val = aData[ofst];
char zBuf[100];
sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
- i = strlen(zBuf);
+ i = (int)strlen(zBuf);
for(j=1; j<4; j++){
if( j>=nByte ){
sprintf(&zBuf[i], " ");
@@ -180,7 +186,7 @@ static void print_decode_line(
sprintf(&zBuf[i], " %02x", aData[ofst+j]);
val = val*256 + aData[ofst+j];
}
- i += strlen(&zBuf[i]);
+ i += (int)strlen(&zBuf[i]);
}
if( asHex ){
sprintf(&zBuf[i], " 0x%08x", val);
@@ -273,14 +279,14 @@ static void print_wal_header(Cksum *pCksum){
/*
** Describe cell content.
*/
-static int describeContent(
+static i64 describeContent(
unsigned char *a, /* Cell content */
- int nLocal, /* Bytes in a[] */
+ i64 nLocal, /* Bytes in a[] */
char *zDesc /* Write description here */
){
int nDesc = 0;
- int n, i, j;
- i64 x, v;
+ int n, j;
+ i64 i, x, v;
const unsigned char *pData;
const unsigned char *pLimit;
char sep = ' ';
@@ -320,15 +326,15 @@ static int describeContent(
}else if( x==9 ){
sprintf(zDesc, "1");
}else if( x>=12 ){
- int size = (x-12)/2;
+ i64 size = (x-12)/2;
if( (x&1)==0 ){
- sprintf(zDesc, "blob(%d)", size);
+ sprintf(zDesc, "blob(%lld)", size);
}else{
- sprintf(zDesc, "txt(%d)", size);
+ sprintf(zDesc, "txt(%lld)", size);
}
pData += size;
}
- j = strlen(zDesc);
+ j = (int)strlen(zDesc);
zDesc += j;
nDesc += j;
}
@@ -339,11 +345,11 @@ static int describeContent(
** Compute the local payload size given the total payload size and
** the page size.
*/
-static int localPayload(i64 nPayload, char cType){
- int maxLocal;
- int minLocal;
- int surplus;
- int nLocal;
+static i64 localPayload(i64 nPayload, char cType){
+ i64 maxLocal;
+ i64 minLocal;
+ i64 surplus;
+ i64 nLocal;
if( cType==13 ){
/* Table leaf */
maxLocal = pagesize-35;
@@ -370,19 +376,19 @@ static int localPayload(i64 nPayload, char cType){
**
** The return value is the local cell size.
*/
-static int describeCell(
+static i64 describeCell(
unsigned char cType, /* Page type */
unsigned char *a, /* Cell content */
int showCellContent, /* Show cell content if true */
char **pzDesc /* Store description here */
){
int i;
- int nDesc = 0;
+ i64 nDesc = 0;
int n = 0;
int leftChild;
i64 nPayload;
i64 rowid;
- int nLocal;
+ i64 nLocal;
static char zDesc[1000];
i = 0;
if( cType<=5 ){
@@ -479,17 +485,17 @@ static void decode_btree_page(
for(i=0; i<nCell; i++){
int cofst = iCellPtr + i*2;
char *zDesc;
- int n;
+ i64 n;
cofst = a[cofst]*256 + a[cofst+1];
n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
if( showMap ){
char zBuf[30];
- memset(&zMap[cofst], '*', n);
+ memset(&zMap[cofst], '*', (size_t)n);
zMap[cofst] = '[';
zMap[cofst+n-1] = ']';
sprintf(zBuf, "%d", i);
- j = strlen(zBuf);
+ j = (int)strlen(zBuf);
if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
}
printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
diff --git a/tool/spaceanal.tcl b/tool/spaceanal.tcl
index 6988f6e..a227b85 100644
--- a/tool/spaceanal.tcl
+++ b/tool/spaceanal.tcl
@@ -199,15 +199,17 @@ foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
# is.
#
set gap_cnt 0
- set pglist [db eval {
- SELECT pageno FROM temp.dbstat WHERE name = $name ORDER BY rowid
- }]
- set prev [lindex $pglist 0]
- foreach pgno [lrange $pglist 1 end] {
- if {$pgno != $prev+1} {incr gap_cnt}
- set prev $pgno
+ set prev 0
+ db eval {
+ SELECT pageno, pagetype FROM temp.dbstat
+ WHERE name=$name
+ ORDER BY pageno
+ } {
+ if {$prev>0 && $pagetype=="leaf" && $pageno!=$prev+1} {
+ incr gap_cnt
+ }
+ set prev $pageno
}
-
mem eval {
INSERT INTO space_used VALUES(
$name,
@@ -246,8 +248,19 @@ mem function int integerify
# [quote {hello world's}] == {'hello world''s'}
#
proc quote {txt} {
- regsub -all ' $txt '' q
- return '$q'
+ return [string map {' ''} $txt]
+}
+
+# Output a title line
+#
+proc titleline {title} {
+ if {$title==""} {
+ puts [string repeat * 79]
+ } else {
+ set len [string length $title]
+ set stars [string repeat * [expr 79-$len-5]]
+ puts "*** $title $stars"
+ }
}
# Generate a single line of output in the statistics section of the
@@ -255,7 +268,7 @@ proc quote {txt} {
#
proc statline {title value {extra {}}} {
set len [string length $title]
- set dots [string range {......................................} $len end]
+ set dots [string repeat . [expr 50-$len]]
set len [string length $value]
set sp2 [string range { } $len end]
if {$extra ne ""} {
@@ -287,7 +300,7 @@ proc divide {num denom} {
# Generate a subreport that covers some subset of the database.
# the $where clause determines which subset to analyze.
#
-proc subreport {title where} {
+proc subreport {title where showFrag} {
global pageSize file_pgcnt compressOverhead
# Query the in-memory database for the sum of various statistics
@@ -319,9 +332,7 @@ proc subreport {title 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"
+ titleline $title
puts ""
# Calculate statistics and store the results in TCL variables, as follows:
@@ -375,9 +386,9 @@ proc subreport {title where} {
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
+ if {$showFrag && $total_pages>1} {
+ set fragmentation [percent $gap_cnt [expr {$total_pages-1}]]
+ statline {Non-sequential pages} $gap_cnt $fragmentation
}
statline {Maximum payload per entry} $mx_payload
statline {Entries that use overflow} $ovfl_cnt $ovfl_cnt_percent
@@ -490,9 +501,6 @@ set user_percent [percent $user_payload $file_bytes]
# Output the summary statistics calculated above.
#
puts "/** Disk-Space Utilization Report For $root_filename"
-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
@@ -503,8 +511,8 @@ 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 {Number of defined indices} $nmanindex
+statline {Number of implied indices} $nautoindex
if {$isCompressed} {
statline {Size of uncompressed content in bytes} $file_bytes
set efficiency [percent $true_file_size $file_bytes]
@@ -517,16 +525,27 @@ statline {Bytes of user payload stored} $user_payload $user_percent
# Output table rankings
#
puts ""
-puts "*** Page counts for all tables with their indices ********************"
+titleline "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]
}
+puts ""
+titleline "Page counts for all tables and indices separately"
+puts ""
+mem eval {
+ SELECT
+ upper(name) AS nm,
+ int(int_pages+leaf_pages+ovfl_pages) AS size
+ FROM space_used
+ ORDER BY size+0 DESC, name} {} {
+ statline $nm $size [percent $size $file_pgcnt]
+}
if {$isCompressed} {
puts ""
- puts "*** Bytes of disk space used after compression ***********************"
+ titleline "Bytes of disk space used after compression"
puts ""
set csum 0
mem eval {SELECT tblname,
@@ -546,31 +565,40 @@ if {$isCompressed} {
# Output subreports
#
if {$nindex>0} {
- subreport {All tables and indices} 1
+ subreport {All tables and indices} 1 0
}
-subreport {All tables} {NOT is_index}
+subreport {All tables} {NOT is_index} 0
if {$nindex>0} {
- subreport {All indices} {is_index}
+ subreport {All indices} {is_index} 0
}
-foreach tbl [mem eval {SELECT name FROM space_used WHERE NOT is_index
+foreach tbl [mem eval {SELECT DISTINCT tblname name FROM space_used
ORDER BY name}] {
- regsub ' $tbl '' qn
+ set qn [quote $tbl]
set name [string toupper $tbl]
- set n [mem eval "SELECT count(*) FROM space_used WHERE tblname='$qn'"]
+ set n [mem eval {SELECT count(*) FROM space_used WHERE tblname=$tbl}]
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"
+ set idxlist [mem eval "SELECT name FROM space_used
+ WHERE tblname='$qn' AND is_index
+ ORDER BY 1"]
+ subreport "Table $name and all its indices" "tblname='$qn'" 0
+ subreport "Table $name w/o any indices" "name='$qn'" 1
+ if {[llength $idxlist]>1} {
+ subreport "Indices of table $name" "tblname='$qn' AND is_index" 0
+ }
+ foreach idx $idxlist {
+ set qidx [quote $idx]
+ subreport "Index [string toupper $idx] of table $name" "name='$qidx'" 1
+ }
} else {
- subreport "Table $name" "name='$qn'"
+ subreport "Table $name" "name='$qn'" 1
}
}
# Output instructions on what the numbers above mean.
#
+puts ""
+titleline Definitions
puts {
-*** Definitions ******************************************************
-
Page size in bytes
The number of bytes in a single page of the database file.
@@ -607,11 +635,11 @@ Number of indices
The total number of indices in the database.
-Number of named indices
+Number of defined indices
The number of indices created using an explicit CREATE INDEX statement.
-Automatically generated indices
+Number of implied indices
The number of indices used to implement PRIMARY KEY or UNIQUE constraints
on tables.
@@ -660,13 +688,16 @@ Average unused bytes per entry
category on a per-entry basis. This is the number of unused bytes on
all pages divided by the number of entries.
-Fragmentation
+Non-sequential pages
- 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.
+ The number of pages in the table or index that are out of sequence.
+ Many filesystems are optimized for sequential file access so a small
+ number of non-sequential pages might result in faster queries,
+ especially for larger database files that do not fit in the disk cache.
+ Note that after running VACUUM, the root page of each table or index is
+ at the beginning of the database file and all other pages are in a
+ separate part of the database file, resulting in a single non-
+ sequential page.
Maximum payload per entry
@@ -722,7 +753,7 @@ Unused bytes on all pages
# Output a dump of the in-memory database. This can be used for more
# complex offline analysis.
#
-puts "**********************************************************************"
+titleline {}
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:"
@@ -735,7 +766,7 @@ mem eval {SELECT * FROM space_used} x {
set sep (
foreach col $x(*) {
set v $x($col)
- if {$v=="" || ![string is double $v]} {set v [quote $v]}
+ if {$v=="" || ![string is double $v]} {set v '[quote $v]'}
puts -nonewline $sep$v
set sep ,
}
diff --git a/tool/vdbe-compress.tcl b/tool/vdbe-compress.tcl
index 95cc1eb..a349830 100644
--- a/tool/vdbe-compress.tcl
+++ b/tool/vdbe-compress.tcl
@@ -13,7 +13,7 @@
# Script usage:
#
# mv vdbe.c vdbe.c.template
-# tclsh vdbe-compress.tcl <vdbe.c.template >vdbe.c
+# tclsh vdbe-compress.tcl $CFLAGS <vdbe.c.template >vdbe.c
#
# Modifications made:
#
@@ -42,6 +42,16 @@ set unionDef {} ;# C code of the union
set afterUnion {} ;# C code after the union
set sCtr 0 ;# Context counter
+# If the SQLITE_SMALL_STACK compile-time option is missing, then
+# this transformation becomes a no-op.
+#
+if {![regexp {SQLITE_SMALL_STACK} $argv]} {
+ while {![eof stdin]} {
+ puts [gets stdin]
+ }
+ exit
+}
+
# Read program text up to the spot where the union should be
# inserted.
#
diff --git a/tool/vdbe_profile.tcl b/tool/vdbe_profile.tcl
new file mode 100644
index 0000000..fb1f955
--- /dev/null
+++ b/tool/vdbe_profile.tcl
@@ -0,0 +1,82 @@
+#!/bin/tclsh
+#
+# Run this script in the same directory as the "vdbe_profile.out" file.
+# This script summarizes the results contained in that file.
+#
+if {![file readable vdbe_profile.out]} {
+ error "run this script in the same directory as the vdbe_profile.out file"
+}
+set in [open vdbe_profile.out r]
+set stmt {}
+set allstmt {}
+while {![eof $in]} {
+ set line [gets $in]
+ if {$line==""} continue
+ if {[regexp {^---- } $line]} {
+ set stmt [lindex $line 1]
+ if {[info exists cnt($stmt)]} {
+ incr cnt($stmt)
+ set firsttime 0
+ } else {
+ set cnt($stmt) 1
+ set sql($stmt) {}
+ set firsttime 1
+ lappend allstmt $stmt
+ }
+ continue;
+ }
+ if {[regexp {^-- } $line]} {
+ if {$firsttime} {
+ append sql($stmt) [string range $line 3 end]\n
+ }
+ continue
+ }
+ if {![regexp {^ *\d+ *\d+ *\d+ *\d+ ([A-Z].*)} $line all detail]} continue
+ set c [lindex $line 0]
+ set t [lindex $line 1]
+ set addr [lindex $line 3]
+ set op [lindex $line 4]
+ if {[info exists opcnt($op)]} {
+ incr opcnt($op) $c
+ incr opcycle($op) $t
+ } else {
+ set opcnt($op) $c
+ set opcycle($op) $t
+ }
+ if {[info exists stat($stmt,$addr)]} {
+ foreach {cx tx detail} $stat($stmt,$addr) break
+ incr cx $c
+ incr tx $t
+ set stat($stmt,$addr) [list $cx $tx $detail]
+ } else {
+ set stat($stmt,$addr) [list $c $t $detail]
+ }
+}
+close $in
+
+foreach stmt $allstmt {
+ puts "********************************************************************"
+ puts [string trim $sql($stmt)]
+ puts "Execution count: $cnt($stmt)"
+ for {set i 0} {[info exists stat($stmt,$i)]} {incr i} {
+ foreach {cx tx detail} $stat($stmt,$i) break
+ if {$cx==0} {
+ set ax 0
+ } else {
+ set ax [expr {$tx/$cx}]
+ }
+ puts [format {%8d %12d %12d %4d %s} $cx $tx $ax $i $detail]
+ }
+}
+puts "********************************************************************"
+puts "OPCODES:"
+foreach op [lsort [array names opcnt]] {
+ set cx $opcnt($op)
+ set tx $opcycle($op)
+ if {$cx==0} {
+ set ax 0
+ } else {
+ set ax [expr {$tx/$cx}]
+ }
+ puts [format {%8d %12d %12d %s} $cx $tx $ax $op]
+}
diff --git a/tool/warnings.sh b/tool/warnings.sh
index 78cfb55..5d71361 100644
--- a/tool/warnings.sh
+++ b/tool/warnings.sh
@@ -4,14 +4,14 @@
# compiler warnings in SQLite.
#
rm -f sqlite3.c
-make sqlite3.c-debug
+make sqlite3.c
echo '********** No optimizations. Includes FTS4 and RTREE *********'
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
-ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \
sqlite3.c
-echo '********** No optimizations. ENABLE_STAT3. THREADSAFE=0 *******'
+echo '********** No optimizations. ENABLE_STAT4. THREADSAFE=0 *******'
gcc -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
- -ansi -DSQLITE_ENABLE_STAT3 -DSQLITE_THREADSAFE=0 \
+ -ansi -DSQLITE_ENABLE_STAT4 -DSQLITE_THREADSAFE=0 \
sqlite3.c
echo '********** Optimized -O3. Includes FTS4 and RTREE ************'
gcc -O3 -c -Wshadow -Wall -Wextra -pedantic-errors -Wno-long-long -std=c89 \
diff --git a/tool/win/sqlite.vsix b/tool/win/sqlite.vsix
index 93eefac..1450011 100644
--- a/tool/win/sqlite.vsix
+++ b/tool/win/sqlite.vsix
Binary files differ