diff --git a/Makefile.in b/Makefile.in index 992d0f0d9e..687090a27b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -462,6 +462,7 @@ TESTSRC += \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ + $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ diff --git a/Makefile.msc b/Makefile.msc index a16e76f7c7..32205bcc56 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -1580,6 +1580,7 @@ TESTEXT = \ $(TOP)\ext\misc\normalize.c \ $(TOP)\ext\misc\percentile.c \ $(TOP)\ext\misc\prefixes.c \ + $(TOP)\ext\misc\qpvtab.c \ $(TOP)\ext\misc\regexp.c \ $(TOP)\ext\misc\remember.c \ $(TOP)\ext\misc\series.c \ diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c new file mode 100644 index 0000000000..828a5ba1b8 --- /dev/null +++ b/ext/misc/qpvtab.c @@ -0,0 +1,436 @@ +/* +** 2022-01-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 virtual-table that returns information about +** how the query planner called the xBestIndex method. This virtual table +** is intended for testing and debugging only. +** +** The schema of the virtual table is this: +** +** CREATE TABLE qpvtab( +** vn TEXT, -- Name of an sqlite3_index_info field +** ix INTEGER, -- Array index or value +** cn TEXT, -- Column name +** op INTEGER, -- operator +** ux BOOLEAN, -- "usable" field +** rhs TEXT, -- sqlite3_vtab_rhs_value() +** +** a, b, c, d, e, -- Extra columns to attach constraints to +** +** flags INTEGER HIDDEN -- control flags +** ); +** +** The virtual table returns a description of the sqlite3_index_info object +** that was provided to the (successful) xBestIndex method. There is one +** row in the result table for each field in the sqlite3_index_info object. +** +** The values of the "a" through "e" columns are one of: +** +** 1. TEXT - the same as the column name +** 2. INTEGER - 1 for "a", 2 for "b", and so forth +** +** Option 1 is the default behavior. 2 is use if there is a usable +** constraint on "flags" with an integer right-hand side that where the +** value of the right-hand side has its 0x01 bit set. +** +** All constraints on columns "a" through "e" are marked as "omit". +** +** If there is a usable constraint on "flags" that has a RHS value that +** is an integer and that integer has its 0x02 bit set, then the +** orderByConsumed flag is set. +** +** COMPILE: +** +** gcc -Wall -g -shared -fPIC -I. qpvtab.c -o qqvtab.so +** +** EXAMPLE USAGE: +** +** .load ./qpvtab +** SELECT rowid, *, flags FROM qpvtab(102) +** WHERE a=19 +** AND b BETWEEN 4.5 and 'hello' +** AND c<>x'aabbcc' +** ORDER BY d, e DESC; +*/ +#if !defined(SQLITEINT_H) +#include "sqlite3ext.h" +#endif +SQLITE_EXTENSION_INIT1 +#include +#include +#include + +#if !defined(SQLITE_OMIT_VIRTUALTABLE) + +/* qpvtab_vtab is a subclass of sqlite3_vtab which is +** underlying representation of the virtual table +*/ +typedef struct qpvtab_vtab qpvtab_vtab; +struct qpvtab_vtab { + sqlite3_vtab base; /* Base class - must be first */ +}; + +/* qpvtab_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 qpvtab_cursor qpvtab_cursor; +struct qpvtab_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_int64 iRowid; /* The rowid */ + const char *zData; /* Data to return */ + int nData; /* Number of bytes of data */ + int flags; /* Flags value */ +}; + +/* +** Names of columns +*/ +static const char *azColname[] = { + "vn", + "ix", + "cn", + "op", + "ux", + "rhs", + "a", "b", "c", "d", "e", + "flags" +}; + +/* +** The qpvtabConnect() method is invoked to create a new +** qpvtab virtual table. +*/ +static int qpvtabConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + qpvtab_vtab *pNew; + int rc; + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(" + " vn TEXT," + " ix INT," + " cn TEXT," + " op INT," + " ux BOOLEAN," + " rhs TEXT," + " a, b, c, d, e," + " flags INT HIDDEN)" + ); +#define QPVTAB_VN 0 +#define QPVTAB_IX 1 +#define QPVTAB_CN 2 +#define QPVTAB_OP 3 +#define QPVTAB_UX 4 +#define QPVTAB_RHS 5 +#define QPVTAB_A 6 +#define QPVTAB_B 7 +#define QPVTAB_C 8 +#define QPVTAB_D 9 +#define QPVTAB_E 10 +#define QPVTAB_FLAGS 11 + 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 qpvtab_vtab objects. +*/ +static int qpvtabDisconnect(sqlite3_vtab *pVtab){ + qpvtab_vtab *p = (qpvtab_vtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Constructor for a new qpvtab_cursor object. +*/ +static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + qpvtab_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 qpvtab_cursor. +*/ +static int qpvtabClose(sqlite3_vtab_cursor *cur){ + qpvtab_cursor *pCur = (qpvtab_cursor*)cur; + sqlite3_free(pCur); + return SQLITE_OK; +} + + +/* +** Advance a qpvtab_cursor to its next row of output. +*/ +static int qpvtabNext(sqlite3_vtab_cursor *cur){ + qpvtab_cursor *pCur = (qpvtab_cursor*)cur; + if( pCur->iRowidnData ){ + const char *z = &pCur->zData[pCur->iRowid]; + const char *zEnd = strchr(z, '\n'); + if( zEnd ) zEnd++; + pCur->iRowid = (int)(zEnd - pCur->zData); + } + return SQLITE_OK; +} + +/* +** Return values of columns for the row at which the qpvtab_cursor +** is currently pointing. +*/ +static int qpvtabColumn( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + qpvtab_cursor *pCur = (qpvtab_cursor*)cur; + if( i>=QPVTAB_VN && i<=QPVTAB_RHS && pCur->iRowidnData ){ + const char *z = &pCur->zData[pCur->iRowid]; + const char *zEnd; + int j; + j = QPVTAB_VN; + while(1){ + zEnd = strchr(z, j==QPVTAB_RHS ? '\n' : ','); + if( j==i || zEnd==0 ) break; + z = zEnd+1; + j++; + } + if( zEnd==z ){ + sqlite3_result_null(ctx); + }else if( i==QPVTAB_IX || i==QPVTAB_OP || i==QPVTAB_UX ){ + sqlite3_result_int(ctx, atoi(z)); + }else{ + sqlite3_result_text64(ctx, z, zEnd-z, SQLITE_TRANSIENT, SQLITE_UTF8); + } + }else if( i>=QPVTAB_A && i<=QPVTAB_E ){ + if( pCur->flags & 1 ){ + sqlite3_result_int(ctx, i-QPVTAB_A+1); + }else{ + char x = 'a'+i-QPVTAB_A; + sqlite3_result_text64(ctx, &x, 1, SQLITE_TRANSIENT, SQLITE_UTF8); + } + }else if( i==QPVTAB_FLAGS ){ + sqlite3_result_int(ctx, pCur->flags); + } + return SQLITE_OK; +} + +/* +** Return the rowid for the current row. In this implementation, the +** rowid is the same as the output value. +*/ +static int qpvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + qpvtab_cursor *pCur = (qpvtab_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 qpvtabEof(sqlite3_vtab_cursor *cur){ + qpvtab_cursor *pCur = (qpvtab_cursor*)cur; + return pCur->iRowid>=pCur->nData; +} + +/* +** This method is called to "rewind" the qpvtab_cursor object back +** to the first row of output. This method is always called at least +** once prior to any call to qpvtabColumn() or qpvtabRowid() or +** qpvtabEof(). +*/ +static int qpvtabFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + qpvtab_cursor *pCur = (qpvtab_cursor *)pVtabCursor; + pCur->iRowid = 0; + pCur->zData = idxStr; + pCur->nData = (int)strlen(idxStr); + pCur->flags = idxNum; + return SQLITE_OK; +} + +/* +** Append the text of a value to pStr +*/ +static void qpvtabStrAppendValue( + sqlite3_str *pStr, + sqlite3_value *pVal +){ + switch( sqlite3_value_type(pVal) ){ + case SQLITE_NULL: + sqlite3_str_appendf(pStr, "NULL"); + break; + case SQLITE_INTEGER: + sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pVal)); + break; + case SQLITE_FLOAT: + sqlite3_str_appendf(pStr, "%!f", sqlite3_value_double(pVal)); + break; + case SQLITE_TEXT: { + int i; + const char *a = (const char*)sqlite3_value_text(pVal); + int n = sqlite3_value_bytes(pVal); + sqlite3_str_append(pStr, "'", 1); + for(i=0; inConstraint); + for(i=0; inConstraint; i++){ + sqlite3_value *pVal; + int iCol = pIdxInfo->aConstraint[i].iColumn; + if( iCol==QPVTAB_FLAGS && pIdxInfo->aConstraint[i].usable ){ + pVal = 0; + rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); + assert( rc==SQLITE_OK || pVal==0 ); + if( pVal ){ + pIdxInfo->idxNum = sqlite3_value_int(pVal); + if( pIdxInfo->idxNum & 2 ) pIdxInfo->orderByConsumed = 1; + } + } + sqlite3_str_appendf(pStr,"aConstraint,%d,%s,%d,%d,", + i, + azColname[iCol], + pIdxInfo->aConstraint[i].op, + pIdxInfo->aConstraint[i].usable); + pVal = 0; + rc = sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal); + assert( rc==SQLITE_OK || pVal==0 ); + if( pVal ){ + qpvtabStrAppendValue(pStr, pVal); + } + sqlite3_str_append(pStr, "\n", 1); + if( iCol>=QPVTAB_A && pIdxInfo->aConstraint[i].usable ){ + pIdxInfo->aConstraintUsage[i].argvIndex = ++k; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + } + sqlite3_str_appendf(pStr, "nOrderBy,%d,,,,\n", pIdxInfo->nOrderBy); + for(i=0; inOrderBy; i++){ + int iCol = pIdxInfo->aOrderBy[i].iColumn; + sqlite3_str_appendf(pStr, "aOrderBy,%d,%s,%d,,\n",i, + iCol>=0 ? azColname[iCol] : "rowid", + pIdxInfo->aOrderBy[i].desc + ); + } + sqlite3_str_appendf(pStr, "sqlite3_vtab_distinct,%d,,,,\n", + sqlite3_vtab_distinct(pIdxInfo)); + sqlite3_str_appendf(pStr, "idxFlags,%d,,,,\n", pIdxInfo->idxFlags); + sqlite3_str_appendf(pStr, "colUsed,%d,,,,\n", (int)pIdxInfo->colUsed); + pIdxInfo->estimatedCost = (double)10; + pIdxInfo->estimatedRows = 10; + sqlite3_str_appendf(pStr, "idxNum,%d,,,,\n", pIdxInfo->idxNum); + sqlite3_str_appendf(pStr, "orderByConsumed,%d,,,,\n", + pIdxInfo->orderByConsumed); + pIdxInfo->idxStr = sqlite3_str_finish(pStr); + pIdxInfo->needToFreeIdxStr = 1; + return SQLITE_OK; +} + +/* +** This following structure defines all the methods for the +** virtual table. +*/ +static sqlite3_module qpvtabModule = { + /* iVersion */ 0, + /* xCreate */ 0, + /* xConnect */ qpvtabConnect, + /* xBestIndex */ qpvtabBestIndex, + /* xDisconnect */ qpvtabDisconnect, + /* xDestroy */ 0, + /* xOpen */ qpvtabOpen, + /* xClose */ qpvtabClose, + /* xFilter */ qpvtabFilter, + /* xNext */ qpvtabNext, + /* xEof */ qpvtabEof, + /* xColumn */ qpvtabColumn, + /* xRowid */ qpvtabRowid, + /* xUpdate */ 0, + /* xBegin */ 0, + /* xSync */ 0, + /* xCommit */ 0, + /* xRollback */ 0, + /* xFindMethod */ 0, + /* xRename */ 0, + /* xSavepoint */ 0, + /* xRelease */ 0, + /* xRollbackTo */ 0, + /* xShadowName */ 0 +}; +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_qpvtab_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = sqlite3_create_module(db, "qpvtab", &qpvtabModule, 0); +#endif + return rc; +} diff --git a/main.mk b/main.mk index 47f68d094b..7786a28ca3 100644 --- a/main.mk +++ b/main.mk @@ -378,6 +378,7 @@ TESTSRC += \ $(TOP)/ext/misc/normalize.c \ $(TOP)/ext/misc/percentile.c \ $(TOP)/ext/misc/prefixes.c \ + $(TOP)/ext/misc/qpvtab.c \ $(TOP)/ext/misc/regexp.c \ $(TOP)/ext/misc/remember.c \ $(TOP)/ext/misc/series.c \ diff --git a/manifest b/manifest index beb535c254..5e78b5ab3e 100644 --- a/manifest +++ b/manifest @@ -1,11 +1,11 @@ -C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\sbegin-concurrent-pnu-wal2\sbranch. -D 2022-01-17T19:46:10.859 +C Merge\sthe\slatest\strunk\schanges\sinto\sbegin-concurrent-pnu-wal2 +D 2022-01-25T16:05:35.952 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 95adc067f0340ec77a71cb07f79141c4819a63d488b0d12ac6c08b40f52cc28d +F Makefile.in dfbb94b000362dc16265e3787a1ecb4b71467e4a89869c1815f843ad15124d3e F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 -F Makefile.msc c73d82b80836e90df926e25bcaea7acfd46b6d3ec3d6fecdfbf6810c48a75360 +F Makefile.msc fe098b3b2d7b8cc41fc8ffa41550ba09d61c1d6a09cb57d473fcf87b95868572 F README.md 2dd87a5c1d108b224921f3dd47dea567973f706e1f6959386282a626f459a70c F VERSION 392c2f83569705069415a5d98b1c138ec8fe8a56a663a0d94cea019e806537b2 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@ -319,6 +319,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196 +F ext/misc/qpvtab.c c662fa0a452ad286e49b6c83ac917600656b2eb47d2225ff6185c56bf80cf8d2 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c @@ -479,7 +480,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 33660d5cd3a0cc1373e5c1f5ec8bd619fc3e78b6e1a14a55d12d77ff5cc182bb +F main.mk 8c75c39eac0df2e562fc0f610e87937771405201299ea4a525f33b2d4167b82f F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -491,25 +492,25 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 67ef8e685f547038b7ad93a7c6571f790d0a5bb1c00632d5466ffb4ccf3ee6e8 +F src/alter.c e8ac1df663bf4ec74920edd1299435f2a616d2404de0ac4013c151ea4e7a11f2 F src/analyze.c 7518b99e07c5494111fe3bd867f28f804b6c5c1ad0703ec3d116de9bab3fa516 -F src/attach.c e3f9d9a2a4a844750f3f348f37afb244535f21382cbfcd840152cb21cb41cfaf +F src/attach.c f26d400f3ffe2cdca01406bca70e5f58c5488bf165b4fc37c228136dfcf1b583 F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf -F src/backup.c 3014889fa06e20e6adfa0d07b60097eec1f6e5b06671625f476a714d2356513d +F src/backup.c 58880b9a9adf88f1a57cb3b0db6b891626ae76113ebd0f2417a87c2634edfc65 F src/bitvec.c 3907fcbe8a0c8c2db58d97087d15cdabbf2842adb9125df9ab9ff87d3db16775 F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6 F src/btree.c a7f304a0c872b6d1f1fc162123b5b0a43dd1874b30ecdf26a77956f2e3e12c7f F src/btree.h 900067641b64d619e6e2a93bd115c952a52f41d3bee32e551e2a4ceee05fc431 F src/btreeInt.h 3f19f0be5af0b68cff55e58df4b11e7a0692d9e8a820ceaeba4084659a86cf28 -F src/build.c 99a4dee6e4e18d0e5f9340f4739637fa8b5b52b3efa1a94fc6c3210819dc16b2 +F src/build.c 38cffd6b81ecdd464c73a75d0b8bd9f71d769bbb6279b2f8dc8de05dc3d712f2 F src/callback.c 4c19af69835787bfe790ac560f3071a824eb629f34e41f97b52ce5235c77de1c F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 2cce39df1a13e05b7633e6d21b651f21492471f991dd7b323a4ee4e7b7f0b7f1 -F src/date.c ab8e01d928f201f5dee0bc6d54d6702fdcec96dff4d58c387447671f6a46d191 +F src/date.c e25773f06a8f9043bfa1e5fa0bee93483c41933adfff0891752f00eadd12ab1c F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a F src/dbstat.c 861e08690fcb0f2ee1165eff0060ea8d4f3e2ea10f80dab7d32ad70443a6ff2d -F src/delete.c 19814f621cde10b1771a0dea7fe25d3d7d39975b8d4be4888537d30860e7c08c -F src/expr.c 827179c78d2ca7cc318392811de8151c60eacf7ce804b13e61bb7ef38f954846 +F src/delete.c 52897a8516dc40753503c25eed0e305f09cc50ae474f22b0b4fd31d3b879cc08 +F src/expr.c 9658bccd1598211ace848c8ca9480dbf8be08dfee1db5cf03897b34b7b6e8fef F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c 5b73f7a7c00f06017531a5bd258cbc2c7a294e55a7f84a729fe27aa525242560 F src/func.c 88b07637d07b3bf7f89a6fe2da8edad7d34f962a69708afe48d4c28aa682515e @@ -518,12 +519,12 @@ F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 F src/hwtime.h cb1d7e3e1ed94b7aa6fde95ae2c2daccc3df826be26fc9ed7fd90d1750ae6144 F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743 -F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2 w ext/misc/json1.c +F src/insert.c 1eea44389de3768ac98588c1410171cd53e7c6ad1af74049983dcbac82093de0 +F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa -F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d +F src/loadext.c 657534339585ac234839e5187aa51d8802f292e0771c4f874b3af1f1223f81e2 F src/main.c 4806307e415189bbf91df6467d6ff923643fdeb84e782ef0d5451a14465db935 -F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b +F src/malloc.c fec841aa0a0400a6f7d20706178a5d8e8219a6bf562b6fe712c17f6c26813266 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75 @@ -551,24 +552,24 @@ F src/parse.y c7c307018e6d328523833cf7ee7738cf95e81c2b272e97076665caf9221fc9a2 F src/pcache.c 084e638432c610f95aea72b8509f0845d2791293f39d1b82f0c0a7e089c3bb6b F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c 54881292a9a5db202b2c0ac541c5e3ef9a5e8c4f1c1383adb2601d5499a60e65 -F src/pragma.c cb0cc54e1ae56082f6eabc4c0013ee3fd31844bda83e9c5aaf95616572db2439 +F src/pragma.c cb52183ed4fe56c806323f84576bf50306bd42104addaf7f4375195f13635dc3 F src/pragma.h f98354c48571c490927029510566839bf9e7242569bfbb48032dafeb008481d2 -F src/prepare.c 45fe7408eb78d80eca8392669070a6e5caf231b09e5c7b1ff65c1ad64a3734c5 +F src/prepare.c 1e23522c934d90ff42de1b9b4f782fdf0fb690b06b92d7480b471ccb2b5899ea F src/printf.c 975f1f5417f2526365b6e6d7f22332e3e11806dad844701d92846292b654ba9a F src/random.c c6e61d041f230d46c658e6dfe7165fc1ecb0093d5fe28cfe74f389d261dc3af8 -F src/resolve.c 359bc0e445d427583d2ab6110433a5dc777f64a0ecdf8d24826d8b475233ead9 +F src/resolve.c 24032ae57aec10df2f3fa2e20be0aae7d256bc704124b76c52d763440c7c0fe9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 -F src/select.c df53cc54f99fbfc3807c1a3d9ee238301069f49b482ffe6d7b42d24371354d75 -F src/shell.c.in 4690f216dc4da0c104a8fd9f9e12bec0483242e630324aa7a3ccd155922e346e -F src/sqlite.h.in c9b6376ff29699b8061357c39e37ec29522d367982d2e0b5af74d8d48c557910 +F src/select.c 760d9f28dbeaafef9ea54c509af94ff5695df73ad5c854efd53de32a0564ed56 +F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 +F src/sqlite.h.in d54bb1cc5e5cfb577c7e188813a06b785299121c6f5822a3770f22da3245ce33 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 -F src/sqlite3ext.h 01eb85e4f2759a5ee79c183f4b2877889d4ffdc49d27ae74529c9579e3c8c0ef -F src/sqliteInt.h 60518b1113603dd132dab78d2d30f856e6f5b402bd094409b952b65536e80ef0 +F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a +F src/sqliteInt.h ce80b11178ecf2724472273cb3c530aa822e26ed2908b0a7ad7f2b76b1c27173 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c 48f291e1a7e672a7204884d4c164a8ed3a522ff087c361ada2991f5d54e987f6 -F src/test1.c 545b5b782d80b8a13e5496ac3ded6a80beb3f4c3a6ee9f1cd80f36ac26746c7b +F src/test1.c 5d39b01f7ce4c0d50e1e83c574e1693de2e88036aa298bc156a03b7d388f6ca1 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159 @@ -624,32 +625,32 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c b74d878aa7c82ec8460779468061a96185e22257f68ab785b69abce354b70446 F src/treeview.c 9dfdb7ff7f6645d0a6458dbdf4ffac041c071c4533a6db8bb6e502b979ac67bc -F src/trigger.c 40e7c3dcff57a770d5fa38ba21ed4725572fd2e224c58af61eb980598b60f9c8 -F src/update.c 457fbf994ccc67f99e4c2c54018b553be65a3d38910c5b88e67abeacd65eac1e +F src/trigger.c 692972e4393dfc8017a1a527c1ea1b96ce3d101e84584cd832fcfb83d22b50b2 +F src/update.c 3164c399697c3655dbc1b0089be5c38707d6565271fa3c62100f95e520127a0a F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 89e51820bcb468ff3877a8d942f5cc807208087f021227e0927693e928a195bc +F src/util.c 602fe229f32a96ceccae4f40824129669582096f7c355f53dbac156c9fecef23 F src/vacuum.c 72867c740476d13f1c397015e4d3168b4e96a237a80b9afa67e1bb8500bfeeab -F src/vdbe.c e120656de4cfbc078253e970cd9f0f0891378a66efbd3890ec956dd44a62b8dd +F src/vdbe.c 1b1e8ebe3840915922dcd11934f85d348d0384de564d258de01841a6ad337eb2 F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe F src/vdbeInt.h d89d5d2150500cfb08615329fd20aea9d746bba5f2c3ecb8a17e2d2d9be029e5 F src/vdbeapi.c 22c79072ae7d8a01e9bcae8ba16e918d60d202eaa9553b5fda38f99f7464d99a -F src/vdbeaux.c 00fc867d51b1080550784c0a2c49abca472c73d2bb53767e8199299eb582cbca -F src/vdbeblob.c 29c4118f7ee615cdee829e8401f6ead1b96b95d545b4de0042f6de39c962c652 +F src/vdbeaux.c 312d57e71e6e152243867390308aec667e05a4e7e676a674114a33f6687d3702 +F src/vdbeblob.c 5e61ce31aca17db8fb60395407457a8c1c7fb471dde405e0cd675974611dcfcd F src/vdbemem.c da4d594084d581be6436582bb44bb128feeb138a3e6c313eda6749ebdc3a65ec F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35 F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823 F src/vdbevtab.c f99b275366c5fc5e2d99f734729880994ab9500bdafde7fae3b02d562b9d323c -F src/vtab.c a47cc12ebaa350800c0c87b6b0095debbb5a6ed32727bcab9d82ad070a81b738 +F src/vtab.c e0eaf5b8f7f8929088485a76bea2ff6124adb12e0eb5c0997287ff5e0e4c0517 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c 5b92441319c3c7cf2833e64d7d1f38efa59a9458240c991b67a105541432c3d1 +F src/wal.c 151479223abeaf4f9a4210f243a9a9ab4cccaa062855028a8c375b711b1d0611 F src/wal.h 7a733af13b966ecb81872ce397e862116b3575ea53245b90b139a2873ee87825 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b -F src/where.c eedf0311d59095bcd8523bd13bf25865e1caf1fab9beddbff9093340a1a409c7 -F src/whereInt.h 91865afa4a3540bb3bd643619acc56fbceff7defeb8f249b8e157fd5325d88be -F src/wherecode.c 6a594ed25bfbeb60d455868b7be62637575e4f1949152de4336e4825e0c54ba6 +F src/where.c a14990c7b35e95f8f9cb0dc0d6d2e32fa99135a716a04027cefa48138d280ecb +F src/whereInt.h 8a215acde0f833a4dea3d30a7bbed9f48b4b547b5d5e34cd02acee366476ab80 +F src/wherecode.c 8da0f873278ed6aad42bf2028404d7178dd9cfcdc7179ecc61a87591a15a07d2 F src/whereexpr.c 9f64c39e53070584e99e4d20c1dd3397e125fabbae8fd414ffec574c410ac7d3 -F src/window.c 5d3b397b0c026d0ff5890244ac41359e524c01ae31e78782e1ff418c3e271a9e +F src/window.c dfaec4abc6012cbc18e4a202ca3a5d5a0efcc4011d86a06d882ddaab8aedee4d F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7 @@ -662,7 +663,7 @@ F test/alter2.test a966ccfcddf9ce0a4e0e6ff1aca9e6e7948e0e242cd7e43fc091948521807 F test/alter3.test ffc4ab29ce78a3517a66afd69b2730667e3471622509c283b2bd4c46f680fba3 F test/alter4.test 716caa071dd8a3c6d57225778d15d3c3cbf5e34b2e84ae44199aeb2bbf50a707 F test/alterauth.test 63442ba61ceb0c1eeb63aac1f4f5cebfa509d352276059d27106ae256bafc959 -F test/alterauth2.test 381b1ab603c9ef96314a3158528ea17f7964449385a28eeaf8191120b2e24a8d +F test/alterauth2.test 48967abae0494d9a300d1c92473d99fcb66edfcc23579c89322f033f49410adc F test/altercol.test 9471187fe155d9c4211ae185e104ff48ce8f114262ee1256cf1e110b339c725f F test/altercorrupt.test 2e1d705342cf9d7de884518ddbb053fd52d7e60d2b8869b7b63b2fda68435c12 F test/alterdropcol.test a653a3945f964d26845ec0cd0a8e74189f46de3119a984c5bc45457da392612e @@ -705,7 +706,7 @@ F test/attach2.test 256bd240da1835fb8408dd59fb7ef71f8358c7a756c46662434d11d07ba3 F test/attach3.test c59d92791070c59272e00183b7353eeb94915976 F test/attach4.test 00e754484859998d124d144de6d114d920f2ed6ca2f961e6a7f4183c714f885e F test/attachmalloc.test 12c4f028e570acf9e0a4b0b7fe6f536e21f3d5ebddcece423603d0569beaf438 -F test/auth.test 567d917e0baddb6d0026a251cff977a3ab2c805a3cef906ba8653aafe7ad7240 +F test/auth.test 0f246deec5cb2f6f893f8fbb76628f182c08fe40f178b254dd72467ca012f657 F test/auth2.test 9eb7fce9f34bf1f50d3f366fb3e606be5a2000a1 F test/auth3.test 76d20a7fa136d63bcfcf8bcb65c0b1455ed71078d81f22bcd0550d3eb18594ab F test/autoanalyze1.test b9cc3f32a990fa56669b668d237c6d53e983554ae80c0604992e18869a0b2dec @@ -885,8 +886,8 @@ F test/e_createtable.test 04c50b7fe41c12ed9cd88fbbc09b4900bcfc66f98ad198874fc993 F test/e_delete.test ab39084f26ae1f033c940b70ebdbbd523dc4962e F test/e_droptrigger.test 235c610f8bf8ec44513e222b9085c7e49fad65ad0c1975ac2577109dd06fd8fa F test/e_dropview.test 74e405df7fa0f762e0c9445b166fe03955856532e2bb234c372f7c51228d75e7 -F test/e_expr.test e164550b9f8fd9c130283d1eae692dff9e2ba67f4dbd35f7325021f5d4b8851c -F test/e_fkey.test ee321dbca3c53204da46cb40b8ec80192e995035f9ea26576ddcd842b1f0877f +F test/e_expr.test bc6aa5906ba967587525bc746ea403011557cf6d8e4fc9efb1fab5dae7fb4fd6 +F test/e_fkey.test feeba6238aeff9d809fb6236b351da8df4ae9bda89e088e54526b31a0cbfeec5 F test/e_fts3.test 17ba7c373aba4d4f5696ba147ee23fd1a1ef70782af050e03e262ca187c5ee07 F test/e_insert.test f02f7f17852b2163732c6611d193f84fc67bc641fb4882c77a464076e5eba80e F test/e_reindex.test 2b0e29344497d9a8a999453a003cb476b6b1d2eef2d6c120f83c2d3a429f3164 @@ -1086,7 +1087,7 @@ F test/fuzzdata4.db b502c7d5498261715812dd8b3c2005bad08b3a26e6489414bd13926cd3e4 F test/fuzzdata5.db e35f64af17ec48926481cfaf3b3855e436bd40d1cfe2d59a9474cb4b748a52a5 F test/fuzzdata6.db 92a80e4afc172c24f662a10a612d188fb272de4a9bd19e017927c95f737de6d7 F test/fuzzdata7.db 0166b56fd7a6b9636a1d60ef0a060f86ddaecf99400a666bb6e5bbd7199ad1f2 -F test/fuzzdata8.db a0c7af954bcbb226ed7ae087b3b6d6e48eaac8fac5671150ffd23c35b55bffe6 +F test/fuzzdata8.db 738ed275638f2423cdef240a44eba3b9bc272eec619e68b00429d4575a15e50b F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8 F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14 F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc @@ -1308,7 +1309,7 @@ F test/pragma5.test 7b33fc43e2e41abf17f35fb73f71b49671a380ea92a6c94b6ce530a25f8d F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8 F test/prefixes.test b524a1c44bffec225b9aec98bd728480352aa8532ac4c15771fb85e8beef65d9 F test/printf.test 390d0d7fcffc3c4ea3c1bb4cbb267444e32b33b048ae21895f23a291844fe1da -F test/printf2.test 30b5dd0b4b992dc5626496846ecce17ff592cacbcb11c3e589f3ac4d7e129dae +F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60 F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/pushdown.test 5e72c51c5e33253ed639ccee1e01ce62d62b6eee5ca893cd82334e4ee7b1d7fc @@ -1338,7 +1339,7 @@ F test/round1.test 768018b04522ca420b1aba8a24bd76091d269f3bce3902af3ec6ebcee41ab F test/rowallock.test 3f88ec6819489d0b2341c7a7528ae17c053ab7cc F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81 F test/rowid.test e29025be95baf6b32f0d5edef59a7633028325896a98f1caa8019559ca910350 -F test/rowvalue.test 37effea4dd83555ea969a9461dfcffb25e6731a5db7c388e232410999c100853 +F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0d509d F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256 F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58 @@ -1404,7 +1405,7 @@ F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/shell1.test 70f46b5d07776a107335c3c2c9cbd0431d44637bfeae1f6b9ded5e33b4c7c0bf F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566 -F test/shell4.test 3ed6c4b42fd695efcbc25d69ef759dbb15855ca8e52ba6c5ee076f8b435f48be +F test/shell4.test 8f6c0fce4abed19a8a7f7262517149812a04caa905d01bdc8f5e92573504b759 F test/shell5.test b85069bfcf3159b225228629ab2c3e69aa923d098fea8ea074b5dcd743522e2c F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3 F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f @@ -1455,7 +1456,7 @@ F test/stat.test 123212a20ceb496893d5254a5f6c76442ce549fdc08d1702d8288a2bbaac840 F test/statfault.test 55f86055f9cd7b2d962a621b8a04215c1cebd4eaaecde92d279442327fe648a0 F test/stmt.test 54ed2cc0764bf3e48a058331813c3dbd19fc1d0827c3d8369914a5d8f564ec75 F test/stmtvtab1.test 6873dfb24f8e79cbb5b799b95c2e4349060eb7a3b811982749a84b359468e2d5 -F test/strict1.test a3ec495471f24c1a6e1a1664bd23e24ccdb27ae93b1a763ee1942ec955b68e71 +F test/strict1.test 4d2b492152b984fd7e8196d23eb88e2ccb0ef9e46ca2f96c2ce7147ceef9d168 F test/strict2.test b22c7a98b5000aef937f1990776497f0e979b1a23bc4f63e2d53b00e59b20070 F test/subjournal.test 8d4e2572c0ee9a15549f0d8e40863161295107e52f07a3e8012a2e1fdd093c49 F test/subquery.test 3a1a5b600b8d4f504d2a2c61f33db820983dba94a0ef3e4aedca8f0165eaecb8 @@ -1741,7 +1742,9 @@ F test/vtabK.test 13293177528fada1235c0112db0d187d754af1355c5a39371abd365104e3af F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c3784c6783 F test/vtab_err.test dcc8b7b9cb67522b3fe7a272c73856829dae4ab7fdb30399aea1b6981bda2b65 F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad +F test/vtabdistinct.test 7688f0889358f849fd60bbfde1ded38b014b18066076d4bfbb75395804dfe072 F test/vtabdrop.test 65d4cf6722972e5499bdaf0c0d70ee3b8133944a4e4bc31862563f32a7edca12 +F test/vtabrhs1.test c138346be341916ecc9d918dcfc2657d27bce211a350a82b01d62d224b167b56 F test/wal.test 07d61ee522bc203235a7979645d6c5d84f86882afad5b678532ee37b5b85b133 F test/wal2.test 88d34cc62bf5460ad6b1f837cd56ab55731bb0637ff99e113b790873d9b60b14 F test/wal2big.test 0b4ec526f9ca4bbabc355042c38045ae2e253fb46eb327bb7693d0122bc6968b @@ -1783,7 +1786,7 @@ F test/walpersist.test 8c6b7e3ec1ba91b5e4dc4e0921d6d3f87cd356a6 F test/walprotocol.test 1b3f922125e341703f6e946d77fdc564d38fb3e07a9385cfdc6c99cac1ecf878 F test/walprotocol2.test 7e4bedd5ee83607e2928ac438bf7332a396b980d3e02aa0746509ce11ad1f13c F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20 -F test/walro2.test 0e79dd15cbdb4f482c01ea248373669c732414a726b357d04846a816afafb768 +F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc F test/walrofault.test 5a25f91c16a68bae65edec7cdef4495e5c6494c8408743fe9b29045fa6665cd0 F test/walsetlk.test 3185bebc90557e0d611442c8d64f7a0cb7b06f8e156eea37a4a7358f722715be F test/walshared.test 0befc811dcf0b287efae21612304d15576e35417 @@ -1970,8 +1973,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P a92eca6c9ca45208df5764e50dec2b257244176f5893b4bee7f9475c4e8d8c8b 82f031b41dbf041cdbdfb2f3ccc8d118b22d9e523e54a32e882a67535e9973c5 -R 2fdaa4db2743ba577b9d263296a14e36 +P 45fa7efecb8d28dd68b844c9f74b751aab6e7ad7d03f03e8a5881f0e09fb86a2 84f737abd1c05d66627b601dbd9be42c83d9c8e7a6c7fb4945dd85e7765569d9 +R 321ee36c92c8cff9bf63670a59c57c51 U drh -Z 99557125a79fbfb6d45a17bf98121af0 +Z 716d86d917c6bcad39dcdc83dab82ee5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 037008113e..e58451f246 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -45fa7efecb8d28dd68b844c9f74b751aab6e7ad7d03f03e8a5881f0e09fb86a2 \ No newline at end of file +3d949775e3029c6bf7cb06946ce52e2dfab451a3f7e685d654807cb5ac750b02 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index f3f4f570e6..4de7cc51d3 100644 --- a/src/alter.c +++ b/src/alter.c @@ -324,7 +324,9 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ int r1; /* Temporary registers */ db = pParse->db; - if( pParse->nErr || db->mallocFailed ) return; + assert( db->pParse==pParse ); + if( pParse->nErr ) return; + assert( db->mallocFailed==0 ); pNew = pParse->pNewTable; assert( pNew ); @@ -735,7 +737,9 @@ struct RenameCtx { ** following a valid object, it may not be used in comparison operations. */ static void renameTokenCheckAll(Parse *pParse, const void *pPtr){ - if( pParse->nErr==0 && pParse->db->mallocFailed==0 ){ + assert( pParse==pParse->db->pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr==0 ){ const RenameToken *p; u8 i = 0; for(p=pParse->pRename; p; p=p->pNext){ @@ -1132,7 +1136,7 @@ static int renameParseSql( /* Parse the SQL statement passed as the first argument. If no error ** occurs and the parse does not result in a new table, index or ** trigger object, the database must be corrupt. */ - memset(p, 0, sizeof(Parse)); + sqlite3ParseObjectInit(p, db); p->eParseMode = PARSE_MODE_RENAME; p->db = db; p->nQueryLoop = 1; @@ -1417,7 +1421,7 @@ static void renameParseCleanup(Parse *pParse){ sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->zErrMsg); renameTokenFree(db, pParse->pRename); - sqlite3ParserReset(pParse); + sqlite3ParseObjectReset(pParse); } /* @@ -2131,6 +2135,12 @@ void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const Token *pName){ iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); zDb = db->aDb[iDb].zDbSName; +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Invoke the authorization callback. */ + if( sqlite3AuthCheck(pParse, SQLITE_ALTER_TABLE, zDb, pTab->zName, zCol) ){ + goto exit_drop_column; + } +#endif renameTestSchema(pParse, zDb, iDb==1, "", 0); renameFixQuotes(pParse, zDb, iDb==1); sqlite3NestedParse(pParse, diff --git a/src/attach.c b/src/attach.c index 2123278c47..e587f6cc5e 100644 --- a/src/attach.c +++ b/src/attach.c @@ -354,7 +354,7 @@ static void codeAttach( } #ifndef SQLITE_OMIT_AUTHORIZATION - if( pAuthArg ){ + if( ALWAYS(pAuthArg) ){ char *zAuthArg; if( pAuthArg->op==TK_STRING ){ assert( !ExprHasProperty(pAuthArg, EP_IntValue) ); diff --git a/src/backup.c b/src/backup.c index 2b286de6bd..bfd155bcac 100644 --- a/src/backup.c +++ b/src/backup.c @@ -85,14 +85,13 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){ if( i==1 ){ Parse sParse; int rc = 0; - memset(&sParse, 0, sizeof(sParse)); - sParse.db = pDb; + sqlite3ParseObjectInit(&sParse,pErrorDb); if( sqlite3OpenTempDatabase(&sParse) ){ sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg); rc = SQLITE_ERROR; } sqlite3DbFree(pErrorDb, sParse.zErrMsg); - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); if( rc ){ return 0; } diff --git a/src/build.c b/src/build.c index b242d7d734..0ee67f9b6e 100644 --- a/src/build.c +++ b/src/build.c @@ -143,11 +143,13 @@ void sqlite3FinishCoding(Parse *pParse){ assert( pParse->pToplevel==0 ); db = pParse->db; + assert( db->pParse==pParse ); if( pParse->nested ) return; - if( db->mallocFailed || pParse->nErr ){ - if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR; + if( pParse->nErr ){ + if( db->mallocFailed ) pParse->rc = SQLITE_NOMEM; return; } + assert( db->mallocFailed==0 ); /* Begin by generating some termination code at the end of the ** vdbe program @@ -280,7 +282,9 @@ void sqlite3FinishCoding(Parse *pParse){ /* Get the VDBE program ready for execution */ - if( v && pParse->nErr==0 && !db->mallocFailed ){ + assert( v!=0 || pParse->nErr ); + assert( db->mallocFailed==0 || pParse->nErr ); + if( pParse->nErr==0 ){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ assert( pParse->pAinc==0 || pParse->nTab>0 ); @@ -2384,10 +2388,11 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){ pTab->iPKey = -1; sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0, SQLITE_IDXTYPE_PRIMARYKEY); - if( db->mallocFailed || pParse->nErr ){ + if( pParse->nErr ){ pTab->tabFlags &= ~TF_WithoutRowid; return; } + assert( db->mallocFailed==0 ); pPk = sqlite3PrimaryKeyIndex(pTab); assert( pPk->nKeyCol==1 ); }else{ @@ -3128,10 +3133,10 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ */ sqlite3ColumnsFromExprList(pParse, pTable->pCheck, &pTable->nCol, &pTable->aCol); - if( db->mallocFailed==0 - && pParse->nErr==0 + if( pParse->nErr==0 && pTable->nCol==pSel->pEList->nExpr ){ + assert( db->mallocFailed==0 ); sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel, SQLITE_AFF_NONE); } @@ -3750,7 +3755,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){ tnum = pIndex->tnum; } pKey = sqlite3KeyInfoOfIndex(pParse, pIndex); - assert( pKey!=0 || db->mallocFailed || pParse->nErr ); + assert( pKey!=0 || pParse->nErr ); /* Open the sorter cursor if we are to use one. */ iSorter = pParse->nTab++; @@ -3914,9 +3919,11 @@ void sqlite3CreateIndex( char *zExtra = 0; /* Extra space after the Index object */ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */ - if( db->mallocFailed || pParse->nErr>0 ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto exit_create_index; } + assert( db->mallocFailed==0 ); if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){ goto exit_create_index; } @@ -3980,7 +3987,6 @@ void sqlite3CreateIndex( pDb = &db->aDb[iDb]; assert( pTab!=0 ); - assert( pParse->nErr==0 ); if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 && db->init.busy==0 && pTblName!=0 @@ -4544,10 +4550,10 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists){ sqlite3 *db = pParse->db; int iDb; - assert( pParse->nErr==0 ); /* Never called with prior errors */ if( db->mallocFailed ){ goto exit_drop_index; } + assert( pParse->nErr==0 ); /* Never called with prior non-OOM errors */ assert( pName->nSrc==1 ); if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){ goto exit_drop_index; diff --git a/src/date.c b/src/date.c index b018c39f68..2117b1b853 100644 --- a/src/date.c +++ b/src/date.c @@ -658,7 +658,8 @@ static int parseModifier( sqlite3_context *pCtx, /* Function context */ const char *z, /* The text of the modifier */ int n, /* Length of zMod in bytes */ - DateTime *p /* The date/time value to be modified */ + DateTime *p, /* The date/time value to be modified */ + int idx /* Parameter index of the modifier */ ){ int rc = 1; double r; @@ -671,6 +672,7 @@ static int parseModifier( ** a unix timestamp, depending on its magnitude. */ if( sqlite3_stricmp(z, "auto")==0 ){ + if( idx>1 ) return 1; /* IMP: R-33611-57934 */ if( !p->rawS || p->validJD ){ rc = 0; p->rawS = 0; @@ -695,6 +697,7 @@ static int parseModifier( ** SQLite (0..5373484.5) then the result will be NULL. */ if( sqlite3_stricmp(z, "julianday")==0 ){ + if( idx>1 ) return 1; if( p->validJD && p->rawS ){ rc = 0; p->rawS = 0; @@ -938,7 +941,7 @@ static int isDate( for(i=1; iisError || !validJulianDay(p->iJD) ) return 1; diff --git a/src/delete.c b/src/delete.c index 9c56858e79..b51e038918 100644 --- a/src/delete.c +++ b/src/delete.c @@ -293,9 +293,11 @@ void sqlite3DeleteFrom( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto delete_from_cleanup; } + assert( db->mallocFailed==0 ); assert( pTabList->nSrc==1 ); diff --git a/src/expr.c b/src/expr.c index a51d37a7b7..d58097cb61 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2803,8 +2803,7 @@ int sqlite3FindInIndex( CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs); int j; - assert( pReq!=0 || pRhs->iColumn==XN_ROWID - || pParse->nErr || db->mallocFailed ); + assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr ); for(j=0; jaiColumn[j]!=pRhs->iColumn ) continue; assert( pIdx->azColl[j] ); @@ -3280,10 +3279,8 @@ int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ } pSel->iLimit = 0; if( sqlite3Select(pParse, pSel, &dest) ){ - if( pParse->nErr ){ - pExpr->op2 = pExpr->op; - pExpr->op = TK_ERROR; - } + pExpr->op2 = pExpr->op; + pExpr->op = TK_ERROR; return 0; } pExpr->iTable = rReg = dest.iSDParm; @@ -3500,10 +3497,10 @@ static void sqlite3ExprCodeIN( }else{ destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); } - if( pParse->nErr ) goto sqlite3ExprCodeIN_finished; +// if( pParse->nErr ) goto sqlite3ExprCodeIN_finished; for(i=0; ipLeft, i); - if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; + if( pParse->nErr ) goto sqlite3ExprCodeIN_oom_error; if( sqlite3ExprCanBeNull(p) ){ sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2); VdbeCoverage(v); diff --git a/src/insert.c b/src/insert.c index 9a02ec6954..fd3590cb30 100644 --- a/src/insert.c +++ b/src/insert.c @@ -715,9 +715,11 @@ void sqlite3Insert( #endif db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto insert_cleanup; } + assert( db->mallocFailed==0 ); dest.iSDParm = 0; /* Suppress a harmless compiler warning */ /* If the Select object is really just a simple VALUES() list with a @@ -893,7 +895,9 @@ void sqlite3Insert( dest.nSdst = pTab->nCol; rc = sqlite3Select(pParse, pSelect, &dest); regFromSelect = dest.iSdst; - if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup; + assert( db->pParse==pParse ); + if( rc || pParse->nErr ) goto insert_cleanup; + assert( db->mallocFailed==0 ); sqlite3VdbeEndCoroutine(v, regYield); sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */ assert( pSelect->pEList ); diff --git a/src/loadext.c b/src/loadext.c index 681e12c4e7..a4aad7e748 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -487,6 +487,8 @@ static const sqlite3_api_routines sqlite3Apis = { sqlite3_autovacuum_pages, /* Version 3.38.0 and later */ sqlite3_error_offset, + sqlite3_vtab_rhs_value, + sqlite3_vtab_distinct, }; /* True if x is the directory separator character diff --git a/src/malloc.c b/src/malloc.c index 932cecc210..21e5245891 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -759,8 +759,15 @@ void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ ** has happened. This routine will set db->mallocFailed, and also ** temporarily disable the lookaside memory allocator and interrupt ** any running VDBEs. +** +** Always return a NULL pointer so that this routine can be invoked using +** +** return sqlite3OomFault(db); +** +** and thereby avoid unnecessary stack frame allocations for the overwhelmingly +** common case where no OOM occurs. */ -void sqlite3OomFault(sqlite3 *db){ +void *sqlite3OomFault(sqlite3 *db){ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){ db->mallocFailed = 1; if( db->nVdbeExec>0 ){ @@ -768,9 +775,11 @@ void sqlite3OomFault(sqlite3 *db){ } DisableLookaside; if( db->pParse ){ + sqlite3ErrorMsg(db->pParse, "out of memory"); db->pParse->rc = SQLITE_NOMEM_BKPT; } } + return 0; } /* diff --git a/src/pragma.c b/src/pragma.c index 50a3f20c8c..587238d3a9 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1251,6 +1251,10 @@ void sqlite3Pragma( (void)sqlite3_finalize(pDummy); sqlite3DbFree(db, zSql); } + if( db->mallocFailed ){ + sqlite3ErrorMsg(db->pParse, "out of memory"); + db->pParse->rc = SQLITE_NOMEM_BKPT; + } pHash = &db->aDb[ii].pSchema->tblHash; break; } diff --git a/src/prepare.c b/src/prepare.c index d2cc2d098c..4499e9a413 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -568,8 +568,10 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ /* ** Free all memory allocations in the pParse object */ -void sqlite3ParserReset(Parse *pParse){ +void sqlite3ParseObjectReset(Parse *pParse){ sqlite3 *db = pParse->db; + assert( db!=0 ); + assert( db->pParse==pParse ); assert( pParse->nested==0 ); #ifndef SQLITE_OMIT_SHARED_CACHE sqlite3DbFree(db, pParse->aTableLock); @@ -584,11 +586,12 @@ void sqlite3ParserReset(Parse *pParse){ if( pParse->pConstExpr ){ sqlite3ExprListDelete(db, pParse->pConstExpr); } - if( db ){ - assert( db->lookaside.bDisable >= pParse->disableLookaside ); - db->lookaside.bDisable -= pParse->disableLookaside; - db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; - } + assert( db->lookaside.bDisable >= pParse->disableLookaside ); + db->lookaside.bDisable -= pParse->disableLookaside; + db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue; + assert( pParse->db->pParse==pParse ); + db->pParse = pParse->pOuterParse; + pParse->db = 0; pParse->disableLookaside = 0; } @@ -601,7 +604,7 @@ void sqlite3ParserReset(Parse *pParse){ ** cost for this mechansim (an extra malloc), so it should not be used ** for common cleanups that happen on most calls. But for less ** common cleanups, we save a single NULL-pointer comparison in -** sqlite3ParserReset(), which reduces the total CPU cycle count. +** sqlite3ParseObjectReset(), which reduces the total CPU cycle count. ** ** If a memory allocation error occurs, then the cleanup happens immediately. ** When either SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the @@ -641,6 +644,24 @@ void *sqlite3ParserAddCleanup( return pPtr; } +/* +** Turn bulk memory into a valid Parse object and link that Parse object +** into database connection db. +** +** Call sqlite3ParseObjectReset() to undo this operation. +** +** Caution: Do not confuse this routine with sqlite3ParseObjectInit() which +** is generated by Lemon. +*/ +void sqlite3ParseObjectInit(Parse *pParse, sqlite3 *db){ + memset(PARSE_HDR(pParse), 0, PARSE_HDR_SZ); + memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); + assert( db->pParse!=pParse ); + pParse->pOuterParse = db->pParse; + db->pParse = pParse; + pParse->db = db; +} + /* ** Compile the UTF-8 encoded SQL statement zSql into a statement handle. */ @@ -657,8 +678,12 @@ static int sqlite3Prepare( int i; /* Loop counter */ Parse sParse; /* Parsing context */ - memset(&sParse, 0, PARSE_HDR_SZ); + /* sqlite3ParseObjectInit(&sParse, db); // inlined for performance */ + memset(PARSE_HDR(&sParse), 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ); + sParse.pOuterParse = db->pParse; + db->pParse = &sParse; + sParse.db = db; sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ @@ -714,7 +739,6 @@ static int sqlite3Prepare( sqlite3VtabUnlockList(db); - sParse.db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; @@ -781,7 +805,7 @@ static int sqlite3Prepare( end_prepare: - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); return rc; } static int sqlite3LockAndPrepare( diff --git a/src/resolve.c b/src/resolve.c index 5bcaf8dcd6..aae046e760 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -1214,7 +1214,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ break; } } - return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); + return pParse->nErr ? WRC_Abort : WRC_Continue; } /* @@ -1628,7 +1629,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ */ if( (p->selFlags & SF_Expanded)==0 ){ sqlite3SelectPrep(pParse, p, pOuterNC); - return (pParse->nErr || db->mallocFailed) ? WRC_Abort : WRC_Prune; + return pParse->nErr ? WRC_Abort : WRC_Prune; } isCompound = p->pPrior!=0; @@ -1676,7 +1677,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ if( pItem->zName ) pParse->zAuthContext = pItem->zName; sqlite3ResolveSelectNames(pParse, pItem->pSelect, pOuterNC); pParse->zAuthContext = zSavedContext; - if( pParse->nErr || db->mallocFailed ) return WRC_Abort; + if( pParse->nErr ) return WRC_Abort; + assert( db->mallocFailed==0 ); /* If the number of references to the outer context changed when ** expressions in the sub-select were resolved, the sub-select diff --git a/src/select.c b/src/select.c index 432d015de0..4ad3c58367 100644 --- a/src/select.c +++ b/src/select.c @@ -1404,7 +1404,7 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ p->nRef = 1; memset(&p[1], 0, nExtra); }else{ - sqlite3OomFault(db); + return (KeyInfo*)sqlite3OomFault(db); } return p; } @@ -3436,6 +3436,7 @@ static int multiSelectOrderBy( for(i=2; ipPrior; } } pPrior = pSplit->pPrior; + assert( pPrior!=0 ); pSplit->pPrior = 0; pPrior->pNext = 0; assert( p->pOrderBy == pOrderBy ); @@ -5587,7 +5588,8 @@ static int selectExpander(Walker *pWalker, Select *p){ /* Process NATURAL keywords, and ON and USING clauses of joins. */ - if( pParse->nErr || db->mallocFailed || sqliteProcessJoin(pParse, p) ){ + assert( db->mallocFailed==0 || pParse->nErr!=0 ); + if( pParse->nErr || sqliteProcessJoin(pParse, p) ){ return WRC_Abort; } @@ -5884,12 +5886,13 @@ void sqlite3SelectPrep( NameContext *pOuterNC /* Name context for container */ ){ assert( p!=0 || pParse->db->mallocFailed ); + assert( pParse->db->pParse==pParse ); if( pParse->db->mallocFailed ) return; if( p->selFlags & SF_HasTypeInfo ) return; sqlite3SelectExpand(pParse, p); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; sqlite3ResolveSelectNames(pParse, p, pOuterNC); - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; sqlite3SelectAddTypeInfo(pParse, p); } @@ -5906,8 +5909,10 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){ int i; struct AggInfo_func *pFunc; int nReg = pAggInfo->nFunc + pAggInfo->nColumn; + assert( pParse->db->pParse==pParse ); + assert( pParse->db->mallocFailed==0 || pParse->nErr!=0 ); if( nReg==0 ) return; - if( pParse->nErr || pParse->db->mallocFailed ) return; + if( pParse->nErr ) return; #ifdef SQLITE_DEBUG /* Verify that all AggInfo registers are within the range specified by ** AggInfo.mnReg..AggInfo.mxReg */ @@ -6330,10 +6335,12 @@ int sqlite3Select( u8 minMaxFlag; /* Flag for min/max queries */ db = pParse->db; + assert( pParse==db->pParse ); v = sqlite3GetVdbe(pParse); - if( p==0 || db->mallocFailed || pParse->nErr ){ + if( p==0 || pParse->nErr ){ return 1; } + assert( db->mallocFailed==0 ); if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; #if SELECTTRACE_ENABLED SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); @@ -6368,9 +6375,10 @@ int sqlite3Select( p->selFlags |= SF_NoopOrderBy; } sqlite3SelectPrep(pParse, p, 0); - if( pParse->nErr || db->mallocFailed ){ + if( pParse->nErr ){ goto select_end; } + assert( db->mallocFailed==0 ); assert( p->pEList!=0 ); #if SELECTTRACE_ENABLED if( sqlite3SelectTrace & 0x104 ){ @@ -6414,7 +6422,7 @@ int sqlite3Select( #ifndef SQLITE_OMIT_WINDOWFUNC if( sqlite3WindowRewrite(pParse, p) ){ - assert( db->mallocFailed || pParse->nErr>0 ); + assert( pParse->nErr ); goto select_end; } #if SELECTTRACE_ENABLED @@ -7509,7 +7517,7 @@ int sqlite3Select( */ select_end: assert( db->mallocFailed==0 || db->mallocFailed==1 ); - pParse->nErr += db->mallocFailed; + assert( db->mallocFailed==0 || pParse->nErr!=0 ); sqlite3ExprListDelete(db, pMinMaxOrderBy); #ifdef SQLITE_DEBUG if( pAggInfo && !db->mallocFailed ){ diff --git a/src/shell.c.in b/src/shell.c.in index c15d2e54d4..0a903672cc 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1086,6 +1086,7 @@ struct ShellState { u8 bSafeModePersist; /* The long-term value of bSafeMode */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */ + int inputNesting; /* Track nesting level of .read and other redirects */ int outCount; /* Revert to stdout when reaching zero */ int cnt; /* Number of records displayed so far */ int lineno; /* Line number of last line read from in */ @@ -1250,6 +1251,12 @@ static const char *modeDescr[] = { #define SEP_Unit "\x1F" #define SEP_Record "\x1E" +/* +** Limit input nesting via .read or any other input redirect. +** It's not too expensive, so a generous allowance can be made. +*/ +#define MAX_INPUT_NESTING 25 + /* ** A callback for the sqlite3_log() interface. */ @@ -10940,6 +10947,13 @@ static int process_input(ShellState *p){ int startline = 0; /* Line number for start of current input */ QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ + if( p->inputNesting==MAX_INPUT_NESTING ){ + /* This will be more informative in a later version. */ + utf8_printf(stderr,"Input nesting limit (%d) reached at line %d." + " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); + return 1; + } + ++p->inputNesting; p->lineno = 0; while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ fflush(p->out); @@ -11022,6 +11036,7 @@ static int process_input(ShellState *p){ } free(zSql); free(zLine); + --p->inputNesting; return errCnt>0; } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 6c5548ecd2..7c043a5281 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9471,13 +9471,14 @@ int sqlite3_vtab_nochange(sqlite3_context*); /* ** CAPI3REF: Determine The Collation For a Virtual Table Constraint +** METHOD: sqlite3_index_info ** ** This function may only be called from within a call to the [xBestIndex] ** method of a [virtual table]. This function returns a pointer to a string ** that is the name of the appropriate collation sequence to use for text ** comparisons on the constraint identified by its arguments. ** -** The first argument must be the pointer to the sqlite3_index_info object +** The first argument must be the pointer to the [sqlite3_index_info] object ** that is the first parameter to the xBestIndex() method. The second argument ** must be an index into the aConstraint[] array belonging to the ** sqlite3_index_info structure passed to xBestIndex. @@ -9485,7 +9486,7 @@ int sqlite3_vtab_nochange(sqlite3_context*); ** Important: ** The first parameter must be the same pointer that is passed into the ** xBestMethod() method. The first parameter may not be a pointer to a -** different sqlite3_index_info object, even an exact copy. +** different [sqlite3_index_info] object, even an exact copy. ** ** The return value is computed as follows: ** @@ -9503,6 +9504,99 @@ int sqlite3_vtab_nochange(sqlite3_context*); */ SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int); +/* +** CAPI3REF: Determine if a virtual table query is DISTINCT +** METHOD: sqlite3_index_info +** +** This API may only be used from within an xBestIndex() callback. The +** results of calling it from outside of an xBestIndex() callback are +** undefined and probably harmful. +** +** ^The sqlite3_vtab_distinct() returns an integer that is either 0, 1, or +** 2. The integer returned by sqlite3_vtab_distinct() gives the virtual table +** additional information about how the query planner wants the output to be +** ordered. As long as the virtual table can meet the ordering requirements +** of the query planner, it may set the "orderByConsumed" flag. +** +**
  1. +** ^If the sqlite3_vtab_distinct() interface returns 0, that means +** that the query planner needs the virtual table to return all rows in the +** sort order defined by the "nOrderBy" and "aOrderBy" fields of the +** [sqlite3_index_info] object. This is the default expectation. If the +** virtual table outputs all rows in sorted order, then it is always safe for +** the xBestIndex method to set the "orderByConsumed" flag, regardless of +** what the return value from sqlite3_vtab_distinct(). +**

  2. +** ^(If the sqlite3_vtab_distinct() interface returns 1, that means +** that the query planner does not need the rows to be returned in sorted order +** as long as all rows with the same values in all columns identified by the +** "aOrderBy" field are adjacent.)^ This mode is used when the query planner +** is doing a GROUP BY. +**

  3. +** ^(If the sqlite3_vtab_distinct() interface returns 2, that means +** that the query planner does not need the rows returned in any particular +** order, as long as rows with the same values in all "aOrderBy" columns +** are adjacent.)^ ^(Furthermore, only a single row for each particular +** combination of values in the columns identified by the "aOrderBy" field +** needs to be returned.)^ ^It is ok always for two or more rows with the same +** values in all "aOrderBy" columns to be returned, as long as all such rows +** are adjacent. ^The virtual table may, if it chooses, omit extra rows +** that have the same value for all columns identified by "aOrderBy". +** ^However omitting the extra rows is optional. +** This mode is used for a DISTINCT query. +**

