aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordan <Dan Kennedy>2024-03-11 17:27:19 +0000
committerdan <Dan Kennedy>2024-03-11 17:27:19 +0000
commit815e055bff42d57f49cf46cc70771cdcf5930d83 (patch)
treea23d064d4d7ea012f510076fab54aec6bcb93f6a
parent5dfff386e53a0ec1acd9854f676f9c2dda0cf25e (diff)
downloadsqlite-815e055bff42d57f49cf46cc70771cdcf5930d83.tar.gz
sqlite-815e055bff42d57f49cf46cc70771cdcf5930d83.zip
Attempt to reduce the memory used by VALUES clauses in as many statements as possible, not just INSERT. This branch still has problems.
FossilOrigin-Name: 17d1f7cfabc7593d0725051b0c7c9619a23a482265f30f15ab9493fef5caeeb0
-rw-r--r--manifest29
-rw-r--r--manifest.uuid2
-rw-r--r--src/expr.c1
-rw-r--r--src/insert.c137
-rw-r--r--src/parse.y27
-rw-r--r--src/select.c2
-rw-r--r--src/sqliteInt.h4
-rw-r--r--src/where.c1
-rw-r--r--test/values.test256
-rw-r--r--test/valuesfault.test37
10 files changed, 451 insertions, 45 deletions
diff --git a/manifest b/manifest
index 49e6d916e..afde134bd 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sthe\s".import"\scommand\sin\sthe\sCLI\sso\sthat\sit\sworks\scorrectly\swith\stables\nthat\scontain\scomputed\scolumns.\n[forum:/forumpost/ca014d7358|forum\spost\sca014d7358].
-D 2024-03-11T11:24:59.268
+C Attempt\sto\sreduce\sthe\smemory\sused\sby\sVALUES\sclauses\sin\sas\smany\sstatements\sas\spossible,\snot\sjust\sINSERT.\sThis\sbranch\sstill\shas\sproblems.
+D 2024-03-11T17:27:19.668
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -700,7 +700,7 @@ F src/date.c 126ba2ab10aeb2e7ba6e089b5f07b747c0625b8287f78b60da346eda8d23c875
F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500
-F src/expr.c 05516e8b7d7d22f98160a0360fde69edce3304a430600567ed33e66d588ca59b
+F src/expr.c 7d0280860f8683fa1e81202304a5107f493fe9bd52be6590d0d0d6c3564c7a07
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c a47610f0a5c6cb0ad79f8fcef039c01833dec0c751bb695f28dc0ec6a4c3ba00
F src/func.c 4204c56196847faefef57fa14e43b8e4d65eb8d7e65318abe463472e3fd148cb
@@ -709,7 +709,7 @@ F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c eb33ea46dcab93e90f112fced343aaf41f59cbd2e951d5066f1f9302be1c2f34
+F src/insert.c 2c34cf51e9d8c00498ba68b7f130e448038bb31f624c9c1ca80fa8e3237b51fb
F src/json.c e2e40760d6689134c3e2ece38c6a496b34ff5e2661a8f238444a119af666fdce
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
@@ -739,7 +739,7 @@ F src/os_win.c 6ff43bac175bd9ed79e7c0f96840b139f2f51d01689a638fd05128becf94908a
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c ff60e98138d2499082ac6230f01ac508aba545315debccfca2fd6042f5f10fcd
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
-F src/parse.y 6209f01e8e7495379571454744fa82a5cfc2e7eeb89e46dee3f410d73ea6252d
+F src/parse.y 3d022ba4c68bf8c7c4c40b3e364d7a8226c4089fc9ff7a3a95f969f8f1bec959
F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75
F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
@@ -750,12 +750,12 @@ F src/printf.c 10e8bad30042f8bd6114a013b4afc229ec8ad255ab27518d7d9f52e8cbc5cd0a
F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c ef87e3bc7700bfe761a7bbee2ce6084f1766dc816dd82a3ae77c133eec898432
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
-F src/select.c 43fabfc01bf87addd15e39f112f1e2ade15b19594835ab8a9e5bd50839d4e1b1
+F src/select.c 2a12c6ed8131db7c49b6f8141c3420cab825cfc6bd222d347fa1382f36b174b7
F src/shell.c.in cf80c636bccb0ff9db46995e39d69ca21fde2a8a331d1691e4d62a69d7841c8a
F src/sqlite.h.in 19a2db3995a699bd7f6dfb423856242bfceb7ec849a93c91d241d19fc28d9f0f
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
-F src/sqliteInt.h 6123ce6ca6a1ef351c3b87189e92c92042728f16c088de56b9b5bc2552d0ae33
+F src/sqliteInt.h b3ac44ca090011813d5a07f63be59785a4558e74052eb94b686f52eef1af684b
F src/sqliteLimit.h 6878ab64bdeb8c24a1d762d45635e34b96da21132179023338c93f820eee6728
F src/status.c cb11f8589a6912af2da3bb1ec509a94dd8ef27df4d4c1a97e0bcf2309ece972b
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@ -835,7 +835,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89
F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
-F src/where.c 33eaaeef3aef10c2b9e82096e70a174d6636e35cb0b180321b8ddf804590e5cd
+F src/where.c 8bb70b3caadcf35073f8857ecd83d33d97bf6aa03de3342287037bae43c6f364
F src/whereInt.h 82a13766f13d1a53b05387c2e60726289ef26404bc7b9b1f7770204d97357fb8
F src/wherecode.c 5d77db30a2a3dd532492ae882de114edba2fae672622056b1c7fd61f5917a8f1
F src/whereexpr.c 6ebd90b553f4bb5c7df5a4b2f39b6a7c81a67484353827cdd2048f2514ec6f30
@@ -1921,6 +1921,8 @@ F test/vacuum4.test 7ea76b769fffeb41f925303b04cbcf5a5bbeabe55e4c60ae754ff24eeeb7
F test/vacuum5.test 263b144d537e92ad8e9ca8a73cc6e1583f41cfd0dda9432b87f7806174a2f48c
F test/vacuum6.test b137b04bf3392d3f5c3b8fda0ce85a6775a70ca112f6559f74ff52dc9ce042fd
F test/vacuummem.test 4b30f5b95a9ff86e9d5c20741e50a898b2dc10b0962a3211571eb165357003fb
+F test/values.test 8438dea6826738291ff2019993d62e1eae7a3efb66ea8312fa9ac9bfaf0f95cb
+F test/valuesfault.test 2ef23ed965e3bd08e268cdc38a0d11653390ddbbe1e8e2e98d16f55edd30f6e8
F test/varint.test bbce22cda8fc4d135bcc2b589574be8410614e62
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
F test/view.test d4c4281e1679245829db35597817282f60dc513fc39cc5439078f009bd118487
@@ -2177,8 +2179,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P cb8d9c269c01f78f09069a2361e15087d9aeac63e9f1fd63fbd96b951384e9aa
-R 9bba8f8b4bc3bf41627766fe1aa44b9c
-U drh
-Z 1d2e3bf230fd6cd6fbc6ff6c84cfd8fb
+P 95a9c88b258f18ac671a4c712603931167cc8bd0b86e41481b200c08043338b5
+R 62b6db11e5c879766b23978dd0871f55
+T *branch * exp-values-clause2
+T *sym-exp-values-clause2 *
+T -sym-trunk *
+U dan
+Z 521a450017e9a427470d7f09cc068c5e
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index c57a5b98b..633c59c43 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-95a9c88b258f18ac671a4c712603931167cc8bd0b86e41481b200c08043338b5 \ No newline at end of file
+17d1f7cfabc7593d0725051b0c7c9619a23a482265f30f15ab9493fef5caeeb0 \ No newline at end of file
diff --git a/src/expr.c b/src/expr.c
index 6640d1907..e4bfa995d 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1869,6 +1869,7 @@ SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
+ pNewItem->regResult = pOldItem->regResult;
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
}
diff --git a/src/insert.c b/src/insert.c
index 095298b90..7b253e926 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -577,6 +577,104 @@ void sqlite3AutoincrementEnd(Parse *pParse){
# define autoIncStep(A,B,C)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
+void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
+ if( pVal->pSrc->nSrc>0 ){
+ SrcItem *pItem = &pVal->pSrc->a[0];
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
+ }
+}
+
+static int multiValueIsConstant(ExprList *pRow){
+ int ii;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ if( 0==sqlite3ExprIsConstant(pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+}
+
+Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
+ SrcItem *p;
+ SelectDest dest;
+ Select *pSelect = 0;
+
+ if( pParse->db->init.busy
+ || pParse->pNewTrigger
+ || pParse->bHasWith
+ || multiValueIsConstant(pRow)==0
+ || pLeft->pPrior
+ ){
+ int f = SF_Values | SF_MultiValue;
+ if( pLeft->pPrior || pLeft->pSrc->nSrc ){
+ sqlite3MultiValuesEnd(pParse, pLeft);
+ f = SF_Values;
+ }
+ /* This VALUES clause is part of a VIEW or some other schema item. In
+ ** this case the co-routine cannot be coded immediately. */
+ pSelect = sqlite3SelectNew(pParse,pRow,0,0,0,0,0,f,0);
+ pLeft->selFlags &= ~SF_MultiValue;
+ if( pSelect ){
+ pSelect->op = TK_ALL;
+ pSelect->pPrior = pLeft;
+ pLeft = pSelect;
+ }
+ }else{
+
+ if( pLeft->pSrc->nSrc==0 ){
+ /* Co-routine has not yet been started. */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ Select *pRet;
+
+ if( v==0 ) return pLeft;
+ pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0);
+ if( pRet==0 ) return pLeft;
+ p = &pRet->pSrc->a[0];
+ pRet->pSrc->nSrc = 1;
+
+ p->pSelect = pLeft;
+ p->fg.viaCoroutine = 1;
+ p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ p->regReturn = ++pParse->nMem;
+
+ sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+ sqlite3Select(pParse, pLeft, &dest);
+ p->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+
+ pLeft = pRet;
+ }else{
+ p = &pLeft->pSrc->a[0];
+ }
+
+ if( pParse->nErr==0 ){
+ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, SF_Values, 0);
+ if( pSelect ){
+ if( p->pSelect->pEList->nExpr!=pSelect->pEList->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, pSelect);
+ }else{
+ sqlite3SelectPrep(pParse, pSelect, 0);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( pSelect->pWin ){
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+ dest.iSdst = p->regResult;
+ dest.nSdst = pRow->nExpr;
+ dest.iSDParm = p->regReturn;
+ sqlite3Select(pParse, pSelect, &dest);
+ }else
+#endif
+ {
+ sqlite3ExprCodeExprList(pParse, pSelect->pEList,p->regResult,0,0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
+ }
+ }
+ sqlite3SelectDelete(pParse->db, pSelect);
+ }
+ }
+ }
+
+ return pLeft;
+}
/* Forward declaration */
static int xferOptimization(
@@ -914,24 +1012,31 @@ void sqlite3Insert(
/* Data is coming from a SELECT or from a multi-row VALUES clause.
** Generate a co-routine to run the SELECT. */
int regYield; /* Register holding co-routine entry-point */
- int addrTop; /* Top of the co-routine */
int rc; /* Result code */
- regYield = ++pParse->nMem;
- addrTop = sqlite3VdbeCurrentAddr(v) + 1;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
- dest.iSdst = bIdListInOrder ? regData : 0;
- dest.nSdst = pTab->nCol;
- rc = sqlite3Select(pParse, pSelect, &dest);
- regFromSelect = dest.iSdst;
- assert( 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 );
- nColumn = pSelect->pEList->nExpr;
+ if( pSelect->pSrc->nSrc==1 && pSelect->pSrc->a[0].fg.viaCoroutine ){
+ SrcItem *pItem = &pSelect->pSrc->a[0];
+ dest.iSDParm = regYield = pItem->regReturn;
+ regFromSelect = pItem->regResult;
+ nColumn = pItem->pSelect->pEList->nExpr;
+ }else{
+ int addrTop; /* Top of the co-routine */
+ regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
+ rc = sqlite3Select(pParse, pSelect, &dest);
+ regFromSelect = dest.iSdst;
+ assert( 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 );
+ nColumn = pSelect->pEList->nExpr;
+ }
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
diff --git a/src/parse.y b/src/parse.y
index 37c9fa8bc..515d512f2 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -562,9 +562,13 @@ cmd ::= select(X). {
}
%ifndef SQLITE_OMIT_CTE
-select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
-select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
+select(A) ::= withkw wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
+select(A) ::= withkw RECURSIVE wqlist(W) selectnowith(X).
{A = attachWithToSelect(pParse,X,W);}
+
+withkw ::= WITH. {
+ pParse->bHasWith = 1;
+}
%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(A). {
Select *p = A;
@@ -622,7 +626,9 @@ oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
%endif
-oneselect(A) ::= values(A).
+oneselect(A) ::= values(A). {
+ sqlite3MultiValuesEnd(pParse, A);
+}
%type values {Select*}
%destructor values {sqlite3SelectDelete(pParse->db, $$);}
@@ -630,16 +636,7 @@ values(A) ::= VALUES LP nexprlist(X) RP. {
A = sqlite3SelectNew(pParse,X,0,0,0,0,0,SF_Values,0);
}
values(A) ::= values(A) COMMA LP nexprlist(Y) RP. {
- Select *pRight, *pLeft = A;
- pRight = sqlite3SelectNew(pParse,Y,0,0,0,0,0,SF_Values|SF_MultiValue,0);
- if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
- if( pRight ){
- pRight->op = TK_ALL;
- pRight->pPrior = pLeft;
- A = pRight;
- }else{
- A = pLeft;
- }
+ A = sqlite3MultiValues(pParse, A, Y);
}
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
@@ -1754,8 +1751,8 @@ anylist ::= anylist ANY.
with ::= .
%ifndef SQLITE_OMIT_CTE
-with ::= WITH wqlist(W). { sqlite3WithPush(pParse, W, 1); }
-with ::= WITH RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
+with ::= withkw wqlist(W). { sqlite3WithPush(pParse, W, 1); }
+with ::= withkw RECURSIVE wqlist(W). { sqlite3WithPush(pParse, W, 1); }
%type wqas {u8}
wqas(A) ::= AS. {A = M10d_Any;}
diff --git a/src/select.c b/src/select.c
index 81e802d6e..4158dd2fd 100644
--- a/src/select.c
+++ b/src/select.c
@@ -7636,7 +7636,7 @@ int sqlite3Select(
/* Generate code for all sub-queries in the FROM clause
*/
pSub = pItem->pSelect;
- if( pSub==0 ) continue;
+ if( pSub==0 || pItem->addrFillSub!=0 ) continue;
/* The code for a subquery should only be generated once. */
assert( pItem->addrFillSub==0 );
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index e11b5b3a4..391339271 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3812,6 +3812,7 @@ struct Parse {
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
+ u8 bHasWith; /* True if statement contains WITH */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
#endif
@@ -4486,6 +4487,9 @@ struct Window {
** due to the SQLITE_SUBTYPE flag */
};
+Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow);
+void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal);
+
#ifndef SQLITE_OMIT_WINDOWFUNC
void sqlite3WindowDelete(sqlite3*, Window*);
void sqlite3WindowUnlinkFromSelect(Window*);
diff --git a/src/where.c b/src/where.c
index 9850d22cc..9abc8e30f 100644
--- a/src/where.c
+++ b/src/where.c
@@ -6896,6 +6896,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
if( pTabItem->fg.viaCoroutine ){
testcase( pParse->db->mallocFailed );
+ assert( pTabItem->regResult>=0 );
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
pTabItem->regResult, 0);
continue;
diff --git a/test/values.test b/test/values.test
new file mode 100644
index 000000000..66e29ef6f
--- /dev/null
+++ b/test/values.test
@@ -0,0 +1,256 @@
+# 2024 March 3
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix values
+
+
+do_execsql_test 1.0 {
+ CREATE TABLE x1(a, b, c);
+}
+
+
+explain_i {
+ INSERT INTO x1(a, b, c) VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);
+}
+do_execsql_test 1.1.1 {
+ INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);
+}
+do_execsql_test 1.1.2 {
+ SELECT * FROM x1;
+} {
+ 1 1 1
+ 2 2 2
+ 3 3 3
+ 4 4 4
+}
+
+do_execsql_test 1.2.0 {
+ DELETE FROM x1
+}
+do_execsql_test 1.2.1 {
+ INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3) UNION ALL SELECT 4, 4, 4;
+ SELECT * FROM x1;
+} {1 1 1 2 2 2 3 3 3 4 4 4}
+
+sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 4
+
+do_execsql_test 1.2.2 {
+ DELETE FROM x1;
+ INSERT INTO x1
+ VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)
+ UNION ALL SELECT 6, 6, 6;
+ SELECT * FROM x1;
+} {1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6}
+
+do_execsql_test 1.2.3 {
+ DELETE FROM x1;
+ INSERT INTO x1
+ VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)
+ UNION ALL SELECT 6, 6, 6;
+ SELECT * FROM x1;
+} {1 1 1 2 2 2 3 3 3 4 4 4 6 6 6}
+
+do_execsql_test 1.2.4 {
+ DELETE FROM x1;
+ INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3) UNION ALL SELECT 6, 6, 6;
+ SELECT * FROM x1;
+} {
+ 1 1 1
+ 2 2 2
+ 3 3 3
+ 6 6 6
+}
+
+set a 4
+set b 5
+set c 6
+do_execsql_test 1.2.5 {
+ DELETE FROM x1;
+ INSERT INTO x1
+ VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3),
+ (4, 4, $a), (5, 5, $b), (6, 6, $c)
+}
+
+do_execsql_test 1.2.6 {
+ SELECT * FROM x1;
+} {
+ 1 1 1
+ 2 2 2
+ 3 3 3
+ 4 4 4
+ 5 5 5
+ 6 6 6
+}
+
+#-------------------------------------------------------------------------
+# SQLITE_LIMIT_COMPOUND_SELECT set to 0.
+#
+reset_db
+
+do_execsql_test 2.0 {
+ CREATE TABLE x1(a, b, c);
+}
+
+sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 3
+
+do_catchsql_test 2.1.1 {
+ INSERT INTO x1 VALUES
+ (1, 1, 1),
+ (2, 2, 2),
+ (3, 3, 3),
+ (4, 4, 4),
+ (5, 5, 5),
+ (6, 6, 6),
+ (7, 7, 7),
+ (8, 8, 8),
+ (9, 9, 9),
+ (10, 10, 10, 10)
+} {1 {all VALUES must have the same number of terms}}
+
+do_catchsql_test 2.1.2 {
+ INSERT INTO x1 VALUES
+ (1, 1, 1),
+ (2, 2, 2, 2),
+ (3, 3, 3),
+ (4, 4, 4),
+ (5, 5, 5),
+ (6, 6, 6),
+ (7, 7, 7),
+ (8, 8, 8),
+ (9, 9, 9),
+ (10, 10, 10)
+} {1 {all VALUES must have the same number of terms}}
+
+sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 0
+
+do_execsql_test 2.2 {
+ INSERT INTO x1 VALUES
+ (1, 1, 1),
+ (2, 2, 2),
+ (3, 3, 3),
+ (4, 4, 4),
+ (5, 5, 5),
+ (6, 6, 6),
+ (7, 7, 7),
+ (8, 8, 8),
+ (9, 9, 9),
+ (10, 10, 10)
+} {}
+do_execsql_test 2.3 {
+ INSERT INTO x1 VALUES
+ (1, 1, 1),
+ (2, 2, 2),
+ (3, 3, 3),
+ (4, 4, 4),
+ (5, 5, 5),
+ (6, 6, 6),
+ (7, 7, 7),
+ (8, 8, 8),
+ (9, 9, 9),
+ (10, 10, 10)
+ UNION ALL
+ SELECT 5, 12, 12
+ ORDER BY 1
+} {}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 3.0 {
+ CREATE TABLE y1(x, y);
+}
+
+do_execsql_test 3.1.1 {
+ DELETE FROM y1;
+ INSERT INTO y1 VALUES(1, 2), (3, 4), (row_number() OVER (), 5);
+}
+do_execsql_test 3.1.2 {
+ SELECT * FROM y1;
+} {1 2 3 4 1 5}
+do_execsql_test 3.2.1 {
+ DELETE FROM y1;
+ INSERT INTO y1 VALUES(1, 2), (3, 4), (row_number() OVER (), 6)
+ , (row_number() OVER (), 7)
+}
+do_execsql_test 3.1.2 {
+ SELECT * FROM y1;
+} {1 2 3 4 1 6 1 7}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 4.0 {
+ CREATE TABLE x1(a PRIMARY KEY, b) WITHOUT ROWID;
+}
+
+foreach {tn iLimit} {1 0 2 3} {
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT $iLimit
+
+ do_execsql_test 4.1.1 {
+ DELETE FROM x1;
+ INSERT INTO x1 VALUES
+ (1, 1),
+ (2, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d')) ))
+ }
+ do_execsql_test 4.1.2 {
+ SELECT * FROM x1
+ } {1 1 2 a}
+
+ do_execsql_test 4.2.1 {
+ DELETE FROM x1;
+ INSERT INTO x1 VALUES
+ (1, 1),
+ (2, 2),
+ (3, 3),
+ (4, 4),
+ (5, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d')) ))
+ }
+ do_execsql_test 4.2.2 {
+ SELECT * FROM x1
+ } {1 1 2 2 3 3 4 4 5 a}
+
+ do_execsql_test 4.3.1 {
+ DELETE FROM x1;
+ INSERT INTO x1 VALUES
+ (1, (SELECT * FROM (VALUES('a'), ('b'), ('c'), ('d'), ('e')) ))
+ }
+ do_execsql_test 4.3.2 {
+ SELECT * FROM x1
+ } {1 a}
+}
+
+#------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 5.0 {
+ CREATE VIEW v1 AS VALUES(1, 2, 3), (4, 5, 6), (7, 8, 9);
+}
+do_execsql_test 5.1 {
+ SELECT * FROM v1
+} {1 2 3 4 5 6 7 8 9}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 6.0 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1), (2);
+}
+
+do_execsql_test 6.1 {
+ SELECT ( VALUES( x ), ( x ) ) FROM t1;
+} {1 2}
+
+
+finish_test
diff --git a/test/valuesfault.test b/test/valuesfault.test
new file mode 100644
index 000000000..bc5dddfb0
--- /dev/null
+++ b/test/valuesfault.test
@@ -0,0 +1,37 @@
+# 2024 March 3
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix valuesfault
+source $testdir/malloc_common.tcl
+
+
+do_execsql_test 1.0 {
+ CREATE TABLE x1(a, b, c);
+}
+faultsim_save_and_close
+
+do_faultsim_test 1 -prep {
+ faultsim_restore_and_reopen
+ sqlite3_limit db SQLITE_LIMIT_COMPOUND_SELECT 2
+} -body {
+ execsql {
+ INSERT INTO x1 VALUES(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+
+finish_test