summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2018-06-08 04:23:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2018-06-08 04:23:52 +0000
commit5b88439d6c7982b3db1383139e37a1d265ea26cf (patch)
treebbfaafaafd2a9f6d61dcaef5706f4dd51687f19c
parentReleasing progress-linux version 3.23.1-1~dschinn1. (diff)
downloadsqlite3-5b88439d6c7982b3db1383139e37a1d265ea26cf.zip
sqlite3-5b88439d6c7982b3db1383139e37a1d265ea26cf.tar.xz
Merging upstream version 3.24.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--Makefile.in9
-rw-r--r--Makefile.msc11
-rw-r--r--VERSION2
-rw-r--r--autoconf/Makefile.am4
-rw-r--r--autoconf/Makefile.fallback19
-rw-r--r--autoconf/Makefile.msc2
-rw-r--r--autoconf/configure.ac20
-rwxr-xr-xconfigure18
-rw-r--r--doc/lemon.html27
-rw-r--r--ext/expert/expert1.test50
-rw-r--r--ext/expert/sqlite3expert.c12
-rw-r--r--ext/fts3/fts3.c2
-rw-r--r--ext/fts3/fts3Int.h2
-rw-r--r--ext/fts3/fts3_expr.c89
-rw-r--r--ext/fts5/test/fts5plan.test27
-rw-r--r--ext/fts5/test/fts5unicode.test27
-rw-r--r--ext/misc/closure.c18
-rw-r--r--ext/misc/completion.c38
-rw-r--r--ext/misc/csv.c10
-rw-r--r--ext/misc/dbdump.c37
-rw-r--r--ext/misc/json1.c2
-rw-r--r--ext/misc/templatevtab.c267
-rw-r--r--ext/rbu/rbu.c46
-rw-r--r--ext/rbu/rbu1.test1
-rw-r--r--ext/rbu/rbu_common.tcl16
-rw-r--r--ext/rbu/rbusplit.test95
-rw-r--r--ext/rbu/sqlite3rbu.c20
-rw-r--r--ext/rtree/rtree.c330
-rw-r--r--ext/rtree/rtree1.test48
-rw-r--r--ext/rtree/rtree3.test2
-rw-r--r--ext/rtree/rtree6.test30
-rw-r--r--ext/rtree/rtree8.test39
-rw-r--r--ext/rtree/rtreeC.test122
-rw-r--r--ext/rtree/rtreeH.test80
-rw-r--r--main.mk6
-rw-r--r--manifest365
-rw-r--r--manifest.uuid2
-rw-r--r--src/attach.c18
-rw-r--r--src/auth.c2
-rw-r--r--src/btmutex.c6
-rw-r--r--src/btree.c212
-rw-r--r--src/btree.h21
-rw-r--r--src/build.c70
-rw-r--r--src/callback.c2
-rw-r--r--src/ctime.c3
-rw-r--r--src/delete.c27
-rw-r--r--src/expr.c56
-rw-r--r--src/fkey.c7
-rw-r--r--src/func.c10
-rw-r--r--src/global.c3
-rw-r--r--src/insert.c268
-rw-r--r--src/loadext.c21
-rw-r--r--src/main.c73
-rw-r--r--src/mutex.c1
-rw-r--r--src/os_unix.c2
-rw-r--r--src/os_win.c69
-rw-r--r--src/parse.y94
-rw-r--r--src/pragma.c20
-rw-r--r--src/prepare.c17
-rw-r--r--src/printf.c140
-rw-r--r--src/resolve.c145
-rw-r--r--src/select.c911
-rw-r--r--src/shell.c.in557
-rw-r--r--src/sqlite.h.in269
-rw-r--r--src/sqlite3ext.h30
-rw-r--r--src/sqliteInt.h131
-rw-r--r--src/test1.c24
-rw-r--r--src/test_malloc.c18
-rw-r--r--src/tokenize.c6
-rw-r--r--src/treeview.c57
-rw-r--r--src/trigger.c11
-rw-r--r--src/update.c266
-rw-r--r--src/upsert.c250
-rw-r--r--src/vacuum.c12
-rw-r--r--src/vdbe.c52
-rw-r--r--src/vdbe.h22
-rw-r--r--src/vdbeInt.h9
-rw-r--r--src/vdbeapi.c22
-rw-r--r--src/vdbeaux.c172
-rw-r--r--src/vdbemem.c8
-rw-r--r--src/vdbetrace.c32
-rw-r--r--src/vtab.c23
-rw-r--r--src/where.c23
-rw-r--r--src/whereInt.h4
-rw-r--r--src/wherecode.c82
-rw-r--r--src/whereexpr.c1
-rw-r--r--test/analyze3.test30
-rw-r--r--test/analyze4.test2
-rw-r--r--test/analyze6.test30
-rw-r--r--test/analyze7.test32
-rw-r--r--test/analyze8.test22
-rw-r--r--test/analyze9.test42
-rw-r--r--test/analyzeA.test16
-rw-r--r--test/analyzeD.test16
-rw-r--r--test/analyzeF.test4
-rw-r--r--test/autoinc.test172
-rw-r--r--test/autoindex1.test57
-rw-r--r--test/autoindex3.test5
-rw-r--r--test/autoindex5.test5
-rw-r--r--test/bestindex1.test29
-rw-r--r--test/bestindex2.test43
-rw-r--r--test/bestindex3.test30
-rw-r--r--test/bigmmap.test12
-rw-r--r--test/closure01.test19
-rw-r--r--test/cost.test87
-rw-r--r--test/coveridxscan.test9
-rw-r--r--test/csv01.test16
-rw-r--r--test/delete.test12
-rw-r--r--test/e_createtable.test10
-rw-r--r--test/e_fkey.test46
-rw-r--r--test/eqp.test440
-rw-r--r--test/fts3aux1.test50
-rw-r--r--test/fts3expr.test2
-rw-r--r--test/fts3expr4.test3
-rw-r--r--test/fts3join.test8
-rw-r--r--test/fts3query.test20
-rw-r--r--test/fuzz_malloc.test15
-rw-r--r--test/fuzzcheck.c36
-rw-r--r--test/fuzzdata5.dbbin6964224 -> 7195648 bytes
-rw-r--r--test/fuzzdata6.dbbin0 -> 1785856 bytes
-rw-r--r--test/index6.test5
-rw-r--r--test/index7.test5
-rw-r--r--test/indexedby.test153
-rw-r--r--test/indexexpr2.test18
-rw-r--r--test/istrue.test15
-rw-r--r--test/join2.test47
-rw-r--r--test/join5.test80
-rw-r--r--test/json101.test30
-rw-r--r--test/malloc5.test7
-rw-r--r--test/mallocK.test9
-rw-r--r--test/misc7.test33
-rw-r--r--test/orderby1.test24
-rw-r--r--test/oserror.test4
-rw-r--r--test/pager1.test2
-rw-r--r--test/permutations.test20
-rw-r--r--test/resetdb.test158
-rw-r--r--test/rollback2.test4
-rw-r--r--test/rowvalue.test10
-rw-r--r--test/rowvalue4.test88
-rw-r--r--test/scanstatus.test12
-rw-r--r--test/selectA.test13
-rw-r--r--test/selectD.test2
-rw-r--r--test/shell1.test2
-rw-r--r--test/skipscan2.test2
-rw-r--r--test/skipscan6.test13
-rw-r--r--test/soak.test4
-rw-r--r--test/sorterref.test50
-rw-r--r--test/speedtest1.c59
-rw-r--r--test/tester.tcl73
-rw-r--r--test/tkt-385a5b56b9.test21
-rw-r--r--test/tkt-78e04e52ea.test10
-rw-r--r--test/tkt-b75a9ca6b0.test22
-rw-r--r--test/tkt3442.test31
-rw-r--r--test/tpch01.test12
-rw-r--r--test/trigger1.test40
-rw-r--r--test/triggerE.test1
-rw-r--r--test/unordered.test29
-rw-r--r--test/update.test22
-rw-r--r--test/upsert1.test115
-rw-r--r--test/upsert2.test170
-rw-r--r--test/upsert3.test58
-rw-r--r--test/upsert4.test407
-rw-r--r--test/upsertfault.test38
-rw-r--r--test/where.test45
-rw-r--r--test/where3.test58
-rw-r--r--test/where7.test14
-rw-r--r--test/where8.test5
-rw-r--r--test/where9.test80
-rw-r--r--test/whereG.test20
-rw-r--r--test/whereI.test12
-rw-r--r--test/whereJ.test8
-rw-r--r--test/wherelimit.test17
-rw-r--r--test/with1.test13
-rw-r--r--test/with3.test51
-rw-r--r--test/without_rowid1.test12
-rw-r--r--test/wordcount.c27
-rw-r--r--test/zipfile2.test12
-rw-r--r--tool/lemon.c104
-rw-r--r--tool/lempar.c140
-rw-r--r--tool/mkkeywordhash.c17
-rw-r--r--tool/mkmsvcmin.tcl2
-rw-r--r--tool/mkopcodeh.tcl3
-rw-r--r--tool/mksqlite3c.tcl1
-rw-r--r--tool/speed-check.sh6
-rw-r--r--tool/sqldiff.c40
-rw-r--r--www/about.html3
-rw-r--r--www/aff_short.html2
-rw-r--r--www/amalgamation.html2
-rw-r--r--www/appfileformat.html3
-rw-r--r--www/assert.html4
-rw-r--r--www/bindptr.html4
-rw-r--r--www/books.html37
-rw-r--r--www/c3ref/c_abort_rollback.html4
-rw-r--r--www/c3ref/c_config_covering_index_scan.html22
-rw-r--r--www/c3ref/c_dbconfig_enable_fkey.html20
-rw-r--r--www/c3ref/c_index_scan_unique.html6
-rw-r--r--www/c3ref/c_source_id.html6
-rw-r--r--www/c3ref/c_testctrl_always.html2
-rw-r--r--www/c3ref/c_win32_data_directory_type.html109
-rw-r--r--www/c3ref/constlist.html38
-rw-r--r--www/c3ref/context.html30
-rw-r--r--www/c3ref/funclist.html51
-rw-r--r--www/c3ref/keyword_check.html151
-rw-r--r--www/c3ref/objlist.html31
-rw-r--r--www/c3ref/serialize.html2
-rw-r--r--www/c3ref/sqlite3.html30
-rw-r--r--www/c3ref/stmt.html60
-rw-r--r--www/c3ref/str.html167
-rw-r--r--www/c3ref/str_append.html138
-rw-r--r--www/c3ref/str_errcode.html132
-rw-r--r--www/c3ref/str_finish.html114
-rw-r--r--www/c3ref/str_new.html126
-rw-r--r--www/c3ref/value.html30
-rw-r--r--www/c3ref/vtab_nochange.html6
-rw-r--r--www/c3ref/win32_set_directory.html126
-rw-r--r--www/capi3ref.html603
-rw-r--r--www/changes.html97
-rw-r--r--www/chronology.html6
-rw-r--r--www/cli.html9
-rw-r--r--www/compile.html31
-rw-r--r--www/copyright.html21
-rw-r--r--www/crew.html2
-rw-r--r--www/doc_backlink_crossref.html342
-rw-r--r--www/doc_keyword_crossref.html2
-rw-r--r--www/doc_pagelink_crossref.html148
-rw-r--r--www/doc_target_crossref.html21
-rw-r--r--www/doclist.html2
-rw-r--r--www/eqp.html283
-rw-r--r--www/famous.html16
-rw-r--r--www/footprint.html2
-rw-r--r--www/images/books/sanderson2018.jpgbin0 -> 5148 bytes
-rw-r--r--www/images/foreignlogos/loc.gifbin0 -> 2634 bytes
-rw-r--r--www/images/foreignlogos/loc.jpgbin0 -> 5429 bytes
-rw-r--r--www/images/loc.jpgbin0 -> 78248 bytes
-rw-r--r--www/images/syntax/alter-table-stmt.gifbin9431 -> 8766 bytes
-rw-r--r--www/images/syntax/analyze-stmt.gifbin7137 -> 6728 bytes
-rw-r--r--www/images/syntax/attach-stmt.gifbin5490 -> 5367 bytes
-rw-r--r--www/images/syntax/begin-stmt.gifbin6729 -> 6722 bytes
-rw-r--r--www/images/syntax/column-constraint.gifbin19406 -> 18373 bytes
-rw-r--r--www/images/syntax/column-def.gifbin5143 -> 5187 bytes
-rw-r--r--www/images/syntax/column-name-list.gifbin4603 -> 4583 bytes
-rw-r--r--www/images/syntax/comment-syntax.gifbin8578 -> 8283 bytes
-rw-r--r--www/images/syntax/commit-stmt.gifbin5151 -> 5122 bytes
-rw-r--r--www/images/syntax/common-table-expression.gifbin6672 -> 6725 bytes
-rw-r--r--www/images/syntax/compound-operator.gifbin6150 -> 6114 bytes
-rw-r--r--www/images/syntax/compound-select-stmt.gifbin16903 -> 16600 bytes
-rw-r--r--www/images/syntax/conflict-clause.gifbin8054 -> 7994 bytes
-rw-r--r--www/images/syntax/create-index-stmt.gifbin12194 -> 12375 bytes
-rw-r--r--www/images/syntax/create-table-stmt.gifbin15181 -> 14950 bytes
-rw-r--r--www/images/syntax/create-trigger-stmt.gifbin24299 -> 23965 bytes
-rw-r--r--www/images/syntax/create-view-stmt.gifbin12494 -> 11720 bytes
-rw-r--r--www/images/syntax/create-virtual-table-stmt.gifbin11779 -> 11399 bytes
-rw-r--r--www/images/syntax/cte-table-name.gifbin5571 -> 5268 bytes
-rw-r--r--www/images/syntax/delete-stmt-limited.gifbin12587 -> 12709 bytes
-rw-r--r--www/images/syntax/delete-stmt.gifbin6999 -> 6735 bytes
-rw-r--r--www/images/syntax/detach-stmt.gifbin4964 -> 4955 bytes
-rw-r--r--www/images/syntax/drop-index-stmt.gifbin6600 -> 6484 bytes
-rw-r--r--www/images/syntax/drop-table-stmt.gifbin6491 -> 6418 bytes
-rw-r--r--www/images/syntax/drop-trigger-stmt.gifbin6650 -> 6600 bytes
-rw-r--r--www/images/syntax/drop-view-stmt.gifbin6544 -> 6377 bytes
-rw-r--r--www/images/syntax/expr.gifbin50809 -> 49323 bytes
-rw-r--r--www/images/syntax/factored-select-stmt.gifbin13369 -> 13048 bytes
-rw-r--r--www/images/syntax/foreign-key-clause.gifbin20531 -> 20380 bytes
-rw-r--r--www/images/syntax/indexed-column.gifbin7288 -> 7095 bytes
-rw-r--r--www/images/syntax/insert-stmt.gifbin22580 -> 24239 bytes
-rw-r--r--www/images/syntax/join-clause.gifbin5729 -> 5494 bytes
-rw-r--r--www/images/syntax/join-constraint.gifbin6842 -> 6440 bytes
-rw-r--r--www/images/syntax/join-operator.gifbin7876 -> 7827 bytes
-rw-r--r--www/images/syntax/numeric-literal.gifbin10986 -> 10736 bytes
-rw-r--r--www/images/syntax/ordering-term.gifbin6316 -> 6018 bytes
-rw-r--r--www/images/syntax/pragma-stmt.gifbin7536 -> 7135 bytes
-rw-r--r--www/images/syntax/pragma-value.gifbin5082 -> 4845 bytes
-rw-r--r--www/images/syntax/qualified-table-name.gifbin7407 -> 8571 bytes
-rw-r--r--www/images/syntax/raise-function.gifbin7990 -> 7970 bytes
-rw-r--r--www/images/syntax/recursive-cte.gifbin7238 -> 6825 bytes
-rw-r--r--www/images/syntax/reindex-stmt.gifbin7318 -> 6830 bytes
-rw-r--r--www/images/syntax/release-stmt.gifbin5000 -> 4990 bytes
-rw-r--r--www/images/syntax/result-column.gifbin6839 -> 6958 bytes
-rw-r--r--www/images/syntax/rollback-stmt.gifbin6463 -> 6479 bytes
-rw-r--r--www/images/syntax/savepoint-stmt.gifbin4358 -> 4237 bytes
-rw-r--r--www/images/syntax/select-core.gifbin17531 -> 17343 bytes
-rw-r--r--www/images/syntax/select-stmt.gifbin27785 -> 27836 bytes
-rw-r--r--www/images/syntax/signed-number.gifbin4784 -> 4775 bytes
-rw-r--r--www/images/syntax/simple-select-stmt.gifbin12158 -> 11988 bytes
-rw-r--r--www/images/syntax/sql-stmt-list.gifbin4055 -> 4018 bytes
-rw-r--r--www/images/syntax/sql-stmt.gifbin24459 -> 21719 bytes
-rw-r--r--www/images/syntax/table-constraint.gifbin13709 -> 13662 bytes
-rw-r--r--www/images/syntax/table-or-subquery.gifbin21484 -> 20755 bytes
-rw-r--r--www/images/syntax/type-name.gifbin7130 -> 7264 bytes
-rw-r--r--www/images/syntax/update-stmt-limited.gifbin22306 -> 20900 bytes
-rw-r--r--www/images/syntax/update-stmt.gifbin15870 -> 14650 bytes
-rw-r--r--www/images/syntax/upsert-clause.gifbin0 -> 13289 bytes
-rw-r--r--www/images/syntax/vacuum-stmt.gifbin4484 -> 4489 bytes
-rw-r--r--www/images/syntax/with-clause.gifbin6818 -> 6825 bytes
-rw-r--r--www/index.html2
-rw-r--r--www/keyword_index.html37
-rw-r--r--www/lang.html31
-rw-r--r--www/lang_UPSERT.html265
-rw-r--r--www/lang_aggfunc.html30
-rw-r--r--www/lang_altertable.html80
-rw-r--r--www/lang_analyze.html4
-rw-r--r--www/lang_attach.html60
-rw-r--r--www/lang_comment.html4
-rw-r--r--www/lang_conflict.html15
-rw-r--r--www/lang_corefunc.html30
-rw-r--r--www/lang_createindex.html64
-rw-r--r--www/lang_createtable.html136
-rw-r--r--www/lang_createtrigger.html123
-rw-r--r--www/lang_createview.html60
-rw-r--r--www/lang_createvtab.html4
-rw-r--r--www/lang_delete.html108
-rw-r--r--www/lang_detach.html4
-rw-r--r--www/lang_dropindex.html4
-rw-r--r--www/lang_droptable.html4
-rw-r--r--www/lang_droptrigger.html4
-rw-r--r--www/lang_dropview.html4
-rw-r--r--www/lang_expr.html56
-rw-r--r--www/lang_indexedby.html4
-rw-r--r--www/lang_insert.html102
-rw-r--r--www/lang_reindex.html4
-rw-r--r--www/lang_savepoint.html12
-rw-r--r--www/lang_select.html408
-rw-r--r--www/lang_transaction.html12
-rw-r--r--www/lang_update.html112
-rw-r--r--www/lang_vacuum.html4
-rw-r--r--www/lang_with.html72
-rw-r--r--www/locrsf.html172
-rw-r--r--www/lts.html8
-rw-r--r--www/news.html8
-rw-r--r--www/onefile.html6
-rw-r--r--www/opcode.html28
-rw-r--r--www/optoverview.html4
-rw-r--r--www/partialindex.html64
-rw-r--r--www/pragma.html58
-rw-r--r--www/printf.html4
-rw-r--r--www/prosupport.html2
-rw-r--r--www/quirks.html4
-rw-r--r--www/releaselog/3_13_0.html25
-rw-r--r--www/releaselog/3_20_0.html2
-rw-r--r--www/releaselog/3_20_1.html2
-rw-r--r--www/releaselog/3_23_1.html2
-rw-r--r--www/releaselog/3_24_0.html168
-rw-r--r--www/releaselog/current.html178
-rw-r--r--www/requirements.html738
-rw-r--r--www/rescode.html60
-rw-r--r--www/rowidtable.html2
-rw-r--r--www/rtree.html52
-rw-r--r--www/search.d/search.dbbin7884800 -> 7979008 bytes
-rw-r--r--www/session.html150
-rw-r--r--www/session/changeset_iter.html30
-rw-r--r--www/session/constlist.html30
-rw-r--r--www/session/funclist.html30
-rw-r--r--www/session/objlist.html30
-rw-r--r--www/session/session.html30
-rw-r--r--www/sitemap.html7
-rw-r--r--www/sqlar.html22
-rw-r--r--www/syntax.html31
-rw-r--r--www/syntax/column-name-list.html4
-rw-r--r--www/syntax/common-table-expression.html2
-rw-r--r--www/syntax/compound-operator.html2
-rw-r--r--www/syntax/expr.html4
-rw-r--r--www/syntax/indexed-column.html4
-rw-r--r--www/syntax/insert-stmt.html2
-rw-r--r--www/syntax/join-clause.html2
-rw-r--r--www/syntax/join-constraint.html2
-rw-r--r--www/syntax/join-operator.html2
-rw-r--r--www/syntax/literal-value.html2
-rw-r--r--www/syntax/ordering-term.html2
-rw-r--r--www/syntax/raise-function.html2
-rw-r--r--www/syntax/result-column.html2
-rw-r--r--www/syntax/select-stmt.html2
-rw-r--r--www/syntax/signed-number.html2
-rw-r--r--www/syntax/table-or-subquery.html2
-rw-r--r--www/syntax/type-name.html2
-rw-r--r--www/syntax/upsert-clause.html103
-rw-r--r--www/syntaxdiagrams.html47
-rw-r--r--www/tempfiles.html9
-rw-r--r--www/th3.html127
-rw-r--r--www/toc.dbbin77824 -> 77824 bytes
-rw-r--r--www/versionnumbers.html2
-rw-r--r--www/vtab.html20
-rw-r--r--www/vtablist.html258
-rw-r--r--www/wal.html73
-rw-r--r--www/walformat.html14
-rw-r--r--www/whynotgit.html295
385 files changed, 12906 insertions, 5028 deletions
diff --git a/Makefile.in b/Makefile.in
index 96ef340..ac84fe8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -187,7 +187,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \
random.lo resolve.lo rowset.lo rtree.lo \
sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
- update.lo util.lo vacuum.lo \
+ update.lo upsert.lo util.lo vacuum.lo \
vdbe.lo vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
utf.lo vtab.lo
@@ -284,6 +284,7 @@ SRC = \
$(TOP)/src/trigger.c \
$(TOP)/src/utf.c \
$(TOP)/src/update.c \
+ $(TOP)/src/upsert.c \
$(TOP)/src/util.c \
$(TOP)/src/vacuum.c \
$(TOP)/src/vdbe.c \
@@ -571,7 +572,8 @@ FUZZDATA = \
$(TOP)/test/fuzzdata2.db \
$(TOP)/test/fuzzdata3.db \
$(TOP)/test/fuzzdata4.db \
- $(TOP)/test/fuzzdata5.db
+ $(TOP)/test/fuzzdata5.db \
+ $(TOP)/test/fuzzdata6.db
# Standard options to testfixture
#
@@ -914,6 +916,9 @@ trigger.lo: $(TOP)/src/trigger.c $(HDR)
update.lo: $(TOP)/src/update.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/update.c
+upsert.lo: $(TOP)/src/upsert.c $(HDR)
+ $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/upsert.c
+
utf.lo: $(TOP)/src/utf.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/utf.c
diff --git a/Makefile.msc b/Makefile.msc
index 43d4a37..8513996 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -1193,7 +1193,7 @@ LIBOBJS0 = vdbe.lo parse.lo alter.lo analyze.lo attach.lo auth.lo \
random.lo resolve.lo rowset.lo rtree.lo \
sqlite3session.lo select.lo sqlite3rbu.lo status.lo \
table.lo threads.lo tokenize.lo treeview.lo trigger.lo \
- update.lo util.lo vacuum.lo \
+ update.lo upsert.lo util.lo vacuum.lo \
vdbeapi.lo vdbeaux.lo vdbeblob.lo vdbemem.lo vdbesort.lo \
vdbetrace.lo wal.lo walker.lo where.lo wherecode.lo whereexpr.lo \
utf.lo vtab.lo
@@ -1292,6 +1292,7 @@ SRC01 = \
$(TOP)\src\trigger.c \
$(TOP)\src\utf.c \
$(TOP)\src\update.c \
+ $(TOP)\src\upsert.c \
$(TOP)\src\util.c \
$(TOP)\src\vacuum.c \
$(TOP)\src\vdbe.c \
@@ -1598,7 +1599,8 @@ FUZZDATA = \
$(TOP)\test\fuzzdata2.db \
$(TOP)\test\fuzzdata3.db \
$(TOP)\test\fuzzdata4.db \
- $(TOP)\test\fuzzdata5.db
+ $(TOP)\test\fuzzdata5.db \
+ $(TOP)\test\fuzzdata6.db
# <</mark>>
# Additional compiler options for the shell. These are only effective
@@ -1672,7 +1674,7 @@ $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP)
sqlite3.def: libsqlite3.lib
echo EXPORTS > sqlite3.def
dumpbin /all libsqlite3.lib \
- | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup)?_[^@]*)(?:@\d+)?$$" \1 \
+ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+1 _?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@]*)(?:@\d+)?$$" \1 \
| sort >> sqlite3.def
# <</block2>>
@@ -1993,6 +1995,9 @@ trigger.lo: $(TOP)\src\trigger.c $(HDR)
update.lo: $(TOP)\src\update.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\update.c
+upsert.lo: $(TOP)\src\upsert.c $(HDR)
+ $(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\upsert.c
+
utf.lo: $(TOP)\src\utf.c $(HDR)
$(LTCOMPILE) $(CORE_COMPILE_OPTS) -c $(TOP)\src\utf.c
diff --git a/VERSION b/VERSION
index 9b2f2a1..954e228 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.23.1
+3.24.0
diff --git a/autoconf/Makefile.am b/autoconf/Makefile.am
index db24692..a9a8d80 100644
--- a/autoconf/Makefile.am
+++ b/autoconf/Makefile.am
@@ -1,5 +1,5 @@
-AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @ZLIB_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE
+AM_CFLAGS = @THREADSAFE_FLAGS@ @DYNAMIC_EXTENSION_FLAGS@ @FTS5_FLAGS@ @JSON1_FLAGS@ @ZLIB_FLAGS@ @SESSION_FLAGS@ -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE @DEBUG_FLAGS@
lib_LTLIBRARIES = libsqlite3.la
libsqlite3_la_SOURCES = sqlite3.c
@@ -14,7 +14,7 @@ sqlite3_CFLAGS = $(AM_CFLAGS) -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_D
include_HEADERS = sqlite3.h sqlite3ext.h
-EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs
+EXTRA_DIST = sqlite3.1 tea Makefile.msc sqlite3.rc README.txt Replace.cs Makefile.fallback
pkgconfigdir = ${libdir}/pkgconfig
pkgconfig_DATA = sqlite3.pc
diff --git a/autoconf/Makefile.fallback b/autoconf/Makefile.fallback
new file mode 100644
index 0000000..9355b14
--- /dev/null
+++ b/autoconf/Makefile.fallback
@@ -0,0 +1,19 @@
+#!/usr/bin/make
+#
+# If the configure script does not work, then this Makefile is available
+# as a backup. Manually configure the variables below.
+#
+# Note: This makefile works out-of-the-box on MacOS 10.2 (Jaguar)
+#
+CC = gcc
+CFLAGS = -O0 -I.
+LIBS = -lz
+COPTS += -D_BSD_SOURCE
+COPTS += -DSQLITE_ENABLE_LOCKING_STYLE=0
+COPTS += -DSQLITE_THREADSAFE=0
+COPTS += -DSQLITE_OMIT_LOAD_EXTENSION
+COPTS += -DSQLITE_WITHOUT_ZONEMALLOC
+COPTS += -DSQLITE_ENABLE_RTREE
+
+sqlite3: shell.c sqlite3.c
+ $(CC) $(CFLAGS) $(COPTS) -o sqlite3 shell.c sqlite3.c $(LIBS)
diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc
index d99549b..dbcb8c9 100644
--- a/autoconf/Makefile.msc
+++ b/autoconf/Makefile.msc
@@ -966,7 +966,7 @@ Replace.exe:
sqlite3.def: Replace.exe $(LIBOBJ)
echo EXPORTS > sqlite3.def
dumpbin /all $(LIBOBJ) \
- | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
+ | .\Replace.exe "^\s+/EXPORT:_?(sqlite3(?:session|changeset|changegroup|rebaser)?_[^@,]*)(?:@\d+|,DATA)?$$" $$1 true \
| sort >> sqlite3.def
$(SQLITE3EXE): shell.c $(SHELL_CORE_DEP) $(LIBRESOBJS) $(SHELL_CORE_SRC) $(SQLITE3H)
diff --git a/autoconf/configure.ac b/autoconf/configure.ac
index 2680bec..8ba2218 100644
--- a/autoconf/configure.ac
+++ b/autoconf/configure.ac
@@ -115,8 +115,8 @@ AC_SUBST(DYNAMIC_EXTENSION_FLAGS)
# --enable-fts5
#
AC_ARG_ENABLE(fts5, [AS_HELP_STRING(
- [--enable-fts5], [include fts5 support [default=no]])],
- [], [enable_fts5=no])
+ [--enable-fts5], [include fts5 support [default=yes]])],
+ [], [enable_fts5=yes])
if test x"$enable_fts5" = "xyes"; then
AC_SEARCH_LIBS(log, m)
FTS5_FLAGS=-DSQLITE_ENABLE_FTS5
@@ -128,8 +128,8 @@ AC_SUBST(FTS5_FLAGS)
# --enable-json1
#
AC_ARG_ENABLE(json1, [AS_HELP_STRING(
- [--enable-json1], [include json1 support [default=no]])],
- [], [enable_json1=no])
+ [--enable-json1], [include json1 support [default=yes]])],
+ [], [enable_json1=yes])
if test x"$enable_json1" = "xyes"; then
JSON1_FLAGS=-DSQLITE_ENABLE_JSON1
fi
@@ -149,6 +149,18 @@ AC_SUBST(SESSION_FLAGS)
#-----------------------------------------------------------------------
#-----------------------------------------------------------------------
+# --enable-debug
+#
+AC_ARG_ENABLE(debug, [AS_HELP_STRING(
+ [--enable-debug], [build with debugging features enabled [default=no]])],
+ [], [enable_session=no])
+if test x"$enable_debug" = "xyes"; then
+ DEBUG_FLAGS="-DSQLITE_DEBUG -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE"
+fi
+AC_SUBST(DEBUG_FLAGS)
+#-----------------------------------------------------------------------
+
+#-----------------------------------------------------------------------
# --enable-static-shell
#
AC_ARG_ENABLE(static-shell, [AS_HELP_STRING(
diff --git a/configure b/configure
index baead2e..ea11601 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for sqlite 3.23.1.
+# Generated by GNU Autoconf 2.69 for sqlite 3.24.0.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='sqlite'
PACKAGE_TARNAME='sqlite'
-PACKAGE_VERSION='3.23.1'
-PACKAGE_STRING='sqlite 3.23.1'
+PACKAGE_VERSION='3.24.0'
+PACKAGE_STRING='sqlite 3.24.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1465,7 +1465,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 sqlite 3.23.1 to adapt to many kinds of systems.
+\`configure' configures sqlite 3.24.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1530,7 +1530,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of sqlite 3.23.1:";;
+ short | recursive ) echo "Configuration of sqlite 3.24.0:";;
esac
cat <<\_ACEOF
@@ -1655,7 +1655,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-sqlite configure 3.23.1
+sqlite configure 3.24.0
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2074,7 +2074,7 @@ 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 sqlite $as_me 3.23.1, which was
+It was created by sqlite $as_me 3.24.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -12242,7 +12242,7 @@ 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 sqlite $as_me 3.23.1, which was
+This file was extended by sqlite $as_me 3.24.0, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -12308,7 +12308,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-sqlite config.status 3.23.1
+sqlite config.status 3.24.0
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/doc/lemon.html b/doc/lemon.html
index 3ed8517..4f0849e 100644
--- a/doc/lemon.html
+++ b/doc/lemon.html
@@ -99,6 +99,9 @@ Show only the basis for each parser state in the report file.
<li><b>-c</b>
Do not compress the generated action tables. The parser will be a
little larger and slower, but it will detect syntax errors sooner.
+<li><b>-d</b><i>directory</i>
+Write all output files into <i>directory</i>. Normally, output files
+are written into the directory that contains the input grammar file.
<li><b>-D<i>name</i></b>
Define C preprocessor macro <i>name</i>. This macro is usable by
"<tt><a href='#pifdef'>%ifdef</a></tt>" and
@@ -679,6 +682,30 @@ of type "MyStruct*" and all action routines will have access to
a variable named "pAbc" that is the value of the 4th parameter
in the most recent call to Parse().</p>
+<p>The <tt>%extra_context</tt> directive works the same except that it
+is passed in on the ParseAlloc() or ParseInit() routines instead of
+on Parse().
+
+<a name='extractx'></a>
+<h4>The <tt>%extra_context</tt> directive</h4>
+
+The <tt>%extra_context</tt> directive instructs Lemon to add a 2th parameter
+to the parameter list of the ParseAlloc() and ParseInif() functions. Lemon
+doesn't do anything itself with these extra argument, but it does
+store the value make it available to C-code action routines, destructors,
+and so forth. For example, if the grammar file contains:</p>
+
+<p><pre>
+ %extra_context { MyStruct *pAbc }
+</pre></p>
+
+<p>Then the ParseAlloc() and ParseInit() functions will have an 2th parameter
+of type "MyStruct*" and all action routines will have access to
+a variable named "pAbc" that is the value of that 2th parameter.</p>
+
+<p>The <tt>%extra_argument</tt> directive works the same except that it
+is passed in on the Parse() routine instead of on ParseAlloc()/ParseInit().
+
<a name='pfallback'></a>
<h4>The <tt>%fallback</tt> directive</h4>
diff --git a/ext/expert/expert1.test b/ext/expert/expert1.test
index 3d693ec..d1564c5 100644
--- a/ext/expert/expert1.test
+++ b/ext/expert/expert1.test
@@ -95,7 +95,7 @@ do_setup_rec_test $tn.1 { CREATE TABLE t1(a, b, c) } {
SELECT * FROM t1
} {
(no new indexes)
- 0|0|0|SCAN TABLE t1
+ SCAN TABLE t1
}
do_setup_rec_test $tn.2 {
@@ -104,7 +104,7 @@ do_setup_rec_test $tn.2 {
SELECT * FROM t1 WHERE b>?;
} {
CREATE INDEX t1_idx_00000062 ON t1(b);
- 0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000062 (b>?)
+ SEARCH TABLE t1 USING INDEX t1_idx_00000062 (b>?)
}
do_setup_rec_test $tn.3 {
@@ -113,7 +113,7 @@ do_setup_rec_test $tn.3 {
SELECT * FROM t1 WHERE b COLLATE nocase BETWEEN ? AND ?
} {
CREATE INDEX t1_idx_3e094c27 ON t1(b COLLATE NOCASE);
- 0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_3e094c27 (b>? AND b<?)
+ SEARCH TABLE t1 USING INDEX t1_idx_3e094c27 (b>? AND b<?)
}
do_setup_rec_test $tn.4 {
@@ -122,7 +122,7 @@ do_setup_rec_test $tn.4 {
SELECT a FROM t1 ORDER BY b;
} {
CREATE INDEX t1_idx_00000062 ON t1(b);
- 0|0|0|SCAN TABLE t1 USING INDEX t1_idx_00000062
+ SCAN TABLE t1 USING INDEX t1_idx_00000062
}
do_setup_rec_test $tn.5 {
@@ -131,7 +131,7 @@ do_setup_rec_test $tn.5 {
SELECT a FROM t1 WHERE a=? ORDER BY b;
} {
CREATE INDEX t1_idx_000123a7 ON t1(a, b);
- 0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
+ SEARCH TABLE t1 USING COVERING INDEX t1_idx_000123a7 (a=?)
}
do_setup_rec_test $tn.6 {
@@ -140,7 +140,7 @@ do_setup_rec_test $tn.6 {
SELECT min(a) FROM t1
} {
CREATE INDEX t1_idx_00000061 ON t1(a);
- 0|0|0|SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
+ SEARCH TABLE t1 USING COVERING INDEX t1_idx_00000061
}
do_setup_rec_test $tn.7 {
@@ -149,7 +149,7 @@ do_setup_rec_test $tn.7 {
SELECT * FROM t1 ORDER BY a, b, c;
} {
CREATE INDEX t1_idx_033e95fe ON t1(a, b, c);
- 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_033e95fe
+ SCAN TABLE t1 USING COVERING INDEX t1_idx_033e95fe
}
#do_setup_rec_test $tn.1.8 {
@@ -167,7 +167,7 @@ do_setup_rec_test $tn.8.1 {
SELECT * FROM t1 WHERE a=?
} {
CREATE INDEX t1_idx_00000061 ON t1(a);
- 0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000061 (a=?)
+ SEARCH TABLE t1 USING INDEX t1_idx_00000061 (a=?)
}
do_setup_rec_test $tn.8.2 {
CREATE TABLE t1(a, b COLLATE nocase, c);
@@ -175,7 +175,7 @@ do_setup_rec_test $tn.8.2 {
SELECT * FROM t1 ORDER BY a ASC, b DESC, c ASC;
} {
CREATE INDEX t1_idx_5cb97285 ON t1(a, b DESC, c);
- 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5cb97285
+ SCAN TABLE t1 USING COVERING INDEX t1_idx_5cb97285
}
@@ -187,7 +187,7 @@ do_setup_rec_test $tn.9.1 {
SELECT * FROM "t t" WHERE a=?
} {
CREATE INDEX 't t_idx_00000061' ON 't t'(a);
- 0|0|0|SEARCH TABLE t t USING INDEX t t_idx_00000061 (a=?)
+ SEARCH TABLE t t USING INDEX t t_idx_00000061 (a=?)
}
do_setup_rec_test $tn.9.2 {
@@ -196,7 +196,7 @@ do_setup_rec_test $tn.9.2 {
SELECT * FROM "t t" WHERE b BETWEEN ? AND ?
} {
CREATE INDEX 't t_idx_00000062' ON 't t'(b);
- 0|0|0|SEARCH TABLE t t USING INDEX t t_idx_00000062 (b>? AND b<?)
+ SEARCH TABLE t t USING INDEX t t_idx_00000062 (b>? AND b<?)
}
# Columns with names that require quotes.
@@ -207,7 +207,7 @@ do_setup_rec_test $tn.10.1 {
SELECT * FROM t3 WHERE "b b" = ?
} {
CREATE INDEX t3_idx_00050c52 ON t3('b b');
- 0|0|0|SEARCH TABLE t3 USING INDEX t3_idx_00050c52 (b b=?)
+ SEARCH TABLE t3 USING INDEX t3_idx_00050c52 (b b=?)
}
do_setup_rec_test $tn.10.2 {
@@ -216,7 +216,7 @@ do_setup_rec_test $tn.10.2 {
SELECT * FROM t3 ORDER BY "b b"
} {
CREATE INDEX t3_idx_00050c52 ON t3('b b');
- 0|0|0|SCAN TABLE t3 USING INDEX t3_idx_00050c52
+ SCAN TABLE t3 USING INDEX t3_idx_00050c52
}
# Transitive constraints
@@ -229,8 +229,8 @@ do_setup_rec_test $tn.11.1 {
} {
CREATE INDEX t5_idx_000123a7 ON t5(a, b);
CREATE INDEX t6_idx_00000063 ON t6(c);
- 0|0|1|SEARCH TABLE t6 USING INDEX t6_idx_00000063 (c=?)
- 0|1|0|SEARCH TABLE t5 USING COVERING INDEX t5_idx_000123a7 (a=? AND b=?)
+ SEARCH TABLE t6 USING INDEX t6_idx_00000063 (c=?)
+ SEARCH TABLE t5 USING COVERING INDEX t5_idx_000123a7 (a=? AND b=?)
}
# OR terms.
@@ -242,8 +242,9 @@ do_setup_rec_test $tn.12.1 {
} {
CREATE INDEX t7_idx_00000062 ON t7(b);
CREATE INDEX t7_idx_00000061 ON t7(a);
- 0|0|0|SEARCH TABLE t7 USING INDEX t7_idx_00000061 (a=?)
- 0|0|0|SEARCH TABLE t7 USING INDEX t7_idx_00000062 (b=?)
+ MULTI-INDEX OR
+ SEARCH TABLE t7 USING INDEX t7_idx_00000061 (a=?)
+ SEARCH TABLE t7 USING INDEX t7_idx_00000062 (b=?)
}
# rowid terms.
@@ -254,7 +255,7 @@ do_setup_rec_test $tn.13.1 {
SELECT * FROM t8 WHERE rowid=?
} {
(no new indexes)
- 0|0|0|SEARCH TABLE t8 USING INTEGER PRIMARY KEY (rowid=?)
+ SEARCH TABLE t8 USING INTEGER PRIMARY KEY (rowid=?)
}
do_setup_rec_test $tn.13.2 {
CREATE TABLE t8(a, b);
@@ -262,7 +263,7 @@ do_setup_rec_test $tn.13.2 {
SELECT * FROM t8 ORDER BY rowid
} {
(no new indexes)
- 0|0|0|SCAN TABLE t8
+ SCAN TABLE t8
}
do_setup_rec_test $tn.13.3 {
CREATE TABLE t8(a, b);
@@ -270,7 +271,7 @@ do_setup_rec_test $tn.13.3 {
SELECT * FROM t8 WHERE a=? ORDER BY rowid
} {
CREATE INDEX t8_idx_00000061 ON t8(a);
- 0|0|0|SEARCH TABLE t8 USING INDEX t8_idx_00000061 (a=?)
+ SEARCH TABLE t8 USING INDEX t8_idx_00000061 (a=?)
}
# Triggers
@@ -285,7 +286,7 @@ do_setup_rec_test $tn.14 {
INSERT INTO t9 VALUES(?, ?, ?);
} {
CREATE INDEX t10_idx_00000062 ON t10(b);
- 0|0|0|SEARCH TABLE t10 USING INDEX t10_idx_00000062 (b=?)
+ SEARCH TABLE t10 USING INDEX t10_idx_00000062 (b=?)
}
do_setup_rec_test $tn.15 {
@@ -301,8 +302,8 @@ do_setup_rec_test $tn.15 {
SELECT * FROM t2, t1 WHERE b=? AND d=? AND t2.rowid=t1.rowid
} {
CREATE INDEX t2_idx_00000064 ON t2(d);
- 0|0|0|SEARCH TABLE t2 USING INDEX t2_idx_00000064 (d=?)
- 0|1|1|SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)
+ SEARCH TABLE t2 USING INDEX t2_idx_00000064 (d=?)
+ SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)
}
do_setup_rec_test $tn.16 {
@@ -311,7 +312,7 @@ do_setup_rec_test $tn.16 {
SELECT * FROM t1 WHERE b IS NOT NULL;
} {
(no new indexes)
- 0|0|0|SCAN TABLE t1
+ SCAN TABLE t1
}
}
@@ -379,4 +380,3 @@ do_execsql_test 3.2 {
finish_test
-
diff --git a/ext/expert/sqlite3expert.c b/ext/expert/sqlite3expert.c
index e0602bc..695aaec 100644
--- a/ext/expert/sqlite3expert.c
+++ b/ext/expert/sqlite3expert.c
@@ -1123,9 +1123,9 @@ int idxFindIndexes(
"EXPLAIN QUERY PLAN %s", pStmt->zSql
);
while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){
- int iSelectid = sqlite3_column_int(pExplain, 0);
- int iOrder = sqlite3_column_int(pExplain, 1);
- int iFrom = sqlite3_column_int(pExplain, 2);
+ /* int iId = sqlite3_column_int(pExplain, 0); */
+ /* int iParent = sqlite3_column_int(pExplain, 1); */
+ /* int iNotUsed = sqlite3_column_int(pExplain, 2); */
const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3);
int nDetail = STRLEN(zDetail);
int i;
@@ -1152,9 +1152,9 @@ int idxFindIndexes(
}
}
- pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%d|%d|%d|%s\n",
- iSelectid, iOrder, iFrom, zDetail
- );
+ if( zDetail[0]!='-' ){
+ pStmt->zEQP = idxAppendText(&rc, pStmt->zEQP, "%s\n", zDetail);
+ }
}
for(pEntry=hIdx.pFirst; pEntry; pEntry=pEntry->pNext){
diff --git a/ext/fts3/fts3.c b/ext/fts3/fts3.c
index f514542..93776d0 100644
--- a/ext/fts3/fts3.c
+++ b/ext/fts3/fts3.c
@@ -3963,7 +3963,7 @@ int sqlite3Fts3Init(sqlite3 *db){
#ifdef SQLITE_TEST
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3ExprInitTestInterface(db);
+ rc = sqlite3Fts3ExprInitTestInterface(db, pHash);
}
#endif
diff --git a/ext/fts3/fts3Int.h b/ext/fts3/fts3Int.h
index c3cab9d..077bad7 100644
--- a/ext/fts3/fts3Int.h
+++ b/ext/fts3/fts3Int.h
@@ -584,7 +584,7 @@ int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
);
void sqlite3Fts3ExprFree(Fts3Expr *);
#ifdef SQLITE_TEST
-int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
+int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash*);
int sqlite3Fts3InitTerm(sqlite3 *db);
#endif
diff --git a/ext/fts3/fts3_expr.c b/ext/fts3/fts3_expr.c
index 788e502..9f42b44 100644
--- a/ext/fts3/fts3_expr.c
+++ b/ext/fts3/fts3_expr.c
@@ -1109,34 +1109,6 @@ void sqlite3Fts3ExprFree(Fts3Expr *pDel){
#include <stdio.h>
/*
-** Function to query the hash-table of tokenizers (see README.tokenizers).
-*/
-static int queryTestTokenizer(
- sqlite3 *db,
- const char *zName,
- const sqlite3_tokenizer_module **pp
-){
- int rc;
- sqlite3_stmt *pStmt;
- const char zSql[] = "SELECT fts3_tokenizer(?)";
-
- *pp = 0;
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
-
- sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){
- memcpy((void *)pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp));
- }
- }
-
- return sqlite3_finalize(pStmt);
-}
-
-/*
** Return a pointer to a buffer containing a text representation of the
** expression passed as the first argument. The buffer is obtained from
** sqlite3_malloc(). It is the responsibility of the caller to use
@@ -1203,12 +1175,12 @@ static char *exprToString(Fts3Expr *pExpr, char *zBuf){
**
** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2');
*/
-static void fts3ExprTest(
+static void fts3ExprTestCommon(
+ int bRebalance,
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
- sqlite3_tokenizer_module const *pModule = 0;
sqlite3_tokenizer *pTokenizer = 0;
int rc;
char **azCol = 0;
@@ -1218,7 +1190,9 @@ static void fts3ExprTest(
int ii;
Fts3Expr *pExpr;
char *zBuf = 0;
- sqlite3 *db = sqlite3_context_db_handle(context);
+ Fts3Hash *pHash = (Fts3Hash*)sqlite3_user_data(context);
+ const char *zTokenizer = 0;
+ char *zErr = 0;
if( argc<3 ){
sqlite3_result_error(context,
@@ -1227,23 +1201,17 @@ static void fts3ExprTest(
return;
}
- rc = queryTestTokenizer(db,
- (const char *)sqlite3_value_text(argv[0]), &pModule);
- if( rc==SQLITE_NOMEM ){
- sqlite3_result_error_nomem(context);
- goto exprtest_out;
- }else if( !pModule ){
- sqlite3_result_error(context, "No such tokenizer module", -1);
- goto exprtest_out;
- }
-
- rc = pModule->xCreate(0, 0, &pTokenizer);
- assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
- if( rc==SQLITE_NOMEM ){
- sqlite3_result_error_nomem(context);
- goto exprtest_out;
+ zTokenizer = (const char*)sqlite3_value_text(argv[0]);
+ rc = sqlite3Fts3InitTokenizer(pHash, zTokenizer, &pTokenizer, &zErr);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ sqlite3_result_error(context, zErr, -1);
+ }
+ sqlite3_free(zErr);
+ return;
}
- pTokenizer->pModule = pModule;
zExpr = (const char *)sqlite3_value_text(argv[1]);
nExpr = sqlite3_value_bytes(argv[1]);
@@ -1257,7 +1225,7 @@ static void fts3ExprTest(
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
}
- if( sqlite3_user_data(context) ){
+ if( bRebalance ){
char *zDummy = 0;
rc = sqlite3Fts3ExprParse(
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr, &zDummy
@@ -1283,23 +1251,38 @@ static void fts3ExprTest(
sqlite3Fts3ExprFree(pExpr);
exprtest_out:
- if( pModule && pTokenizer ){
- rc = pModule->xDestroy(pTokenizer);
+ if( pTokenizer ){
+ rc = pTokenizer->pModule->xDestroy(pTokenizer);
}
sqlite3_free(azCol);
}
+static void fts3ExprTest(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ fts3ExprTestCommon(0, context, argc, argv);
+}
+static void fts3ExprTestRebalance(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ fts3ExprTestCommon(1, context, argc, argv);
+}
+
/*
** Register the query expression parser test function fts3_exprtest()
** with database connection db.
*/
-int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
+int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash){
int rc = sqlite3_create_function(
- db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0
+ db, "fts3_exprtest", -1, SQLITE_UTF8, (void*)pHash, fts3ExprTest, 0, 0
);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
- -1, SQLITE_UTF8, (void *)1, fts3ExprTest, 0, 0
+ -1, SQLITE_UTF8, (void*)pHash, fts3ExprTestRebalance, 0, 0
);
}
return rc;
diff --git a/ext/fts5/test/fts5plan.test b/ext/fts5/test/fts5plan.test
index a7b70f5..8f57e39 100644
--- a/ext/fts5/test/fts5plan.test
+++ b/ext/fts5/test/fts5plan.test
@@ -29,38 +29,37 @@ do_execsql_test 1.0 {
do_eqp_test 1.1 {
SELECT * FROM t1, f1 WHERE f1 MATCH t1.x
} {
- 0 0 0 {SCAN TABLE t1}
- 0 1 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:
}
do_eqp_test 1.2 {
SELECT * FROM t1, f1 WHERE f1 > t1.x
} {
- 0 0 1 {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:}
- 0 1 0 {SCAN TABLE t1}
+ QUERY PLAN
+ |--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:
+ `--SCAN TABLE t1
}
do_eqp_test 1.3 {
SELECT * FROM f1 WHERE f1 MATCH ? ORDER BY ff
} {
- 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE f1 VIRTUAL TABLE INDEX 65537:
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 1.4 {
SELECT * FROM f1 ORDER BY rank
} {
- 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 0:}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE f1 VIRTUAL TABLE INDEX 0:
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 1.5 {
SELECT * FROM f1 WHERE rank MATCH ?
-} {
- 0 0 0 {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:}
-}
-
-
-
+} {SCAN TABLE f1 VIRTUAL TABLE INDEX 2:}
finish_test
diff --git a/ext/fts5/test/fts5unicode.test b/ext/fts5/test/fts5unicode.test
index a9874cc..e2d0f60 100644
--- a/ext/fts5/test/fts5unicode.test
+++ b/ext/fts5/test/fts5unicode.test
@@ -41,7 +41,6 @@ foreach {tn t} {1 ascii 2 unicode61} {
#-------------------------------------------------------------------------
# Check that "unicode61" really is the default tokenizer.
#
-
do_execsql_test 2.0 "
CREATE VIRTUAL TABLE t1 USING fts5(x);
CREATE VIRTUAL TABLE t2 USING fts5(x, tokenize = unicode61);
@@ -56,5 +55,31 @@ do_execsql_test 2.1 "
SELECT 't3' FROM t3 WHERE t3 MATCH '\xE0\xE8\xEC';
" {t1 t2}
+#-------------------------------------------------------------------------
+# Check that codepoints that require 4 bytes to store in utf-8 (those that
+# require 17 or more bits to store).
+#
+
+set A [db one {SELECT char(0x1F75E)}] ;# Type So
+set B [db one {SELECT char(0x1F5FD)}] ;# Type So
+set C [db one {SELECT char(0x2F802)}] ;# Type Lo
+set D [db one {SELECT char(0x2F808)}] ;# Type Lo
+
+do_execsql_test 3.0 "
+ CREATE VIRTUAL TABLE xyz USING fts5(x,
+ tokenize = \"unicode61 separators '$C' tokenchars '$A'\"
+ );
+ CREATE VIRTUAL TABLE xyz_v USING fts5vocab(xyz, row);
+
+ INSERT INTO xyz VALUES('$A$B$C$D');
+"
+
+do_execsql_test 3.1 {
+ SELECT * FROM xyz_v;
+} [list $A 1 1 $D 1 1]
+
+
+
+
finish_test
diff --git a/ext/misc/closure.c b/ext/misc/closure.c
index 510c46e..74bffc7 100644
--- a/ext/misc/closure.c
+++ b/ext/misc/closure.c
@@ -826,17 +826,12 @@ 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
@@ -893,6 +888,18 @@ static int closureBestIndex(
** or else the result is an empty set. */
iPlan = 0;
}
+ if( (iPlan&1)==0 ){
+ /* If there is no usable "root=?" term, then set the index-type to 0.
+ ** Also clear any argvIndex variables already set. This is necessary
+ ** to prevent the core from throwing an "xBestIndex malfunction error"
+ ** error (because the argvIndex values are not contiguously assigned
+ ** starting from 1). */
+ rCost *= 1e30;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ pIdxInfo->aConstraintUsage[i].argvIndex = 0;
+ }
+ iPlan = 0;
+ }
pIdxInfo->idxNum = iPlan;
if( pIdxInfo->nOrderBy==1
&& pIdxInfo->aOrderBy[0].iColumn==CLOSURE_COL_ID
@@ -900,7 +907,6 @@ static int closureBestIndex(
){
pIdxInfo->orderByConsumed = 1;
}
- if( seenMatch && (iPlan&1)==0 ) rCost *= 1e30;
pIdxInfo->estimatedCost = rCost;
return SQLITE_OK;
diff --git a/ext/misc/completion.c b/ext/misc/completion.c
index 7809635..89c4f5d 100644
--- a/ext/misc/completion.c
+++ b/ext/misc/completion.c
@@ -62,6 +62,7 @@ struct completion_cursor {
char *zPrefix; /* The prefix for the word we want to complete */
char *zLine; /* The whole that we want to complete */
const char *zCurrentRow; /* Current output row */
+ int szRow; /* Length of the zCurrentRow string */
sqlite3_stmt *pStmt; /* Current statement */
sqlite3_int64 iRowid; /* The rowid */
int ePhase; /* Current phase */
@@ -175,32 +176,6 @@ static int completionClose(sqlite3_vtab_cursor *cur){
}
/*
-** All SQL keywords understood by SQLite
-*/
-static const char *completionKwrds[] = {
- "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
- "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
- "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
- "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
- "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
- "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
- "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
- "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
- "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
- "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
- "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
- "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
- "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
- "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
- "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
- "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
- "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
- "WITH", "WITHOUT",
-};
-#define completionKwCount \
- (int)(sizeof(completionKwrds)/sizeof(completionKwrds[0]))
-
-/*
** Advance a completion_cursor to its next row of output.
**
** The ->ePhase, ->j, and ->pStmt fields of the completion_cursor object
@@ -222,11 +197,11 @@ static int completionNext(sqlite3_vtab_cursor *cur){
while( pCur->ePhase!=COMPLETION_EOF ){
switch( pCur->ePhase ){
case COMPLETION_KEYWORDS: {
- if( pCur->j >= completionKwCount ){
+ if( pCur->j >= sqlite3_keyword_count() ){
pCur->zCurrentRow = 0;
pCur->ePhase = COMPLETION_DATABASES;
}else{
- pCur->zCurrentRow = completionKwrds[pCur->j++];
+ sqlite3_keyword_name(pCur->j++, &pCur->zCurrentRow, &pCur->szRow);
}
iCol = -1;
break;
@@ -298,6 +273,7 @@ static int completionNext(sqlite3_vtab_cursor *cur){
if( sqlite3_step(pCur->pStmt)==SQLITE_ROW ){
/* Extract the next row of content */
pCur->zCurrentRow = (const char*)sqlite3_column_text(pCur->pStmt, iCol);
+ pCur->szRow = sqlite3_column_bytes(pCur->pStmt, iCol);
}else{
/* When all rows are finished, advance to the next phase */
sqlite3_finalize(pCur->pStmt);
@@ -307,7 +283,9 @@ static int completionNext(sqlite3_vtab_cursor *cur){
}
}
if( pCur->nPrefix==0 ) break;
- if( sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0 ){
+ if( pCur->nPrefix<=pCur->szRow
+ && sqlite3_strnicmp(pCur->zPrefix, pCur->zCurrentRow, pCur->nPrefix)==0
+ ){
break;
}
}
@@ -327,7 +305,7 @@ static int completionColumn(
completion_cursor *pCur = (completion_cursor*)cur;
switch( i ){
case COMPLETION_COLUMN_CANDIDATE: {
- sqlite3_result_text(ctx, pCur->zCurrentRow, -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(ctx, pCur->zCurrentRow, pCur->szRow,SQLITE_TRANSIENT);
break;
}
case COMPLETION_COLUMN_PREFIX: {
diff --git a/ext/misc/csv.c b/ext/misc/csv.c
index c634ab3..ec90f96 100644
--- a/ext/misc/csv.c
+++ b/ext/misc/csv.c
@@ -132,6 +132,7 @@ static int csv_reader_open(
}
p->in = fopen(zFilename, "rb");
if( p->in==0 ){
+ sqlite3_free(p->zIn);
csv_reader_reset(p);
csv_errmsg(p, "cannot open '%s' for reading", zFilename);
return 1;
@@ -204,7 +205,8 @@ static int csv_append(CsvReader *p, char c){
** + Store the character that terminates the field in p->cTerm. Store
** EOF on end-of-file.
**
-** Return "" at EOF. Return 0 on an OOM error.
+** Return 0 at EOF or on OOM. On EOF, the p->cTerm character will have
+** been set to EOF.
*/
static char *csv_read_one_field(CsvReader *p){
int c;
@@ -212,7 +214,7 @@ static char *csv_read_one_field(CsvReader *p){
c = csv_getc(p);
if( c==EOF ){
p->cTerm = EOF;
- return "";
+ return 0;
}
if( c=='"' ){
int pc, ppc;
@@ -543,8 +545,7 @@ static int csvtabConnect(
pNew->nCol = nCol;
}else{
do{
- const char *z = csv_read_one_field(&sRdr);
- if( z==0 ) goto csvtab_connect_oom;
+ csv_read_one_field(&sRdr);
pNew->nCol++;
}while( sRdr.cTerm==',' );
}
@@ -662,7 +663,6 @@ static int csvtabNext(sqlite3_vtab_cursor *cur){
do{
z = csv_read_one_field(&pCur->rdr);
if( z==0 ){
- csv_xfer_error(pTab, &pCur->rdr);
break;
}
if( i<pTab->nCol ){
diff --git a/ext/misc/dbdump.c b/ext/misc/dbdump.c
index b4d6422..2ca34a1 100644
--- a/ext/misc/dbdump.c
+++ b/ext/misc/dbdump.c
@@ -141,45 +141,12 @@ static void appendText(DText *p, char const *zAppend, char quote){
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
- /* All SQLite keywords, in alphabetical order */
- static const char *azKeywords[] = {
- "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
- "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
- "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
- "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
- "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
- "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
- "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
- "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
- "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
- "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
- "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
- "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
- "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
- "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
- "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
- "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
- "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
- "WITH", "WITHOUT",
- };
- int i, lwr, upr, mid, c;
+ int i;
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
for(i=0; zName[i]; i++){
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
}
- lwr = 0;
- upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1;
- while( lwr<=upr ){
- mid = (lwr+upr)/2;
- c = sqlite3_stricmp(azKeywords[mid], zName);
- if( c==0 ) return '"';
- if( c<0 ){
- lwr = mid+1;
- }else{
- upr = mid-1;
- }
- }
- return 0;
+ return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
diff --git a/ext/misc/json1.c b/ext/misc/json1.c
index c1d2334..735e3c9 100644
--- a/ext/misc/json1.c
+++ b/ext/misc/json1.c
@@ -2118,7 +2118,7 @@ static int jsonEachColumn(
}
if( p->eType==JSON_ARRAY ){
jsonPrintf(30, &x, "[%d]", p->iRowid);
- }else{
+ }else if( p->eType==JSON_OBJECT ){
jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
}
}
diff --git a/ext/misc/templatevtab.c b/ext/misc/templatevtab.c
new file mode 100644
index 0000000..95b5f60
--- /dev/null
+++ b/ext/misc/templatevtab.c
@@ -0,0 +1,267 @@
+/*
+** 2018-04-19
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file implements a template virtual-table.
+** Developers can make a copy of this file as a baseline for writing
+** new virtual tables and/or table-valued functions.
+**
+** Steps for writing a new virtual table implementation:
+**
+** (1) Make a copy of this file. Perhaps call it "mynewvtab.c"
+**
+** (2) Replace this header comment with something appropriate for
+** the new virtual table
+**
+** (3) Change every occurrence of "templatevtab" to some other string
+** appropriate for the new virtual table. Ideally, the new string
+** should be the basename of the source file: "mynewvtab". Also
+** globally change "TEMPLATEVTAB" to "MYNEWVTAB".
+**
+** (4) Run a test compilation to make sure the unmodified virtual
+** table works.
+**
+** (5) Begin making incremental changes, testing as you go, to evolve
+** the new virtual table to do what you want it to do.
+**
+** This template is minimal, in the sense that it uses only the required
+** methods on the sqlite3_module object. As a result, templatevtab is
+** a read-only and eponymous-only table. Those limitation can be removed
+** by adding new methods.
+**
+** This template implements an eponymous-only virtual table with a rowid and
+** two columns named "a" and "b". The table as 10 rows with fixed integer
+** values. Usage example:
+**
+** SELECT rowid, a, b FROM templatevtab;
+*/
+#if !defined(SQLITEINT_H)
+#include "sqlite3ext.h"
+#endif
+SQLITE_EXTENSION_INIT1
+#include <string.h>
+#include <assert.h>
+
+/* templatevtab_vtab is a subclass of sqlite3_vtab which is
+** underlying representation of the virtual table
+*/
+typedef struct templatevtab_vtab templatevtab_vtab;
+struct templatevtab_vtab {
+ sqlite3_vtab base; /* Base class - must be first */
+ /* Add new fields here, as necessary */
+};
+
+/* templatevtab_cursor is a subclass of sqlite3_vtab_cursor which will
+** serve as the underlying representation of a cursor that scans
+** over rows of the result
+*/
+typedef struct templatevtab_cursor templatevtab_cursor;
+struct templatevtab_cursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ /* Insert new fields here. For this templatevtab we only keep track
+ ** of the rowid */
+ sqlite3_int64 iRowid; /* The rowid */
+};
+
+/*
+** The templatevtabConnect() method is invoked to create a new
+** template virtual table.
+**
+** Think of this routine as the constructor for templatevtab_vtab objects.
+**
+** All this routine needs to do is:
+**
+** (1) Allocate the templatevtab_vtab object and initialize all fields.
+**
+** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
+** result set of queries against the virtual table will look like.
+*/
+static int templatevtabConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ templatevtab_vtab *pNew;
+ int rc;
+
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(a,b)"
+ );
+ /* For convenience, define symbolic names for the index to each column. */
+#define TEMPLATEVTAB_A 0
+#define TEMPLATEVTAB_B 1
+ if( rc==SQLITE_OK ){
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ *ppVtab = (sqlite3_vtab*)pNew;
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+/*
+** This method is the destructor for templatevtab_vtab objects.
+*/
+static int templatevtabDisconnect(sqlite3_vtab *pVtab){
+ templatevtab_vtab *p = (templatevtab_vtab*)pVtab;
+ sqlite3_free(p);
+ return SQLITE_OK;
+}
+
+/*
+** Constructor for a new templatevtab_cursor object.
+*/
+static int templatevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ templatevtab_cursor *pCur;
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Destructor for a templatevtab_cursor.
+*/
+static int templatevtabClose(sqlite3_vtab_cursor *cur){
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
+ sqlite3_free(pCur);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance a templatevtab_cursor to its next row of output.
+*/
+static int templatevtabNext(sqlite3_vtab_cursor *cur){
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
+ pCur->iRowid++;
+ return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the templatevtab_cursor
+** is currently pointing.
+*/
+static int templatevtabColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
+ switch( i ){
+ case TEMPLATEVTAB_A:
+ sqlite3_result_int(ctx, 1000 + pCur->iRowid);
+ break;
+ default:
+ assert( i==TEMPLATEVTAB_B );
+ sqlite3_result_int(ctx, 2000 + pCur->iRowid);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row. In this implementation, the
+** rowid is the same as the output value.
+*/
+static int templatevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
+ *pRowid = pCur->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int templatevtabEof(sqlite3_vtab_cursor *cur){
+ templatevtab_cursor *pCur = (templatevtab_cursor*)cur;
+ return pCur->iRowid>=10;
+}
+
+/*
+** This method is called to "rewind" the templatevtab_cursor object back
+** to the first row of output. This method is always called at least
+** once prior to any call to templatevtabColumn() or templatevtabRowid() or
+** templatevtabEof().
+*/
+static int templatevtabFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ templatevtab_cursor *pCur = (templatevtab_cursor *)pVtabCursor;
+ pCur->iRowid = 1;
+ return SQLITE_OK;
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the virtual table. This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+*/
+static int templatevtabBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ pIdxInfo->estimatedCost = (double)10;
+ pIdxInfo->estimatedRows = 10;
+ return SQLITE_OK;
+}
+
+/*
+** This following structure defines all the methods for the
+** virtual table.
+*/
+static sqlite3_module templatevtabModule = {
+ /* iVersion */ 0,
+ /* xCreate */ 0,
+ /* xConnect */ templatevtabConnect,
+ /* xBestIndex */ templatevtabBestIndex,
+ /* xDisconnect */ templatevtabDisconnect,
+ /* xDestroy */ 0,
+ /* xOpen */ templatevtabOpen,
+ /* xClose */ templatevtabClose,
+ /* xFilter */ templatevtabFilter,
+ /* xNext */ templatevtabNext,
+ /* xEof */ templatevtabEof,
+ /* xColumn */ templatevtabColumn,
+ /* xRowid */ templatevtabRowid,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindMethod */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0
+};
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_templatevtab_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ int rc = SQLITE_OK;
+ SQLITE_EXTENSION_INIT2(pApi);
+ rc = sqlite3_create_module(db, "templatevtab", &templatevtabModule, 0);
+ return rc;
+}
diff --git a/ext/rbu/rbu.c b/ext/rbu/rbu.c
index be5c724..1dac452 100644
--- a/ext/rbu/rbu.c
+++ b/ext/rbu/rbu.c
@@ -29,7 +29,9 @@ void usage(const char *zArgv0){
"Where options are:\n"
"\n"
" -step NSTEP\n"
+" -statstep NSTATSTEP\n"
" -vacuum\n"
+" -presql SQL\n"
"\n"
" If the -vacuum switch is not present, argument RBU-DB must be an RBU\n"
" database containing an update suitable for target database TARGET-DB.\n"
@@ -79,7 +81,9 @@ int main(int argc, char **argv){
char *zErrmsg; /* Error message, if any */
sqlite3rbu *pRbu; /* RBU handle */
int nStep = 0; /* Maximum number of step() calls */
+ int nStatStep = 0; /* Report stats after this many step calls */
int bVacuum = 0;
+ const char *zPreSql = 0;
int rc;
sqlite3_int64 nProgress = 0;
int nArgc = argc-2;
@@ -90,9 +94,18 @@ int main(int argc, char **argv){
int nArg = strlen(zArg);
if( nArg>1 && nArg<=8 && 0==memcmp(zArg, "-vacuum", nArg) ){
bVacuum = 1;
+ }else if( nArg>1 && nArg<=7
+ && 0==memcmp(zArg, "-presql", nArg) && i<nArg-1 ){
+ i++;
+ zPreSql = argv[i];
}else if( nArg>1 && nArg<=5 && 0==memcmp(zArg, "-step", nArg) && i<nArg-1 ){
i++;
nStep = atoi(argv[i]);
+ }else if( nArg>1 && nArg<=9
+ && 0==memcmp(zArg, "-statstep", nArg) && i<nArg-1
+ ){
+ i++;
+ nStatStep = atoi(argv[i]);
}else{
usage(argv[0]);
}
@@ -112,13 +125,40 @@ int main(int argc, char **argv){
}
report_rbu_vfs(pRbu);
+ if( zPreSql && pRbu ){
+ sqlite3 *db = sqlite3rbu_db(pRbu, 0);
+ rc = sqlite3_exec(db, zPreSql, 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3 *db = sqlite3rbu_db(pRbu, 1);
+ rc = sqlite3_exec(db, zPreSql, 0, 0, 0);
+ }
+ }
+
/* If nStep is less than or equal to zero, call
** sqlite3rbu_step() until either the RBU has been completely applied
** or an error occurs. Or, if nStep is greater than zero, call
** sqlite3rbu_step() a maximum of nStep times. */
- for(i=0; (nStep<=0 || i<nStep) && sqlite3rbu_step(pRbu)==SQLITE_OK; i++);
- nProgress = sqlite3rbu_progress(pRbu);
- rc = sqlite3rbu_close(pRbu, &zErrmsg);
+ if( rc==SQLITE_OK ){
+ for(i=0; (nStep<=0 || i<nStep) && sqlite3rbu_step(pRbu)==SQLITE_OK; i++){
+ if( nStatStep>0 && (i % nStatStep)==0 ){
+ sqlite3_int64 nUsed;
+ sqlite3_int64 nHighwater;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &nUsed, &nHighwater, 0);
+ fprintf(stdout, "memory used=%lld highwater=%lld", nUsed, nHighwater);
+ if( bVacuum==0 ){
+ int one;
+ int two;
+ sqlite3rbu_bp_progress(pRbu, &one, &two);
+ fprintf(stdout, " progress=%d/%d\n", one, two);
+ }else{
+ fprintf(stdout, "\n");
+ }
+ fflush(stdout);
+ }
+ }
+ nProgress = sqlite3rbu_progress(pRbu);
+ rc = sqlite3rbu_close(pRbu, &zErrmsg);
+ }
/* Let the user know what happened. */
switch( rc ){
diff --git a/ext/rbu/rbu1.test b/ext/rbu/rbu1.test
index cd41728..1d16c1c 100644
--- a/ext/rbu/rbu1.test
+++ b/ext/rbu/rbu1.test
@@ -139,6 +139,7 @@ foreach {tn3 create_vfs destroy_vfs} {
foreach {tn2 cmd} {
1 run_rbu
2 step_rbu 3 step_rbu_uri 4 step_rbu_state
+ 5 step_rbu_legacy
} {
foreach {tn schema} {
1 {
diff --git a/ext/rbu/rbu_common.tcl b/ext/rbu/rbu_common.tcl
index b216fe0..2b263b7 100644
--- a/ext/rbu/rbu_common.tcl
+++ b/ext/rbu/rbu_common.tcl
@@ -70,6 +70,22 @@ proc step_rbu {target rbu} {
set rc
}
+proc step_rbu_legacy {target rbu} {
+ while 1 {
+ sqlite3rbu rbu $target $rbu
+ set state [rbu state]
+ check_prestep_state $target $state
+ set rc [rbu step]
+ check_poststep_state $rc $target $state
+ rbu close
+ if {$rc != "SQLITE_OK"} break
+ sqlite3 tmpdb $rbu
+ tmpdb eval { DELETE FROM rbu_state WHERE k==10 }
+ tmpdb close
+ }
+ set rc
+}
+
proc do_rbu_vacuum_test {tn step} {
forcedelete state.db
uplevel [list do_test $tn.1 {
diff --git a/ext/rbu/rbusplit.test b/ext/rbu/rbusplit.test
new file mode 100644
index 0000000..678f388
--- /dev/null
+++ b/ext/rbu/rbusplit.test
@@ -0,0 +1,95 @@
+# 2018 April 28
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+#
+
+source [file join [file dirname [info script]] rbu_common.tcl]
+set ::testprefix rbusplit
+
+db close
+sqlite3_shutdown
+sqlite3_config_uri 1
+
+autoinstall_test_functions
+
+proc build_db {db} {
+ $db eval {
+ CREATE TABLE t1(a PRIMARY KEY, b, c);
+ CREATE TABLE t2(a PRIMARY KEY, b, c);
+
+ CREATE INDEX t1c ON t1(c);
+ }
+}
+
+proc build_rbu {filename} {
+ forcedelete $filename
+ sqlite3 dbRbu $filename
+ dbRbu eval {
+ CREATE TABLE data0_t1(a, b, c, rbu_control);
+ CREATE TABLE data1_t1(a, b, c, rbu_control);
+ CREATE TABLE data2_t1(a, b, c, rbu_control);
+ CREATE TABLE data3_t1(a, b, c, rbu_control);
+
+ CREATE TABLE data_t2(a, b, c, rbu_control);
+
+ INSERT INTO data0_t1 VALUES(1, 1, 1, 0);
+ INSERT INTO data0_t1 VALUES(2, 2, 2, 0);
+ INSERT INTO data0_t1 VALUES(3, 3, 3, 0);
+ INSERT INTO data0_t1 VALUES(4, 4, 4, 0);
+ INSERT INTO data1_t1 VALUES(5, 5, 5, 0);
+ INSERT INTO data1_t1 VALUES(6, 6, 6, 0);
+ INSERT INTO data1_t1 VALUES(7, 7, 7, 0);
+ INSERT INTO data1_t1 VALUES(8, 8, 8, 0);
+ INSERT INTO data3_t1 VALUES(9, 9, 9, 0);
+
+ INSERT INTO data_t2 VALUES(1, 1, 1, 0);
+ INSERT INTO data_t2 VALUES(2, 2, 2, 0);
+ INSERT INTO data_t2 VALUES(3, 3, 3, 0);
+ INSERT INTO data_t2 VALUES(4, 4, 4, 0);
+ INSERT INTO data_t2 VALUES(5, 5, 5, 0);
+ INSERT INTO data_t2 VALUES(6, 6, 6, 0);
+ INSERT INTO data_t2 VALUES(7, 7, 7, 0);
+ INSERT INTO data_t2 VALUES(8, 8, 8, 0);
+ INSERT INTO data_t2 VALUES(9, 9, 9, 0);
+ }
+
+ dbRbu close
+}
+
+foreach {tn cmd} {
+ 1 run_rbu
+ 2 step_rbu
+} {
+ reset_db
+ build_db db
+ build_rbu testrbu.db
+
+ do_test 1.$tn.1 {
+ $cmd test.db testrbu.db
+ } {SQLITE_DONE}
+ do_execsql_test 1.$tn.1 {
+ SELECT * FROM t1;
+ } {
+ 1 1 1 2 2 2 3 3 3 4 4 4
+ 5 5 5 6 6 6 7 7 7 8 8 8
+ 9 9 9
+ }
+ do_execsql_test 1.$tn.2 {
+ SELECT * FROM t2;
+ } {
+ 1 1 1 2 2 2 3 3 3 4 4 4
+ 5 5 5 6 6 6 7 7 7 8 8 8
+ 9 9 9
+ }
+}
+
+finish_test
+
diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c
index a3d54f9..065b13c 100644
--- a/ext/rbu/sqlite3rbu.c
+++ b/ext/rbu/sqlite3rbu.c
@@ -153,6 +153,10 @@
**
** RBU_STATE_OALSZ:
** Valid if STAGE==1. The size in bytes of the *-oal file.
+**
+** RBU_STATE_DATATBL:
+** Only valid if STAGE==1. The RBU database name of the table
+** currently being read.
*/
#define RBU_STATE_STAGE 1
#define RBU_STATE_TBL 2
@@ -163,6 +167,7 @@
#define RBU_STATE_COOKIE 7
#define RBU_STATE_OALSZ 8
#define RBU_STATE_PHASEONESTEP 9
+#define RBU_STATE_DATATBL 10
#define RBU_STAGE_OAL 1
#define RBU_STAGE_MOVE 2
@@ -205,6 +210,7 @@ typedef sqlite3_int64 i64;
struct RbuState {
int eStage;
char *zTbl;
+ char *zDataTbl;
char *zIdx;
i64 iWalCksum;
int nRow;
@@ -2268,6 +2274,7 @@ static sqlite3 *rbuOpenDbhandle(
static void rbuFreeState(RbuState *p){
if( p ){
sqlite3_free(p->zTbl);
+ sqlite3_free(p->zDataTbl);
sqlite3_free(p->zIdx);
sqlite3_free(p);
}
@@ -2338,6 +2345,10 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
break;
+ case RBU_STATE_DATATBL:
+ pRet->zDataTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
+ break;
+
default:
rc = SQLITE_CORRUPT;
break;
@@ -3112,7 +3123,8 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
"(%d, %lld), "
"(%d, %lld), "
"(%d, %lld), "
- "(%d, %lld) ",
+ "(%d, %lld), "
+ "(%d, %Q) ",
p->zStateDb,
RBU_STATE_STAGE, eStage,
RBU_STATE_TBL, p->objiter.zTbl,
@@ -3122,7 +3134,8 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
RBU_STATE_CKPT, p->iWalCksum,
RBU_STATE_COOKIE, (i64)pFd->iCookie,
RBU_STATE_OALSZ, p->iOalSz,
- RBU_STATE_PHASEONESTEP, p->nPhaseOneStep
+ RBU_STATE_PHASEONESTEP, p->nPhaseOneStep,
+ RBU_STATE_DATATBL, p->objiter.zDataTbl
)
);
assert( pInsert==0 || rc==SQLITE_OK );
@@ -3378,7 +3391,8 @@ static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
|| rbuStrCompare(pIter->zIdx, pState->zIdx)
- || rbuStrCompare(pIter->zTbl, pState->zTbl)
+ || (pState->zDataTbl==0 && rbuStrCompare(pIter->zTbl, pState->zTbl))
+ || (pState->zDataTbl && rbuStrCompare(pIter->zDataTbl, pState->zDataTbl))
)){
rc = rbuObjIterNext(p, pIter);
}
diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c
index 00513d4..e1b16ec 100644
--- a/ext/rtree/rtree.c
+++ b/ext/rtree/rtree.c
@@ -24,14 +24,15 @@
**
** CREATE TABLE %_node(nodeno INTEGER PRIMARY KEY, data BLOB)
** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
-** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER)
+** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
**
** The data for each node of the r-tree structure is stored in the %_node
** table. For each node that is not the root node of the r-tree, there is
** an entry in the %_parent table associating the node with its parent.
** And for each row of data in the table, there is an entry in the %_rowid
** table that maps from the entries rowid to the id of the node that it
-** is stored on.
+** is stored on. If the r-tree contains auxiliary columns, those are stored
+** on the end of the %_rowid table.
**
** The root node of an r-tree always exists, even if the r-tree table is
** empty. The nodeno of the root node is always 1. All other nodes in the
@@ -94,6 +95,9 @@ typedef struct RtreeSearchPoint RtreeSearchPoint;
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
#define RTREE_MAX_DIMENSIONS 5
+/* Maximum number of auxiliary columns */
+#define RTREE_MAX_AUX_COLUMN 100
+
/* Size of hash table Rtree.aHash. This hash table is not expected to
** ever contain very many entries, so a fixed number of buckets is
** used.
@@ -122,12 +126,15 @@ struct Rtree {
u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
u8 nBytesPerCell; /* Bytes consumed per cell */
u8 inWrTrans; /* True if inside write transaction */
+ u8 nAux; /* # of auxiliary columns in %_rowid */
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 */
u32 nBusy; /* Current number of users of this structure */
i64 nRowEst; /* Estimated number of rows in this table */
u32 nCursor; /* Number of open cursors */
+ u32 nNodeRef; /* Number RtreeNodes with positive nRef */
+ char *zReadAuxSql; /* SQL for statement to read aux data */
/* List of nodes removed during a CondenseTree operation. List is
** linked together via the pointer normally used for hash chains -
@@ -154,6 +161,9 @@ struct Rtree {
sqlite3_stmt *pWriteParent;
sqlite3_stmt *pDeleteParent;
+ /* Statement for writing to the "aux:" fields, if there are any */
+ sqlite3_stmt *pWriteAux;
+
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
};
@@ -230,6 +240,7 @@ struct RtreeCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
u8 atEOF; /* True if at end of search */
u8 bPoint; /* True if sPoint is valid */
+ u8 bAuxValid; /* True if pReadAux is valid */
int iStrategy; /* Copy of idxNum search parameter */
int nConstraint; /* Number of entries in aConstraint */
RtreeConstraint *aConstraint; /* Search constraints. */
@@ -237,6 +248,7 @@ struct RtreeCursor {
int nPoint; /* Number of slots used in aPoint[] */
int mxLevel; /* iLevel value for root of the tree */
RtreeSearchPoint *aPoint; /* Priority queue for search points */
+ sqlite3_stmt *pReadAux; /* Statement to read aux-data */
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 */
@@ -523,6 +535,7 @@ static int writeInt64(u8 *p, i64 i){
*/
static void nodeReference(RtreeNode *p){
if( p ){
+ assert( p->nRef>0 );
p->nRef++;
}
}
@@ -590,6 +603,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize);
pNode->zData = (u8 *)&pNode[1];
pNode->nRef = 1;
+ pRtree->nNodeRef++;
pNode->pParent = pParent;
pNode->isDirty = 1;
nodeReference(pParent);
@@ -623,10 +637,10 @@ static int nodeAcquire(
/* Check if the requested node is already in the hash table. If so,
** increase its reference count and return it.
*/
- if( (pNode = nodeHashLookup(pRtree, iNode)) ){
+ if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
if( pParent && !pNode->pParent ){
- nodeReference(pParent);
+ pParent->nRef++;
pNode->pParent = pParent;
}
pNode->nRef++;
@@ -665,6 +679,7 @@ static int nodeAcquire(
pNode->pParent = pParent;
pNode->zData = (u8 *)&pNode[1];
pNode->nRef = 1;
+ pRtree->nNodeRef++;
pNode->iNode = iNode;
pNode->isDirty = 0;
pNode->pNext = 0;
@@ -705,7 +720,10 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
- sqlite3_free(pNode);
+ if( pNode ){
+ pRtree->nNodeRef--;
+ sqlite3_free(pNode);
+ }
*ppNode = 0;
}
@@ -802,8 +820,10 @@ static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode ){
assert( pNode->nRef>0 );
+ assert( pRtree->nNodeRef>0 );
pNode->nRef--;
if( pNode->nRef==0 ){
+ pRtree->nNodeRef--;
if( pNode->iNode==1 ){
pRtree->iDepth = -1;
}
@@ -920,8 +940,9 @@ static void rtreeRelease(Rtree *pRtree){
pRtree->nBusy--;
if( pRtree->nBusy==0 ){
pRtree->inWrTrans = 0;
- pRtree->nCursor = 0;
+ assert( pRtree->nCursor==0 );
nodeBlobReset(pRtree);
+ assert( pRtree->nNodeRef==0 );
sqlite3_finalize(pRtree->pWriteNode);
sqlite3_finalize(pRtree->pDeleteNode);
sqlite3_finalize(pRtree->pReadRowid);
@@ -930,6 +951,8 @@ static void rtreeRelease(Rtree *pRtree){
sqlite3_finalize(pRtree->pReadParent);
sqlite3_finalize(pRtree->pWriteParent);
sqlite3_finalize(pRtree->pDeleteParent);
+ sqlite3_finalize(pRtree->pWriteAux);
+ sqlite3_free(pRtree->zReadAuxSql);
sqlite3_free(pRtree);
}
}
@@ -1018,6 +1041,7 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
RtreeCursor *pCsr = (RtreeCursor *)cur;
assert( pRtree->nCursor>0 );
freeCursorConstraints(pCsr);
+ sqlite3_finalize(pCsr->pReadAux);
sqlite3_free(pCsr->aPoint);
for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
sqlite3_free(pCsr);
@@ -1389,7 +1413,7 @@ static RtreeSearchPoint *rtreeSearchPointNew(
if( ii<RTREE_CACHE_SZ ){
assert( pCur->aNode[ii]==0 );
pCur->aNode[ii] = pCur->aNode[0];
- }else{
+ }else{
nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
}
pCur->aNode[0] = 0;
@@ -1560,6 +1584,10 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
/* Move to the next entry that matches the configured constraints. */
RTREE_QUEUE_TRACE(pCsr, "POP-Nx:");
+ if( pCsr->bAuxValid ){
+ pCsr->bAuxValid = 0;
+ sqlite3_reset(pCsr->pReadAux);
+ }
rtreeSearchPointPop(pCsr);
rc = rtreeStepToLeaf(pCsr);
return rc;
@@ -1594,7 +1622,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
if( p==0 ) return SQLITE_OK;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
- }else{
+ }else if( i<=pRtree->nDim2 ){
nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
@@ -1605,7 +1633,27 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
assert( pRtree->eCoordType==RTREE_COORD_INT32 );
sqlite3_result_int(ctx, c.i);
}
- }
+ }else{
+ if( !pCsr->bAuxValid ){
+ if( pCsr->pReadAux==0 ){
+ rc = sqlite3_prepare_v3(pRtree->db, pRtree->zReadAuxSql, -1, 0,
+ &pCsr->pReadAux, 0);
+ if( rc ) return rc;
+ }
+ sqlite3_bind_int64(pCsr->pReadAux, 1,
+ nodeGetRowid(pRtree, pNode, p->iCell));
+ rc = sqlite3_step(pCsr->pReadAux);
+ if( rc==SQLITE_ROW ){
+ pCsr->bAuxValid = 1;
+ }else{
+ sqlite3_reset(pCsr->pReadAux);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+ }
+ }
+ sqlite3_result_value(ctx,
+ sqlite3_column_value(pCsr->pReadAux, i - pRtree->nDim2 + 1));
+ }
return SQLITE_OK;
}
@@ -1683,14 +1731,17 @@ static int rtreeFilter(
int ii;
int rc = SQLITE_OK;
int iCell = 0;
+ sqlite3_stmt *pStmt;
rtreeReference(pRtree);
/* Reset the cursor to the same state as rtreeOpen() leaves it in. */
freeCursorConstraints(pCsr);
sqlite3_free(pCsr->aPoint);
+ pStmt = pCsr->pReadAux;
memset(pCsr, 0, sizeof(RtreeCursor));
pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
+ pCsr->pReadAux = pStmt;
pCsr->iStrategy = idxNum;
if( idxNum==1 ){
@@ -1853,10 +1904,14 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
*/
pIdxInfo->estimatedCost = 30.0;
pIdxInfo->estimatedRows = 1;
+ pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE;
return SQLITE_OK;
}
- if( p->usable && (p->iColumn>0 || p->op==SQLITE_INDEX_CONSTRAINT_MATCH) ){
+ if( p->usable
+ && ((p->iColumn>0 && p->iColumn<=pRtree->nDim2)
+ || p->op==SQLITE_INDEX_CONSTRAINT_MATCH)
+ ){
u8 op;
switch( p->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break;
@@ -2429,7 +2484,7 @@ static int SplitNode(
}else{
pLeft = pNode;
pRight = nodeNew(pRtree, pLeft->pParent);
- nodeReference(pLeft);
+ pLeft->nRef++;
}
if( !pLeft || !pRight ){
@@ -2919,6 +2974,7 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
rc = reinsertNodeContent(pRtree, pLeaf);
}
pRtree->pDeleted = pLeaf->pNext;
+ pRtree->nNodeRef--;
sqlite3_free(pLeaf);
}
@@ -3015,7 +3071,7 @@ static int rtreeConstraintError(Rtree *pRtree, int iCol){
static int rtreeUpdate(
sqlite3_vtab *pVtab,
int nData,
- sqlite3_value **azData,
+ sqlite3_value **aData,
sqlite_int64 *pRowid
){
Rtree *pRtree = (Rtree *)pVtab;
@@ -3023,6 +3079,12 @@ static int rtreeUpdate(
RtreeCell cell; /* New cell to insert if nData>1 */
int bHaveRowid = 0; /* Set to 1 after new rowid is determined */
+ if( pRtree->nNodeRef ){
+ /* Unable to write to the btree while another cursor is reading from it,
+ ** since the write might do a rebalance which would disrupt the read
+ ** cursor. */
+ return SQLITE_LOCKED_VTAB;
+ }
rtreeReference(pRtree);
assert(nData>=1);
@@ -3041,8 +3103,10 @@ static int rtreeUpdate(
*/
if( nData>1 ){
int ii;
+ int nn = nData - 4;
- /* Populate the cell.aCoord[] array. The first coordinate is azData[3].
+ if( nn > pRtree->nDim2 ) nn = pRtree->nDim2;
+ /* Populate the cell.aCoord[] array. The first coordinate is aData[3].
**
** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared
** with "column" that are interpreted as table constraints.
@@ -3050,13 +3114,12 @@ static int rtreeUpdate(
** This problem was discovered after years of use, so we silently ignore
** these kinds of misdeclared tables to avoid breaking any legacy.
*/
- assert( nData<=(pRtree->nDim2 + 3) );
#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
- for(ii=0; ii<nData-4; ii+=2){
- cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
- cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
+ for(ii=0; ii<nn; ii+=2){
+ cell.aCoord[ii].f = rtreeValueDown(aData[ii+3]);
+ cell.aCoord[ii+1].f = rtreeValueUp(aData[ii+4]);
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
rc = rtreeConstraintError(pRtree, ii+1);
goto constraint;
@@ -3065,9 +3128,9 @@ static int rtreeUpdate(
}else
#endif
{
- for(ii=0; ii<nData-4; ii+=2){
- cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
- cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
+ for(ii=0; ii<nn; ii+=2){
+ cell.aCoord[ii].i = sqlite3_value_int(aData[ii+3]);
+ cell.aCoord[ii+1].i = sqlite3_value_int(aData[ii+4]);
if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
rc = rtreeConstraintError(pRtree, ii+1);
goto constraint;
@@ -3077,10 +3140,10 @@ static int rtreeUpdate(
/* If a rowid value was supplied, check if it is already present in
** the table. If so, the constraint has failed. */
- if( sqlite3_value_type(azData[2])!=SQLITE_NULL ){
- cell.iRowid = sqlite3_value_int64(azData[2]);
- if( sqlite3_value_type(azData[0])==SQLITE_NULL
- || sqlite3_value_int64(azData[0])!=cell.iRowid
+ if( sqlite3_value_type(aData[2])!=SQLITE_NULL ){
+ cell.iRowid = sqlite3_value_int64(aData[2]);
+ if( sqlite3_value_type(aData[0])==SQLITE_NULL
+ || sqlite3_value_int64(aData[0])!=cell.iRowid
){
int steprc;
sqlite3_bind_int64(pRtree->pReadRowid, 1, cell.iRowid);
@@ -3099,16 +3162,16 @@ static int rtreeUpdate(
}
}
- /* If azData[0] is not an SQL NULL value, it is the rowid of a
+ /* If aData[0] is not an SQL NULL value, it is the rowid of a
** record to delete from the r-tree table. The following block does
** just that.
*/
- if( sqlite3_value_type(azData[0])!=SQLITE_NULL ){
- rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(azData[0]));
+ if( sqlite3_value_type(aData[0])!=SQLITE_NULL ){
+ rc = rtreeDeleteRowid(pRtree, sqlite3_value_int64(aData[0]));
}
- /* If the azData[] array contains more than one element, elements
- ** (azData[2]..azData[argc-1]) contain a new record to insert into
+ /* If the aData[] array contains more than one element, elements
+ ** (aData[2]..aData[argc-1]) contain a new record to insert into
** the r-tree structure.
*/
if( rc==SQLITE_OK && nData>1 ){
@@ -3133,6 +3196,16 @@ static int rtreeUpdate(
rc = rc2;
}
}
+ if( pRtree->nAux ){
+ sqlite3_stmt *pUp = pRtree->pWriteAux;
+ int jj;
+ sqlite3_bind_int64(pUp, 1, *pRowid);
+ for(jj=0; jj<pRtree->nAux; jj++){
+ sqlite3_bind_value(pUp, jj+2, aData[pRtree->nDim2+3+jj]);
+ }
+ sqlite3_step(pUp);
+ rc = sqlite3_reset(pUp);
+ }
}
constraint:
@@ -3289,18 +3362,18 @@ static int rtreeSqlInit(
#define N_STATEMENT 8
static const char *azSql[N_STATEMENT] = {
/* Write the xxx_node table */
- "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(:1, :2)",
- "DELETE FROM '%q'.'%q_node' WHERE nodeno = :1",
+ "INSERT OR REPLACE INTO '%q'.'%q_node' VALUES(?1, ?2)",
+ "DELETE FROM '%q'.'%q_node' WHERE nodeno = ?1",
/* Read and write the xxx_rowid table */
- "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = :1",
- "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(:1, :2)",
- "DELETE FROM '%q'.'%q_rowid' WHERE rowid = :1",
+ "SELECT nodeno FROM '%q'.'%q_rowid' WHERE rowid = ?1",
+ "INSERT OR REPLACE INTO '%q'.'%q_rowid' VALUES(?1, ?2)",
+ "DELETE FROM '%q'.'%q_rowid' WHERE rowid = ?1",
/* Read and write the xxx_parent table */
- "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = :1",
- "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(:1, :2)",
- "DELETE FROM '%q'.'%q_parent' WHERE nodeno = :1"
+ "SELECT parentnode FROM '%q'.'%q_parent' WHERE nodeno = ?1",
+ "INSERT OR REPLACE INTO '%q'.'%q_parent' VALUES(?1, ?2)",
+ "DELETE FROM '%q'.'%q_parent' WHERE nodeno = ?1"
};
sqlite3_stmt **appStmt[N_STATEMENT];
int i;
@@ -3308,14 +3381,25 @@ static int rtreeSqlInit(
pRtree->db = db;
if( isCreate ){
- char *zCreate = sqlite3_mprintf(
-"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);"
-"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);"
-"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,"
- " parentnode INTEGER);"
-"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))",
- zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize
- );
+ char *zCreate;
+ sqlite3_str *p = sqlite3_str_new(db);
+ int ii;
+ sqlite3_str_appendf(p,
+ "CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY,nodeno",
+ zDb, zPrefix);
+ for(ii=0; ii<pRtree->nAux; ii++){
+ sqlite3_str_appendf(p,",a%d",ii);
+ }
+ sqlite3_str_appendf(p,
+ ");CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY,data);",
+ zDb, zPrefix);
+ sqlite3_str_appendf(p,
+ "CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,parentnode);",
+ zDb, zPrefix);
+ sqlite3_str_appendf(p,
+ "INSERT INTO \"%w\".\"%w_node\"VALUES(1,zeroblob(%d))",
+ zDb, zPrefix, pRtree->iNodeSize);
+ zCreate = sqlite3_str_finish(p);
if( !zCreate ){
return SQLITE_NOMEM;
}
@@ -3337,7 +3421,17 @@ static int rtreeSqlInit(
rc = rtreeQueryStat1(db, pRtree);
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
- char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
+ char *zSql;
+ const char *zFormat;
+ if( i!=3 || pRtree->nAux==0 ){
+ zFormat = azSql[i];
+ }else {
+ /* An UPSERT is very slightly slower than REPLACE, but it is needed
+ ** if there are auxiliary columns */
+ zFormat = "INSERT INTO\"%w\".\"%w_rowid\"(rowid,nodeno)VALUES(?1,?2)"
+ "ON CONFLICT(rowid)DO UPDATE SET nodeno=excluded.nodeno";
+ }
+ zSql = sqlite3_mprintf(zFormat, zDb, zPrefix);
if( zSql ){
rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
appStmt[i], 0);
@@ -3346,6 +3440,32 @@ static int rtreeSqlInit(
}
sqlite3_free(zSql);
}
+ if( pRtree->nAux ){
+ pRtree->zReadAuxSql = sqlite3_mprintf(
+ "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
+ zDb, zPrefix);
+ if( pRtree->zReadAuxSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_str *p = sqlite3_str_new(db);
+ int ii;
+ char *zSql;
+ sqlite3_str_appendf(p, "UPDATE \"%w\".\"%w_rowid\"SET ", zDb, zPrefix);
+ for(ii=0; ii<pRtree->nAux; ii++){
+ if( ii ) sqlite3_str_append(p, ",", 1);
+ sqlite3_str_appendf(p,"a%d=?%d",ii,ii+2);
+ }
+ sqlite3_str_appendf(p, " WHERE rowid=?1");
+ zSql = sqlite3_str_finish(p);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT,
+ &pRtree->pWriteAux, 0);
+ sqlite3_free(zSql);
+ }
+ }
+ }
return rc;
}
@@ -3448,17 +3568,22 @@ static int rtreeInit(
int nDb; /* Length of string argv[1] */
int nName; /* Length of string argv[2] */
int eCoordType = (pAux ? RTREE_COORD_INT32 : RTREE_COORD_REAL32);
+ sqlite3_str *pSql;
+ char *zSql;
+ int ii = 4;
+ int iErr;
const char *aErrMsg[] = {
0, /* 0 */
"Wrong number of columns for an rtree table", /* 1 */
"Too few columns for an rtree table", /* 2 */
- "Too many columns for an rtree table" /* 3 */
+ "Too many columns for an rtree table", /* 3 */
+ "Auxiliary rtree columns must be last" /* 4 */
};
- int iErr = (argc<6) ? 2 : argc>(RTREE_MAX_DIMENSIONS*2+4) ? 3 : argc%2;
- if( aErrMsg[iErr] ){
- *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
+ assert( RTREE_MAX_AUX_COLUMN<256 ); /* Aux columns counted by a u8 */
+ if( argc>RTREE_MAX_AUX_COLUMN+3 ){
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[3]);
return SQLITE_ERROR;
}
@@ -3476,53 +3601,73 @@ static int rtreeInit(
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
- pRtree->nDim = (u8)((argc-4)/2);
- pRtree->nDim2 = pRtree->nDim*2;
- pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
pRtree->eCoordType = (u8)eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
- /* Figure out the node size to use. */
- rc = getNodeSize(db, pRtree, isCreate, pzErr);
/* Create/Connect to the underlying relational database schema. If
** that is successful, call sqlite3_declare_vtab() to configure
** the r-tree table schema.
*/
- if( rc==SQLITE_OK ){
- if( (rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate)) ){
- *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ pSql = sqlite3_str_new(db);
+ sqlite3_str_appendf(pSql, "CREATE TABLE x(%s", argv[3]);
+ for(ii=4; ii<argc; ii++){
+ if( argv[ii][0]=='+' ){
+ pRtree->nAux++;
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]+1);
+ }else if( pRtree->nAux>0 ){
+ break;
}else{
- char *zSql = sqlite3_mprintf("CREATE TABLE x(%s", argv[3]);
- char *zTmp;
- int ii;
- for(ii=4; zSql && ii<argc; ii++){
- zTmp = zSql;
- zSql = sqlite3_mprintf("%s, %s", zTmp, argv[ii]);
- sqlite3_free(zTmp);
- }
- if( zSql ){
- zTmp = zSql;
- zSql = sqlite3_mprintf("%s);", zTmp);
- sqlite3_free(zTmp);
- }
- if( !zSql ){
- rc = SQLITE_NOMEM;
- }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
- *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
- }
- sqlite3_free(zSql);
+ pRtree->nDim2++;
+ sqlite3_str_appendf(pSql, ",%s", argv[ii]);
}
}
-
- if( rc==SQLITE_OK ){
- *ppVtab = (sqlite3_vtab *)pRtree;
+ sqlite3_str_appendf(pSql, ");");
+ zSql = sqlite3_str_finish(pSql);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else if( ii<argc ){
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[4]);
+ rc = SQLITE_ERROR;
+ }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }
+ sqlite3_free(zSql);
+ if( rc ) goto rtreeInit_fail;
+ pRtree->nDim = pRtree->nDim2/2;
+ if( pRtree->nDim<1 ){
+ iErr = 2;
+ }else if( pRtree->nDim2>RTREE_MAX_DIMENSIONS*2 ){
+ iErr = 3;
+ }else if( pRtree->nDim2 % 2 ){
+ iErr = 1;
}else{
- assert( *ppVtab==0 );
- assert( pRtree->nBusy==1 );
- rtreeRelease(pRtree);
+ iErr = 0;
+ }
+ if( iErr ){
+ *pzErr = sqlite3_mprintf("%s", aErrMsg[iErr]);
+ goto rtreeInit_fail;
+ }
+ pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
+
+ /* Figure out the node size to use. */
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
+ if( rc ) goto rtreeInit_fail;
+ rc = rtreeSqlInit(pRtree, db, argv[1], argv[2], isCreate);
+ if( rc ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ goto rtreeInit_fail;
}
+
+ *ppVtab = (sqlite3_vtab *)pRtree;
+ return SQLITE_OK;
+
+rtreeInit_fail:
+ if( rc==SQLITE_OK ) rc = SQLITE_ERROR;
+ assert( *ppVtab==0 );
+ assert( pRtree->nBusy==1 );
+ rtreeRelease(pRtree);
return rc;
}
@@ -3751,7 +3896,7 @@ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){
** two tables are:
**
** CREATE TABLE %_parent(nodeno INTEGER PRIMARY KEY, parentnode INTEGER)
-** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER)
+** CREATE TABLE %_rowid(rowid INTEGER PRIMARY KEY, nodeno INTEGER, ...)
**
** In both cases, this function checks that there exists an entry with
** IPK value iKey and the second column set to iVal.
@@ -3766,8 +3911,8 @@ static void rtreeCheckMapping(
int rc;
sqlite3_stmt *pStmt;
const char *azSql[2] = {
- "SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?",
- "SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?"
+ "SELECT parentnode FROM %Q.'%q_parent' WHERE nodeno=?1",
+ "SELECT nodeno FROM %Q.'%q_rowid' WHERE rowid=?1"
};
assert( bLeaf==0 || bLeaf==1 );
@@ -3951,6 +4096,7 @@ static int rtreeCheckTable(
RtreeCheck check; /* Common context for various routines */
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
int bEnd = 0; /* True if transaction should be closed */
+ int nAux = 0; /* Number of extra columns. */
/* Initialize the context object */
memset(&check, 0, sizeof(check));
@@ -3966,11 +4112,21 @@ static int rtreeCheckTable(
bEnd = 1;
}
+ /* Find the number of auxiliary columns */
+ if( check.rc==SQLITE_OK ){
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
+ if( pStmt ){
+ nAux = sqlite3_column_count(pStmt) - 2;
+ sqlite3_finalize(pStmt);
+ }
+ check.rc = SQLITE_OK;
+ }
+
/* Find number of dimensions in the rtree table. */
pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
if( pStmt ){
int rc;
- check.nDim = (sqlite3_column_count(pStmt) - 1) / 2;
+ check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
if( check.nDim<1 ){
rtreeCheckAppendMsg(&check, "Schema corrupt or not an rtree");
}else if( SQLITE_ROW==sqlite3_step(pStmt) ){
diff --git a/ext/rtree/rtree1.test b/ext/rtree/rtree1.test
index ac6e8d9..bdaad54 100644
--- a/ext/rtree/rtree1.test
+++ b/ext/rtree/rtree1.test
@@ -476,11 +476,11 @@ foreach {tn sql_template testdata} {
}
3 "UPDATE %CONF% t1 SET idx = 2 WHERE idx = 4" {
- ROLLBACK 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
- ABORT 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
- IGNORE 1 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
- FAIL 1 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
- REPLACE 1 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
+ ROLLBACK 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6}
+ ABORT 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
+ IGNORE 0 0 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
+ FAIL 0 1 {1 1 2 3 4 2 2 3 4 5 3 3 4 5 6 4 4 5 6 7}
+ REPLACE 0 0 {1 1 2 3 4 2 4 5 6 7 3 3 4 5 6}
}
3 "UPDATE %CONF% t1 SET idx = ((idx+1)%5)+1 WHERE idx > 2" {
@@ -609,5 +609,43 @@ do_execsql_test 15.2 {
COMMIT;
}
+# Test cases for the new auxiliary columns feature
+#
+do_catchsql_test 16.100 {
+ CREATE VIRTUAL TABLE t16 USING rtree(id,x0,x1,y0,+aux1,x1);
+} {1 {Auxiliary rtree columns must be last}}
+do_test 16.110 {
+ set sql {
+ CREATE VIRTUAL TABLE t16 USING rtree(
+ id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
+ }
+ for {set i 12} {$i<=100} {incr i} {
+ append sql ", +a$i"
+ }
+ append sql ");"
+ execsql $sql
+} {}
+do_test 16.120 {
+ set sql {
+ CREATE VIRTUAL TABLE t16b USING rtree(
+ id, x00, x01, x10, x11, x20, x21, x30, x31, x40, x41
+ }
+ for {set i 12} {$i<=101} {incr i} {
+ append sql ", +a$i"
+ }
+ append sql ");"
+ catchsql $sql
+} {1 {Too many columns for an rtree table}}
+
+do_execsql_test 16.130 {
+ DROP TABLE IF EXISTS rt1;
+ CREATE VIRTUAL TABLE rt1 USING rtree(id, x1, x2, +aux);
+ INSERT INTO rt1 VALUES(1, 1, 2, 'aux1');
+ INSERT INTO rt1 VALUES(2, 2, 3, 'aux2');
+ INSERT INTO rt1 VALUES(3, 3, 4, 'aux3');
+ INSERT INTO rt1 VALUES(4, 4, 5, 'aux4');
+ SELECT * FROM rt1 WHERE id IN (1, 2, 3, 4);
+} {1 1.0 2.0 aux1 2 2.0 3.0 aux2 3 3.0 4.0 aux3 4 4.0 5.0 aux4}
+
expand_all_sql db
finish_test
diff --git a/ext/rtree/rtree3.test b/ext/rtree/rtree3.test
index 1d863c6..e37d18e 100644
--- a/ext/rtree/rtree3.test
+++ b/ext/rtree/rtree3.test
@@ -81,7 +81,7 @@ do_faultsim_test rtree3-2 -faults oom* -prep {
do_malloc_test rtree3-3.prep {
faultsim_delete_and_reopen
execsql {
- CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2);
+ CREATE VIRTUAL TABLE rt USING rtree(ii, x1, x2, y1, y2, +a1, +a2);
INSERT INTO rt VALUES(NULL, 3, 5, 7, 9);
}
faultsim_save_and_close
diff --git a/ext/rtree/rtree6.test b/ext/rtree/rtree6.test
index 4066048..6800b4b 100644
--- a/ext/rtree/rtree6.test
+++ b/ext/rtree/rtree6.test
@@ -74,42 +74,48 @@ do_test rtree6-1.5 {
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:C0}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0
+ `--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:C0}
- 0 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0
+ `--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 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:
+ `--SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)
}
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=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1
+ `--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:C0E1}
- 0 1 1 {SEARCH TABLE t2 USING AUTOMATIC PARTIAL COVERING INDEX (v=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:C0E1
+ `--SEARCH TABLE t2 USING AUTOMATIC PARTIAL 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 1 1 {SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 2:
+ `--SEARCH TABLE t2 USING INTEGER PRIMARY KEY (rowid=?)
}
do_execsql_test rtree6-3.1 {
diff --git a/ext/rtree/rtree8.test b/ext/rtree/rtree8.test
index ac18fb4..68b68c4 100644
--- a/ext/rtree/rtree8.test
+++ b/ext/rtree/rtree8.test
@@ -38,11 +38,20 @@ do_test rtree8-1.1.1 {
} {}
do_test rtree8-1.1.2 {
set res [list]
- db eval { SELECT * FROM t1 } {
- lappend res $x1 $x2
+ set rc [catch {
+ db eval { SELECT * FROM t1 } {
+ lappend res $x1 $x2
+ if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
+ }
+ } msg];
+ lappend rc $msg
+ set rc
+} {1 {database table is locked}}
+do_test rtree8-1.1.2b {
+ db eval { SELECT * FROM t1 ORDER BY +id } {
if {$id==3} { db eval { DELETE FROM t1 WHERE id>3 } }
}
- set res
+ db eval {SELECT x1, x2 FROM t1}
} {1 3 2 4 3 5}
do_test rtree8-1.1.3 {
execsql { SELECT * FROM t1 }
@@ -170,5 +179,29 @@ do_test rtree8-5.4 {
} {}
do_rtree_integrity_test rtree8-5.5 t2
+# 2018-05-24
+# The following script caused an assertion fault and/or segfault
+# prior to the fix that prevents simultaneous reads and writes on
+# the same rtree virtual table.
+#
+do_test rtree8-6.1 {
+ db close
+ sqlite3 db :memory:
+ db eval {
+ PRAGMA page_size=512;
+ CREATE VIRTUAL TABLE t1 USING rtree(id,x1,x2,y1,y2);
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c WHERE x<49)
+ INSERT INTO t1 SELECT x, x, x+1, x, x+1 FROM c;
+ }
+ set rc [catch {
+ db eval {SELECT id FROM t1} x {
+ db eval {DELETE FROM t1 WHERE id=$x(id)}
+ }
+ } msg]
+ lappend rc $msg
+} {1 {database table is locked}}
+
+
+
finish_test
diff --git a/ext/rtree/rtreeC.test b/ext/rtree/rtreeC.test
index a5e5b97..19a1f7f 100644
--- a/ext/rtree/rtreeC.test
+++ b/ext/rtree/rtreeC.test
@@ -29,31 +29,35 @@ 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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:
+ `--SCAN TABLE t
}
do_execsql_test 2.0 {
@@ -82,31 +86,35 @@ 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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE r_tree VIRTUAL TABLE INDEX 2:
+ `--SCAN TABLE t
}
#-------------------------------------------------------------------------
@@ -119,20 +127,25 @@ do_execsql_test 3.1 {
}
do_eqp_test 3.2.1 { SELECT * FROM t1 CROSS JOIN t2 } {
- 0 0 0 {SCAN TABLE t1}
- 0 1 1 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t2
+ `--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:}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--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}
+ QUERY PLAN
+ |--SCAN TABLE t3 VIRTUAL TABLE INDEX 2:
+ `--SCAN TABLE t1
}
#--------------------------------------------------------------------
@@ -165,7 +178,7 @@ do_execsql_test 4.3 {
reset_db
do_execsql_test 5.1 {
CREATE TABLE t1(x PRIMARY KEY, y);
- CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
+ CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2, +d1);
INSERT INTO t1(x) VALUES(1);
INSERT INTO t1(x) SELECT x+1 FROM t1; -- 2
@@ -179,7 +192,7 @@ do_execsql_test 5.1 {
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;
+ INSERT INTO rt SELECT x, x, x+1, printf('x%04xy',x) FROM t1 WHERE x<=5;
}
do_rtree_integrity_test 5.1.1 rt
@@ -189,8 +202,9 @@ do_rtree_integrity_test 5.1.1 rt
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:}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCAN TABLE rt VIRTUAL TABLE INDEX 1:
}
# Now create enough ANALYZE data to tell SQLite that virtual table "rt"
@@ -205,8 +219,9 @@ 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=?)}
+ QUERY PLAN
+ |--SCAN TABLE rt VIRTUAL TABLE INDEX 2:
+ `--SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (x=?)
}
# Delete the ANALYZE data. "t1" should be the outer loop again.
@@ -217,8 +232,9 @@ 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:}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCAN TABLE rt VIRTUAL TABLE INDEX 1:
}
# This time create and attach a database that contains ANALYZE data for
@@ -241,8 +257,9 @@ do_test 5.7 {
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:}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCAN TABLE rt VIRTUAL TABLE INDEX 1:
}
#--------------------------------------------------------------------
@@ -299,9 +316,9 @@ do_execsql_test 7.0 {
}
-proc do_eqp_execsql_test {tn sql res} {
- set query "EXPLAIN QUERY PLAN $sql ; $sql "
- uplevel [list do_execsql_test $tn $query $res]
+proc do_eqp_execsql_test {tn sql res1 res2} {
+ do_eqp_test $tn.1 $sql $res1
+ do_execsql_test $tn.2 $sql $res2
}
do_eqp_execsql_test 7.1 {
@@ -309,9 +326,11 @@ do_eqp_execsql_test 7.1 {
ON (y1 BETWEEN ymin AND ymax)
WHERE (x1 BETWEEN xmin AND xmax);
} {
- 0 0 0 {SCAN TABLE xdir}
- 0 1 2 {SCAN TABLE ydir}
- 0 2 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B2D3B0D1}
+ QUERY PLAN
+ |--SCAN TABLE xdir
+ |--SCAN TABLE ydir
+ `--SCAN TABLE rt VIRTUAL TABLE INDEX 2:B2D3B0D1
+} {
2 4
}
@@ -320,10 +339,11 @@ do_eqp_execsql_test 7.2 {
ON (y1 BETWEEN ymin AND ymax)
WHERE (x1 BETWEEN xmin AND xmax);
} {
- 0 0 0 {SCAN TABLE xdir}
- 0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
- 0 2 2 {SCAN TABLE ydir}
-
+ QUERY PLAN
+ |--SCAN TABLE xdir
+ |--SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1
+ `--SCAN TABLE ydir
+} {
5 1 2 7 12 14 {}
5 2 2 7 8 12 10
5 4 5 5 10 10 10
@@ -334,9 +354,11 @@ do_eqp_execsql_test 7.3 {
ON (y1 BETWEEN ymin AND ymax)
WHERE (x1 BETWEEN xmin AND xmax);
} {
- 0 0 0 {SCAN TABLE xdir}
- 0 1 1 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
- 0 2 2 {SCAN TABLE ydir}
+ QUERY PLAN
+ |--SCAN TABLE xdir
+ |--SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1
+ `--SCAN TABLE ydir
+} {
2 4
}
@@ -345,9 +367,11 @@ do_eqp_execsql_test 7.4 {
ON (y1 BETWEEN ymin AND ymax)
WHERE (x1 BETWEEN xmin AND xmax);
} {
- 0 0 1 {SCAN TABLE xdir}
- 0 1 0 {SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1}
- 0 2 2 {SCAN TABLE ydir}
+ QUERY PLAN
+ |--SCAN TABLE xdir
+ |--SCAN TABLE rt VIRTUAL TABLE INDEX 2:B0D1
+ `--SCAN TABLE ydir
+} {
2 4
}
diff --git a/ext/rtree/rtreeH.test b/ext/rtree/rtreeH.test
new file mode 100644
index 0000000..bff765d
--- /dev/null
+++ b/ext/rtree/rtreeH.test
@@ -0,0 +1,80 @@
+# 2018-05-16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file contains tests for the r-tree module, specifically the
+# auxiliary column mechanism.
+
+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
+ifcapable !rtree { finish_test ; return }
+
+do_execsql_test rtreeH-100 {
+ CREATE VIRTUAL TABLE t1 USING rtree(id,x0,x1,y0,y1,+label,+other);
+ INSERT INTO t1(x0,x1,y0,y1,label) VALUES
+ (0,10,0,10,'lower-left corner'),
+ (0,10,90,100,'upper-left corner'),
+ (90,100,0,10,'lower-right corner'),
+ (90,100,90,100,'upper-right corner'),
+ (40,60,40,60,'center'),
+ (0,5,0,100,'left edge'),
+ (95,100,0,100,'right edge'),
+ (0,100,0,5,'bottom edge'),
+ (0,100,95,100,'top edge'),
+ (0,100,0,100,'the whole thing'),
+ (0,50,0,100,'left half'),
+ (51,100,0,100,'right half'),
+ (0,100,0,50,'bottom half'),
+ (0,100,51,100,'top half');
+} {}
+do_execsql_test rtreeH-101 {
+ SELECT * FROM t1_rowid ORDER BY rowid
+} {1 1 {lower-left corner} {} 2 1 {upper-left corner} {} 3 1 {lower-right corner} {} 4 1 {upper-right corner} {} 5 1 center {} 6 1 {left edge} {} 7 1 {right edge} {} 8 1 {bottom edge} {} 9 1 {top edge} {} 10 1 {the whole thing} {} 11 1 {left half} {} 12 1 {right half} {} 13 1 {bottom half} {} 14 1 {top half} {}}
+
+do_execsql_test rtreeH-102 {
+ SELECT * FROM t1 WHERE rowid=5;
+} {5 40.0 60.0 40.0 60.0 center {}}
+do_execsql_test rtreeH-103 {
+ SELECT * FROM t1 WHERE label='center';
+} {5 40.0 60.0 40.0 60.0 center {}}
+
+do_rtree_integrity_test rtreeH-110 t1
+
+do_execsql_test rtreeH-120 {
+ SELECT label FROM t1 WHERE x1<=50 ORDER BY id
+} {{lower-left corner} {upper-left corner} {left edge} {left half}}
+do_execsql_test rtreeH-121 {
+ SELECT label FROM t1 WHERE x1<=50 AND label NOT LIKE '%corner%' ORDER BY id
+} {{left edge} {left half}}
+
+do_execsql_test rtreeH-200 {
+ WITH RECURSIVE
+ c1(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM c1 WHERE x<99),
+ c2(y) AS (VALUES(0) UNION ALL SELECT y+1 FROM c2 WHERE y<99)
+ INSERT INTO t1(id, x0,x1,y0,y1,label)
+ SELECT 1000+x+y*100, x, x+1, y, y+1, printf('box-%d,%d',x,y) FROM c1, c2;
+} {}
+
+do_execsql_test rtreeH-210 {
+ SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
+ ORDER BY id;
+} {box-48,48 box-49,48 box-48,49 box-49,49}
+
+do_execsql_test rtreeH-300 {
+ UPDATE t1 SET label='x'||label
+ WHERE x0>=49 AND x1<=50 AND y0>=49 AND y1<=50;
+ SELECT label FROM t1 WHERE x0>=48 AND x1<=50 AND y0>=48 AND y1<=50
+ ORDER BY id;
+} {box-48,48 box-49,48 box-48,49 xbox-49,49}
+
+
+finish_test
diff --git a/main.mk b/main.mk
index e5722c5..8dc423c 100644
--- a/main.mk
+++ b/main.mk
@@ -72,7 +72,7 @@ LIBOBJ+= vdbe.o parse.o \
random.o resolve.o rowset.o rtree.o \
select.o sqlite3rbu.o status.o stmt.o \
table.o threads.o tokenize.o treeview.o trigger.o \
- update.o userauth.o util.o vacuum.o \
+ update.o upsert.o userauth.o util.o vacuum.o \
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
utf.o vtab.o
@@ -162,6 +162,7 @@ SRC = \
$(TOP)/src/trigger.c \
$(TOP)/src/utf.c \
$(TOP)/src/update.c \
+ $(TOP)/src/upsert.c \
$(TOP)/src/util.c \
$(TOP)/src/vacuum.c \
$(TOP)/src/vdbe.c \
@@ -500,7 +501,8 @@ FUZZDATA = \
$(TOP)/test/fuzzdata2.db \
$(TOP)/test/fuzzdata3.db \
$(TOP)/test/fuzzdata4.db \
- $(TOP)/test/fuzzdata5.db
+ $(TOP)/test/fuzzdata5.db \
+ $(TOP)/test/fuzzdata6.db
# Standard options to testfixture
#
diff --git a/manifest b/manifest
index 0b37262..b1ee444 100644
--- a/manifest
+++ b/manifest
@@ -1,22 +1,23 @@
-C Version\s3.23.1
-D 2018-04-10T17:39:29.721
+C Version\s3.24.0
+D 2018-06-04T19:24:41.225
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
-F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
+F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc bdcad21b027a56a73e54a1121cfb9edd0a35c0abfa53aa12c2f996006ff99960
+F Makefile.msc 681fb88cccf1fd58c0b9648f6a09b75332206ef72ca76012ad11699c320cec5f
F README.md 7764d56778d567913ef11c82da9ab94aefa0826f7c243351e4e2d7adaef6f373
-F VERSION 7169eb6959db9ad1b7004ae3b754ef6e703eb7d8dde3b07d2e63103413eb25fb
+F VERSION b7c9d1d11cb70ef8e90cfcf3c944aa58a9f801cc2ad487eebb0a110c16dfc2df
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
-F autoconf/Makefile.am 2c274948734e03c51790ff51468f91db8d570bcca864284d9c6d6e777264cd7e
-F autoconf/Makefile.msc 1223d1520e0b833041ad87b377fae61cc3e08d14c5aae4c1a9e36249225bd4e6
+F autoconf/Makefile.am 465fb475ee5a4b8a63d26c889cb36a4edbff0325b6859eb29a13cdf5c91b005c
+F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
+F autoconf/Makefile.msc 6a46d0659d6e4a25346102bcba40a7fb4b0b8b2dc4faabdf0187488c6dd580d6
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
-F autoconf/configure.ac 18fca06f884213be062dd5e07c5297079cc45893d9cd3f522ce426e715033e3d
+F autoconf/configure.ac 5f6cf281ae675685335ce7f43f8c2d59d3ac8ba4174a57b8e83097c5e10dcf4e
F autoconf/tea/Makefile.in b438a7020446c8a8156e8d97c8914a04833da6fd
F autoconf/tea/README 3e9a3c060f29a44344ab50aec506f4db903fb873
F autoconf/tea/aclocal.m4 52c47aac44ce0ddb1f918b6993e8beb8eee88f43
@@ -32,10 +33,10 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 6376abec766e9a0785178b1823b5a587e9f1ccbc
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
-F configure 41d0e05b0d289c1c981aafe5c4070713c8e70b5a7d3472360764a3fce08a82a8 x
+F configure 80e2dcad8ab88aacc58b55eb0e395f79184b45fcfaa3f36fc20d2e71cfa0a7e4 x
F configure.ac d4529ebb26ae046269334f1dac65f2b1d6927c2efe22b2ec24dce24dfe4f83dd
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
-F doc/lemon.html 278113807f49d12d04179a93fab92b5b917a08771152ca7949d34e928efa3941
+F doc/lemon.html ac63db056bce24b7368e29319cd1a7eb5f1798cc85922d96a80b6c3a4ff9f51b
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
@@ -44,8 +45,8 @@ F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74
F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
-F ext/expert/expert1.test fd21496d8e52c817a7741f467f42b0502c0ac7e07dcdd1d6e15a3e8154ed4e41
-F ext/expert/sqlite3expert.c 1dfa561e64dc0f89d56b96e6afda87468c34b43604c2df50c47e3f4362778fb2
+F ext/expert/expert1.test baa05800dc1c569faa7ce6232cead0fcedc07fea038b3a2489eaaf710831d546
+F ext/expert/sqlite3expert.c 89b7b59be610b929958db02416539a46ac088fd84f81623c4190aff62e92c3c4
F ext/expert/sqlite3expert.h af6354f8ee5c9e025024e63fec3bd640a802afcc3099a44d804752cf0791d811
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@@ -78,11 +79,11 @@ 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 f1c58503bc81c3dab1a70b25e146878ae40fccc716fd7c9b817995b661bc896f
+F ext/fts3/fts3.c b577921c7e56ea27893da06691f15721801375ebd411a0e24ec5f6fec3c572c3
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
-F ext/fts3/fts3Int.h eb2502000148e80913b965db3e59f29251266d0a
+F ext/fts3/fts3Int.h 3378157f383540857a466420b8279626204434c3eb0dc948ad9bcd3991fc41f5
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
-F ext/fts3/fts3_expr.c dfd571a24412779ac01f25c01d888c6ef7b2d0ef
+F ext/fts3/fts3_expr.c 3b1dbceddd8622599f3cc2626897667fe40487aaa1676707d6c37ec5a8422fc1
F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
F ext/fts3/fts3_icu.c deb46f7020d87ea7a14a433fb7a7f4bef42a9652
@@ -187,7 +188,7 @@ F ext/fts5/test/fts5near.test 211477940142d733ac04fad97cb24095513ab2507073a99c27
F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618834bf1fcc3e7b84da
F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1
F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b
-F ext/fts5/test/fts5plan.test e30e8378441114ef6977a3dc24ecd203caa670d782124dfc9a6e9af09f7abc4d
+F ext/fts5/test/fts5plan.test 00dc4c974938b509db7cb3680407f068ee6e9cc824f492f68cb741a7b679fe41
F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15
F ext/fts5/test/fts5porter2.test 0d251a673f02fa13ca7f011654873b3add20745f7402f108600a23e52d8c7457
F ext/fts5/test/fts5prefix.test a0fa67b06650f2deaa7bf27745899d94e0fb547ad9ecbd08bfad98c04912c056
@@ -204,7 +205,7 @@ F ext/fts5/test/fts5synonym2.test b54cce5c34ec08ed616f646635538ae82e34a0e28f947e
F ext/fts5/test/fts5tok1.test ce6551e41ff56f30b69963577324624733bed0d1753589f06120d664d9cd45c9
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
F ext/fts5/test/fts5tokenizer.test 6aeb5e8061ffc0ff9a5299f27beaee3b2b4b8b336d4f107262bca338bea8f8e9
-F ext/fts5/test/fts5unicode.test 1e5570df758f7e0b27ff0e087ee96f559d5cc9ade80afa9421537a8a20a7cfbf
+F ext/fts5/test/fts5unicode.test 17056f4efe6b0a5d4f41fdf7a7dc9af2873004562eaa899d40633b93dc95f5a9
F ext/fts5/test/fts5unicode2.test 9b3df486de05fb4bde4aa7ee8de2e6dae1df6eb90e3f2e242c9383b95d314e3e
F ext/fts5/test/fts5unicode3.test c3caecbe8264629ffe653b43ca5790b9793eba4422f92203e5247558e5a534e7
F ext/fts5/test/fts5unindexed.test 9021af86a0fb9fc616f7a69a996db0116e7936d0db63892db6bafabbec21af4d
@@ -271,16 +272,16 @@ F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a2
F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb1967af7
F ext/misc/btreeinfo.c 78c8c57d325185ccc04b7679e5b020e34a4d9c87453e6b7ac943d0a26cee3256
F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005
-F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
-F ext/misc/completion.c 0d0bd16378415b982e7119baddef52a0d2cc25860c238a9d2832b0cc6a84a16d
+F ext/misc/closure.c fe928228e8dfb2f00227311c203ccba9c2e5561f4f6de6da87e5b4a30cd8af15
+F ext/misc/completion.c e75b8886a2531f9a7ec02dab5f179bb37e6bd46b5da7665a6cbf2dfbe2daa483
F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
-F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
-F ext/misc/dbdump.c 22018e00eb50e9ebf9067c92d4e7162dc5006a3efc4e0c19bc3829825a1043b0
+F ext/misc/csv.c 65297bcce8d5acd5aadef42acbe739aef5a2ef5e74c7b73361ca19f3e21de657
+F ext/misc/dbdump.c 69ef1be5b210538f77dfcc6fcb55b4b5f5e98b1e0bcfd67d818711e10761db4d
F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0e3
F ext/misc/fileio.c 48c7751c78fc4cdd29d8c862fd2f3f98bbfefa2a3cf1ca1496df4bf02eb8cded
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
-F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
+F ext/misc/json1.c 02f5f0d9c131df79f4ee168d2b426f0f0d273b7771fc0bb5293c4e7692d9a2ee
F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567
F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
@@ -297,6 +298,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
F ext/misc/spellfix.c 54d650f44f3a69a851814791bd4d304575cdbbf78d96d4f0801b44a8f31a58c5
F ext/misc/sqlar.c 57d5bc45cd5492208e451f697404be88f8612527d64c9d42f96b325b64983d74
F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
+F ext/misc/templatevtab.c 8251b31011dd00fc38e739c78c234c930be42b3b274bbe0493b79cd40db02a9e
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/unionvtab.c 0b3173f69b8899da640a13a345dc5ef1400199405f738abe6145b2454195b8ff
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
@@ -306,8 +308,8 @@ F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
F ext/misc/zipfile.c c4de8f0ad446ce4a49aae11ff7b771cd7af60d7136c0bcfb53da1475b9075e79
F ext/misc/zorder.c b0ff58fa643afa1d846786d51ea8d5c4b6b35aa0254ab5a82617db92f3adda64
-F ext/rbu/rbu.c ea7d1b7eb44c123a2a619332e19fe5313500705c4a58aaa1887905c0d83ffc2e
-F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
+F ext/rbu/rbu.c 4ae41f01d78dfae2204a63bcf39d5eb3548d039b094c29eec9a8d6683a66a5d3
+F ext/rbu/rbu1.test 41123c64e8c88bd14eb7d3f8562f37fa87aaeb154b1eade2881de21d3504be55
F ext/rbu/rbu10.test 1846519a438697f45e9dcb246908af81b551c29e1078d0304fae83f1fed7e9ee
F ext/rbu/rbu11.test 9bc68c2d3dbeb1720153626e3bd0466dcc017702
F ext/rbu/rbu12.test bde22ed0004dd5d1888c72a84ae407e574aeae16
@@ -322,7 +324,7 @@ F ext/rbu/rbu9.test 0806d1772c9f4981774ff028de6656e4183082af
F ext/rbu/rbuA.test 4e58e46e60d4064248614c43303d71f1b18cc804dd834ce6a913b3861828b28d
F ext/rbu/rbuB.test c25bc325b8072a766e56bb76c001866b405925c2
F ext/rbu/rbuC.test efe47db508a0269b683cb2a1913a425ffd39a831
-F ext/rbu/rbu_common.tcl ebb8d81f44dc20e360cff1f34eb2ad0def33128805c5b36afcc44ab338509589
+F ext/rbu/rbu_common.tcl acfb7fbbaf8d46a9f6f6a5ec795616c84d705e1565d918afe43f0ff53ea0efa5
F ext/rbu/rbucollate.test 86d6fc9b8f59a27b7b5a6e20b5e29816d338a0dbdea8c54bfcc549a0d437f3ea
F ext/rbu/rbucrash.test 61470d977a06a0abc2ec35b05d82a1d7d87d10f4ffabad14c1c231edc942ad66
F ext/rbu/rbucrash2.test b2ecbdd7bb72c88bd217c65bd00dafa07f7f2d4d
@@ -337,10 +339,11 @@ F ext/rbu/rbumulti.test 2cf153ab3d5861ff26517dc6cbaec430787a59f1d50e8771fe7a7529
F ext/rbu/rbuprogress.test 1849d4e0e50616edf5ce75ce7db86622e656b5cf
F ext/rbu/rburesume.test 8acb77f4a422ff55acfcfc9cc15a5cb210b1de83
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
+F ext/rbu/rbusplit.test 69271c790732b28bd465551d80b0a9a3f074e189896ee8490ce56d22078c124d
F ext/rbu/rbutemplimit.test cd553a9288d515d0b5f87d277e76fd18c4aa740b761e7880fab11ce986ea18d1
F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8
F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa
-F ext/rbu/sqlite3rbu.c f6e9ca388b5d4680fbf266a4d10a21aec11d6baf48f6d06fd53f6b205fad959f
+F ext/rbu/sqlite3rbu.c f438fea899d15d13ff3e3133242b9e378c37b5a3d76add8c342c68bdd65c6819
F ext/rbu/sqlite3rbu.h b42bcd4d8357268c6c39ab2a60b29c091e89328fa8cc49c8fac5ab8d007e79b2
F ext/rbu/test_rbu.c baa23eb28457580673d2175e5f0c29ced0cd320ee819b13ad362398c53b96e90
F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15
@@ -353,24 +356,25 @@ F ext/repair/test/checkfreelist01.test 3e8aa6aeb4007680c94a8d07b41c339aa635cc782
F ext/repair/test/checkindex01.test 6945d0ffc0c1dc993b2ce88036b26e0f5d6fcc65da70fc9df27c2647bb358b0f
F ext/repair/test/test.tcl 686d76d888dffd021f64260abf29a55c57b2cedfa7fc69150b42b1d6119aac3c
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
-F ext/rtree/rtree.c bc61010e978b5b8ae6dbb90274a2fbb5db5ff5e2880b5c6e8abd48eea77264db
+F ext/rtree/rtree.c cb6d4bd43c118354fe5b5213843da058259467ecdbac0c6f71ead0fd89acf4ec
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
-F ext/rtree/rtree1.test 47e2095bebea6813754fd7afa6a20e2b7b4ebcd5cb7dbcb6932b6c9f86bbf972
+F ext/rtree/rtree1.test 309afc04d4287542b2cd74f933296832cc681c7b014d9405cb329b62053a5349
F ext/rtree/rtree2.test 5f25b01acd03470067a2d52783b2eb0a50bf836803d4342d20ca39e541220fe2
-F ext/rtree/rtree3.test 2cafe8265d1ff28f206fce88d114f208349df482
+F ext/rtree/rtree3.test 4ee5d7df86040efe3d8d84f141f2962a7745452200a7cba1db06f86d97050499
F ext/rtree/rtree4.test 304de65d484540111b896827e4261815e5dca4ce28eeecd58be648cd73452c4b
F ext/rtree/rtree5.test 49c9041d713d54560b315c2c7ef7207ee287eba1b20f8266968a06f2e55d3142
-F ext/rtree/rtree6.test 916a641d2beac01b9880871ff07612d56c1e466190a27c82ab36ffd58be03b9f
+F ext/rtree/rtree6.test 593e0d36510d5ac1d1fb39b018274ff17604fe8fdca8cf1f8e16559cea1477f4
F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5
-F ext/rtree/rtree8.test 649f5a37ec656028a4a32674b9b1183104285a7625a09d2a8f52a1cef72c93f2
+F ext/rtree/rtree8.test 924926d7c64ac59fcca0809de472d9dd73c612f54daae1cf992bdd7dac90305b
F ext/rtree/rtree9.test c646f12c8c1c68ef015c6c043d86a0c42488e2e68ed1bb1b0771a7ca246cbabf
F ext/rtree/rtreeA.test 20623ca337ca3bd7e008cc9fb49e44dbe97f1a80b238e10a12bb4afcd0da3776
F ext/rtree/rtreeB.test 4cec297f8e5c588654bbf3c6ed0903f10612be8a2878055dd25faf8c71758bc9
-F ext/rtree/rtreeC.test d9d06dda1aee68b4dc227dfcc899f335f8b621e9d1920ee3d4e5dab8ccd71db7
+F ext/rtree/rtreeC.test 128928549d22b65c381ab1366760d08703cd75e34f6a7a506ece38f9330b7282
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
F ext/rtree/rtreeE.test e65d3fc625da1800b412fc8785817327d43ccfec5f5973912d8c9e471928caa9
F ext/rtree/rtreeF.test 81ffa7ef51c4e4618d497a57328c265bf576990c7070633b623b23cd450ed331
F ext/rtree/rtreeG.test 1b9ca6e3effb48f4161edaa463ddeaa8fca4b2526d084f9cbf5dbe4e0184939c
+F ext/rtree/rtreeH.test aa08cc4fa8005b4c67446c7110205055b4d6da90e760e6f44b82dfa4cdf8d87a
F ext/rtree/rtree_perf.tcl 6c18c1f23cd48e0f948930c98dfdd37dfccb5195
F ext/rtree/rtree_util.tcl db734b4c5e75fed6acc56d9701f2235345acfdec750b5fc7b587936f5f6bceed
F ext/rtree/rtreecheck.test 4d29103d1e16fcbf90135d1c637b833688492b063b2971dfb5dc6ba76555cfee
@@ -413,7 +417,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 63668484c95454af7fc04a384da27ac556f27368d6d0c345e405e1677c66768f
+F main.mk 3f50dfe5cb4257c1aca96e417636ed51bc2561e71d31a21e9ccdf66feb912f43
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -427,35 +431,35 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
F src/analyze.c 71fbbeb7b25417592f54d869fe90c28b48e4cecb9926ef9b06d90fb0aec48941
-F src/attach.c f6f212c43dddba79dfcb723fb9470785f3ff55bde8953cd9d2546f3022070a41
-F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
+F src/attach.c 4a3138bd771d5426ae4344d8d5e900440af29fabc5ec2f39f69a45010dfbccd7
+F src/auth.c a38f3c63c974787ecf75e3213f8cac6568b9a7af7591fb0372ec0517dd16dca8
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
-F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
-F src/btree.c 9eb9531c65346bbfccf5325384b7db1849daf4db6601dcfe21ba5c5b20623b64
-F src/btree.h 0866c0a08255142ea0e754aabd211c843cab32045c978a592a43152405ed0c84
+F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
+F src/btree.c 44405d03a05496e13b40b3a208bcf941c42c815da90d6ff4b04267a6985e5eb7
+F src/btree.h 448f15b98ea85dcf7e4eb76f731cadb89636c676ad25dfaac6de77cd66556598
F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
-F src/build.c 61320fb84034c24313de699f3385c6bfe093c925b4df2931c6eb63d7c94ec62a
-F src/callback.c fe677cb5f5abb02f7a772a62a98c2f516426081df68856e8f2d5f950929b966a
+F src/build.c 5fc41458505331bfb0c175f40b9a13cb335f826bed3ae311aaae000c132d7b16
+F src/callback.c 36caff1e7eb7deb58572d59c41cee8f064a11d00297616995c5050ea0cfc1288
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
-F src/ctime.c bd9da3f1ff21b432564a16ef0b154cff03585dc43742842e99c58907c6cb4bef
+F src/ctime.c 849d4cebe008cfc6e4799b034a172b4eaf8856b100739632a852732ba66eee48
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
-F src/delete.c 20c8788451dc737a967c87ea53ad43544d617f5b57d32ccce8bd52a0daf9e89b
-F src/expr.c 6a41ceb27924dcfb6dc910a283ce74e136c9c305aba87a5acbfca32f5c49caa7
+F src/delete.c 4c8c7604277a2041647f96b78f4b9a47858e9217e4fb333d35e7b5ab32c5b57f
+F src/expr.c 16f90ae2af2a100bc430a89184afde54878d82f18267e8d00bc4f33e695a7c57
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
-F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
-F src/global.c 01506976bd75e5e7b977207a6a05062e2dd0050012f8071be06bbea22ec6d69a
+F src/fkey.c b1da9ef8dc834603bb0d28972378a7ce65897847f9a1e89ab800bbdf24c788ee
+F src/func.c e2e3c02621a528a472933fd4733a5da635676f1461be73293f6e9f62f18d4eaa
+F src/global.c 9bf034fd560bdd514715170ed8460bb7f823cec113f0569ef3f18a20c7ccd128
F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c b9ff71cc2913d1d57698a1e22bf853261a9a642baf62bdf40ddeb3809adb85b5
+F src/insert.c 76b1dc902e4c3930d9a17a40cd8ee2e94b1fd8cce766672caef164a6d5d4df1d
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
-F src/loadext.c f6e4e416a736369f9e80eba609f0acda97148a8b0453784d670c78d3eed2f302
-F src/main.c 1648fc7a9bcfdbfd9a9a04af96ff2796c3164b3f3c7e56ed63a3c51cd11d198d
+F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b
+F src/main.c 0402e234155e0aad75fe6cd204864f492495be8605a50b4b3d4d72895cced3ce
F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
@@ -465,7 +469,7 @@ F src/mem5.c 9bf955937b07f8c32541c8a9991f33ce3173d944
F src/memdb.c e94c478a757c4307fd170fe0a7650ef4cf722c59e5a95a8a7896ffedc1679139
F src/memjournal.c 6f3d36a0a8f72f48f6c3c722f04301ac64f2515435fa42924293e46fc7994661
F src/msvc.h 4942752b6a253116baaa8de75256c51a459a5e81
-F src/mutex.c b021263554c8a3995e9d53193b8194b96d1ed28e06c3b532dd7f7d29cf0c7d53
+F src/mutex.c bae36f8af32c22ad80bbf0ccebec63c252b6a2b86e4d3e42672ff287ebf4a604
F src/mutex.h 779d588e3b7756ec3ecf7d78cde1d84aba414f85
F src/mutex_noop.c 9d4309c075ba9cc7249e19412d3d62f7f94839c4
F src/mutex_unix.c aaf9ebc3f89df28483c52208497a99a02cc3650011422fc9d4c57e4392f7fe58
@@ -475,33 +479,33 @@ F src/os.c 1cb0d1d1b3a4267966dee6e292d2b2cdf88e47c0c59cebff27ecafac052dd165
F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
-F src/os_unix.c 2b53b0b8ddc580db096252c721729e5f5f2f355b4fc056f8f3fb328aeb3c9e8a
-F src/os_win.c eb03c6d52f893bcd7fdd4c6006674c13c1b5e49543fec98d605201af2997171c
+F src/os_unix.c c230a7a24766320d8414afd087edcd43e499fb45e86361f6f4f464f343d965a9
+F src/os_win.c ac29c25cde4cfb4adacc59cdec4aa45698ca0e29164ea127859585ccd9faa354
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 1bb6a57fa0465296a4d6109a1a64610a0e7adde1f3acf3ef539a9d972908ce8f
F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388
-F src/parse.y 22ca6e5bb34bbf94e4f91bb1cae6fefad7c03c2e0f29fe9b14b4192e8421f234
+F src/parse.y 0b81472496809693a139067fea1fe3f14a7be8aa26c8f18bb327034d1252a06a
F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd
F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
-F src/pragma.c bea56df3ae0637768c0da4fbbb8f2492f780980d95000034a105ff291bf7ca69
+F src/pragma.c c0d13c0e82a9197aef5533d63300c5b0c8a216ae1fd14ada64e1f12f398d7e82
F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
-F src/prepare.c b086fea6a1952db88beca31fdd621201ee5e4ce3f02905248cc3035a8174aa89
-F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
+F src/prepare.c 95a9dba7a5d032039a77775188cb3b6fb17f2fa1a0b7cd915b30b4b823383ffa
+F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
-F src/resolve.c 66c73fcb7719b8ff0e841b58338f13604ff3e2b50a723f9b8f383595735262f6
+F src/resolve.c 14602f46800ba182ea6a490e0f304127d29ac1f724bdadcc639e25d3223fcf6e
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c dfcd77a9bec9d2bcb221ed93c153cb38cc609faa6404e2dc0ae9491aac110112
-F src/shell.c.in cc960721e56ebc1a78773bb5d2f5608b54275f945cbe49e4afe919d6888062a7
-F src/sqlite.h.in e0be726ea6e4e6571724d39d242472ecd8bd1ba6f84ade88e1641bde98a6d02b
+F src/select.c 3291892add3a8f01dc3754e40ef9e30ad22c78e3404a388ae58f0390a1fb29eb
+F src/shell.c.in c29cb307d6275131e6f9874e0fa73f87acf40a22c4a82faba2059a93b4d294d1
+F src/sqlite.h.in 63b07f76731f2b1e55c48fdb9f0508dcc6fbe3971010b8612ffd847c3c56d9a1
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
-F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
-F src/sqliteInt.h a4837c57f9a3e2af100bc59f4be60d16b823f18131f8cef6a6685440f775eebd
+F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
+F src/sqliteInt.h d2bd297dba08f2390a91c31ff775e0964e9663df5b2910a569fe6f830b8b2beb
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tclsqlite.c 916a92de77ec5cbe27818ca194d8cf0c58aa7ad5b87527098f6aa5a6068800ce
-F src/test1.c 1ab7cbbb6693e08364c1a9241e2aee17f8c4925e4cc52396be77ae6845a05828
+F src/test1.c 51aa5f3030217ca45eb62e90944838794d4faaae7a8f60e0330ae01f30bc997b
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
@@ -528,7 +532,7 @@ F src/test_intarray.c 988fc61cb0ff539f4172c0d95f15287c92516f64
F src/test_intarray.h f3b7672f5d1056eac563c0d6ea8480a660b1475c
F src/test_journal.c 619f2aa10e0d7a5f87c0f06825bc61dfce1c6b9c7f3ad990fb13de6c3b8874a3
F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd
-F src/test_malloc.c 5201422e2403e66a7a9c2b7d8df806acd8d2a0429822adb7e932f324e7b5b3c6
+F src/test_malloc.c dec0aa821b230773aeb3dd11d652c1193f7cedb18a20b25659bc672288115242
F src/test_md5.c 7268e1e8c399d4a5e181b64ac20e1e6f3bc4dd9fc87abac02db145a3d951fa8c
F src/test_multiplex.c e054459f7633f3ff8ce1245da724f9a8be189e4e
F src/test_multiplex.h 5436d03f2d0501d04f3ed50a75819e190495b635
@@ -553,31 +557,32 @@ F src/test_windirent.c a895e2c068a06644eef91a7f0a32182445a893b9a0f33d0cdb4283dca
F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a90484215
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
-F src/tokenize.c 5b0c661a85f783d35b9883830736eeb63be4aefc4f6b7d9cd081d48782c041e2
-F src/treeview.c 14d5d1254702ec96876aa52642cb31548612384134970409fae333b25b39d6bb
-F src/trigger.c a34539c69433276d37b0da9a89c117726ff2d292c0902895af1f393a983cd3a1
-F src/update.c 97d4c9514229f540f8c441e124d5af7f93c5b030c9574539d01e99462e273998
+F src/tokenize.c bbde32eac9eb1280f5292bcdfef66f5a57e43176cbf9347e0efab9f75e133f97
+F src/treeview.c 2c5c4bc0a443401db5fd621542150452ddf5055d38edd4eef868bc2b6bfb0260
+F src/trigger.c 4ace6d1d5ba9a89822deb287317f33c810440526eafe185c2d8a48c31df1e995
+F src/update.c 46dc24c6158446aaab45caee09b6d99327cb479268b83ffeb5b701823da3b67b
+F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
-F src/vacuum.c 762ee9bbf8733d87d8cd06f58d950e881982e416f8c767334a40ffd341b6bff5
-F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55
-F src/vdbe.h 134beb7a12a6213c00eba58febaede33447cc4441bc568a0d9c144b33fc3720a
-F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110
-F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
-F src/vdbeaux.c 2756ac68ac259c416554100598fc291870063288cd7e1af22847f57b3e130e56
+F src/vacuum.c 37730af7540033135909ecaee3667dddec043293428d8718546d0d64ba4a5025
+F src/vdbe.c 71e5a72d89eabf7f88ef0ad4a429d3c3ee5d10de76af3818c47b275a6b3a9097
+F src/vdbe.h e3f43bcc27ff30b0f25a6104d0cb5657e1c4b5e1b5cd2dd2216d5bcc2156a746
+F src/vdbeInt.h 42d3e65ea0c664f6d9bc9a53de645c0baf8566ff0188409ff3b8d2abc327bc17
+F src/vdbeapi.c 765a0bbe01311626417de6cb743f7f25f9f98435c98a9df4bb0714d11014633d
+F src/vdbeaux.c b00d35805a2b326d1371ab7ce8f3a95c8af35b1431367ffe482fc2c735d69fb1
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
-F src/vdbemem.c 414e28d3a7e2a8bee2bb247de115dcbc68e3cbac284d5862d077002f7a93bce1
+F src/vdbemem.c 803323406d8623a7619ea5d5f74016697eeaed19c02b98ce9c3013e77dbe1c38
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
-F src/vdbetrace.c 48e11ebe040c6b41d146abed2602e3d00d621d7ebe4eb29b0a0f1617fd3c2f6c
-F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a
+F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
+F src/vtab.c 10ea07dec111de6fb0a4fc87a7ffa4c65fdc088a19dbfaf7d6f2b128f2e8eb7b
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c aa9cffc7a2bad6b826a86c8562dd4978398720ed41cb8ee7aa9d054eb8b456a0
F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
-F src/where.c d6e5f2056e9a60251e79780fc598a5943e88a3c0fa0019d54922e59f99019287
-F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff
-F src/wherecode.c 982b7450c53fb272f61a1d20c93e960260ea4dfe8e2e9bacc190e2a041a1f1a4
-F src/whereexpr.c 53532be687e12f3cd314f1e204cd4fbdac7ad250e918a182b048121e16e828ae
+F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1
+F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53
+F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8
+F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -592,19 +597,19 @@ F test/alter4.test b6d7b86860111864f6cddb54af313f5862dda23b
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/amatch1.test b5ae7065f042b7f4c1c922933f4700add50cdb9f
F test/analyze.test b3a9c67d00e1df7588a5b7be9a0292899f94fe8cac1f94a017277474ca2e59df
-F test/analyze3.test 8b3ef8ba6d1096b76c40e0925c0fe51e700d2b779cdda40914580de3f9b9d80f
-F test/analyze4.test eff2df19b8dd84529966420f29ea52edc6b56213
+F test/analyze3.test ff62d9029e6deb2c914490c6b00caf7fae47cc85cdc046e4a0d0a4d4b87c71d8
+F test/analyze4.test cdf88f3f72b0f0643a1ff6c730fc5af1e42464d47478d9fbac84c333f72c014e
F test/analyze5.test 765c4e284aa69ca172772aa940946f55629bc8c4
-F test/analyze6.test f1c552ce39cca4ec922a7e4e0e5d0203d6b3281f
-F test/analyze7.test bb1409afc9e8629e414387ef048b8e0e3e0bdc4f
-F test/analyze8.test c05a461d0a6b05991106467d0c47480f2e709c82
-F test/analyze9.test dac0bdc7eab965b9ad639ca83564d98717aaf13ce5a776f23d9a3680238cecd8
-F test/analyzeA.test 3335697f6700c7052295cfd0067fc5b2aacddf9a
+F test/analyze6.test 7b2667b879976ac4b90d8df80d5456328684f1f6f6fdef9469d6e53401f2f469
+F test/analyze7.test a37f4d9cb699a8af068ae02df1bb08bf844df8e98a92a8126cbff89e226879d8
+F test/analyze8.test e32a970564271114786703750e6939cf81dea4b8580874e38e9213ee092f6936
+F test/analyze9.test 9fbf0e0101eef4f5dc149769aa14e10b76ee06e7c28598264b32173cd1999a54
+F test/analyzeA.test 22a892d67bd2223126335b99774cce56ba91122cfe82446d2927afc43ad667dc
F test/analyzeB.test a4c1c3048f6d9e090eb76e83eecb18bcf6d31a70
F test/analyzeC.test 555a6cc388b9818b6eda6df816f01ce0a75d3a93
-F test/analyzeD.test 42af58de25a6436502e43006e9e59e2d71bcb0cf
+F test/analyzeD.test e50cd0b3e6063216cc0c88a1776e8645dc0bd65a6bb275769cbee33b7fd8d90c
F test/analyzeE.test 8684e8ac5722fb97c251887ad97e5d496a98af1d
-F test/analyzeF.test f423125b557f11ad71bb29765ef9c34b6dcf4ab7
+F test/analyzeF.test 9e1a0537949eb5483642b1140a5c39e5b4025939024b935398471fa552f4dabb
F test/analyzer1.test 459fa02c445ddbf0101a3bad47b34290a35f2e49
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
@@ -622,12 +627,12 @@ F test/auth.test 3d6cd8f3978ba55b1202574e6ecd79c6e00914ca44b9bfd6c1fe6fb873fcac8
F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1
F test/auth3.test db21405b95257c24d29273b6b31d0efc59e1d337e3d5804ba2d1fd4897b1ae49
F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec
-F test/autoinc.test 6ae8fb69c9f656962464ae4e6667045d0dfc3b46
-F test/autoindex1.test 788d0894aa3aee1220036d20696e98733fb7ca02265cb1e801700177120c1aeb
+F test/autoinc.test 9d202b7dce6571e52b744138eff12610214501acd635abdd72d18736cd06fd22
+F test/autoindex1.test a09958fa756129af10b6582bcbf3cbdf11e305e027b393f393caef801159dee0
F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df
-F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972
+F test/autoindex3.test 2dd997d6590438b53e4f715f9278aa91c9299cf3f81246a0915269c35beb790e
F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf
-F test/autoindex5.test 96f084a5e6024ea07cace5888df3223f3ea86990
+F test/autoindex5.test 5f0135dc3b266277b8c1904624439097d8e8020dd7197eda13fda23c35c21a05
F test/autovacuum.test 0831cd34e14695d297187f7f6519265e3121c5b0a1720e548e86829e796129e9
F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4
F test/avtrans.test b7dc25459ecbd86c6fa9c606ee3068f59d81e225118617dcf2bbb6ded2ade89e
@@ -641,15 +646,15 @@ F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
F test/badutf2.test f5bc7f2d280670ecd79b9cf4f0f1760c607fe51f
F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
-F test/bestindex1.test 0cf1bd2d7b97d3a3a8c10736125274f64765c4ee
-F test/bestindex2.test 4a06b8922ab2fd09434870da8d1cdf525aaf7060
-F test/bestindex3.test 578b6a52dab819e63f28e3640e04b32c85aed320
+F test/bestindex1.test 852170bddbb21daa121fabcc274640ff83d7d8705912e8b5fe7ed2c5a9a9224a
+F test/bestindex2.test 9a0ccd320b6525eec3a706aae6cdab7e1b7b5abca75027e39f39f755e76e5928
+F test/bestindex3.test 001788a114ad96d81d5154fe77c7f1e26e84b3a2b5635ca29e4f96f6decc534e
F test/bestindex4.test 4cb5ff7dbaebadb87d366f51969271778423b455
F test/bestindex5.test 412b42f8036b28d8b2f3534d89389ad946a4b1a65a12263f51936f7424296f1b
F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
-F test/bigmmap.test abe819e6e1ac1db0c3bfe364ff58889d96e7896b2bbc8bdf1afc77cdeb7d7a9b
+F test/bigmmap.test 31dad31573638bd32de866cdefd11843f75685be4ba6aec1a47918f098f1899b
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
F test/bigsort.test 8299fa9298f4f1e02fc7d2712e8b77d6cd60e5a2
F test/bind.test 1e136709b306f7ed3192d349c2930d89df6ab621654ad6f1a72381d3fe76f483
@@ -681,7 +686,7 @@ F test/cast.test 5ceb920718d280b61163500a7d29e0e0a86458b1cbd92d96f962c9d970aa385
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
F test/check.test 33a698e8c63613449d85d624a38ef669bf20331daabebe3891c9405dd6df463a
F test/close.test 799ea4599d2f5704b0a30f477d17c2c760d8523fa5d0c8be4a7df2a8cad787d8
-F test/closure01.test b1703ba40639cfc9b295cf478d70739415eec6a4
+F test/closure01.test 9905883f1b171a4638f98fc764879f154e214a306d3d8daf412a15e7f3a9b1e0
F test/coalesce.test cee0dccb9fbd2d494b77234bccf9dc6c6786eb91
F test/collate1.test 08c18e7512a5a32c97938854263fa15362eeb846
F test/collate2.test 9aaa410a00734e48bcb27f3872617d6f69b2a621
@@ -720,9 +725,9 @@ F test/corruptH.test 79801d97ec5c2f9f3c87739aa1ec2eb786f96454
F test/corruptI.test 075fe1d75aa1d84e2949be56b6264376c41502e4
F test/corruptJ.test 4d5ccc4bf959464229a836d60142831ef76a5aa4
F test/corruptK.test 91550557849244a9904f4e090052e3f2c1c3f1106840d58b00ffaa3a8c2d3fc0
-F test/cost.test 1eedbfd868f806f3fa08ff072b04cf270dcf61c8
+F test/cost.test b37db8a10d467a69e71a9f3d40bbb266c2f587742b37c6912f6e3f7185a0e216
F test/count.test cb2e0f934c6eb33670044520748d2ecccd46259c
-F test/coveridxscan.test b629e896b14df2f000a99b8d170d80589c46562c
+F test/coveridxscan.test 5ec98719a2e2914e8908dc75f7247d9b54a26df04625f846ac7900d5483f7296
F test/crash.test fb9dc4a02dcba30d4aa5c2c226f98b220b2b959f
F test/crash2.test 5b14d4eb58b880e231361d3b609b216acda86651
F test/crash3.test 8f5de9d32ab9ab95475a9efe7f47a940aa889418
@@ -735,7 +740,7 @@ F test/crashM.test d95f59046fa749b0d0822edf18a717788c8f318d
F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2
F test/createtab.test b5de160630b209c4b8925bdcbbaf48cc90b67fe8
F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
-F test/csv01.test 526fc6aefd052badd5a0283f86b4b395c3df76bfe98d96c801f494f5e2c7836c
+F test/csv01.test 6e1445b3207d574cff22fc41a8e549dfcf2466ee90546ada97d22a90fa89eb58
F test/ctime.test 78749e6c9a5f0010d67985be80788f841e3cd2da18114e2ed6010399a7d807f3
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
F test/cursorhint2.test 0078ae1ded4afcf5eb80d06e3a72b6e1c3f1a646aab26eeb583b0a9ec6f0d56e
@@ -746,7 +751,7 @@ F test/dbpage.test dbf50a4d361f9e45a979432c727506065113124478a7d2db12074fa655e65
F test/dbstatus.test c15fa97f743dac7ce996814c84b56317e138895ee15ce27f15b608aa6924c90a
F test/dbstatus2.test f5fe0afed3fa45e57cfa70d1147606c20d2ba23feac78e9a172f2fe8ab5b78ef
F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
-F test/delete.test acc38fca8ee4851467705b1c2cfea64cd26667e5
+F test/delete.test 31832b0c45ecb51a54348c68db173be462985901e6ed7f403d6d7a8f70ab4ef0
F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa
F test/delete3.test 555e84a00a99230b7d049d477a324a631126a6ab
F test/delete4.test 21d2113217eeaacac2d99defe14fe6611615ae86
@@ -763,12 +768,12 @@ F test/e_blobclose.test 4b3c8c60c2171164d472059c73e9f3c1844bb66d
F test/e_blobopen.test e95e1d40f995056f6f322cd5e1a1b83a27e1a145
F test/e_blobwrite.test f87ff598b67af5b3ec002a8d83e804dc8d23808e88cf0080c176612fc9ffce14
F test/e_changes.test fd66105385153dbf21fdb35eb8ef6c3e1eade579
-F test/e_createtable.test d4c6059d44dcd4b636de9aae322766062b471844
+F test/e_createtable.test e1c4512b9e15fe7bedbbfc0c6daac8a4af65f32dc3c48310b0135b5735857b26
F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e
F test/e_droptrigger.test 3cd080807622c13e5bbb61fc9a57bd7754da2412
F test/e_dropview.test 21ce09c361227ddbc9819a5608ee2700c276bdd5
F test/e_expr.test ca8896601ade1e27c6559614c7f32c63d44636fdfa720436a160f09b8bf66c89
-F test/e_fkey.test dcdc6ad26b1d4f07636208de4c1c22aae7c0597a685a6c10fe6da91f3191dd96
+F test/e_fkey.test 9778696ef9fceacebed8d482d02b47287981faaedf6f73db563ea8a7afb546da
F test/e_fts3.test 8cf40550bb088a6aa187c818c00fabe26ef82900a4cd5c66b427ccafe28bedaa
F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e
F test/e_reindex.test 2bebf7b393e519198b7c654407221cf171a439b8
@@ -788,7 +793,7 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 83437a79ba1545a55fb549309175c683fb334473
F test/enc3.test 6807f7a7740a00361ca8d0ccd66bc60c8dc5f2b6
F test/enc4.test c8f1ce3618508fd0909945beb8b8831feef2c020
-F test/eqp.test 3fe051af50921284189d1970eb653f9fcf5117d2
+F test/eqp.test 4fd69d25f21d8679f5fce13e639975879d89abf6acce4bd9cede133b7482aba7
F test/errmsg.test eae9f091eb39ce7e20305de45d8e5d115b68fa856fba4ea6757b6ca3705ff7f9
F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c
F test/exclusive.test 1206b87e192497d78c7f35552e86a9d05421498da300fb1cce5ca5351ccde3c3
@@ -866,7 +871,7 @@ F test/fts3an.test a49ccadc07a2f7d646ec1b81bc09da2d85a85b18
F test/fts3ao.test c416d50c4fdb6f32a15205b3d0a49eb74fcea92feb66b531a83c904770de5ff1
F test/fts3atoken.test 4b4c16fdcfc972f2cdbba212375a060a86ccf5f1
F test/fts3auto.test b981fea19b132b4e6878f50d7c1f369b28f68eb9
-F test/fts3aux1.test 24b8a69e342a6684e3929408b245cdf5de0d45f8094108fa0a57cd007228963d
+F test/fts3aux1.test 7a170e172afdbceb67f5baa05941fd4fbf56af42f61daa3d140f4b4bf4cb68f6
F test/fts3aux2.test 7ae2b2c13aefdf4169279a27a5f51780ce57f6ba
F test/fts3b.test c15c4a9d04e210d0be67e54ce6a87b927168fbf9c1e3faec8c1a732c366fd491
F test/fts3c.test fc723a9cf10b397fdfc2b32e73c53c8b1ec02958
@@ -882,15 +887,15 @@ F test/fts3defer2.test c540f5f5c2840f70c68fd9b597df817ec7170468
F test/fts3defer3.test dd53fc13223c6d8264a98244e9b19abd35ed71cd
F test/fts3drop.test 1b906e293d6773812587b3dc458cb9e8f3f0c297
F test/fts3e.test 1f6c6ac9cc8b772ca256e6b22aaeed50c9350851
-F test/fts3expr.test 9466627007804d855bf9df2a0cfb3dac23686fdc
+F test/fts3expr.test ebae205a7a89446c32583bcd492dcb817b9f6b31819bb4dde2583bb99c77e526
F test/fts3expr2.test 18da930352e5693eaa163a3eacf96233b7290d1a
F test/fts3expr3.test c4d4a7d6327418428c96e0a3a1137c251b8dfbf8
-F test/fts3expr4.test c39a15d676b14fc439d9bf845aa7bddcf4a74dc3
+F test/fts3expr4.test cac5dd815fe6b5921741abdccdde3b7f50b86394de91e13308ee7986859c4a9f
F test/fts3expr5.test f9abfffbf5e53d48a33e12a1e8f8ba2c551c9b49
F test/fts3fault.test 9fb7d6266a38806de841f7244bac1b0fe3a1477184bbb10b172d19d2ca6ad692
F test/fts3fault2.test 6a17a11d8034b1c4eca9f3091649273d56c49ff049e2173df8060f94341e9da0
F test/fts3first.test dbdedd20914c8d539aa3206c9b34a23775644641
-F test/fts3join.test 69aeb460d0ea17527a2b1b8d24ce18f29c7f207a7261867a2618f639c8020459
+F test/fts3join.test 949b4f5ae3ae9cc2423cb865d711e32476bdb205ab2be923fdf48246e4a44166
F test/fts3malloc.test b0e4c133b8d61d4f6d112d8110f8320e9e453ef6
F test/fts3matchinfo.test ce864e0bd92429df8008f31cf557269ba172482a
F test/fts3misc.test ba03a83b831555cfd18c6c862b24b70a53ce7497fe55077f7c4b7c9ce83c2eed
@@ -898,7 +903,7 @@ F test/fts3near.test 7e3354d46f155a822b59c0e957fd2a70c1d7e905
F test/fts3offsets.test b85fd382abdc78ebce721d8117bd552dfb75094c
F test/fts3prefix.test fa794eaab0bdae466494947b0b153d7844478ab2
F test/fts3prefix2.test e1f0a822ca661dced7f12ce392e14eaf65609dce
-F test/fts3query.test f33eb71a1fe1084ea585eeb7ee76b390729f5170
+F test/fts3query.test ca5dffabdfe9aef2ebcc89e02ce515898f86f8c30a365283831c53e0e08e5821
F test/fts3rank.test cd99bc83a3c923c8d52afd90d86979cf05fc41849f892faeac3988055ef37b99
F test/fts3rnd.test 1320d8826a845e38a96e769562bf83d7a92a15d0
F test/fts3shared.test 57e26a801f21027b7530da77db54286a6fe4997e
@@ -936,13 +941,14 @@ F test/fuzz.test 96083052bf5765e4518c1ba686ce2bab785670d1
F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1
F test/fuzz3.test 9c813e6613b837cb7a277b0383cd66bfa07042b4cf0317157c35852f30043c31
F test/fuzz_common.tcl a87dfbb88c2a6b08a38e9a070dabd129e617b45b
-F test/fuzz_malloc.test 328f70aaca63adf29b4c6f06505ed0cf57ca7c26
-F test/fuzzcheck.c 5eb86c6ac96833ee622f45bf47e8045999c1b4b10d05e4eb809894a4b39f2f84
+F test/fuzz_malloc.test 5b257a7652d8ee90b22e9cf80d9dbea31a4f3e6fed1d33de57b24b1bdb211d79
+F test/fuzzcheck.c 3885207dc217c4dcdb2de4a3cb169a263afeef51ab9bd0ba8567289f0a19a470
F test/fuzzdata1.db 7ee3227bad0e7ccdeb08a9e6822916777073c664
F test/fuzzdata2.db f03a420d3b822cc82e4f894ca957618fbe9c4973
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
F test/fuzzdata4.db 1882f0055fb63214d8407ddc7aca9b0b1c59af21
-F test/fuzzdata5.db 9f0cdcc5c6e83b90cf9ae343bd07f684d2da2de7
+F test/fuzzdata5.db 117d821cde02e30a687f6361a34b98e6e0b05062df523cfee163c58564403b68
+F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
@@ -977,13 +983,13 @@ F test/index2.test f835d5e13ca163bd78c4459ca15fd2e4ed487407
F test/index3.test 81bc47890b8abfb181bc35f8d10b56c069803386
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test 8621491915800ec274609e42e02a97d67e9b13e7
-F test/index6.test b4fc812290067a578b98bb2667b676db89e202a7
-F test/index7.test 7feababe16f2091b229c22aff2bcc1d4d6b9d2bb
+F test/index6.test d07ea75b8c21f125c6f325522e8df8c05c91e9251ec923a31d0582b2ba4a617d
+F test/index7.test 72b59b8ddc5c13f4962886b4011eb9975014317d17ef36c6297921362fb7dd98
F test/index8.test bc2e3db70e8e62459aaa1bd7e4a9b39664f8f9d7
F test/index9.test 0aa3e509dddf81f93380396e40e9bb386904c1054924ba8fa9bcdfe85a8e7721
-F test/indexedby.test faa585e315e868f09bce0eb39c41d6134649b13d2801638294d3ae616edf1609
+F test/indexedby.test a52c8c6abfae4fbfb51d99440de4ca1840dbacc606b05e29328a2a8ba7cd914e
F test/indexexpr1.test 635261197bcdc19b9b2c59bbfa7227d525c00e9587faddb2d293c44d287ce60e
-F test/indexexpr2.test 13247bac49143196556eb3f65e97ef301bd3e993f4511558b5db322ddc370ea6
+F test/indexexpr2.test 08551aa8956a91582979e17b6d369f321a5cb6ed6d3e952925a3e54e9e7de216
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
F test/insert.test 38742b5e9601c8f8d76e9b7555f7270288c2d371
@@ -1004,12 +1010,12 @@ F test/ioerr3.test d3cec5e1a11ad6d27527d0d38573fbff14c71bdd
F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
-F test/istrue.test d6e659764da5ccc03adcdba18fe77d7917ba5e4abd04ef14bd4e4cf43e024b5b
+F test/istrue.test 62372ad3ddcc5d0eb8ff9097dcb0aad8961bf1b9cb45ba634f6e284695126f9a
F test/join.test 2ad9d7fe10e0cc06bc7803c22e5533be11cdadbc592f5f95d789a873b57a5a66
-F test/join2.test f5ea0fd3b0a441c8e439706339dcd17cec63a896a755c04a30bfd442ecce1190
+F test/join2.test 10f7047e723ebd68b2f47189be8eed20451a6f665d8bf46f1774c640d1062417
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
-F test/join5.test c6bd62effc37a152bea735f9ef241b19bb967bd4593dc99b20e2fc55ae707e38
+F test/join5.test 5a2da0c3ea852a7063d3e72fc7d5a04a6de5ef6e6d85092582f69033f7459adc
F test/join6.test cfe6503791ceb0cbb509966740286ec423cbf10b
F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
@@ -1017,7 +1023,7 @@ F test/journal3.test c9c29883f5bf535ae82ae21c472df6263806a22e467b6db7cd0d6d54530
F test/jrnlmode.test a6693f2bed4541a21e703aaa37bb3e10de154130645952933b82b2dec0a8b539
F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d
F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
-F test/json101.test 24e97954e3bd6404f3715888c7f8f835e36e19c7ae6513b5d9ab2d381498962d
+F test/json101.test b40a9f5395d8e669b0bc3eb550ad2ae9e5ada01fbce23c446c2a30a305a6d575
F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb28487bc1
F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa
@@ -1046,7 +1052,7 @@ F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
F test/malloc.test 18dd1c4188c81ca79cf123527c71b19ee0c31feb9947fdffb0dc6ceb1436816a
F test/malloc3.test 6e88bae6312854a4adb4ecc2a6a5ea8c59b4db778b724ba718e1c43fc8c3c136
F test/malloc4.test 957337613002b7058a85116493a262f679f3a261
-F test/malloc5.test f6eb6eca07a4c75f2897bf43a404689b6295bb95ab2e07d4b52eda743f925a27
+F test/malloc5.test 2e4ad7684a13389a44a840499cd47173a8d05f22f082d7d083eece433a7a64eb
F test/malloc6.test 2f039d9821927eacae43e1831f815e157659a151
F test/malloc7.test 7c68a32942858bc715284856c5507446bba88c3a
F test/malloc8.test 9b7a3f8cb9cf0b12fff566e80a980b1767bd961d
@@ -1062,7 +1068,7 @@ F test/mallocG.test 0ff91b65c50bdaba680fb75d87fe4ad35bb7934f
F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test 6c23a71df077fa5d387be90e7e669c5b368ca38a
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
-F test/mallocK.test 27cb5566a6e5f2d76f9d4aa2eca45524401fd61e
+F test/mallocK.test 1f4b5efbf61715ab79b20b38739ff4b3d110ceb53f54e5db6da1f01c083707ab
F test/mallocL.test fb311ff80afddf3b1a75e52289081f4754d901dc
F test/mallocM.test 78bbe9d3da84a5c679123cdb40d7b2010b18fc46e13897e4f253c6ba6fbff134
F test/malloc_common.tcl aac62499b76be719fac31e7a3e54a7fd53272e7f
@@ -1083,7 +1089,7 @@ F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
F test/misc5.test 60e1fc758a93cacd19eb2fafcd1d40d150a05047546c7a92389c98047d621901
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
-F test/misc7.test 567e223b6497da2226a0340befaf2d663c91ad57a48aede21a35a984a2882d41
+F test/misc7.test 349855706310f0de6b91645d199f6874f518627fd057743fa4e3689b60d06efc
F test/misc8.test 8fb0f31d7a8aed484d759773ab8ad12ec746a477f4a67394a4af0e677494c3ca
F test/misuse.test 9e7f78402005e833af71dcab32d048003869eca5abcaccc985d4f8dc1d86bcc7
F test/mjournal.test 9d86e697dcbc5da2c4e8caba9b176b5765fe65e80c88c278b8c09a917e436795
@@ -1115,7 +1121,7 @@ F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/optfuzz-db01.c a0c256905c8ac79f9a5de2f374a3d9f757bef0dca2a238dc7c10cc8a38031834
F test/optfuzz-db01.txt 21f6bdeadc701cf11528276e2a55c70bfcb846ba42df327f979bd9e7b6ce7041
F test/optfuzz.c 50e330304eb1992e15ddd11f3daaad9bcc0d9aaad09cb2bcc77f9515df2e88b1
-F test/orderby1.test 4d22a7c75f6a83fc1f188cc7bb5192285fdf2552
+F test/orderby1.test e4501f54721f804ca56922e253403ac6775f88e9f07569994ce99212b3ca5b10
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
@@ -1124,11 +1130,11 @@ F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
F test/orderby9.test 87fb9548debcc2cd141c5299002dd94672fa76a3
-F test/oserror.test b32dc34f2363ef18532e3a0a7358e3e7e321974f
+F test/oserror.test e7b3416be4b9d5dd2fe0b42dd394daaddbb6c83eeec1f0e47b120b53e0ad3ace
F test/ossfuzz.c c4c4547e2c92ac52f10038b073a03248251a23c1c559728f63a18aeca0e79f03
F test/ossshell.c f125c5bd16e537a2549aa579b328dd1c59905e7ab1338dfc210e755bb7b69f17
F test/ovfl.test 199c482696defceacee8c8e0e0ef36da62726b2f
-F test/pager1.test f596d3bd53ce96e1d87d44d223d2ae6c8867dd782c425e5eb28b5721fa6aaa97
+F test/pager1.test a32ce299ed01ffb06e84a3af467ae1f3389786b316f40c4359f442c79144736b
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
F test/pager3.test 4e9a83d6ca0838d7c602c9eb93d1357562d9059c1e02ffb138a8271020838370
F test/pager4.test a122e9e6925d5b23b31e3dfef8c6a44bbf19590e
@@ -1141,7 +1147,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 8ada8c1dee071e0fc275bc8bc2db7de537d625cad949d2200664b99a0a89eac5
+F test/permutations.test 10793f1de89a226fa22dde9ba9398de22571fee1bfb53a935a11be4aa014704f
F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d
F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
@@ -1167,17 +1173,18 @@ F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/regexp2.test 40e894223b3d6672655481493f1be12012f2b33c
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl 5f15ab8056799e9a6e26a310d49236d2e774d6a30d0ec74601e18d4ce146b79c x
+F test/resetdb.test 2107b34a8c437183f48eb833905df43818160a55f4ac69bbaf54f356fb2b27bd
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 06680159bc6746d0f26276e339e3ae2f951c64812468308838e0a3362d911eaa
-F test/rollback2.test 8435d6ff0f13f51d2a4181c232e706005fa90fc5
+F test/rollback2.test bc868d57899dc6972e2b4483faae0e03365a0556941474eec487ae21d8d38bb6
F test/rollbackfault.test 0e646aeab8840c399cfbfa43daab46fd609cf04a
F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
F test/rowid.test 5b7509f384f4f6fae1af3c8c104c8ca299fea18d
-F test/rowvalue.test 32861d6a933ded868035f2ec79aeb993a2a46eb7a6d282ae13415a4c2e369463
+F test/rowvalue.test ef851a80f7e6de93b51caca9e4b6b7d2dcd540bbcca7d51860e80435b8b4c0de
F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
-F test/rowvalue4.test 4b556d7de161a0dd8cff095c336e913986398bea
+F test/rowvalue4.test cbd1cead27a797d11ec93301fd801c89e97eb1809b3d984b7f16a3876e362eac
F test/rowvalue5.test c81c7d8cf36711ab37675ad7376084ae2a359cb6
F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
F test/rowvalue7.test 5d06ff19d9e6969e574a2e662a531dd0c67801a8
@@ -1193,7 +1200,7 @@ F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
-F test/scanstatus.test 1ba0e2ee25dcd1d55ec770803b19832cffaecbf0b15d376807759ebeed3669b0
+F test/scanstatus.test d14842d0a2757ee059bcffa365746453d60952ba1077980c9a348a9fefbd232a
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
@@ -1211,10 +1218,10 @@ F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test f659f231489349e8c5734e610803d7654207318f
F test/select8.test 8c8f5ae43894c891efc5755ed905467d1d67ad5d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
-F test/selectA.test 101e722370ac6e84978c2958b8931c78b10a1709
+F test/selectA.test b8a590f6493cad5b0bb4dfe1709bf7dcda0b6c40bb4caf32d1e36a89eebc8fc5
F test/selectB.test 954e4e49cf1f896d61794e440669e03a27ceea25
F test/selectC.test e25243f8ca503e06f252eb0218976d07cfeceac3
-F test/selectD.test b0f02a04ef7737decb24e08be2c39b9664b43394
+F test/selectD.test cfa9f27dbdbeaf2d021d26c79b6c790d8f26e5506d50117c7696d3e136d6d9e7
F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf
F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3
F test/selectG.test 089f7d3d7e6db91566f00b036cb353107a2cca6220eb1cb264085a836dae8840
@@ -1234,7 +1241,7 @@ F test/sharedA.test 49d87ec54ab640fbbc3786ee3c01de94aaa482a3a9f834ad3fe92770eb69
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8926a939
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
-F test/shell1.test e2f7d375ae80eb16590af23d6c40cd0140b6d1f92642c1b71eb94630a144ee08
+F test/shell1.test 50154b0c4779df435b9e60ca60104b05f1cc217eab1aa383131359329e73d939
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test ac8c2b744014c3e9a0e26bfd829ab65f00923dc1a91ffd044863e9423cc91494
F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
@@ -1247,21 +1254,22 @@ F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
F test/sidedelete.test f0ad71abe6233e3b153100f3b8d679b19a488329
F test/skipscan1.test 8ab5d2c7c5cd3fe7f172d366e6e74e887cb33cb4
-F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
+F test/skipscan2.test ef143c6e4a5ba4f19c1d1e3f517811f7942bdf2142736cc568feb34e0b5fb763
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
-F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
+F test/skipscan6.test 0b4cd1b4ac9f84d91454df513c99a4932fa07e8f27b8049bea605068b3e34ac7
F test/snapshot.test 85735bd997a4f6d710140c28fd860519a299649f
F test/snapshot2.test 925e42427e923262db63c9d7155183f889e3e99feaedec4075f659e51608344f
F test/snapshot3.test 9719443594a04778861bd20d12596c5f880af177d6cd62f111da3198cafc6096
F test/snapshot_fault.test 52c5e97ebd218846a8ae2da4d147d3e77d71f963
-F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
+F test/soak.test 18944cf21b94a7fe0df02016a6ee1e9632bc4e8d095a0cb49d95e15d5cca2d5c
F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087
F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b
F test/sort2.test cc23b7c19d684657559e8a55b02f7fcee03851d0
F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5
F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c
F test/sort5.test 6b43ae0e2169b5ceed441844492e55ba7f1ae0790528395ddf7888ab3094525d
+F test/sorterref.test a13ed207a0eea3c7898f308f979bfb518f68c598ec737d2c494dfd3deaa83506
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
@@ -1271,7 +1279,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c
-F test/speedtest1.c 20cc4028b0e88392b5a635c2ea5d5e777d569bf7258aead37f8be7a886c38344
+F test/speedtest1.c cc7e6b4a7c9f3e3d1a497ae3f75236a832a2ce0f6a9b017f95d996c821605bfb
F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
@@ -1311,7 +1319,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
-F test/tester.tcl 94901a4625d9a2229666dd5c44120ddf7f0fb639470710ef74a4cefc7b039e07
+F test/tester.tcl aa7558f20fcf7dd9151325f849d9103bd450235093bc078073bf0f080991e3c4
F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1332,7 +1340,7 @@ F test/tkt-2d1a5c67d.test be1326f3061caec85085f4c9ee4490561ca037c0
F test/tkt-2ea2425d34.test 1cf13e6f75d149b3209a0cb32927a82d3d79fb28
F test/tkt-31338dca7e.test 6fb8807851964da0d24e942f2e19c7c705b9fb58
F test/tkt-313723c356.test 4b306ad45c736cedf2f5221f6155b92143244b6d
-F test/tkt-385a5b56b9.test c0a06ada41d7f06b1686da0e718553f853771d1e
+F test/tkt-385a5b56b9.test 5204a7cba0e28c99df0acbf95af5e1af4d32965a7a14de6eccebf949607618b1
F test/tkt-38cb5df375.test f3cc8671f1eb604d4ae9cf886ed4366bec656678
F test/tkt-3998683a16.test 6d1d04d551ed1704eb3396ca87bb9ccc8c5c1eb7
F test/tkt-3a77c9714e.test b08bca26de1140bdf004a37716582a43d7bd8be8
@@ -1347,7 +1355,7 @@ 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 1b2e6bf4f1d9887b216b6da774e5f25915ec8118
+F test/tkt-78e04e52ea.test 1b5be1bac961833a9fd70fe50738cb4064822c61f82c54f7d488435ec806ea62
F test/tkt-7a31705a7e6.test 9e9c057b6a9497c8f7ba7b16871029414ccf6550e7345d9085d6d71c9a56bb6f
F test/tkt-7bbfb7d442.test 7b2cd79c7a17ae6750e75ec1a7846712a69c9d18
F test/tkt-80ba201079.test 105a721e6aad0ae3c5946d7615d1e4d03f6145b8
@@ -1365,7 +1373,7 @@ F test/tkt-a8a0d2996a.test 76662ff0622c90e7ce7bbcb4d9e1129acddf877d17c3489f2da7f
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-b75a9ca6b0.test 1bc0381538fd21f96a10dbabc10ffc51b5b2e5f412d34bae571273ca784003d7
F test/tkt-ba7cbfaedc.test b4c0deccc12aeb55cfdb57935b16b5d67c5a9877
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
@@ -1430,7 +1438,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 53840ec5325bb94544792aad4c20476f81dc26b1
+F test/tkt3442.test a1fc47c669e651d16494de3ff349bcb53281456f2ca02c8bc14220b6044bbfe8
F test/tkt3457.test 5651e2cbb94645b677ec663160b9e192b87b7d365aecdfb24e19f749575a6fc2
F test/tkt3461.test 228ea328a5a21e8663f80ee3d212a6ad92549a19
F test/tkt3493.test 1686cbde85f8721fc1bdc0ee72f2ef2f63139218
@@ -1466,7 +1474,7 @@ F test/tkt3992.test f3e7d548ac26f763b47bc0f750da3d03c81071da
F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 18dbc6617f7a4b90e938d1bd6d26ad18daafaf08
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
-F test/tpch01.test 04adbf8d8300fa60a222f28d901abd76e7be6dd4
+F test/tpch01.test 7c4eb8cdd79c568f46d344b3e789c9fdb8a766d112871352704861f3fca32a2a
F test/trace.test a659a9862957f4789e37a92b3bf6d2caf5c86b02cdeefc41e850ae53acf6992a
F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
F test/trace3.test 1dff966888773ff1bfea01c080caf15417892b3f998408fe920c4791f7337144
@@ -1474,7 +1482,7 @@ F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
F test/transitive1.test 293300f46916569f08875cdb2fe2134be2c27677
-F test/trigger1.test ea9624cc1dae05645469df6119fa815f9e6f1e8c
+F test/trigger1.test 17e4b43e656c4b354df2357634a6ba887990f510c43629f4feca30e3338d2a61
F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
@@ -1487,7 +1495,7 @@ F test/triggerA.test fe5597f47ee21bacb4936dc827994ed94161e332
F test/triggerB.test 56780c031b454abac2340dbb3b71ac5c56c3d7fe
F test/triggerC.test 302d8995f5ffe63bbc15053abb3ef7a39cf5a092
F test/triggerD.test 8e7f3921a92a5797d472732108109e44575fa650
-F test/triggerE.test 15fa63f1097db1f83dd62d121616006978063d1f
+F test/triggerE.test d9e9b364dfd527c84ac0de53045406325487feecb32888d482eca64421a50d99
F test/triggerF.test 6a8c22bd058cf467f0c7d112afe87f7a8c579c0c4681b914b8f19020f48528a4
F test/triggerG.test d5caeef6144ede2426dd13211fd72248241ff2ebc68e12a4c0bf30f5faa21499
F test/tt3_checkpoint.c 9e75cf7c1c364f52e1c47fd0f14c4340a9db0fe1
@@ -1503,9 +1511,14 @@ F test/unionvtabfault.test 26b6854d5aef9005cd630513025690bff1b7378ae9c97b81e2a3c
F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
-F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
-F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
+F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
+F test/update.test 1148de8d913e9817717990603aadeca07aab9ddbb10a30f167cbfd8d3a3ccb60
F test/update2.test 5e67667e1c54017d964e626db765cf8bedcf87483c184f4c575bdb8c1dd2313e
+F test/upsert1.test d587db593f131e112a98a05685c418e0eacc28df5905403e4ca04cd74c39a3fc
+F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
+F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
+F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
+F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35
F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
@@ -1572,52 +1585,52 @@ F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cf
F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417
F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
F test/walthread.test 14b20fcfa6ae152f5d8e12f5dc8a8a724b7ef189f5d8ef1e2ceab79f2af51747
-F test/where.test f0c325563acde44f2c4ea6ba348e9e29f7121757
+F test/where.test f19ea3fa31c425b04af42c8b192a5b595ee84498df8d27dcd79ec043b25fbbfb
F test/where2.test 478d2170637b9211f593120648858593bf2445a1
-F test/where3.test 54cdeb02157acc979de41530b804ae7b09552bf1
+F test/where3.test 2341a294e17193a6b1699ea7f192124a5286ca6acfcc3f4b06d16c931fbcda2c
F test/where4.test 4a371bfcc607f41d233701bdec33ac2972908ba8
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
-F test/where7.test f520bcec2c3d12dc4615623b06b2aec7c2d67e94
-F test/where8.test 98eedca0d375fb400b8377269c4b4686582dfb45
-F test/where9.test 729c3ba9b47e8f9f1aab96bae7dad2a524f1d1a2
+F test/where7.test e579da972eb3372edc9de850efc221848c763f9e4feafc8426d84a4453b92b23
+F test/where8.test 461ca40265ed996a6305da99bb024b0e41602bb586acf544c08f95922358e49f
+F test/where9.test ad2ddb339d10d324763c3da60502b8631f15a2397b869192fbd4e82f40e167d3
F test/whereA.test 6c6a420ca7d313242f9b1bd471dc80e4d0f8323700ba9c78df0bb843d4daa3b4
F test/whereB.test 0def95db3bdec220a731c7e4bec5930327c1d8c5
F test/whereC.test cae295158703cb3fc23bf1a108a9ab730efff0f6
F test/whereD.test 711d4df58d6d4fb9b3f5ce040b818564198be002
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
F test/whereF.test 3d9412b1199d3e2bed34fcb76b4c48d0bf4df95d27e3f8dd27b6f8b4716d0d89
-F test/whereG.test dde4c52a97385a55be6a7cd46be8373f0cf35501
+F test/whereG.test 0158783235a6dd82fc0e37652b8522b186b9510594ac0a4bff0c4101b4396a52
F test/whereH.test e4b07f7a3c2f5d31195cd33710054c78667573b2
-F test/whereI.test eab5b226bbc344ac70d7dc09b963a064860ae6d7
-F test/whereJ.test 55a3221706a7ab706293f17cc8f96da563bf0767
+F test/whereI.test b7769ee8dbefd987fb266715fee887f05f9ff180016b06fca7fa402df739193b
+F test/whereJ.test 88287550f6ee604422403b053455b1ad894eeaa5c35d348532dfa1439286cb9a
F test/whereK.test f8e3cf26a8513ecc7f514f54df9f0572c046c42b
F test/wherefault.test 1374c3aa198388925246475f84ad4cd5f9528864
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
-F test/wherelimit.test 1dee70c9cc147330156d75e23de88f771e624998b03ae316cb64e1d249f129d8
+F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
F test/wherelimit2.test be78ba3aa1831c6358fd7d5b9809bfd520f0c2a7d63a295e8f182e140ff137c3
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
-F test/with1.test ca08e291249a810a2ec9b72ceef5575e07d5925b360fcf6652ae6fe06ac4dced
+F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
-F test/with3.test e71604a0e53cba82bc04c703987cb1d6751ec0b6
+F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
F test/with4.test 257be66c0c67fee1defbbac0f685c3465e2cad037f21ce65f23f86084f198205
F test/withM.test 693b61765f2b387b5e3e24a4536e2e82de15ff64
-F test/without_rowid1.test 1cb47a1a5ba5b2946f18703fabf9fb2a237b0a8180538793ecbaed834d0df765
+F test/without_rowid1.test 533add9100255e4cc430d371b3ecfb79f11f956b86c3a1b9d34413bf8e482d8f
F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99
F test/without_rowid3.test 2724c787a51a5dce09d078453a758117b4b728f1
F test/without_rowid4.test 4e08bcbaee0399f35d58b5581881e7a6243d458a
F test/without_rowid5.test 89b1c587bd92a0590e440da33e7666bf4891572a
F test/without_rowid6.test 1f99644e6508447fb050f73697350c7ceca3392e
-F test/wordcount.c cb589cec469a1d90add05b1f8cee75c7210338d87a5afd65260ed5c0f4bbf8ac
+F test/wordcount.c d721a4b6fae93e6e33449700bce1686bc23257c27425bc3ef1599dc912adec66
F test/writecrash.test f1da7f7adfe8d7f09ea79b42e5ca6dcc41102f27f8e334ad71539501ddd910cc
F test/zeroblob.test 3857870fe681b8185654414a9bccfde80b62a0fa
F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
F test/zipfile.test a61f6ba6dbaaf4983849df84a31df140c7ddd1362e2fa9ecd3cdf5cd123b7f18
-F test/zipfile2.test fc2f08d5ec19c18c83289fbed32e378dc5116519972166e57a244da7bf2e5805
+F test/zipfile2.test 8a18965530258140ce301b2c2f4245bc505ffead2e17a97fa34c7ace379ea5e0
F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c
F tool/GetFile.cs a15e08acb5dd7539b75ba23501581d7c2b462cb5
F tool/GetTclKit.bat 8995df40c4209808b31f24de0b58f90930239a234f7591e3675d45bfbb990c5d
@@ -1636,8 +1649,8 @@ F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
-F tool/lemon.c c1a87d15983f96851b253707985dba8783fbe41ba21ba1b76720e06c8a262206
-F tool/lempar.c 468a155e8729cfbccfe1d85bf60d064f1dab76167a51149ec5c7928a2de63953
+F tool/lemon.c 33892e2a243865f73e6c6e7cecce3c6eb4bb95db4a3d9d86d146c8064feb92fd
+F tool/lempar.c bf7db78e7213f1d51516710483eab506fd52bf632c7abfb3e2e9b885c90c03e1
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
@@ -1645,17 +1658,17 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439
F tool/mkautoconfamal.sh 422fc365358a2e92876ffc62971a0ff28ed472fc8bcf9de0df921c736fdeca5e
F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
F tool/mkctimec.tcl dd183b73ae1c28249669741c250525f0407e579a70482371668fd5f130d9feb3
-F tool/mkkeywordhash.c 2e852ac0dfdc5af18886dc1ce7e9676d11714ae3df0a282dc7d90b3a0fe2033c
-F tool/mkmsvcmin.tcl 8baf26690b80d861d0ac341b29880eec6ade39e4f11fe690271ded9cb90563a3
+F tool/mkkeywordhash.c 20f366ad3794e1db42e333a6f35fa41a024f2e3528579c9d58eb13eaa3ab4913
+F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc87d59f36d4cc4f070eb2e625f14159dc2f5c4204e6a13ea
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
-F tool/mkopcodeh.tcl 4ee2a30ccbd900dc4d5cdb61bdab87cd2166cd2affcc78c9cc0b8d22a65b2eee
+F tool/mkopcodeh.tcl 17d1ccc05a926e19e3a9679ea3e4d1aaa15ba753e2fa7363e6e81c80e0ef8b86
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 2144bc8550a6471a029db262a132d2df4b9e0db61b90398bf64f5b7b3f8d92cd
F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5
F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl fef88397668ae83166735c41af99d79f56afaabb
-F tool/mksqlite3c.tcl a03cee30de81a2e67b93e5c659f24113a003677c557daeb008205c8e6d4345d6
+F tool/mksqlite3c.tcl d532ccbe81234f766bab6e5c0451c99529debcea138caccc1862a9645b2d54f2
F tool/mksqlite3h.tcl 080873e3856eceb9d289a08a00c4b30f875ea3feadcbece796bd509b1532792c
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5
@@ -1675,14 +1688,14 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/spaceanal.tcl 4bfd19aad7eb3ce0372ef0255f58035e0bba4ff5e9acfd763a10c6fb365c8dec
-F tool/speed-check.sh 4ff9b095cf1a7643f0264e7fb7d23f0b12b7cce587a9de315877c378e90eeaf4
+F tool/speed-check.sh 27c7fe178d5b2f7c90a04a127907acda0bfe637fa85b13c43e03e5ed39b008b6
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
F tool/split-sqlite3c.tcl 3efcd4240b738f6bb2b5af0aea7e1e0ef9bc1c61654f645076cec883030b710c
-F tool/sqldiff.c 30879bbc8de686df4624e86adce2d8981f500904c1cfb55b5d1eea2ffd9341eb
+F tool/sqldiff.c 579d7e4e42c30a963781654c87d2868823120b55d59e16ca77b0edbab0218713
F tool/sqlite3_analyzer.c.in 7eeaae8b0d7577662acaabbb11107af0659d1b41bc1dfdd4d91422de27127968
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
@@ -1717,11 +1730,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 1fdaf2c34431adcac1c7ff29aae0623c4cbaa6a7f38e843c786bd407d8b3e730
-R 4cac5ebce3c2344d130b0b75df1b28c6
+P 1ecb3aa13de5c8dc611b814ff34010de0bd90aae73d88aa37a59c4627be4cc2d
+R 2db4b8b40a57a71d528932145db72c5a
T +bgcolor * #d0c0ff
T +sym-release *
-T +sym-version-3.23.1 *
+T +sym-version-3.24.0 *
U drh
-Z 96974a1a3d914d4bcb8af6c333b7c418
+Z 1c60c6ad1e28023fcabbb88fe23d7708
# Remove this line to create a well-formed manifest.
diff --git a/manifest.uuid b/manifest.uuid
index 6b1d373..25ed459 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd148b3b
+c7ee0833225bfd8c5ec2f9bf62b97c4e04d03bd9566366d5221ac8fb199a87ca
diff --git a/src/attach.c b/src/attach.c
index f85952f..ca0fd9f 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -158,7 +158,7 @@ static void attachFunc(
sqlite3_free( zPath );
db->nDb++;
}
- db->skipBtreeMutex = 0;
+ db->noSharedCache = 0;
if( rc==SQLITE_CONSTRAINT ){
rc = SQLITE_ERROR;
zErrDyn = sqlite3MPrintf(db, "database is already attached");
@@ -230,6 +230,7 @@ static void attachFunc(
if( rc==SQLITE_OK ){
sqlite3BtreeEnterAll(db);
db->init.iDb = 0;
+ db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
rc = sqlite3Init(db, &zErrDyn);
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
@@ -502,6 +503,9 @@ int sqlite3FixSrcList(
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
#endif
+ if( pItem->fg.isTabFunc && sqlite3FixExprList(pFix, pItem->u1.pFuncArg) ){
+ return 1;
+ }
}
return 0;
}
@@ -601,6 +605,18 @@ int sqlite3FixTriggerStep(
if( sqlite3FixExprList(pFix, pStep->pExprList) ){
return 1;
}
+#ifndef SQLITE_OMIT_UPSERT
+ if( pStep->pUpsert ){
+ Upsert *pUp = pStep->pUpsert;
+ if( sqlite3FixExprList(pFix, pUp->pUpsertTarget)
+ || sqlite3FixExpr(pFix, pUp->pUpsertTargetWhere)
+ || sqlite3FixExprList(pFix, pUp->pUpsertSet)
+ || sqlite3FixExpr(pFix, pUp->pUpsertWhere)
+ ){
+ return 1;
+ }
+ }
+#endif
pStep = pStep->pNext;
}
return 0;
diff --git a/src/auth.c b/src/auth.c
index 7d6f851..03e4cf9 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -150,6 +150,7 @@ void sqlite3AuthRead(
int iDb; /* The index of the database the expression refers to */
int iCol; /* Index of column in table */
+ assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
if( db->xAuth==0 ) return;
iDb = sqlite3SchemaToIndex(pParse->db, pSchema);
if( iDb<0 ){
@@ -158,7 +159,6 @@ void sqlite3AuthRead(
return;
}
- assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER );
if( pExpr->op==TK_TRIGGER ){
pTab = pParse->pTriggerTab;
}else{
diff --git a/src/btmutex.c b/src/btmutex.c
index ddffb67..275a93f 100644
--- a/src/btmutex.c
+++ b/src/btmutex.c
@@ -195,10 +195,10 @@ static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
skipOk = 0;
}
}
- db->skipBtreeMutex = skipOk;
+ db->noSharedCache = skipOk;
}
void sqlite3BtreeEnterAll(sqlite3 *db){
- if( db->skipBtreeMutex==0 ) btreeEnterAll(db);
+ if( db->noSharedCache==0 ) btreeEnterAll(db);
}
static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){
int i;
@@ -210,7 +210,7 @@ static void SQLITE_NOINLINE btreeLeaveAll(sqlite3 *db){
}
}
void sqlite3BtreeLeaveAll(sqlite3 *db){
- if( db->skipBtreeMutex==0 ) btreeLeaveAll(db);
+ if( db->noSharedCache==0 ) btreeLeaveAll(db);
}
#ifndef NDEBUG
diff --git a/src/btree.c b/src/btree.c
index 4025039..fb3b89f 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -864,7 +864,11 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
** back to where it ought to be if this routine returns true.
*/
int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
- return pCur->eState!=CURSOR_VALID;
+ assert( EIGHT_BYTE_ALIGNMENT(pCur)
+ || pCur==sqlite3BtreeFakeValidCursor() );
+ assert( offsetof(BtCursor, eState)==0 );
+ assert( sizeof(pCur->eState)==1 );
+ return CURSOR_VALID != *(u8*)pCur;
}
/*
@@ -2972,6 +2976,10 @@ static void setDefaultSyncFlag(BtShared *pBt, u8 safety_level){
# define setDefaultSyncFlag(pBt,safety_level)
#endif
+/* Forward declaration */
+static int newDatabase(BtShared*);
+
+
/*
** Get a reference to pPage1 of the database file. This will
** also acquire a readlock on that file.
@@ -3003,6 +3011,9 @@ static int lockBtree(BtShared *pBt){
if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){
nPage = nPageFile;
}
+ if( (pBt->db->flags & SQLITE_ResetDatabase)!=0 ){
+ nPage = 0;
+ }
if( nPage>0 ){
u32 pageSize;
u32 usableSize;
@@ -6219,7 +6230,9 @@ static int clearCell(
if( pInfo->nLocal==pInfo->nPayload ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
- if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){
+ testcase( pCell + pInfo->nSize == pPage->aDataEnd );
+ testcase( pCell + (pInfo->nSize-1) == pPage->aDataEnd );
+ if( pCell + pInfo->nSize > pPage->aDataEnd ){
/* Cell extends past end of page */
return SQLITE_CORRUPT_PAGE(pPage);
}
@@ -8145,6 +8158,94 @@ static int balance(BtCursor *pCur){
return rc;
}
+/* Overwrite content from pX into pDest. Only do the write if the
+** content is different from what is already there.
+*/
+static int btreeOverwriteContent(
+ MemPage *pPage, /* MemPage on which writing will occur */
+ u8 *pDest, /* Pointer to the place to start writing */
+ const BtreePayload *pX, /* Source of data to write */
+ int iOffset, /* Offset of first byte to write */
+ int iAmt /* Number of bytes to be written */
+){
+ int nData = pX->nData - iOffset;
+ if( nData<=0 ){
+ /* Overwritting with zeros */
+ int i;
+ for(i=0; i<iAmt && pDest[i]==0; i++){}
+ if( i<iAmt ){
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( rc ) return rc;
+ memset(pDest + i, 0, iAmt - i);
+ }
+ }else{
+ if( nData<iAmt ){
+ /* Mixed read data and zeros at the end. Make a recursive call
+ ** to write the zeros then fall through to write the real data */
+ int rc = btreeOverwriteContent(pPage, pDest+nData, pX, iOffset+nData,
+ iAmt-nData);
+ if( rc ) return rc;
+ iAmt = nData;
+ }
+ if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){
+ int rc = sqlite3PagerWrite(pPage->pDbPage);
+ if( rc ) return rc;
+ memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Overwrite the cell that cursor pCur is pointing to with fresh content
+** contained in pX.
+*/
+static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
+ int iOffset; /* Next byte of pX->pData to write */
+ int nTotal = pX->nData + pX->nZero; /* Total bytes of to write */
+ int rc; /* Return code */
+ MemPage *pPage = pCur->pPage; /* Page being written */
+ BtShared *pBt; /* Btree */
+ Pgno ovflPgno; /* Next overflow page to write */
+ u32 ovflPageSize; /* Size to write on overflow page */
+
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ /* Overwrite the local portion first */
+ rc = btreeOverwriteContent(pPage, pCur->info.pPayload, pX,
+ 0, pCur->info.nLocal);
+ if( rc ) return rc;
+ if( pCur->info.nLocal==nTotal ) return SQLITE_OK;
+
+ /* Now overwrite the overflow pages */
+ iOffset = pCur->info.nLocal;
+ assert( nTotal>=0 );
+ assert( iOffset>=0 );
+ ovflPgno = get4byte(pCur->info.pPayload + iOffset);
+ pBt = pPage->pBt;
+ ovflPageSize = pBt->usableSize - 4;
+ do{
+ rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
+ if( rc ) return rc;
+ if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ if( iOffset+ovflPageSize<(u32)nTotal ){
+ ovflPgno = get4byte(pPage->aData);
+ }else{
+ ovflPageSize = nTotal - iOffset;
+ }
+ rc = btreeOverwriteContent(pPage, pPage->aData+4, pX,
+ iOffset, ovflPageSize);
+ }
+ sqlite3PagerUnref(pPage->pDbPage);
+ if( rc ) return rc;
+ iOffset += ovflPageSize;
+ }while( iOffset<nTotal );
+ return SQLITE_OK;
+}
+
/*
** Insert a new record into the BTree. The content of the new record
@@ -8235,35 +8336,86 @@ int sqlite3BtreeInsert(
invalidateIncrblobCursors(p, pCur->pgnoRoot, pX->nKey, 0);
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
- ** to a row with the same key as the new entry being inserted. */
- assert( (flags & BTREE_SAVEPOSITION)==0 ||
- ((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) );
+ ** to a row with the same key as the new entry being inserted.
+ */
+#ifdef SQLITE_DEBUG
+ if( flags & BTREE_SAVEPOSITION ){
+ assert( pCur->curFlags & BTCF_ValidNKey );
+ assert( pX->nKey==pCur->info.nKey );
+ assert( pCur->info.nSize!=0 );
+ assert( loc==0 );
+ }
+#endif
- /* 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 */
+ /* On the other hand, BTREE_SAVEPOSITION==0 does not imply
+ ** that the cursor is not pointing to a row to be overwritten.
+ ** So do a complete check.
+ */
if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey ){
- loc = 0;
+ /* The cursor is pointing to the entry that is to be
+ ** overwritten */
+ assert( pX->nData>=0 && pX->nZero>=0 );
+ if( pCur->info.nSize!=0
+ && pCur->info.nPayload==(u32)pX->nData+pX->nZero
+ ){
+ /* New entry is the same size as the old. Do an overwrite */
+ return btreeOverwriteCell(pCur, pX);
+ }
+ assert( loc==0 );
}else if( loc==0 ){
+ /* The cursor is *not* pointing to the cell to be overwritten, nor
+ ** to an adjacent cell. Move the cursor so that it is pointing either
+ ** to the cell to be overwritten or an adjacent cell.
+ */
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
if( rc ) return rc;
}
- }else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
- if( pX->nMem ){
- UnpackedRecord r;
- r.pKeyInfo = pCur->pKeyInfo;
- r.aMem = pX->aMem;
- r.nField = pX->nMem;
- r.default_rc = 0;
- r.errCode = 0;
- r.r1 = 0;
- r.r2 = 0;
- r.eqSeen = 0;
- rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
- }else{
- rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
+ }else{
+ /* This is an index or a WITHOUT ROWID table */
+
+ /* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
+ ** to a row with the same key as the new entry being inserted.
+ */
+ assert( (flags & BTREE_SAVEPOSITION)==0 || loc==0 );
+
+ /* If the cursor is not already pointing either to the cell to be
+ ** overwritten, or if a new cell is being inserted, if the cursor is
+ ** not pointing to an immediately adjacent cell, then move the cursor
+ ** so that it does.
+ */
+ if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
+ if( pX->nMem ){
+ UnpackedRecord r;
+ r.pKeyInfo = pCur->pKeyInfo;
+ r.aMem = pX->aMem;
+ r.nField = pX->nMem;
+ r.default_rc = 0;
+ r.errCode = 0;
+ r.r1 = 0;
+ r.r2 = 0;
+ r.eqSeen = 0;
+ rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
+ }else{
+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
+ }
+ if( rc ) return rc;
}
- if( rc ) return rc;
+
+ /* If the cursor is currently pointing to an entry to be overwritten
+ ** and the new content is the same as as the old, then use the
+ ** overwrite optimization.
+ */
+ if( loc==0 ){
+ getCellInfo(pCur);
+ if( pCur->info.nKey==pX->nKey ){
+ BtreePayload x2;
+ x2.pData = pX->pKey;
+ x2.nData = pX->nKey;
+ x2.nZero = 0;
+ return btreeOverwriteCell(pCur, &x2);
+ }
+ }
+
}
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
@@ -9102,14 +9254,14 @@ static void checkAppendMsg(
pCheck->nErr++;
va_start(ap, zFormat);
if( pCheck->errMsg.nChar ){
- sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
+ sqlite3_str_append(&pCheck->errMsg, "\n", 1);
}
if( pCheck->zPfx ){
- sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
+ sqlite3_str_appendf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
}
- sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
+ sqlite3_str_vappendf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
- if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
+ if( pCheck->errMsg.accError==SQLITE_NOMEM ){
pCheck->mallocFailed = 1;
}
}
@@ -9693,11 +9845,11 @@ integrity_ck_cleanup:
sqlite3PageFree(sCheck.heap);
sqlite3_free(sCheck.aPgRef);
if( sCheck.mallocFailed ){
- sqlite3StrAccumReset(&sCheck.errMsg);
+ sqlite3_str_reset(&sCheck.errMsg);
sCheck.nErr++;
}
*pnErr = sCheck.nErr;
- if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
+ if( sCheck.nErr==0 ) sqlite3_str_reset(&sCheck.errMsg);
/* Make sure this analysis did not leave any unref() pages. */
assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
sqlite3BtreeLeave(p);
diff --git a/src/btree.h b/src/btree.h
index e8e114b..b5bf9f7 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -259,13 +259,28 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
** entry in either an index or table btree.
**
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
-** an arbitrary key and no data. These btrees have pKey,nKey set to their
-** key and pData,nData,nZero set to zero.
+** an arbitrary key and no data. These btrees have pKey,nKey set to the
+** key and the pData,nData,nZero fields are uninitialized. The aMem,nMem
+** fields give an array of Mem objects that are a decomposition of the key.
+** The nMem field might be zero, indicating that no decomposition is available.
**
** Table btrees (used for rowid tables) contain an integer rowid used as
** the key and passed in the nKey field. The pKey field is zero.
** pData,nData hold the content of the new entry. nZero extra zero bytes
** are appended to the end of the content when constructing the entry.
+** The aMem,nMem fields are uninitialized for table btrees.
+**
+** Field usage summary:
+**
+** Table BTrees Index Btrees
+**
+** pKey always NULL encoded key
+** nKey the ROWID length of pKey
+** pData data not used
+** aMem not used decomposed key value
+** nMem not used entries in aMem
+** nData length of pData not used
+** nZero extra zeros after pData not used
**
** This object is used to pass information into sqlite3BtreeInsert(). The
** same information used to be passed as five separate parameters. But placing
@@ -276,7 +291,7 @@ int sqlite3BtreeDelete(BtCursor*, u8 flags);
struct BtreePayload {
const void *pKey; /* Key content for indexes. NULL for tables */
sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
- const void *pData; /* Data for tables. NULL for indexes */
+ const void *pData; /* Data for tables. */
sqlite3_value *aMem; /* First of nMem value in the unpacked pKey */
u16 nMem; /* Number of aMem[] value. Might be zero */
int nData; /* Size of pData. 0 if none. */
diff --git a/src/build.c b/src/build.c
index c286b4b..3d982f7 100644
--- a/src/build.c
+++ b/src/build.c
@@ -343,24 +343,27 @@ Table *sqlite3LocateTable(
const char *zDbase /* Name of the database. Might be NULL */
){
Table *p;
+ sqlite3 *db = pParse->db;
/* Read the database schema. If an error occurs, leave an error message
** and code in pParse and return NULL. */
- if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
+ if( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0
+ && SQLITE_OK!=sqlite3ReadSchema(pParse)
+ ){
return 0;
}
- p = sqlite3FindTable(pParse->db, zName, zDbase);
+ p = sqlite3FindTable(db, zName, zDbase);
if( p==0 ){
const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
+ if( sqlite3FindDbName(db, zDbase)<1 ){
/* If zName is the not the name of a table in the schema created using
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
- Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
+ Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
- pMod = sqlite3PragmaVtabRegister(pParse->db, zName);
+ pMod = sqlite3PragmaVtabRegister(db, zName);
}
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
return pMod->pEpoTab;
@@ -525,6 +528,7 @@ void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
DbSetProperty(db, iDb, DB_ResetWanted);
DbSetProperty(db, 1, DB_ResetWanted);
+ db->mDbFlags &= ~DBFLAG_SchemaKnownOk;
}
if( db->nSchemaLock==0 ){
@@ -550,7 +554,7 @@ void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
sqlite3SchemaClear(pDb->pSchema);
}
}
- db->mDbFlags &= ~DBFLAG_SchemaChange;
+ db->mDbFlags &= ~(DBFLAG_SchemaChange|DBFLAG_SchemaKnownOk);
sqlite3VtabUnlockList(db);
sqlite3BtreeLeaveAll(db);
sqlite3CollapseDatabaseArray(db);
@@ -1095,15 +1099,20 @@ void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
if( pType->n==0 ){
/* If there is no type specified, columns have the default affinity
- ** 'BLOB'. */
+ ** 'BLOB' with a default size of 4 bytes. */
pCol->affinity = SQLITE_AFF_BLOB;
pCol->szEst = 1;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( 4>=sqlite3GlobalConfig.szSorterRef ){
+ pCol->colFlags |= COLFLAG_SORTERREF;
+ }
+#endif
}else{
zType = z + sqlite3Strlen30(z) + 1;
memcpy(zType, pType->z, pType->n);
zType[pType->n] = 0;
sqlite3Dequote(zType);
- pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
+ pCol->affinity = sqlite3AffinityType(zType, pCol);
pCol->colFlags |= COLFLAG_HASTYPE;
}
p->nCol++;
@@ -1163,7 +1172,7 @@ 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, u8 *pszEst){
+char sqlite3AffinityType(const char *zIn, Column *pCol){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
const char *zChar = 0;
@@ -1200,27 +1209,32 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){
}
}
- /* If pszEst is not NULL, store an estimate of the field size. The
+ /* If pCol 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( pCol ){
+ int v = 0; /* default size is approx 4 bytes */
if( aff<SQLITE_AFF_NUMERIC ){
if( zChar ){
while( zChar[0] ){
if( sqlite3Isdigit(zChar[0]) ){
- int v = 0;
+ /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
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)*/
+ v = 16; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
}
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( v>=sqlite3GlobalConfig.szSorterRef ){
+ pCol->colFlags |= COLFLAG_SORTERREF;
+ }
+#endif
+ v = v/4 + 1;
+ if( v>255 ) v = 255;
+ pCol->szEst = v;
}
return aff;
}
@@ -2829,6 +2843,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); VdbeCoverage(v);
regRecord = sqlite3GetTempReg(pParse);
+ sqlite3MultiWrite(pParse);
sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0);
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
@@ -2842,12 +2857,13 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
if( IsUniqueIndex(pIndex) ){
- int j2 = sqlite3VdbeCurrentAddr(v) + 3;
- sqlite3VdbeGoto(v, j2);
+ int j2 = sqlite3VdbeGoto(v, 1);
addr2 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
pIndex->nKeyCol); VdbeCoverage(v);
sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
+ sqlite3VdbeJumpHere(v, j2);
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
@@ -3010,7 +3026,11 @@ void sqlite3CreateIndex(
#if SQLITE_USER_AUTHENTICATION
&& sqlite3UserAuthTable(pTab->zName)==0
#endif
- && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){
+#ifdef SQLITE_ALLOW_SQLITE_MASTER_INDEX
+ && sqlite3StrICmp(&pTab->zName[7],"master")!=0
+#endif
+ && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0
+ ){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
@@ -4189,16 +4209,16 @@ void sqlite3UniqueConstraint(
sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
if( pIdx->aColExpr ){
- sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
+ sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName);
}else{
for(j=0; j<pIdx->nKeyCol; j++){
char *zCol;
assert( pIdx->aiColumn[j]>=0 );
zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
- if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
- sqlite3StrAccumAppendAll(&errMsg, pTab->zName);
- sqlite3StrAccumAppend(&errMsg, ".", 1);
- sqlite3StrAccumAppendAll(&errMsg, zCol);
+ if( j ) sqlite3_str_append(&errMsg, ", ", 2);
+ sqlite3_str_appendall(&errMsg, pTab->zName);
+ sqlite3_str_append(&errMsg, ".", 1);
+ sqlite3_str_appendall(&errMsg, zCol);
}
}
zErr = sqlite3StrAccumFinish(&errMsg);
diff --git a/src/callback.c b/src/callback.c
index 0396df7..a629b68 100644
--- a/src/callback.c
+++ b/src/callback.c
@@ -406,10 +406,12 @@ FuncDef *sqlite3FindFunction(
if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
FuncDef *pOther;
+ u8 *z;
pBest->zName = (const char*)&pBest[1];
pBest->nArg = (u16)nArg;
pBest->funcFlags = enc;
memcpy((char*)&pBest[1], zName, nName+1);
+ for(z=(u8*)pBest->zName; *z; z++) *z = sqlite3UpperToLower[*z];
pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
if( pOther==pBest ){
sqlite3DbFree(db, pBest);
diff --git a/src/ctime.c b/src/ctime.c
index 1877aee..da79f3a 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -286,6 +286,9 @@ static const char * const sqlite3azCompileOpt[] = {
#if SQLITE_ENABLE_SNAPSHOT
"ENABLE_SNAPSHOT",
#endif
+#if SQLITE_ENABLE_SORTER_REFERENCES
+ "ENABLE_SORTER_REFERENCES",
+#endif
#if SQLITE_ENABLE_SQLLOG
"ENABLE_SQLLOG",
#endif
diff --git a/src/delete.c b/src/delete.c
index 2b69e47..b682658 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -238,7 +238,7 @@ void sqlite3DeleteFrom(
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 memCnt = 0; /* Memory cell used for change counting */
int rcauth; /* Value returned by authorization callback */
int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
@@ -343,7 +343,7 @@ void sqlite3DeleteFrom(
goto delete_from_cleanup;
}
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, 1, iDb);
+ sqlite3BeginWriteOperation(pParse, bComplex, iDb);
/* If we are trying to delete from a view, realize that view into
** an ephemeral table.
@@ -371,7 +371,10 @@ void sqlite3DeleteFrom(
/* Initialize the counter of the number of rows deleted, if
** we are counting rows.
*/
- if( db->flags & SQLITE_CountRows ){
+ if( (db->flags & SQLITE_CountRows)!=0
+ && !pParse->nested
+ && !pParse->pTriggerTab
+ ){
memCnt = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, memCnt);
}
@@ -399,7 +402,7 @@ void sqlite3DeleteFrom(
assert( !isView );
sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
if( HasRowid(pTab) ){
- sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
+ sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt ? memCnt : -1,
pTab->zName, P4_STATIC);
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
@@ -444,9 +447,10 @@ void sqlite3DeleteFrom(
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
+ if( eOnePass!=ONEPASS_SINGLE ) sqlite3MultiWrite(pParse);
/* Keep track of the number of rows to be deleted */
- if( db->flags & SQLITE_CountRows ){
+ if( memCnt ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
@@ -549,13 +553,16 @@ void sqlite3DeleteFrom(
if( IsVirtual(pTab) ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
- sqlite3VdbeChangeP5(v, OE_Abort);
assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
sqlite3MayAbort(pParse);
- if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
- pParse->isMultiWrite = 0;
+ if( eOnePass==ONEPASS_SINGLE ){
+ sqlite3VdbeAddOp1(v, OP_Close, iTabCur);
+ if( sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
}
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
+ sqlite3VdbeChangeP5(v, OE_Abort);
}else
#endif
{
@@ -589,7 +596,7 @@ void sqlite3DeleteFrom(
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
+ if( memCnt ){
sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC);
diff --git a/src/expr.c b/src/expr.c
index eac5476..91eb44b 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1363,6 +1363,7 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
+ pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
}
return pNew;
@@ -1825,6 +1826,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
}
/* Fall through */
case TK_IF_NULL_ROW:
+ case TK_REGISTER:
+ testcase( pExpr->op==TK_REGISTER );
testcase( pExpr->op==TK_IF_NULL_ROW );
pWalker->eCode = 0;
return WRC_Abort;
@@ -1842,8 +1845,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
}
/* Fall through */
default:
- testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail will disallow */
- testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail will disallow */
+ testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
+ testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
return WRC_Continue;
}
}
@@ -2407,11 +2410,8 @@ int sqlite3FindInIndex(
if( colUsed==(MASKBIT(nExpr)-1) ){
/* If we reach this point, that means the index pIdx is usable */
int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
-#ifndef SQLITE_OMIT_EXPLAIN
- sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
- sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
- P4_DYNAMIC);
-#endif
+ ExplainQueryPlan((pParse, 0,
+ "USING INDEX %s FOR IN-OPERATOR",pIdx->zName));
sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
@@ -2606,17 +2606,6 @@ int sqlite3CodeSubselect(
jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
-#ifndef SQLITE_OMIT_EXPLAIN
- if( pParse->explain==2 ){
- char *zMsg = sqlite3MPrintf(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);
- }
-#endif
-
switch( pExpr->op ){
case TK_IN: {
int addr; /* Address of OP_OpenEphemeral instruction */
@@ -2654,6 +2643,9 @@ int sqlite3CodeSubselect(
Select *pSelect = pExpr->x.pSelect;
ExprList *pEList = pSelect->pEList;
+ ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
+ jmpIfDynamic>=0?"":"CORRELATED "
+ ));
assert( !isRowid );
/* If the LHS and RHS of the IN operator do not match, that
** error will have been caught long before we reach this point. */
@@ -2695,7 +2687,6 @@ int sqlite3CodeSubselect(
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
int r1, r2, r3;
-
affinity = sqlite3ExprAffinity(pLeft);
if( !affinity ){
affinity = SQLITE_AFF_BLOB;
@@ -2776,6 +2767,8 @@ int sqlite3CodeSubselect(
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
pSel = pExpr->x.pSelect;
+ ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
+ jmpIfDynamic>=0?"":"CORRELATED "));
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
pParse->nMem += nReg;
@@ -3538,6 +3531,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
return 0;
}
+expr_code_doover:
if( pExpr==0 ){
op = TK_NULL;
}else{
@@ -3998,7 +3992,8 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
case TK_SPAN:
case TK_COLLATE:
case TK_UPLUS: {
- return sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ pExpr = pExpr->pLeft;
+ goto expr_code_doover; /* 2018-04-28: Prevent deep recursion. OSSFuzz. */
}
case TK_TRIGGER: {
@@ -4036,10 +4031,9 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
assert( p1>=0 && p1<(pTab->nCol*2+2) );
sqlite3VdbeAddOp2(v, OP_Param, p1, target);
- VdbeComment((v, "%s.%s -> $%d",
+ VdbeComment((v, "r[%d]=%s.%s", target,
(pExpr->iTable ? "new" : "old"),
- (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName),
- target
+ (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName)
));
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -4371,6 +4365,12 @@ int sqlite3ExprCodeExprList(
if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( pItem->bSorterRef ){
+ i--;
+ n--;
+ }else
+#endif
if( (flags & SQLITE_ECEL_REF)!=0 && (j = pItem->u.x.iOrderByCol)>0 ){
if( flags & SQLITE_ECEL_OMITREF ){
i--;
@@ -4899,8 +4899,10 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
if( pA->op==TK_FUNCTION ){
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
+ }else if( pA->op==TK_COLLATE ){
+ if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
}else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
- return pA->op==TK_COLLATE ? 1 : 2;
+ return 2;
}
}
if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
@@ -4909,7 +4911,8 @@ int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTab){
if( sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2;
if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2;
if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
- if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){
+ assert( (combinedFlags & EP_Reduced)==0 );
+ if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE ){
if( pA->iColumn!=pB->iColumn ) return 2;
if( pA->iTable!=pB->iTable
&& (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
@@ -5255,8 +5258,9 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
NameContext *pNC = pWalker->u.pNC;
Parse *pParse = pNC->pParse;
SrcList *pSrcList = pNC->pSrcList;
- AggInfo *pAggInfo = pNC->pAggInfo;
+ AggInfo *pAggInfo = pNC->uNC.pAggInfo;
+ assert( pNC->ncFlags & NC_UAggInfo );
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
diff --git a/src/fkey.c b/src/fkey.c
index c366c1b..256b19d 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -331,6 +331,12 @@ static void fkLookupParent(
int iCur = pParse->nTab - 1; /* Cursor number to use */
int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */
+ sqlite3VdbeVerifyAbortable(v,
+ (!pFKey->isDeferred
+ && !(pParse->db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel
+ && !pParse->isMultiWrite) ? OE_Abort : OE_Ignore);
+
/* If nIncr is less than zero, then check at runtime if there are any
** outstanding constraints to resolve. If there are not, there is no need
** to check if deleting this row resolves any outstanding violations.
@@ -738,6 +744,7 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){
** constraints are violated.
*/
if( (db->flags & SQLITE_DeferFKs)==0 ){
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
VdbeCoverage(v);
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
diff --git a/src/func.c b/src/func.c
index deb7e74..17a267e 100644
--- a/src/func.c
+++ b/src/func.c
@@ -251,7 +251,7 @@ static void printfFunc(
x.apArg = argv+1;
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
str.printfFlags = SQLITE_PRINTF_SQLFUNC;
- sqlite3XPrintf(&str, zFormat, &x);
+ sqlite3_str_appendf(&str, zFormat, &x);
n = str.nChar;
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
SQLITE_DYNAMIC);
@@ -1654,20 +1654,20 @@ static void groupConcatStep(
zSep = ",";
nSep = 1;
}
- if( zSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
+ if( zSep ) sqlite3_str_append(pAccum, zSep, nSep);
}
zVal = (char*)sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
- if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
+ if( zVal ) sqlite3_str_append(pAccum, zVal, nVal);
}
}
static void groupConcatFinalize(sqlite3_context *context){
StrAccum *pAccum;
pAccum = sqlite3_aggregate_context(context, 0);
if( pAccum ){
- if( pAccum->accError==STRACCUM_TOOBIG ){
+ if( pAccum->accError==SQLITE_TOOBIG ){
sqlite3_result_error_toobig(context);
- }else if( pAccum->accError==STRACCUM_NOMEM ){
+ }else if( pAccum->accError==SQLITE_NOMEM ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
diff --git a/src/global.c b/src/global.c
index 04a3d18..f0362d1 100644
--- a/src/global.c
+++ b/src/global.c
@@ -240,7 +240,8 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* xTestCallback */
#endif
0, /* bLocaltimeFault */
- 0x7ffffffe /* iOnceResetThreshold */
+ 0x7ffffffe, /* iOnceResetThreshold */
+ SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */
};
/*
diff --git a/src/insert.c b/src/insert.c
index 9f7032c..3d51642 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -226,11 +226,26 @@ static int autoIncBegin(
Table *pTab /* The table we are writing to */
){
int memId = 0; /* Register holding maximum rowid */
+ assert( pParse->db->aDb[iDb].pSchema!=0 );
if( (pTab->tabFlags & TF_Autoincrement)!=0
&& (pParse->db->mDbFlags & DBFLAG_Vacuum)==0
){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo;
+ Table *pSeqTab = pParse->db->aDb[iDb].pSchema->pSeqTab;
+
+ /* Verify that the sqlite_sequence table exists and is an ordinary
+ ** rowid table with exactly two columns.
+ ** Ticket d8dc2b3a58cd5dc2918a1d4acb 2018-05-23 */
+ if( pSeqTab==0
+ || !HasRowid(pSeqTab)
+ || IsVirtual(pSeqTab)
+ || pSeqTab->nCol!=2
+ ){
+ pParse->nErr++;
+ pParse->rc = SQLITE_CORRUPT_SEQUENCE;
+ return 0;
+ }
pInfo = pToplevel->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
@@ -488,7 +503,8 @@ void sqlite3Insert(
SrcList *pTabList, /* Name of table into which we are inserting */
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 */
+ int onError, /* How to handle constraint errors */
+ Upsert *pUpsert /* ON CONFLICT clauses for upsert, or NULL */
){
sqlite3 *db; /* The main database structure */
Table *pTab; /* The table to insert into. aka TABLE */
@@ -783,7 +799,10 @@ void sqlite3Insert(
/* Initialize the count of rows to be inserted
*/
- if( db->flags & SQLITE_CountRows ){
+ if( (db->flags & SQLITE_CountRows)!=0
+ && !pParse->nested
+ && !pParse->pTriggerTab
+ ){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
@@ -803,6 +822,19 @@ void sqlite3Insert(
pParse->nMem += pIdx->nColumn;
}
}
+#ifndef SQLITE_OMIT_UPSERT
+ if( pUpsert ){
+ pTabList->a[0].iCursor = iDataCur;
+ pUpsert->pUpsertSrc = pTabList;
+ pUpsert->regData = regData;
+ pUpsert->iDataCur = iDataCur;
+ pUpsert->iIdxCur = iIdxCur;
+ if( pUpsert->pUpsertTarget ){
+ sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
+ }
+ }
+#endif
+
/* This is the top of the main insertion loop */
if( useTempTable ){
@@ -1005,7 +1037,7 @@ void sqlite3Insert(
int isReplace; /* Set to true if constraints may cause a replace */
int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
- regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
+ regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0, pUpsert
);
sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
@@ -1028,7 +1060,7 @@ void sqlite3Insert(
/* Update the count of rows that are inserted
*/
- if( (db->flags & SQLITE_CountRows)!=0 ){
+ if( regRowCount ){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
@@ -1065,7 +1097,7 @@ insert_end:
** generating code because of a call to sqlite3NestedParse(), do not
** invoke the callback function.
*/
- if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){
+ if( regRowCount ){
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC);
@@ -1074,6 +1106,7 @@ insert_end:
insert_cleanup:
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pList);
+ sqlite3UpsertDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
sqlite3IdListDelete(db, pColumn);
sqlite3DbFree(db, aRegIdx);
@@ -1146,6 +1179,44 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
}
/*
+** An instance of the ConstraintAddr object remembers the byte-code addresses
+** for sections of the constraint checks that deal with uniqueness constraints
+** on the rowid and on the upsert constraint.
+**
+** This information is passed into checkReorderConstraintChecks() to insert
+** some OP_Goto operations so that the rowid and upsert constraints occur
+** in the correct order relative to other constraints.
+*/
+typedef struct ConstraintAddr ConstraintAddr;
+struct ConstraintAddr {
+ int ipkTop; /* Subroutine for rowid constraint check */
+ int upsertTop; /* Label for upsert constraint check subroutine */
+ int upsertTop2; /* Copy of upsertTop not cleared by the call */
+ int upsertBtm; /* upsert constraint returns to this label */
+ int ipkBtm; /* Return opcode rowid constraint check */
+};
+
+/*
+** Generate any OP_Goto operations needed to cause constraints to be
+** run that haven't already been run.
+*/
+static void reorderConstraintChecks(Vdbe *v, ConstraintAddr *p){
+ if( p->upsertTop ){
+ testcase( sqlite3VdbeLabelHasBeenResolved(v, p->upsertTop) );
+ sqlite3VdbeGoto(v, p->upsertTop);
+ VdbeComment((v, "call upsert subroutine"));
+ sqlite3VdbeResolveLabel(v, p->upsertBtm);
+ p->upsertTop = 0;
+ }
+ if( p->ipkTop ){
+ sqlite3VdbeGoto(v, p->ipkTop);
+ VdbeComment((v, "call rowid unique-check subroutine"));
+ sqlite3VdbeJumpHere(v, p->ipkBtm);
+ p->ipkTop = 0;
+ }
+}
+
+/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
@@ -1240,7 +1311,8 @@ void sqlite3GenerateConstraintChecks(
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 *aiChng /* column i is unchanged if aiChng[i]<0 */
+ int *aiChng, /* column i is unchanged if aiChng[i]<0 */
+ Upsert *pUpsert /* ON CONFLICT clauses, if any. NULL otherwise */
){
Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
@@ -1253,10 +1325,11 @@ void sqlite3GenerateConstraintChecks(
int addr1; /* Address of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
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 */
+ ConstraintAddr sAddr;/* Address information for constraint reordering */
+ Index *pUpIdx = 0; /* Index to which to apply the upsert */
u8 isUpdate; /* True if this is an UPDATE operation */
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
+ int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
isUpdate = regOldData!=0;
db = pParse->db;
@@ -1264,6 +1337,7 @@ void sqlite3GenerateConstraintChecks(
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
+ memset(&sAddr, 0, sizeof(sAddr));
/* 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
@@ -1346,6 +1420,7 @@ void sqlite3GenerateConstraintChecks(
Expr *pExpr = pCheck->a[i].pExpr;
if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
allOk = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeVerifyAbortable(v, onError);
sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
if( onError==OE_Ignore ){
sqlite3VdbeGoto(v, ignoreDest);
@@ -1363,6 +1438,46 @@ void sqlite3GenerateConstraintChecks(
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
+ /* UNIQUE and PRIMARY KEY constraints should be handled in the following
+ ** order:
+ **
+ ** (1) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
+ ** (2) OE_Update
+ ** (3) OE_Replace
+ **
+ ** OE_Fail and OE_Ignore must happen before any changes are made.
+ ** OE_Update guarantees that only a single row will change, so it
+ ** must happen before OE_Replace. Technically, OE_Abort and OE_Rollback
+ ** could happen in any order, but they are grouped up front for
+ ** convenience.
+ **
+ ** Constraint checking code is generated in this order:
+ ** (A) The rowid constraint
+ ** (B) Unique index constraints that do not have OE_Replace as their
+ ** default conflict resolution strategy
+ ** (C) Unique index that do use OE_Replace by default.
+ **
+ ** The ordering of (2) and (3) is accomplished by making sure the linked
+ ** list of indexes attached to a table puts all OE_Replace indexes last
+ ** in the list. See sqlite3CreateIndex() for where that happens.
+ */
+
+ if( pUpsert ){
+ if( pUpsert->pUpsertTarget==0 ){
+ /* An ON CONFLICT DO NOTHING clause, without a constraint-target.
+ ** Make all unique constraint resolution be OE_Ignore */
+ assert( pUpsert->pUpsertSet==0 );
+ overrideError = OE_Ignore;
+ pUpsert = 0;
+ }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
+ /* If the constraint-target is on some column other than
+ ** then ROWID, then we might need to move the UPSERT around
+ ** so that it occurs in the correct order. */
+ sAddr.upsertTop = sAddr.upsertTop2 = sqlite3VdbeMakeLabel(v);
+ sAddr.upsertBtm = sqlite3VdbeMakeLabel(v);
+ }
+ }
+
/* If rowid is changing, make sure the new rowid does not previously
** exist in the table.
*/
@@ -1377,13 +1492,13 @@ void sqlite3GenerateConstraintChecks(
onError = OE_Abort;
}
- if( isUpdate ){
- /* pkChng!=0 does not mean that the rowid has changed, 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);
+ /* figure out whether or not upsert applies in this case */
+ if( pUpsert && pUpsert->pUpsertIdx==0 ){
+ if( pUpsert->pUpsertSet==0 ){
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
+ }else{
+ onError = OE_Update; /* DO UPDATE */
+ }
}
/* If the response to a rowid conflict is REPLACE but the response
@@ -1391,21 +1506,34 @@ void sqlite3GenerateConstraintChecks(
** 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;
- }
- }
+ assert( OE_Update>OE_Replace );
+ assert( OE_Ignore<OE_Replace );
+ assert( OE_Fail<OE_Replace );
+ assert( OE_Abort<OE_Replace );
+ assert( OE_Rollback<OE_Replace );
+ if( onError>=OE_Replace
+ && (pUpsert || onError!=overrideError)
+ && pTab->pIndex
+ ){
+ sAddr.ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
+ }
+
+ if( isUpdate ){
+ /* pkChng!=0 does not mean that the rowid has changed, 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);
}
/* Check to see if the new rowid already exists in the table. Skip
** the following conflict logic if it does not. */
+ VdbeNoopComment((v, "uniqueness check for ROWID"));
+ sqlite3VdbeVerifyAbortable(v, onError);
sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
VdbeCoverage(v);
- /* Generate code that deals with a rowid collision */
switch( onError ){
default: {
onError = OE_Abort;
@@ -1414,6 +1542,9 @@ void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
+ testcase( onError==OE_Rollback );
+ testcase( onError==OE_Abort );
+ testcase( onError==OE_Fail );
sqlite3RowidConstraint(pParse, onError, pTab);
break;
}
@@ -1450,14 +1581,13 @@ void sqlite3GenerateConstraintChecks(
regNewData, 1, 0, OE_Replace, 1, -1);
}else{
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
- if( HasRowid(pTab) ){
- /* This OP_Delete opcode fires the pre-update-hook only. It does
- ** not modify the b-tree. It is more efficient to let the coming
- ** OP_Insert replace the existing entry than it is to delete the
- ** existing entry and then insert a new one. */
- sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
- sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
- }
+ assert( HasRowid(pTab) );
+ /* This OP_Delete opcode fires the pre-update-hook only. It does
+ ** not modify the b-tree. It is more efficient to let the coming
+ ** OP_Insert replace the existing entry than it is to delete the
+ ** existing entry and then insert a new one. */
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
if( pTab->pIndex ){
sqlite3MultiWrite(pParse);
@@ -1467,16 +1597,22 @@ void sqlite3GenerateConstraintChecks(
seenReplace = 1;
break;
}
+#ifndef SQLITE_OMIT_UPSERT
+ case OE_Update: {
+ sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, 0, iDataCur);
+ /* Fall through */
+ }
+#endif
case OE_Ignore: {
- /*assert( seenReplace==0 );*/
+ testcase( onError==OE_Ignore );
sqlite3VdbeGoto(v, ignoreDest);
break;
}
}
sqlite3VdbeResolveLabel(v, addrRowidOk);
- if( ipkTop ){
- ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, ipkTop);
+ if( sAddr.ipkTop ){
+ sAddr.ipkBtm = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, sAddr.ipkTop-1);
}
}
@@ -1494,12 +1630,21 @@ void sqlite3GenerateConstraintChecks(
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
+ if( pUpIdx==pIdx ){
+ addrUniqueOk = sAddr.upsertBtm;
+ upsertBypass = sqlite3VdbeGoto(v, 0);
+ VdbeComment((v, "Skip upsert subroutine"));
+ sqlite3VdbeResolveLabel(v, sAddr.upsertTop2);
+ }else{
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
+ }
+ VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
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 ){
@@ -1559,6 +1704,24 @@ void sqlite3GenerateConstraintChecks(
onError = OE_Abort;
}
+ /* Figure out if the upsert clause applies to this index */
+ if( pUpIdx==pIdx ){
+ if( pUpsert->pUpsertSet==0 ){
+ onError = OE_Ignore; /* DO NOTHING is the same as INSERT OR IGNORE */
+ }else{
+ onError = OE_Update; /* DO UPDATE */
+ }
+ }
+
+ /* Invoke subroutines to handle IPK replace and upsert prior to running
+ ** the first REPLACE constraint check. */
+ if( onError==OE_Replace ){
+ testcase( sAddr.ipkTop );
+ testcase( sAddr.upsertTop
+ && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
+ reorderConstraintChecks(v, &sAddr);
+ }
+
/* Collision detection may be omitted if all of the following are true:
** (1) The conflict resolution algorithm is REPLACE
** (2) The table is a WITHOUT ROWID table
@@ -1580,6 +1743,7 @@ void sqlite3GenerateConstraintChecks(
/* Check to see if the new index entry will be unique */
sqlite3ExprCachePush(pParse);
+ sqlite3VdbeVerifyAbortable(v, onError);
sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
regIdx, pIdx->nKeyCol); VdbeCoverage(v);
@@ -1641,25 +1805,37 @@ void sqlite3GenerateConstraintChecks(
/* Generate code that executes if the new index entry is not unique */
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
- || onError==OE_Ignore || onError==OE_Replace );
+ || onError==OE_Ignore || onError==OE_Replace || onError==OE_Update );
switch( onError ){
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
+ testcase( onError==OE_Rollback );
+ testcase( onError==OE_Abort );
+ testcase( onError==OE_Fail );
sqlite3UniqueConstraint(pParse, onError, pIdx);
break;
}
+#ifndef SQLITE_OMIT_UPSERT
+ case OE_Update: {
+ sqlite3UpsertDoUpdate(pParse, pUpsert, pTab, pIdx, iIdxCur+ix);
+ /* Fall through */
+ }
+#endif
case OE_Ignore: {
+ testcase( onError==OE_Ignore );
sqlite3VdbeGoto(v, ignoreDest);
break;
}
default: {
Trigger *pTrigger = 0;
assert( onError==OE_Replace );
- sqlite3MultiWrite(pParse);
if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
}
+ if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
+ sqlite3MultiWrite(pParse);
+ }
sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
regR, nPkField, 0, OE_Replace,
(pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
@@ -1667,14 +1843,19 @@ void sqlite3GenerateConstraintChecks(
break;
}
}
- sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ if( pUpIdx==pIdx ){
+ sqlite3VdbeJumpHere(v, upsertBypass);
+ }else{
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ }
sqlite3ExprCachePop(pParse);
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
+
}
- if( ipkTop ){
- sqlite3VdbeGoto(v, ipkTop+1);
- sqlite3VdbeJumpHere(v, ipkBottom);
- }
+ testcase( sAddr.ipkTop!=0 );
+ testcase( sAddr.upsertTop
+ && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
+ reorderConstraintChecks(v, &sAddr);
*pbMayReplace = seenReplace;
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
@@ -2174,6 +2355,7 @@ static int xferOptimization(
emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
if( pDest->iPKey>=0 ){
addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ sqlite3VdbeVerifyAbortable(v, onError);
addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
VdbeCoverage(v);
sqlite3RowidConstraint(pParse, onError, pDest);
diff --git a/src/loadext.c b/src/loadext.c
index cf58237..3a48176 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -434,7 +434,22 @@ static const sqlite3_api_routines sqlite3Apis = {
/* Version 3.22.0 and later */
sqlite3_vtab_nochange,
sqlite3_value_nochange,
- sqlite3_vtab_collation
+ sqlite3_vtab_collation,
+ /* Version 3.24.0 and later */
+ sqlite3_keyword_count,
+ sqlite3_keyword_name,
+ sqlite3_keyword_check,
+ sqlite3_str_new,
+ sqlite3_str_finish,
+ sqlite3_str_appendf,
+ sqlite3_str_vappendf,
+ sqlite3_str_append,
+ sqlite3_str_appendall,
+ sqlite3_str_appendchar,
+ sqlite3_str_reset,
+ sqlite3_str_errcode,
+ sqlite3_str_length,
+ sqlite3_str_value
};
/*
@@ -500,10 +515,8 @@ static int sqlite3LoadExtension(
#if SQLITE_OS_UNIX || SQLITE_OS_WIN
for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
- int bExists = 0;
if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
- sqlite3OsAccess(pVfs, zAltFile, SQLITE_ACCESS_EXISTS, &bExists);
- if( bExists ) handle = sqlite3OsDlOpen(pVfs, zAltFile);
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
sqlite3_free(zAltFile);
}
#endif
diff --git a/src/main.c b/src/main.c
index 5b6c867..8e89cc5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -642,6 +642,17 @@ int sqlite3_config(int op, ...){
break;
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ case SQLITE_CONFIG_SORTERREF_SIZE: {
+ int iVal = va_arg(ap, int);
+ if( iVal<0 ){
+ iVal = SQLITE_DEFAULT_SORTERREF_SIZE;
+ }
+ sqlite3GlobalConfig.szSorterRef = (u32)iVal;
+ break;
+ }
+#endif /* SQLITE_ENABLE_SORTER_REFERENCES */
+
default: {
rc = SQLITE_ERROR;
break;
@@ -823,6 +834,7 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
{ SQLITE_DBCONFIG_ENABLE_QPSG, SQLITE_EnableQPSG },
{ SQLITE_DBCONFIG_TRIGGER_EQP, SQLITE_TriggerEQP },
+ { SQLITE_DBCONFIG_RESET_DATABASE, SQLITE_ResetDatabase },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -1796,11 +1808,13 @@ int sqlite3_create_function_v2(
#endif
sqlite3_mutex_enter(db->mutex);
if( xDestroy ){
- pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
+ pArg = (FuncDestructor *)sqlite3Malloc(sizeof(FuncDestructor));
if( !pArg ){
+ sqlite3OomFault(db);
xDestroy(p);
goto out;
}
+ pArg->nRef = 0;
pArg->xDestroy = xDestroy;
pArg->pUserData = p;
}
@@ -1808,7 +1822,7 @@ int sqlite3_create_function_v2(
if( pArg && pArg->nRef==0 ){
assert( rc!=SQLITE_OK );
xDestroy(p);
- sqlite3DbFree(db, pArg);
+ sqlite3_free(pArg);
}
out:
@@ -1847,6 +1861,28 @@ int sqlite3_create_function16(
/*
+** 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
+** SQL function that use this routine so that the functions will exist
+** for name resolution but are actually overloaded by the xFindFunction
+** method of virtual tables.
+*/
+static void sqlite3InvalidFunction(
+ sqlite3_context *context, /* The function calling context */
+ int NotUsed, /* Number of arguments to the function */
+ sqlite3_value **NotUsed2 /* Value of each argument */
+){
+ const char *zName = (const char*)sqlite3_user_data(context);
+ char *zErr;
+ UNUSED_PARAMETER2(NotUsed, NotUsed2);
+ zErr = sqlite3_mprintf(
+ "unable to use function %s in the requested context", zName);
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+}
+
+/*
** Declare that a function has been overloaded by a virtual table.
**
** If the function already exists as a regular global function, then
@@ -1863,7 +1899,8 @@ int sqlite3_overload_function(
const char *zName,
int nArg
){
- int rc = SQLITE_OK;
+ int rc;
+ char *zCopy;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zName==0 || nArg<-2 ){
@@ -1871,13 +1908,13 @@ int sqlite3_overload_function(
}
#endif
sqlite3_mutex_enter(db->mutex);
- if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
- rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
- 0, sqlite3InvalidFunction, 0, 0, 0);
- }
- rc = sqlite3ApiExit(db, rc);
+ rc = sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)!=0;
sqlite3_mutex_leave(db->mutex);
- return rc;
+ if( rc ) return SQLITE_OK;
+ zCopy = sqlite3_mprintf(zName);
+ if( zCopy==0 ) return SQLITE_NOMEM;
+ return sqlite3_create_function_v2(db, zName, nArg, SQLITE_UTF8,
+ zCopy, sqlite3InvalidFunction, 0, 0, sqlite3_free);
}
#ifndef SQLITE_OMIT_TRACE
@@ -3850,24 +3887,6 @@ int sqlite3_test_control(int op, ...){
break;
}
-#ifdef SQLITE_N_KEYWORD
- /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord)
- **
- ** If zWord is a keyword recognized by the parser, then return the
- ** number of keywords. Or if zWord is not a keyword, return 0.
- **
- ** This test feature is only available in the amalgamation since
- ** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite
- ** is built using separate source files.
- */
- case SQLITE_TESTCTRL_ISKEYWORD: {
- const char *zWord = va_arg(ap, const char*);
- int n = sqlite3Strlen30(zWord);
- rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0;
- break;
- }
-#endif
-
/* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
**
** If parameter onoff is non-zero, configure the wrappers so that all
diff --git a/src/mutex.c b/src/mutex.c
index 3699f91..3ad7b89 100644
--- a/src/mutex.c
+++ b/src/mutex.c
@@ -358,4 +358,3 @@ int sqlite3_mutex_notheld(sqlite3_mutex *p){
#endif
#endif /* !defined(SQLITE_MUTEX_OMIT) */
-
diff --git a/src/os_unix.c b/src/os_unix.c
index 2b9c117..0dd461d 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -3779,7 +3779,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
do{
err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
}while( err==EINTR );
- if( err ) return SQLITE_IOERR_WRITE;
+ if( err && err!=EINVAL ) return SQLITE_IOERR_WRITE;
#else
/* If the OS does not have posix_fallocate(), fake it. Write a
** single byte to the last byte in each block that falls entirely
diff --git a/src/os_win.c b/src/os_win.c
index 5344269..3eb2f3c 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -316,22 +316,6 @@ struct winVfsAppData {
#endif
/*
- * The value used with sqlite3_win32_set_directory() to specify that
- * the data directory should be changed.
- */
-#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
-# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
-#endif
-
-/*
- * The value used with sqlite3_win32_set_directory() to specify that
- * the temporary directory should be changed.
- */
-#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
-# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
-#endif
-
-/*
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
* various Win32 API heap functions instead of our own.
*/
@@ -1927,13 +1911,13 @@ char *sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
}
/*
-** This function sets the data directory or the temporary directory based on
-** the provided arguments. The type argument must be 1 in order to set the
-** data directory or 2 in order to set the temporary directory. The zValue
-** argument is the name of the directory to use. The return value will be
-** SQLITE_OK if successful.
+** This function is the same as sqlite3_win32_set_directory (below); however,
+** it accepts a UTF-8 string.
*/
-int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
+int sqlite3_win32_set_directory8(
+ unsigned long type, /* Identifier for directory being set or reset */
+ const char *zValue /* New value for directory being set or reset */
+){
char **ppDirectory = 0;
#ifndef SQLITE_OMIT_AUTOINIT
int rc = sqlite3_initialize();
@@ -1949,21 +1933,54 @@ int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
);
assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
if( ppDirectory ){
- char *zValueUtf8 = 0;
+ char *zCopy = 0;
if( zValue && zValue[0] ){
- zValueUtf8 = winUnicodeToUtf8(zValue);
- if ( zValueUtf8==0 ){
+ zCopy = sqlite3_mprintf("%s", zValue);
+ if ( zCopy==0 ){
return SQLITE_NOMEM_BKPT;
}
}
sqlite3_free(*ppDirectory);
- *ppDirectory = zValueUtf8;
+ *ppDirectory = zCopy;
return SQLITE_OK;
}
return SQLITE_ERROR;
}
/*
+** This function is the same as sqlite3_win32_set_directory (below); however,
+** it accepts a UTF-16 string.
+*/
+int sqlite3_win32_set_directory16(
+ unsigned long type, /* Identifier for directory being set or reset */
+ const void *zValue /* New value for directory being set or reset */
+){
+ int rc;
+ char *zUtf8 = 0;
+ if( zValue ){
+ zUtf8 = sqlite3_win32_unicode_to_utf8(zValue);
+ if( zUtf8==0 ) return SQLITE_NOMEM_BKPT;
+ }
+ rc = sqlite3_win32_set_directory8(type, zUtf8);
+ if( zUtf8 ) sqlite3_free(zUtf8);
+ return rc;
+}
+
+/*
+** This function sets the data directory or the temporary directory based on
+** the provided arguments. The type argument must be 1 in order to set the
+** data directory or 2 in order to set the temporary directory. The zValue
+** argument is the name of the directory to use. The return value will be
+** SQLITE_OK if successful.
+*/
+int sqlite3_win32_set_directory(
+ unsigned long type, /* Identifier for directory being set or reset */
+ void *zValue /* New value for directory being set or reset */
+){
+ return sqlite3_win32_set_directory16(type, zValue);
+}
+
+/*
** The return value of winGetLastErrorMsg
** is zero if the error message fits in the buffer, or non-zero
** otherwise (if the message was truncated).
diff --git a/src/parse.y b/src/parse.y
index c82177a..a29a98d 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -24,8 +24,9 @@
%token_type {Token}
%default_type {Token}
-// The generated parser function takes a 4th argument as follows:
-%extra_argument {Parse *pParse}
+// An extra argument to the constructor for the parser, which is available
+// to all actions.
+%extra_context {Parse *pParse}
// This code runs whenever there is a syntax error
//
@@ -114,9 +115,9 @@ input ::= cmdlist.
cmdlist ::= cmdlist ecmd.
cmdlist ::= ecmd.
ecmd ::= SEMI.
-ecmd ::= explain cmdx SEMI.
-explain ::= .
+ecmd ::= cmdx SEMI.
%ifndef SQLITE_OMIT_EXPLAIN
+ecmd ::= explain cmdx.
explain ::= EXPLAIN. { pParse->explain = 1; }
explain ::= EXPLAIN QUERY PLAN. { pParse->explain = 2; }
%endif SQLITE_OMIT_EXPLAIN
@@ -205,7 +206,8 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
//
%fallback ID
ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST COLUMNKW
- CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL FOR
+ CONFLICT DATABASE DEFERRED DESC DETACH DO
+ EACH END EXCLUSIVE EXPLAIN FAIL FOR
IGNORE IMMEDIATE INITIALLY INSTEAD LIKE_KW MATCH NO PLAN
QUERY KEY OF OFFSET PRAGMA RAISE RECURSIVE RELEASE REPLACE RESTRICT ROW
ROLLBACK SAVEPOINT TEMP TRIGGER VACUUM VIEW VIRTUAL WITH WITHOUT
@@ -239,6 +241,7 @@ columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
%left CONCAT.
%left COLLATE.
%right BITNOT.
+%nonassoc ON.
// An IDENTIFIER can be a generic identifier, or one of several
// keywords. Any non-standard keyword can also be an identifier.
@@ -464,6 +467,7 @@ cmd ::= select(X). {
}
}
+%ifndef SQLITE_OMIT_CTE
select(A) ::= WITH wqlist(W) selectnowith(X). {
Select *p = X;
if( p ){
@@ -484,6 +488,7 @@ select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X). {
}
A = p;
}
+%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(X). {
Select *p = X;
if( p ){
@@ -688,6 +693,21 @@ fullname(A) ::= nm(X).
fullname(A) ::= nm(X) DOT nm(Y).
{A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
+%type xfullname {SrcList*}
+%destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
+xfullname(A) ::= nm(X).
+ {A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
+xfullname(A) ::= nm(X) DOT nm(Y).
+ {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
+xfullname(A) ::= nm(X) DOT nm(Y) AS nm(Z). {
+ A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/
+ if( A ) A->a[0].zAlias = sqlite3NameFromToken(pParse->db, &Z);
+}
+xfullname(A) ::= nm(X) AS nm(Z). {
+ A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/
+ if( A ) A->a[0].zAlias = sqlite3NameFromToken(pParse->db, &Z);
+}
+
%type joinop {int}
joinop(X) ::= COMMA|JOIN. { X = JT_INNER; }
joinop(X) ::= JOIN_KW(A) JOIN.
@@ -697,10 +717,27 @@ joinop(X) ::= JOIN_KW(A) nm(B) JOIN.
joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN.
{X = sqlite3JoinType(pParse,&A,&B,&C);/*X-overwrites-A*/}
+// There is a parsing abiguity in an upsert statement that uses a
+// SELECT on the RHS of a the INSERT:
+//
+// INSERT INTO tab SELECT * FROM aaa JOIN bbb ON CONFLICT ...
+// here ----^^
+//
+// When the ON token is encountered, the parser does not know if it is
+// the beginning of an ON CONFLICT clause, or the beginning of an ON
+// clause associated with the JOIN. The conflict is resolved in favor
+// of the JOIN. If an ON CONFLICT clause is intended, insert a dummy
+// WHERE clause in between, like this:
+//
+// INSERT INTO tab SELECT * FROM aaa JOIN bbb WHERE true ON CONFLICT ...
+//
+// The [AND] and [OR] precedence marks in the rules for on_opt cause the
+// ON in this context to always be interpreted as belonging to the JOIN.
+//
%type on_opt {Expr*}
%destructor on_opt {sqlite3ExprDelete(pParse->db, $$);}
-on_opt(N) ::= ON expr(E). {N = E;}
-on_opt(N) ::= . {N = 0;}
+on_opt(N) ::= ON expr(E). {N = E;}
+on_opt(N) ::= . [OR] {N = 0;}
// Note that this block abuses the Token type just a little. If there is
// no "INDEXED BY" clause, the returned token is empty (z==0 && n==0). If
@@ -781,14 +818,14 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
/////////////////////////// The DELETE statement /////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W)
orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,O,L);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= with DELETE FROM fullname(X) indexed_opt(I) where_opt(W). {
+cmd ::= with DELETE FROM xfullname(X) indexed_opt(I) where_opt(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3DeleteFrom(pParse,X,W,0,0);
}
@@ -803,19 +840,19 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
////////////////////////// The UPDATE command ////////////////////////////////
//
%ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
+cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y)
where_opt(W) orderby_opt(O) limit_opt(L). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
- sqlite3Update(pParse,X,Y,W,R,O,L);
+ sqlite3Update(pParse,X,Y,W,R,O,L,0);
}
%endif
%ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
-cmd ::= with UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
+cmd ::= with UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y)
where_opt(W). {
sqlite3SrcListIndexedBy(pParse, X, &I);
sqlite3ExprListCheckLength(pParse,Y,"set list");
- sqlite3Update(pParse,X,Y,W,R,0,0);
+ sqlite3Update(pParse,X,Y,W,R,0,0,0);
}
%endif
@@ -839,14 +876,31 @@ setlist(A) ::= LP idlist(X) RP EQ expr(Y). {
////////////////////////// The INSERT command /////////////////////////////////
//
-cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) select(S). {
- sqlite3Insert(pParse, X, S, F, R);
+cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
+ upsert(U). {
+ sqlite3Insert(pParse, X, S, F, R, U);
}
-cmd ::= with insert_cmd(R) INTO fullname(X) idlist_opt(F) DEFAULT VALUES.
+cmd ::= with insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES.
{
- sqlite3Insert(pParse, X, 0, F, R);
+ sqlite3Insert(pParse, X, 0, F, R, 0);
}
+%type upsert {Upsert*}
+
+// Because upsert only occurs at the tip end of the INSERT rule for cmd,
+// there is never a case where the value of the upsert pointer will not
+// be destroyed by the cmd action. So comment-out the destructor to
+// avoid unreachable code.
+//%destructor upsert {sqlite3UpsertDelete(pParse->db,$$);}
+upsert(A) ::= . { A = 0; }
+upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW)
+ DO UPDATE SET setlist(Z) where_opt(W).
+ { A = sqlite3UpsertNew(pParse->db,T,TW,Z,W);}
+upsert(A) ::= ON CONFLICT LP sortlist(T) RP where_opt(TW) DO NOTHING.
+ { A = sqlite3UpsertNew(pParse->db,T,TW,0,0); }
+upsert(A) ::= ON CONFLICT DO NOTHING.
+ { A = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+
%type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;}
insert_cmd(A) ::= REPLACE. {A = OE_Replace;}
@@ -1392,9 +1446,9 @@ trigger_cmd(A) ::=
// INSERT
trigger_cmd(A) ::= scanpt(B) insert_cmd(R) INTO
- trnm(X) idlist_opt(F) select(S) scanpt(Z).
- {A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,B,Z);/*A-overwrites-R*/}
-
+ trnm(X) idlist_opt(F) select(S) upsert(U) scanpt(Z). {
+ A = sqlite3TriggerInsertStep(pParse->db,&X,F,S,R,U,B,Z);/*A-overwrites-R*/
+}
// DELETE
trigger_cmd(A) ::= DELETE(B) FROM trnm(X) tridxby where_opt(Y) scanpt(E).
{A = sqlite3TriggerDeleteStep(pParse->db, &X, Y, B.z, E);}
diff --git a/src/pragma.c b/src/pragma.c
index cdebcab..4699c96 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -2216,26 +2216,26 @@ static int pragmaVtabConnect(
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
- sqlite3StrAccumAppendAll(&acc, "CREATE TABLE x");
+ sqlite3_str_appendall(&acc, "CREATE TABLE x");
for(i=0, j=pPragma->iPragCName; i<pPragma->nPragCName; i++, j++){
- sqlite3XPrintf(&acc, "%c\"%s\"", cSep, pragCName[j]);
+ sqlite3_str_appendf(&acc, "%c\"%s\"", cSep, pragCName[j]);
cSep = ',';
}
if( i==0 ){
- sqlite3XPrintf(&acc, "(\"%s\"", pPragma->zName);
+ sqlite3_str_appendf(&acc, "(\"%s\"", pPragma->zName);
cSep = ',';
i++;
}
j = 0;
if( pPragma->mPragFlg & PragFlg_Result1 ){
- sqlite3StrAccumAppendAll(&acc, ",arg HIDDEN");
+ sqlite3_str_appendall(&acc, ",arg HIDDEN");
j++;
}
if( pPragma->mPragFlg & (PragFlg_SchemaOpt|PragFlg_SchemaReq) ){
- sqlite3StrAccumAppendAll(&acc, ",schema HIDDEN");
+ sqlite3_str_appendall(&acc, ",schema HIDDEN");
j++;
}
- sqlite3StrAccumAppend(&acc, ")", 1);
+ sqlite3_str_append(&acc, ")", 1);
sqlite3StrAccumFinish(&acc);
assert( strlen(zBuf) < sizeof(zBuf)-1 );
rc = sqlite3_declare_vtab(db, zBuf);
@@ -2387,13 +2387,13 @@ static int pragmaVtabFilter(
}
}
sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]);
- sqlite3StrAccumAppendAll(&acc, "PRAGMA ");
+ sqlite3_str_appendall(&acc, "PRAGMA ");
if( pCsr->azArg[1] ){
- sqlite3XPrintf(&acc, "%Q.", pCsr->azArg[1]);
+ sqlite3_str_appendf(&acc, "%Q.", pCsr->azArg[1]);
}
- sqlite3StrAccumAppendAll(&acc, pTab->pName->zName);
+ sqlite3_str_appendall(&acc, pTab->pName->zName);
if( pCsr->azArg[0] ){
- sqlite3XPrintf(&acc, "=%Q", pCsr->azArg[0]);
+ sqlite3_str_appendf(&acc, "=%Q", pCsr->azArg[0]);
}
zSql = sqlite3StrAccumFinish(&acc);
if( zSql==0 ) return SQLITE_NOMEM;
diff --git a/src/prepare.c b/src/prepare.c
index c1bd20f..c745f45 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -145,6 +145,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
const char *zMasterName;
int openedTransaction = 0;
+ assert( (db->mDbFlags & DBFLAG_SchemaKnownOk)==0 );
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pSchema );
assert( sqlite3_mutex_held(db->mutex) );
@@ -215,6 +216,9 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
for(i=0; i<ArraySize(meta); i++){
sqlite3BtreeGetMeta(pDb->pBt, i+1, (u32 *)&meta[i]);
}
+ if( (db->flags & SQLITE_ResetDatabase)!=0 ){
+ memset(meta, 0, sizeof(meta));
+ }
pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1];
/* If opening a non-empty database, check the text encoding. For the
@@ -374,6 +378,7 @@ int sqlite3Init(sqlite3 *db, char **pzErrMsg){
}
/* All other schemas after the main schema. The "temp" schema must be last */
for(i=db->nDb-1; i>0; i--){
+ assert( i==1 || sqlite3BtreeHoldsMutex(db->aDb[i].pBt) );
if( !DbHasProperty(db, i, DB_SchemaLoaded) ){
rc = sqlite3InitOne(db, i, pzErrMsg);
if( rc ) return rc;
@@ -395,10 +400,12 @@ int sqlite3ReadSchema(Parse *pParse){
assert( sqlite3_mutex_held(db->mutex) );
if( !db->init.busy ){
rc = sqlite3Init(db, &pParse->zErrMsg);
- }
- if( rc!=SQLITE_OK ){
- pParse->rc = rc;
- pParse->nErr++;
+ if( rc!=SQLITE_OK ){
+ pParse->rc = rc;
+ pParse->nErr++;
+ }else if( db->noSharedCache ){
+ db->mDbFlags |= DBFLAG_SchemaKnownOk;
+ }
}
return rc;
}
@@ -609,7 +616,7 @@ static int sqlite3Prepare(
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
- "selectid", "order", "from", "detail"
+ "id", "parent", "notused", "detail"
};
int iFirst, mx;
if( sParse.explain==2 ){
diff --git a/src/printf.c b/src/printf.c
index fcbd7fd..71296ac 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -134,7 +134,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
** Set the StrAccum object to an error mode.
*/
static void setStrAccumError(StrAccum *p, u8 eError){
- assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
+ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG );
p->accError = eError;
p->nAlloc = 0;
}
@@ -168,8 +168,8 @@ static char *getTextArg(PrintfArguments *p){
/*
** Render a string given by "fmt" into the StrAccum object.
*/
-void sqlite3VXPrintf(
- StrAccum *pAccum, /* Accumulate results here */
+void sqlite3_str_vappendf(
+ sqlite3_str *pAccum, /* Accumulate results here */
const char *fmt, /* Format string */
va_list ap /* arguments */
){
@@ -226,11 +226,11 @@ void sqlite3VXPrintf(
#else
do{ fmt++; }while( *fmt && *fmt != '%' );
#endif
- sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
+ sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
if( *fmt==0 ) break;
}
if( (c=(*++fmt))==0 ){
- sqlite3StrAccumAppend(pAccum, "%", 1);
+ sqlite3_str_append(pAccum, "%", 1);
break;
}
/* Find out what flags are present */
@@ -408,7 +408,7 @@ void sqlite3VXPrintf(
u64 n = (u64)precision + 10 + precision/3;
zOut = zExtra = sqlite3Malloc( n );
if( zOut==0 ){
- setStrAccumError(pAccum, STRACCUM_NOMEM);
+ setStrAccumError(pAccum, SQLITE_NOMEM);
return;
}
nOut = (int)n;
@@ -533,7 +533,7 @@ void sqlite3VXPrintf(
bufpt = zExtra
= sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
if( bufpt==0 ){
- setStrAccumError(pAccum, STRACCUM_NOMEM);
+ setStrAccumError(pAccum, SQLITE_NOMEM);
return;
}
}
@@ -665,11 +665,11 @@ void sqlite3VXPrintf(
if( precision>1 ){
width -= precision-1;
if( width>1 && !flag_leftjustify ){
- sqlite3AppendChar(pAccum, width-1, ' ');
+ sqlite3_str_appendchar(pAccum, width-1, ' ');
width = 0;
}
while( precision-- > 1 ){
- sqlite3StrAccumAppend(pAccum, buf, length);
+ sqlite3_str_append(pAccum, buf, length);
}
}
bufpt = buf;
@@ -755,7 +755,7 @@ void sqlite3VXPrintf(
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
- setStrAccumError(pAccum, STRACCUM_NOMEM);
+ setStrAccumError(pAccum, SQLITE_NOMEM);
return;
}
}else{
@@ -779,7 +779,7 @@ void sqlite3VXPrintf(
pToken = va_arg(ap, Token*);
assert( bArgList==0 );
if( pToken && pToken->n ){
- sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
+ sqlite3_str_append(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
break;
@@ -795,10 +795,10 @@ void sqlite3VXPrintf(
assert( bArgList==0 );
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase ){
- sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
- sqlite3StrAccumAppend(pAccum, ".", 1);
+ sqlite3_str_appendall(pAccum, pItem->zDatabase);
+ sqlite3_str_append(pAccum, ".", 1);
}
- sqlite3StrAccumAppendAll(pAccum, pItem->zName);
+ sqlite3_str_appendall(pAccum, pItem->zName);
length = width = 0;
break;
}
@@ -817,11 +817,11 @@ void sqlite3VXPrintf(
*/
width -= length;
if( width>0 ){
- if( !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
- sqlite3StrAccumAppend(pAccum, bufpt, length);
- if( flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+ if( !flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
+ sqlite3_str_append(pAccum, bufpt, length);
+ if( flag_leftjustify ) sqlite3_str_appendchar(pAccum, width, ' ');
}else{
- sqlite3StrAccumAppend(pAccum, bufpt, length);
+ sqlite3_str_append(pAccum, bufpt, length);
}
if( zExtra ){
@@ -842,13 +842,13 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
char *zNew;
assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
if( p->accError ){
- testcase(p->accError==STRACCUM_TOOBIG);
- testcase(p->accError==STRACCUM_NOMEM);
+ testcase(p->accError==SQLITE_TOOBIG);
+ testcase(p->accError==SQLITE_NOMEM);
return 0;
}
if( p->mxAlloc==0 ){
N = p->nAlloc - p->nChar - 1;
- setStrAccumError(p, STRACCUM_TOOBIG);
+ setStrAccumError(p, SQLITE_TOOBIG);
return N;
}else{
char *zOld = isMalloced(p) ? p->zText : 0;
@@ -860,8 +860,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
szNew += p->nChar;
}
if( szNew > p->mxAlloc ){
- sqlite3StrAccumReset(p);
- setStrAccumError(p, STRACCUM_TOOBIG);
+ sqlite3_str_reset(p);
+ setStrAccumError(p, SQLITE_TOOBIG);
return 0;
}else{
p->nAlloc = (int)szNew;
@@ -878,8 +878,8 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
- sqlite3StrAccumReset(p);
- setStrAccumError(p, STRACCUM_NOMEM);
+ sqlite3_str_reset(p);
+ setStrAccumError(p, SQLITE_NOMEM);
return 0;
}
}
@@ -889,7 +889,7 @@ static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
/*
** Append N copies of character c to the given string buffer.
*/
-void sqlite3AppendChar(StrAccum *p, int N, char c){
+void sqlite3_str_appendchar(sqlite3_str *p, int N, char c){
testcase( p->nChar + (i64)N > 0x7fffffff );
if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
return;
@@ -901,9 +901,9 @@ void sqlite3AppendChar(StrAccum *p, int N, char c){
** 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
+** This is a helper routine to sqlite3_str_append() that does special-case
** work (enlarging the buffer) using tail recursion, so that the
-** sqlite3StrAccumAppend() routine can use fast calling semantics.
+** sqlite3_str_append() routine can use fast calling semantics.
*/
static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
N = sqlite3StrAccumEnlarge(p, N);
@@ -917,7 +917,7 @@ static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int 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){
+void sqlite3_str_append(sqlite3_str *p, const char *z, int N){
assert( z!=0 || N==0 );
assert( p->zText!=0 || p->nChar==0 || p->accError );
assert( N>=0 );
@@ -934,8 +934,8 @@ 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));
+void sqlite3_str_appendall(sqlite3_str *p, const char *z){
+ sqlite3_str_append(p, z, sqlite3Strlen30(z));
}
@@ -952,7 +952,7 @@ static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
memcpy(zText, p->zText, p->nChar+1);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
- setStrAccumError(p, STRACCUM_NOMEM);
+ setStrAccumError(p, SQLITE_NOMEM);
}
p->zText = zText;
return zText;
@@ -968,13 +968,55 @@ char *sqlite3StrAccumFinish(StrAccum *p){
}
/*
+** This singleton is an sqlite3_str object that is returned if
+** sqlite3_malloc() fails to provide space for a real one. This
+** sqlite3_str object accepts no new text and always returns
+** an SQLITE_NOMEM error.
+*/
+static sqlite3_str sqlite3OomStr = {
+ 0, 0, 0, 0, 0, SQLITE_NOMEM, 0
+};
+
+/* Finalize a string created using sqlite3_str_new().
+*/
+char *sqlite3_str_finish(sqlite3_str *p){
+ char *z;
+ if( p!=0 && p!=&sqlite3OomStr ){
+ z = sqlite3StrAccumFinish(p);
+ sqlite3_free(p);
+ }else{
+ z = 0;
+ }
+ return z;
+}
+
+/* Return any error code associated with p */
+int sqlite3_str_errcode(sqlite3_str *p){
+ return p ? p->accError : SQLITE_NOMEM;
+}
+
+/* Return the current length of p in bytes */
+int sqlite3_str_length(sqlite3_str *p){
+ return p ? p->nChar : 0;
+}
+
+/* Return the current value for p */
+char *sqlite3_str_value(sqlite3_str *p){
+ if( p==0 || p->nChar==0 ) return 0;
+ p->zText[p->nChar] = 0;
+ return p->zText;
+}
+
+/*
** Reset an StrAccum string. Reclaim all malloced memory.
*/
-void sqlite3StrAccumReset(StrAccum *p){
+void sqlite3_str_reset(StrAccum *p){
if( isMalloced(p) ){
sqlite3DbFree(p->db, p->zText);
p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
}
+ p->nAlloc = 0;
+ p->nChar = 0;
p->zText = 0;
}
@@ -1002,6 +1044,18 @@ void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
p->printfFlags = 0;
}
+/* Allocate and initialize a new dynamic string object */
+sqlite3_str *sqlite3_str_new(sqlite3 *db){
+ sqlite3_str *p = sqlite3_malloc64(sizeof(*p));
+ if( p ){
+ sqlite3StrAccumInit(p, 0, 0, 0,
+ db ? db->aLimit[SQLITE_LIMIT_LENGTH] : SQLITE_MAX_LENGTH);
+ }else{
+ p = &sqlite3OomStr;
+ }
+ return p;
+}
+
/*
** Print into memory obtained from sqliteMalloc(). Use the internal
** %-conversion extensions.
@@ -1014,9 +1068,9 @@ char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){
sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
acc.printfFlags = SQLITE_PRINTF_INTERNAL;
- sqlite3VXPrintf(&acc, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
- if( acc.accError==STRACCUM_NOMEM ){
+ if( acc.accError==SQLITE_NOMEM ){
sqlite3OomFault(db);
}
return z;
@@ -1054,7 +1108,7 @@ char *sqlite3_vmprintf(const char *zFormat, va_list ap){
if( sqlite3_initialize() ) return 0;
#endif
sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
- sqlite3VXPrintf(&acc, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
return z;
}
@@ -1099,7 +1153,7 @@ char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
}
#endif
sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
- sqlite3VXPrintf(&acc, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
zBuf[acc.nChar] = 0;
return zBuf;
}
@@ -1121,7 +1175,7 @@ char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
** allocate memory because it might be called while the memory allocator
** mutex is held.
**
-** sqlite3VXPrintf() might ask for *temporary* memory allocations for
+** sqlite3_str_vappendf() might ask for *temporary* memory allocations for
** certain format characters (%q) or for very large precisions or widths.
** Care must be taken that any sqlite3_log() calls that occur while the
** memory mutex is held do not use these mechanisms.
@@ -1131,7 +1185,7 @@ static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
- sqlite3VXPrintf(&acc, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
sqlite3StrAccumFinish(&acc));
}
@@ -1160,7 +1214,7 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
char zBuf[500];
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
va_start(ap,zFormat);
- sqlite3VXPrintf(&acc, zFormat, ap);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
va_end(ap);
sqlite3StrAccumFinish(&acc);
#ifdef SQLITE_OS_TRACE_PROC
@@ -1177,12 +1231,12 @@ void sqlite3DebugPrintf(const char *zFormat, ...){
/*
-** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
+** variable-argument wrapper around sqlite3_str_vappendf(). The bFlags argument
** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
*/
-void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
+void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
- sqlite3VXPrintf(p, zFormat, ap);
+ sqlite3_str_vappendf(p, zFormat, ap);
va_end(ap);
}
diff --git a/src/resolve.c b/src/resolve.c
index 7ae49bd..d9ce286 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -75,29 +75,31 @@ static void resolveAlias(
assert( pOrig!=0 );
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
- if( pDup==0 ) return;
- if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
- if( pExpr->op==TK_COLLATE ){
- pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
- }
- ExprSetProperty(pDup, EP_Alias);
-
- /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
- ** prevents ExprDelete() from deleting the Expr structure itself,
- ** allowing it to be repopulated by the memcpy() on the following line.
- ** The pExpr->u.zToken might point into memory that will be freed by the
- ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
- ** make a copy of the token before doing the sqlite3DbFree().
- */
- ExprSetProperty(pExpr, EP_Static);
- sqlite3ExprDelete(db, pExpr);
- memcpy(pExpr, pDup, sizeof(*pExpr));
- 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->flags |= EP_MemToken;
+ if( pDup!=0 ){
+ if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
+ if( pExpr->op==TK_COLLATE ){
+ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
+ }
+ ExprSetProperty(pDup, EP_Alias);
+
+ /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
+ ** prevents ExprDelete() from deleting the Expr structure itself,
+ ** allowing it to be repopulated by the memcpy() on the following line.
+ ** The pExpr->u.zToken might point into memory that will be freed by the
+ ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
+ ** make a copy of the token before doing the sqlite3DbFree().
+ */
+ ExprSetProperty(pExpr, EP_Static);
+ sqlite3ExprDelete(db, pExpr);
+ memcpy(pExpr, pDup, sizeof(*pExpr));
+ 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->flags |= EP_MemToken;
+ }
+ sqlite3DbFree(db, pDup);
}
- sqlite3DbFree(db, pDup);
+ ExprSetProperty(pExpr, EP_Alias);
}
@@ -191,7 +193,7 @@ 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; /* True if resolved to a trigger column */
+ int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
Table *pTab = 0; /* Table hold the row */
Column *pCol; /* A column of pTab */
@@ -296,22 +298,35 @@ static int lookupName(
}
} /* if( pSrcList ) */
-#ifndef SQLITE_OMIT_TRIGGER
+#if !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT)
/* If we have not already resolved the name, then maybe
- ** it is a new.* or old.* trigger argument reference
+ ** it is a new.* or old.* trigger argument reference. Or
+ ** maybe it is an excluded.* from an upsert.
*/
- if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
- int op = pParse->eTriggerOp;
- assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
- if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
- pExpr->iTable = 1;
- pTab = pParse->pTriggerTab;
- }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
- pExpr->iTable = 0;
- pTab = pParse->pTriggerTab;
- }else{
- pTab = 0;
+ if( zDb==0 && zTab!=0 && cntTab==0 ){
+ pTab = 0;
+#ifndef SQLITE_OMIT_TRIGGER
+ if( pParse->pTriggerTab!=0 ){
+ int op = pParse->eTriggerOp;
+ assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
+ if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
+ pExpr->iTable = 1;
+ pTab = pParse->pTriggerTab;
+ }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
+ pExpr->iTable = 0;
+ pTab = pParse->pTriggerTab;
+ }
+ }
+#endif /* SQLITE_OMIT_TRIGGER */
+#ifndef SQLITE_OMIT_UPSERT
+ if( (pNC->ncFlags & NC_UUpsert)!=0 ){
+ Upsert *pUpsert = pNC->uNC.pUpsert;
+ if( pUpsert && sqlite3StrICmp("excluded",zTab)==0 ){
+ pTab = pUpsert->pUpsertSrc->a[0].pTab;
+ pExpr->iTable = 2;
+ }
}
+#endif /* SQLITE_OMIT_UPSERT */
if( pTab ){
int iCol;
@@ -331,24 +346,36 @@ static int lookupName(
}
if( iCol<pTab->nCol ){
cnt++;
- if( iCol<0 ){
- pExpr->affinity = SQLITE_AFF_INTEGER;
- }else if( pExpr->iTable==0 ){
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
- }else{
- testcase( iCol==31 );
- testcase( iCol==32 );
- pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+#ifndef SQLITE_OMIT_UPSERT
+ if( pExpr->iTable==2 ){
+ testcase( iCol==(-1) );
+ pExpr->iTable = pNC->uNC.pUpsert->regData + iCol;
+ eNewExprOp = TK_REGISTER;
+ ExprSetProperty(pExpr, EP_Alias);
+ }else
+#endif /* SQLITE_OMIT_UPSERT */
+ {
+#ifndef SQLITE_OMIT_TRIGGER
+ if( iCol<0 ){
+ pExpr->affinity = SQLITE_AFF_INTEGER;
+ }else if( pExpr->iTable==0 ){
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+ }else{
+ testcase( iCol==31 );
+ testcase( iCol==32 );
+ pParse->newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<<iCol));
+ }
+ pExpr->pTab = pTab;
+ pExpr->iColumn = (i16)iCol;
+ eNewExprOp = TK_TRIGGER;
+#endif /* SQLITE_OMIT_TRIGGER */
}
- pExpr->iColumn = (i16)iCol;
- pExpr->pTab = pTab;
- isTrigger = 1;
}
}
}
-#endif /* !defined(SQLITE_OMIT_TRIGGER) */
+#endif /* !defined(SQLITE_OMIT_TRIGGER) || !defined(SQLITE_OMIT_UPSERT) */
/*
** Perhaps the name is a reference to the ROWID
@@ -383,10 +410,12 @@ static int lookupName(
** is supported for backwards compatibility only. Hence, we issue a warning
** on sqlite3_log() whenever the capability is used.
*/
- if( (pEList = pNC->pEList)!=0
- && zTab==0
+ if( (pNC->ncFlags & NC_UEList)!=0
&& cnt==0
+ && zTab==0
){
+ pEList = pNC->uNC.pEList;
+ assert( pEList!=0 );
for(j=0; j<pEList->nExpr; j++){
char *zAs = pEList->a[j].zName;
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
@@ -483,7 +512,7 @@ static int lookupName(
pExpr->pLeft = 0;
sqlite3ExprDelete(db, pExpr->pRight);
pExpr->pRight = 0;
- pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN);
+ pExpr->op = eNewExprOp;
ExprSetProperty(pExpr, EP_Leaf);
lookupname_end:
if( cnt==1 ){
@@ -915,8 +944,8 @@ static int resolveOrderByTermToExprList(
memset(&nc, 0, sizeof(nc));
nc.pParse = pParse;
nc.pSrcList = pSelect->pSrc;
- nc.pEList = pEList;
- nc.ncFlags = NC_AllowAgg;
+ nc.uNC.pEList = pEList;
+ nc.ncFlags = NC_AllowAgg|NC_UEList;
nc.nErr = 0;
db = pParse->db;
savedSuppErr = db->suppressErr;
@@ -1299,7 +1328,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** Minor point: If this is the case, then the expression will be
** re-evaluated for each reference to it.
*/
- sNC.pEList = p->pEList;
+ assert( (sNC.ncFlags & (NC_UAggInfo|NC_UUpsert))==0 );
+ sNC.uNC.pEList = p->pEList;
+ sNC.ncFlags |= NC_UEList;
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
@@ -1532,7 +1563,7 @@ void sqlite3ResolveSelfReference(
Table *pTab, /* The table being referenced */
int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
Expr *pExpr, /* Expression to resolve. May be NULL. */
- ExprList *pList /* Expression list to resolve. May be NUL. */
+ ExprList *pList /* Expression list to resolve. May be NULL. */
){
SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
diff --git a/src/select.c b/src/select.c
index 4ee7521..529df0f 100644
--- a/src/select.c
+++ b/src/select.c
@@ -21,7 +21,7 @@
/***/ int sqlite3SelectTrace = 0;
# define SELECTTRACE(K,P,S,X) \
if(sqlite3SelectTrace&(K)) \
- sqlite3DebugPrintf("%s/%p: ",(S)->zSelName,(S)),\
+ sqlite3DebugPrintf("%s/%d/%p: ",(S)->zSelName,(P)->addrExplain,(S)),\
sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
@@ -44,6 +44,20 @@ struct DistinctCtx {
/*
** An instance of the following object is used to record information about
** the ORDER BY (or GROUP BY) clause of query is being coded.
+**
+** The aDefer[] array is used by the sorter-references optimization. For
+** example, assuming there is no index that can be used for the ORDER BY,
+** for the query:
+**
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10;
+**
+** it may be more efficient to add just the "a" values to the sorter, and
+** retrieve the associated "bigblob" values directly from table t1 as the
+** 10 smallest "a" values are extracted from the sorter.
+**
+** When the sorter-reference optimization is used, there is one entry in the
+** aDefer[] array for each database table that may be read as values are
+** extracted from the sorter.
*/
typedef struct SortCtx SortCtx;
struct SortCtx {
@@ -56,6 +70,15 @@ struct SortCtx {
int labelDone; /* Jump here when done, ex: LIMIT reached */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ u8 nDefer; /* Number of valid entries in aDefer[] */
+ struct DeferredCsr {
+ Table *pTab; /* Table definition */
+ int iCsr; /* Cursor number for table */
+ int nKey; /* Number of PK columns for table pTab (>=1) */
+ } aDefer[4];
+#endif
+ struct RowLoadInfo *pDeferredRowLoad; /* Deferred row loading info or NULL */
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
@@ -515,6 +538,62 @@ static KeyInfo *keyInfoFromExprList(
);
/*
+** An instance of this object holds information (beyond pParse and pSelect)
+** needed to load the next result row that is to be added to the sorter.
+*/
+typedef struct RowLoadInfo RowLoadInfo;
+struct RowLoadInfo {
+ int regResult; /* Store results in array of registers here */
+ u8 ecelFlags; /* Flag argument to ExprCodeExprList() */
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ ExprList *pExtra; /* Extra columns needed by sorter refs */
+ int regExtraResult; /* Where to load the extra columns */
+#endif
+};
+
+/*
+** This routine does the work of loading query data into an array of
+** registers so that it can be added to the sorter.
+*/
+static void innerLoopLoadRow(
+ Parse *pParse, /* Statement under construction */
+ Select *pSelect, /* The query being coded */
+ RowLoadInfo *pInfo /* Info needed to complete the row load */
+){
+ sqlite3ExprCodeExprList(pParse, pSelect->pEList, pInfo->regResult,
+ 0, pInfo->ecelFlags);
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( pInfo->pExtra ){
+ sqlite3ExprCodeExprList(pParse, pInfo->pExtra, pInfo->regExtraResult, 0, 0);
+ sqlite3ExprListDelete(pParse->db, pInfo->pExtra);
+ }
+#endif
+}
+
+/*
+** Code the OP_MakeRecord instruction that generates the entry to be
+** added into the sorter.
+**
+** Return the register in which the result is stored.
+*/
+static int makeSorterRecord(
+ Parse *pParse,
+ SortCtx *pSort,
+ Select *pSelect,
+ int regBase,
+ int nBase
+){
+ int nOBSat = pSort->nOBSat;
+ Vdbe *v = pParse->pVdbe;
+ int regOut = ++pParse->nMem;
+ if( pSort->pDeferredRowLoad ){
+ innerLoopLoadRow(pParse, pSelect, pSort->pDeferredRowLoad);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regOut);
+ return regOut;
+}
+
+/*
** Generate code that will push the record in registers regData
** through regData+nData-1 onto the sorter.
*/
@@ -524,7 +603,7 @@ static void pushOntoSorter(
Select *pSelect, /* The whole SELECT statement */
int regData, /* First register holding data to be sorted */
int regOrigData, /* First register holding data before packing */
- int nData, /* Number of elements in the data array */
+ int nData, /* Number of elements in the regData data array */
int nPrefixReg /* No. of reg prior to regData available for use */
){
Vdbe *v = pParse->pVdbe; /* Stmt under construction */
@@ -532,16 +611,32 @@ static void pushOntoSorter(
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
int regBase; /* Regs for sorter record */
- int regRecord = ++pParse->nMem; /* Assembled sorter record */
+ int regRecord = 0; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
int op; /* Opcode to add sorter record to sorter */
int iLimit; /* LIMIT counter */
+ int iSkip = 0; /* End of the sorter insert loop */
assert( bSeq==0 || bSeq==1 );
+
+ /* Three cases:
+ ** (1) The data to be sorted has already been packed into a Record
+ ** by a prior OP_MakeRecord. In this case nData==1 and regData
+ ** will be completely unrelated to regOrigData.
+ ** (2) All output columns are included in the sort record. In that
+ ** case regData==regOrigData.
+ ** (3) Some output columns are omitted from the sort record due to
+ ** the SQLITE_ENABLE_SORTER_REFERENCE optimization, or due to the
+ ** SQLITE_ECEL_OMITREF optimization, or due to the
+ ** SortCtx.pDeferredRowLoad optimiation. In any of these cases
+ ** regOrigData is 0 to prevent this routine from trying to copy
+ ** values that might not yet exist.
+ */
assert( nData==1 || regData==regOrigData || regOrigData==0 );
+
if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq );
- regBase = regData - nExpr - bSeq;
+ regBase = regData - nPrefixReg;
}else{
regBase = pParse->nMem + 1;
pParse->nMem += nBase;
@@ -557,7 +652,6 @@ static void pushOntoSorter(
if( nPrefixReg==0 && nData>0 ){
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
if( nOBSat>0 ){
int regPrevKey; /* The first nOBSat columns of the previous row */
int addrFirst; /* Address of the OP_IfNot opcode */
@@ -566,6 +660,7 @@ static void pushOntoSorter(
int nKey; /* Number of sorting key columns, including OP_Sequence */
KeyInfo *pKI; /* Original KeyInfo on the sorter table */
+ regRecord = makeSorterRecord(pParse, pSort, pSelect, regBase, nBase);
regPrevKey = pParse->nMem+1;
pParse->nMem += pSort->nOBSat;
nKey = nExpr - pSort->nOBSat + bSeq;
@@ -599,6 +694,34 @@ static void pushOntoSorter(
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
}
+ if( iLimit ){
+ /* At this point the values for the new sorter entry are stored
+ ** in an array of registers. They need to be composed into a record
+ ** and inserted into the sorter if either (a) there are currently
+ ** less than LIMIT+OFFSET items or (b) the new record is smaller than
+ ** the largest record currently in the sorter. If (b) is true and there
+ ** are already LIMIT+OFFSET items in the sorter, delete the largest
+ ** entry before inserting the new one. This way there are never more
+ ** than LIMIT+OFFSET items in the sorter.
+ **
+ ** If the new record does not need to be inserted into the sorter,
+ ** jump to the next iteration of the loop. Or, if the
+ ** pSort->bOrderedInnerLoop flag is set to indicate that the inner
+ ** loop delivers items in sorted order, jump to the next iteration
+ ** of the outer loop.
+ */
+ int iCsr = pSort->iECursor;
+ sqlite3VdbeAddOp2(v, OP_IfNotZero, iLimit, sqlite3VdbeCurrentAddr(v)+4);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Last, iCsr, 0);
+ iSkip = sqlite3VdbeAddOp4Int(v, OP_IdxLE,
+ iCsr, 0, regBase+nOBSat, nExpr-nOBSat);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp1(v, OP_Delete, iCsr);
+ }
+ if( regRecord==0 ){
+ regRecord = makeSorterRecord(pParse, pSort, pSelect, regBase, nBase);
+ }
if( pSort->sortFlags & SORTFLAG_UseSorter ){
op = OP_SorterInsert;
}else{
@@ -606,33 +729,10 @@ static void pushOntoSorter(
}
sqlite3VdbeAddOp4Int(v, op, pSort->iECursor, regRecord,
regBase+nOBSat, nBase-nOBSat);
- if( iLimit ){
- int addr;
- int r1 = 0;
- /* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
- ** register is initialized with value of LIMIT+OFFSET.) After the sorter
- ** fills up, delete the least entry in the sorter after each insert.
- ** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
- addr = sqlite3VdbeAddOp1(v, OP_IfNotZero, iLimit); VdbeCoverage(v);
- sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
- if( pSort->bOrderedInnerLoop ){
- r1 = ++pParse->nMem;
- sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
- VdbeComment((v, "seq"));
- }
- sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
- if( pSort->bOrderedInnerLoop ){
- /* If the inner loop is driven by an index such that values from
- ** the same iteration of the inner loop are in sorted order, then
- ** immediately jump to the next iteration of an inner loop if the
- ** entry from the current iteration does not fit into the top
- ** LIMIT+OFFSET entries of the sorter. */
- int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
- sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- VdbeCoverage(v);
- }
- sqlite3VdbeJumpHere(v, addr);
+ if( iSkip ){
+ assert( pSort->bOrderedInnerLoop==0 || pSort->bOrderedInnerLoop==1 );
+ sqlite3VdbeChangeP2(v, iSkip,
+ sqlite3VdbeCurrentAddr(v) + pSort->bOrderedInnerLoop);
}
}
@@ -678,6 +778,87 @@ static void codeDistinct(
sqlite3ReleaseTempReg(pParse, r1);
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+/*
+** This function is called as part of inner-loop generation for a SELECT
+** statement with an ORDER BY that is not optimized by an index. It
+** determines the expressions, if any, that the sorter-reference
+** optimization should be used for. The sorter-reference optimization
+** is used for SELECT queries like:
+**
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10
+**
+** If the optimization is used for expression "bigblob", then instead of
+** storing values read from that column in the sorter records, the PK of
+** the row from table t1 is stored instead. Then, as records are extracted from
+** the sorter to return to the user, the required value of bigblob is
+** retrieved directly from table t1. If the values are very large, this
+** can be more efficient than storing them directly in the sorter records.
+**
+** The ExprList_item.bSorterRef flag is set for each expression in pEList
+** for which the sorter-reference optimization should be enabled.
+** Additionally, the pSort->aDefer[] array is populated with entries
+** for all cursors required to evaluate all selected expressions. Finally.
+** output variable (*ppExtra) is set to an expression list containing
+** expressions for all extra PK values that should be stored in the
+** sorter records.
+*/
+static void selectExprDefer(
+ Parse *pParse, /* Leave any error here */
+ SortCtx *pSort, /* Sorter context */
+ ExprList *pEList, /* Expressions destined for sorter */
+ ExprList **ppExtra /* Expressions to append to sorter record */
+){
+ int i;
+ int nDefer = 0;
+ ExprList *pExtra = 0;
+ for(i=0; i<pEList->nExpr; i++){
+ struct ExprList_item *pItem = &pEList->a[i];
+ if( pItem->u.x.iOrderByCol==0 ){
+ Expr *pExpr = pItem->pExpr;
+ Table *pTab = pExpr->pTab;
+ if( pExpr->op==TK_COLUMN && pExpr->iColumn>=0 && pTab && !IsVirtual(pTab)
+ && (pTab->aCol[pExpr->iColumn].colFlags & COLFLAG_SORTERREF)
+ ){
+ int j;
+ for(j=0; j<nDefer; j++){
+ if( pSort->aDefer[j].iCsr==pExpr->iTable ) break;
+ }
+ if( j==nDefer ){
+ if( nDefer==ArraySize(pSort->aDefer) ){
+ continue;
+ }else{
+ int nKey = 1;
+ int k;
+ Index *pPk = 0;
+ if( !HasRowid(pTab) ){
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ nKey = pPk->nKeyCol;
+ }
+ for(k=0; k<nKey; k++){
+ Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
+ if( pNew ){
+ pNew->iTable = pExpr->iTable;
+ pNew->pTab = pExpr->pTab;
+ pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
+ pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
+ }
+ }
+ pSort->aDefer[nDefer].pTab = pExpr->pTab;
+ pSort->aDefer[nDefer].iCsr = pExpr->iTable;
+ pSort->aDefer[nDefer].nKey = nKey;
+ nDefer++;
+ }
+ }
+ pItem->bSorterRef = 1;
+ }
+ }
+ }
+ pSort->nDefer = (u8)nDefer;
+ *ppExtra = pExtra;
+}
+#endif
+
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
@@ -704,6 +885,7 @@ static void selectInnerLoop(
int iParm = pDest->iSDParm; /* First argument to disposal method */
int nResultCol; /* Number of result columns */
int nPrefixReg = 0; /* Number of extra registers before regResult */
+ RowLoadInfo sRowLoadInfo; /* Info for deferred row loading */
/* Usually, regResult is the first cell in an array of memory cells
** containing the current result row. In this case regOrig is set to the
@@ -750,10 +932,14 @@ static void selectInnerLoop(
VdbeComment((v, "%s", p->pEList->a[i].zName));
}
}else if( eDest!=SRT_Exists ){
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ ExprList *pExtra = 0;
+#endif
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
- u8 ecelFlags;
+ u8 ecelFlags; /* "ecel" is an abbreviation of "ExprCodeExprList" */
+ ExprList *pEList;
if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
ecelFlags = SQLITE_ECEL_DUP;
}else{
@@ -767,18 +953,68 @@ static void selectInnerLoop(
** This allows the p->pEList field to be omitted from the sorted record,
** saving space and CPU cycles. */
ecelFlags |= (SQLITE_ECEL_OMITREF|SQLITE_ECEL_REF);
+
for(i=pSort->nOBSat; i<pSort->pOrderBy->nExpr; i++){
int j;
if( (j = pSort->pOrderBy->a[i].u.x.iOrderByCol)>0 ){
p->pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat;
}
}
- regOrig = 0;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ selectExprDefer(pParse, pSort, p->pEList, &pExtra);
+ if( pExtra && pParse->db->mallocFailed==0 ){
+ /* If there are any extra PK columns to add to the sorter records,
+ ** allocate extra memory cells and adjust the OpenEphemeral
+ ** instruction to account for the larger records. This is only
+ ** required if there are one or more WITHOUT ROWID tables with
+ ** composite primary keys in the SortCtx.aDefer[] array. */
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
+ pOp->p2 += (pExtra->nExpr - pSort->nDefer);
+ pOp->p4.pKeyInfo->nAllField += (pExtra->nExpr - pSort->nDefer);
+ pParse->nMem += pExtra->nExpr;
+ }
+#endif
+
+ /* Adjust nResultCol to account for columns that are omitted
+ ** from the sorter by the optimizations in this branch */
+ pEList = p->pEList;
+ for(i=0; i<pEList->nExpr; i++){
+ if( pEList->a[i].u.x.iOrderByCol>0
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ || pEList->a[i].bSorterRef
+#endif
+ ){
+ nResultCol--;
+ regOrig = 0;
+ }
+ }
+
+ testcase( regOrig );
+ testcase( eDest==SRT_Set );
+ testcase( eDest==SRT_Mem );
+ testcase( eDest==SRT_Coroutine );
+ testcase( eDest==SRT_Output );
assert( eDest==SRT_Set || eDest==SRT_Mem
|| eDest==SRT_Coroutine || eDest==SRT_Output );
}
- nResultCol = sqlite3ExprCodeExprList(pParse,p->pEList,regResult,
- 0,ecelFlags);
+ sRowLoadInfo.regResult = regResult;
+ sRowLoadInfo.ecelFlags = ecelFlags;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ sRowLoadInfo.pExtra = pExtra;
+ sRowLoadInfo.regExtraResult = regResult + nResultCol;
+ if( pExtra ) nResultCol += pExtra->nExpr;
+#endif
+ if( p->iLimit
+ && (ecelFlags & SQLITE_ECEL_OMITREF)!=0
+ && nPrefixReg>0
+ ){
+ assert( pSort!=0 );
+ assert( hasDistinct==0 );
+ pSort->pDeferredRowLoad = &sRowLoadInfo;
+ regOrig = 0;
+ }else{
+ innerLoopLoadRow(pParse, p, &sRowLoadInfo);
+ }
}
/* If the DISTINCT keyword was present on the SELECT statement
@@ -894,7 +1130,8 @@ static void selectInnerLoop(
}
#endif
if( pSort ){
- pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
+ assert( regResult==regOrig );
+ pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, regOrig, 1, nPrefixReg);
}else{
int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@@ -1161,11 +1398,7 @@ static const char *selectOpName(int id){
** is determined by the zUsage argument.
*/
static void explainTempTable(Parse *pParse, const char *zUsage){
- if( pParse->explain==2 ){
- Vdbe *v = pParse->pVdbe;
- char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
- sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
- }
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s", zUsage));
}
/*
@@ -1183,42 +1416,6 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
# define explainSetInteger(y,z)
#endif
-#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT)
-/*
-** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
-** is a no-op. Otherwise, it adds a single row of output to the EQP result,
-** where the caption is of one of the two forms:
-**
-** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
-** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
-**
-** where iSub1 and iSub2 are the integers passed as the corresponding
-** function parameters, and op is the text representation of the parameter
-** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
-** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
-** false, or the second form if it is true.
-*/
-static void explainComposite(
- Parse *pParse, /* Parse context */
- int op, /* One of TK_UNION, TK_EXCEPT etc. */
- int iSub1, /* Subquery id 1 */
- int iSub2, /* Subquery id 2 */
- int bUseTmp /* True if a temp table was used */
-){
- assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
- if( pParse->explain==2 ){
- Vdbe *v = pParse->pVdbe;
- char *zMsg = sqlite3MPrintf(
- pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
- bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
- );
- sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
- }
-}
-#else
-/* No-op versions of the explainXXX() functions and macros. */
-# define explainComposite(v,w,x,y,z)
-#endif
/*
** If the inner loop was generated using a non-null pOrderBy argument,
@@ -1236,7 +1433,7 @@ static void generateSortTail(
Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
- int addr;
+ int addr; /* Top of output loop. Jump for Next. */
int addrOnce = 0;
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
@@ -1245,11 +1442,11 @@ static void generateSortTail(
int regRow;
int regRowid;
int iCol;
- int nKey;
+ int nKey; /* Number of key columns in sorter record */
int iSortTab; /* Sorter cursor to read from */
- int nSortData; /* Trailing values to read from sorter */
int i;
int bSeq; /* True if sorter record includes seq. no. */
+ int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a;
assert( addrBreak<0 );
@@ -1258,15 +1455,24 @@ static void generateSortTail(
sqlite3VdbeGoto(v, addrBreak);
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
+
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ /* Open any cursors needed for sorter-reference expressions */
+ for(i=0; i<pSort->nDefer; i++){
+ Table *pTab = pSort->aDefer[i].pTab;
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ sqlite3OpenTable(pParse, pSort->aDefer[i].iCsr, iDb, pTab, OP_OpenRead);
+ nRefKey = MAX(nRefKey, pSort->aDefer[i].nKey);
+ }
+#endif
+
iTab = pSort->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
regRow = pDest->iSdst;
- nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
regRow = sqlite3GetTempRange(pParse, nColumn);
- nSortData = nColumn;
}
nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
@@ -1275,7 +1481,8 @@ static void generateSortTail(
if( pSort->labelBkOut ){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
+ nKey+1+nColumn+nRefKey);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
@@ -1288,18 +1495,59 @@ static void generateSortTail(
iSortTab = iTab;
bSeq = 1;
}
- for(i=0, iCol=nKey+bSeq-1; i<nSortData; i++){
+ for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( aOutEx[i].bSorterRef ) continue;
+#endif
if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
}
- for(i=nSortData-1; i>=0; i--){
- int iRead;
- if( aOutEx[i].u.x.iOrderByCol ){
- iRead = aOutEx[i].u.x.iOrderByCol-1;
- }else{
- iRead = iCol--;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( pSort->nDefer ){
+ int iKey = iCol+1;
+ int regKey = sqlite3GetTempRange(pParse, nRefKey);
+
+ for(i=0; i<pSort->nDefer; i++){
+ int iCsr = pSort->aDefer[i].iCsr;
+ Table *pTab = pSort->aDefer[i].pTab;
+ int nKey = pSort->aDefer[i].nKey;
+
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
+ sqlite3VdbeCurrentAddr(v)+1, regKey);
+ }else{
+ int k;
+ int iJmp;
+ assert( sqlite3PrimaryKeyIndex(pTab)->nKeyCol==nKey );
+ for(k=0; k<nKey; k++){
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey+k);
+ }
+ iJmp = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp4Int(v, OP_SeekGE, iCsr, iJmp+2, regKey, nKey);
+ sqlite3VdbeAddOp4Int(v, OP_IdxLE, iCsr, iJmp+3, regKey, nKey);
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
+ }
+ }
+ sqlite3ReleaseTempRange(pParse, regKey, nRefKey);
+ }
+#endif
+ for(i=nColumn-1; i>=0; i--){
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( aOutEx[i].bSorterRef ){
+ sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i);
+ }else
+#endif
+ {
+ int iRead;
+ if( aOutEx[i].u.x.iOrderByCol ){
+ iRead = aOutEx[i].u.x.iOrderByCol-1;
+ }else{
+ iRead = iCol--;
+ }
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
+ VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName : aOutEx[i].zSpan));
}
- sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
- VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
case SRT_Table:
@@ -1617,7 +1865,7 @@ static void generateColumnNames(
}
#endif
- if( pParse->colNamesSet || db->mallocFailed ) return;
+ if( pParse->colNamesSet ) return;
/* Column names are determined by the left-most term of a compound select */
while( pSelect->pPrior ) pSelect = pSelect->pPrior;
SELECTTRACE(1,pParse,pSelect,("generating column names\n"));
@@ -2144,6 +2392,7 @@ static void generateWithRecursiveQuery(
/* Store the results of the setup-query in Queue. */
pSetup->pNext = 0;
+ ExplainQueryPlan((pParse, 1, "SETUP"));
rc = sqlite3Select(pParse, pSetup, &destQueue);
pSetup->pNext = p;
if( rc ) goto end_of_recursive_query;
@@ -2178,6 +2427,7 @@ static void generateWithRecursiveQuery(
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
}else{
p->pPrior = 0;
+ ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
sqlite3Select(pParse, p, &destQueue);
assert( p->pPrior==0 );
p->pPrior = pSetup;
@@ -2223,10 +2473,9 @@ static int multiSelectValues(
Select *p, /* The right-most of SELECTs to be coded */
SelectDest *pDest /* What to do with query results */
){
- Select *pPrior;
- Select *pRightmost = p;
int nRow = 1;
int rc = 0;
+ int bShowAll = p->pLimit==0;
assert( p->selFlags & SF_MultiValue );
do{
assert( p->selFlags & SF_Values );
@@ -2235,14 +2484,13 @@ static int multiSelectValues(
if( p->pPrior==0 ) break;
assert( p->pPrior->pNext==p );
p = p->pPrior;
- nRow++;
+ nRow += bShowAll;
}while(1);
+ ExplainQueryPlan((pParse, 0, "SCAN %d CONSTANT ROW%s", nRow,
+ nRow==1 ? "" : "S"));
while( p ){
- pPrior = p->pPrior;
- p->pPrior = 0;
- rc = sqlite3Select(pParse, p, pDest);
- p->pPrior = pPrior;
- if( rc || pRightmost->pLimit ) break;
+ selectInnerLoop(pParse, p, -1, 0, 0, pDest, 1, 1);
+ if( !bShowAll ) break;
p->nSelectRow = nRow;
p = p->pNext;
}
@@ -2291,10 +2539,6 @@ static int multiSelect(
SelectDest dest; /* Alternative data destination */
Select *pDelete = 0; /* Chain of simple selects to delete */
sqlite3 *db; /* Database connection */
-#ifndef SQLITE_OMIT_EXPLAIN
- 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.
@@ -2345,217 +2589,231 @@ static int multiSelect(
*/
if( p->pOrderBy ){
return multiSelectOrderBy(pParse, p, pDest);
- }else
+ }else{
- /* Generate code for the left and right SELECT statements.
- */
- switch( p->op ){
- case TK_ALL: {
- int addr = 0;
- int nLimit;
- assert( !pPrior->pLimit );
- pPrior->iLimit = p->iLimit;
- pPrior->iOffset = p->iOffset;
- pPrior->pLimit = p->pLimit;
- explainSetInteger(iSub1, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, pPrior, &dest);
- p->pLimit = 0;
- if( rc ){
- goto multi_select_end;
- }
- p->pPrior = 0;
- p->iLimit = pPrior->iLimit;
- p->iOffset = pPrior->iOffset;
- if( p->iLimit ){
- addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
- VdbeComment((v, "Jump ahead if LIMIT reached"));
- if( p->iOffset ){
- sqlite3VdbeAddOp3(v, OP_OffsetLimit,
- p->iLimit, p->iOffset+1, p->iOffset);
+#ifndef SQLITE_OMIT_EXPLAIN
+ if( pPrior->pPrior==0 ){
+ ExplainQueryPlan((pParse, 1, "COMPOUND QUERY"));
+ ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
+ }
+#endif
+
+ /* Generate code for the left and right SELECT statements.
+ */
+ switch( p->op ){
+ case TK_ALL: {
+ int addr = 0;
+ int nLimit;
+ assert( !pPrior->pLimit );
+ pPrior->iLimit = p->iLimit;
+ pPrior->iOffset = p->iOffset;
+ pPrior->pLimit = p->pLimit;
+ rc = sqlite3Select(pParse, pPrior, &dest);
+ p->pLimit = 0;
+ if( rc ){
+ goto multi_select_end;
}
+ p->pPrior = 0;
+ p->iLimit = pPrior->iLimit;
+ p->iOffset = pPrior->iOffset;
+ if( p->iLimit ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
+ VdbeComment((v, "Jump ahead if LIMIT reached"));
+ if( p->iOffset ){
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+ p->iLimit, p->iOffset+1, p->iOffset);
+ }
+ }
+ ExplainQueryPlan((pParse, 1, "UNION ALL"));
+ rc = sqlite3Select(pParse, p, &dest);
+ testcase( rc!=SQLITE_OK );
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ if( pPrior->pLimit
+ && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
+ ){
+ p->nSelectRow = sqlite3LogEst((u64)nLimit);
+ }
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ }
+ break;
}
- explainSetInteger(iSub2, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, p, &dest);
- testcase( rc!=SQLITE_OK );
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
- if( pPrior->pLimit
- && sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
- && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
- ){
- p->nSelectRow = sqlite3LogEst((u64)nLimit);
- }
- if( addr ){
- sqlite3VdbeJumpHere(v, addr);
- }
- break;
- }
- case TK_EXCEPT:
- case TK_UNION: {
- int unionTab; /* Cursor number of the temporary table holding result */
- u8 op = 0; /* One of the SRT_ operations to apply to self */
- int priorOp; /* The SRT_ operation to apply to prior selects */
- Expr *pLimit; /* Saved values of p->nLimit */
- int addr;
- SelectDest uniondest;
-
- testcase( p->op==TK_EXCEPT );
- testcase( p->op==TK_UNION );
- priorOp = SRT_Union;
- if( dest.eDest==priorOp ){
- /* We can reuse a temporary table generated by a SELECT to our
- ** right.
+ case TK_EXCEPT:
+ case TK_UNION: {
+ int unionTab; /* Cursor number of the temp table holding result */
+ u8 op = 0; /* One of the SRT_ operations to apply to self */
+ int priorOp; /* The SRT_ operation to apply to prior selects */
+ Expr *pLimit; /* Saved values of p->nLimit */
+ int addr;
+ SelectDest uniondest;
+
+ testcase( p->op==TK_EXCEPT );
+ testcase( p->op==TK_UNION );
+ priorOp = SRT_Union;
+ if( dest.eDest==priorOp ){
+ /* We can reuse a temporary table generated by a SELECT to our
+ ** right.
+ */
+ assert( p->pLimit==0 ); /* Not allowed on leftward elements */
+ unionTab = dest.iSDParm;
+ }else{
+ /* We will need to create our own temporary table to hold the
+ ** intermediate results.
+ */
+ unionTab = pParse->nTab++;
+ assert( p->pOrderBy==0 );
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
+ assert( p->addrOpenEphm[0] == -1 );
+ p->addrOpenEphm[0] = addr;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
+ assert( p->pEList );
+ }
+
+ /* Code the SELECT statements to our left
*/
- assert( p->pLimit==0 ); /* Not allowed on leftward elements */
- unionTab = dest.iSDParm;
- }else{
- /* We will need to create our own temporary table to hold the
- ** intermediate results.
+ assert( !pPrior->pOrderBy );
+ sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
+ rc = sqlite3Select(pParse, pPrior, &uniondest);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT statement
+ */
+ if( p->op==TK_EXCEPT ){
+ op = SRT_Except;
+ }else{
+ assert( p->op==TK_UNION );
+ op = SRT_Union;
+ }
+ p->pPrior = 0;
+ pLimit = p->pLimit;
+ p->pLimit = 0;
+ uniondest.eDest = op;
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
+ selectOpName(p->op)));
+ rc = sqlite3Select(pParse, p, &uniondest);
+ testcase( rc!=SQLITE_OK );
+ /* Query flattening in sqlite3Select() might refill p->pOrderBy.
+ ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
+ sqlite3ExprListDelete(db, p->pOrderBy);
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ p->pOrderBy = 0;
+ if( p->op==TK_UNION ){
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ }
+ sqlite3ExprDelete(db, p->pLimit);
+ p->pLimit = pLimit;
+ p->iLimit = 0;
+ p->iOffset = 0;
+
+ /* Convert the data in the temporary table into whatever form
+ ** it is that we currently need.
*/
- unionTab = pParse->nTab++;
+ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
+ if( dest.eDest!=priorOp ){
+ int iCont, iBreak, iStart;
+ assert( p->pEList );
+ iBreak = sqlite3VdbeMakeLabel(v);
+ iCont = sqlite3VdbeMakeLabel(v);
+ computeLimitRegisters(pParse, p, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
+ iStart = sqlite3VdbeCurrentAddr(v);
+ selectInnerLoop(pParse, p, unionTab,
+ 0, 0, &dest, iCont, iBreak);
+ sqlite3VdbeResolveLabel(v, iCont);
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
+ sqlite3VdbeResolveLabel(v, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
+ }
+ break;
+ }
+ default: assert( p->op==TK_INTERSECT ); {
+ int tab1, tab2;
+ int iCont, iBreak, iStart;
+ Expr *pLimit;
+ int addr;
+ SelectDest intersectdest;
+ int r1;
+
+ /* INTERSECT is different from the others since it requires
+ ** two temporary tables. Hence it has its own case. Begin
+ ** by allocating the tables we will need.
+ */
+ tab1 = pParse->nTab++;
+ tab2 = pParse->nTab++;
assert( p->pOrderBy==0 );
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
+
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
- }
-
- /* Code the SELECT statements to our left
- */
- assert( !pPrior->pOrderBy );
- sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
- explainSetInteger(iSub1, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, pPrior, &uniondest);
- if( rc ){
- goto multi_select_end;
- }
-
- /* Code the current SELECT statement
- */
- if( p->op==TK_EXCEPT ){
- op = SRT_Except;
- }else{
- assert( p->op==TK_UNION );
- op = SRT_Union;
- }
- p->pPrior = 0;
- pLimit = p->pLimit;
- p->pLimit = 0;
- uniondest.eDest = op;
- explainSetInteger(iSub2, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, p, &uniondest);
- testcase( rc!=SQLITE_OK );
- /* Query flattening in sqlite3Select() might refill p->pOrderBy.
- ** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
- sqlite3ExprListDelete(db, p->pOrderBy);
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- p->pOrderBy = 0;
- if( p->op==TK_UNION ){
- p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
- }
- sqlite3ExprDelete(db, p->pLimit);
- p->pLimit = pLimit;
- p->iLimit = 0;
- p->iOffset = 0;
-
- /* Convert the data in the temporary table into whatever form
- ** it is that we currently need.
- */
- assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
- if( dest.eDest!=priorOp ){
- int iCont, iBreak, iStart;
+
+ /* Code the SELECTs to our left into temporary table "tab1".
+ */
+ sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
+ rc = sqlite3Select(pParse, pPrior, &intersectdest);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT into temporary table "tab2"
+ */
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
+ assert( p->addrOpenEphm[1] == -1 );
+ p->addrOpenEphm[1] = addr;
+ p->pPrior = 0;
+ pLimit = p->pLimit;
+ p->pLimit = 0;
+ intersectdest.iSDParm = tab2;
+ ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
+ selectOpName(p->op)));
+ rc = sqlite3Select(pParse, p, &intersectdest);
+ testcase( rc!=SQLITE_OK );
+ pDelete = p->pPrior;
+ p->pPrior = pPrior;
+ if( p->nSelectRow>pPrior->nSelectRow ){
+ p->nSelectRow = pPrior->nSelectRow;
+ }
+ sqlite3ExprDelete(db, p->pLimit);
+ p->pLimit = pLimit;
+
+ /* Generate code to take the intersection of the two temporary
+ ** tables.
+ */
assert( p->pEList );
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
- iStart = sqlite3VdbeCurrentAddr(v);
- selectInnerLoop(pParse, p, unionTab,
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
+ r1 = sqlite3GetTempReg(pParse);
+ iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, r1);
+ selectInnerLoop(pParse, p, tab1,
0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
+ break;
}
- break;
}
- default: assert( p->op==TK_INTERSECT ); {
- int tab1, tab2;
- int iCont, iBreak, iStart;
- Expr *pLimit;
- int addr;
- SelectDest intersectdest;
- int r1;
-
- /* INTERSECT is different from the others since it requires
- ** two temporary tables. Hence it has its own case. Begin
- ** by allocating the tables we will need.
- */
- tab1 = pParse->nTab++;
- tab2 = pParse->nTab++;
- assert( p->pOrderBy==0 );
-
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
- assert( p->addrOpenEphm[0] == -1 );
- p->addrOpenEphm[0] = addr;
- findRightmost(p)->selFlags |= SF_UsesEphemeral;
- assert( p->pEList );
-
- /* Code the SELECTs to our left into temporary table "tab1".
- */
- sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
- explainSetInteger(iSub1, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, pPrior, &intersectdest);
- if( rc ){
- goto multi_select_end;
- }
-
- /* Code the current SELECT into temporary table "tab2"
- */
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
- assert( p->addrOpenEphm[1] == -1 );
- p->addrOpenEphm[1] = addr;
- p->pPrior = 0;
- pLimit = p->pLimit;
- p->pLimit = 0;
- intersectdest.iSDParm = tab2;
- explainSetInteger(iSub2, pParse->iNextSelectId);
- rc = sqlite3Select(pParse, p, &intersectdest);
- testcase( rc!=SQLITE_OK );
- pDelete = p->pPrior;
- p->pPrior = pPrior;
- if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
- sqlite3ExprDelete(db, p->pLimit);
- p->pLimit = pLimit;
-
- /* Generate code to take the intersection of the two temporary
- ** tables.
- */
- assert( p->pEList );
- iBreak = sqlite3VdbeMakeLabel(v);
- iCont = sqlite3VdbeMakeLabel(v);
- computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
- r1 = sqlite3GetTempReg(pParse);
- iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
- sqlite3ReleaseTempReg(pParse, r1);
- selectInnerLoop(pParse, p, tab1,
- 0, 0, &dest, iCont, iBreak);
- sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
- sqlite3VdbeResolveLabel(v, iBreak);
- sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
- sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
- break;
+
+ #ifndef SQLITE_OMIT_EXPLAIN
+ if( p->pNext==0 ){
+ ExplainQueryPlanPop(pParse);
}
+ #endif
}
-
- explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
-
+
/* Compute collating sequences used by
** temporary tables needed to implement the compound select.
** Attach the KeyInfo structure to all temporary tables.
@@ -2893,10 +3151,6 @@ static int multiSelectOrderBy(
ExprList *pOrderBy; /* The ORDER BY clause */
int nOrderBy; /* Number of terms in the ORDER BY clause */
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
-#ifndef SQLITE_OMIT_EXPLAIN
- int iSub1; /* EQP id of left-hand query */
- int iSub2; /* EQP id of right-hand query */
-#endif
assert( p->pOrderBy!=0 );
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
@@ -3016,6 +3270,8 @@ static int multiSelectOrderBy(
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+ ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
+
/* Generate a coroutine to evaluate the SELECT statement to the
** left of the compound operator - the "A" select.
*/
@@ -3023,7 +3279,7 @@ static int multiSelectOrderBy(
addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
VdbeComment((v, "left SELECT"));
pPrior->iLimit = regLimitA;
- explainSetInteger(iSub1, pParse->iNextSelectId);
+ ExplainQueryPlan((pParse, 1, "LEFT"));
sqlite3Select(pParse, pPrior, &destA);
sqlite3VdbeEndCoroutine(v, regAddrA);
sqlite3VdbeJumpHere(v, addr1);
@@ -3038,7 +3294,7 @@ static int multiSelectOrderBy(
savedOffset = p->iOffset;
p->iLimit = regLimitB;
p->iOffset = 0;
- explainSetInteger(iSub2, pParse->iNextSelectId);
+ ExplainQueryPlan((pParse, 1, "RIGHT"));
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
@@ -3150,7 +3406,7 @@ static int multiSelectOrderBy(
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
- explainComposite(pParse, p->op, iSub1, iSub2, 0);
+ ExplainQueryPlanPop(pParse);
return pParse->nErr!=0;
}
#endif
@@ -3637,9 +3893,8 @@ static int flattenSubquery(
if( pPrior ) pPrior->pNext = pNew;
pNew->pNext = p;
p->pPrior = pNew;
- SELECTTRACE(2,pParse,p,
- ("compound-subquery flattener creates %s.%p as peer\n",
- pNew->zSelName, pNew));
+ SELECTTRACE(2,pParse,p,("compound-subquery flattener"
+ " creates %s.%p as peer\n",pNew->zSelName, pNew));
}
if( db->mallocFailed ) return 1;
}
@@ -4939,14 +5194,11 @@ static void explainSimpleCount(
){
if( pParse->explain==2 ){
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
- char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
+ sqlite3VdbeExplain(pParse, 0, "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
- );
}
}
#else
@@ -5159,19 +5411,15 @@ int sqlite3Select(
ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
u8 minMaxFlag; /* Flag for min/max queries */
-#ifndef SQLITE_OMIT_EXPLAIN
- int iRestoreSelectId = pParse->iSelectId;
- pParse->iSelectId = pParse->iNextSelectId++;
-#endif
-
db = pParse->db;
+ v = sqlite3GetVdbe(pParse);
if( p==0 || db->mallocFailed || pParse->nErr ){
return 1;
}
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
memset(&sAggInfo, 0, sizeof(sAggInfo));
#if SELECTTRACE_ENABLED
- SELECTTRACE(1,pParse,p, ("begin processing:\n"));
+ SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
if( sqlite3SelectTrace & 0x100 ){
sqlite3TreeViewSelect(0, p, 0);
}
@@ -5202,16 +5450,12 @@ int sqlite3Select(
assert( p->pEList!=0 );
isAgg = (p->selFlags & SF_Aggregate)!=0;
#if SELECTTRACE_ENABLED
- if( sqlite3SelectTrace & 0x100 ){
- SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
+ if( sqlite3SelectTrace & 0x104 ){
+ SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
- /* Get a pointer the VDBE under construction, allocating a new VDBE if one
- ** does not already exist */
- v = sqlite3GetVdbe(pParse);
- if( v==0 ) goto select_end;
if( pDest->eDest==SRT_Output ){
generateColumnNames(pParse, p);
}
@@ -5304,10 +5548,13 @@ int sqlite3Select(
*/
if( p->pPrior ){
rc = multiSelect(pParse, p, pDest);
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
#if SELECTTRACE_ENABLED
- SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
+ SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ sqlite3TreeViewSelect(0, p, 0);
+ }
#endif
+ if( p->pNext==0 ) ExplainQueryPlanPop(pParse);
return rc;
}
#endif
@@ -5419,7 +5666,7 @@ int sqlite3Select(
VdbeComment((v, "%s", pItem->pTab->zName));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
- explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
+ ExplainQueryPlan((pParse, 1, "CO-ROUTINE 0x%p", pSub));
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->nRowLogEst = pSub->nSelectRow;
pItem->fg.viaCoroutine = 1;
@@ -5454,12 +5701,11 @@ int sqlite3Select(
pPrior = isSelfJoinView(pTabList, pItem);
if( pPrior ){
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
- explainSetInteger(pItem->iSelectId, pPrior->iSelectId);
assert( pPrior->pSelect!=0 );
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
}else{
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
- explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
+ ExplainQueryPlan((pParse, 1, "MATERIALIZE 0x%p", pSub));
sqlite3Select(pParse, pSub, &dest);
}
pItem->pTab->nRowLogEst = pSub->nSelectRow;
@@ -5686,7 +5932,8 @@ int sqlite3Select(
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
- sNC.pAggInfo = &sAggInfo;
+ sNC.uNC.pAggInfo = &sAggInfo;
+ VVA_ONLY( sNC.ncFlags = NC_UAggInfo; )
sAggInfo.mnReg = pParse->nMem+1;
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
sAggInfo.pGroupBy = pGroupBy;
@@ -6075,6 +6322,7 @@ int sqlite3Select(
if( sSort.pOrderBy ){
explainTempTable(pParse,
sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
+ assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
@@ -6090,12 +6338,15 @@ int sqlite3Select(
** successful coding of the SELECT.
*/
select_end:
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
sqlite3ExprListDelete(db, pMinMaxOrderBy);
sqlite3DbFree(db, sAggInfo.aCol);
sqlite3DbFree(db, sAggInfo.aFunc);
#if SELECTTRACE_ENABLED
- SELECTTRACE(1,pParse,p,("end processing\n"));
+ SELECTTRACE(0x1,pParse,p,("end processing\n"));
+ if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
+ sqlite3TreeViewSelect(0, p, 0);
+ }
#endif
+ ExplainQueryPlanPop(pParse);
return rc;
}
diff --git a/src/shell.c.in b/src/shell.c.in
index ddbf825..b9d7cec 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -436,6 +436,12 @@ void utf8_printf(FILE *out, const char *zFormat, ...){
# define raw_printf fprintf
#endif
+/* Indicate out-of-memory and exit. */
+static void shell_out_of_memory(void){
+ raw_printf(stderr,"Error: out of memory\n");
+ exit(1);
+}
+
/*
** Write I/O traces to the following stream.
*/
@@ -768,45 +774,12 @@ static void appendText(ShellText *p, char const *zAppend, char quote){
** Return '"' if quoting is required. Return 0 if no quoting is required.
*/
static char quoteChar(const char *zName){
- /* All SQLite keywords, in alphabetical order */
- static const char *azKeywords[] = {
- "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS",
- "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY",
- "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
- "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT_DATE",
- "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
- "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
- "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN",
- "FAIL", "FOR", "FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF",
- "IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER",
- "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY",
- "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL", "NO", "NOT", "NOTNULL",
- "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN", "PRAGMA",
- "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP",
- "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT",
- "ROLLBACK", "ROW", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
- "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER", "UNION", "UNIQUE",
- "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN", "WHERE",
- "WITH", "WITHOUT",
- };
- int i, lwr, upr, mid, c;
+ int i;
if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
for(i=0; zName[i]; i++){
if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
}
- lwr = 0;
- upr = sizeof(azKeywords)/sizeof(azKeywords[0]) - 1;
- while( lwr<=upr ){
- mid = (lwr+upr)/2;
- c = sqlite3_stricmp(azKeywords[mid], zName);
- if( c==0 ) return '"';
- if( c<0 ){
- lwr = mid+1;
- }else{
- upr = mid-1;
- }
- }
- return 0;
+ return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
/*
@@ -1005,6 +978,23 @@ struct ExpertInfo {
int bVerbose;
};
+/* A single line in the EQP output */
+typedef struct EQPGraphRow EQPGraphRow;
+struct EQPGraphRow {
+ int iEqpId; /* ID for this row */
+ int iParentId; /* ID of the parent row */
+ EQPGraphRow *pNext; /* Next row in sequence */
+ char zText[1]; /* Text to display for this row */
+};
+
+/* All EQP output is collected into an instance of the following */
+typedef struct EQPGraph EQPGraph;
+struct EQPGraph {
+ EQPGraphRow *pRow; /* Linked list of all rows of the EQP output */
+ EQPGraphRow *pLast; /* Last element of the pRow list */
+ char zPrefix[100]; /* Graph prefix */
+};
+
/*
** State information about the database connection is contained in an
** instance of the following structure.
@@ -1014,10 +1004,13 @@ struct ShellState {
sqlite3 *db; /* The database */
u8 autoExplain; /* Automatically turn on .explain mode */
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
+ u8 autoEQPtest; /* autoEQP is in test mode */
u8 statsOn; /* True to display memory stats before each finalize */
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
+ u8 nEqpLevel; /* Depth of the EQP output graph */
+ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
@@ -1051,6 +1044,7 @@ struct ShellState {
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
+ EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
#if defined(SQLITE_ENABLE_SESSION)
int nSession; /* Number of active sessions */
OpenSession aSession[4]; /* Array of sessions. [0] is in focus. */
@@ -1061,10 +1055,10 @@ struct ShellState {
/* Allowed values for ShellState.autoEQP
*/
-#define AUTOEQP_off 0
-#define AUTOEQP_on 1
-#define AUTOEQP_trigger 2
-#define AUTOEQP_full 3
+#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
+#define AUTOEQP_on 1 /* Automatic EQP is on */
+#define AUTOEQP_trigger 2 /* On and also show plans for triggers */
+#define AUTOEQP_full 3 /* Show full EXPLAIN */
/* Allowed values for ShellState.openMode
*/
@@ -1107,6 +1101,7 @@ struct ShellState {
#define MODE_Explain 9 /* Like MODE_Column, but do not truncate data */
#define MODE_Ascii 10 /* Use ASCII unit and record separators (0x1F/0x1E) */
#define MODE_Pretty 11 /* Pretty-print schemas */
+#define MODE_EQP 12 /* Converts EXPLAIN QUERY PLAN output into a graph */
static const char *modeDescr[] = {
"line",
@@ -1121,6 +1116,7 @@ static const char *modeDescr[] = {
"explain",
"ascii",
"prettyprint",
+ "eqp"
};
/*
@@ -1669,7 +1665,93 @@ static int wsToEol(const char *z){
}
return 1;
}
-
+
+/*
+** Add a new entry to the EXPLAIN QUERY PLAN data
+*/
+static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
+ EQPGraphRow *pNew;
+ int nText = strlen30(zText);
+ if( p->autoEQPtest ){
+ utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
+ }
+ pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
+ if( pNew==0 ) shell_out_of_memory();
+ pNew->iEqpId = iEqpId;
+ pNew->iParentId = p2;
+ memcpy(pNew->zText, zText, nText+1);
+ pNew->pNext = 0;
+ if( p->sGraph.pLast ){
+ p->sGraph.pLast->pNext = pNew;
+ }else{
+ p->sGraph.pRow = pNew;
+ }
+ p->sGraph.pLast = pNew;
+}
+
+/*
+** Free and reset the EXPLAIN QUERY PLAN data that has been collected
+** in p->sGraph.
+*/
+static void eqp_reset(ShellState *p){
+ EQPGraphRow *pRow, *pNext;
+ for(pRow = p->sGraph.pRow; pRow; pRow = pNext){
+ pNext = pRow->pNext;
+ sqlite3_free(pRow);
+ }
+ memset(&p->sGraph, 0, sizeof(p->sGraph));
+}
+
+/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
+** pOld, or return the first such line if pOld is NULL
+*/
+static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){
+ EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
+ while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
+ return pRow;
+}
+
+/* Render a single level of the graph that has iEqpId as its parent. Called
+** recursively to render sublevels.
+*/
+static void eqp_render_level(ShellState *p, int iEqpId){
+ EQPGraphRow *pRow, *pNext;
+ int n = strlen30(p->sGraph.zPrefix);
+ char *z;
+ for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
+ pNext = eqp_next_row(p, iEqpId, pRow);
+ z = pRow->zText;
+ utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
+ if( n<(int)sizeof(p->sGraph.zPrefix)-7 ){
+ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
+ eqp_render_level(p, pRow->iEqpId);
+ p->sGraph.zPrefix[n] = 0;
+ }
+ }
+}
+
+/*
+** Display and reset the EXPLAIN QUERY PLAN data
+*/
+static void eqp_render(ShellState *p){
+ EQPGraphRow *pRow = p->sGraph.pRow;
+ if( pRow ){
+ if( pRow->zText[0]=='-' ){
+ if( pRow->pNext==0 ){
+ eqp_reset(p);
+ return;
+ }
+ utf8_printf(p->out, "%s\n", pRow->zText+3);
+ p->sGraph.pRow = pRow->pNext;
+ sqlite3_free(pRow);
+ }else{
+ utf8_printf(p->out, "QUERY PLAN\n");
+ }
+ p->sGraph.zPrefix[0] = 0;
+ eqp_render_level(p, 0);
+ eqp_reset(p);
+ }
+}
/*
** This is the callback routine that the shell
@@ -2020,6 +2102,10 @@ static int shell_callback(
utf8_printf(p->out, "%s", p->rowSeparator);
break;
}
+ case MODE_EQP: {
+ eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
+ break;
+ }
}
return 0;
}
@@ -2118,10 +2204,7 @@ static void set_table_name(ShellState *p, const char *zName){
n = strlen30(zName);
if( cQuote ) n += n+2;
z = p->zDestTable = malloc( n+1 );
- if( z==0 ){
- raw_printf(stderr,"Error: out of memory\n");
- exit(1);
- }
+ if( z==0 ) shell_out_of_memory();
n = 0;
if( cQuote ) z[n++] = cQuote;
for(i=0; zName[i]; i++){
@@ -2861,11 +2944,13 @@ static int shell_exec(
rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
if( rc==SQLITE_OK ){
while( sqlite3_step(pExplain)==SQLITE_ROW ){
- raw_printf(pArg->out,"--EQP-- %d,",sqlite3_column_int(pExplain, 0));
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1));
- raw_printf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2));
- utf8_printf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3));
+ const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
+ int iEqpId = sqlite3_column_int(pExplain, 0);
+ int iParentId = sqlite3_column_int(pExplain, 1);
+ if( zEQPLine[0]=='-' ) eqp_render(pArg);
+ eqp_append(pArg, iEqpId, iParentId, zEQPLine);
}
+ eqp_render(pArg);
}
sqlite3_finalize(pExplain);
sqlite3_free(zEQP);
@@ -2893,11 +2978,16 @@ static int shell_exec(
if( pArg ){
pArg->cMode = pArg->mode;
- if( pArg->autoExplain
- && sqlite3_column_count(pStmt)==8
- && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
- ){
- pArg->cMode = MODE_Explain;
+ if( pArg->autoExplain ){
+ if( sqlite3_column_count(pStmt)==8
+ && sqlite3_strlike("EXPLAIN%", zStmtSql,0)==0
+ ){
+ pArg->cMode = MODE_Explain;
+ }
+ if( sqlite3_column_count(pStmt)==4
+ && sqlite3_strlike("EXPLAIN QUERY PLAN%", zStmtSql,0)==0 ){
+ pArg->cMode = MODE_EQP;
+ }
}
/* If the shell is currently in ".explain" mode, gather the extra
@@ -2909,6 +2999,7 @@ static int shell_exec(
exec_prepared_stmt(pArg, pStmt);
explain_data_delete(pArg);
+ eqp_render(pArg);
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
@@ -2986,10 +3077,7 @@ static char **tableColumnList(ShellState *p, const char *zTab){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
- if( azCol==0 ){
- raw_printf(stderr, "Error: out of memory\n");
- exit(1);
- }
+ if( azCol==0 ) shell_out_of_memory();
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
if( sqlite3_column_int(pStmt, 5) ){
@@ -3235,6 +3323,7 @@ static char zHelp[] =
".auth ON|OFF Show authorizer callbacks\n"
#endif
".backup ?DB? FILE Backup DB (default \"main\") to FILE\n"
+ " Add \"--append\" to open using appendvfs.\n"
".bail on|off Stop after hitting an error. Default OFF\n"
".binary on|off Turn binary output on or off. Default OFF\n"
".cd DIRECTORY Change the working directory to DIRECTORY\n"
@@ -3242,6 +3331,7 @@ static char zHelp[] =
".check GLOB Fail if output since .testcase does not match\n"
".clone NEWDB Clone data into NEWDB from the existing database\n"
".databases List names and files of attached databases\n"
+ ".dbconfig ?op? ?val? List or change sqlite3_db_config() options\n"
".dbinfo ?DB? Show status information about the database\n"
".dump ?TABLE? ... Dump the database in an SQL text format\n"
" If TABLE specified, only dump tables matching\n"
@@ -3451,14 +3541,17 @@ static int session_filter(void *pCtx, const char *zTab){
** Otherwise, assume an ordinary database regardless of the filename if
** the type cannot be determined from content.
*/
-static int deduceDatabaseType(const char *zName, int dfltZip){
+int deduceDatabaseType(const char *zName, int dfltZip){
FILE *f = fopen(zName, "rb");
size_t n;
int rc = SHELL_OPEN_UNSPEC;
char zBuf[100];
if( f==0 ){
- if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ) return SHELL_OPEN_ZIPFILE;
- return SHELL_OPEN_NORMAL;
+ if( dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
+ return SHELL_OPEN_ZIPFILE;
+ }else{
+ return SHELL_OPEN_NORMAL;
+ }
}
fseek(f, -25, SEEK_END);
n = fread(zBuf, 25, 1, f);
@@ -3471,22 +3564,39 @@ static int deduceDatabaseType(const char *zName, int dfltZip){
&& zBuf[3]==0x06 ){
rc = SHELL_OPEN_ZIPFILE;
}else if( n==0 && dfltZip && sqlite3_strlike("%.zip",zName,0)==0 ){
- return SHELL_OPEN_ZIPFILE;
+ rc = SHELL_OPEN_ZIPFILE;
}
}
fclose(f);
return rc;
}
+/* Flags for open_db().
+**
+** The default behavior of open_db() is to exit(1) if the database fails to
+** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
+** but still returns without calling exit.
+**
+** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
+** ZIP archive if the file does not exist or is empty and its name matches
+** the *.zip pattern.
+*/
+#define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */
+#define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */
+
/*
** 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(ShellState *p, int keepAlive){
+static void open_db(ShellState *p, int openFlags){
if( p->db==0 ){
- sqlite3_initialize();
- if( p->openMode==SHELL_OPEN_UNSPEC && access(p->zDbFilename,0)==0 ){
- p->openMode = (u8)deduceDatabaseType(p->zDbFilename, 0);
+ if( p->openMode==SHELL_OPEN_UNSPEC ){
+ if( p->zDbFilename==0 || p->zDbFilename[0]==0 ){
+ p->openMode = SHELL_OPEN_NORMAL;
+ }else{
+ p->openMode = (u8)deduceDatabaseType(p->zDbFilename,
+ (openFlags & OPEN_DB_ZIPFILE)!=0);
+ }
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
@@ -3512,7 +3622,7 @@ static void open_db(ShellState *p, int keepAlive){
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(p->db));
- if( keepAlive ) return;
+ if( openFlags & OPEN_DB_KEEPALIVE ) return;
exit(1);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
@@ -3546,6 +3656,17 @@ static void open_db(ShellState *p, int keepAlive){
}
}
+/*
+** Attempt to close the databaes connection. Report errors.
+*/
+void close_db(sqlite3 *db){
+ int rc = sqlite3_close(db);
+ if( rc ){
+ utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n",
+ rc, sqlite3_errmsg(db));
+ }
+}
+
#if HAVE_READLINE || HAVE_EDITLINE
/*
** Readline completion callbacks
@@ -3587,7 +3708,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){
char zBuf[1000];
if( nLine>sizeof(zBuf)-30 ) return;
- if( zLine[0]=='.' ) return;
+ if( zLine[0]=='.' || zLine[0]=='#') return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
@@ -3787,10 +3908,7 @@ static void import_append_char(ImportCtx *p, int c){
if( p->n+1>=p->nAlloc ){
p->nAlloc += p->nAlloc + 100;
p->z = sqlite3_realloc64(p->z, p->nAlloc);
- if( p->z==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( p->z==0 ) shell_out_of_memory();
}
p->z[p->n++] = (char)c;
}
@@ -3951,10 +4069,7 @@ static void tryToCloneData(
}
n = sqlite3_column_count(pQuery);
zInsert = sqlite3_malloc64(200 + nTable + n*3);
- if( zInsert==0 ){
- raw_printf(stderr, "out of memory\n");
- goto end_data_xfer;
- }
+ if( zInsert==0 ) shell_out_of_memory();
sqlite3_snprintf(200+nTable,zInsert,
"INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable);
i = strlen30(zInsert);
@@ -4132,7 +4247,7 @@ static void tryToClone(ShellState *p, const char *zNewDb){
sqlite3_exec(newDb, "COMMIT;", 0, 0, 0);
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
}
- sqlite3_close(newDb);
+ close_db(newDb);
}
/*
@@ -4296,14 +4411,6 @@ static int shellDatabaseError(sqlite3 *db){
}
/*
-** Print an out-of-memory message to stderr and return 1.
-*/
-static int shellNomemError(void){
- raw_printf(stderr, "Error: out of memory\n");
- return 1;
-}
-
-/*
** Compare the pattern in zGlob[] against the text in z[]. Return TRUE
** if they match and FALSE (0) if they do not match.
**
@@ -4795,6 +4902,7 @@ struct ArCommand {
u8 bZip; /* True if the archive is a ZIP */
u8 bDryRun; /* True if --dry-run */
u8 bAppend; /* True if --append */
+ u8 fromCmdLine; /* Run from -A instead of .archive */
int nArg; /* Number of command arguments */
char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */
const char *zFile; /* --file argument, or NULL */
@@ -4841,13 +4949,18 @@ static int arUsage(FILE *f){
** Print an error message for the .ar command to stderr and return
** SQLITE_ERROR.
*/
-static int arErrorMsg(const char *zFmt, ...){
+static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
va_list ap;
char *z;
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
- raw_printf(stderr, "Error: %s (try \".ar --help\")\n", z);
+ utf8_printf(stderr, "Error: %s\n", z);
+ if( pAr->fromCmdLine ){
+ utf8_printf(stderr, "Use \"-A\" for more help\n");
+ }else{
+ utf8_printf(stderr, "Use \".archive --help\" for more help\n");
+ }
sqlite3_free(z);
return SQLITE_ERROR;
}
@@ -4878,7 +4991,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
case AR_CMD_UPDATE:
case AR_CMD_HELP:
if( pAr->eCmd ){
- return arErrorMsg("multiple command options");
+ return arErrorMsg(pAr, "multiple command options");
}
pAr->eCmd = eSwitch;
break;
@@ -4938,8 +5051,6 @@ static int arParseCommand(
return arUsage(stderr);
}else{
char *z = azArg[1];
- memset(pAr, 0, sizeof(ArCommand));
-
if( z[0]!='-' ){
/* Traditional style [tar] invocation */
int i;
@@ -4951,11 +5062,11 @@ static int arParseCommand(
if( z[i]==pOpt->cShort ) break;
}
if( pOpt==pEnd ){
- return arErrorMsg("unrecognized option: %c", z[i]);
+ return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
}
if( pOpt->bArg ){
if( iArg>=nArg ){
- return arErrorMsg("option requires an argument: %c",z[i]);
+ return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
}
zArg = azArg[iArg++];
}
@@ -4989,7 +5100,7 @@ static int arParseCommand(
if( z[i]==pOpt->cShort ) break;
}
if( pOpt==pEnd ){
- return arErrorMsg("unrecognized option: %c\n", z[i]);
+ return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
}
if( pOpt->bArg ){
if( i<(n-1) ){
@@ -4997,7 +5108,7 @@ static int arParseCommand(
i = n;
}else{
if( iArg>=(nArg-1) ){
- return arErrorMsg("option requires an argument: %c\n",z[i]);
+ return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
}
zArg = azArg[++iArg];
}
@@ -5019,7 +5130,7 @@ static int arParseCommand(
const char *zLong = pOpt->zLong;
if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
if( pMatch ){
- return arErrorMsg("ambiguous option: %s",z);
+ return arErrorMsg(pAr, "ambiguous option: %s",z);
}else{
pMatch = pOpt;
}
@@ -5027,11 +5138,11 @@ static int arParseCommand(
}
if( pMatch==0 ){
- return arErrorMsg("unrecognized option: %s", z);
+ return arErrorMsg(pAr, "unrecognized option: %s", z);
}
if( pMatch->bArg ){
if( iArg>=(nArg-1) ){
- return arErrorMsg("option requires an argument: %s", z);
+ return arErrorMsg(pAr, "option requires an argument: %s", z);
}
zArg = azArg[++iArg];
}
@@ -5160,6 +5271,7 @@ static int arListCommand(ArCommand *pAr){
}
}
shellFinalize(&rc, pSql);
+ sqlite3_free(zWhere);
return rc;
}
@@ -5362,12 +5474,14 @@ end_ar_transaction:
*/
static int arDotCommand(
ShellState *pState, /* Current shell tool state */
+ int fromCmdLine, /* True if -A command-line option, not .ar cmd */
char **azArg, /* Array of arguments passed to dot command */
int nArg /* Number of entries in azArg[] */
){
ArCommand cmd;
int rc;
memset(&cmd, 0, sizeof(cmd));
+ cmd.fromCmdLine = fromCmdLine;
rc = arParseCommand(azArg, nArg, &cmd);
if( rc==SQLITE_OK ){
int eDbType = SHELL_OPEN_UNSPEC;
@@ -5414,7 +5528,7 @@ static int arDotCommand(
shellPutsFunc, 0, 0);
}
- if( cmd.zSrcTable==0 && cmd.bZip==0 ){
+ if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
if( cmd.eCmd!=AR_CMD_CREATE
&& sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
){
@@ -5450,7 +5564,7 @@ static int arDotCommand(
}
end_ar_command:
if( cmd.db!=pState->db ){
- sqlite3_close(cmd.db);
+ close_db(cmd.db);
}
sqlite3_free(cmd.zSrcTable);
@@ -5530,7 +5644,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
if( c=='a' && strncmp(azArg[0], "archive", n)==0 ){
open_db(p, 0);
- rc = arDotCommand(p, azArg, nArg);
+ rc = arDotCommand(p, 0, azArg, nArg);
}else
#endif
@@ -5542,11 +5656,14 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
+ const char *zVfs = 0;
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
- while( z[0]=='-' ) z++;
- /* No options to process at this time */
+ if( z[1]=='-' ) z++;
+ if( strcmp(z, "-append")==0 ){
+ zVfs = "apndvfs";
+ }else
{
utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
return 1;
@@ -5557,7 +5674,7 @@ static int do_meta_command(char *zLine, ShellState *p){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
- raw_printf(stderr, "too many arguments to .backup\n");
+ raw_printf(stderr, "Usage: .backup ?DB? ?--append? FILENAME\n");
return 1;
}
}
@@ -5566,17 +5683,18 @@ static int do_meta_command(char *zLine, ShellState *p){
return 1;
}
if( zDb==0 ) zDb = "main";
- rc = sqlite3_open(zDestFile, &pDest);
+ rc = sqlite3_open_v2(zDestFile, &pDest,
+ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
- sqlite3_close(pDest);
+ close_db(pDest);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
- sqlite3_close(pDest);
+ close_db(pDest);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){}
@@ -5587,7 +5705,7 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
rc = 1;
}
- sqlite3_close(pDest);
+ close_db(pDest);
}else
if( c=='b' && n>=3 && strncmp(azArg[0], "bail", n)==0 ){
@@ -5699,7 +5817,35 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else
- if( c=='d' && strncmp(azArg[0], "dbinfo", n)==0 ){
+ if( c=='d' && n>=3 && strncmp(azArg[0], "dbconfig", n)==0 ){
+ static const struct DbConfigChoices {const char *zName; int op;} aDbConfig[] = {
+ { "enable_fkey", SQLITE_DBCONFIG_ENABLE_FKEY },
+ { "enable_trigger", SQLITE_DBCONFIG_ENABLE_TRIGGER },
+ { "fts3_tokenizer", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
+ { "load_extension", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
+ { "no_ckpt_on_close", SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
+ { "enable_qpsg", SQLITE_DBCONFIG_ENABLE_QPSG },
+ { "trigger_eqp", SQLITE_DBCONFIG_TRIGGER_EQP },
+ { "reset_database", SQLITE_DBCONFIG_RESET_DATABASE },
+ };
+ int ii, v;
+ open_db(p, 0);
+ for(ii=0; ii<ArraySize(aDbConfig); ii++){
+ if( nArg>1 && strcmp(azArg[1], aDbConfig[ii].zName)!=0 ) continue;
+ if( nArg>=3 ){
+ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
+ }
+ sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
+ utf8_printf(p->out, "%18s %s\n", aDbConfig[ii].zName, v ? "on" : "off");
+ if( nArg>1 ) break;
+ }
+ if( nArg>1 && ii==ArraySize(aDbConfig) ){
+ utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]);
+ utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n");
+ }
+ }else
+
+ if( c=='d' && n>=3 && strncmp(azArg[0], "dbinfo", n)==0 ){
rc = shell_dbinfo_command(p, nArg, azArg);
}else
@@ -5802,10 +5948,14 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
+ p->autoEQPtest = 0;
if( strcmp(azArg[1],"full")==0 ){
p->autoEQP = AUTOEQP_full;
}else if( strcmp(azArg[1],"trigger")==0 ){
p->autoEQP = AUTOEQP_trigger;
+ }else if( strcmp(azArg[1],"test")==0 ){
+ p->autoEQP = AUTOEQP_on;
+ p->autoEQPtest = 1;
}else{
p->autoEQP = (u8)booleanValue(azArg[1]);
}
@@ -5894,11 +6044,11 @@ static int do_meta_command(char *zLine, ShellState *p){
callback, &data, &zErrMsg);
data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
- shell_exec(p, "SELECT * FROM sqlite_stat1", &zErrMsg);
+ shell_exec(&data, "SELECT * FROM sqlite_stat1", &zErrMsg);
data.zDestTable = "sqlite_stat3";
- shell_exec(p, "SELECT * FROM sqlite_stat3", &zErrMsg);
+ shell_exec(&data, "SELECT * FROM sqlite_stat3", &zErrMsg);
data.zDestTable = "sqlite_stat4";
- shell_exec(p, "SELECT * FROM sqlite_stat4", &zErrMsg);
+ shell_exec(&data, "SELECT * FROM sqlite_stat4", &zErrMsg);
raw_printf(p->out, "ANALYZE sqlite_master;\n");
}
}else
@@ -5996,9 +6146,8 @@ static int do_meta_command(char *zLine, ShellState *p){
sCtx.cRowSep = p->rowSeparator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
xCloser(sCtx.in);
- return 1;
+ shell_out_of_memory();
}
nByte = strlen30(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
@@ -6043,9 +6192,8 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCol==0 ) return 0; /* no columns, no error */
zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
xCloser(sCtx.in);
- return 1;
+ shell_out_of_memory();
}
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
j = strlen30(zSql);
@@ -6121,12 +6269,17 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_stmt *pStmt;
int tnum = 0;
int i;
- if( nArg!=3 ){
- utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n");
+ if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
+ utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
+ " .imposter off\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
+ if( nArg==2 ){
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 1);
+ goto meta_command_exit;
+ }
zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master"
" WHERE name='%q' AND type='index'", azArg[1]);
sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
@@ -6368,7 +6521,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int newFlag = 0; /* True to delete file before opening */
/* Close the existing database */
session_close_all(p);
- sqlite3_close(p->db);
+ close_db(p->db);
p->db = 0;
p->zDbFilename = 0;
sqlite3_free(p->zFreeOnClose);
@@ -6398,7 +6551,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zNewFilename ){
if( newFlag ) shellDeleteFile(zNewFilename);
p->zDbFilename = zNewFilename;
- open_db(p, 1);
+ open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
@@ -6548,14 +6701,14 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
- sqlite3_close(pSrc);
+ close_db(pSrc);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
- sqlite3_close(pSrc);
+ close_db(pSrc);
return 1;
}
while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
@@ -6575,7 +6728,7 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
rc = 1;
}
- sqlite3_close(pSrc);
+ close_db(pSrc);
}else
if( c=='s' && strncmp(azArg[0], "scanstats", n)==0 ){
@@ -7095,7 +7248,7 @@ static int do_meta_command(char *zLine, ShellState *p){
utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n",
azArg[i], azArg[0]);
raw_printf(stderr, "Should be one of: --schema"
- " --sha3-224 --sha3-255 --sha3-384 --sha3-512\n");
+ " --sha3-224 --sha3-256 --sha3-384 --sha3-512\n");
rc = 1;
goto meta_command_exit;
}
@@ -7259,7 +7412,10 @@ static int do_meta_command(char *zLine, ShellState *p){
initText(&s);
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
- if( rc ) return shellDatabaseError(p->db);
+ if( rc ){
+ sqlite3_finalize(pStmt);
+ return shellDatabaseError(p->db);
+ }
if( nArg>2 && c=='i' ){
/* It is an historical accident that the .indexes command shows an error
@@ -7267,6 +7423,7 @@ static int do_meta_command(char *zLine, ShellState *p){
** command does not. */
raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
rc = 1;
+ sqlite3_finalize(pStmt);
goto meta_command_exit;
}
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
@@ -7311,18 +7468,12 @@ static int do_meta_command(char *zLine, ShellState *p){
char **azNew;
int n2 = nAlloc*2 + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
- if( azNew==0 ){
- rc = shellNomemError();
- break;
- }
+ if( azNew==0 ) shell_out_of_memory();
nAlloc = n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
- if( 0==azResult[nRow] ){
- rc = shellNomemError();
- break;
- }
+ if( 0==azResult[nRow] ) shell_out_of_memory();
nRow++;
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
@@ -7383,9 +7534,6 @@ static int do_meta_command(char *zLine, ShellState *p){
{ "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" },
/*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" }, */
{ "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"},
-#ifdef SQLITE_N_KEYWORD
- { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD, "IDENTIFIER" },
-#endif
{ "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" },
{ "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" },
{ "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" },
@@ -7497,17 +7645,6 @@ static int do_meta_command(char *zLine, ShellState *p){
}
break;
- /* sqlite3_test_control(int, char *) */
-#ifdef SQLITE_N_KEYWORD
- case SQLITE_TESTCTRL_ISKEYWORD:
- if( nArg==3 ){
- const char *opt = azArg[2];
- rc2 = sqlite3_test_control(testctrl, opt);
- isOk = 1;
- }
- break;
-#endif
-
case SQLITE_TESTCTRL_IMPOSTER:
if( nArg==5 ){
rc2 = sqlite3_test_control(testctrl, p->db,
@@ -7803,7 +7940,7 @@ static int line_is_complete(char *zSql, int nSql){
}
/*
-** Run a single line of SQL
+** Run a single line of SQL. Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
@@ -7876,13 +8013,15 @@ static int process_input(ShellState *p, FILE *in){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
continue;
}
- if( zLine && zLine[0]=='.' && nSql==0 ){
+ if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
- rc = do_meta_command(zLine, p);
- if( rc==2 ){ /* exit requested */
- break;
- }else if( rc ){
- errCnt++;
+ if( zLine[0]=='.' ){
+ rc = do_meta_command(zLine, p);
+ if( rc==2 ){ /* exit requested */
+ break;
+ }else if( rc ){
+ errCnt++;
+ }
}
continue;
}
@@ -7893,10 +8032,7 @@ static int process_input(ShellState *p, FILE *in){
if( nSql+nLine+2>=nAlloc ){
nAlloc = nSql+nLine+100;
zSql = realloc(zSql, nAlloc);
- if( zSql==0 ){
- raw_printf(stderr, "Error: out of memory\n");
- exit(1);
- }
+ if( zSql==0 ) shell_out_of_memory();
}
nSqlPrior = nSql;
if( nSql==0 ){
@@ -7927,7 +8063,7 @@ static int process_input(ShellState *p, FILE *in){
}
}
if( nSql && !_all_whitespace(zSql) ){
- runOneSqlLine(p, zSql, in, startline);
+ errCnt += runOneSqlLine(p, zSql, in, startline);
}
free(zSql);
free(zLine);
@@ -8025,7 +8161,6 @@ static void process_sqliterc(
" cannot read ~/.sqliterc\n");
return;
}
- sqlite3_initialize();
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
sqliterc = zBuf;
}
@@ -8076,6 +8211,9 @@ static const char zOptions[] =
" -quote set output mode to 'quote'\n"
" -readonly open the database read-only\n"
" -separator SEP set output column separator. Default: '|'\n"
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ " -sorterref SIZE sorter references threshold size\n"
+#endif
" -stats print memory stats before each finalize\n"
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
@@ -8100,6 +8238,17 @@ static void usage(int showDetail){
}
/*
+** Internal check: Verify that the SQLite is uninitialized. Print a
+** error message if it is initialized.
+*/
+static void verify_uninitialized(void){
+ if( sqlite3_config(-1)==SQLITE_MISUSE ){
+ utf8_printf(stdout, "WARNING: attempt to configure SQLite after"
+ " initialization.\n");
+ }
+}
+
+/*
** Initialize the state information in data
*/
static void main_init(ShellState *data) {
@@ -8110,6 +8259,7 @@ static void main_init(ShellState *data) {
memcpy(data->rowSeparator,SEP_Row, 2);
data->showHeader = 0;
data->shellFlgs = SHFLG_Lookaside;
+ verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_URI, 1);
sqlite3_config(SQLITE_CONFIG_LOG, shellLog, data);
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
@@ -8173,6 +8323,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
int readStdin = 1;
int nCmd = 0;
char **azCmd = 0;
+ const char *zVfs = 0; /* Value of -vfs command-line option */
+#if !SQLITE_SHELL_IS_UTF8
+ char **argvToFree = 0;
+ int argcToFree = 0;
+#endif
setBinaryMode(stdin, 0);
setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
@@ -8196,25 +8351,19 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
*/
#if !SQLITE_SHELL_IS_UTF8
sqlite3_initialize();
- argv = malloc(sizeof(argv[0])*argc);
- if( argv==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ argvToFree = malloc(sizeof(argv[0])*argc*2);
+ argcToFree = argc;
+ argv = argvToFree + argc;
+ if( argv==0 ) shell_out_of_memory();
for(i=0; i<argc; i++){
char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
int n;
- if( z==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( z==0 ) shell_out_of_memory();
n = (int)strlen(z);
argv[i] = malloc( n+1 );
- if( argv[i]==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( argv[i]==0 ) shell_out_of_memory();
memcpy(argv[i], z, n+1);
+ argvToFree[i] = argv[i];
sqlite3_free(z);
}
sqlite3_shutdown();
@@ -8249,6 +8398,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
** the size of the alternative malloc heap,
** and the first command to execute.
*/
+ verify_uninitialized();
for(i=1; i<argc; i++){
char *z;
z = argv[i];
@@ -8261,10 +8411,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
readStdin = 0;
nCmd++;
azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
- if( azCmd==0 ){
- raw_printf(stderr, "out of memory\n");
- exit(1);
- }
+ if( azCmd==0 ) shell_out_of_memory();
azCmd[nCmd-1] = z;
}
}
@@ -8331,14 +8478,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( strcmp(z,"-mmap")==0 ){
sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ }else if( strcmp(z,"-sorterref")==0 ){
+ sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
+ sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
+#endif
}else if( strcmp(z,"-vfs")==0 ){
- sqlite3_vfs *pVfs = sqlite3_vfs_find(cmdline_option_value(argc,argv,++i));
- if( pVfs ){
- sqlite3_vfs_register(pVfs, 1);
- }else{
- utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
- exit(1);
- }
+ zVfs = cmdline_option_value(argc, argv, ++i);
#ifdef SQLITE_HAVE_ZLIB
}else if( strcmp(z,"-zip")==0 ){
data.openMode = SHELL_OPEN_ZIPFILE;
@@ -8355,6 +8501,34 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#endif
}
}
+ verify_uninitialized();
+
+
+#ifdef SQLITE_SHELL_INIT_PROC
+ {
+ /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
+ ** of a C-function that will perform initialization actions on SQLite that
+ ** occur just before or after sqlite3_initialize(). Use this compile-time
+ ** option to embed this shell program in larger applications. */
+ extern void SQLITE_SHELL_INIT_PROC(void);
+ SQLITE_SHELL_INIT_PROC();
+ }
+#else
+ /* All the sqlite3_config() calls have now been made. So it is safe
+ ** to call sqlite3_initialize() and process any command line -vfs option. */
+ sqlite3_initialize();
+#endif
+
+ if( zVfs ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
+ if( pVfs ){
+ sqlite3_vfs_register(pVfs, 1);
+ }else{
+ utf8_printf(stderr, "no such VFS: \"%s\"\n", argv[i]);
+ exit(1);
+ }
+ }
+
if( data.zDbFilename==0 ){
#ifndef SQLITE_OMIT_MEMORYDB
data.zDbFilename = ":memory:";
@@ -8467,6 +8641,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
i+=2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ }else if( strcmp(z,"-sorterref")==0 ){
+ i++;
+#endif
}else if( strcmp(z,"-vfs")==0 ){
i++;
#ifdef SQLITE_ENABLE_VFSTRACE
@@ -8507,12 +8685,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
" with \"%s\"\n", z);
return 1;
}
- open_db(&data, 0);
+ open_db(&data, OPEN_DB_ZIPFILE);
if( z[2] ){
argv[i] = &z[2];
- arDotCommand(&data, argv+(i-1), argc-(i-1));
+ arDotCommand(&data, 1, argv+(i-1), argc-(i-1));
}else{
- arDotCommand(&data, argv+i, argc-i);
+ arDotCommand(&data, 1, argv+i, argc-i);
}
readStdin = 0;
break;
@@ -8591,7 +8769,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
set_table_name(&data, 0);
if( data.db ){
session_close_all(&data);
- sqlite3_close(data.db);
+ close_db(data.db);
}
sqlite3_free(data.zFreeOnClose);
find_home_dir(1);
@@ -8599,8 +8777,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
data.doXdgOpen = 0;
clearTempFile(&data);
#if !SQLITE_SHELL_IS_UTF8
- for(i=0; i<argc; i++) free(argv[i]);
- free(argv);
+ for(i=0; i<argcToFree; i++) free(argvToFree[i]);
+ free(argvToFree);
#endif
+ /* Clear the global data structure so that valgrind will detect memory
+ ** leaks */
+ memset(&data, 0, sizeof(data));
return rc;
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 202155d..3d2effa 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -504,6 +504,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
+#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<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))
@@ -511,6 +512,7 @@ int sqlite3_exec(
#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_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<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))
@@ -1930,6 +1932,22 @@ struct sqlite3_mem_methods {
** I/O required to support statement rollback.
** The default value for this setting is controlled by the
** [SQLITE_STMTJRNL_SPILL] compile-time option.
+**
+** [[SQLITE_CONFIG_SORTERREF_SIZE]]
+** <dt>SQLITE_CONFIG_SORTERREF_SIZE
+** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter
+** of type (int) - the new value of the sorter-reference size threshold.
+** Usually, when SQLite uses an external sort to order records according
+** to an ORDER BY clause, all fields required by the caller are present in the
+** sorted records. However, if SQLite determines based on the declared type
+** of a table column that its values are likely to be very large - larger
+** than the configured sorter-reference size threshold - then a reference
+** is stored in each sorted record and the required column values loaded
+** from the database as records are returned in sorted order. The default
+** value for this option is to never use this optimization. Specifying a
+** negative value for this option restores the default behaviour.
+** This option is only available if SQLite is compiled with the
+** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1959,6 +1977,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
+#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2095,6 +2114,21 @@ struct sqlite3_mem_methods {
** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
** it is not disabled, 1 if it is.
** </dd>
+**
+** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
+** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
+** [VACUUM] in order to reset a database back to an empty database
+** with no schema and no content. The following process works even for
+** a badly corrupted database file:
+** <ol>
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
+** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
+** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
+** </ol>
+** Because resetting a database is destructive and irreversible, the
+** process requires the use of this obscure API and multiple steps to help
+** ensure that it does not happen by accident.
+** </dd>
** </dl>
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@@ -2106,7 +2140,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1008 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -5493,6 +5528,41 @@ SQLITE_EXTERN char *sqlite3_temp_directory;
SQLITE_EXTERN char *sqlite3_data_directory;
/*
+** CAPI3REF: Win32 Specific Interface
+**
+** These interfaces are available only on Windows. The
+** [sqlite3_win32_set_directory] interface is used to set the value associated
+** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to
+** zValue, depending on the value of the type parameter. The zValue parameter
+** should be NULL to cause the previous value to be freed via [sqlite3_free];
+** a non-NULL value will be copied into memory obtained from [sqlite3_malloc]
+** prior to being used. The [sqlite3_win32_set_directory] interface returns
+** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported,
+** or [SQLITE_NOMEM] if memory could not be allocated. The value of the
+** [sqlite3_data_directory] variable is intended to act as a replacement for
+** the current directory on the sub-platforms of Win32 where that concept is
+** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and
+** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the
+** sqlite3_win32_set_directory interface except the string parameter must be
+** UTF-8 or UTF-16, respectively.
+*/
+int sqlite3_win32_set_directory(
+ unsigned long type, /* Identifier for directory being set or reset */
+ void *zValue /* New value for directory being set or reset */
+);
+int sqlite3_win32_set_directory8(unsigned long type, const char *zValue);
+int sqlite3_win32_set_directory16(unsigned long type, const void *zValue);
+
+/*
+** CAPI3REF: Win32 Directory Types
+**
+** These macros are only available on Windows. They define the allowed values
+** for the type argument to the [sqlite3_win32_set_directory] interface.
+*/
+#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1
+#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2
+
+/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
** METHOD: sqlite3
@@ -6224,6 +6294,10 @@ struct sqlite3_index_info {
/*
** CAPI3REF: Virtual Table Scan Flags
+**
+** Virtual table implementations are allowed to set the
+** [sqlite3_index_info].idxFlags field to some combination of
+** these bits.
*/
#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
@@ -6999,7 +7073,7 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
-#define SQLITE_TESTCTRL_ISKEYWORD 16
+#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
@@ -7014,6 +7088,189 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
/*
+** CAPI3REF: SQL Keyword Checking
+**
+** These routines provide access to the set of SQL language keywords
+** recognized by SQLite. Applications can uses these routines to determine
+** whether or not a specific identifier needs to be escaped (for example,
+** by enclosing in double-quotes) so as not to confuse the parser.
+**
+** The sqlite3_keyword_count() interface returns the number of distinct
+** keywords understood by SQLite.
+**
+** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
+** makes *Z point to that keyword expressed as UTF8 and writes the number
+** of bytes in the keyword into *L. The string that *Z points to is not
+** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
+** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z
+** or L are NULL or invalid pointers then calls to
+** sqlite3_keyword_name(N,Z,L) result in undefined behavior.
+**
+** The sqlite3_keyword_check(Z,L) interface checks to see whether or not
+** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero
+** if it is and zero if not.
+**
+** The parser used by SQLite is forgiving. It is often possible to use
+** a keyword as an identifier as long as such use does not result in a
+** parsing ambiguity. For example, the statement
+** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and
+** creates a new table named "BEGIN" with three columns named
+** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid
+** using keywords as identifiers. Common techniques used to avoid keyword
+** name collisions include:
+** <ul>
+** <li> Put all identifier names inside double-quotes. This is the official
+** SQL way to escape identifier names.
+** <li> Put identifier names inside &#91;...&#93;. This is not standard SQL,
+** but it is what SQL Server does and so lots of programmers use this
+** technique.
+** <li> Begin every identifier with the letter "Z" as no SQL keywords start
+** with "Z".
+** <li> Include a digit somewhere in every identifier name.
+** </ul>
+**
+** Note that the number of keywords understood by SQLite can depend on
+** compile-time options. For example, "VACUUM" is not a keyword if
+** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also,
+** new keywords may be added to future releases of SQLite.
+*/
+int sqlite3_keyword_count(void);
+int sqlite3_keyword_name(int,const char**,int*);
+int sqlite3_keyword_check(const char*,int);
+
+/*
+** CAPI3REF: Dynamic String Object
+** KEYWORDS: {dynamic string}
+**
+** An instance of the sqlite3_str object contains a dynamically-sized
+** string under construction.
+**
+** The lifecycle of an sqlite3_str object is as follows:
+** <ol>
+** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
+** <li> ^Text is appended to the sqlite3_str object using various
+** methods, such as [sqlite3_str_appendf()].
+** <li> ^The sqlite3_str object is destroyed and the string it created
+** is returned using the [sqlite3_str_finish()] interface.
+** </ol>
+*/
+typedef struct sqlite3_str sqlite3_str;
+
+/*
+** CAPI3REF: Create A New Dynamic String Object
+** CONSTRUCTOR: sqlite3_str
+**
+** ^The [sqlite3_str_new(D)] interface allocates and initializes
+** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
+** [sqlite3_str_new()] must be freed by a subsequent call to
+** [sqlite3_str_finish(X)].
+**
+** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
+** valid [sqlite3_str] object, though in the event of an out-of-memory
+** error the returned object might be a special singleton that will
+** silently reject new text, always return SQLITE_NOMEM from
+** [sqlite3_str_errcode()], always return 0 for
+** [sqlite3_str_length()], and always return NULL from
+** [sqlite3_str_finish(X)]. It is always safe to use the value
+** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
+** to any of the other [sqlite3_str] methods.
+**
+** The D parameter to [sqlite3_str_new(D)] may be NULL. If the
+** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum
+** length of the string contained in the [sqlite3_str] object will be
+** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead
+** of [SQLITE_MAX_LENGTH].
+*/
+sqlite3_str *sqlite3_str_new(sqlite3*);
+
+/*
+** CAPI3REF: Finalize A Dynamic String
+** DESTRUCTOR: sqlite3_str
+**
+** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X
+** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()]
+** that contains the constructed string. The calling application should
+** pass the returned value to [sqlite3_free()] to avoid a memory leak.
+** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
+** errors were encountered during construction of the string. ^The
+** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
+** string in [sqlite3_str] object X is zero bytes long.
+*/
+char *sqlite3_str_finish(sqlite3_str*);
+
+/*
+** CAPI3REF: Add Content To A Dynamic String
+** METHOD: sqlite3_str
+**
+** These interfaces add content to an sqlite3_str object previously obtained
+** from [sqlite3_str_new()].
+**
+** ^The [sqlite3_str_appendf(X,F,...)] and
+** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
+** functionality of SQLite to append formatted text onto the end of
+** [sqlite3_str] object X.
+**
+** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
+** onto the end of the [sqlite3_str] object X. N must be non-negative.
+** S must contain at least N non-zero bytes of content. To append a
+** zero-terminated string in its entirety, use the [sqlite3_str_appendall()]
+** method instead.
+**
+** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of
+** zero-terminated string S onto the end of [sqlite3_str] object X.
+**
+** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the
+** single-byte character C onto the end of [sqlite3_str] object X.
+** ^This method can be used, for example, to add whitespace indentation.
+**
+** ^The [sqlite3_str_reset(X)] method resets the string under construction
+** inside [sqlite3_str] object X back to zero bytes in length.
+**
+** These methods do not return a result code. ^If an error occurs, that fact
+** is recorded in the [sqlite3_str] object and can be recovered by a
+** subsequent call to [sqlite3_str_errcode(X)].
+*/
+void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...);
+void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list);
+void sqlite3_str_append(sqlite3_str*, const char *zIn, int N);
+void sqlite3_str_appendall(sqlite3_str*, const char *zIn);
+void sqlite3_str_appendchar(sqlite3_str*, int N, char C);
+void sqlite3_str_reset(sqlite3_str*);
+
+/*
+** CAPI3REF: Status Of A Dynamic String
+** METHOD: sqlite3_str
+**
+** These interfaces return the current status of an [sqlite3_str] object.
+**
+** ^If any prior errors have occurred while constructing the dynamic string
+** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return
+** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns
+** [SQLITE_NOMEM] following any out-of-memory error, or
+** [SQLITE_TOOBIG] if the size of the dynamic string exceeds
+** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors.
+**
+** ^The [sqlite3_str_length(X)] method returns the current length, in bytes,
+** of the dynamic string under construction in [sqlite3_str] object X.
+** ^The length returned by [sqlite3_str_length(X)] does not include the
+** zero-termination byte.
+**
+** ^The [sqlite3_str_value(X)] method returns a pointer to the current
+** content of the dynamic string under construction in X. The value
+** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
+** and might be freed or altered by any subsequent method on the same
+** [sqlite3_str] object. Applications must not used the pointer returned
+** [sqlite3_str_value(X)] after any subsequent method call on the same
+** object. ^Applications may change the content of the string returned
+** by [sqlite3_str_value(X)] as long as they do not write into any bytes
+** outside the range of 0 to [sqlite3_str_length(X)] and do not read or
+** write any byte after any subsequent sqlite3_str method call.
+*/
+int sqlite3_str_errcode(sqlite3_str*);
+int sqlite3_str_length(sqlite3_str*);
+char *sqlite3_str_value(sqlite3_str*);
+
+/*
** CAPI3REF: SQLite Runtime Status
**
** ^These interfaces are used to retrieve runtime status information
@@ -8282,11 +8539,11 @@ int sqlite3_vtab_on_conflict(sqlite3 *);
** method of a [virtual table], then it returns true if and only if the
** column is being fetched as part of an UPDATE operation during which the
** column value will not change. Applications might use this to substitute
-** a lighter-weight value to return that the corresponding [xUpdate] method
-** understands as a "no-change" value.
+** a return value that is less expensive to compute and that the corresponding
+** [xUpdate] method understands as a "no-change" value.
**
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
-** the column is not changed by the UPDATE statement, they the xColumn
+** the column is not changed by the UPDATE statement, then the xColumn
** method can optionally return without setting a result, without calling
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
** In that case, [sqlite3_value_nochange(X)] will return true for the
@@ -8781,7 +9038,7 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
** values of D and S.
** The size of the database is written into *P even if the
-** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy
+** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h
index 1409370..e0cd2f4 100644
--- a/src/sqlite3ext.h
+++ b/src/sqlite3ext.h
@@ -295,6 +295,21 @@ struct sqlite3_api_routines {
int (*vtab_nochange)(sqlite3_context*);
int (*value_nochange)(sqlite3_value*);
const char *(*vtab_collation)(sqlite3_index_info*,int);
+ /* Version 3.24.0 and later */
+ int (*keyword_count)(void);
+ int (*keyword_name)(int,const char**,int*);
+ int (*keyword_check)(const char*,int);
+ sqlite3_str *(*str_new)(sqlite3*);
+ char *(*str_finish)(sqlite3_str*);
+ void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
+ void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
+ void (*str_append)(sqlite3_str*, const char *zIn, int N);
+ void (*str_appendall)(sqlite3_str*, const char *zIn);
+ void (*str_appendchar)(sqlite3_str*, int N, char C);
+ void (*str_reset)(sqlite3_str*);
+ int (*str_errcode)(sqlite3_str*);
+ int (*str_length)(sqlite3_str*);
+ char *(*str_value)(sqlite3_str*);
};
/*
@@ -565,6 +580,21 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
#define sqlite3_value_nochange sqlite3_api->value_nochange
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
+/* Version 3.24.0 and later */
+#define sqlite3_keyword_count sqlite3_api->keyword_count
+#define sqlite3_keyword_name sqlite3_api->keyword_name
+#define sqlite3_keyword_check sqlite3_api->keyword_check
+#define sqlite3_str_new sqlite3_api->str_new
+#define sqlite3_str_finish sqlite3_api->str_finish
+#define sqlite3_str_appendf sqlite3_api->str_appendf
+#define sqlite3_str_vappendf sqlite3_api->str_vappendf
+#define sqlite3_str_append sqlite3_api->str_append
+#define sqlite3_str_appendall sqlite3_api->str_appendall
+#define sqlite3_str_appendchar sqlite3_api->str_appendchar
+#define sqlite3_str_reset sqlite3_api->str_reset
+#define sqlite3_str_errcode sqlite3_api->str_errcode
+#define sqlite3_str_length sqlite3_api->str_length
+#define sqlite3_str_value sqlite3_api->str_value
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index d932ae8..91fde72 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -638,6 +638,13 @@
#endif
/*
+** Default value for the SQLITE_CONFIG_SORTERREF_SIZE option.
+*/
+#ifndef SQLITE_DEFAULT_SORTERREF_SIZE
+# define SQLITE_DEFAULT_SORTERREF_SIZE 0x7fffffff
+#endif
+
+/*
** The compile-time options SQLITE_MMAP_READWRITE and
** SQLITE_ENABLE_BATCH_ATOMIC_WRITE are not compatible with one another.
** You must choose one or the other (or neither) but not both.
@@ -1086,7 +1093,7 @@ typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
typedef struct SelectDest SelectDest;
typedef struct SrcList SrcList;
-typedef struct StrAccum StrAccum;
+typedef struct sqlite3_str StrAccum; /* Internal alias for sqlite3_str */
typedef struct Table Table;
typedef struct TableLock TableLock;
typedef struct Token Token;
@@ -1095,6 +1102,7 @@ typedef struct Trigger Trigger;
typedef struct TriggerPrg TriggerPrg;
typedef struct TriggerStep TriggerStep;
typedef struct UnpackedRecord UnpackedRecord;
+typedef struct Upsert Upsert;
typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
@@ -1354,7 +1362,7 @@ struct sqlite3 {
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
u8 mTrace; /* zero or more SQLITE_TRACE flags */
- u8 skipBtreeMutex; /* True if no shared-cache backends */
+ u8 noSharedCache; /* True if no shared-cache backends */
u8 nSqlExec; /* Number of pending OP_SqlExec opcodes */
int nextPagesize; /* Pagesize after VACUUM if >0 */
u32 magic; /* Magic number for detect library misuse */
@@ -1498,6 +1506,7 @@ struct sqlite3 {
#define SQLITE_Fts3Tokenizer 0x00400000 /* Enable fts3_tokenizer(2) */
#define SQLITE_EnableQPSG 0x00800000 /* Query Planner Stability Guarantee*/
#define SQLITE_TriggerEQP 0x01000000 /* Show trigger EXPLAIN QUERY PLAN */
+#define SQLITE_ResetDatabase 0x02000000 /* Reset the database */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
@@ -1514,6 +1523,7 @@ struct sqlite3 {
#define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */
#define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */
#define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */
+#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
@@ -1759,6 +1769,7 @@ struct Column {
#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
#define COLFLAG_UNIQUE 0x0008 /* Column def contains "UNIQUE" or "PK" */
+#define COLFLAG_SORTERREF 0x0010 /* Use sorter-refs with this column */
/*
** A "Collating Sequence" is defined by an instance of the following
@@ -2046,13 +2057,12 @@ struct FKey {
#define OE_Fail 3 /* Stop the operation but leave all prior changes */
#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */
#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */
-
-#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
-#define OE_SetNull 7 /* Set the foreign key value to NULL */
-#define OE_SetDflt 8 /* Set the foreign key value to its default */
-#define OE_Cascade 9 /* Cascade the changes */
-
-#define OE_Default 10 /* Do whatever the default action is */
+#define OE_Update 6 /* Process as a DO UPDATE in an upsert */
+#define OE_Restrict 7 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
+#define OE_SetNull 8 /* Set the foreign key value to NULL */
+#define OE_SetDflt 9 /* Set the foreign key value to its default */
+#define OE_Cascade 10 /* Cascade the changes */
+#define OE_Default 11 /* Do whatever the default action is */
/*
@@ -2499,6 +2509,7 @@ struct ExprList {
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
unsigned reusable :1; /* Constant expression is reusable */
+ unsigned bSorterRef :1; /* Defer evaluation until after sorting */
union {
struct {
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@@ -2598,9 +2609,6 @@ struct SrcList {
unsigned viaCoroutine :1; /* Implemented as a co-routine */
unsigned isRecursive :1; /* True for recursive reference in WITH */
} fg;
-#ifndef SQLITE_OMIT_EXPLAIN
- u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
-#endif
int iCursor; /* The VDBE cursor number used to access this table */
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
@@ -2682,8 +2690,11 @@ struct SrcList {
struct NameContext {
Parse *pParse; /* The parser */
SrcList *pSrcList; /* One or more tables used to resolve names */
- ExprList *pEList; /* Optional list of result-set columns */
- AggInfo *pAggInfo; /* Information about aggregates at this level */
+ union {
+ ExprList *pEList; /* Optional list of result-set columns */
+ AggInfo *pAggInfo; /* Information about aggregates at this level */
+ Upsert *pUpsert; /* ON CONFLICT clause information from an upsert */
+ } uNC;
NameContext *pNext; /* Next outer name context. NULL for outermost */
int nRef; /* Number of names resolved by this context */
int nErr; /* Number of errors encountered while resolving names */
@@ -2705,18 +2716,48 @@ struct NameContext {
#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
+#define NC_UEList 0x0080 /* True if uNC.pEList is used */
+#define NC_UAggInfo 0x0100 /* True if uNC.pAggInfo is used */
+#define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */
#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
#define NC_Complex 0x2000 /* True if a function or subquery seen */
/*
+** An instance of the following object describes a single ON CONFLICT
+** clause in an upsert.
+**
+** The pUpsertTarget field is only set if the ON CONFLICT clause includes
+** conflict-target clause. (In "ON CONFLICT(a,b)" the "(a,b)" is the
+** conflict-target clause.) The pUpsertTargetWhere is the optional
+** WHERE clause used to identify partial unique indexes.
+**
+** pUpsertSet is the list of column=expr terms of the UPDATE statement.
+** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING. The
+** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the
+** WHERE clause is omitted.
+*/
+struct Upsert {
+ ExprList *pUpsertTarget; /* Optional description of conflicting index */
+ Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
+ ExprList *pUpsertSet; /* The SET clause from an ON CONFLICT UPDATE */
+ Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
+ /* The fields above comprise the parse tree for the upsert clause.
+ ** The fields below are used to transfer information from the INSERT
+ ** processing down into the UPDATE processing while generating code.
+ ** Upsert owns the memory allocated above, but not the memory below. */
+ Index *pUpsertIdx; /* Constraint that pUpsertTarget identifies */
+ SrcList *pUpsertSrc; /* Table to be updated */
+ int regData; /* First register holding array of VALUES */
+ int iDataCur; /* Index of the data cursor */
+ int iIdxCur; /* Index of the first index cursor */
+};
+
+/*
** An instance of the following structure contains all information
** needed to generate code for a single SELECT statement.
**
-** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0.
-** If there is a LIMIT clause, the parser sets nLimit to the value of the
-** limit and nOffset to the value of the offset (or 0 if there is not
-** offset). But later on, nLimit and nOffset become the memory locations
-** in the VDBE that record the limit and offset counters.
+** See the header comment on the computeLimitRegisters() routine for a
+** detailed description of the meaning of the iLimit and iOffset fields.
**
** addrOpenEphm[] entries contain the address of OP_OpenEphemeral opcodes.
** These addresses must be stored so that we can go back and fill in
@@ -2776,8 +2817,7 @@ struct Select {
#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
-#define SF_ComplexResult 0x40000 /* Result set contains subquery or function */
-
+#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -3047,8 +3087,7 @@ struct Parse {
#endif
int nHeight; /* Expression tree height of current sub-select */
#ifndef SQLITE_OMIT_EXPLAIN
- int iSelectId; /* ID of current select for EXPLAIN output */
- int iNextSelectId; /* Next available select ID for EXPLAIN output */
+ int addrExplain; /* Address of current OP_Explain opcode */
#endif
VList *pVList; /* Mapping between variable names and numbers */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
@@ -3207,8 +3246,9 @@ struct TriggerStep {
Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE. */
+ ExprList *pExprList; /* SET clause for UPDATE */
IdList *pIdList; /* Column names for INSERT */
+ Upsert *pUpsert; /* Upsert clauses on an INSERT */
char *zSpan; /* Original SQL text of this command */
TriggerStep *pNext; /* Next in the link-list */
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
@@ -3233,17 +3273,15 @@ struct DbFixer {
** An objected used to accumulate the text of a string where we
** do not necessarily know how big the string will be in the end.
*/
-struct StrAccum {
+struct sqlite3_str {
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
char *zText; /* The string collected so far */
u32 nAlloc; /* Amount of space allocated in zText */
u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
u32 nChar; /* Length of the string so far */
- u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
+ u8 accError; /* SQLITE_NOMEM or SQLITE_TOOBIG */
u8 printfFlags; /* SQLITE_PRINTF flags below */
};
-#define STRACCUM_NOMEM 1
-#define STRACCUM_TOOBIG 2
#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
@@ -3320,6 +3358,7 @@ struct Sqlite3Config {
#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int iOnceResetThreshold; /* When to reset OP_Once counters */
+ u32 szSorterRef; /* Min size in bytes to use sorter-refs */
};
/*
@@ -3611,8 +3650,6 @@ struct PrintfArguments {
sqlite3_value **apArg; /* The argument values */
};
-void sqlite3VXPrintf(StrAccum*, const char*, va_list);
-void sqlite3XPrintf(StrAccum*, const char*, ...);
char *sqlite3MPrintf(sqlite3*,const char*, ...);
char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
@@ -3740,7 +3777,7 @@ void sqlite3DeleteTable(sqlite3*, Table*);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
-void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
+void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upsert*);
void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
int sqlite3IdListIndex(IdList*,const char*);
@@ -3770,7 +3807,8 @@ void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
-void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*);
+void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
+ Upsert*);
WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
void sqlite3WhereEnd(WhereInfo*);
LogEst sqlite3WhereOutputRowCount(WhereInfo*);
@@ -3863,7 +3901,7 @@ void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, 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*,int*);
+ u8,u8,int,int*,int*,Upsert*);
#ifdef SQLITE_ENABLE_NULL_TRIM
void sqlite3SetMakeRecordP5(Vdbe*,Table*);
#else
@@ -3916,7 +3954,8 @@ void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int);
TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*,
const char*,const char*);
TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
- Select*,u8,const char*,const char*);
+ Select*,u8,Upsert*,
+ const char*,const char*);
TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8,
const char*,const char*);
TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*,
@@ -4102,7 +4141,7 @@ 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*, u8*);
+char sqlite3AffinityType(const char*, Column*);
void sqlite3Analyze(Parse*, Token*, Token*);
int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
int sqlite3FindDb(sqlite3*, Token*);
@@ -4127,17 +4166,14 @@ int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
FuncDestructor *pDestructor
);
+void sqlite3NoopDestructor(void*);
void sqlite3OomFault(sqlite3*);
void sqlite3OomClear(sqlite3*);
int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);
void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
-void sqlite3StrAccumAppend(StrAccum*,const char*,int);
-void sqlite3StrAccumAppendAll(StrAccum*,const char*);
-void sqlite3AppendChar(StrAccum*,int,char);
char *sqlite3StrAccumFinish(StrAccum*);
-void sqlite3StrAccumReset(StrAccum*);
void sqlite3SelectDestInit(SelectDest*,int,int);
Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
@@ -4164,10 +4200,10 @@ char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
** The interface to the LEMON-generated parser
*/
#ifndef SQLITE_AMALGAMATION
- void *sqlite3ParserAlloc(void*(*)(u64));
+ void *sqlite3ParserAlloc(void*(*)(u64), Parse*);
void sqlite3ParserFree(void*, void(*)(void*));
#endif
-void sqlite3Parser(void*, int, Token, Parse*);
+void sqlite3Parser(void*, int, Token);
#ifdef YYTRACKMAXSTACKDEPTH
int sqlite3ParserStackPeak(void*);
#endif
@@ -4233,7 +4269,6 @@ int sqlite3VtabCallConnect(Parse*, Table*);
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 *);
@@ -4255,6 +4290,18 @@ const char *sqlite3JournalModename(int);
#define sqlite3WithPush(x,y,z)
#define sqlite3WithDelete(x,y)
#endif
+#ifndef SQLITE_OMIT_UPSERT
+ Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
+ void sqlite3UpsertDelete(sqlite3*,Upsert*);
+ Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
+ int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
+ void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
+#else
+#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0)
+#define sqlite3UpsertDelete(x,y)
+#define sqlite3UpsertDup(x,y) ((Upsert*)0)
+#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
diff --git a/src/test1.c b/src/test1.c
index bc8f389..f2511d2 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -2257,6 +2257,27 @@ static int SQLITE_TCLAPI test_config_sqllog(
#endif
/*
+** Usage: sqlite3_config_sorterref
+**
+** Set the SQLITE_CONFIG_SORTERREF_SIZE configuration option
+*/
+static int SQLITE_TCLAPI test_config_sorterref(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int iVal;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NBYTE");
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[1], &iVal) ) return TCL_ERROR;
+ sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, iVal);
+ return TCL_OK;
+}
+
+/*
** Usage: vfs_current_time_int64
**
** Return the value returned by the default VFS's xCurrentTimeInt64 method.
@@ -7397,6 +7418,8 @@ static int SQLITE_TCLAPI test_sqlite3_db_config(
{ "LOAD_EXTENSION", SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION },
{ "NO_CKPT_ON_CLOSE",SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE },
{ "QPSG", SQLITE_DBCONFIG_ENABLE_QPSG },
+ { "TRIGGER_EQP", SQLITE_DBCONFIG_TRIGGER_EQP },
+ { "RESET_DB", SQLITE_DBCONFIG_RESET_DATABASE },
};
int i;
int v;
@@ -7751,6 +7774,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_delete_database", test_delete_database, 0 },
{ "atomic_batch_write", test_atomic_batch_write, 0 },
{ "sqlite3_mmap_warm", test_mmap_warm, 0 },
+ { "sqlite3_config_sorterref", test_config_sorterref, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
diff --git a/src/test_malloc.c b/src/test_malloc.c
index 33bc380..32a03e7 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -32,6 +32,8 @@ static struct MemFault {
int nRepeat; /* Number of times to repeat the failure */
int nBenign; /* Number of benign failures seen since last config */
int nFail; /* Number of failures seen since last config */
+ int nOkBefore; /* Successful allocations prior to the first fault */
+ int nOkAfter; /* Successful allocations after a fault */
u8 enable; /* True if enabled */
int isInstalled; /* True if the fault simulation layer is installed */
int isBenignMode; /* True if malloc failures are considered benign */
@@ -48,17 +50,31 @@ static void sqlite3Fault(void){
}
/*
+** This routine exists as a place to set a breakpoint that will
+** fire the first time any malloc() fails on a single test case.
+** The sqlite3Fault() routine above runs on every malloc() failure.
+** This routine only runs on the first such failure.
+*/
+static void sqlite3FirstFault(void){
+ static int cnt2 = 0;
+ cnt2++;
+}
+
+/*
** Check to see if a fault should be simulated. Return true to simulate
** the fault. Return false if the fault should not be simulated.
*/
static int faultsimStep(void){
if( likely(!memfault.enable) ){
+ memfault.nOkAfter++;
return 0;
}
if( memfault.iCountdown>0 ){
memfault.iCountdown--;
+ memfault.nOkBefore++;
return 0;
}
+ if( memfault.nFail==0 ) sqlite3FirstFault();
sqlite3Fault();
memfault.nFail++;
if( memfault.isBenignMode>0 ){
@@ -133,6 +149,8 @@ static void faultsimConfig(int nDelay, int nRepeat){
memfault.nRepeat = nRepeat;
memfault.nBenign = 0;
memfault.nFail = 0;
+ memfault.nOkBefore = 0;
+ memfault.nOkAfter = 0;
memfault.enable = nDelay>=0;
/* Sometimes, when running multi-threaded tests, the isBenignMode
diff --git a/src/tokenize.c b/src/tokenize.c
index e6da3fb..b591d22 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -496,9 +496,9 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
/* sqlite3ParserTrace(stdout, "parser: "); */
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
pEngine = &sEngine;
- sqlite3ParserInit(pEngine);
+ sqlite3ParserInit(pEngine, pParse);
#else
- pEngine = sqlite3ParserAlloc(sqlite3Malloc);
+ pEngine = sqlite3ParserAlloc(sqlite3Malloc, pParse);
if( pEngine==0 ){
sqlite3OomFault(db);
return SQLITE_NOMEM_BKPT;
@@ -542,7 +542,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
}else{
pParse->sLastToken.z = zSql;
pParse->sLastToken.n = n;
- sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
+ sqlite3Parser(pEngine, tokenType, pParse->sLastToken);
lastTokenParsed = tokenType;
zSql += n;
if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
diff --git a/src/treeview.c b/src/treeview.c
index 2e92c99..970d85e 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -58,15 +58,17 @@ static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
if( p ){
for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
- sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
+ sqlite3_str_append(&acc, p->bLine[i] ? "| " : " ", 4);
}
- sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
+ sqlite3_str_append(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
+ }
+ if( zFormat!=0 ){
+ va_start(ap, zFormat);
+ sqlite3_str_vappendf(&acc, zFormat, ap);
+ va_end(ap);
+ assert( acc.nChar>0 );
+ sqlite3_str_append(&acc, "\n", 1);
}
- va_start(ap, zFormat);
- sqlite3VXPrintf(&acc, zFormat, ap);
- va_end(ap);
- assert( acc.nChar>0 );
- if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
fflush(stdout);
@@ -99,17 +101,17 @@ void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
char zLine[1000];
const struct Cte *pCte = &pWith->a[i];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
- sqlite3XPrintf(&x, "%s", pCte->zName);
+ sqlite3_str_appendf(&x, "%s", pCte->zName);
if( pCte->pCols && pCte->pCols->nExpr>0 ){
char cSep = '(';
int j;
for(j=0; j<pCte->pCols->nExpr; j++){
- sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
+ sqlite3_str_appendf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
cSep = ',';
}
- sqlite3XPrintf(&x, ")");
+ sqlite3_str_appendf(&x, ")");
}
- sqlite3XPrintf(&x, " AS");
+ sqlite3_str_appendf(&x, " AS");
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
@@ -174,20 +176,20 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
StrAccum x;
char zLine[100];
sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
- sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
+ sqlite3_str_appendf(&x, "{%d,*}", pItem->iCursor);
if( pItem->zDatabase ){
- sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
+ sqlite3_str_appendf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
}else if( pItem->zName ){
- sqlite3XPrintf(&x, " %s", pItem->zName);
+ sqlite3_str_appendf(&x, " %s", pItem->zName);
}
if( pItem->pTab ){
- sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
+ sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName);
}
if( pItem->zAlias ){
- sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
+ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias);
}
if( pItem->fg.jointype & JT_LEFT ){
- sqlite3XPrintf(&x, " LEFT-JOIN");
+ sqlite3_str_appendf(&x, " LEFT-JOIN");
}
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
@@ -536,16 +538,21 @@ void sqlite3TreeViewBareExprList(
for(i=0; i<pList->nExpr; i++){
int j = pList->a[i].u.x.iOrderByCol;
char *zName = pList->a[i].zName;
+ int moreToFollow = i<pList->nExpr - 1;
if( j || zName ){
- sqlite3TreeViewPush(pView, 0);
- }
- if( zName ){
- sqlite3TreeViewLine(pView, "AS %s", zName);
- }
- if( j ){
- sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
+ sqlite3TreeViewPush(pView, moreToFollow);
+ moreToFollow = 0;
+ sqlite3TreeViewLine(pView, 0);
+ if( zName ){
+ fprintf(stdout, "AS %s ", zName);
+ }
+ if( j ){
+ fprintf(stdout, "iOrderByCol=%d", j);
+ }
+ fprintf(stdout, "\n");
+ fflush(stdout);
}
- sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
+ sqlite3TreeViewExpr(pView, pList->a[i].pExpr, moreToFollow);
if( j || zName ){
sqlite3TreeViewPop(pView);
}
diff --git a/src/trigger.c b/src/trigger.c
index 9f7bff5..044f256 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -25,6 +25,7 @@ void sqlite3DeleteTriggerStep(sqlite3 *db, TriggerStep *pTriggerStep){
sqlite3ExprListDelete(db, pTmp->pExprList);
sqlite3SelectDelete(db, pTmp->pSelect);
sqlite3IdListDelete(db, pTmp->pIdList);
+ sqlite3UpsertDelete(db, pTmp->pUpsert);
sqlite3DbFree(db, pTmp->zSpan);
sqlite3DbFree(db, pTmp);
@@ -416,6 +417,7 @@ TriggerStep *sqlite3TriggerInsertStep(
IdList *pColumn, /* List of columns in pTableName to insert into */
Select *pSelect, /* A SELECT statement that supplies values */
u8 orconf, /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
+ Upsert *pUpsert, /* ON CONFLICT clauses for upsert */
const char *zStart, /* Start of SQL text */
const char *zEnd /* End of SQL text */
){
@@ -427,9 +429,13 @@ TriggerStep *sqlite3TriggerInsertStep(
if( pTriggerStep ){
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
pTriggerStep->pIdList = pColumn;
+ pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
}else{
+ testcase( pColumn );
sqlite3IdListDelete(db, pColumn);
+ testcase( pUpsert );
+ sqlite3UpsertDelete(db, pUpsert);
}
sqlite3SelectDelete(db, pSelect);
@@ -746,7 +752,7 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep),
sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3ExprDup(db, pStep->pWhere, 0),
- pParse->eOrconf, 0, 0
+ pParse->eOrconf, 0, 0, 0
);
break;
}
@@ -755,7 +761,8 @@ static int codeTriggerProgram(
targetSrcList(pParse, pStep),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
- pParse->eOrconf
+ pParse->eOrconf,
+ sqlite3UpsertDup(db, pStep->pUpsert)
);
break;
}
diff --git a/src/update.c b/src/update.c
index 4b8207d..9860943 100644
--- a/src/update.c
+++ b/src/update.c
@@ -93,7 +93,8 @@ void sqlite3Update(
Expr *pWhere, /* The WHERE clause. May be null */
int onError, /* How to handle constraint errors */
ExprList *pOrderBy, /* ORDER BY clause. May be null */
- Expr *pLimit /* LIMIT clause. May be null */
+ Expr *pLimit, /* LIMIT clause. May be null */
+ Upsert *pUpsert /* ON CONFLICT clause, or null */
){
int i, j; /* Loop counters */
Table *pTab; /* The table to be updated */
@@ -200,16 +201,23 @@ void sqlite3Update(
** need to occur right after the database cursor. So go ahead and
** allocate enough space, just in case.
*/
- pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
+ iBaseCur = iDataCur = pParse->nTab++;
iIdxCur = iDataCur+1;
pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ testcase( pPk!=0 && pPk!=pTab->pIndex );
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
- if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
+ if( pPk==pIdx ){
iDataCur = pParse->nTab;
- pTabList->a[0].iCursor = iDataCur;
}
pParse->nTab++;
}
+ if( pUpsert ){
+ /* On an UPSERT, reuse the same cursors already opened by INSERT */
+ iDataCur = pUpsert->iDataCur;
+ iIdxCur = pUpsert->iIdxCur;
+ pParse->nTab = iBaseCur;
+ }
+ pTabList->a[0].iCursor = iDataCur;
/* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
** Initialize aXRef[] and aToOpen[] to their default values.
@@ -226,6 +234,8 @@ void sqlite3Update(
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
+ sNC.uNC.pUpsert = pUpsert;
+ sNC.ncFlags = NC_UUpsert;
/* Resolve the column names in all the expressions of the
** of the UPDATE statement. Also find the column index
@@ -329,7 +339,7 @@ void sqlite3Update(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto update_cleanup;
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
- sqlite3BeginWriteOperation(pParse, 1, iDb);
+ sqlite3BeginWriteOperation(pParse, pTrigger || hasFK, iDb);
/* Allocate required registers. */
if( !IsVirtual(pTab) ){
@@ -380,8 +390,16 @@ void sqlite3Update(
}
#endif
- /* Initialize the count of updated rows */
- if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
+ /* Jump to labelBreak to abandon further processing of this UPDATE */
+ labelContinue = labelBreak = sqlite3VdbeMakeLabel(v);
+
+ /* Not an UPSERT. Normal processing. Begin by
+ ** initialize the count of updated rows */
+ if( (db->flags&SQLITE_CountRows)!=0
+ && !pParse->pTriggerTab
+ && !pParse->nested
+ && pUpsert==0
+ ){
regRowCount = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
@@ -394,46 +412,61 @@ void sqlite3Update(
iPk = pParse->nMem+1;
pParse->nMem += nPk;
regKey = ++pParse->nMem;
- iEph = pParse->nTab++;
-
- sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
- addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
- sqlite3VdbeSetP4KeyInfo(pParse, pPk);
- }
-
- /* Begin the database scan.
- **
- ** Do not consider a single-pass strategy for a multi-row update if
- ** there are any triggers or foreign keys to process, or rows may
- ** be deleted as a result of REPLACE conflict handling. Any of these
- ** things might disturb a cursor being used to scan through the table
- ** or index, causing a single-pass approach to malfunction. */
- flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
- if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
- flags |= WHERE_ONEPASS_MULTIROW;
- }
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
- if( pWInfo==0 ) goto update_cleanup;
-
- /* A one-pass strategy that might update more than one row may not
- ** be used if any column of the index used for the scan is being
- ** updated. Otherwise, if there is an index on "b", statements like
- ** the following could create an infinite loop:
- **
- ** UPDATE t1 SET b=b+1 WHERE b>?
- **
- ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
- ** strategy that uses an index for which one or more columns are being
- ** updated. */
- eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
- if( eOnePass==ONEPASS_MULTI ){
- int iCur = aiCurOnePass[1];
- if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
- eOnePass = ONEPASS_OFF;
+ if( pUpsert==0 ){
+ iEph = pParse->nTab++;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, iPk, iPk+nPk-1);
+ addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
}
- assert( iCur!=iDataCur || !HasRowid(pTab) );
}
+ if( pUpsert ){
+ /* If this is an UPSERT, then all cursors have already been opened by
+ ** the outer INSERT and the data cursor should be pointing at the row
+ ** that is to be updated. So bypass the code that searches for the
+ ** row(s) to be updated.
+ */
+ pWInfo = 0;
+ eOnePass = ONEPASS_SINGLE;
+ sqlite3ExprIfFalse(pParse, pWhere, labelBreak, SQLITE_JUMPIFNULL);
+ }else{
+ /* Begin the database scan.
+ **
+ ** Do not consider a single-pass strategy for a multi-row update if
+ ** there are any triggers or foreign keys to process, or rows may
+ ** be deleted as a result of REPLACE conflict handling. Any of these
+ ** things might disturb a cursor being used to scan through the table
+ ** or index, causing a single-pass approach to malfunction. */
+ flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
+ if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
+ flags |= WHERE_ONEPASS_MULTIROW;
+ }
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
+ if( pWInfo==0 ) goto update_cleanup;
+
+ /* A one-pass strategy that might update more than one row may not
+ ** be used if any column of the index used for the scan is being
+ ** updated. Otherwise, if there is an index on "b", statements like
+ ** the following could create an infinite loop:
+ **
+ ** UPDATE t1 SET b=b+1 WHERE b>?
+ **
+ ** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
+ ** strategy that uses an index for which one or more columns are being
+ ** updated. */
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ if( eOnePass!=ONEPASS_SINGLE ){
+ sqlite3MultiWrite(pParse);
+ if( eOnePass==ONEPASS_MULTI ){
+ int iCur = aiCurOnePass[1];
+ if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
+ eOnePass = ONEPASS_OFF;
+ }
+ assert( iCur!=iDataCur || !HasRowid(pTab) );
+ }
+ }
+ }
+
if( HasRowid(pTab) ){
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
** mode, write the rowid into the FIFO. In either of the one-pass modes,
@@ -453,7 +486,7 @@ void sqlite3Update(
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
}
if( eOnePass ){
- sqlite3VdbeChangeToNoop(v, addrOpen);
+ if( addrOpen ) sqlite3VdbeChangeToNoop(v, addrOpen);
nKey = nPk;
regKey = iPk;
}else{
@@ -463,59 +496,58 @@ void sqlite3Update(
}
}
- if( eOnePass!=ONEPASS_MULTI ){
- sqlite3WhereEnd(pWInfo);
- }
-
- labelBreak = sqlite3VdbeMakeLabel(v);
- if( !isView ){
- int addrOnce = 0;
-
- /* Open every index that needs updating. */
- if( eOnePass!=ONEPASS_OFF ){
- if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
- if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
- }
-
- if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
- addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ if( pUpsert==0 ){
+ if( eOnePass!=ONEPASS_MULTI ){
+ sqlite3WhereEnd(pWInfo);
}
- sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
- 0, 0);
- if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
- }
-
- /* Top of the update loop */
- if( eOnePass!=ONEPASS_OFF ){
- if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
- assert( pPk );
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
- VdbeCoverageNeverTaken(v);
+
+ if( !isView ){
+ int addrOnce = 0;
+
+ /* Open every index that needs updating. */
+ if( eOnePass!=ONEPASS_OFF ){
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
+ }
+
+ if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ }
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur,
+ aToOpen, 0, 0);
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
}
- if( eOnePass==ONEPASS_SINGLE ){
- labelContinue = labelBreak;
- }else{
+
+ /* Top of the update loop */
+ if( eOnePass!=ONEPASS_OFF ){
+ if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
+ assert( pPk );
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey,nKey);
+ VdbeCoverageNeverTaken(v);
+ }
+ if( eOnePass!=ONEPASS_SINGLE ){
+ labelContinue = sqlite3VdbeMakeLabel(v);
+ }
+ 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_RowData, iEph, regKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
+ VdbeCoverage(v);
+ }else{
+ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet,labelBreak,
+ regOldRowid);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
+ VdbeCoverage(v);
}
- 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_RowData, iEph, regKey);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
- VdbeCoverage(v);
- }else{
- labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
- regOldRowid);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
- VdbeCoverage(v);
}
- /* If the record number will change, set register regNewRowid to
- ** contain the new value. If the record number is not being modified,
+ /* If the rowid value will change, set register regNewRowid to
+ ** contain the new value. If the rowid is not being modified,
** then regNewRowid is the same register as regOldRowid, which is
** already populated. */
assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
@@ -579,6 +611,12 @@ void sqlite3Update(
testcase( i==31 );
testcase( i==32 );
sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
+ if( tmask & TRIGGER_BEFORE ){
+ /* This value will be recomputed in After-BEFORE-trigger-reload-loop
+ ** below, so make sure that it is not cached and reused.
+ ** Ticket d85fffd6ffe856092ed8daefa811b1e399706b28. */
+ sqlite3ExprCacheRemove(pParse, regNew+i, 1);
+ }
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}
@@ -607,10 +645,14 @@ void sqlite3Update(
VdbeCoverage(v);
}
- /* If it did not delete it, the row-trigger may still have modified
+ /* After-BEFORE-trigger-reload-loop:
+ ** If it did not delete it, the BEFORE trigger may still have modified
** some of the columns of the row being updated. Load the values for
- ** all columns not modified by the update statement into their
- ** registers in case this has happened.
+ ** all columns not modified by the update statement into their registers
+ ** in case this has happened. Only unmodified columns are reloaded.
+ ** The values computed for modified columns use the values before the
+ ** BEFORE trigger runs. See test case trigger1-18.0 (added 2018-04-26)
+ ** for an example.
*/
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]<0 && i!=pTab->iPKey ){
@@ -626,7 +668,7 @@ void sqlite3Update(
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
- aXRef);
+ aXRef, 0);
/* Do FK constraint checks. */
if( hasFK ){
@@ -696,7 +738,7 @@ void sqlite3Update(
/* Increment the row counter
*/
- if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){
+ if( regRowCount ){
sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}
@@ -723,16 +765,15 @@ void sqlite3Update(
** maximum rowid counter values recorded while inserting into
** autoincrement tables.
*/
- if( pParse->nested==0 && pParse->pTriggerTab==0 ){
+ if( pParse->nested==0 && pParse->pTriggerTab==0 && pUpsert==0 ){
sqlite3AutoincrementEnd(pParse);
}
/*
- ** Return the number of rows that were changed. If this routine is
- ** generating code because of a call to sqlite3NestedParse(), do not
- ** invoke the callback function.
+ ** Return the number of rows that were changed, if we are tracking
+ ** that information.
*/
- if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){
+ if( regRowCount ){
sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC);
@@ -804,7 +845,7 @@ static void updateVirtualTable(
int regRowid; /* Register for ephem table rowid */
int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
- int bOnePass; /* True to use onepass strategy */
+ int eOnePass; /* True to use onepass strategy */
int addr; /* Address of OP_OpenEphemeral */
/* Allocate nArg registers in which to gather the arguments for VUpdate. Then
@@ -849,19 +890,20 @@ static void updateVirtualTable(
sqlite3VdbeAddOp2(v, OP_SCopy, regArg+2+iPk, regArg+1);
}
- bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
+
+ /* There is no ONEPASS_MULTI on virtual tables */
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
- if( bOnePass ){
+ if( eOnePass ){
/* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
- ** above. Also, if this is a top-level parse (not a trigger), clear the
- ** multi-write flag so that the VM does not open a statement journal */
+ ** above. */
sqlite3VdbeChangeToNoop(v, addr);
- if( sqlite3IsToplevel(pParse) ){
- pParse->isMultiWrite = 0;
- }
+ sqlite3VdbeAddOp1(v, OP_Close, iCsr);
}else{
/* Create a record from the argument register contents and insert it into
** the ephemeral table. */
+ sqlite3MultiWrite(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
#ifdef SQLITE_DEBUG
/* Signal an assert() within OP_MakeRecord that it is allowed to
@@ -873,7 +915,7 @@ static void updateVirtualTable(
}
- if( bOnePass==0 ){
+ if( eOnePass==ONEPASS_OFF ){
/* End the virtual table scan */
sqlite3WhereEnd(pWInfo);
@@ -893,7 +935,7 @@ static void updateVirtualTable(
/* End of the ephemeral table scan. Or, if using the onepass strategy,
** jump to here if the scan visited zero rows. */
- if( bOnePass==0 ){
+ if( eOnePass==ONEPASS_OFF ){
sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr);
sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
diff --git a/src/upsert.c b/src/upsert.c
new file mode 100644
index 0000000..31a905d
--- /dev/null
+++ b/src/upsert.c
@@ -0,0 +1,250 @@
+/*
+** 2018-04-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 code to implement various aspects of UPSERT
+** processing and handling of the Upsert object.
+*/
+#include "sqliteInt.h"
+
+#ifndef SQLITE_OMIT_UPSERT
+/*
+** Free a list of Upsert objects
+*/
+void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
+ if( p ){
+ sqlite3ExprListDelete(db, p->pUpsertTarget);
+ sqlite3ExprDelete(db, p->pUpsertTargetWhere);
+ sqlite3ExprListDelete(db, p->pUpsertSet);
+ sqlite3ExprDelete(db, p->pUpsertWhere);
+ sqlite3DbFree(db, p);
+ }
+}
+
+/*
+** Duplicate an Upsert object.
+*/
+Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
+ if( p==0 ) return 0;
+ return sqlite3UpsertNew(db,
+ sqlite3ExprListDup(db, p->pUpsertTarget, 0),
+ sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
+ sqlite3ExprListDup(db, p->pUpsertSet, 0),
+ sqlite3ExprDup(db, p->pUpsertWhere, 0)
+ );
+}
+
+/*
+** Create a new Upsert object.
+*/
+Upsert *sqlite3UpsertNew(
+ sqlite3 *db, /* Determines which memory allocator to use */
+ ExprList *pTarget, /* Target argument to ON CONFLICT, or NULL */
+ Expr *pTargetWhere, /* Optional WHERE clause on the target */
+ ExprList *pSet, /* UPDATE columns, or NULL for a DO NOTHING */
+ Expr *pWhere /* WHERE clause for the ON CONFLICT UPDATE */
+){
+ Upsert *pNew;
+ pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
+ if( pNew==0 ){
+ sqlite3ExprListDelete(db, pTarget);
+ sqlite3ExprDelete(db, pTargetWhere);
+ sqlite3ExprListDelete(db, pSet);
+ sqlite3ExprDelete(db, pWhere);
+ return 0;
+ }else{
+ pNew->pUpsertTarget = pTarget;
+ pNew->pUpsertTargetWhere = pTargetWhere;
+ pNew->pUpsertSet = pSet;
+ pNew->pUpsertWhere = pWhere;
+ pNew->pUpsertIdx = 0;
+ }
+ return pNew;
+}
+
+/*
+** Analyze the ON CONFLICT clause described by pUpsert. Resolve all
+** symbols in the conflict-target.
+**
+** Return SQLITE_OK if everything works, or an error code is something
+** is wrong.
+*/
+int sqlite3UpsertAnalyzeTarget(
+ Parse *pParse, /* The parsing context */
+ SrcList *pTabList, /* Table into which we are inserting */
+ Upsert *pUpsert /* The ON CONFLICT clauses */
+){
+ Table *pTab; /* That table into which we are inserting */
+ int rc; /* Result code */
+ int iCursor; /* Cursor used by pTab */
+ Index *pIdx; /* One of the indexes of pTab */
+ ExprList *pTarget; /* The conflict-target clause */
+ Expr *pTerm; /* One term of the conflict-target clause */
+ NameContext sNC; /* Context for resolving symbolic names */
+ Expr sCol[2]; /* Index column converted into an Expr */
+
+ assert( pTabList->nSrc==1 );
+ assert( pTabList->a[0].pTab!=0 );
+ assert( pUpsert!=0 );
+ assert( pUpsert->pUpsertTarget!=0 );
+
+ /* Resolve all symbolic names in the conflict-target clause, which
+ ** includes both the list of columns and the optional partial-index
+ ** WHERE clause.
+ */
+ memset(&sNC, 0, sizeof(sNC));
+ sNC.pParse = pParse;
+ sNC.pSrcList = pTabList;
+ rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
+ if( rc ) return rc;
+ rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
+ if( rc ) return rc;
+
+ /* Check to see if the conflict target matches the rowid. */
+ pTab = pTabList->a[0].pTab;
+ pTarget = pUpsert->pUpsertTarget;
+ iCursor = pTabList->a[0].iCursor;
+ if( HasRowid(pTab)
+ && pTarget->nExpr==1
+ && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
+ && pTerm->iColumn==XN_ROWID
+ ){
+ /* The conflict-target is the rowid of the primary table */
+ assert( pUpsert->pUpsertIdx==0 );
+ return SQLITE_OK;
+ }
+
+ /* Initialize sCol[0..1] to be an expression parse tree for a
+ ** single column of an index. The sCol[0] node will be the TK_COLLATE
+ ** operator and sCol[1] will be the TK_COLUMN operator. Code below
+ ** will populate the specific collation and column number values
+ ** prior to comparing against the conflict-target expression.
+ */
+ memset(sCol, 0, sizeof(sCol));
+ sCol[0].op = TK_COLLATE;
+ sCol[0].pLeft = &sCol[1];
+ sCol[1].op = TK_COLUMN;
+ sCol[1].iTable = pTabList->a[0].iCursor;
+
+ /* Check for matches against other indexes */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int ii, jj, nn;
+ if( !IsUniqueIndex(pIdx) ) continue;
+ if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
+ if( pIdx->pPartIdxWhere ){
+ if( pUpsert->pUpsertTargetWhere==0 ) continue;
+ if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
+ pIdx->pPartIdxWhere, iCursor)!=0 ){
+ continue;
+ }
+ }
+ nn = pIdx->nKeyCol;
+ for(ii=0; ii<nn; ii++){
+ Expr *pExpr;
+ sCol[0].u.zToken = (char*)pIdx->azColl[ii];
+ if( pIdx->aiColumn[ii]==XN_EXPR ){
+ assert( pIdx->aColExpr!=0 );
+ assert( pIdx->aColExpr->nExpr>ii );
+ pExpr = pIdx->aColExpr->a[ii].pExpr;
+ if( pExpr->op!=TK_COLLATE ){
+ sCol[0].pLeft = pExpr;
+ pExpr = &sCol[0];
+ }
+ }else{
+ sCol[0].pLeft = &sCol[1];
+ sCol[1].iColumn = pIdx->aiColumn[ii];
+ pExpr = &sCol[0];
+ }
+ for(jj=0; jj<nn; jj++){
+ if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
+ break; /* Column ii of the index matches column jj of target */
+ }
+ }
+ if( jj>=nn ){
+ /* The target contains no match for column jj of the index */
+ break;
+ }
+ }
+ if( ii<nn ){
+ /* Column ii of the index did not match any term of the conflict target.
+ ** Continue the search with the next index. */
+ continue;
+ }
+ pUpsert->pUpsertIdx = pIdx;
+ return SQLITE_OK;
+ }
+ sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
+ "PRIMARY KEY or UNIQUE constraint");
+ return SQLITE_ERROR;
+}
+
+/*
+** Generate bytecode that does an UPDATE as part of an upsert.
+**
+** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
+** In this case parameter iCur is a cursor open on the table b-tree that
+** currently points to the conflicting table row. Otherwise, if pIdx
+** is not NULL, then pIdx is the constraint that failed and iCur is a
+** cursor points to the conflicting row.
+*/
+void sqlite3UpsertDoUpdate(
+ Parse *pParse, /* The parsing and code-generating context */
+ Upsert *pUpsert, /* The ON CONFLICT clause for the upsert */
+ Table *pTab, /* The table being updated */
+ Index *pIdx, /* The UNIQUE constraint that failed */
+ int iCur /* Cursor for pIdx (or pTab if pIdx==NULL) */
+){
+ Vdbe *v = pParse->pVdbe;
+ sqlite3 *db = pParse->db;
+ SrcList *pSrc; /* FROM clause for the UPDATE */
+ int iDataCur = pUpsert->iDataCur;
+
+ assert( v!=0 );
+ VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
+ if( pIdx && iCur!=iDataCur ){
+ if( HasRowid(pTab) ){
+ int regRowid = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ int nPk = pPk->nKeyCol;
+ int iPk = pParse->nMem+1;
+ int i;
+ pParse->nMem += nPk;
+ for(i=0; i<nPk; i++){
+ int k;
+ assert( pPk->aiColumn[i]>=0 );
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, k, iPk+i);
+ VdbeComment((v, "%s.%s", pIdx->zName,
+ pTab->aCol[pPk->aiColumn[i]].zName));
+ }
+ sqlite3VdbeVerifyAbortable(v, OE_Abort);
+ i = sqlite3VdbeAddOp4Int(v, OP_Found, iDataCur, 0, iPk, nPk);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
+ "corrupt database", P4_STATIC);
+ sqlite3VdbeJumpHere(v, i);
+ }
+ }
+ /* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
+ ** we have to make a copy before passing it down into sqlite3Update() */
+ pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
+ sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
+ pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
+ pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
+ pUpsert->pUpsertWhere = 0; /* Will have been deleted by sqlite3Update() */
+ VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
+}
+
+#endif /* SQLITE_OMIT_UPSERT */
diff --git a/src/vacuum.c b/src/vacuum.c
index c7a14a6..fe29514 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -39,8 +39,14 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
- assert( sqlite3_strnicmp(zSubSql,"SELECT",6)!=0 || CORRUPT_DB );
- if( zSubSql && zSubSql[0]!='S' ){
+ /* The secondary SQL must be one of CREATE TABLE, CREATE INDEX,
+ ** or INSERT. Historically there have been attacks that first
+ ** corrupt the sqlite_master.sql field with other kinds of statements
+ ** then run VACUUM to get those statements to execute at inappropriate
+ ** times. */
+ if( zSubSql
+ && (strncmp(zSubSql,"CRE",3)==0 || strncmp(zSubSql,"INS",3)==0)
+ ){
rc = execSql(db, pzErrMsg, zSubSql);
if( rc!=SQLITE_OK ) break;
}
@@ -253,7 +259,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execSqlF(db, pzErrMsg,
"SELECT sql FROM \"%w\".sqlite_master"
- " WHERE type='index' AND length(sql)>10",
+ " WHERE type='index'",
zDbMain
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
diff --git a/src/vdbe.c b/src/vdbe.c
index 70537ce..cab69b5 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -915,6 +915,9 @@ case OP_Yield: { /* in1, jump */
*/
case OP_HaltIfNull: { /* in3 */
pIn3 = &aMem[pOp->p3];
+#ifdef SQLITE_DEBUG
+ if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
+#endif
if( (pIn3->flags & MEM_Null)==0 ) break;
/* Fall through into OP_Halt */
}
@@ -954,6 +957,9 @@ case OP_Halt: {
int pcx;
pcx = (int)(pOp - aOp);
+#ifdef SQLITE_DEBUG
+ if( pOp->p2==OE_Abort ){ sqlite3VdbeAssertAbortable(p); }
+#endif
if( pOp->p1==SQLITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
pFrame = p->pFrame;
@@ -3324,6 +3330,8 @@ case OP_ReadCookie: { /* out2 */
*/
case OP_SetCookie: {
Db *pDb;
+
+ sqlite3VdbeIncrWriteCounter(p, 0);
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p1) );
@@ -4287,11 +4295,8 @@ case OP_NewRowid: { /* out2 */
pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
- if( !pC->isTable ){
- rc = SQLITE_CORRUPT_BKPT;
- goto abort_due_to_error;
- }
assert( pC!=0 );
+ assert( pC->isTable );
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0 );
{
@@ -4460,6 +4465,7 @@ case OP_InsertInt: {
assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
REGISTER_TRACE(pOp->p2, pData);
+ sqlite3VdbeIncrWriteCounter(p, pC);
if( pOp->opcode==OP_Insert ){
pKey = &aMem[pOp->p3];
@@ -4574,6 +4580,7 @@ case OP_Delete: {
assert( pC->eCurType==CURTYPE_BTREE );
assert( pC->uc.pCursor!=0 );
assert( pC->deferredMoveto==0 );
+ sqlite3VdbeIncrWriteCounter(p, pC);
#ifdef SQLITE_DEBUG
if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){
@@ -4742,10 +4749,10 @@ case OP_SorterData: {
** If the P1 cursor must be pointing to a valid row (not a NULL row)
** of a real table, not a pseudo-table.
**
-** If P3!=0 then this opcode is allowed to make an ephermeral pointer
+** If P3!=0 then this opcode is allowed to make an ephemeral pointer
** into the database page. That means that the content of the output
** register will be invalidated as soon as the cursor moves - including
-** moves caused by other cursors that "save" the the current cursors
+** moves caused by other cursors that "save" the current cursors
** position in order that they can write to the same table. If P3==0
** then a copy of the data is made into memory. P3!=0 is faster, but
** P3==0 is safer.
@@ -5192,6 +5199,7 @@ case OP_IdxInsert: { /* in2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
+ sqlite3VdbeIncrWriteCounter(p, pC);
assert( pC!=0 );
assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
@@ -5238,6 +5246,7 @@ case OP_IdxDelete: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->eCurType==CURTYPE_BTREE );
+ sqlite3VdbeIncrWriteCounter(p, pC);
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
assert( pOp->p5==0 );
@@ -5460,6 +5469,7 @@ case OP_Destroy: { /* out2 */
int iMoved;
int iDb;
+ sqlite3VdbeIncrWriteCounter(p, 0);
assert( p->readOnly==0 );
assert( pOp->p1>1 );
pOut = out2Prerelease(p, pOp);
@@ -5509,6 +5519,7 @@ case OP_Destroy: { /* out2 */
case OP_Clear: {
int nChange;
+ sqlite3VdbeIncrWriteCounter(p, 0);
nChange = 0;
assert( p->readOnly==0 );
assert( DbMaskTest(p->btreeMask, pOp->p2) );
@@ -5558,13 +5569,14 @@ case OP_ResetSorter: {
** Allocate a new b-tree in the main database file if P1==0 or in the
** TEMP database file if P1==1 or in an attached database if
** P1>1. The P3 argument must be 1 (BTREE_INTKEY) for a rowid table
-** it must be 2 (BTREE_BLOBKEY) for a index or WITHOUT ROWID table.
+** it must be 2 (BTREE_BLOBKEY) for an index or WITHOUT ROWID table.
** The root page number of the new b-tree is stored in register P2.
*/
case OP_CreateBtree: { /* out2 */
int pgno;
Db *pDb;
+ sqlite3VdbeIncrWriteCounter(p, 0);
pOut = out2Prerelease(p, pOp);
pgno = 0;
assert( pOp->p3==BTREE_INTKEY || pOp->p3==BTREE_BLOBKEY );
@@ -5584,6 +5596,7 @@ case OP_CreateBtree: { /* out2 */
** Run the SQL statement or statements specified in the P4 string.
*/
case OP_SqlExec: {
+ sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
db->nSqlExec--;
@@ -5673,6 +5686,7 @@ case OP_LoadAnalysis: {
** schema consistent with what is on disk.
*/
case OP_DropTable: {
+ sqlite3VdbeIncrWriteCounter(p, 0);
sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p4.z);
break;
}
@@ -5686,6 +5700,7 @@ case OP_DropTable: {
** schema consistent with what is on disk.
*/
case OP_DropIndex: {
+ sqlite3VdbeIncrWriteCounter(p, 0);
sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p4.z);
break;
}
@@ -5699,6 +5714,7 @@ case OP_DropIndex: {
** schema consistent with what is on disk.
*/
case OP_DropTrigger: {
+ sqlite3VdbeIncrWriteCounter(p, 0);
sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p4.z);
break;
}
@@ -6745,7 +6761,7 @@ case OP_VFilter: { /* jump */
** If the VColumn opcode is being used to fetch the value of
** an unchanging column during an UPDATE operation, then the P5
** value is 1. Otherwise, P5 is 0. The P5 value is returned
-** by sqlite3_vtab_nochange() routine can can be used
+** by sqlite3_vtab_nochange() routine and can be used
** by virtual table implementations to return special "no-change"
** marks which can be more efficient, depending on the virtual table.
*/
@@ -6908,6 +6924,7 @@ case OP_VUpdate: {
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
assert( p->readOnly==0 );
+ sqlite3VdbeIncrWriteCounter(p, 0);
pVtab = pOp->p4.pVtab->pVtab;
if( pVtab==0 || NEVER(pVtab->pModule==0) ){
rc = SQLITE_LOCKED;
@@ -7225,6 +7242,22 @@ case OP_CursorHint: {
}
#endif /* SQLITE_ENABLE_CURSOR_HINTS */
+#ifdef SQLITE_DEBUG
+/* Opcode: Abortable * * * * *
+**
+** Verify that an Abort can happen. Assert if an Abort at this point
+** might cause database corruption. This opcode only appears in debugging
+** builds.
+**
+** An Abort is safe if either there have been no writes, or if there is
+** an active statement journal.
+*/
+case OP_Abortable: {
+ sqlite3VdbeAssertAbortable(p);
+ break;
+}
+#endif
+
/* Opcode: Noop * * * * *
**
** Do nothing. This instruction is often useful as a jump
@@ -7236,8 +7269,9 @@ case OP_CursorHint: {
** This opcode records information from the optimizer. It is the
** the same as a no-op. This opcodesnever appears in a real VM program.
*/
-default: { /* This is really OP_Noop and OP_Explain */
+default: { /* This is really OP_Noop, OP_Explain */
assert( pOp->opcode==OP_Noop || pOp->opcode==OP_Explain );
+
break;
}
diff --git a/src/vdbe.h b/src/vdbe.h
index f002e05..51bbdf9 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -197,7 +197,24 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int);
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
# define sqlite3VdbeVerifyNoResultRow(A)
#endif
-VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
+#if defined(SQLITE_DEBUG)
+ void sqlite3VdbeVerifyAbortable(Vdbe *p, int);
+#else
+# define sqlite3VdbeVerifyAbortable(A,B)
+#endif
+VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
+#ifndef SQLITE_OMIT_EXPLAIN
+ void sqlite3VdbeExplain(Parse*,u8,const char*,...);
+ void sqlite3VdbeExplainPop(Parse*);
+ int sqlite3VdbeExplainParent(Parse*);
+# define ExplainQueryPlan(P) sqlite3VdbeExplain P
+# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
+# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
+#else
+# define ExplainQueryPlan(P)
+# define ExplainQueryPlanPop(P)
+# define ExplainQueryPlanParent(P) 0
+#endif
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
@@ -220,6 +237,9 @@ void sqlite3VdbeClearObject(sqlite3*,Vdbe*);
void sqlite3VdbeMakeReady(Vdbe*,Parse*);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
+#ifdef SQLITE_COVERAGE_TEST
+ int sqlite3VdbeLabelHasBeenResolved(Vdbe*,int);
+#endif
int sqlite3VdbeCurrentAddr(Vdbe*);
#ifdef SQLITE_DEBUG
int sqlite3VdbeAssertMayAbort(Vdbe *, int);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 44f901a..0f10e37 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -379,6 +379,7 @@ struct Vdbe {
int nOp; /* Number of instructions in the program */
#ifdef SQLITE_DEBUG
int rcApp; /* errcode set by sqlite3_result_error_code() */
+ u32 nWrite; /* Number of write operations that have occurred */
#endif
u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
@@ -514,6 +515,14 @@ int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
+#ifdef SQLITE_DEBUG
+ void sqlite3VdbeIncrWriteCounter(Vdbe*, VdbeCursor*);
+ void sqlite3VdbeAssertAbortable(Vdbe*);
+#else
+# define sqlite3VdbeIncrWriteCounter(V,C)
+# define sqlite3VdbeAssertAbortable(V)
+#endif
+
#if !defined(SQLITE_OMIT_SHARED_CACHE)
void sqlite3VdbeEnter(Vdbe*);
#else
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 2a6e1f8..ced3e90 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -788,28 +788,6 @@ sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
}
/*
-** 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
-** SQL function that use this routine so that the functions will exist
-** for name resolution but are actually overloaded by the xFindFunction
-** method of virtual tables.
-*/
-void sqlite3InvalidFunction(
- sqlite3_context *context, /* The function calling context */
- int NotUsed, /* Number of arguments to the function */
- sqlite3_value **NotUsed2 /* Value of each argument */
-){
- const char *zName = context->pFunc->zName;
- char *zErr;
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
- zErr = sqlite3_mprintf(
- "unable to use function %s in the requested context", zName);
- sqlite3_result_error(context, zErr, -1);
- sqlite3_free(zErr);
-}
-
-/*
** Create a new aggregate context for p and return a pointer to
** its pMem->z element.
*/
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 78777bd..38ef85f 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -303,6 +303,49 @@ int sqlite3VdbeAddOp4Dup8(
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
}
+#ifndef SQLITE_OMIT_EXPLAIN
+/*
+** Return the address of the current EXPLAIN QUERY PLAN baseline.
+** 0 means "none".
+*/
+int sqlite3VdbeExplainParent(Parse *pParse){
+ VdbeOp *pOp;
+ if( pParse->addrExplain==0 ) return 0;
+ pOp = sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain);
+ return pOp->p2;
+}
+
+/*
+** Add a new OP_Explain opcode.
+**
+** If the bPush flag is true, then make this opcode the parent for
+** subsequent Explains until sqlite3VdbeExplainPop() is called.
+*/
+void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
+ if( pParse->explain==2 ){
+ char *zMsg;
+ Vdbe *v = pParse->pVdbe;
+ va_list ap;
+ int iThis;
+ va_start(ap, zFmt);
+ zMsg = sqlite3VMPrintf(pParse->db, zFmt, ap);
+ va_end(ap);
+ v = pParse->pVdbe;
+ iThis = v->nOp;
+ sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
+ zMsg, P4_DYNAMIC);
+ if( bPush) pParse->addrExplain = iThis;
+ }
+}
+
+/*
+** Pop the EXPLAIN QUERY PLAN stack one level.
+*/
+void sqlite3VdbeExplainPop(Parse *pParse){
+ pParse->addrExplain = sqlite3VdbeExplainParent(pParse);
+}
+#endif /* SQLITE_OMIT_EXPLAIN */
+
/*
** Add an OP_ParseSchema opcode. This routine is broken out from
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
@@ -392,10 +435,29 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){
assert( j<p->nLabel );
assert( j>=0 );
if( p->aLabel ){
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("RESOLVE LABEL %d to %d\n", x, v->nOp);
+ }
+#endif
+ assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */
p->aLabel[j] = v->nOp;
}
}
+#ifdef SQLITE_COVERAGE_TEST
+/*
+** Return TRUE if and only if the label x has already been resolved.
+** Return FALSE (zero) if label x is still unresolved.
+**
+** This routine is only used inside of testcase() macros, and so it
+** only exists when measuring test coverage.
+*/
+int sqlite3VdbeLabelHasBeenResolved(Vdbe *v, int x){
+ return v->pParse->aLabel && v->pParse->aLabel[ADDR(x)]>=0;
+}
+#endif /* SQLITE_COVERAGE_TEST */
+
/*
** Mark the VDBE as one that can only be run one time.
*/
@@ -541,6 +603,32 @@ int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
}
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
+#ifdef SQLITE_DEBUG
+/*
+** Increment the nWrite counter in the VDBE if the cursor is not an
+** ephemeral cursor, or if the cursor argument is NULL.
+*/
+void sqlite3VdbeIncrWriteCounter(Vdbe *p, VdbeCursor *pC){
+ if( pC==0
+ || (pC->eCurType!=CURTYPE_SORTER
+ && pC->eCurType!=CURTYPE_PSEUDO
+ && !pC->isEphemeral)
+ ){
+ p->nWrite++;
+ }
+}
+#endif
+
+#ifdef SQLITE_DEBUG
+/*
+** Assert if an Abort at this point in time might result in a corrupt
+** database.
+*/
+void sqlite3VdbeAssertAbortable(Vdbe *p){
+ assert( p->nWrite==0 || p->usesStmtJournal );
+}
+#endif
+
/*
** This routine is called after all opcodes have been inserted. It loops
** through all the opcodes and fixes up some details.
@@ -701,6 +789,17 @@ void sqlite3VdbeVerifyNoResultRow(Vdbe *p){
#endif
/*
+** Generate code (a single OP_Abortable opcode) that will
+** verify that the VDBE program can safely call Abort in the current
+** context.
+*/
+#if defined(SQLITE_DEBUG)
+void sqlite3VdbeVerifyAbortable(Vdbe *p, int onError){
+ if( onError==OE_Abort ) sqlite3VdbeAddOp0(p, OP_Abortable);
+}
+#endif
+
+/*
** This function returns a pointer to the array of opcodes associated with
** the Vdbe passed as the first argument. It is the callers responsibility
** to arrange for the returned array to be eventually freed using the
@@ -1244,23 +1343,23 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
const char *zOp = 0;
switch( pExpr->op ){
case TK_STRING:
- sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
+ sqlite3_str_appendf(p, "%Q", pExpr->u.zToken);
break;
case TK_INTEGER:
- sqlite3XPrintf(p, "%d", pExpr->u.iValue);
+ sqlite3_str_appendf(p, "%d", pExpr->u.iValue);
break;
case TK_NULL:
- sqlite3XPrintf(p, "NULL");
+ sqlite3_str_appendf(p, "NULL");
break;
case TK_REGISTER: {
- sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
+ sqlite3_str_appendf(p, "r[%d]", pExpr->iTable);
break;
}
case TK_COLUMN: {
if( pExpr->iColumn<0 ){
- sqlite3XPrintf(p, "rowid");
+ sqlite3_str_appendf(p, "rowid");
}else{
- sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
+ sqlite3_str_appendf(p, "c%d", (int)pExpr->iColumn);
}
break;
}
@@ -1292,18 +1391,18 @@ static void displayP4Expr(StrAccum *p, Expr *pExpr){
case TK_NOTNULL: zOp = "NOTNULL"; break;
default:
- sqlite3XPrintf(p, "%s", "expr");
+ sqlite3_str_appendf(p, "%s", "expr");
break;
}
if( zOp ){
- sqlite3XPrintf(p, "%s(", zOp);
+ sqlite3_str_appendf(p, "%s(", zOp);
displayP4Expr(p, pExpr->pLeft);
if( pExpr->pRight ){
- sqlite3StrAccumAppend(p, ",", 1);
+ sqlite3_str_append(p, ",", 1);
displayP4Expr(p, pExpr->pRight);
}
- sqlite3StrAccumAppend(p, ")", 1);
+ sqlite3_str_append(p, ")", 1);
}
}
#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
@@ -1324,14 +1423,15 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
assert( pKeyInfo->aSortOrder!=0 );
- sqlite3XPrintf(&x, "k(%d", pKeyInfo->nKeyField);
+ sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
- sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
+ sqlite3_str_appendf(&x, ",%s%s",
+ pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
}
- sqlite3StrAccumAppend(&x, ")", 1);
+ sqlite3_str_append(&x, ")", 1);
break;
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
@@ -1342,31 +1442,31 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
#endif
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
+ sqlite3_str_appendf(&x, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
FuncDef *pDef = pOp->p4.pFunc;
- sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
case P4_FUNCCTX: {
FuncDef *pDef = pOp->p4.pCtx->pFunc;
- sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3_str_appendf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
#endif
case P4_INT64: {
- sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
+ sqlite3_str_appendf(&x, "%lld", *pOp->p4.pI64);
break;
}
case P4_INT32: {
- sqlite3XPrintf(&x, "%d", pOp->p4.i);
+ sqlite3_str_appendf(&x, "%d", pOp->p4.i);
break;
}
case P4_REAL: {
- sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
+ sqlite3_str_appendf(&x, "%.16g", *pOp->p4.pReal);
break;
}
case P4_MEM: {
@@ -1374,9 +1474,9 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
- sqlite3XPrintf(&x, "%lld", pMem->u.i);
+ sqlite3_str_appendf(&x, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
- sqlite3XPrintf(&x, "%.16g", pMem->u.r);
+ sqlite3_str_appendf(&x, "%.16g", pMem->u.r);
}else if( pMem->flags & MEM_Null ){
zP4 = "NULL";
}else{
@@ -1388,7 +1488,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
#ifndef SQLITE_OMIT_VIRTUALTABLE
case P4_VTAB: {
sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
- sqlite3XPrintf(&x, "vtab:%p", pVtab);
+ sqlite3_str_appendf(&x, "vtab:%p", pVtab);
break;
}
#endif
@@ -1398,14 +1498,14 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
int n = ai[0]; /* The first element of an INTARRAY is always the
** count of the number of elements to follow */
for(i=1; i<=n; i++){
- sqlite3XPrintf(&x, ",%d", ai[i]);
+ sqlite3_str_appendf(&x, ",%d", ai[i]);
}
zTemp[0] = '[';
- sqlite3StrAccumAppend(&x, "]", 1);
+ sqlite3_str_append(&x, "]", 1);
break;
}
case P4_SUBPROGRAM: {
- sqlite3XPrintf(&x, "program");
+ sqlite3_str_appendf(&x, "program");
break;
}
case P4_DYNBLOB:
@@ -1414,7 +1514,7 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
break;
}
case P4_TABLE: {
- sqlite3XPrintf(&x, "%s", pOp->p4.pTab->zName);
+ sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
break;
}
default: {
@@ -1626,6 +1726,9 @@ void sqlite3VdbeFrameDelete(VdbeFrame *p){
** p->explain==2, only OP_Explain instructions are listed and these
** are shown in a different format. p->explain==2 is used to implement
** EXPLAIN QUERY PLAN.
+** 2018-04-24: In p->explain==2 mode, the OP_Init opcodes of triggers
+** are also shown, so that the boundaries between the main program and
+** each trigger are clear.
**
** When p->explain==1, first the main program is listed, then each of
** the trigger subprograms are listed one by one.
@@ -1688,7 +1791,7 @@ int sqlite3VdbeList(
}
}
- do{
+ while(1){ /* Loop exits via break */
i = p->pc++;
if( i>=nRow ){
p->rc = SQLITE_OK;
@@ -1734,7 +1837,10 @@ int sqlite3VdbeList(
nRow += pOp->p4.pProgram->nOp;
}
}
- }while( p->explain==2 && pOp->opcode!=OP_Explain );
+ if( p->explain<2 ) break;
+ if( pOp->opcode==OP_Explain ) break;
+ if( pOp->opcode==OP_Init && p->pc>1 ) break;
+ }
if( rc==SQLITE_OK ){
if( db->u1.isInterrupted ){
@@ -2926,6 +3032,9 @@ int sqlite3VdbeReset(Vdbe *p){
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
p->pResultSet = 0;
+#ifdef SQLITE_DEBUG
+ p->nWrite = 0;
+#endif
/* Save profiling information from this VDBE run.
*/
@@ -3848,13 +3957,10 @@ static int sqlite3IntFloatCompare(i64 i, double r){
i64 y;
double s;
if( r<-9223372036854775808.0 ) return +1;
- if( r>9223372036854775807.0 ) return -1;
+ if( r>=9223372036854775808.0 ) return -1;
y = (i64)r;
if( i<y ) return -1;
- if( i>y ){
- if( y==SMALLEST_INT64 && r>0.0 ) return -1;
- return +1;
- }
+ if( i>y ) return +1;
s = (double)i;
if( s<r ) return -1;
if( s>r ) return +1;
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 8df3c0d..933eee0 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -800,7 +800,7 @@ void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
}
/* A no-op destructor */
-static void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
+void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
/*
** Set the value stored in *pMem should already be a NULL.
@@ -1478,12 +1478,16 @@ static int valueFromExpr(
0, SQLITE_DYNAMIC);
}
#endif
-
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
else if( op==TK_FUNCTION && pCtx!=0 ){
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
}
#endif
+ else if( op==TK_TRUEFALSE ){
+ pVal = valueNew(db, pCtx);
+ pVal->flags = MEM_Int;
+ pVal->u.i = pExpr->u.zToken[4]==0;
+ }
*ppVal = pVal;
return rc;
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index c993d9a..b6d9381 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -93,17 +93,17 @@ char *sqlite3VdbeExpandSql(
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
- sqlite3StrAccumAppend(&out, "-- ", 3);
+ sqlite3_str_append(&out, "-- ", 3);
assert( (zRawSql - zStart) > 0 );
- sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
+ sqlite3_str_append(&out, zStart, (int)(zRawSql-zStart));
}
}else if( p->nVar==0 ){
- sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql));
+ sqlite3_str_append(&out, zRawSql, sqlite3Strlen30(zRawSql));
}else{
while( zRawSql[0] ){
n = findNextHostParameter(zRawSql, &nToken);
assert( n>0 );
- sqlite3StrAccumAppend(&out, zRawSql, n);
+ sqlite3_str_append(&out, zRawSql, n);
zRawSql += n;
assert( zRawSql[0] || nToken==0 );
if( nToken==0 ) break;
@@ -129,11 +129,11 @@ char *sqlite3VdbeExpandSql(
assert( idx>0 && idx<=p->nVar );
pVar = &p->aVar[idx-1];
if( pVar->flags & MEM_Null ){
- sqlite3StrAccumAppend(&out, "NULL", 4);
+ sqlite3_str_append(&out, "NULL", 4);
}else if( pVar->flags & MEM_Int ){
- sqlite3XPrintf(&out, "%lld", pVar->u.i);
+ sqlite3_str_appendf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
+ sqlite3_str_appendf(&out, "%!.15g", pVar->u.r);
}else if( pVar->flags & MEM_Str ){
int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
@@ -143,7 +143,7 @@ char *sqlite3VdbeExpandSql(
utf8.db = db;
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
- out.accError = STRACCUM_NOMEM;
+ out.accError = SQLITE_NOMEM;
out.nAlloc = 0;
}
pVar = &utf8;
@@ -156,38 +156,38 @@ char *sqlite3VdbeExpandSql(
while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
}
#endif
- sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
+ sqlite3_str_appendf(&out, "'%.*q'", nOut, pVar->z);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOut<pVar->n ){
- sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3_str_appendf(&out, "/*+%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);
+ sqlite3_str_appendf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
- sqlite3StrAccumAppend(&out, "x'", 2);
+ sqlite3_str_append(&out, "x'", 2);
nOut = pVar->n;
#ifdef SQLITE_TRACE_SIZE_LIMIT
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);
+ sqlite3_str_appendf(&out, "%02x", pVar->z[i]&0xff);
}
- sqlite3StrAccumAppend(&out, "'", 1);
+ sqlite3_str_append(&out, "'", 1);
#ifdef SQLITE_TRACE_SIZE_LIMIT
if( nOut<pVar->n ){
- sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ sqlite3_str_appendf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
#endif
}
}
}
- if( out.accError ) sqlite3StrAccumReset(&out);
+ if( out.accError ) sqlite3_str_reset(&out);
return sqlite3StrAccumFinish(&out);
}
diff --git a/src/vtab.c b/src/vtab.c
index bc1fa3e..7894bbc 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -1049,9 +1049,6 @@ FuncDef *sqlite3VtabOverloadFunction(
void *pArg = 0;
FuncDef *pNew;
int rc = 0;
- char *zLowerName;
- unsigned char *z;
-
/* Check to see the left operand is a column in a virtual table */
if( NEVER(pExpr==0) ) return pDef;
@@ -1066,16 +1063,22 @@ FuncDef *sqlite3VtabOverloadFunction(
if( pMod->xFindFunction==0 ) return pDef;
/* Call the xFindFunction method on the virtual table implementation
- ** to see if the implementation wants to overload this function
+ ** to see if the implementation wants to overload this function.
+ **
+ ** Though undocumented, we have historically always invoked xFindFunction
+ ** with an all lower-case function name. Continue in this tradition to
+ ** avoid any chance of an incompatibility.
*/
- zLowerName = sqlite3DbStrDup(db, pDef->zName);
- if( zLowerName ){
- for(z=(unsigned char*)zLowerName; *z; z++){
- *z = sqlite3UpperToLower[*z];
+#ifdef SQLITE_DEBUG
+ {
+ int i;
+ for(i=0; pDef->zName[i]; i++){
+ unsigned char x = (unsigned char)pDef->zName[i];
+ assert( x==sqlite3UpperToLower[x] );
}
- rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
- sqlite3DbFree(db, zLowerName);
}
+#endif
+ rc = pMod->xFindFunction(pVtab, nArg, pDef->zName, &xSFunc, &pArg);
if( rc==0 ){
return pDef;
}
diff --git a/src/where.c b/src/where.c
index a1cf743..b83915e 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2420,15 +2420,12 @@ static int whereLoopAddBtreeIndex(
** to mix with a lower range bound from some other source */
if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
- /* Do not allow IS constraints from the WHERE clause to be used by the
+ /* Do not allow constraints from the WHERE clause to be used by the
** right table of a LEFT JOIN. Only constraints in the ON clause are
** allowed */
if( (pSrc->fg.jointype & JT_LEFT)!=0
&& !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
- && (eOp & (WO_IS|WO_ISNULL))!=0
){
- testcase( eOp & WO_IS );
- testcase( eOp & WO_ISNULL );
continue;
}
@@ -2865,14 +2862,16 @@ static int whereLoopAddBtree(
/* TUNING: One-time cost for computing the automatic index is
** estimated to be X*N*log2(N) where N is the number of rows in
** the table being indexed and where X is 7 (LogEst=28) for normal
- ** tables or 1.375 (LogEst=4) for views and subqueries. The value
+ ** tables or 0.5 (LogEst=-10) for views and subqueries. The value
** of X is smaller for views and subqueries so that the query planner
** will be more aggressive about generating automatic indexes for
** those objects, since there is no opportunity to add schema
** indexes on subqueries and views. */
- pNew->rSetup = rLogSize + rSize + 4;
+ pNew->rSetup = rLogSize + rSize;
if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
- pNew->rSetup += 24;
+ pNew->rSetup += 28;
+ }else{
+ pNew->rSetup -= 10;
}
ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
if( pNew->rSetup<0 ) pNew->rSetup = 0;
@@ -4008,12 +4007,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
- if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){
+ if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<3 ){
/* Do not use an automatic index if the this loop is expected
- ** to run less than 2 times. */
+ ** to run less than 1.25 times. It is tempting to also exclude
+ ** automatic index usage on an outer loop, but sometimes an automatic
+ ** index is useful in the outer loop of a correlated subquery. */
assert( 10==sqlite3LogEst(2) );
continue;
}
+
/* At this point, pWLoop is a candidate to be the next loop.
** Compute its cost */
rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
@@ -4595,6 +4597,7 @@ WhereInfo *sqlite3WhereBegin(
if( wctrlFlags & WHERE_WANT_DISTINCT ){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
}else{
/* Assign a bit from the bitmask to every term in the FROM clause.
**
@@ -4990,7 +4993,7 @@ WhereInfo *sqlite3WhereBegin(
}
#endif
addrExplain = sqlite3WhereExplainOneScan(
- pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
+ pParse, pTabList, pLevel, wctrlFlags
);
pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
diff --git a/src/whereInt.h b/src/whereInt.h
index 9092957..4b6213a 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -467,12 +467,10 @@ int sqlite3WhereExplainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
- int iLevel, /* Value for "level" column of output */
- int iFrom, /* Value for "from" column of output */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
);
#else
-# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
+# define sqlite3WhereExplainOneScan(u,v,w,x) 0
#endif /* SQLITE_OMIT_EXPLAIN */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
void sqlite3WhereAddScanStatus(
diff --git a/src/wherecode.c b/src/wherecode.c
index 47fc92f..c9edab7 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -51,23 +51,23 @@ static void explainAppendTerm(
int i;
assert( nTerm>=1 );
- if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+ if( bAnd ) sqlite3_str_append(pStr, " AND ", 5);
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
for(i=0; i<nTerm; i++){
- if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
- sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
+ if( i ) sqlite3_str_append(pStr, ",", 1);
+ sqlite3_str_appendall(pStr, explainIndexColumnName(pIdx, iTerm+i));
}
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
+ if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
- sqlite3StrAccumAppend(pStr, zOp, 1);
+ sqlite3_str_append(pStr, zOp, 1);
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ if( nTerm>1 ) sqlite3_str_append(pStr, "(", 1);
for(i=0; i<nTerm; i++){
- if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
- sqlite3StrAccumAppend(pStr, "?", 1);
+ if( i ) sqlite3_str_append(pStr, ",", 1);
+ sqlite3_str_append(pStr, "?", 1);
}
- if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
+ if( nTerm>1 ) sqlite3_str_append(pStr, ")", 1);
}
/*
@@ -91,11 +91,11 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
int i, j;
if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
- sqlite3StrAccumAppend(pStr, " (", 2);
+ sqlite3_str_append(pStr, " (", 2);
for(i=0; i<nEq; i++){
const char *z = explainIndexColumnName(pIndex, i);
- if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
+ if( i ) sqlite3_str_append(pStr, " AND ", 5);
+ sqlite3_str_appendf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
}
j = i;
@@ -106,7 +106,7 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
- sqlite3StrAccumAppend(pStr, ")", 1);
+ sqlite3_str_append(pStr, ")", 1);
}
/*
@@ -122,8 +122,6 @@ int sqlite3WhereExplainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
- int iLevel, /* Value for "level" column of output */
- int iFrom, /* Value for "from" column of output */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
int ret = 0;
@@ -134,7 +132,6 @@ int sqlite3WhereExplainOneScan(
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
- 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 */
@@ -151,15 +148,15 @@ int sqlite3WhereExplainOneScan(
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
- sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
+ sqlite3_str_appendall(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
- sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
+ sqlite3_str_appendf(&str, " SUBQUERY 0x%p", pItem->pSelect);
}else{
- sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
+ sqlite3_str_appendf(&str, " TABLE %s", pItem->zName);
}
if( pItem->zAlias ){
- sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
+ sqlite3_str_appendf(&str, " AS %s", pItem->zAlias);
}
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
@@ -182,8 +179,8 @@ int sqlite3WhereExplainOneScan(
zFmt = "INDEX %s";
}
if( zFmt ){
- sqlite3StrAccumAppend(&str, " USING ", 7);
- sqlite3XPrintf(&str, zFmt, pIdx->zName);
+ sqlite3_str_append(&str, " USING ", 7);
+ sqlite3_str_appendf(&str, zFmt, pIdx->zName);
explainIndexRange(&str, pLoop);
}
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
@@ -198,23 +195,26 @@ int sqlite3WhereExplainOneScan(
assert( flags&WHERE_TOP_LIMIT);
zRangeOp = "<";
}
- sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+ sqlite3_str_appendf(&str,
+ " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
+ sqlite3_str_appendf(&str, " VIRTUAL TABLE INDEX %d:%s",
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
if( pLoop->nOut>=10 ){
- sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
+ sqlite3_str_appendf(&str, " (~%llu rows)",
+ sqlite3LogEstToInt(pLoop->nOut));
}else{
- sqlite3StrAccumAppend(&str, " (~1 row)", 9);
+ sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
zMsg = sqlite3StrAccumFinish(&str);
- ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
+ ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
+ pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
}
return ret;
}
@@ -1215,6 +1215,9 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** initialize a memory cell that records if this table matches any
** row of the left table of the join.
*/
+ assert( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
+ || pLevel->iFrom>0 || (pTabItem[0].fg.jointype & JT_LEFT)==0
+ );
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
pLevel->iLeftJoin = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
@@ -1748,9 +1751,16 @@ Bitmask sqlite3WhereCodeOneLoopStart(
/* If pIdx is an index on one or more expressions, then look through
** all the expressions in pWInfo and try to transform matching expressions
** into reference to index columns.
+ **
+ ** Do not do this for the RHS of a LEFT JOIN. This is because the
+ ** expression may be evaluated after OP_NullRow has been executed on
+ ** the cursor. In this case it is important to do the full evaluation,
+ ** as the result of the expression may not be NULL, even if all table
+ ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a
*/
- whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
-
+ if( pLevel->iLeftJoin==0 ){
+ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo);
+ }
/* Record the instruction used to terminate the loop. */
if( pLoop->wsFlags & WHERE_ONEROW ){
@@ -1906,7 +1916,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
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;
testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
@@ -1925,13 +1934,17 @@ Bitmask sqlite3WhereCodeOneLoopStart(
** sub-WHERE clause is to to invoke the main loop body as a subroutine.
*/
wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
+ ExplainQueryPlan((pParse, 1, "MULTI-INDEX OR"));
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; /* Current OR clause term */
int jmp1 = 0; /* Address of jump operation */
- if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
+ assert( (pTabItem[0].fg.jointype & JT_LEFT)==0
+ || ExprHasProperty(pOrExpr, EP_FromJoin)
+ );
+ if( pAndExpr ){
pAndExpr->pLeft = pOrExpr;
pOrExpr = pAndExpr;
}
@@ -1943,7 +1956,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
if( pSubWInfo ){
WhereLoop *pSubLoop;
int addrExplain = sqlite3WhereExplainOneScan(
- pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
+ pParse, pOrTab, &pSubWInfo->a[0], 0
);
sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
@@ -2042,6 +2055,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
}
}
+ ExplainQueryPlanPop(pParse);
pLevel->u.pCovidx = pCov;
if( pCov ) pLevel->iIdxCur = iCovCur;
if( pAndExpr ){
@@ -2114,7 +2128,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
}
pE = pTerm->pExpr;
assert( pE!=0 );
- if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
+ if( (pTabItem->fg.jointype&JT_LEFT) && !ExprHasProperty(pE,EP_FromJoin) ){
continue;
}
diff --git a/src/whereexpr.c b/src/whereexpr.c
index 313c5ee..2975008 100644
--- a/src/whereexpr.c
+++ b/src/whereexpr.c
@@ -818,7 +818,6 @@ static void exprAnalyzeOrTerm(
}else{
sqlite3ExprListDelete(db, pList);
}
- pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */
}
}
}
diff --git a/test/analyze3.test b/test/analyze3.test
index 3e721a0..b7b324a 100644
--- a/test/analyze3.test
+++ b/test/analyze3.test
@@ -118,10 +118,10 @@ do_execsql_test analyze3-1.1.x {
#
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<?)}}
+} {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 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
# 2017-06-26: Verify that the SQLITE_DBCONFIG_ENABLE_QPSG setting disables
# the use of bound parameters by STAT4
@@ -131,27 +131,27 @@ unset -nocomplain l
unset -nocomplain u
do_eqp_test analyze3-1.1.3.100 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
set l 200
set u 300
do_eqp_test analyze3-1.1.3.101 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
set l 0
set u 1100
do_eqp_test analyze3-1.1.3.102 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
db cache flush
sqlite3_db_config db ENABLE_QPSG 1
do_eqp_test analyze3-1.1.3.103 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}}
+} {SEARCH TABLE t1 USING INDEX i1 (x>? AND x<?)}
db cache flush
sqlite3_db_config db ENABLE_QPSG 0
do_eqp_test analyze3-1.1.3.104 {
SELECT sum(y) FROM t1 WHERE x>$l AND x<$u
-} {0 0 0 {SCAN TABLE t1}}
+} {SCAN TABLE t1}
do_test analyze3-1.1.4 {
sf_execsql { SELECT sum(y) FROM t1 WHERE x>200 AND x<300 }
@@ -201,10 +201,10 @@ do_execsql_test analyze3-2.1.x {
} {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<?)}}
+} {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 {SCAN TABLE t2}}
+} {SCAN TABLE t2}
do_test analyze3-1.2.4 {
sf_execsql { SELECT sum(y) FROM t2 WHERE x>12 AND x<20 }
@@ -253,10 +253,10 @@ do_execsql_test analyze3-1.3.x {
} {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<?)}}
+} {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 {SCAN TABLE t3}}
+} {SCAN TABLE t3}
do_test analyze3-1.3.4 {
sf_execsql { SELECT sum(y) FROM t3 WHERE x>200 AND x<300 }
@@ -308,10 +308,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<?)}}
+} {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}}
+} {SCAN TABLE t1}
# Return the first argument if like_match_blobs is true (the default)
# or the second argument if not
@@ -698,11 +698,11 @@ do_test analyze3-6.1 {
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=?)}}
+} {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=?)}}
+} {SEARCH TABLE t1 USING INDEX i2 (c=?)}
#-----------------------------------------------------------------------------
# 2015-04-20.
diff --git a/test/analyze4.test b/test/analyze4.test
index 974ed89..9fc98aa 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=?)}}
+} {/*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/analyze6.test b/test/analyze6.test
index 31ace8e..34d00f5 100644
--- a/test/analyze6.test
+++ b/test/analyze6.test
@@ -61,14 +61,18 @@ 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} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}}
+} {/*SCAN TABLE cat USING COVERING INDEX catx*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} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?)}}
+do_eqp_test analyze6-1.2 {
+ SELECT count(*) FROM cat, ev WHERE x=y
+} {
+ QUERY PLAN
+ |--SCAN TABLE cat USING COVERING INDEX catx
+ `--SEARCH TABLE ev USING COVERING INDEX evy (y=?)
+}
# Ticket [83ea97620bd3101645138b7b0e71c12c5498fe3d] 2011-03-30
@@ -82,26 +86,26 @@ do_test analyze6-2.1 {
ANALYZE;
}
eqp {SELECT * FROM t201 WHERE z=5}
-} {0 0 0 {SEARCH TABLE t201 USING INDEX t201z (z=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
do_test analyze6-2.4 {
execsql {
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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
do_test analyze6-2.7 {
execsql {
INSERT INTO t201 VALUES(4,5,7);
@@ -111,12 +115,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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*SEARCH TABLE t201 USING INTEGER PRIMARY KEY (rowid=?)*/}
finish_test
diff --git a/test/analyze7.test b/test/analyze7.test
index 7666454..d6da6c1 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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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,40 +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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+ } {/*SEARCH TABLE t1 USING INDEX t1cd (c=?)*/}
} else {
# 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=?)}}
+ } {/*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=?)}}
+} {/*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=?)}}
+ } {/*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=?)}}
+ } {/*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=?)}}
+} {/*SEARCH TABLE t1 USING INDEX t1cd (c=? AND d=?)*/}
finish_test
diff --git a/test/analyze8.test b/test/analyze8.test
index 1079e68..daa83ef 100644
--- a/test/analyze8.test
+++ b/test/analyze8.test
@@ -61,25 +61,25 @@ 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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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=?)}}
+} {/*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<?)}}
+} {/*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
@@ -99,17 +99,17 @@ do_execsql_test 3.0 {
} {50 376 32}
do_test 3.1 {
eqp {SELECT * FROM t1 WHERE b BETWEEN 30 AND 34 AND c BETWEEN 0 AND 100000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
+} {/*SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)*/}
do_test 3.2 {
eqp {SELECT * FROM t1
WHERE b BETWEEN 30 AND 34 AND c BETWEEN 800000 AND 900000}
-} {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
+} {/*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=?)}}
+} {/*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<?)}}
+} {/*SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)*/}
finish_test
diff --git a/test/analyze9.test b/test/analyze9.test
index 7c69806..75a0265 100644
--- a/test/analyze9.test
+++ b/test/analyze9.test
@@ -987,7 +987,8 @@ do_eqp_test 21.3 {
reset_db
do_execsql_test 22.0 {
CREATE TABLE t3(a, b, c, d, PRIMARY KEY(a, b)) WITHOUT ROWID;
-}
+ SELECT * FROM t3;
+} {}
do_execsql_test 22.1 {
WITH r(x) AS (
SELECT 1
@@ -1055,15 +1056,11 @@ do_eqp_test 23.1 {
-- Formerly used index i41. But i41 is not a covering index whereas
-- the PRIMARY KEY is a covering index, and so as of 2017-10-15, the
-- PRIMARY KEY is preferred.
-} {
- 0 0 0 {SEARCH TABLE t4 USING PRIMARY KEY (c=? AND b=? AND a<?)}
-}
+} {SEARCH TABLE t4 USING PRIMARY KEY (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<?)}
-}
+} {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;
@@ -1108,33 +1105,26 @@ ifcapable stat4&&cte {
# Term (b<?) is estimated at 25%. Better than (a<30) but not as
# good as (a<20).
- do_eqp_test 25.2.1 { SELECT * FROM t6 WHERE a<30 AND b<? } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b<?)}
- }
- do_eqp_test 25.2.2 { SELECT * FROM t6 WHERE a<20 AND b<? } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX aa (a<?)}
- }
+ do_eqp_test 25.2.1 { SELECT * FROM t6 WHERE a<30 AND b<? } \
+ {SEARCH TABLE t6 USING INDEX bb (b<?)}
+ do_eqp_test 25.2.2 { SELECT * FROM t6 WHERE a<20 AND b<? } \
+ {SEARCH TABLE t6 USING INDEX aa (a<?)}
# Term (b BETWEEN ? AND ?) is estimated at 1/64.
do_eqp_test 25.3.1 {
SELECT * FROM t6 WHERE a BETWEEN 5 AND 10 AND b BETWEEN ? AND ?
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
# Term (b BETWEEN ? AND 60) is estimated to return roughly 15 rows -
# 60 from (b<=60) multiplied by 0.25 for the b>=? term. Better than
# (a<20) but not as good as (a<10).
do_eqp_test 25.4.1 {
SELECT * FROM t6 WHERE a < 10 AND (b BETWEEN ? AND 60)
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX aa (a<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX aa (a<?)}
+
do_eqp_test 25.4.2 {
SELECT * FROM t6 WHERE a < 20 AND (b BETWEEN ? AND 60)
- } {
- 0 0 0 {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
- }
+ } {SEARCH TABLE t6 USING INDEX bb (b>? AND b<?)}
}
#-------------------------------------------------------------------------
@@ -1190,9 +1180,7 @@ do_execsql_test 26.1.3 {
#
do_eqp_test 26.1.4 {
SELECT * FROM t1 WHERE x = 10000 AND y < 50 AND z = 444;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1z (z=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1z (z=?)}
# This test - 26.2.* - tests that another manifestation of the same problem
@@ -1241,9 +1229,7 @@ do_execsql_test 26.2.1 {
do_eqp_test 26.2.2 {
SELECT * FROM t1 WHERE x='B' AND y>25 AND z=?;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX i1 (x=? AND y>?)}
-}
+} {SEARCH TABLE t1 USING INDEX i1 (x=? AND y>?)}
finish_test
diff --git a/test/analyzeA.test b/test/analyzeA.test
index a2da10e..afcbe84 100644
--- a/test/analyzeA.test
+++ b/test/analyzeA.test
@@ -136,10 +136,10 @@ foreach {tn analyze_cmd} {
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=?)}}
+ } {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=?)}}
+ } {SEARCH TABLE t1 USING INDEX t1c (c=?)}
do_execsql_test 1.$tn.3.1 {
SELECT count(*) FROM t1 WHERE b BETWEEN 0 AND 50
@@ -156,31 +156,31 @@ foreach {tn analyze_cmd} {
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<?)}}
+ } {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<?)}}
+ } {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}
do_eqp_test 1.$tn.3.7 {
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<?)}}
+ } {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}
do_eqp_test 1.$tn.3.8 {
SELECT * FROM t1
WHERE b BETWEEN cast('0' AS int) AND cast('50.0' AS real)
AND c BETWEEN cast('0' AS numeric) AND cast('50.0' AS real)
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}}
+ } {SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)}
do_eqp_test 1.$tn.3.9 {
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<?)}}
+ } {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}
do_eqp_test 1.$tn.3.10 {
SELECT * FROM t1
WHERE b BETWEEN cast('75' AS int) AND cast('125.0' AS real)
AND c BETWEEN cast('75' AS numeric) AND cast('125.0' AS real)
- } {0 0 0 {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}}
+ } {SEARCH TABLE t1 USING INDEX t1c (c>? AND c<?)}
}
finish_test
diff --git a/test/analyzeD.test b/test/analyzeD.test
index 4588aae..9c7ed75 100644
--- a/test/analyzeD.test
+++ b/test/analyzeD.test
@@ -63,9 +63,7 @@ do_test 1.1 {
#
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a=3001 AND c=150;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
do_test 1.3 {
execsql { DELETE FROM sqlite_stat1 }
@@ -80,9 +78,7 @@ do_test 1.3 {
#
do_eqp_test 1.4 {
SELECT * FROM t1 WHERE a=3001 AND c=150;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1_ab (a=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1_ab (a=?)}
do_test 1.5 {
execsql {
@@ -93,9 +89,7 @@ do_test 1.5 {
do_eqp_test 1.6 {
SELECT * FROM t1 WHERE a=13 AND c=150;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
do_test 1.7 {
execsql { DELETE FROM sqlite_stat1 }
@@ -108,8 +102,6 @@ do_test 1.7 {
# gets this right, even without stat1 data.
do_eqp_test 1.8 {
SELECT * FROM t1 WHERE a=13 AND c=150;
-} {
- 0 0 0 {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
-}
+} {SEARCH TABLE t1 USING INDEX t1_c (c=?)}
finish_test
diff --git a/test/analyzeF.test b/test/analyzeF.test
index 76891dd..143cecd 100644
--- a/test/analyzeF.test
+++ b/test/analyzeF.test
@@ -62,7 +62,7 @@ foreach {tn where idx} {
11 "x = nullif('19', 0) AND y = nullif('4', 0)" {t1y (y=?)}
12 "x = nullif('4', 0) AND y = nullif('19', 0)" {t1y (y=?)}
} {
- set res "0 0 0 {SEARCH TABLE t1 USING INDEX $idx}"
+ set res "SEARCH TABLE t1 USING INDEX $idx"
do_eqp_test 1.$tn "SELECT * FROM t1 WHERE $where" $res
}
@@ -92,7 +92,7 @@ foreach {tn where idx} {
3 "x = nondet4() AND y = nondet19()" {t1y (y=?)}
4 "x = nondet19() AND y = nondet4()" {t1y (y=?)}
} {
- set res "0 0 0 {SEARCH TABLE t1 USING INDEX $idx}"
+ set res "SEARCH TABLE t1 USING INDEX $idx"
do_eqp_test 3.$tn "SELECT * FROM t1 WHERE $where" $res
}
diff --git a/test/autoinc.test b/test/autoinc.test
index 8ac12b1..75b16da 100644
--- a/test/autoinc.test
+++ b/test/autoinc.test
@@ -25,6 +25,11 @@ ifcapable {!autoinc} {
return
}
+if {[permutation]=="inmemory_journal"} {
+ finish_test
+ return
+}
+
sqlite3_db_config_lookaside db 0 0 0
# The database is initially empty.
@@ -675,6 +680,173 @@ do_execsql_test autoinc-10.1 {
SELECT * FROM sqlite_sequence;
} {t10a 888 t10b 888}
+# 2018-04-21 autoincrement does not cause problems for upsert
+#
+do_execsql_test autoinc-11.1 {
+ CREATE TABLE t11(a INTEGER PRIMARY KEY AUTOINCREMENT,b UNIQUE);
+ INSERT INTO t11(a,b) VALUES(2,3),(5,6),(4,3),(1,2)
+ ON CONFLICT(b) DO UPDATE SET a=a+1000;
+ SELECT seq FROM sqlite_sequence WHERE name='t11';
+} {5}
+# 2018-05-23 ticket d8dc2b3a58cd5dc2918a1d4acbba4676a23ada4c
+# Does not crash if the sqlite_sequence table schema is missing
+# or corrupt.
+#
+do_test autoinc-12.1 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE fake_sequence(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql=replace(sql,'fake_','sqlite_'),
+ name='sqlite_sequence',
+ tbl_name='sqlite_sequence'
+ WHERE name='fake_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ }} msg]
+ lappend res $msg
+} {1 {database disk image is malformed}}
+do_test autoinc-12.2 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql=replace(sql,'sqlite_','x_'),
+ name='x_sequence',
+ tbl_name='x_sequence'
+ WHERE name='sqlite_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two');
+ }} msg]
+ lappend res $msg
+} {1 {database disk image is malformed}}
+ifcapable vtab {
+ set err "database disk image is malformed"
+} else {
+ set err {malformed database schema (sqlite_sequence) - near "VIRTUAL": syntax error}
+}
+do_test autoinc-12.3 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql='CREATE VIRTUAL TABLE sqlite_sequence USING sqlite_dbpage'
+ WHERE name='sqlite_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two');
+ }} msg]
+ lappend res $msg
+} [list 1 $err]
+do_test autoinc-12.4 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ CREATE TABLE fake(name TEXT PRIMARY KEY,seq) WITHOUT ROWID;
+ }
+ set root1 [db one {SELECT rootpage FROM sqlite_master
+ WHERE name='sqlite_sequence'}]
+ set root2 [db one {SELECT rootpage FROM sqlite_master
+ WHERE name='fake'}]
+ db eval {
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET rootpage=$root2
+ WHERE name='sqlite_sequence';
+ UPDATE sqlite_master SET rootpage=$root1
+ WHERE name='fake';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two');
+ }} msg]
+ lappend res $msg
+} {1 {database disk image is malformed}}
+breakpoint
+do_test autoinc-12.5 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql='CREATE TABLE sqlite_sequence(x)'
+ WHERE name='sqlite_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two');
+ }} msg]
+ lappend res $msg
+} {1 {database disk image is malformed}}
+do_test autoinc-12.6 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql='CREATE TABLE sqlite_sequence(x,y INTEGER PRIMARY KEY)'
+ WHERE name='sqlite_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two'),('three'),('four');
+ INSERT INTO t1(b) VALUES('five');
+ PRAGMA integrity_check;
+ }} msg]
+ lappend res $msg
+} {0 ok}
+do_test autoinc-12.7 {
+ db close
+ forcedelete test.db
+ sqlite3 db test.db
+ db eval {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);
+ INSERT INTO t1(b) VALUES('one');
+ PRAGMA writable_schema=on;
+ UPDATE sqlite_master SET
+ sql='CREATE TABLE sqlite_sequence(y INTEGER PRIMARY KEY,x)'
+ WHERE name='sqlite_sequence';
+ }
+ db close
+ sqlite3 db test.db
+ set res [catch {db eval {
+ INSERT INTO t1(b) VALUES('two'),('three'),('four');
+ INSERT INTO t1(b) VALUES('five');
+ PRAGMA integrity_check;
+ }} msg]
+ lappend res $msg
+} {0 ok}
finish_test
diff --git a/test/autoindex1.test b/test/autoindex1.test
index bf9eca8..1978b8d 100644
--- a/test/autoindex1.test
+++ b/test/autoindex1.test
@@ -177,35 +177,36 @@ do_execsql_test autoindex1-500 {
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
+}
+do_eqp_test autoindex1-500.1 {
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=?)}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t502}
+ QUERY PLAN
+ |--SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)
+ `--LIST SUBQUERY
+ `--SCAN TABLE t502
}
-do_execsql_test autoindex1-501 {
- EXPLAIN QUERY PLAN
+do_eqp_test autoindex1-501 {
SELECT b FROM t501
WHERE t501.a IN (SELECT x FROM t502 WHERE y=t501.b);
} {
- 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=?)}
+ QUERY PLAN
+ |--SCAN TABLE t501
+ `--CORRELATED LIST SUBQUERY
+ `--SEARCH TABLE t502 USING AUTOMATIC COVERING INDEX (y=?)
}
-do_execsql_test autoindex1-502 {
- EXPLAIN QUERY PLAN
+do_eqp_test autoindex1-502 {
SELECT b FROM t501
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=?)}
- 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t502}
+ QUERY PLAN
+ |--SEARCH TABLE t501 USING INTEGER PRIMARY KEY (rowid=?)
+ `--CORRELATED LIST SUBQUERY
+ `--SCAN TABLE t502
}
-
# The following code checks a performance regression reported on the
# mailing list on 2010-10-19. The problem is that the nRowEst field
# of ephermeral tables was not being initialized correctly and so no
@@ -257,7 +258,8 @@ do_execsql_test autoindex1-600 {
ON sheep (originating_flock);
CREATE INDEX sheep_reg_flock_index
ON sheep (registering_flock);
- EXPLAIN QUERY PLAN
+}
+do_eqp_test autoindex1-600a {
SELECT x.sheep_no, x.registering_flock, x.date_of_registration
FROM sheep x LEFT JOIN
(SELECT s.sheep_no, prev.flock_no, prev.owner_person_id,
@@ -274,21 +276,26 @@ do_execsql_test autoindex1-600 {
WHERE y.sheep_no IS NULL
ORDER BY x.registering_flock;
} {
- 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<?)}
- 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=?)}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--SCAN TABLE sheep AS s
+ | |--SEARCH TABLE flock_owner AS prev USING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date<?)
+ | `--CORRELATED SCALAR SUBQUERY
+ | `--SEARCH TABLE flock_owner AS later USING COVERING INDEX sqlite_autoindex_flock_owner_1 (flock_no=? AND owner_change_date>? AND owner_change_date<?)
+ |--SCAN TABLE sheep AS x USING INDEX sheep_reg_flock_index
+ `--SEARCH SUBQUERY xxxxxx AS y USING AUTOMATIC COVERING INDEX (sheep_no=?)
}
do_execsql_test autoindex1-700 {
CREATE TABLE t5(a, b, c);
- EXPLAIN QUERY PLAN SELECT a FROM t5 WHERE b=10 ORDER BY c;
+}
+do_eqp_test autoindex1-700a {
+ SELECT a FROM t5 WHERE b=10 ORDER BY c;
} {
- 0 0 0 {SCAN TABLE t5}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t5
+ `--USE TEMP B-TREE FOR ORDER BY
}
# The following checks a performance issue reported on the sqlite-dev
diff --git a/test/autoindex3.test b/test/autoindex3.test
index c99a175..9dcf249 100644
--- a/test/autoindex3.test
+++ b/test/autoindex3.test
@@ -84,8 +84,9 @@ do_execsql_test 210 {
do_eqp_test 220 {
select count(*) from u, v where u.b = v.b and v.e > 34;
} {
- 0 0 1 {SEARCH TABLE v USING INDEX ve (e>?)}
- 0 1 0 {SEARCH TABLE u USING AUTOMATIC COVERING INDEX (b=?)}
+ QUERY PLAN
+ |--SEARCH TABLE v USING INDEX ve (e>?)
+ `--SEARCH TABLE u USING AUTOMATIC COVERING INDEX (b=?)
}
diff --git a/test/autoindex5.test b/test/autoindex5.test
index 649ae12..aba546a 100644
--- a/test/autoindex5.test
+++ b/test/autoindex5.test
@@ -84,8 +84,7 @@ do_execsql_test autoindex5-1.0 {
# The following query should use an automatic index for the view
# in FROM clause of the subquery of the second result column.
#
-do_execsql_test autoindex5-1.1 {
- EXPLAIN QUERY PLAN
+do_eqp_test autoindex5-1.1 {
SELECT
st.bug_name,
(SELECT ALL debian_cve.bug FROM debian_cve
@@ -103,7 +102,7 @@ do_execsql_test autoindex5-1.1 {
AND ( sp.release = 'sid' OR sp.release = 'stretch' OR sp.release = 'jessie'
OR sp.release = 'wheezy' OR sp.release = 'squeeze' )
ORDER BY sp.name, st.bug_name, sp.release, sp.subrelease;
-} {/SEARCH SUBQUERY 2 USING AUTOMATIC COVERING INDEX .bug_name=/}
+} {SEARCH SUBQUERY * USING AUTOMATIC COVERING INDEX (bug_name=?)}
#-------------------------------------------------------------------------
# Test that ticket [8a2adec1] has been fixed.
diff --git a/test/bestindex1.test b/test/bestindex1.test
index f90f96b..1d79c73 100644
--- a/test/bestindex1.test
+++ b/test/bestindex1.test
@@ -51,16 +51,11 @@ do_execsql_test 1.0 {
do_eqp_test 1.1 {
SELECT * FROM x1 WHERE a = 'abc'
-} {
- 0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
-}
+} {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
do_eqp_test 1.2 {
SELECT * FROM x1 WHERE a IN ('abc', 'def');
-} {
- 0 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
-}
+} {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:eq!}
#-------------------------------------------------------------------------
#
@@ -145,24 +140,24 @@ foreach {tn mode} {
} {1 4}
set plan(use) {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'
+ `--USE TEMP B-TREE FOR ORDER BY
}
set plan(omit) {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x WHERE a='%1%'
+ `--USE TEMP B-TREE FOR ORDER BY
}
set plan(use2) {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:SELECT * FROM t1x
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 2.2.$mode.6 {
SELECT rowid FROM t1 WHERE a IN ('one', 'four') ORDER BY +rowid
- } $plan($mode)
+ } [string map {"\n " "\n"} $plan($mode)]
}
# 2016-04-09.
diff --git a/test/bestindex2.test b/test/bestindex2.test
index 8bc3fbc..c17665f 100644
--- a/test/bestindex2.test
+++ b/test/bestindex2.test
@@ -89,40 +89,40 @@ do_execsql_test 1.0 {
do_eqp_test 1.1 {
SELECT * FROM t1 WHERE a='abc'
-} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
-}
+} {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
+
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a='abc' AND b='def'
-} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=? AND b=?)}
-}
+} {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=? AND b=?)}
+
do_eqp_test 1.3 {
SELECT * FROM t1 WHERE a='abc' AND a='def'
-} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
-}
+} {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:indexed(a=?)}
+
do_eqp_test 1.4 {
SELECT * FROM t1,t2 WHERE c=a
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
- 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:
+ `--SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
}
do_eqp_test 1.5 {
SELECT * FROM t1, t2 CROSS JOIN t3 WHERE t2.c = +t1.b AND t3.e=t2.d
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
- 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
- 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:
+ |--SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
+ `--SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
do_eqp_test 1.6 {
SELECT * FROM t1, t2, t3 WHERE t2.c = +t1.b AND t3.e = t2.d
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
- 0 1 1 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
- 0 2 2 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+ QUERY PLAN
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:
+ |--SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
+ `--SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
do_execsql_test 1.7.1 {
@@ -132,10 +132,11 @@ do_eqp_test 1.7.2 {
SELECT * FROM x1 CROSS JOIN t1, t2, t3
WHERE t1.a = t2.c AND t1.b = t3.e
} {
- 0 0 0 {SCAN TABLE x1}
- 0 1 1 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:}
- 0 2 2 {SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)}
- 0 3 3 {SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)}
+ QUERY PLAN
+ |--SCAN TABLE x1
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:
+ |--SCAN TABLE t2 VIRTUAL TABLE INDEX 0:indexed(c=?)
+ `--SCAN TABLE t3 VIRTUAL TABLE INDEX 0:indexed(e=?)
}
finish_test
diff --git a/test/bestindex3.test b/test/bestindex3.test
index 70c60d6..4b125d4 100644
--- a/test/bestindex3.test
+++ b/test/bestindex3.test
@@ -79,28 +79,28 @@ do_execsql_test 1.0 {
do_eqp_test 1.1 {
SELECT * FROM t1 WHERE a LIKE 'abc';
-} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?}
-}
+} {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?}
do_eqp_test 1.2 {
SELECT * FROM t1 WHERE a = 'abc';
-} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?}
-}
+} {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?}
do_eqp_test 1.3 {
SELECT * FROM t1 WHERE a = 'abc' OR b = 'def';
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?}
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?}
+ QUERY PLAN
+ `--MULTI-INDEX OR
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a EQ ?
+ `--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?
}
do_eqp_test 1.4 {
SELECT * FROM t1 WHERE a LIKE 'abc%' OR b = 'def';
} {
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?}
- 0 0 0 {SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?}
+ QUERY PLAN
+ `--MULTI-INDEX OR
+ |--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:a LIKE ?
+ `--SCAN TABLE t1 VIRTUAL TABLE INDEX 0:b EQ ?
}
do_execsql_test 1.5 {
@@ -147,10 +147,12 @@ ifcapable !icu {
do_eqp_test 2.2 {
SELECT * FROM t2 WHERE x LIKE 'abc%' OR y = 'def'
- } {
- 0 0 0 {SEARCH TABLE t2 USING INDEX t2x (x>? AND x<?)}
- 0 0 0 {SEARCH TABLE t2 USING INDEX t2y (y=?)}
- }
+ } [string map {"\n " \n} {
+ QUERY PLAN
+ `--MULTI-INDEX OR
+ |--SEARCH TABLE t2 USING INDEX t2x (x>? AND x<?)
+ `--SEARCH TABLE t2 USING INDEX t2y (y=?)
+ }]
}
#-------------------------------------------------------------------------
diff --git a/test/bigmmap.test b/test/bigmmap.test
index 9284bda..7da1df2 100644
--- a/test/bigmmap.test
+++ b/test/bigmmap.test
@@ -92,13 +92,13 @@ for {set i 0} {$i < 9} {incr i} {
SELECT * FROM t$t AS o WHERE
NOT EXISTS( SELECT * FROM t$t AS i WHERE a=o.a AND +b=o.b AND +c=o.c )
ORDER BY b, c;
- " "
- 0 0 0 {SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1}
- 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
- 1 0 0 {SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)}
- "
+ " [string map {"\n " "\n"} "
+ QUERY PLAN
+ |--SCAN TABLE t$t AS o USING COVERING INDEX sqlite_autoindex_t${t}_1
+ `--CORRELATED SCALAR SUBQUERY
+ `--SEARCH TABLE t$t AS i USING INTEGER PRIMARY KEY (rowid=?)
+ "]
}
}
finish_test
-
diff --git a/test/closure01.test b/test/closure01.test
index 213ef4e..ee3f2dd 100644
--- a/test/closure01.test
+++ b/test/closure01.test
@@ -273,4 +273,23 @@ do_execsql_test 5.1 {
ORDER BY id;
} {8 9 10 11 12 13 14 15}
+#-------------------------------------------------------------------------
+# At one point the following join query was causing a malfunction in
+# xBestIndex.
+#
+do_execsql_test 6.0 {
+ CREATE TABLE t4 (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ parent_id INTEGER
+ );
+ CREATE VIRTUAL TABLE vt4 USING transitive_closure (
+ idcolumn=id, parentcolumn=parent_id, tablename=t4
+ );
+}
+
+do_execsql_test 6.1 {
+ SELECT * FROM t4, vt4 WHERE t4.id = vt4.root AND vt4.id=4 AND vt4.depth=2;
+}
+
finish_test
diff --git a/test/cost.test b/test/cost.test
index 9c10d82..5d23e0e 100644
--- a/test/cost.test
+++ b/test/cost.test
@@ -24,8 +24,9 @@ do_execsql_test 1.1 {
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=?)}
+ QUERY PLAN
+ |--SCAN TABLE t3 USING COVERING INDEX i3
+ `--SEARCH TABLE t4 USING INDEX i4 (c=?)
}
@@ -38,9 +39,7 @@ do_execsql_test 2.1 {
# 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}
-}
+} {SCAN TABLE t1 USING INDEX i1}
do_execsql_test 3.1 {
CREATE TABLE t5(a INTEGER PRIMARY KEY,b,c,d,e,f,g);
@@ -57,10 +56,12 @@ do_eqp_test 3.2 {
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}
+ QUERY PLAN
+ |--MULTI-INDEX OR
+ | |--SEARCH TABLE t5 USING INDEX t5b (b=?)
+ | |--SEARCH TABLE t5 USING INDEX t5c (c=?)
+ | `--SEARCH TABLE t5 USING INDEX t5d (d=?)
+ `--USE TEMP B-TREE FOR ORDER BY
}
#-------------------------------------------------------------------------
@@ -79,14 +80,11 @@ do_execsql_test 4.1 {
}
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=?)}
-}
+} {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<?)}
-}
+} {SEARCH TABLE t1 USING INDEX i2 (b>? AND b<?)}
#-------------------------------------------------------------------------
@@ -100,15 +98,17 @@ do_execsql_test 5.1 {
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}
+ QUERY PLAN
+ |--SCAN TABLE t2 USING INDEX t2i1
+ `--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}
+ QUERY PLAN
+ |--SEARCH TABLE t2 USING INDEX t2i1 (x>? AND x<?)
+ `--USE TEMP B-TREE FOR ORDER BY
}
# where7.test, where8.test:
@@ -122,9 +122,11 @@ do_execsql_test 6.1 {
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}
+ QUERY PLAN
+ |--MULTI-INDEX OR
+ | |--SEARCH TABLE t3 USING INDEX t3i1 (b>? AND b<?)
+ | `--SEARCH TABLE t3 USING INDEX t3i2 (c=?)
+ `--USE TEMP B-TREE FOR ORDER BY
}
#-------------------------------------------------------------------------
@@ -145,9 +147,11 @@ do_eqp_test 7.2 {
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}
+ QUERY PLAN
+ |--MULTI-INDEX OR
+ | |--SEARCH TABLE t1 USING INDEX t1b (b>? AND b<?)
+ | `--SEARCH TABLE t1 USING INDEX t1b (b=?)
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 7.3 {
@@ -155,15 +159,11 @@ do_eqp_test 7.3 {
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}
-}
+} {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}
-}
+} {SCAN TABLE t1}
#-------------------------------------------------------------------------
#
@@ -194,10 +194,11 @@ do_eqp_test 8.2 {
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}
+ QUERY PLAN
+ |--SCAN TABLE track
+ |--SEARCH TABLE album USING INTEGER PRIMARY KEY (rowid=?)
+ |--SEARCH TABLE composer USING INTEGER PRIMARY KEY (rowid=?)
+ `--USE TEMP B-TREE FOR DISTINCT
}
#-------------------------------------------------------------------------
@@ -263,27 +264,19 @@ ifcapable stat4 {
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=?)}
- }
+ } {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=?)}
- }
+ } {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=?)}
- }
+ } {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=?)}
- }
+ } {SEARCH TABLE t6 USING INDEX t6i1 (a=? AND b=?)}
}
finish_test
diff --git a/test/coveridxscan.test b/test/coveridxscan.test
index a8d1a8f..cc98644 100644
--- a/test/coveridxscan.test
+++ b/test/coveridxscan.test
@@ -109,16 +109,11 @@ do_execsql_test 5.1.0 "
do_eqp_test 5.1.1 {
SELECT * FROM t1 ORDER BY c1, c2;
-} {
- 0 0 0 {SCAN TABLE t1 USING COVERING INDEX i1}
-}
+} {SCAN TABLE t1 USING COVERING INDEX i1}
do_eqp_test 5.1.2 {
SELECT * FROM t2 ORDER BY c1, c2;
-} {
- 0 0 0 {SCAN TABLE t2 USING COVERING INDEX i2}
-}
-
+} {SCAN TABLE t2 USING COVERING INDEX i2}
finish_test
diff --git a/test/csv01.test b/test/csv01.test
index 2f45f24..f30f7ac 100644
--- a/test/csv01.test
+++ b/test/csv01.test
@@ -140,5 +140,21 @@ do_catchsql_test 4.2 {
);
} {1 {vtable constructor failed: t5}}
+# 2018-04-24
+# Memory leak reported on the sqlite-users mailing list by Ralf Junker.
+#
+do_catchsql_test 4.3 {
+ CREATE VIRTUAL TABLE IF NOT EXISTS temp.t1
+ USING csv(filename='FileDoesNotExist.csv');
+} {1 {cannot open 'FileDoesNotExist.csv' for reading}}
+
+# 2018-06-02
+# Problem with single-column CSV support reported on the mailing list
+# by Trent W. Buck.
+#
+do_execsql_test 4.4 {
+ CREATE VIRTUAL TABLE temp.trent USING csv(data='1');
+ SELECT * FROM trent;
+} {1}
finish_test
diff --git a/test/delete.test b/test/delete.test
index 8e84ed4..a448e52 100644
--- a/test/delete.test
+++ b/test/delete.test
@@ -403,5 +403,17 @@ do_execsql_test delete-10.2 {
SELECT * FROM t1 WHERE a='1' AND b='2';
}
+do_execsql_test delete-11.0 {
+ CREATE TABLE t11(a INTEGER PRIMARY KEY, b INT);
+ WITH RECURSIVE cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<20)
+ INSERT INTO t11(a,b) SELECT x, (x*17)%100 FROM cnt;
+ SELECT * FROM t11;
+} {1 17 2 34 3 51 4 68 5 85 6 2 7 19 8 36 9 53 10 70 11 87 12 4 13 21 14 38 15 55 16 72 17 89 18 6 19 23 20 40}
+do_execsql_test delete-11.1 {
+ DELETE FROM t11 AS xyz
+ WHERE EXISTS(SELECT 1 FROM t11 WHERE t11.a>xyz.a AND t11.b<=xyz.b);
+ SELECT * FROM t11;
+} {6 2 12 4 18 6 19 23 20 40}
+
finish_test
diff --git a/test/e_createtable.test b/test/e_createtable.test
index f07fbb9..65cb7f2 100644
--- a/test/e_createtable.test
+++ b/test/e_createtable.test
@@ -656,11 +656,11 @@ do_createtable_tests 2.1 -tclquery {
5 "CREATE TABLE x1 AS SELECT count(a) AS a, max(b) FROM t1" {a max(b)}
}
-# EVIDENCE-OF: R-37111-22855 The declared type of each column is
+# EVIDENCE-OF: R-55407-45319 The declared type of each column is
# determined by the expression affinity of the corresponding expression
# in the result set of the SELECT statement, as follows: Expression
# Affinity Column Declared Type TEXT "TEXT" NUMERIC "NUM" INTEGER "INT"
-# REAL "REAL" NONE "" (empty string)
+# REAL "REAL" BLOB (a.k.a "NONE") "" (empty string)
#
do_createtable_tests 2.2 -tclquery {
table_column_decltypes x1
@@ -1385,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=?)}}
+ {/*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}}
+ {/*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>?)}}
+ {/*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
diff --git a/test/e_fkey.test b/test/e_fkey.test
index 33e7fed..740078c 100644
--- a/test/e_fkey.test
+++ b/test/e_fkey.test
@@ -24,7 +24,24 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
-proc eqp {sql {db db}} { uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db }
+proc eqp {sql {db db}} {
+ uplevel [subst -nocommands {
+ set eqpres [list]
+ $db eval "$sql" {
+ lappend eqpres [set detail]
+ }
+ set eqpres
+ }]
+}
+
+proc do_detail_test {tn sql res} {
+ set normalres [list {*}$res]
+ uplevel [subst -nocommands {
+ do_test $tn {
+ eqp { $sql }
+ } {$normalres}
+ }]
+}
###########################################################################
### SECTION 2: Enabling Foreign Key Support
@@ -970,20 +987,20 @@ do_test e_fkey-25.1 {
);
}
} {}
-do_execsql_test e_fkey-25.2 {
+do_detail_test e_fkey-25.2 {
PRAGMA foreign_keys = OFF;
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
EXPLAIN QUERY PLAN SELECT rowid FROM track WHERE trackartist = ?;
} {
- 0 0 0 {SCAN TABLE artist}
- 0 0 0 {SCAN TABLE track}
+ {SCAN TABLE artist}
+ {SCAN TABLE track}
}
-do_execsql_test e_fkey-25.3 {
+do_detail_test e_fkey-25.3 {
PRAGMA foreign_keys = ON;
EXPLAIN QUERY PLAN DELETE FROM artist WHERE 1;
} {
- 0 0 0 {SCAN TABLE artist}
- 0 0 0 {SCAN TABLE track}
+ {SCAN TABLE artist}
+ {SCAN TABLE track}
}
do_test e_fkey-25.4 {
execsql {
@@ -1097,21 +1114,20 @@ do_test e_fkey-27.1 {
do_test e_fkey-27.2 {
eqp { INSERT INTO artist VALUES(?, ?) }
} {}
-do_execsql_test e_fkey-27.3 {
+do_detail_test e_fkey-27.3 {
EXPLAIN QUERY PLAN UPDATE artist SET artistid = ?, artistname = ?
} {
- 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=?)}
+ {SCAN TABLE artist}
+ {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
+ {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
}
-do_execsql_test e_fkey-27.4 {
+do_detail_test e_fkey-27.4 {
EXPLAIN QUERY PLAN DELETE FROM artist
} {
- 0 0 0 {SCAN TABLE artist}
- 0 0 0 {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
+ {SCAN TABLE artist}
+ {SEARCH TABLE track USING COVERING INDEX trackindex (trackartist=?)}
}
-
###########################################################################
### SECTION 4.1: Composite Foreign Key Constraints
###########################################################################
diff --git a/test/eqp.test b/test/eqp.test
index c955a80..5f397ef 100644
--- a/test/eqp.test
+++ b/test/eqp.test
@@ -43,78 +43,110 @@ do_execsql_test 1.1 {
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=?)}
- 0 0 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
- 0 1 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--MULTI-INDEX OR
+ | |--SEARCH TABLE t1 USING INDEX i1 (a=?)
+ | `--SEARCH TABLE t1 USING INDEX i2 (b=?)
+ `--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}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i1 (a=?)}
- 0 1 1 {SEARCH TABLE t1 USING INDEX i2 (b=?)}
+ QUERY PLAN
+ |--SCAN TABLE t2
+ `--MULTI-INDEX OR
+ |--SEARCH TABLE t1 USING INDEX i1 (a=?)
+ `--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}
+ QUERY PLAN
+ `--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}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1 USING COVERING INDEX i1
+ `--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=?)}
+ QUERY PLAN
+ `--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}
- 0 0 0 {USE TEMP B-TREE FOR GROUP BY}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t3
+ |--USE TEMP B-TREE FOR GROUP BY
+ `--USE TEMP B-TREE FOR DISTINCT
}
do_eqp_test 1.7 {
SELECT * FROM t3 JOIN (SELECT 1)
} {
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--SCAN CONSTANT ROW
+ |--SCAN SUBQUERY xxxxxx
+ `--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}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | | `--SCAN CONSTANT ROW
+ | `--UNION USING TEMP B-TREE
+ | `--SCAN CONSTANT ROW
+ |--SCAN SUBQUERY xxxxxx
+ `--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}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (EXCEPT)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | | `--SCAN CONSTANT ROW
+ | `--EXCEPT USING TEMP B-TREE
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--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}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 USING TEMP B-TREE (INTERSECT)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | | `--SCAN CONSTANT ROW
+ | `--INTERSECT USING TEMP B-TREE
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--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}
- 1 0 0 {COMPOUND SUBQUERIES 2 AND 3 (UNION ALL)}
- 0 0 1 {SCAN SUBQUERY 1}
- 0 1 0 {SCAN TABLE t3}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | `--COMPOUND QUERY
+ | |--LEFT-MOST SUBQUERY
+ | | `--SCAN CONSTANT ROW
+ | `--UNION ALL
+ | `--SCAN TABLE t3
+ |--SCAN SUBQUERY xxxxxx
+ `--SCAN TABLE t3
}
#-------------------------------------------------------------------------
@@ -129,48 +161,58 @@ do_execsql_test 2.1 {
}
det 2.2.1 "SELECT DISTINCT min(x), max(x) FROM t1 GROUP BY x ORDER BY 1" {
- 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}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--USE TEMP B-TREE FOR GROUP BY
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--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}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t2 USING COVERING INDEX t2i1
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 2.2.3 "SELECT DISTINCT * FROM t1" {
- 0 0 0 {SCAN TABLE t1}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--USE TEMP B-TREE FOR DISTINCT
}
det 2.2.4 "SELECT DISTINCT * FROM t1, t2" {
- 0 0 0 {SCAN TABLE t1}
- 0 1 1 {SCAN TABLE t2}
- 0 0 0 {USE TEMP B-TREE FOR DISTINCT}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--SCAN TABLE t2
+ `--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}
- 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}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ |--SCAN TABLE t2
+ |--USE TEMP B-TREE FOR DISTINCT
+ `--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}
- 0 1 0 {SCAN TABLE t1}
+ QUERY PLAN
+ |--SCAN TABLE t2 USING COVERING INDEX t2i1
+ `--SCAN TABLE t1
}
det 2.3.1 "SELECT max(x) FROM t2" {
- 0 0 0 {SEARCH TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ `--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}
+ QUERY PLAN
+ `--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}
+ QUERY PLAN
+ `--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=?)}
+ QUERY PLAN
+ `--SEARCH TABLE t1 USING INTEGER PRIMARY KEY (rowid=?)
}
@@ -181,40 +223,46 @@ 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}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--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}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--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}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t1 AS sub}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ |--SCAN TABLE t1 AS sub
+ `--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}
- 0 0 0 {EXECUTE SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2 USING COVERING INDEX t2i1}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--SCALAR SUBQUERY
+ `--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}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 0 0 0 {SCAN SUBQUERY 1}
- 0 0 0 {USE TEMP B-TREE FOR ORDER BY}
+ QUERY PLAN
+ |--CO-ROUTINE xxxxxx
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ |--SCAN SUBQUERY xxxxxx
+ `--USE TEMP B-TREE FOR ORDER BY
}
det 3.2.2 {
SELECT * FROM
@@ -222,34 +270,40 @@ 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}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 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}
+ QUERY PLAN
+ |--MATERIALIZE xxxxxx
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ |--MATERIALIZE xxxxxx
+ | `--SCAN TABLE t2 USING INDEX t2i1
+ |--SCAN SUBQUERY xxxxxx AS x1
+ |--SCAN SUBQUERY xxxxxx AS x2
+ `--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}
- 0 0 0 {EXECUTE LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--LIST SUBQUERY
+ `--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}
- 0 0 0 {EXECUTE CORRELATED LIST SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--CORRELATED LIST SUBQUERY
+ `--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}
- 0 0 0 {EXECUTE CORRELATED SCALAR SUBQUERY 1}
- 1 0 0 {SCAN TABLE t2}
+ QUERY PLAN
+ |--SCAN TABLE t1
+ `--CORRELATED SCALAR SUBQUERY
+ `--SCAN TABLE t2
}
#-------------------------------------------------------------------------
@@ -258,119 +312,158 @@ det 3.3.3 {
do_eqp_test 4.1.1 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2
} {
- 1 0 0 {SCAN TABLE t1}
- 2 0 0 {SCAN TABLE t2}
- 0 0 0 {COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)}
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN TABLE t1
+ `--UNION ALL
+ `--SCAN TABLE t2
}
do_eqp_test 4.1.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 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)}
+ QUERY PLAN
+ `--MERGE (UNION ALL)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.1.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2
} {
- 1 0 0 {SCAN TABLE t1}
- 1 0 0 {USE TEMP B-TREE FOR ORDER BY}
- 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)}
+ QUERY PLAN
+ `--MERGE (UNION)
+ |--LEFT
+ | |--SCAN TABLE t1
+ | `--USE TEMP B-TREE FOR ORDER BY
+ `--RIGHT
+ |--SCAN TABLE t2
+ `--USE TEMP B-TREE FOR ORDER BY
}
do_eqp_test 4.1.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2
} {</