+** +** ^For the purposes of comparing virtual table output values to see if the +** values are same value for sorting purposes, two NULL values are considered +** to be the same. In other words, the comparison operator is "IS" +** (or "IS NOT DISTINCT FROM") and not "==". +** +** If a virtual table implementation is unable to meet the requirements +** specified above, then it must not set the "orderByConsumed" flag in the +** [sqlite3_index_info] object or an incorrect answer may result. +** +** ^A virtual table implementation is always free to return rows in any order +** it wants, as long as the "orderByConsumed" flag is not set. ^When the +** the "orderByConsumed" flag is unset, the query planner will add extra +** [bytecode] to ensure that the final results returned by the SQL query are +** ordered correctly. The use of the "orderByConsumed" flag and the +** sqlite3_vtab_distinct() interface is merely an optimization. ^Careful +** use of the sqlite3_vtab_distinct() interface and the "orderByConsumed" +** flag might help queries against a virtual table to run faster. Being +** overly aggressive and setting the "orderByConsumed" flag when it is not +** valid to do so, on the other hand, might cause SQLite to return incorrect +** results. +*/ +int sqlite3_vtab_distinct(sqlite3_index_info*); + +/* +** CAPI3REF: Constraint values in xBestIndex() +** METHOD: sqlite3_index_info +** +** This API may only be used from within an xBestIndex() callback. The +** results of calling it from outside of an xBestIndex() callback are +** undefined and probably harmful. +** +** ^When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within +** the [xBestIndex] method of a [virtual table] implementation, with P being +** a copy of the [sqlite3_index_info] object pointer passed into xBestIndex and +** J being a 0-based index into P->aConstraint[], then this routine +** attempts to set *V to be the value on the right-hand side of +** that constraint if the right-hand side is a known constant. ^If the +** right-hand side of the constraint is not known, then *V is set to a NULL +** pointer. ^The sqlite3_vtab_rhs_value(P,J,V) interface returns SQLITE_OK if +** and only if *V is set to a value. ^The sqlite3_vtab_rhs_value(P,J,V) +** inteface returns SQLITE_NOTFOUND if the right-hand side of the J-th +** constraint is not available. ^The sqlite3_vtab_rhs_value() interface +** can return an result code other than SQLITE_OK or SQLITE_NOTFOUND if +** something goes wrong. +** +** ^The sqlite3_value object returned in *V remains valid for the duration of +** the xBestIndex method code. ^When xBestIndex returns, the sqlite3_value +** object returned by sqlite3_vtab_rhs_value() is automatically deallocated. +*/ +int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal); + /* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 5b82346227..88010b9d8d 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -346,6 +346,8 @@ struct sqlite3_api_routines { void*, void(*)(void*)); /* Version 3.38.0 and later */ int (*error_offset)(sqlite3*); + int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**); + int (*vtab_distinct)(sqlite3_index_info*); }; /* @@ -659,6 +661,8 @@ typedef int (*sqlite3_loadext_entry)( #define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages /* Version 3.38.0 and later */ #define sqlite3_error_offset sqlite3_api->error_offset +#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value +#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct #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 53c2969265..38a733e669 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3604,6 +3604,7 @@ struct Parse { **************************************************************************/ int aTempReg[8]; /* Holding area for temporary registers */ + Parse *pOuterParse; /* Outer Parse object when nested */ Token sNameToken; /* Token with unqualified schema object name */ /************************************************************************ @@ -3654,7 +3655,8 @@ struct Parse { /* ** Sizes and pointers of various parts of the Parse object. */ -#define PARSE_HDR_SZ offsetof(Parse,aTempReg) /* Recursive part w/o aColCache*/ +#define PARSE_HDR(X) (((char*)(X))+offsetof(Parse,zErrMsg)) +#define PARSE_HDR_SZ (offsetof(Parse,aTempReg)-offsetof(Parse,zErrMsg)) /* Recursive part w/o aColCache*/ #define PARSE_RECURSE_SZ offsetof(Parse,sLastToken) /* Recursive part */ #define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */ #define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */ @@ -4996,7 +4998,7 @@ int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *, FuncDestructor *pDestructor ); void sqlite3NoopDestructor(void*); -void sqlite3OomFault(sqlite3*); +void *sqlite3OomFault(sqlite3*); void sqlite3OomClear(sqlite3*); int sqlite3ApiExit(sqlite3 *db, int); int sqlite3OpenTempDatabase(Parse *); @@ -5117,7 +5119,8 @@ FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*); int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); -void sqlite3ParserReset(Parse*); +void sqlite3ParseObjectInit(Parse*,sqlite3*); +void sqlite3ParseObjectReset(Parse*); void *sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*); #ifdef SQLITE_ENABLE_NORMALIZE char *sqlite3Normalize(Vdbe*, const char*); diff --git a/src/test1.c b/src/test1.c index b68b038458..c6c2cb2f47 100644 --- a/src/test1.c +++ b/src/test1.c @@ -7568,6 +7568,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( #ifndef SQLITE_OMIT_VIRTUALTABLE extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*); #endif + extern int sqlite3_qpvtab_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*); @@ -7598,6 +7599,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( #ifndef SQLITE_OMIT_VIRTUALTABLE { "prefixes", sqlite3_prefixes_init }, #endif + { "qpvtab", sqlite3_qpvtab_init }, { "regexp", sqlite3_regexp_init }, { "remember", sqlite3_remember_init }, { "series", sqlite3_series_init }, diff --git a/src/trigger.c b/src/trigger.c index 6b71c9816e..420ef7061c 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -918,6 +918,7 @@ static void codeReturningTrigger( assert( v!=0 ); assert( pParse->bReturning ); + assert( db->pParse==pParse ); pReturning = pParse->u1.pReturning; assert( pTrigger == &(pReturning->retTrig) ); memset(&sSelect, 0, sizeof(sSelect)); @@ -928,7 +929,8 @@ static void codeReturningTrigger( sFrom.a[0].pTab = pTab; sFrom.a[0].iCursor = -1; sqlite3SelectPrep(pParse, &sSelect, 0); - if( db->mallocFailed==0 && pParse->nErr==0 ){ + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); sqlite3GenerateColumnNames(pParse, &sSelect); } sqlite3ExprListDelete(db, sSelect.pEList); @@ -946,7 +948,7 @@ static void codeReturningTrigger( pParse->eTriggerOp = pTrigger->op; pParse->pTriggerTab = pTab; if( sqlite3ResolveExprListNames(&sNC, pNew)==SQLITE_OK - && !db->mallocFailed + && ALWAYS(!db->mallocFailed) ){ int i; int nCol = pNew->nExpr; @@ -1110,8 +1112,8 @@ static TriggerPrg *codeRowTrigger( Vdbe *v; /* Temporary VM */ NameContext sNC; /* Name context for sub-vdbe */ SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ - Parse *pSubParse; /* Parse context for sub-vdbe */ int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + Parse sSubParse; /* Parse context for sub-vdbe */ assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); assert( pTop->pVdbe ); @@ -1133,19 +1135,17 @@ static TriggerPrg *codeRowTrigger( /* Allocate and populate a new Parse context to use for coding the ** trigger sub-program. */ - pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); - if( !pSubParse ) return 0; + sqlite3ParseObjectInit(&sSubParse, db); memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pSubParse; - pSubParse->db = db; - pSubParse->pTriggerTab = pTab; - pSubParse->pToplevel = pTop; - pSubParse->zAuthContext = pTrigger->zName; - pSubParse->eTriggerOp = pTrigger->op; - pSubParse->nQueryLoop = pParse->nQueryLoop; - pSubParse->disableVtab = pParse->disableVtab; + sNC.pParse = &sSubParse; + sSubParse.pTriggerTab = pTab; + sSubParse.pToplevel = pTop; + sSubParse.zAuthContext = pTrigger->zName; + sSubParse.eTriggerOp = pTrigger->op; + sSubParse.nQueryLoop = pParse->nQueryLoop; + sSubParse.disableVtab = pParse->disableVtab; - v = sqlite3GetVdbe(pSubParse); + v = sqlite3GetVdbe(&sSubParse); if( v ){ VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", pTrigger->zName, onErrorText(orconf), @@ -1171,14 +1171,14 @@ static TriggerPrg *codeRowTrigger( if( db->mallocFailed==0 && SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) ){ - iEndTrigger = sqlite3VdbeMakeLabel(pSubParse); - sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + iEndTrigger = sqlite3VdbeMakeLabel(&sSubParse); + sqlite3ExprIfFalse(&sSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } sqlite3ExprDelete(db, pWhen); } /* Code the trigger program into the sub-vdbe. */ - codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + codeTriggerProgram(&sSubParse, pTrigger->step_list, orconf); /* Insert an OP_Halt at the end of the sub-program. */ if( iEndTrigger ){ @@ -1186,23 +1186,24 @@ static TriggerPrg *codeRowTrigger( } sqlite3VdbeAddOp0(v, OP_Halt); VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); + transferParseError(pParse, &sSubParse); - transferParseError(pParse, pSubParse); - if( db->mallocFailed==0 && pParse->nErr==0 ){ + if( pParse->nErr==0 ){ + assert( db->mallocFailed==0 ); pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); } - pProgram->nMem = pSubParse->nMem; - pProgram->nCsr = pSubParse->nTab; + pProgram->nMem = sSubParse.nMem; + pProgram->nCsr = sSubParse.nTab; pProgram->token = (void *)pTrigger; - pPrg->aColmask[0] = pSubParse->oldmask; - pPrg->aColmask[1] = pSubParse->newmask; + pPrg->aColmask[0] = sSubParse.oldmask; + pPrg->aColmask[1] = sSubParse.newmask; sqlite3VdbeDelete(v); + }else{ + transferParseError(pParse, &sSubParse); } - assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); - sqlite3ParserReset(pSubParse); - sqlite3StackFree(db, pSubParse); - + assert( !sSubParse.pTriggerPrg && !sSubParse.nMaxArg ); + sqlite3ParseObjectReset(&sSubParse); return pPrg; } @@ -1257,7 +1258,7 @@ void sqlite3CodeRowTriggerDirect( Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ TriggerPrg *pPrg; pPrg = getRowTrigger(pParse, p, pTab, orconf); - assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); + assert( pPrg || pParse->nErr ); /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program ** is a pointer to the sub-vdbe containing the trigger program. */ diff --git a/src/update.c b/src/update.c index b174e1eb50..03ff489256 100644 --- a/src/update.c +++ b/src/update.c @@ -347,9 +347,11 @@ void sqlite3Update( memset(&sContext, 0, sizeof(sContext)); db = pParse->db; - if( pParse->nErr || db->mallocFailed ){ + assert( db->pParse==pParse ); + if( pParse->nErr ){ goto update_cleanup; } + assert( db->mallocFailed==0 ); /* Locate the table which we want to update. */ diff --git a/src/util.c b/src/util.c index dec205df43..84608739ae 100644 --- a/src/util.c +++ b/src/util.c @@ -189,6 +189,8 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ char *zMsg; va_list ap; sqlite3 *db = pParse->db; + assert( db!=0 ); + assert( db->pParse==pParse ); db->errByteOffset = -2; va_start(ap, zFormat); zMsg = sqlite3VMPrintf(db, zFormat, ap); @@ -196,6 +198,10 @@ void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ if( db->errByteOffset<-1 ) db->errByteOffset = -1; if( db->suppressErr ){ sqlite3DbFree(db, zMsg); + if( db->mallocFailed ){ + pParse->nErr++; + pParse->rc = SQLITE_NOMEM; + } }else{ pParse->nErr++; sqlite3DbFree(db, pParse->zErrMsg); diff --git a/src/vdbe.c b/src/vdbe.c index 9cb44e96f5..88a6dbbda3 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -677,7 +677,6 @@ static u64 filterHash(const Mem *aMem, const Op *pOp){ int i, mx; u64 h = 0; - i = pOp->p3; assert( pOp->p4type==P4_INT32 ); for(i=pOp->p3, mx=i+pOp->p4.i; iflags & (MEM_Real|MEM_IntReal))==MEM_Real ); + testcase( (pIn1->flags & (MEM_Real|MEM_IntReal))==MEM_IntReal ); if( pIn1->flags & MEM_Int ){ /* When applying REAL affinity, if the result is still an MEM_Int ** that will fit in 6 bytes, then change the type to MEM_IntReal @@ -3003,7 +3004,7 @@ case OP_TypeCheck: { pIn1->flags |= MEM_Real; pIn1->flags &= ~MEM_Int; } - }else if( (pIn1->flags & MEM_Real)==0 ){ + }else if( (pIn1->flags & (MEM_Real|MEM_IntReal))==0 ){ goto vdbe_type_error; } break; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index e2d13e0799..5e042a6b32 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1378,8 +1378,7 @@ void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){ */ static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){ assert( p->nOp>0 || p->aOp==0 ); - assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed - || p->pParse->nErr>0 ); + assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->pParse->nErr>0 ); if( p->nOp ){ assert( p->aOp ); sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment); diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 512442fd2a..a18ee05b52 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -152,10 +152,9 @@ int sqlite3_blob_open( sqlite3_mutex_enter(db->mutex); pBlob = (Incrblob *)sqlite3DbMallocZero(db, sizeof(Incrblob)); - do { - memset(&sParse, 0, sizeof(Parse)); + while(1){ + sqlite3ParseObjectInit(&sParse,db); if( !pBlob ) goto blob_open_out; - sParse.db = db; sqlite3DbFree(db, zErr); zErr = 0; @@ -332,7 +331,9 @@ int sqlite3_blob_open( goto blob_open_out; } rc = blobSeekToRow(pBlob, iRow, &zErr); - } while( (++nAttempt)=SQLITE_MAX_SCHEMA_RETRY || rc!=SQLITE_SCHEMA ) break; + sqlite3ParseObjectReset(&sParse); + } blob_open_out: if( rc==SQLITE_OK && db->mallocFailed==0 ){ @@ -343,7 +344,7 @@ blob_open_out: } sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr); sqlite3DbFree(db, zErr); - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; diff --git a/src/vtab.c b/src/vtab.c index ef86dd6bd4..11c076e4e6 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -825,9 +825,8 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ pTab = pCtx->pTab; assert( IsVirtual(pTab) ); - memset(&sParse, 0, sizeof(sParse)); + sqlite3ParseObjectInit(&sParse, db); sParse.eParseMode = PARSE_MODE_DECLARE_VTAB; - sParse.db = db; /* We should never be able to reach this point while loading the ** schema. Nevertheless, defend against that (turn off db->init.busy) ** in case a bug arises. */ @@ -881,7 +880,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){ sqlite3VdbeFinalize(sParse.pVdbe); } sqlite3DeleteTable(db, sParse.pNewTable); - sqlite3ParserReset(&sParse); + sqlite3ParseObjectReset(&sParse); db->init.busy = initBusy; assert( (rc&0xff)==rc ); diff --git a/src/wal.c b/src/wal.c index e8016d8df9..85a2093d8b 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3237,7 +3237,9 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ } /* Allocate a buffer to read frames into */ - szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE; + assert( (pWal->szPage & (pWal->szPage-1))==0 ); + assert( pWal->szPage>=512 && pWal->szPage<=65536 ); + szFrame = pWal->szPage + WAL_FRAME_HDRSIZE; aFrame = (u8 *)sqlite3_malloc64(szFrame); if( aFrame==0 ){ rc = SQLITE_NOMEM_BKPT; @@ -3251,7 +3253,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){ ** the caller. */ aSaveCksum[0] = pWal->hdr.aFrameCksum[0]; aSaveCksum[1] = pWal->hdr.aFrameCksum[1]; - for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); + for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->szPage); iOffset+szFrame<=szWal; iOffset+=szFrame ){ diff --git a/src/where.c b/src/where.c index 35bad3f696..32ec17ec42 100644 --- a/src/where.c +++ b/src/where.c @@ -30,8 +30,12 @@ */ typedef struct HiddenIndexInfo HiddenIndexInfo; struct HiddenIndexInfo { - WhereClause *pWC; /* The Where clause being analyzed */ - Parse *pParse; /* The parsing context */ + WhereClause *pWC; /* The Where clause being analyzed */ + Parse *pParse; /* The parsing context */ + int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ + sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST + ** because extra space is allocated to hold up + ** to nTerm such values */ }; /* Forward declaration of methods */ @@ -1095,18 +1099,18 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter( /* ** Allocate and populate an sqlite3_index_info structure. It is the ** responsibility of the caller to eventually release the structure -** by passing the pointer returned by this function to sqlite3_free(). +** by passing the pointer returned by this function to freeIndexInfo(). */ static sqlite3_index_info *allocateIndexInfo( - Parse *pParse, /* The parsing context */ + WhereInfo *pWInfo, /* The WHERE clause */ WhereClause *pWC, /* The WHERE clause being analyzed */ Bitmask mUnusable, /* Ignore terms with these prereqs */ SrcItem *pSrc, /* The FROM clause term that is the vtab */ - ExprList *pOrderBy, /* The ORDER BY clause */ u16 *pmNoOmit /* Mask of terms not to omit */ ){ int i, j; int nTerm; + Parse *pParse = pWInfo->pParse; struct sqlite3_index_constraint *pIdxCons; struct sqlite3_index_orderby *pIdxOrderBy; struct sqlite3_index_constraint_usage *pUsage; @@ -1116,7 +1120,9 @@ static sqlite3_index_info *allocateIndexInfo( sqlite3_index_info *pIdxInfo; u16 mNoOmit = 0; const Table *pTab; - + int eDistinct = 0; + ExprList *pOrderBy = pWInfo->pOrderBy; + assert( pSrc!=0 ); pTab = pSrc->pTab; assert( pTab!=0 ); @@ -1200,6 +1206,9 @@ static sqlite3_index_info *allocateIndexInfo( } if( i==n){ nOrderBy = n; + if( (pWInfo->wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY)) ){ + eDistinct = 1 + ((pWInfo->wctrlFlags & WHERE_DISTINCTBY)!=0); + } } } @@ -1207,13 +1216,14 @@ static sqlite3_index_info *allocateIndexInfo( */ pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) ); + + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) + + sizeof(sqlite3_value*)*nTerm ); if( pIdxInfo==0 ){ sqlite3ErrorMsg(pParse, "out of memory"); return 0; } pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; - pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1]; + pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm]; pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm]; pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy]; pIdxInfo->aConstraint = pIdxCons; @@ -1221,6 +1231,7 @@ static sqlite3_index_info *allocateIndexInfo( pIdxInfo->aConstraintUsage = pUsage; pHidden->pWC = pWC; pHidden->pParse = pParse; + pHidden->eDistinct = eDistinct; for(i=j=0, pTerm=pWC->a; inTerm; i++, pTerm++){ u16 op; if( (pTerm->wtFlags & TERM_OK)==0 ) continue; @@ -1278,6 +1289,24 @@ static sqlite3_index_info *allocateIndexInfo( return pIdxInfo; } +/* +** Free an sqlite3_index_info structure allocated by allocateIndexInfo() +** and possibly modified by xBestIndex methods. +*/ +static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden; + int i; + assert( pIdxInfo!=0 ); + pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->pParse!=0 ); + assert( pHidden->pParse->db==db ); + for(i=0; inConstraint; i++){ + sqlite3ValueFree(pHidden->aRhs[i]); /* IMP: R-14553-25174 */ + pHidden->aRhs[i] = 0; + } + sqlite3DbFree(db, pIdxInfo); +} + /* ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() @@ -3633,6 +3662,54 @@ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){ return zRet; } +/* +** This interface is callable from within the xBestIndex callback only. +** +** If possible, set (*ppVal) to point to an object containing the value +** on the right-hand-side of constraint iCons. +*/ +int sqlite3_vtab_rhs_value( + sqlite3_index_info *pIdxInfo, /* Copy of first argument to xBestIndex */ + int iCons, /* Constraint for which RHS is wanted */ + sqlite3_value **ppVal /* Write value extracted here */ +){ + HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1]; + sqlite3_value *pVal = 0; + int rc = SQLITE_OK; + if( iCons<0 || iCons>=pIdxInfo->nConstraint ){ + rc = SQLITE_MISUSE; /* EV: R-30545-25046 */ + }else{ + if( pH->aRhs[iCons]==0 ){ + WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset]; + rc = sqlite3ValueFromExpr( + pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db), + SQLITE_AFF_BLOB, &pH->aRhs[iCons] + ); + testcase( rc!=SQLITE_OK ); + } + pVal = pH->aRhs[iCons]; + } + *ppVal = pVal; + + if( rc==SQLITE_OK && pVal==0 ){ /* IMP: R-19933-32160 */ + rc = SQLITE_NOTFOUND; /* IMP: R-36424-56542 */ + } + + return rc; +} + + +/* +** Return true if ORDER BY clause may be handled as DISTINCT. +*/ +int sqlite3_vtab_distinct(sqlite3_index_info *pIdxInfo){ + HiddenIndexInfo *pHidden = (HiddenIndexInfo*)&pIdxInfo[1]; + assert( pHidden->eDistinct==0 + || pHidden->eDistinct==1 + || pHidden->eDistinct==2 ); + return pHidden->eDistinct; +} + /* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. @@ -3682,8 +3759,7 @@ static int whereLoopAddVirtual( pNew = pBuilder->pNew; pSrc = &pWInfo->pTabList->a[pNew->iTab]; assert( IsVirtual(pSrc->pTab) ); - p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy, - &mNoOmit); + p = allocateIndexInfo(pWInfo, pWC, mUnusable, pSrc, &mNoOmit); if( p==0 ) return SQLITE_NOMEM_BKPT; pNew->rSetup = 0; pNew->wsFlags = WHERE_VIRTUALTABLE; @@ -3691,7 +3767,7 @@ static int whereLoopAddVirtual( pNew->u.vtab.needFree = 0; nConstraint = p->nConstraint; if( whereLoopResize(pParse->db, pNew, nConstraint) ){ - sqlite3DbFree(pParse->db, p); + freeIndexInfo(pParse->db, p); return SQLITE_NOMEM_BKPT; } @@ -3771,7 +3847,7 @@ static int whereLoopAddVirtual( } if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); - sqlite3DbFreeNN(pParse->db, p); + freeIndexInfo(pParse->db, p); WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc)); return rc; } @@ -3815,7 +3891,6 @@ static int whereLoopAddOr( int i, j; sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm)); @@ -5207,7 +5282,6 @@ WhereInfo *sqlite3WhereBegin( /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 ); if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0; - sWLB.pOrderBy = pOrderBy; /* The number of tables in the FROM clause is limited by the number of ** bits in a Bitmask @@ -5417,9 +5491,10 @@ WhereInfo *sqlite3WhereBegin( if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){ pWInfo->revMask = ALLBITS; } - if( pParse->nErr || db->mallocFailed ){ + if( pParse->nErr ){ goto whereBeginError; } + assert( db->mallocFailed==0 ); #ifdef WHERETRACE_ENABLED if( sqlite3WhereTrace ){ sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); diff --git a/src/whereInt.h b/src/whereInt.h index a97b4afc69..1304a40a77 100644 --- a/src/whereInt.h +++ b/src/whereInt.h @@ -386,7 +386,6 @@ struct WhereMaskSet { struct WhereLoopBuilder { WhereInfo *pWInfo; /* Information about this WHERE */ WhereClause *pWC; /* WHERE clause terms */ - ExprList *pOrderBy; /* ORDER BY clause */ WhereLoop *pNew; /* Template WhereLoop */ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */ #ifdef SQLITE_ENABLE_STAT4 diff --git a/src/wherecode.c b/src/wherecode.c index a08ff84d28..b3ae06cae2 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -824,6 +824,9 @@ static int codeAllEqualityTerms( sqlite3VdbeAddOp2(v, OP_Copy, r1, regBase+j); } } + } + for(j=nSkip; jaLTerm[j]; if( pTerm->eOperator & WO_IN ){ if( pTerm->pExpr->flags & EP_xIsSelect ){ /* No affinity ever needs to be (or should be) applied to a value @@ -838,7 +841,8 @@ static int codeAllEqualityTerms( sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); VdbeCoverage(v); } - if( pParse->db->mallocFailed==0 && pParse->nErr==0 ){ + if( pParse->nErr==0 ){ + assert( pParse->db->mallocFailed==0 ); if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){ zAff[j] = SQLITE_AFF_BLOB; } @@ -2356,7 +2360,7 @@ Bitmask sqlite3WhereCodeOneLoopStart( WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, WHERE_OR_SUBCLAUSE, iCovCur); - assert( pSubWInfo || pParse->nErr || db->mallocFailed ); + assert( pSubWInfo || pParse->nErr ); if( pSubWInfo ){ WhereLoop *pSubLoop; int addrExplain = sqlite3WhereExplainOneScan( diff --git a/src/window.c b/src/window.c index d7c412a3a6..2b2e856ec9 100644 --- a/src/window.c +++ b/src/window.c @@ -1106,12 +1106,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ sqlite3ParserAddCleanup(pParse, sqlite3DbFree, pTab); } - if( rc ){ - if( pParse->nErr==0 ){ - assert( pParse->db->mallocFailed ); - sqlite3ErrorToParser(pParse->db, SQLITE_NOMEM); - } - } + assert( rc==SQLITE_OK || pParse->nErr!=0 ); return rc; } diff --git a/test/alterauth2.test b/test/alterauth2.test index 27ded1c3ea..6f9242d364 100644 --- a/test/alterauth2.test +++ b/test/alterauth2.test @@ -99,6 +99,7 @@ do_auth_test 1.2 { do_auth_test 1.3 { ALTER TABLE t2 DROP COLUMN c; } { + {SQLITE_ALTER_TABLE main t2 c {}} {SQLITE_FUNCTION {} like {} {}} {SQLITE_FUNCTION {} sqlite_drop_column {} {}} {SQLITE_FUNCTION {} sqlite_rename_quotefix {} {}} diff --git a/test/auth.test b/test/auth.test index d8f23a15e2..d8afa2dbff 100644 --- a/test/auth.test +++ b/test/auth.test @@ -2069,6 +2069,15 @@ ifcapable {altertable} { do_test auth-1.302 { set authargs } {main t5 {} {}} + db eval BEGIN + set authargs {} + do_execsql_test auth-1.302-drop-1 { + ALTER TABLE t5 DROP COLUMN new_col_1; + } {} + db eval ROLLBACK + do_test auth-1.302-drop-2 { + set authargs + } {main t5 new_col_1 {}} do_test auth-1.303 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { @@ -2088,6 +2097,16 @@ ifcapable {altertable} { do_test auth-1.305 { set authargs } {main t5 {} {}} + db eval BEGIN + set authargs {} + do_execsql_test auth-1.305-drop-1 { + ALTER TABLE t5 DROP COLUMN new_col_1; + SELECT 1 FROM sqlite_schema WHERE name='t5' AND sql LIKE '%new_col_1%'; + } {1} + db eval ROLLBACK + do_test auth-1.305-drop-2 { + set authargs + } {main t5 new_col_1 {}} do_test auth-1.306 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { @@ -2104,10 +2123,22 @@ ifcapable {altertable} { set x [execsql {SELECT sql FROM temp.sqlite_master WHERE type='t5'}] regexp new_col_3 $x } {0} - do_test auth-1.308 { set authargs } {main t5 {} {}} + db eval BEGIN + set authargs {} + do_catchsql_test auth-1.308-drop-1 { + ALTER TABLE t5 DROP COLUMN new_col_1; + } {1 {not authorized}} + do_execsql_test auth-1.308-drop-2 { + SELECT 1 FROM sqlite_schema WHERE name='t5' AND sql LIKE '%new_col_1%'; + } {1} + do_test auth-1.308-drop-3 { + set authargs + } {main t5 new_col_1 {}} + db eval ROLLBACK + execsql {DROP TABLE t5} } ;# ifcapable altertable @@ -2159,7 +2190,7 @@ ifcapable {cte} { # MAIN: CREATE TABLE t1(a,b); # ifcapable altertable&&vtab { - do_test 1.350 { + do_test auth-1.350 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] @@ -2177,7 +2208,7 @@ ifcapable altertable&&vtab { do_test auth-1.352 { set authargs } {main t1 {} {}} - do_test 1.353 { + do_test auth-1.353 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] @@ -2195,7 +2226,7 @@ ifcapable altertable&&vtab { do_test auth-1.355 { set authargs } {main t1 {} {}} - do_test 1.356 { + do_test auth-1.356 { proc auth {code arg1 arg2 arg3 arg4 args} { if {$code=="SQLITE_ALTER_TABLE"} { set ::authargs [list $arg1 $arg2 $arg3 $arg4] @@ -2207,10 +2238,10 @@ ifcapable altertable&&vtab { ALTER TABLE t1 RENAME COLUMN bcdefg TO b; } } {1 {not authorized}} - do_execsql_test auth-1.356 { + do_execsql_test auth-1.357 { SELECT name FROM pragma_table_info('t1') ORDER BY cid; } {a bcdefg} - do_test auth-1.357 { + do_test auth-1.358 { set authargs } {main t1 {} {}} } diff --git a/test/e_expr.test b/test/e_expr.test index 93ca0268c8..3f8d5ad85c 100644 --- a/test/e_expr.test +++ b/test/e_expr.test @@ -265,13 +265,10 @@ do_execsql_test e_expr-6.4 {SELECT -72%5} {-2} do_execsql_test e_expr-6.5 {SELECT 72.35%5} {2.0} #------------------------------------------------------------------------- -# Test that the results of all binary operators are either numeric or -# NULL, except for the || operator, which may evaluate to either a text -# value or NULL. -# -# EVIDENCE-OF: R-20665-17792 The result of any binary operator is either -# a numeric value or NULL, except for the || concatenation operator -# which always evaluates to either NULL or a text value. +# EVIDENCE-OF: R-15904-00746 The result of any binary operator is either +# a numeric value or NULL, except for the || concatenation operator, and +# the -> and ->> extract operators which evaluate to either +# NULL or a text value. # set literals { 1 'abc' 2 'hexadecimal' 3 '' diff --git a/test/e_fkey.test b/test/e_fkey.test index ab3c29033b..3662a39981 100644 --- a/test/e_fkey.test +++ b/test/e_fkey.test @@ -48,9 +48,9 @@ proc do_detail_test {tn sql res} { ########################################################################### #------------------------------------------------------------------------- -# EVIDENCE-OF: R-33710-56344 In order to use foreign key constraints in +# EVIDENCE-OF: R-37672-59189 In order to use foreign key constraints in # SQLite, the library must be compiled with neither -# SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER defined. +# SQLITE_OMIT_FOREIGN_KEY nor SQLITE_OMIT_TRIGGER defined. # ifcapable trigger&&foreignkey { do_test e_fkey-1 { diff --git a/test/fuzzdata8.db b/test/fuzzdata8.db index 960b157625..46112fff5f 100644 Binary files a/test/fuzzdata8.db and b/test/fuzzdata8.db differ diff --git a/test/printf2.test b/test/printf2.test index 998038f88e..2f6208b17f 100644 --- a/test/printf2.test +++ b/test/printf2.test @@ -12,10 +12,13 @@ # focus of this file is testing the printf() SQL function. # # -# EVIDENCE-OF: R-63057-40065 The printf(FORMAT,...) SQL function works +# EVIDENCE-OF: R-32560-14372 The format(FORMAT,...) SQL function works # like the sqlite3_mprintf() C-language function and the printf() # function from the standard C library. # +# EVIDENCE-OF: R-64900-53159 The printf() SQL function is an alias for +# the format() SQL function. +# set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -24,7 +27,7 @@ source $testdir/tester.tcl # then the result is NULL. # do_execsql_test printf2-1.1 { - SELECT quote(printf()), quote(printf(NULL,1,2,3)); + SELECT quote(format()), quote(format(NULL,1,2,3)); } {NULL NULL} @@ -32,31 +35,31 @@ do_execsql_test printf2-1.2 { SELECT printf('hello'); } {hello} do_execsql_test printf2-1.3 { - SELECT printf('%d,%d,%d',55,-11,3421); + SELECT format('%d,%d,%d',55,-11,3421); } {55,-11,3421} do_execsql_test printf2-1.4 { SELECT printf('%d,%d,%d',55,'-11',3421); } {55,-11,3421} do_execsql_test printf2-1.5 { - SELECT printf('%d,%d,%d,%d',55,'-11',3421); + SELECT format('%d,%d,%d,%d',55,'-11',3421); } {55,-11,3421,0} do_execsql_test printf2-1.6 { SELECT printf('%.2f',3.141592653); } {3.14} do_execsql_test printf2-1.7 { - SELECT printf('%.*f',2,3.141592653); + SELECT format('%.*f',2,3.141592653); } {3.14} do_execsql_test printf2-1.8 { SELECT printf('%*.*f',5,2,3.141592653); } {{ 3.14}} do_execsql_test printf2-1.9 { - SELECT printf('%d',314159.2653); + SELECT format('%d',314159.2653); } {314159} do_execsql_test printf2-1.10 { SELECT printf('%lld',314159.2653); } {314159} do_execsql_test printf2-1.11 { - SELECT printf('%lld%n',314159.2653,'hi'); + SELECT format('%lld%n',314159.2653,'hi'); } {314159} do_execsql_test printf2-1.12 { SELECT printf('%n',0); @@ -65,7 +68,7 @@ do_execsql_test printf2-1.12 { # EVIDENCE-OF: R-17002-27534 The %z format is interchangeable with %s. # do_execsql_test printf2-1.12 { - SELECT printf('%.*z',5,'abcdefghijklmnop'); + SELECT format('%.*z',5,'abcdefghijklmnop'); } {abcde} do_execsql_test printf2-1.13 { SELECT printf('%c','abcdefghijklmnop'); diff --git a/test/rowvalue.test b/test/rowvalue.test index c92d344821..7c101d9b3e 100644 --- a/test/rowvalue.test +++ b/test/rowvalue.test @@ -692,6 +692,21 @@ do_execsql_test 30.3 { UPDATE t2 SET (d,d,a)=(SELECT EXISTS(SELECT 1 IN(SELECT max( 1 IN(SELECT x ORDER BY 1)) OVER(PARTITION BY sum((SELECT y FROM t1 UNION SELECT x ORDER BY 1)))INTERSECT SELECT EXISTS(SELECT 1 FROM t1 UNION SELECT x ORDER BY 1) ORDER BY 1) ORDERa)|9 AS blob, 2, 3) FROM t1 WHERE xaConstraint[], then this routine attempts to set *V to be the +# value on the right-hand side of that constraint if the right-hand side +# is a known constant. +# +do_execsql_test 1.1 { + SELECT rhs FROM qpvtab + WHERE cn='a' + AND a=12345 +} {12345} +do_execsql_test 1.2 { + SELECT rhs FROM qpvtab + WHERE cn='a' + AND a<>4.5 +} {4.5} +do_execsql_test 1.3 { + SELECT rhs FROM qpvtab + WHERE cn='a' + AND 'quokka' < a +} {'quokka'} +do_execsql_test 1.4 { + SELECT rhs FROM qpvtab + WHERE cn='a' + AND a IS NULL +} {{}} +do_execsql_test 1.5 { + SELECT rhs FROM qpvtab + WHERE cn='a' + AND a GLOB x'0123' +} {x'0123'} + +# EVIDENCE-OF: R-29440-53190 If the right-hand side of the constraint is +# not known, then *V is set to a NULL pointer. +# +do_execsql_test 2.1 { + SELECT typeof(rhs) FROM qpvtab WHERE cn='a' AND a=format('abc'); +} {null} +do_execsql_test 2.2 { + SELECT typeof(rhs) FROM qpvtab WHERE cn='a' AND a=?2 +} {null} + +# EVIDENCE-OF: R-14553-25174 When xBestIndex returns, the sqlite3_value +# object returned by sqlite3_vtab_rhs_value() is automatically +# deallocated. +# +# Where this not the case, the following "finish_test" statement would +# report a memory leak. +# +finish_test diff --git a/test/walro2.test b/test/walro2.test index 34408c1695..1eb9d1c3ee 100644 --- a/test/walro2.test +++ b/test/walro2.test @@ -50,357 +50,362 @@ do_execsql_test 0.0 { CREATE TABLE t1(x); } {wal} set MINSHMSZ [file size test.db-shm] +set dfltpgsz [db one {PRAGMA page_size}] foreach bZeroShm {0 1} { -set TN [expr $bZeroShm+1] -do_multiclient_test tn { - - # Close all connections and delete the database. - # - code1 { db close } - code2 { db2 close } - code3 { db3 close } - forcedelete test.db - - # Do not run tests with the connections in the same process. - # - if {$tn==2} continue - - foreach c {code1 code2 code3} { - $c { - sqlite3_shutdown - sqlite3_config_uri 1 - } - } - - do_test $TN.1.1 { - code2 { sqlite3 db2 test.db } - sql2 { - CREATE TABLE t1(x, y); - PRAGMA journal_mode = WAL; - INSERT INTO t1 VALUES('a', 'b'); - INSERT INTO t1 VALUES('c', 'd'); - } - file exists test.db-shm - } {1} - - do_test $TN.1.2.1 { - copy_to_test2 $bZeroShm - code1 { - sqlite3 db file:test.db2?readonly_shm=1 - } - - sql1 { SELECT * FROM t1 } - } {a b c d} - do_test $TN.1.2.2 { - sql1 { SELECT * FROM t1 } - } {a b c d} - - do_test $TN.1.3.1 { - code3 { sqlite3 db3 test.db2 } - sql3 { SELECT * FROM t1 } - } {a b c d} - - do_test $TN.1.3.2 { - sql1 { SELECT * FROM t1 } - } {a b c d} - - code1 { db close } - code2 { db2 close } - code3 { db3 close } - - do_test $TN.2.1 { - code2 { sqlite3 db2 test.db } - sql2 { - INSERT INTO t1 VALUES('e', 'f'); - INSERT INTO t1 VALUES('g', 'h'); - } - file exists test.db-shm - } {1} - - do_test $TN.2.2 { - copy_to_test2 $bZeroShm - code1 { - sqlite3 db file:test.db2?readonly_shm=1 - } - sql1 { - BEGIN; - SELECT * FROM t1; - } - } {a b c d e f g h} - - do_test $TN.2.3.1 { - code3 { sqlite3 db3 test.db2 } - sql3 { SELECT * FROM t1 } - } {a b c d e f g h} - do_test $TN.2.3.2 { - sql3 { INSERT INTO t1 VALUES('i', 'j') } - code3 { db3 close } - sql1 { COMMIT } - } {} - do_test $TN.2.3.3 { - sql1 { SELECT * FROM t1 } - } {a b c d e f g h i j} - - - #----------------------------------------------------------------------- - # 3.1.*: That a readonly_shm connection can read a database file if both - # the *-wal and *-shm files are zero bytes in size. - # - # 3.2.*: That it flushes the cache if, between transactions on a db with a - # zero byte *-wal file, some other connection modifies the db, then - # does "PRAGMA wal_checkpoint=truncate" to truncate the wal file - # back to zero bytes in size. - # - # 3.3.*: That, if between transactions some other process wraps the wal - # file, the readonly_shm client reruns recovery. - # - catch { code1 { db close } } - catch { code2 { db2 close } } - catch { code3 { db3 close } } - do_test $TN.3.1.0 { - list [file exists test.db-wal] [file exists test.db-shm] - } {0 0} - do_test $TN.3.1.1 { - close [open test.db-wal w] - close [open test.db-shm w] - code1 { - sqlite3 db file:test.db?readonly_shm=1 - } - sql1 { SELECT * FROM t1 } - } {a b c d e f g h} - - do_test $TN.3.2.0 { - list [file size test.db-wal] [file size test.db-shm] - } {0 0} - do_test $TN.3.2.1 { - code2 { sqlite3 db2 test.db } - sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate } - code2 { db2 close } - sql1 { SELECT * FROM t1 } - } {a b c d e f g h 1 2} - do_test $TN.3.2.2 { - list [file size test.db-wal] [file size test.db-shm] - } [list 0 $MINSHMSZ] - - do_test $TN.3.3.0 { - code2 { sqlite3 db2 test.db } - sql2 { - INSERT INTO t1 VALUES(3, 4); - INSERT INTO t1 VALUES(5, 6); - INSERT INTO t1 VALUES(7, 8); - INSERT INTO t1 VALUES(9, 10); - } - code2 { db2 close } - code1 { db close } - list [file size test.db-wal] [file size test.db-shm] - } [list [wal_file_size 4 1024] $MINSHMSZ] - do_test $TN.3.3.1 { - code1 { sqlite3 db file:test.db?readonly_shm=1 } - sql1 { SELECT * FROM t1 } - } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10} - do_test $TN.3.3.2 { - code2 { sqlite3 db2 test.db } - sql2 { - PRAGMA wal_checkpoint; - DELETE FROM t1; - INSERT INTO t1 VALUES('i', 'ii'); - } - code2 { db2 close } - list [file size test.db-wal] [file size test.db-shm] - } [list [wal_file_size 4 1024] $MINSHMSZ] - do_test $TN.3.3.3 { - sql1 { SELECT * FROM t1 } - } {i ii} - - #----------------------------------------------------------------------- - # - # - catch { code1 { db close } } - catch { code2 { db2 close } } - catch { code3 { db3 close } } - - do_test $TN.4.0 { - code1 { forcedelete test.db } - code1 { sqlite3 db test.db } - sql1 { - PRAGMA journal_mode = wal; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES('hello'); - INSERT INTO t1 VALUES('world'); - } - - copy_to_test2 $bZeroShm - - code1 { db close } - } {} - - do_test $TN.4.1.1 { - code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } - sql2 { SELECT * FROM t1 } - } {hello world} - - do_test $TN.4.1.2 { - code3 { sqlite3 db3 test.db2 } - sql3 { - INSERT INTO t1 VALUES('!'); - PRAGMA wal_checkpoint = truncate; - } - code3 { db3 close } - } {} - do_test $TN.4.1.3 { - sql2 { SELECT * FROM t1 } - } {hello world !} - - catch { code1 { db close } } - catch { code2 { db2 close } } - catch { code3 { db3 close } } - - do_test $TN.4.2.1 { - code1 { sqlite3 db test.db } - sql1 { - INSERT INTO t1 VALUES('!'); - INSERT INTO t1 VALUES('!'); - - PRAGMA cache_size = 10; - CREATE TABLE t2(x); - - BEGIN; - WITH s(i) AS ( - SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500 - ) - INSERT INTO t2 SELECT randomblob(500) FROM s; - SELECT count(*) FROM t2; - } - } {500} - set sz [file size test.db-wal] - do_test $TN.4.2.2.(sz=$sz) { - expr {$sz>400000} - } {1} - do_test $TN.4.2.4 { - file_control_persist_wal db 1; db close - - copy_to_test2 $bZeroShm - code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } - sql2 { - SELECT * FROM t1; - SELECT count(*) FROM t2; - } - } {hello world ! ! 0} - - #----------------------------------------------------------------------- - # - # - catch { code1 { db close } } - catch { code2 { db2 close } } - catch { code3 { db3 close } } - - do_test $TN.5.0 { - code1 { forcedelete test.db } - code1 { sqlite3 db test.db } - sql1 { - PRAGMA journal_mode = wal; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES('hello'); - INSERT INTO t1 VALUES('world'); - INSERT INTO t1 VALUES('!'); - INSERT INTO t1 VALUES('world'); - INSERT INTO t1 VALUES('hello'); - } - - copy_to_test2 $bZeroShm + for {set pgsz 512} {$pgsz<=65536} {set pgsz [expr {$pgsz*2}]} { + set TN [expr $bZeroShm+1]-$pgsz + do_multiclient_test tn { + + # Close all connections and delete the database. + # + code1 { db close } + code2 { db2 close } + code3 { db3 close } + forcedelete test.db + + # Do not run tests with the connections in the same process. + # + if {$tn==2} continue - code1 { db close } - } {} - - do_test $TN.5.1 { - code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } - sql2 { - SELECT * FROM t1; - } - } {hello world ! world hello} - - do_test $TN.5.2 { - code1 { - proc handle_read {op args} { - if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { - set ::res2 [sql2 { SELECT * FROM t1 }] + foreach c {code1 code2 code3} { + $c { + sqlite3_shutdown + sqlite3_config_uri 1 } - puts "$msg xRead $args" - return "SQLITE_OK" } - testvfs tvfs -fullshm 1 - - sqlite3 db file:test.db2?vfs=tvfs - db eval { SELECT * FROM sqlite_master } - - tvfs filter xRead - tvfs script handle_read - } - sql1 { - PRAGMA wal_checkpoint = truncate; - } - code1 { set ::res2 } - } {hello world ! world hello} - - do_test $TN.5.3 { - code1 { db close } - code1 { tvfs delete } - } {} - - #----------------------------------------------------------------------- - # - # - catch { code1 { db close } } - catch { code2 { db2 close } } - catch { code3 { db3 close } } - - do_test $TN.6.1 { - code1 { forcedelete test.db } - code1 { sqlite3 db test.db } - sql1 { - PRAGMA journal_mode = wal; - CREATE TABLE t1(x); - INSERT INTO t1 VALUES('hello'); - INSERT INTO t1 VALUES('world'); - INSERT INTO t1 VALUES('!'); - INSERT INTO t1 VALUES('world'); - INSERT INTO t1 VALUES('hello'); - } - - copy_to_test2 $bZeroShm - code1 { db close } - } {} - - do_test $TN.6.2 { - code1 { - set ::nRem 5 - proc handle_read {op args} { - if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { - incr ::nRem -1 - if {$::nRem==0} { - code2 { sqlite3 db2 test.db2 } - sql2 { PRAGMA wal_checkpoint = truncate } + do_test $TN.1.1 { + code2 { sqlite3 db2 test.db } + sql2 "PRAGMA page_size=$::pgsz" + sql2 { + CREATE TABLE t1(x, y); + PRAGMA journal_mode = WAL; + INSERT INTO t1 VALUES('a', 'b'); + INSERT INTO t1 VALUES('c', 'd'); + } + file exists test.db-shm + } {1} + + do_test $TN.1.2.1 { + copy_to_test2 $bZeroShm + code1 { + sqlite3 db file:test.db2?readonly_shm=1 + } + + sql1 { SELECT * FROM t1 } + } {a b c d} + do_test $TN.1.2.2 { + sql1 { SELECT * FROM t1 } + } {a b c d} + + do_test $TN.1.3.1 { + code3 { sqlite3 db3 test.db2 } + sql3 { SELECT * FROM t1 } + } {a b c d} + + do_test $TN.1.3.2 { + sql1 { SELECT * FROM t1 } + } {a b c d} + + code1 { db close } + code2 { db2 close } + code3 { db3 close } + + do_test $TN.2.1 { + code2 { sqlite3 db2 test.db } + sql2 "PRAGMA page_size=$::pgsz;" + sql2 { + INSERT INTO t1 VALUES('e', 'f'); + INSERT INTO t1 VALUES('g', 'h'); + } + file exists test.db-shm + } {1} + + do_test $TN.2.2 { + copy_to_test2 $bZeroShm + code1 { + sqlite3 db file:test.db2?readonly_shm=1 + } + sql1 { + BEGIN; + SELECT * FROM t1; + } + } {a b c d e f g h} + + do_test $TN.2.3.1 { + code3 { sqlite3 db3 test.db2 } + sql3 { SELECT * FROM t1 } + } {a b c d e f g h} + do_test $TN.2.3.2 { + sql3 { INSERT INTO t1 VALUES('i', 'j') } + code3 { db3 close } + sql1 { COMMIT } + } {} + do_test $TN.2.3.3 { + sql1 { SELECT * FROM t1 } + } {a b c d e f g h i j} + + + #----------------------------------------------------------------------- + # 3.1.*: That a readonly_shm connection can read a database file if both + # the *-wal and *-shm files are zero bytes in size. + # + # 3.2.*: That it flushes the cache if, between transactions on a db with a + # zero byte *-wal file, some other connection modifies the db, then + # does "PRAGMA wal_checkpoint=truncate" to truncate the wal file + # back to zero bytes in size. + # + # 3.3.*: That, if between transactions some other process wraps the wal + # file, the readonly_shm client reruns recovery. + # + catch { code1 { db close } } + catch { code2 { db2 close } } + catch { code3 { db3 close } } + do_test $TN.3.1.0 { + list [file exists test.db-wal] [file exists test.db-shm] + } {0 0} + do_test $TN.3.1.1 { + close [open test.db-wal w] + close [open test.db-shm w] + code1 { + sqlite3 db file:test.db?readonly_shm=1 + } + sql1 { SELECT * FROM t1 } + } {a b c d e f g h} + + do_test $TN.3.2.0 { + list [file size test.db-wal] [file size test.db-shm] + } {0 0} + do_test $TN.3.2.1 { + code2 { sqlite3 db2 test.db } + sql2 { INSERT INTO t1 VALUES(1, 2) ; PRAGMA wal_checkpoint=truncate } + code2 { db2 close } + sql1 { SELECT * FROM t1 } + } {a b c d e f g h 1 2} + if {$pgsz!=$dfltpgsz} continue + do_test $TN.3.2.2 { + list [file size test.db-wal] [file size test.db-shm] + } [list 0 $MINSHMSZ] + do_test $TN.3.3.0 { + code2 { sqlite3 db2 test.db } + sql2 { + INSERT INTO t1 VALUES(3, 4); + INSERT INTO t1 VALUES(5, 6); + INSERT INTO t1 VALUES(7, 8); + INSERT INTO t1 VALUES(9, 10); + } + code2 { db2 close } + code1 { db close } + list [file size test.db-wal] [file size test.db-shm] + } [list [wal_file_size 4 1024] $MINSHMSZ] + do_test $TN.3.3.1 { + code1 { sqlite3 db file:test.db?readonly_shm=1 } + sql1 { SELECT * FROM t1 } + } {a b c d e f g h 1 2 3 4 5 6 7 8 9 10} + do_test $TN.3.3.2 { + code2 { sqlite3 db2 test.db } + sql2 { + PRAGMA wal_checkpoint; + DELETE FROM t1; + INSERT INTO t1 VALUES('i', 'ii'); + } + code2 { db2 close } + list [file size test.db-wal] [file size test.db-shm] + } [list [wal_file_size 4 1024] $MINSHMSZ] + do_test $TN.3.3.3 { + sql1 { SELECT * FROM t1 } + } {i ii} + + #----------------------------------------------------------------------- + # + # + catch { code1 { db close } } + catch { code2 { db2 close } } + catch { code3 { db3 close } } + + do_test $TN.4.0 { + code1 { forcedelete test.db } + code1 { sqlite3 db test.db } + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('hello'); + INSERT INTO t1 VALUES('world'); + } + + copy_to_test2 $bZeroShm + + code1 { db close } + } {} + + do_test $TN.4.1.1 { + code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } + sql2 { SELECT * FROM t1 } + } {hello world} + + do_test $TN.4.1.2 { + code3 { sqlite3 db3 test.db2 } + sql3 { + INSERT INTO t1 VALUES('!'); + PRAGMA wal_checkpoint = truncate; + } + code3 { db3 close } + } {} + do_test $TN.4.1.3 { + sql2 { SELECT * FROM t1 } + } {hello world !} + + catch { code1 { db close } } + catch { code2 { db2 close } } + catch { code3 { db3 close } } + + do_test $TN.4.2.1 { + code1 { sqlite3 db test.db } + sql1 { + INSERT INTO t1 VALUES('!'); + INSERT INTO t1 VALUES('!'); + + PRAGMA cache_size = 10; + CREATE TABLE t2(x); + + BEGIN; + WITH s(i) AS ( + SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<500 + ) + INSERT INTO t2 SELECT randomblob(500) FROM s; + SELECT count(*) FROM t2; + } + } {500} + set sz [file size test.db-wal] + do_test $TN.4.2.2.(sz=$sz) { + expr {$sz>400000} + } {1} + do_test $TN.4.2.4 { + file_control_persist_wal db 1; db close + + copy_to_test2 $bZeroShm + code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } + sql2 { + SELECT * FROM t1; + SELECT count(*) FROM t2; + } + } {hello world ! ! 0} + + #----------------------------------------------------------------------- + # + # + catch { code1 { db close } } + catch { code2 { db2 close } } + catch { code3 { db3 close } } + + do_test $TN.5.0 { + code1 { forcedelete test.db } + code1 { sqlite3 db test.db } + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('hello'); + INSERT INTO t1 VALUES('world'); + INSERT INTO t1 VALUES('!'); + INSERT INTO t1 VALUES('world'); + INSERT INTO t1 VALUES('hello'); + } + + copy_to_test2 $bZeroShm + + code1 { db close } + } {} + + do_test $TN.5.1 { + code2 { sqlite3 db2 file:test.db2?readonly_shm=1 } + sql2 { + SELECT * FROM t1; + } + } {hello world ! world hello} + + do_test $TN.5.2 { + code1 { + proc handle_read {op args} { + if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { + set ::res2 [sql2 { SELECT * FROM t1 }] + } + puts "$msg xRead $args" + return "SQLITE_OK" } + testvfs tvfs -fullshm 1 + + sqlite3 db file:test.db2?vfs=tvfs + db eval { SELECT * FROM sqlite_master } + + tvfs filter xRead + tvfs script handle_read } - return "SQLITE_OK" - } - testvfs tvfs -fullshm 1 - - tvfs filter xRead - tvfs script handle_read - - sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs - db eval { SELECT * FROM t1 } + sql1 { + PRAGMA wal_checkpoint = truncate; + } + code1 { set ::res2 } + } {hello world ! world hello} + + do_test $TN.5.3 { + code1 { db close } + code1 { tvfs delete } + } {} + + #----------------------------------------------------------------------- + # + # + catch { code1 { db close } } + catch { code2 { db2 close } } + catch { code3 { db3 close } } + + do_test $TN.6.1 { + code1 { forcedelete test.db } + code1 { sqlite3 db test.db } + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES('hello'); + INSERT INTO t1 VALUES('world'); + INSERT INTO t1 VALUES('!'); + INSERT INTO t1 VALUES('world'); + INSERT INTO t1 VALUES('hello'); + } + + copy_to_test2 $bZeroShm + + code1 { db close } + } {} + + do_test $TN.6.2 { + code1 { + set ::nRem 5 + proc handle_read {op args} { + if {$op=="xRead" && [file tail [lindex $args 0]]=="test.db2-wal"} { + incr ::nRem -1 + if {$::nRem==0} { + code2 { sqlite3 db2 test.db2 } + sql2 { PRAGMA wal_checkpoint = truncate } + } + } + return "SQLITE_OK" + } + testvfs tvfs -fullshm 1 + + tvfs filter xRead + tvfs script handle_read + + sqlite3 db file:test.db2?readonly_shm=1&vfs=tvfs + db eval { SELECT * FROM t1 } + } + } {hello world ! world hello} + + do_test $TN.6.3 { + code1 { db close } + code1 { tvfs delete } + } {} } - } {hello world ! world hello} - - do_test $TN.6.3 { - code1 { db close } - code1 { tvfs delete } - } {} -} + } ;# for pgsz } ;# foreach bZeroShm finish_test