-C If\sa\squery\suses\san\sindex\swhere\sone\sor\smore\sof\sthe\scolumns\sof\sthe\sindex\sis\nan\sexpression\sand\sif\sthe\scorresponding\sexpression\sis\nused\selsewhere\sin\sthe\squery,\sthen\sstrive\sto\sread\sthe\svalue\sof\sthe\sexpression\nout\sof\sthe\sindex,\srather\sthan\srecomputing\sit.\s\sThis\sis\sthe\n"Indexed\sExpression\sOptimizations".
-D 2022-10-19T11:22:21.760
+C Improve\sthe\sability\sof\sthe\squery\splanner\sto\srecognize\scovering\sindexes\seven\non\stables\swith\smore\sthan\s63\scolumns\sand\swhere\sthe\sindex\sis\sover\scolumns\nbeyond\sthe\s63rd\scolumn.
+D 2022-10-24T13:50:04.830
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c c12833de69cb655751487d2c5a59607e36be1c58ba1f4bd536609909ad47b319
-F src/delete.c 2bee826a5e1c2b2018895084850d69a7f60269ae6fa5e8c247e2a4e9faf2ccad
+F src/delete.c fb363f793fc40df36843cc67fb692659164b6109690f4efdd254ab785f8e511d
F src/expr.c dc17ca9523293e15abe61f3d0002556d765b0fc3b985e67c07ec3de9dd36e636
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
-F src/fkey.c bd0138acdc008c1845ccf92f8e73787880562de649471804801c06fed814c765
+F src/fkey.c e6408a869edeb222b049f6bb991a862a2c4a161da27794018897f196b1c2f584
F src/func.c 2ccf4ae12430b1ae7096be5f0675887e1bd0732828af0ac0f7496339b7c6edee
F src/global.c 0dea3065ea72a65ae941559b6686aad6516d4913e76fa4f79a95ff7787f624ec
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
-F src/select.c a75028a3d35a4e56f7428fd716d6340f0ca6666a98cd48af246ad450c093a5b1
+F src/select.c cd8413de730aa27beb39420d08b0fd821b998819e4e998573d4c06a7fd9a113b
F src/shell.c.in c1986496062f9dba4ed5b70db06b5e0f32e1954cdcfab0b30372c6c186796810
F src/sqlite.h.in 59f5e145b8d7a915ca29c6bf4a1f00e3112c1605c9ac5c627c45060110332ba2
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
-F src/sqliteInt.h d748774587502dcf0e77f54ddde94b3fdff32bdea91f2905b72a290d1a891fb9
+F src/sqliteInt.h c3ee9809b0a1349608a9abd61b3ecf4f6258554f3b777a18457c99aab84eef6f
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F src/tokenize.c d3615f0cbe4db5949503bf5916f3cd4fa5de855d5b4ef560f3b6dd5629423a1e
F src/treeview.c 56724725c62a0d0f408f7c257475dc33309198afee36a1d18be1bc268b09055e
F src/trigger.c 132009e29dc8099e2cbabc67b5608f03487e61d5dc4b1c5c98187ef703326e2c
-F src/update.c 48cfd516a07cd095e9b6a83d345dd683a89dd53724d092bf6ed633c32fdeca16
+F src/update.c d714633472eb810723130c3fe2af8ba9a24368890d25a0be9c813ff304e3c04d
F src/upsert.c 0dd81b40206841814d46942a7337786932475f085716042d0cb2fc7791bf8ca4
F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507
F src/util.c e12939405e77906d06ab0b78c5f513dcd2b7cec2fbb553877b0abfece6067141
F src/wal.c 9eccc7ebb532a7b0fd3cabc16cff576b9afa763472272db67d84fb8cec96f5c0
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c 7607f1a68130c028255d8d56094ea602fc402c79e1e35a46e6282849d90d5fe4
-F src/where.c 5f54a44d4aa05c592ed8e19bad656ebb1a9d05157488edfa991e12b3098290f1
-F src/whereInt.h ffebbbad9359cc602c9cbb24d926f73fc1bf696f0edb4ff896afa32018aad690
-F src/wherecode.c 5e0b6dec8591e13f1f0af828d350e4a5dd2e3518b63d328f21bb38e2456dfeb7
+F src/where.c 6a8b2a825d3d2af6c9c290e4b93804a54551b75bef1b8c40407e298d5fae54a5
+F src/whereInt.h 752a870900422b8a6d77c51d74b9e9ce935ff7d5e7b6caf1e786823c51a18a14
+F src/wherecode.c cf67460973119c7b2141ad67daf8368dfb4871f225e2489f95effaa139007bfd
F src/whereexpr.c ca55a11c2443700fe084a1e039660688d7733c594a37697ee4bd99462e2c2f6a
F src/window.c 038c248267e74ff70a2bb9b1884d40fd145c5183b017823ecb6cbb14bc781478
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/wherelfault.test 9012e4ef5259058b771606616bd007af5d154e64cc25fa9fd4170f6411db44e3
F test/wherelimit.test 592081800806d297dd7449b1030c863d2883d6d42901837ccd2e5a9bd962edb0
F test/wherelimit2.test 9bf0aa56cca40ea0e4c5e2915341355a2bbc0859ec4ce1589197fe2a9d94635f
+F test/widetab1.test ea6e1d8ce3cf3fc9f0b6f5e4264a45d287944ee9becabbe8ad55ba80bb3cc225
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2aeee74
F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P e3754cc18824aad4113ad6d81e33e5c763beb9705fc6af88d8f8c870a03c731d
-Q +2435112867fbd7b6ebb7f2c2b9da57cdf1e23fab6c2869870b66133a9f9faedc
-R ffc6c9dee666f2afa446429523ea6435
+P 3da1032878bdc93f69b02926fb7243b31fe6b1a0ee93af68df52b203b0603dad
+Q +f058773e41495ddbae698f9e9a4f62b7003112ea8614dfad69471340058735e4
+R f77ec747567c3bc99f563b3d82675b4b
U drh
-Z cbff34b53dc91f70749ae3ac7b08f467
+Z 75744969cee191ad91201d95cd6f1fac
# Remove this line to create a well-formed Fossil manifest.
-3da1032878bdc93f69b02926fb7243b31fe6b1a0ee93af68df52b203b0603dad
\ No newline at end of file
+3d1992de4733d4e155f2d6d5268323410d1104ab56db50f08a2bb26bf87d926b
\ No newline at end of file
** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
*/
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,0,0,wcf, iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
** clause. For each row found, increment either the deferred or immediate
** foreign key constraint counter. */
if( pParse->nErr==0 ){
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0, 0);
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
if( pWInfo ){
sqlite3WhereEnd(pWInfo);
/* Begin the database scan. */
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
- p->pEList, wctrlFlags, p->nSelectRow);
+ p->pEList, p, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0, p,
WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
);
if( pWInfo==0 ) goto select_end;
SELECTTRACE(1,pParse,p,("WhereBegin\n"));
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
- 0, minMaxFlag, 0);
+ 0, p, minMaxFlag, 0);
if( pWInfo==0 ){
goto select_end;
}
#define MASKBIT(n) (((Bitmask)1)<<(n))
#define MASKBIT32(n) (((unsigned int)1)<<(n))
#define ALLBITS ((Bitmask)-1)
+#define TOPBIT (((Bitmask)1)<<(BMS-1))
/* A VList object records a mapping between parameters/variables/wildcards
** in the SQL statement (such as $abc, @pqr, or :xyz) and the integer
struct WindowRewrite *pRewrite; /* Window rewrite context */
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
+ struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
} u;
};
void sqlite3DeleteFrom(Parse*, SrcList*, Expr*, ExprList*, Expr*);
void sqlite3Update(Parse*, SrcList*, ExprList*,Expr*,int,ExprList*,Expr*,
Upsert*);
-WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
+WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,Select*,u16,int);
void sqlite3WhereEnd(WhereInfo*);
LogEst sqlite3WhereOutputRowCount(WhereInfo*);
int sqlite3WhereIsDistinct(WhereInfo*);
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
flags |= WHERE_ONEPASS_MULTIROW;
}
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,0,0,flags, iIdxCur);
if( pWInfo==0 ) goto update_cleanup;
/* A one-pass strategy that might update more than one row may not
regRowid = ++pParse->nMem;
/* Start scanning the virtual table */
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
+ pWInfo = sqlite3WhereBegin(pParse, pSrc,pWhere,0,0,0,WHERE_ONEPASS_DESIRED,0);
if( pWInfo==0 ) return;
/* Populate the argument registers. */
return 0;
}
+/*
+** Structure passed to the whereIsCoveringIndex Walker callback.
+*/
+struct CoveringIndexCheck {
+ Index *pIdx; /* The index */
+ int iTabCur; /* Cursor number for the corresponding table */
+};
+
+/*
+** Information passed in is pWalk->u.pCovIdxCk. Call is pCk.
+**
+** If the Expr node references the table with cursor pCk->iTabCur, then
+** make sure that column is covered by the index pCk->pIdx. We know that
+** all columns less than 63 (really BMS-1) are covered, so we don't need
+** to check them. But we do need to check any column at 63 or greater.
+**
+** If the index does not cover the column, then set pWalk->eCode to
+** non-zero and return WRC_Abort to stop the search.
+**
+** If this node does not disprove that the index can be a covering index,
+** then just return WRC_Continue, to continue the search.
+*/
+static int whereIsCoveringIndexWalkCallback(Walker *pWalk, Expr *pExpr){
+ int i; /* Loop counter */
+ const Index *pIdx; /* The index of interest */
+ const i16 *aiColumn; /* Columns contained in the index */
+ u16 nColumn; /* Number of columns in the index */
+ if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_AGG_COLUMN ) return WRC_Continue;
+ if( pExpr->iColumn<(BMS-1) ) return WRC_Continue;
+ if( pExpr->iTable!=pWalk->u.pCovIdxCk->iTabCur ) return WRC_Continue;
+ pIdx = pWalk->u.pCovIdxCk->pIdx;
+ aiColumn = pIdx->aiColumn;
+ nColumn = pIdx->nColumn;
+ for(i=0; i<nColumn; i++){
+ if( aiColumn[i]==pExpr->iColumn ) return WRC_Continue;
+ }
+ pWalk->eCode = 1;
+ return WRC_Abort;
+}
+
+
+/*
+** pIdx is an index that covers all of the low-number columns used by
+** pWInfo->pSelect (columns from 0 through 62). But there are columns
+** in pWInfo->pSelect beyond 62. This routine tries to answer the question
+** of whether pIdx covers *all* columns in the query.
+**
+** Return 0 if pIdx is a covering index. Return non-zero if pIdx is
+** not a covering index or if we are unable to determine if pIdx is a
+** covering index.
+**
+** This routine is an optimization. It is always safe to return non-zero.
+** But returning zero when non-zero should have been returned can lead to
+** incorrect bytecode and assertion faults.
+*/
+static SQLITE_NOINLINE u32 whereIsCoveringIndex(
+ WhereInfo *pWInfo, /* The WHERE clause context */
+ Index *pIdx, /* Index that is being tested */
+ int iTabCur /* Cursor for the table being indexed */
+){
+ int i;
+ struct CoveringIndexCheck ck;
+ Walker w;
+ if( pWInfo->pSelect==0 ){
+ /* We don't have access to the full query, so we cannot check to see
+ ** if pIdx is covering. Assume it is not. */
+ return 1;
+ }
+ for(i=0; i<pIdx->nColumn; i++){
+ if( pIdx->aiColumn[i]>=BMS-1 ) break;
+ }
+ if( i>=pIdx->nColumn ){
+ /* pIdx does not index any columns greater than 62, but we know from
+ ** colMask that columns greater than 62 are used, so this is not a
+ ** covering index */
+ return 1;
+ }
+ ck.pIdx = pIdx;
+ ck.iTabCur = iTabCur;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = whereIsCoveringIndexWalkCallback;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ w.u.pCovIdxCk = &ck;
+ w.eCode = 0;
+ sqlite3WalkSelect(&w, pWInfo->pSelect);
+ return w.eCode;
+}
+
/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
m = 0;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
+ if( m==TOPBIT ){
+ m = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
+ }
pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
}
Expr *pWhere, /* The WHERE clause */
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
ExprList *pResultSet, /* Query result set. Req'd for DISTINCT */
+ Select *pSelect, /* The entire SELECT statement */
u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
** If WHERE_USE_LIMIT, then the limit amount */
pWInfo->wctrlFlags = wctrlFlags;
pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
+ pWInfo->pSelect = pSelect;
memset(&pWInfo->nOBSat, 0,
offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
ExprList *pResultSet; /* Result set of the query */
Expr *pWhere; /* The complete WHERE clause */
LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
+ Select *pSelect; /* The entire SELECT statement containing WHERE */
int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
/* Loop through table entries that match term pOrTerm. */
ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1));
WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, 0,
WHERE_OR_SUBCLAUSE, iCovCur);
assert( pSubWInfo || pParse->nErr || db->mallocFailed );
if( pSubWInfo ){
--- /dev/null
+# 2022-10-24
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements test cases for wide table (tables with more than
+# 64 columns) and indexes that reference columns beyond the 63rd or 64th
+# column.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix widetab1
+
+
+# In order to pick the better index in the following query, SQLite needs to
+# be able to detect when an index that references later columns in a wide
+# table is a covering index.
+#
+do_execsql_test 100 {
+ CREATE TABLE a(
+ a00, a01, a02, a03, a04, a05, a06, a07, a08, a09,
+ a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,
+ a20, a21, a22, a23, a24, a25, a26, a27, a28, a29,
+ a30, a31, a32, a33, a34, a35, a36, a37, a38, a39,
+ a40, a41, a42, a43, a44, a45, a46, a47, a48, a49,
+ a50, a51, a52, a53, a54, a55, a56, a57, a58, a59,
+ pd, bn, vb, bc, cn, ie, qm);
+ CREATE INDEX a1 on a(pd, bn, vb, bc, cn); -- preferred index
+ CREATE INDEX a2 on a(pd, bc, ie, qm); -- suboptimal index
+ CREATE TABLE b(bg, bc, bn, iv, ln, mg);
+ CREATE INDEX b1 on b(bn, iv, bg);
+}
+do_eqp_test 110 {
+ SELECT dc, count(cn)
+ FROM (SELECT coalesce(b.bg, a.bc) as dc, cn
+ FROM a LEFT JOIN b
+ ON a.bn = b.bn
+ AND CASE WHEN a.vb IS NOT NULL THEN 1 ELSE 0 END = b.iv
+ WHERE pd BETWEEN 0 AND 10)
+ GROUP BY dc;
+} {
+ QUERY PLAN
+ |--SEARCH TABLE a USING COVERING INDEX a1 (pd>? AND pd<?)
+ |--SEARCH TABLE b USING COVERING INDEX b1 (bn=? AND iv=?)
+ `--USE TEMP B-TREE FOR GROUP BY
+}
+
+reset_db
+do_execsql_test 200 {
+ CREATE TABLE t1(
+ c00,c01,c02,c03,c04,c05,c06,c07,c08,c09,
+ c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,
+ c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,
+ c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,
+ c40,c41,c42,c43,c44,c45,c46,c47,c48,c49,
+ c50,c51,c52,c53,c54,c55,c56,c57,c58,c59,
+ c60,c61,c62,c63,c64,c65,c66,c67,c68,c69,
+ c70,c71,c72,c73,c74,c75,c76,c77,c78,c79,
+ c80,c81,c82,c83,c84,c85,c86,c87,c88,c89,
+ c90,c91,c92,c93,c94,c95,c96,c97,c98,c99,
+ a,b,c,d,e
+ );
+ CREATE INDEX t1x1 on t1(c00,a,b,
+ c01,c02,c03,c04,c05,c06,c07,c08,c09,
+ c10,c11,c12,c13,c14,c15,c16,c17,c18,c19,
+ c20,c21,c22,c23,c24,c25,c26,c27,c28,c29,
+ c30,c31,c32,c33,c34,c35,c36,c37,c38,c39,
+ c40,c41,c42,c43,c44,c45,c46,c47,c48,c49,
+ c50,c51,c52,c53,c54,c55,c56,c57,c58,c59,
+ c60,c61,c62,c63,c64,c65,c66,c67,c68,c69,
+ c70,c71,c72,c73,c74,c75,c76,c77,c78,c79,
+ c80,c81,c82,c83,c84,c85,c86,c87,c88,c89,
+ c90,c91,c92,c93,c94,c00,c96,c97,c98,c99
+ );
+ CREATE INDEX t1cd ON t1(c,d);
+ CREATE INDEX t1x2 ON t1(c01,c02,c03,a,b);
+ WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x+1000 FROM c WHERE x<9000)
+ INSERT INTO t1 SELECT
+ x+00, x+01, x+02, x+03, x+04, x+05, x+06, x+07, x+08, x+09,
+ x+10, x+11, x+12, x+13, x+14, x+15, x+16, x+17, x+18, x+19,
+ x+20, x+21, x+22, x+23, x+24, x+25, x+26, x+27, x+28, x+29,
+ x+30, x+31, x+32, x+33, x+34, x+35, x+36, x+37, x+38, x+39,
+ x+40, x+41, x+42, x+43, x+44, x+45, x+46, x+47, x+48, x+49,
+ x+50, x+51, x+52, x+53, x+54, x+55, x+56, x+57, x+58, x+59,
+ x+60, x+61, x+62, x+63, x+64, x+65, x+66, x+67, x+68, x+69,
+ x+70, x+71, x+72, x+73, x+74, x+75, x+76, x+77, x+78, x+79,
+ x+80, x+81, x+82, x+83, x+84, x+85, x+86, x+87, x+88, x+89,
+ x+90, x+91, x+92, x+93, x+94, x+95, x+96, x+97, x+98, x+99,
+ x+100, x+101, x+102, x+103, x+104 FROM c;
+}
+
+do_execsql_test 210 {SELECT sum(c62) FROM t1;} 45620
+do_execsql_test 220 {SELECT sum(c63) FROM t1;} 45630
+do_execsql_test 230 {SELECT sum(c64) FROM t1;} 45640
+do_execsql_test 240 {SELECT sum(c65) FROM t1;} 45650
+
+do_execsql_test 300 {
+ BEGIN;
+ SELECT sum(c62) FROM t1;
+ UPDATE t1 SET c62=c62+1 WHERE c00=1000;
+ SELECT sum(c62) FROM t1;
+} {45620 45621}
+do_execsql_test 310 {
+ SELECT sum(c65) FROM t1;
+ UPDATE t1 SET c65=c65+1 WHERE c00=1000;
+ SELECT sum(c65) FROM t1;
+ ROLLBACK;
+} {45650 45651}
+
+do_execsql_test 320 {
+ BEGIN;
+ SELECT count(*) FROM t1;
+ DELETE FROM t1 WHERE c=3102;
+ SELECT COUNT(*) FROM t1;
+ ROLLBACK;
+} {10 9}
+do_execsql_test 330 {
+ BEGIN;
+ SELECT count(*) FROM t1;
+ DELETE FROM t1 WHERE c=3102 AND d=3103;
+ SELECT COUNT(*) FROM t1;
+ ROLLBACK;
+} {10 9}
+do_execsql_test 340 {
+ BEGIN;
+ DELETE FROM t1 WHERE (c,d) IN (VALUES(3102,3103),(4102,4103),(5102,5103),(1,2));
+ SELECT count(*) FROM t1;
+ ROLLBACK;
+} {7}
+
+do_execsql_test 400 {
+ DROP INDEX t1cd;
+ DROP INDEX t1x1;
+ DROP INDEX t1x2;
+ CREATE INDEX t1x3 ON t1(c00,c05,c08);
+}
+do_execsql_test 410 {SELECT sum(c08) FROM t1 WHERE c00 IN (1000,5000);} 6016
+do_execsql_test 420 {SELECT sum(c63) FROM t1 WHERE c00 IN (1000,5000);} 6126
+do_execsql_test 430 {SELECT sum(c64) FROM t1 WHERE c00 IN (1000,5000);} 6128
+
+do_execsql_test 500 {
+ DROP INDEX t1x3;
+ CREATE TABLE t2 AS SELECT * FROM t1;
+ CREATE INDEX t1x4 ON t1(c00, c62, a, b);
+ CREATE INDEX t2x4 ON t2(c01, c62, c63, b, c);
+ SELECT t1.b, t2.b FROM t1 JOIN t2 ON t2.c01=t1.c00+1 WHERE +t1.b<7000
+ ORDER BY +t1.b;
+} {101 101 1101 1101 2101 2101 3101 3101 4101 4101 5101 5101 6101 6101}
+
+finish_test