Index: Makefile.in
==================================================================
--- Makefile.in
+++ Makefile.in
@@ -815,12 +815,11 @@
mv vdbe.new tsrc/vdbe.c
cp fts5.c fts5.h tsrc
touch .target_source
sqlite3.c: .target_source $(TOP)/tool/mksqlite3c.tcl src-verify has_tclsh84
- $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) \
- $(EXTRA_SRC)
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
cp tsrc/sqlite3ext.h .
cp $(TOP)/ext/session/sqlite3session.h .
sqlite3r.h: sqlite3.h has_tclsh84
$(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) --enable-recover >sqlite3r.h
@@ -827,12 +826,11 @@
sqlite3r.c: sqlite3.c sqlite3r.h has_tclsh84
cp $(TOP)/ext/recover/sqlite3recover.c tsrc/
cp $(TOP)/ext/recover/sqlite3recover.h tsrc/
cp $(TOP)/ext/recover/dbdata.c tsrc/
- $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover \
- $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
+ $(TCLSH_CMD) $(TOP)/tool/mksqlite3c.tcl --enable-recover $(AMALGAMATION_LINE_MACROS) $(EXTRA_SRC)
sqlite3ext.h: .target_source
cp tsrc/sqlite3ext.h .
tclsqlite3.c: sqlite3.c
Index: doc/testrunner.md
==================================================================
--- doc/testrunner.md
+++ doc/testrunner.md
@@ -15,11 +15,10 @@
3.1. Commands to Run SQLite Tests
3.2. Running ZipVFS Tests
3.3. Investigating Source Code Test Failures
4. Extra testrunner.tcl Options
-# 4. Extra testrunner.tcl Options
5. Controlling CPU Core Utilization
# 1. Overview
@@ -27,16 +26,22 @@
testrunner.tcl is a Tcl script used to run multiple SQLite tests using
multiple jobs. It supports the following types of tests:
* Tcl test scripts.
- * Tests run with [make] commands. Specifically, at time of writing,
- [make fuzztest], [make mptest], [make sourcetest] and [make threadtest].
+ * Tests run with `make` commands. Examples:
+ - `make mdevtest`
+ - `make releasetest`
+ - `make sdevtest`
+ - `make testrunner`
testrunner.tcl pipes the output of all tests and builds run into log file
-**testrunner.log**, created in the cwd directory. Searching this file for
-"failed" is a good way to find the output of a failed test.
+**testrunner.log**, created in the current working directory. Search this
+file to find details of errors. Suggested search commands:
+
+ * `grep "^!" testrunner.log`
+ * `grep failed testrunner.log`
testrunner.tcl also populates SQLite database **testrunner.db**. This database
contains details of all tests run, running and to be run. A useful query
might be:
@@ -58,21 +63,21 @@
watch ./testfixture $(TESTDIR)/testrunner.tcl status
```
in another terminal is a good way to keep an eye on a long running test.
-Sometimes testrunner.tcl uses the [testfixture] binary that it is run with
+Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
other binaries in specific configurations to test (see "Source Tests").
# 2. Binary Tests
The commands described in this section all run various combinations of the Tcl
-test scripts using the [testfixture] binary used to run the testrunner.tcl
+test scripts using the `testfixture` binary used to run the testrunner.tcl
script (i.e. they do not invoke the compiler to build new binaries, or the
-[make] command to run tests that are not Tcl scripts). The procedure to run
+`make` command to run tests that are not Tcl scripts). The procedure to run
these tests is therefore:
1. Build the "testfixture" (or "testfixture.exe" for windows) binary using
whatever method seems convenient.
@@ -191,11 +196,11 @@
## 3.1. Commands to Run SQLite Tests
The **mdevtest** command is equivalent to running the veryquick tests and
-the [make fuzztest] target once for each of two --enable-all builds - one
+the `make fuzztest` target once for each of two --enable-all builds - one
with debugging enabled and one without:
```
tclsh $TESTDIR/testrunner.tcl mdevtest
```
@@ -281,11 +286,11 @@
# Create a script that recreates build configuration "Have-Not" on Windows:
tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat
```
The generated bash or \*.bat file script accepts a single argument - a makefile
-target to build. This may be used either to run a [make] command test directly,
+target to build. This may be used either to run a `make` command test directly,
or else to build a testfixture (or testfixture.exe) binary with which to
run a Tcl test script, as described above.
# 4. Extra testrunner.tcl Options
@@ -307,10 +312,20 @@
```
# Log the shell commmands that make up the mdevtest test.
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
```
+
+The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl
+from building any binaries or running any tests. The difference is that --explain
+prints on standard output a human-readable summary of all the builds and tests that
+would have been run.
+
+```
+ # Show what builds and tests would have been run
+ tclsh $TESTDIR/testrunner.tcl --explain mdevtest
+```
# 5. Controlling CPU Core Utilization
When running either binary or source code tests, testrunner.tcl reports the
@@ -337,8 +352,5 @@
testrunner.log and testrunner.db files:
```
$ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS
```
-
-
-
Index: ext/misc/cksumvfs.c
==================================================================
--- ext/misc/cksumvfs.c
+++ ext/misc/cksumvfs.c
@@ -444,13 +444,13 @@
** (1) the size indicates that we are dealing with a complete
** database page
** (2) checksum verification is enabled
** (3) we are not in the middle of checkpoint
*/
- if( iAmt>=512 /* (1) */
- && p->verifyCksm /* (2) */
- && !p->inCkpt /* (3) */
+ if( iAmt>=512 && (iAmt & (iAmt-1))==0 /* (1) */
+ && p->verifyCksm /* (2) */
+ && !p->inCkpt /* (3) */
){
u8 cksum[8];
cksmCompute((u8*)zBuf, iAmt-8, cksum);
if( memcmp((u8*)zBuf+iAmt-8, cksum, 8)!=0 ){
sqlite3_log(SQLITE_IOERR_DATA,
Index: ext/rbu/sqlite3rbu.c
==================================================================
--- ext/rbu/sqlite3rbu.c
+++ ext/rbu/sqlite3rbu.c
@@ -197,10 +197,11 @@
#if !defined(SQLITE_AMALGAMATION)
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
#endif
/*
** These values must match the values defined in wal.c for the equivalent
** locks. These are not magic numbers as they are part of the SQLite file
@@ -883,10 +884,11 @@
pIter->bCleanup = 0;
rc = sqlite3_step(pIter->pTblIter);
if( rc!=SQLITE_ROW ){
rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
pIter->zTbl = 0;
+ pIter->zDataTbl = 0;
}else{
pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM;
}
@@ -2977,11 +2979,11 @@
if( p->rc==SQLITE_OK ){
sqlite3_file *pDb = p->pTargetFd->pReal;
u32 volatile *ptr;
p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
if( p->rc==SQLITE_OK ){
- iRet = ((i64)ptr[10] << 32) + ptr[11];
+ iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]);
}
}
return iRet;
}
Index: ext/wasm/api/sqlite3-opfs-async-proxy.js
==================================================================
--- ext/wasm/api/sqlite3-opfs-async-proxy.js
+++ ext/wasm/api/sqlite3-opfs-async-proxy.js
@@ -49,11 +49,11 @@
versions (approximately) 104-107 are extinct) should change our
usage of those methods to remove the "await".
*/
"use strict";
const wPost = (type,...args)=>postMessage({type, payload:args});
-const installAsyncProxy = function(self){
+const installAsyncProxy = function(){
const toss = function(...args){throw new Error(args.join(' '))};
if(globalThis.window === globalThis){
toss("This code cannot run from the main thread.",
"Load it as a Worker from a separate Worker.");
}else if(!navigator?.storage?.getDirectory){
@@ -561,16 +561,13 @@
mTimeEnd();
wTimeEnd();
return;
}
if( state.opfsFlags.OPFS_UNLINK_BEFORE_OPEN & opfsFlags ){
- //log("async proxy opfsFlags =",opfsFlags);
try{
await hDir.removeEntry(filenamePart);
- //log("Unlinked",filename,hDir,filenamePart);
- }
- catch(e){
+ }catch(e){
/* ignoring */
//warn("Ignoring failed Unlink of",filename,":",e);
}
}
const hFile = await hDir.getFileHandle(filenamePart, {create});
@@ -920,7 +917,7 @@
!globalThis.FileSystemFileHandle ||
!globalThis.FileSystemFileHandle.prototype.createSyncAccessHandle ||
!navigator?.storage?.getDirectory){
wPost('opfs-unavailable',"Missing required OPFS APIs.");
}else{
- installAsyncProxy(self);
+ installAsyncProxy();
}
Index: src/build.c
==================================================================
--- src/build.c
+++ src/build.c
@@ -3004,13 +3004,16 @@
/* Legacy versions of SQLite allowed the use of the magic "rowid" column
** on a view, even though views do not have rowids. The following flag
** setting fixes this problem. But the fix can be disabled by compiling
** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
- ** depend upon the old buggy behavior. */
-#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
- p->tabFlags |= TF_NoVisibleRowid;
+ ** depend upon the old buggy behavior. The ability can also be toggled
+ ** using SQLITE_TESTCTRL_ROWID_IN_VIEW */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */
+#else
+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */
#endif
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
Index: src/expr.c
==================================================================
--- src/expr.c
+++ src/expr.c
@@ -216,13 +216,14 @@
if( ExprHasProperty(pExpr, EP_Unlikely) ){
assert( ExprUseXList(pExpr) );
assert( pExpr->x.pList->nExpr>0 );
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
- }else{
- assert( pExpr->op==TK_COLLATE );
+ }else if( pExpr->op==TK_COLLATE ){
pExpr = pExpr->pLeft;
+ }else{
+ break;
}
}
return pExpr;
}
@@ -1867,10 +1868,11 @@
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
pNewItem->fg = pOldItem->fg;
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);
}
pNewItem->u2 = pOldItem->u2;
if( pNewItem->fg.isCte ){
@@ -2343,10 +2345,58 @@
}
}
return pExpr;
}
+/*
+** pExpr is a TK_FUNCTION node. Try to determine whether or not the
+** function is a constant function. A function is constant if all of
+** the following are true:
+**
+** (1) It is a scalar function (not an aggregate or window function)
+** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG
+** property.
+** (3) All of its arguments are constants
+**
+** This routine sets pWalker->eCode to 0 if pExpr is not a constant.
+** It makes no changes to pWalker->eCode if pExpr is constant. In
+** every case, it returns WRC_Abort.
+**
+** Called as a service subroutine from exprNodeIsConstant().
+*/
+static SQLITE_NOINLINE int exprNodeIsConstantFunction(
+ Walker *pWalker,
+ Expr *pExpr
+){
+ int n; /* Number of arguments */
+ ExprList *pList; /* List of arguments */
+ FuncDef *pDef; /* The function */
+ sqlite3 *db; /* The database */
+
+ assert( pExpr->op==TK_FUNCTION );
+ if( ExprHasProperty(pExpr, EP_TokenOnly)
+ || (pList = pExpr->x.pList)==0
+ ){;
+ n = 0;
+ }else{
+ n = pList->nExpr;
+ sqlite3WalkExprList(pWalker, pList);
+ if( pWalker->eCode==0 ) return WRC_Abort;
+ }
+ db = pWalker->pParse->db;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0
+ || pDef->xFinalize!=0
+ || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
+ || ExprHasProperty(pExpr, EP_WinFunc)
+ ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Continue;
+}
+
/*
** These routines are Walker callbacks used to check expressions to
** see if they are "constant" for some definition of constant. The
** Walker.eCode value determines the type of "constant" we are looking
@@ -2371,10 +2421,11 @@
** contain a bound parameter because they were generated by older versions
** of SQLite to be parsed by newer versions of SQLite without raising a
** malformed schema error.
*/
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
+ assert( pWalker->eCode>0 );
/* If pWalker->eCode is 2 then any term of the expression that comes from
** the ON or USING clauses of an outer join disqualifies the expression
** from being considered constant. */
if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_OuterON) ){
@@ -2390,10 +2441,12 @@
if( (pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc))
&& !ExprHasProperty(pExpr, EP_WinFunc)
){
if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL);
return WRC_Continue;
+ }else if( pWalker->pParse ){
+ return exprNodeIsConstantFunction(pWalker, pExpr);
}else{
pWalker->eCode = 0;
return WRC_Abort;
}
case TK_ID:
@@ -2442,13 +2495,14 @@
testcase( pExpr->op==TK_SELECT ); /* sqlite3SelectWalkFail() disallows */
testcase( pExpr->op==TK_EXISTS ); /* sqlite3SelectWalkFail() disallows */
return WRC_Continue;
}
}
-static int exprIsConst(Expr *p, int initFlag, int iCur){
+static int exprIsConst(Parse *pParse, Expr *p, int initFlag, int iCur){
Walker w;
w.eCode = initFlag;
+ w.pParse = pParse;
w.xExprCallback = exprNodeIsConstant;
w.xSelectCallback = sqlite3SelectWalkFail;
#ifdef SQLITE_DEBUG
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
#endif
@@ -2462,13 +2516,19 @@
** and 0 if it involves variables or function calls.
**
** For the purposes of this function, a double-quoted string (ex: "abc")
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
+**
+** The pParse parameter may be NULL. But if it is NULL, there is no way
+** to determine if function calls are constant or not, and hence all
+** function calls will be considered to be non-constant. If pParse is
+** not NULL, then a function call might be constant, depending on the
+** function and on its parameters.
*/
-int sqlite3ExprIsConstant(Expr *p){
- return exprIsConst(p, 1, 0);
+int sqlite3ExprIsConstant(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 1, 0);
}
/*
** Walk an expression tree. Return non-zero if
**
@@ -2480,22 +2540,22 @@
**
** When this routine returns true, it indicates that the expression
** can be added to the pParse->pConstExpr list and evaluated once when
** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
*/
-int sqlite3ExprIsConstantNotJoin(Expr *p){
- return exprIsConst(p, 2, 0);
+static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 2, 0);
}
/*
** Walk an expression tree. Return non-zero if the expression is constant
** for any single row of the table with cursor iCur. In other words, the
** expression must not refer to any non-deterministic function nor any
** table other than iCur.
*/
int sqlite3ExprIsTableConstant(Expr *p, int iCur){
- return exprIsConst(p, 3, iCur);
+ return exprIsConst(0, p, 3, iCur);
}
/*
** Check pExpr to see if it is an constraint on the single data source
** pSrc = &pSrcList->a[iSrc]. In other words, check to see if pExpr
@@ -2648,11 +2708,11 @@
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
*/
int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
assert( isInit==0 || isInit==1 );
- return exprIsConst(p, 4+isInit, 0);
+ return exprIsConst(0, p, 4+isInit, 0);
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
/*
** Walk an expression tree. Return 1 if the expression contains a
@@ -2893,17 +2953,17 @@
#ifndef SQLITE_OMIT_SUBQUERY
/*
** The argument is an IN operator with a list (not a subquery) on the
** right-hand side. Return TRUE if that list is constant.
*/
-static int sqlite3InRhsIsConstant(Expr *pIn){
+static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){
Expr *pLHS;
int res;
assert( !ExprHasProperty(pIn, EP_xIsSelect) );
pLHS = pIn->pLeft;
pIn->pLeft = 0;
- res = sqlite3ExprIsConstant(pIn);
+ res = sqlite3ExprIsConstant(pParse, pIn);
pIn->pLeft = pLHS;
return res;
}
#endif
@@ -3168,11 +3228,11 @@
** the IN operator so return IN_INDEX_NOOP.
*/
if( eType==0
&& (inFlags & IN_INDEX_NOOP_OK)
&& ExprUseXList(pX)
- && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
+ && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2)
){
pParse->nTab--; /* Back out the allocation of the unused cursor */
iTab = -1; /* Cursor is not allocated */
eType = IN_INDEX_NOOP;
}
@@ -3451,11 +3511,11 @@
/* If the expression is not constant then we will need to
** disable the test that was generated above that makes sure
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( addrOnce && !sqlite3ExprIsConstant(pE2) ){
+ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){
sqlite3VdbeChangeToNoop(v, addrOnce-1);
sqlite3VdbeChangeToNoop(v, addrOnce);
ExprClearProperty(pExpr, EP_Subrtn);
addrOnce = 0;
}
@@ -4788,11 +4848,13 @@
if( ExprHasProperty(pExpr, EP_WinFunc) ){
return pExpr->y.pWin->regResult;
}
#endif
- if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( ConstFactorOk(pParse)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
+ ){
/* SQL functions can be expensive. So try to avoid running them
** multiple times if we know they always give the same result */
return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
}
assert( !ExprHasProperty(pExpr, EP_TokenOnly) );
@@ -4819,11 +4881,11 @@
}else if( pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE) ){
sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
}
for(i=0; ia[i].pExpr) ){
+ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){
testcase( i==31 );
constMask |= MASKBIT32(i);
}
if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
@@ -5286,11 +5348,11 @@
int r2;
pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
if( ConstFactorOk(pParse)
&& ALWAYS(pExpr!=0)
&& pExpr->op!=TK_REGISTER
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse, pExpr)
){
*pReg = 0;
r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
}else{
int r1 = sqlite3GetTempReg(pParse);
@@ -5350,11 +5412,11 @@
** results in register target. The results are guaranteed to appear
** in register target. If the expression is constant, then this routine
** might choose to code the expression at initialization time.
*/
void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
- if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target);
}else{
sqlite3ExprCodeCopy(pParse, pExpr, target);
}
}
@@ -5409,11 +5471,11 @@
n--;
}else{
sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
}
}else if( (flags & SQLITE_ECEL_FACTOR)!=0
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i);
}else{
int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
if( inReg!=target+i ){
Index: src/global.c
==================================================================
--- src/global.c
+++ src/global.c
@@ -287,10 +287,13 @@
SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */
#endif
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */
+#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
0, /* iPrngSeed */
Index: src/insert.c
==================================================================
--- src/insert.c
+++ src/insert.c
@@ -575,10 +575,197 @@
*/
# define autoIncBegin(A,B,C) (0)
# define autoIncStep(A,B,C)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
+/*
+** If argument pVal is a Select object returned by an sqlite3MultiValues()
+** that was able to use the co-routine optimization, finish coding the
+** co-routine.
+*/
+void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
+ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){
+ SrcItem *pItem = &pVal->pSrc->a[0];
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
+ }
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are constant.
+*/
+static int exprListIsConstant(Parse *pParse, ExprList *pRow){
+ int ii;
+ for(ii=0; iinExpr; ii++){
+ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are both constant and have no affinity.
+*/
+static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){
+ int ii;
+ if( exprListIsConstant(pParse,pRow)==0 ) return 0;
+ for(ii=0; iinExpr; ii++){
+ assert( pRow->a[ii].pExpr->affExpr==0 );
+ if( 0!=sqlite3ExprAffinity(pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+
+}
+
+/*
+** This function is called by the parser for the second and subsequent
+** rows of a multi-row VALUES clause. Argument pLeft is the part of
+** the VALUES clause already parsed, argument pRow is the vector of values
+** for the new row. The Select object returned represents the complete
+** VALUES clause, including the new row.
+**
+** There are two ways in which this may be achieved - by incremental
+** coding of a co-routine (the "co-routine" method) or by returning a
+** Select object equivalent to the following (the "UNION ALL" method):
+**
+** "pLeft UNION ALL SELECT pRow"
+**
+** If the VALUES clause contains a lot of rows, this compound Select
+** object may consume a lot of memory.
+**
+** When the co-routine method is used, each row that will be returned
+** by the VALUES clause is coded into part of a co-routine as it is
+** passed to this function. The returned Select object is equivalent to:
+**
+** SELECT * FROM (
+** Select object to read co-routine
+** )
+**
+** The co-routine method is used in most cases. Exceptions are:
+**
+** a) If the current statement has a WITH clause. This is to avoid
+** statements like:
+**
+** WITH cte AS ( VALUES('x'), ('y') ... )
+** SELECT * FROM cte AS a, cte AS b;
+**
+** This will not work, as the co-routine uses a hard-coded register
+** for its OP_Yield instructions, and so it is not possible for two
+** cursors to iterate through it concurrently.
+**
+** b) The schema is currently being parsed (i.e. the VALUES clause is part
+** of a schema item like a VIEW or TRIGGER). In this case there is no VM
+** being generated when parsing is taking place, and so generating
+** a co-routine is not possible.
+**
+** c) There are non-constant expressions in the VALUES clause (e.g.
+** the VALUES clause is part of a correlated sub-query).
+**
+** d) One or more of the values in the first row of the VALUES clause
+** has an affinity (i.e. is a CAST expression). This causes problems
+** because the complex rules SQLite uses (see function
+** sqlite3SubqueryColumnTypes() in select.c) to determine the effective
+** affinity of such a column for all rows require access to all values in
+** the column simultaneously.
+*/
+Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
+
+ if( pParse->bHasWith /* condition (a) above */
+ || pParse->db->init.busy /* condition (b) above */
+ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */
+ || (pLeft->pSrc->nSrc==0 &&
+ exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */
+ || IN_SPECIAL_PARSE
+ ){
+ /* The co-routine method cannot be used. Fall back to UNION ALL. */
+ Select *pSelect = 0;
+ int f = SF_Values | SF_MultiValue;
+ if( pLeft->pSrc->nSrc ){
+ sqlite3MultiValuesEnd(pParse, pLeft);
+ f = SF_Values;
+ }else if( pLeft->pPrior ){
+ /* In this case set the SF_MultiValue flag only if it was set on pLeft */
+ f = (f & pLeft->selFlags);
+ }
+ 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{
+ SrcItem *p = 0; /* SrcItem that reads from co-routine */
+
+ if( pLeft->pSrc->nSrc==0 ){
+ /* Co-routine has not yet been started and the special Select object
+ ** that accesses the co-routine has not yet been created. This block
+ ** does both those things. */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Ensure the database schema has been read. This is to ensure we have
+ ** the correct text encoding. */
+ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){
+ sqlite3ReadSchema(pParse);
+ }
+
+ if( pRet ){
+ SelectDest dest;
+ pRet->pSrc->nSrc = 1;
+ pRet->pPrior = pLeft->pPrior;
+ pRet->op = pLeft->op;
+ pLeft->pPrior = 0;
+ pLeft->op = TK_SELECT;
+ assert( pLeft->pNext==0 );
+ assert( pRet->pNext==0 );
+ p = &pRet->pSrc->a[0];
+ p->pSelect = pLeft;
+ p->fg.viaCoroutine = 1;
+ p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ p->regReturn = ++pParse->nMem;
+ p->iCursor = -1;
+ p->u1.nRow = 2;
+ sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+
+ /* Allocate registers for the output of the co-routine. Do so so
+ ** that there are two unused registers immediately before those
+ ** used by the co-routine. This allows the code in sqlite3Insert()
+ ** to use these registers directly, instead of copying the output
+ ** of the co-routine to a separate array for processing. */
+ dest.iSdst = pParse->nMem + 3;
+ dest.nSdst = pLeft->pEList->nExpr;
+ pParse->nMem += 2 + dest.nSdst;
+
+ pLeft->selFlags |= SF_MultiValue;
+ sqlite3Select(pParse, pLeft, &dest);
+ p->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+ pLeft = pRet;
+ }
+ }else{
+ p = &pLeft->pSrc->a[0];
+ assert( !p->fg.isTabFunc && !p->fg.isIndexedBy );
+ p->u1.nRow++;
+ }
+
+ if( pParse->nErr==0 ){
+ assert( p!=0 );
+ if( p->pSelect->pEList->nExpr!=pRow->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, p->pSelect);
+ }else{
+ sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
+ }
+ }
+ sqlite3ExprListDelete(pParse->db, pRow);
+ }
+
+ return pLeft;
+}
/* Forward declaration */
static int xferOptimization(
Parse *pParse, /* Parser context */
Table *pDest, /* The table we are inserting into */
@@ -911,29 +1098,44 @@
** co-routine is the common header to the 3rd and 4th templates.
*/
if( pSelect ){
/* 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
+ && pSelect->pPrior==0
+ ){
+ SrcItem *pItem = &pSelect->pSrc->a[0];
+ dest.iSDParm = pItem->regReturn;
+ regFromSelect = pItem->regResult;
+ nColumn = pItem->pSelect->pEList->nExpr;
+ ExplainQueryPlan((pParse, 0, "SCAN %S", pItem));
+ if( bIdListInOrder && nColumn==pTab->nCol ){
+ regData = regFromSelect;
+ regRowid = regData - 1;
+ regIns = regRowid - (IsVirtual(pTab) ? 1 : 0);
+ }
+ }else{
+ int addrTop; /* Top of the co-routine */
+ int 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
** FALSE if each output row of the SELECT can be written directly into
** the destination table (template 3).
Index: src/main.c
==================================================================
--- src/main.c
+++ src/main.c
@@ -4402,10 +4402,43 @@
}else{
sqlite3GlobalConfig.xAltLocaltime = 0;
}
break;
}
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_ROWID_IN_VIEW, int *pVal);
+ **
+ ** Query or set the sqlite3Config.mNoVisibleRowid flag. Cases:
+ **
+ ** *pVal==1 Allow ROWID in VIEWs
+ ** *pVal==0 Disallow ROWID in VIEWs
+ ** *pVal<0 No change
+ **
+ ** In every case *pVal is written with 1 if ROWID is allowd in VIEWs and
+ ** 0 if not. Changes to the setting only occur if SQLite is compiled
+ ** with -DSQLITE_ALLOW_ROWID_IN_VIEW (hereafter: "SARIV"). With the
+ ** "SARIV" compile-time option the default value for this setting is 1.
+ ** Otherwise this setting defaults to 0. This setting may only be changed
+ ** if SQLite is compiled with "SARIV". Hence, in the normal case when
+ ** SQLite is compiled without "SARIV", this test-control is a no-op
+ ** that always leaves *pVal set to 0.
+ **
+ ** IMPORTANT: If you change this setting while a database connection
+ ** is option, it is very important to run "PRAGMA writable_schema=RESET"
+ ** afterwards in order to reparse all VIEW definitions in the schema.
+ */
+ case SQLITE_TESTCTRL_ROWID_IN_VIEW: {
+ int *pVal = va_arg(ap, int*);
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( *pVal==0 ) sqlite3Config.mNoVisibleRowid = TF_NoVisibleRowid;
+ if( *pVal==1 ) sqlite3Config.mNoVisibleRowid = 0;
+ *pVal = (sqlite3Config.mNoVisibleRowid==0);
+#else
+ *pVal = 0;
+#endif
+ break;
+ }
/* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, sqlite3*);
**
** Toggle the ability to use internal functions on or off for
** the database connection given in the argument.
Index: src/parse.y
==================================================================
--- src/parse.y
+++ src/parse.y
@@ -563,10 +563,11 @@
%ifndef SQLITE_OMIT_CTE
select(A) ::= WITH wqlist(W) selectnowith(X). {A = attachWithToSelect(pParse,X,W);}
select(A) ::= WITH RECURSIVE wqlist(W) selectnowith(X).
{A = attachWithToSelect(pParse,X,W);}
+
%endif /* SQLITE_OMIT_CTE */
select(A) ::= selectnowith(A). {
Select *p = A;
if( p ){
parserDoubleLinkSelect(pParse, p);
@@ -620,28 +621,31 @@
}
}
%endif
-oneselect(A) ::= values(A).
-
+// Single row VALUES clause.
+//
%type values {Select*}
+oneselect(A) ::= values(A).
%destructor values {sqlite3SelectDelete(pParse->db, $$);}
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;
- }
+
+// Multiple row VALUES clause.
+//
+%type mvalues {Select*}
+oneselect(A) ::= mvalues(A). {
+ sqlite3MultiValuesEnd(pParse, A);
+}
+%destructor mvalues {sqlite3SelectDelete(pParse->db, $$);}
+mvalues(A) ::= values(A) COMMA LP nexprlist(Y) RP. {
+ A = sqlite3MultiValues(pParse, A, Y);
+}
+mvalues(A) ::= mvalues(A) COMMA LP nexprlist(Y) RP. {
+ A = sqlite3MultiValues(pParse, A, Y);
}
// The "distinct" nonterminal is true (1) if the DISTINCT keyword is
// present and false (0) if it is not.
//
@@ -1319,11 +1323,11 @@
sqlite3ExprUnmapAndDelete(pParse, A);
A = sqlite3Expr(pParse->db, TK_STRING, N ? "true" : "false");
if( A ) sqlite3ExprIdToTrueFalse(A);
}else{
Expr *pRHS = Y->a[0].pExpr;
- if( Y->nExpr==1 && sqlite3ExprIsConstant(pRHS) && A->op!=TK_VECTOR ){
+ if( Y->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && A->op!=TK_VECTOR ){
Y->a[0].pExpr = 0;
sqlite3ExprListDelete(pParse->db, Y);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
A = sqlite3PExpr(pParse, TK_EQ, A, pRHS);
}else if( Y->nExpr==1 && pRHS->op==TK_SELECT ){
@@ -1759,13 +1763,14 @@
%type wqas {u8}
wqas(A) ::= AS. {A = M10d_Any;}
wqas(A) ::= AS MATERIALIZED. {A = M10d_Yes;}
wqas(A) ::= AS NOT MATERIALIZED. {A = M10d_No;}
-wqitem(A) ::= nm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. {
+wqitem(A) ::= withnm(X) eidlist_opt(Y) wqas(M) LP select(Z) RP. {
A = sqlite3CteNew(pParse, &X, Y, Z, M); /*A-overwrites-X*/
}
+withnm(A) ::= nm(A). {pParse->bHasWith = 1;}
wqlist(A) ::= wqitem(X). {
A = sqlite3WithAdd(pParse, 0, X); /*A-overwrites-X*/
}
wqlist(A) ::= wqlist(A) COMMA wqitem(X). {
A = sqlite3WithAdd(pParse, A, X);
Index: src/printf.c
==================================================================
--- src/printf.c
+++ src/printf.c
@@ -858,10 +858,14 @@
}else{
Select *pSel = pItem->pSelect;
assert( pSel!=0 );
if( pSel->selFlags & SF_NestedFrom ){
sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId);
+ }else if( pSel->selFlags & SF_MultiValue ){
+ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy );
+ sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE",
+ pItem->u1.nRow);
}else{
sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId);
}
}
length = width = 0;
Index: src/select.c
==================================================================
--- src/select.c
+++ src/select.c
@@ -1951,15 +1951,11 @@
/* The "table" is actually a sub-select or a view in the FROM clause
** of the SELECT statement. Return the declaration type and origin
** data for the result-set column of the sub-select.
*/
if( iColpEList->nExpr
-#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
- && iCol>=0
-#else
- && ALWAYS(iCol>=0)
-#endif
+ && (!ViewCanHaveRowid || iCol>=0)
){
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
** test case misc2.2.2) - it always evaluates to NULL.
*/
@@ -4774,11 +4770,11 @@
Expr *pValue, /* The VALUE part of the constraint */
Expr *pExpr /* Overall expression: COLUMN=VALUE or VALUE=COLUMN */
){
int i;
assert( pColumn->op==TK_COLUMN );
- assert( sqlite3ExprIsConstant(pValue) );
+ assert( sqlite3ExprIsConstant(pConst->pParse, pValue) );
if( ExprHasProperty(pColumn, EP_FixedCol) ) return;
if( sqlite3ExprAffinity(pValue)!=0 ) return;
if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pConst->pParse,pExpr)) ){
return;
@@ -4832,14 +4828,14 @@
if( pExpr->op!=TK_EQ ) return;
pRight = pExpr->pRight;
pLeft = pExpr->pLeft;
assert( pRight!=0 );
assert( pLeft!=0 );
- if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){
+ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){
constInsert(pConst,pRight,pLeft,pExpr);
}
- if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){
+ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){
constInsert(pConst,pLeft,pRight,pExpr);
}
}
/*
@@ -5872,11 +5868,12 @@
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
/* The usual case - do not allow ROWID on a subquery */
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
#else
- pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+ /* Legacy compatibility mode */
+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid;
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
@@ -6140,11 +6137,11 @@
if( pFrom->fg.isNestedFrom ){
assert( pFrom->pSelect!=0 );
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
- assert( VisibleRowid(pTab)==0 );
+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
}
pNestedFrom = 0;
@@ -6172,11 +6169,17 @@
}
}else{
pUsing = 0;
}
- nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ nAdd = pTab->nCol;
+ if( VisibleRowid(pTab)
+ && !ViewCanHaveRowid
+ && (selFlags & SF_NestedFrom)!=0
+ ){
+ nAdd++;
+ }
for(j=0; jnCol ){
@@ -7634,11 +7637,11 @@
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/* 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 );
/* Increment Parse.nHeight by the height of the largest expression
Index: src/shell.c.in
==================================================================
--- src/shell.c.in
+++ src/shell.c.in
@@ -10891,10 +10891,11 @@
#endif
{"pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " },
{"prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" },
{"prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" },
{"prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" },
+ {"rowid_in_view", SQLITE_TESTCTRL_ROWID_IN_VIEW,0,"?BOOLEAN?" },
{"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" },
{"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" },
{"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" },
{"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"},
};
@@ -11065,10 +11066,24 @@
isOk = 3;
}
break;
}
#endif
+ case SQLITE_TESTCTRL_ROWID_IN_VIEW: {
+ rc2 = -1;
+ if( nArg>=3 ){
+ if( !ShellHasFlag(p,SHFLG_TestingMode) ){
+ eputz("The --unsafe-testing option is required to change "
+ "this setting\n");
+ }else{
+ rc2 = booleanValue(azArg[2]);
+ }
+ }
+ sqlite3_test_control(testctrl, &rc2);
+ isOk = 1;
+ break;
+ }
#ifdef SQLITE_DEBUG
case SQLITE_TESTCTRL_TUNE: {
if( nArg==4 ){
int id = (int)integerValue(azArg[2]);
int val = (int)integerValue(azArg[3]);
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -8305,10 +8305,11 @@
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
+#define SQLITE_TESTCTRL_ROWID_IN_VIEW 16
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
Index: src/sqliteInt.h
==================================================================
--- src/sqliteInt.h
+++ src/sqliteInt.h
@@ -2533,10 +2533,19 @@
/* Does the table have a rowid */
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is
+** available. By default, this macro is false
+*/
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+# define ViewCanHaveRowid 0
+#else
+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0)
+#endif
+
/*
** Each foreign key constraint is an instance of the following structure.
**
** A foreign key is associated with two tables. The "from" table is
** the table that contains the REFERENCES clause that creates the foreign
@@ -3268,14 +3277,16 @@
** In the colUsed field, the high-order bit (bit 63) is set if the table
** contains more than 63 columns and the 64-th or later column is used.
**
** Union member validity:
**
-** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
-** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
-** u2.pIBIndex fg.isIndexedBy && !fg.isCte
-** u2.pCteUse fg.isCte && !fg.isIndexedBy
+** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
+** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
+** u1.nRow !fg.isTabFunc && !fg.isIndexedBy
+**
+** u2.pIBIndex fg.isIndexedBy && !fg.isCte
+** u2.pCteUse fg.isCte && !fg.isIndexedBy
*/
struct SrcItem {
Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
char *zName; /* Name of the table */
@@ -3309,10 +3320,11 @@
} u3;
Bitmask colUsed; /* Bit N set if column N used. Details above for N>62 */
union {
char *zIndexedBy; /* Identifier from "INDEXED BY " clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
+ u32 nRow; /* Number of rows in a VALUES clause */
} u1;
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
CteUse *pCteUse; /* CTE Usage info when fg.isCte is true */
} u2;
@@ -3810,10 +3822,11 @@
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
u8 okConstFactor; /* OK to factor out constants */
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
#ifdef SQLITE_DEBUG
u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */
@@ -4248,10 +4261,15 @@
sqlite3_int64 mxMemdbSize; /* Default max memdb size */
#endif
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW
+ ** feature is disabled. 0 if rowids can
+ ** occur in views. */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */
@@ -4483,10 +4501,13 @@
int regStartRowid;
int regEndRowid;
u8 bExprArgs; /* Defer evaluation of window function arguments
** 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*);
void sqlite3WindowListDelete(sqlite3 *db, Window *p);
@@ -5052,12 +5073,11 @@
void sqlite3CloseSavepoints(sqlite3 *);
void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
u32 sqlite3IsTrueOrFalse(const char*);
int sqlite3ExprIdToTrueFalse(Expr*);
int sqlite3ExprTruthValue(const Expr*);
-int sqlite3ExprIsConstant(Expr*);
-int sqlite3ExprIsConstantNotJoin(Expr*);
+int sqlite3ExprIsConstant(Parse*,Expr*);
int sqlite3ExprIsConstantOrFunction(Expr*, u8);
int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
int sqlite3ExprIsTableConstant(Expr*,int);
int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
Index: src/where.c
==================================================================
--- src/where.c
+++ src/where.c
@@ -1327,11 +1327,11 @@
for(i=0; ia[i].pExpr;
Expr *pE2;
/* Skip over constant terms in the ORDER BY clause */
- if( sqlite3ExprIsConstant(pExpr) ){
+ if( sqlite3ExprIsConstant(0, pExpr) ){
continue;
}
/* Virtual tables are unable to deal with NULLS FIRST */
if( pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_BIGNULL ) break;
@@ -1439,11 +1439,11 @@
}
assert( j==nTerm );
pIdxInfo->nConstraint = j;
for(i=j=0; ia[i].pExpr;
- if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( sqlite3ExprIsConstant(0, pExpr) ) continue;
assert( pExpr->op==TK_COLUMN
|| (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN
&& pExpr->iColumn==pExpr->pLeft->iColumn) );
pIdxOrderBy[j].iColumn = pExpr->iColumn;
pIdxOrderBy[j].desc = pOrderBy->a[i].fg.sortFlags & KEYINFO_ORDER_DESC;
@@ -3621,11 +3621,11 @@
Expr *pLeft = pPart->pLeft;
Expr *pRight = pPart->pRight;
u8 aff;
if( pLeft->op!=TK_COLUMN ) return;
- if( !sqlite3ExprIsConstant(pRight) ) return;
+ if( !sqlite3ExprIsConstant(0, pRight) ) return;
if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return;
if( pLeft->iColumn<0 ) return;
aff = pIdx->pTable->aCol[pLeft->iColumn].affinity;
if( aff>=SQLITE_AFF_TEXT ){
if( pItem ){
@@ -4995,11 +4995,11 @@
Expr *p;
Bitmask mTerm;
if( MASKBIT(i) & obSat ) continue;
p = pOrderBy->a[i].pExpr;
mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p);
- if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
+ if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue;
if( (mTerm&~orderDistinctMask)==0 ){
obSat |= MASKBIT(i);
}
}
}
@@ -5864,11 +5864,11 @@
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
bMaybeNullRow = 0;
}else{
continue;
}
- if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( sqlite3ExprIsConstant(0,pExpr) ) continue;
if( pExpr->op==TK_FUNCTION ){
/* Functions that might set a subtype should not be replaced by the
** value taken from an expression index since the index omits the
** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
int n;
@@ -6142,11 +6142,15 @@
if( (wctrlFlags & WHERE_WANT_DISTINCT)!=0
&& OptimizationEnabled(db, SQLITE_DistinctOpt)
){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
- ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ if( ALWAYS(pWInfo->pSelect)
+ && (pWInfo->pSelect->selFlags & SF_MultiValue)==0
+ ){
+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ }
}else{
/* Assign a bit from the bitmask to every term in the FROM clause.
**
** The N-th term of the FROM clause is assigned a bitmask of 1<fg.viaCoroutine ){
testcase( pParse->db->mallocFailed );
+ assert( pTabItem->regResult>=0 );
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
pTabItem->regResult, 0);
continue;
}
Index: src/whereexpr.c
==================================================================
--- src/whereexpr.c
+++ src/whereexpr.c
@@ -987,11 +987,11 @@
if( pIdx->aColExpr==0 ) continue;
for(i=0; inKeyCol; i++){
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
assert( pIdx->bHasExpr );
if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
- && !sqlite3ExprIsConstant(pIdx->aColExpr->a[i].pExpr)
+ && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr)
){
aiCurCol[0] = iCur;
aiCurCol[1] = XN_EXPR;
return 1;
}
Index: src/window.c
==================================================================
--- src/window.c
+++ src/window.c
@@ -1162,11 +1162,11 @@
** constant, change it to NULL. The fact that it is then a non-negative
** integer will be caught later. But it is important not to leave
** variable values in the expression tree.
*/
static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
- if( 0==sqlite3ExprIsConstant(pExpr) ){
+ if( 0==sqlite3ExprIsConstant(0,pExpr) ){
if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr);
sqlite3ExprDelete(pParse->db, pExpr);
pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0);
}
return pExpr;
Index: test/altertab3.test
==================================================================
--- test/altertab3.test
+++ test/altertab3.test
@@ -733,7 +733,34 @@
SELECT z, y FROM (
SELECT "t3".* FROM "t3"
);
END}
}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 30.0 {
+ CREATE TABLE t1(a, b);
+ CREATE VIEW v1 AS
+ SELECT ( VALUES(a), (b) ) FROM (
+ SELECT a, b FROM t1
+ )
+ ;
+}
+
+do_execsql_test 30.1 {
+ SELECT * FROM v1
+}
+
+do_execsql_test 30.1 {
+ ALTER TABLE t1 RENAME TO t2;
+}
+do_execsql_test 30.2 {
+ SELECT sql FROM sqlite_schema WHERE type='view'
+} {
+ {CREATE VIEW v1 AS
+ SELECT ( VALUES(a), (b) ) FROM (
+ SELECT a, b FROM "t2"
+ )}
+}
finish_test
ADDED test/cksumvfs.test
Index: test/cksumvfs.test
==================================================================
--- /dev/null
+++ test/cksumvfs.test
@@ -0,0 +1,33 @@
+# 2024 March 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.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix cksumvfs
+
+sqlite3_register_cksumvfs
+db close
+sqlite3 db test.db
+file_control_reservebytes db 8
+
+set text [db one "SELECT hex(randomblob(5000))"]
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, $text);
+}
+
+do_execsql_test 1.1 {
+ SELECT * FROM t1;
+} [list 1 $text]
+
+finish_test
Index: test/func4.test
==================================================================
--- test/func4.test
+++ test/func4.test
@@ -1,6 +1,6 @@
-# 2013 March 10
+# 2023-03-10
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
@@ -7,11 +7,14 @@
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library. The focus of
-# this file is testing the tointeger() and toreal() functions.
+# this file is testing the tointeger() and toreal() functions that are
+# part of the "totype.c" extension. This file does not test the core
+# SQLite library. Failures of tests in this file are related to the
+# ext/misc/totype.c extension.
#
# Several of the toreal() tests are disabled on platforms where floating
# point precision is not high enough to represent their constant integer
# expression arguments as double precision floating point values.
#
@@ -21,10 +24,24 @@
set tcl_precision 0
load_static_extension db totype
set highPrecision(1) [expr \
{[db eval {SELECT tointeger(9223372036854775807 + 1);}] eq {{}}}]
+set highPrecision(2) [expr \
+ {[db eval {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}]
+
+# highPrecision(3) is only known to be false on i586 with gcc-13 and -O2.
+# It is true on the exact same platform with -O0. Both results seem
+# reasonable, so we'll just very the expectation accordingly.
+#
+set highPrecision(3) [expr \
+ {[db eval {SELECT toreal(9007199254740992 + 1);}] eq {{}}}]
+
+if {!$highPrecision(1) || !$highPrecision(2) || !$highPrecision(3)} {
+ puts "NOTICE: use_long_double: [use_long_double] \
+ highPrecision: $highPrecision(1) $highPrecision(2) $highPrecision(3)"
+}
do_execsql_test func4-1.1 {
SELECT tointeger(NULL);
} {{}}
do_execsql_test func4-1.2 {
@@ -193,12 +210,10 @@
do_execsql_test func4-1.55 {
SELECT tointeger(18446744073709551616 + 1);
} {{}}
ifcapable floatingpoint {
- set highPrecision(2) [expr \
- {[db eval {SELECT toreal(-9223372036854775808 + 1);}] eq {{}}}]
do_execsql_test func4-2.1 {
SELECT toreal(NULL);
} {{}}
do_execsql_test func4-2.2 {
@@ -339,14 +354,18 @@
SELECT toreal(9007199254740992 - 1);
} {9007199254740991.0}
do_execsql_test func4-2.45 {
SELECT toreal(9007199254740992);
} {9007199254740992.0}
- if {$highPrecision(2)} {
+ if {$highPrecision(3)} {
+ do_execsql_test func4-2.46 {
+ SELECT toreal(9007199254740992 + 1);
+ } {{}}
+ } else {
do_execsql_test func4-2.46 {
SELECT toreal(9007199254740992 + 1);
- } {{}}
+ } {9007199254740992.0}
}
do_execsql_test func4-2.47 {
SELECT toreal(9007199254740992 + 2);
} {9007199254740994.0}
do_execsql_test func4-2.48 {
@@ -624,14 +643,18 @@
SELECT tointeger(toreal(9007199254740992 - 1));
} {9007199254740991}
do_execsql_test func4-5.22 {
SELECT tointeger(toreal(9007199254740992));
} {9007199254740992}
- if {$highPrecision(2)} {
+ if {$highPrecision(3)} {
do_execsql_test func4-5.23 {
SELECT tointeger(toreal(9007199254740992 + 1));
} {{}}
+ } else {
+ do_execsql_test func4-5.23 {
+ SELECT tointeger(toreal(9007199254740992 + 1));
+ } {9007199254740992}
}
do_execsql_test func4-5.24 {
SELECT tointeger(toreal(9007199254740992 + 2));
} {9007199254740994}
if {$highPrecision(1)} {
Index: test/in4.test
==================================================================
--- test/in4.test
+++ test/in4.test
@@ -456,18 +456,18 @@
ANALYZE sqlite_schema;
} {}
do_execsql_test 11.1 {
SELECT * FROM t1
WHERE b IN (345, (SELECT 1 FROM t1
- WHERE b IN (345 NOT GLOB 510)
+ WHERE b IN (coalesce(1,random()))
AND c GLOB 'abc*xyz'))
AND c BETWEEN 'abc' AND 'xyz';
} {xyz 1 abcdefxyz 99}
do_execsql_test 11.2 {
EXPLAIN SELECT * FROM t1
WHERE b IN (345, (SELECT 1 FROM t1
- WHERE b IN (345 NOT GLOB 510)
+ WHERE b IN (coalesce(1,random()))
AND c GLOB 'abc*xyz'))
AND c BETWEEN 'abc' AND 'xyz';
} {/ SeekScan /}
# 2021-06-25 ticket 6dcbfd11cf666e21
Index: test/sqllimits1.test
==================================================================
--- test/sqllimits1.test
+++ test/sqllimits1.test
@@ -920,11 +920,11 @@
} {0 {}}
do_catchsql_test sqllimits1-18.2 {
INSERT INTO b1 VALUES(1), (2), (3), (4), (5), (6), (7), (8), (9), (10)
UNION VALUES(11);
-} {1 {too many terms in compound SELECT}}
+} {0 {}}
#-------------------------------------------------------------------------
#
reset_db
ifcapable utf16 {
Index: test/testrunner.tcl
==================================================================
--- test/testrunner.tcl
+++ test/testrunner.tcl
@@ -60,20 +60,29 @@
$a0 status
where SWITCHES are:
--buildonly
--dryrun
+ --explain
--jobs NUMBER-OF-JOBS
+ --stop-on-coredump
+ --stop-on-error
--zipvfs ZIPVFS-SOURCE-DIR
-Interesting values for PERMUTATION are:
+Special values for PERMUTATION that work with plain tclsh:
- veryquick - a fast subset of the tcl test scripts. This is the default.
- full - all tcl test scripts.
+ list - show all allowed PERMUTATION arguments.
+ mdevtest - tests recommended prior to normal development check-ins.
+ release - full release test with various builds.
+ sdevtest - like mdevtest but using ASAN and UBSAN.
+
+Other PERMUTATION arguments must be run using testfixture, not tclsh:
+
all - all tcl test scripts, plus a subset of test scripts rerun
with various permutations.
- release - full release test with various builds.
+ full - all tcl test scripts.
+ veryquick - a fast subset of the tcl test scripts. This is the default.
If no PATTERN arguments are present, all tests specified by the PERMUTATION
are run. Otherwise, each pattern is interpreted as a glob pattern. Only
those tcl tests for which the final component of the filename matches at
least one specified pattern are run.
@@ -93,10 +102,12 @@
of sub-processes the test script uses to run tests.
The "script" command outputs the script used to build a configuration.
Add the "-msvc" option for a Windows-compatible script. For a list of
available configurations enter "$a0 script help".
+
+Full documentation here: https://sqlite.org/src/doc/trunk/doc/testrunner.md
}]]
exit 1
}
#-------------------------------------------------------------------------
@@ -130,10 +141,14 @@
}
return $ret
}
proc default_njob {} {
+ global env
+ if {[info exists env(NJOB)] && $env(NJOB)>=1} {
+ return $env(NJOB)
+ }
set nCore [guess_number_of_cores]
if {$nCore<=2} {
set nHelper 1
} else {
set nHelper [expr int($nCore*0.5)]
@@ -156,10 +171,13 @@
set TRG(reporttime) 2000
set TRG(fuzztest) 0 ;# is the fuzztest option present.
set TRG(zipvfs) "" ;# -zipvfs option, if any
set TRG(buildonly) 0 ;# True if --buildonly option
set TRG(dryrun) 0 ;# True if --dryrun option
+set TRG(explain) 0 ;# True for the --explain option
+set TRG(stopOnError) 0 ;# Stop running at first failure
+set TRG(stopOnCore) 0 ;# Stop on a core-dump
switch -nocase -glob -- $tcl_platform(os) {
*darwin* {
set TRG(platform) osx
set TRG(make) make.sh
@@ -450,10 +468,16 @@
if {$isLast} { usage }
} elseif {($n>2 && [string match "$a*" --buildonly]) || $a=="-b"} {
set TRG(buildonly) 1
} elseif {($n>2 && [string match "$a*" --dryrun]) || $a=="-d"} {
set TRG(dryrun) 1
+ } elseif {($n>2 && [string match "$a*" --explain]) || $a=="-e"} {
+ set TRG(explain) 1
+ } elseif {[string match "$a*" --stop-on-error]} {
+ set TRG(stopOnError) 1
+ } elseif {[string match "$a*" --stop-on-coredump]} {
+ set TRG(stopOnCore) 1
} else {
usage
}
} else {
lappend TRG(patternlist) [string map {% *} $a]
@@ -831,10 +855,21 @@
}
}
}
}
+
+# Check to ensure that the interpreter is a full-blown "testfixture"
+# build and not just a "tclsh". If this is not the case, issue an
+# error message and exit.
+#
+proc must_be_testfixture {} {
+ if {[lsearch [info commands] sqlite3_soft_heap_limit]<0} {
+ puts "Use testfixture, not tclsh, for these arguments."
+ exit 1
+ }
+}
proc add_jobs_from_cmdline {patternlist} {
global TRG
if {$TRG(zipvfs)!=""} {
@@ -847,23 +882,32 @@
}
set first [lindex $patternlist 0]
switch -- $first {
all {
+ must_be_testfixture
set patternlist [lrange $patternlist 1 end]
set clist [trd_all_configs]
foreach c $clist {
add_tcl_jobs "" $c $patternlist
}
}
mdevtest {
- add_devtest_jobs {All-O0 All-Debug} [lrange $patternlist 1 end]
+ set config_set {
+ All-O0
+ All-Debug
+ }
+ add_devtest_jobs $config_set [lrange $patternlist 1 end]
}
sdevtest {
- add_devtest_jobs {All-Sanitize All-Debug} [lrange $patternlist 1 end]
+ set config_set {
+ All-Sanitize
+ All-Debug
+ }
+ add_devtest_jobs $config_set [lrange $patternlist 1 end]
}
release {
set patternlist [lrange $patternlist 1 end]
foreach b [trd_builds $TRG(platform)] {
@@ -881,12 +925,20 @@
}
}
}
}
}
+
+ list {
+ set allperm [array names ::testspec]
+ lappend allperm all mdevtest sdevtest release list
+ puts "Allowed values for the PERMUTATION argument: [lsort $allperm]"
+ exit 0
+ }
default {
+ must_be_testfixture
if {[info exists ::testspec($first)]} {
add_tcl_jobs "" $first [lrange $patternlist 1 end]
} else {
add_tcl_jobs "" full $patternlist
}
@@ -946,10 +998,18 @@
if {[info exists TRG(reportlength)]} {
puts -nonewline "[string repeat " " $TRG(reportlength)]\r"
}
puts "FAILED: $job(displayname) ($iJob)"
set state "failed"
+ if {$TRG(stopOnError)} {
+ puts "OUTPUT: $O($iJob)"
+ exit 1
+ }
+ if {$TRG(stopOnCore) && [string first {core dumped} $O($iJob)]>0} {
+ puts "OUTPUT: $O($iJob)"
+ exit 1
+ }
}
set tm [clock_milliseconds]
set jobtm [expr {$tm - $job(starttime)}]
@@ -1006,13 +1066,18 @@
set fd [open [file join $dir $TRG(make)] w]
puts $fd $script
close $fd
}
- set job_cmd $job(cmd)
- if {$TRG(platform)!="win"} {
- set job_cmd "export SQLITE_TMPDIR=\"[file normalize $dir]\"\n$job_cmd"
+ # Add a batch/shell file command to set the directory used for temp
+ # files to the test's working directory. Otherwise, tests that use
+ # large numbers of temp files (e.g. zipvfs), might generate temp
+ # filename collisions.
+ if {$TRG(platform)=="win"} {
+ set set_tmp_dir "SET SQLITE_TMPDIR=[file normalize $dir]"
+ } else {
+ set set_tmp_dir "export SQLITE_TMPDIR=\"[file normalize $dir]\""
}
if { $TRG(dryrun) } {
mark_job_as_finished $job(jobid) "" done 0
@@ -1025,11 +1090,12 @@
} else {
set pwd [pwd]
cd $dir
set fd [open $TRG(run) w]
- puts $fd $job_cmd
+ puts $fd $set_tmp_dir
+ puts $fd $job(cmd)
close $fd
set fd [open "|$TRG(runcmd) 2>@1" r]
cd $pwd
fconfigure $fd -blocking false
@@ -1140,18 +1206,45 @@
r_write_db {
trdb eval { DELETE FROM jobs WHERE displaytype!='bld' }
}
}
}
+
+# Handle the --explain option. Provide a human-readable
+# explanation of all the tests that are in the trdb database jobs
+# table.
+#
+proc explain_layer {indent depid} {
+ global TRG
+ if {$TRG(buildonly)} {
+ set showtests 0
+ } else {
+ set showtests 1
+ }
+ trdb eval {SELECT jobid, displayname, displaytype, dirname
+ FROM jobs WHERE depid=$depid ORDER BY displayname} {
+ if {$displaytype=="bld"} {
+ puts "${indent}$displayname in $dirname"
+ explain_layer "${indent} " $jobid
+ } elseif {$showtests} {
+ puts "${indent}[lindex $displayname end]"
+ }
+ }
+}
+proc explain_tests {} {
+ explain_layer "" ""
+}
sqlite3 trdb $TRG(dbname)
trdb timeout $TRG(timeout)
set tm [lindex [time { make_new_testset }] 0]
-if {$TRG(nJob)>1} {
- puts "splitting work across $TRG(nJob) jobs"
-}
-puts "built testset in [expr $tm/1000]ms.."
-
-handle_buildonly
-run_testset
-trdb close
-#puts [pwd]
+if {$TRG(explain)} {
+ explain_tests
+} else {
+ if {$TRG(nJob)>1} {
+ puts "splitting work across $TRG(nJob) jobs"
+ }
+ puts "built testset in [expr $tm/1000]ms.."
+ handle_buildonly
+ run_testset
+}
+trdb close
Index: test/testrunner_data.tcl
==================================================================
--- test/testrunner_data.tcl
+++ test/testrunner_data.tcl
@@ -106,10 +106,11 @@
set build(Sanitize) {
CC=clang -fsanitize=address,undefined -fno-sanitize-recover=undefined
-DSQLITE_ENABLE_STAT4
-DSQLITE_OMIT_LOOKASIDE=1
-DCONFIG_SLOWDOWN_FACTOR=5.0
+ -DSQLITE_ENABLE_RBU
--enable-debug
--enable-all
}
set build(Stdcall) {
-DUSE_STDCALL=1
ADDED test/values.test
Index: test/values.test
==================================================================
--- /dev/null
+++ test/values.test
@@ -0,0 +1,583 @@
+# 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}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 6.0 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES('x'), ('y');
+}
+
+do_execsql_test 6.1 {
+ SELECT * FROM t1, (VALUES(1), (2))
+} {x 1 x 2 y 1 y 2}
+
+do_execsql_test 6.2 {
+ VALUES(CAST(44 AS REAL)),(55);
+} {44.0 55}
+
+#------------------------------------------------------------------------
+do_execsql_test 7.1 {
+ WITH x1(a, b) AS (
+ VALUES(1, 2), ('a', 'b')
+ )
+ SELECT * FROM x1 one, x1 two
+} {
+ 1 2 1 2
+ 1 2 a b
+ a b 1 2
+ a b a b
+}
+
+#-------------------------------------------------------------------------
+reset_db
+
+set VVV {
+ ( VALUES('a', 'b'), ('c', 'd'), (123, NULL) )
+}
+set VVV2 {
+ (
+ SELECT 'a' AS column1, 'b' AS column2
+ UNION ALL SELECT 'c', 'd' UNION ALL SELECT 123, NULL
+ )
+}
+
+do_execsql_test 8.0 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES('d'), (NULL), (123)
+}
+foreach {tn q res} {
+ 1 "SELECT * FROM t1 LEFT JOIN VVV" {
+ d a b d c d d 123 {}
+ {} a b {} c d {} 123 {}
+ 123 a b 123 c d 123 123 {}
+ }
+
+ 2 "SELECT * FROM t1 LEFT JOIN VVV ON (column1=x)" {
+ d {} {}
+ {} {} {}
+ 123 123 {}
+ }
+
+ 3 "SELECT * FROM t1 RIGHT JOIN VVV" {
+ d a b d c d d 123 {}
+ {} a b {} c d {} 123 {}
+ 123 a b 123 c d 123 123 {}
+ }
+
+ 4 "SELECT * FROM t1 RIGHT JOIN VVV ON (column1=x)" {
+ 123 123 {}
+ {} a b
+ {} c d
+ }
+
+ 5 "SELECT * FROM t1 FULL OUTER JOIN VVV ON (column1=x)" {
+ d {} {}
+ {} {} {}
+ 123 123 {}
+ {} a b
+ {} c d
+ }
+
+ 6 "SELECT count(*) FROM VVV" { 3 }
+
+ 7 "SELECT (SELECT column1 FROM VVV)" { a }
+
+ 8 "SELECT * FROM VVV UNION ALL SELECT * FROM VVV" {
+ a b c d 123 {}
+ a b c d 123 {}
+ }
+
+ 9 "SELECT * FROM VVV INTERSECT SELECT * FROM VVV" {
+ 123 {} a b c d
+ }
+
+ 10 "SELECT * FROM VVV eXCEPT SELECT * FROM VVV" { }
+
+ 11 "SELECT * FROM VVV eXCEPT SELECT 'a', 'b'" { 123 {} c d }
+
+} {
+ set q1 [string map [list VVV $VVV] $q]
+ set q2 [string map [list VVV $VVV2] $q]
+ set q3 "WITH VVV AS $VVV $q"
+
+ do_execsql_test 8.1.$tn.1 $q1 $res
+ do_execsql_test 8.1.$tn.2 $q2 $res
+ do_execsql_test 8.1.$tn.3 $q3 $res
+}
+
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 9.1 {
+ VALUES(456), (123), (NULL) UNION ALL SELECT 122 ORDER BY 1
+} { {} 122 123 456 }
+
+do_execsql_test 9.2 {
+ VALUES (1, 2), (3, 4), (
+ ( SELECT column1 FROM ( VALUES (5, 6), (7, 8) ) ),
+ ( SELECT max(column2) FROM ( VALUES (5, 1), (7, 6) ) )
+ )
+} { 1 2 3 4 5 6 }
+
+do_execsql_test 10.1 {
+ CREATE TABLE a2(a, b, c DEFAULT 'xyz');
+}
+do_execsql_test 10.2 {
+ INSERT INTO a2(a) VALUES(3),(4);
+}
+
+#-------------------------------------------------------------------------
+reset_db
+ifcapable fts5 {
+ do_execsql_test 11.0 {
+ CREATE VIRTUAL TABLE ft USING fts3(x);
+ }
+ do_execsql_test 11.1 {
+ INSERT INTO ft VALUES('one'), ('two');
+ }
+}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 12.0 {
+ CREATE TABLE t1(a, b);
+}
+do_execsql_test 12.1 {
+ INSERT INTO t1 SELECT 1, 2 UNION ALL VALUES(3, 4), (5, 6);
+}
+do_execsql_test 12.2 {
+ SELECT * FROM t1
+} {1 2 3 4 5 6}
+
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 13.0 {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES('xyz');
+
+ SELECT (
+ VALUES( (max(substr('abc', 1, 1), x)) ),
+ (123),
+ (456)
+ )
+ FROM t1;
+} {xyz}
+
+do_catchsql_test 13.1 {
+ VALUES(300), (zeroblob(300) OVER win);
+} {1 {zeroblob() may not be used as a window function}}
+
+#--------------------------------------------------------------------------
+reset_db
+do_execsql_test 14.1 {
+ PRAGMA encoding = utf16;
+ CREATE TABLE t1(a, b);
+} {}
+
+db close
+sqlite3 db test.db
+
+do_execsql_test 14.2 {
+ INSERT INTO t1 VALUES
+ (17, 'craft'),
+ (16, 'urtlek' IN(1,2,3));
+}
+
+#--------------------------------------------------------------------------
+#
+reset_db
+do_eqp_test 15.1 {
+ VALUES(1),(2),(3),(4),(5);
+} {
+ QUERY PLAN
+ `--SCAN 5-ROW VALUES CLAUSE
+}
+do_execsql_test 15.2 {
+ CREATE TABLE t1(a,b);
+}
+do_eqp_test 15.3 {
+ INSERT INTO t1 VALUES
+ (1,2),(3,4),(7,8);
+} {
+ QUERY PLAN
+ `--SCAN 3-ROW VALUES CLAUSE
+}
+do_eqp_test 15.4 {
+ INSERT INTO t1 VALUES
+ (1,2),(3,4),(7,8),
+ (5,row_number()OVER());
+} {
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN 3-ROW VALUES CLAUSE
+ `--UNION ALL
+ |--CO-ROUTINE (subquery-xxxxxx)
+ | `--SCAN CONSTANT ROW
+ `--SCAN (subquery-xxxxxx)
+}
+do_eqp_test 15.5 {
+ SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6)), (VALUES('a'),('b'),('c'));
+} {
+ QUERY PLAN
+ |--SCAN 6-ROW VALUES CLAUSE
+ `--SCAN 3-ROW VALUES CLAUSE
+}
+do_execsql_test 15.6 {
+ CREATE TABLE t2(x,y);
+}
+do_eqp_test 15.7 {
+ SELECT * FROM t2 UNION ALL VALUES(1,2),(3,4),(5,6),(7,8);
+} {
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN t2
+ `--UNION ALL
+ `--SCAN 4-ROW VALUES CLAUSE
+}
+
+#--------------------------------------------------------------------------
+# The VALUES-as-coroutine optimization can be applied to later rows of
+# a VALUES clause even if earlier rows do not qualify.
+#
+reset_db
+do_execsql_test 16.1 {
+ CREATE TABLE t1(a,b);
+}
+do_execsql_test 16.2 {
+ BEGIN;
+ INSERT INTO t1 VALUES(1,2),(3,4),(5,6),
+ (7,row_number()OVER()),
+ (9,10), (11,12), (13,14), (15,16);
+ SELECT * FROM t1 ORDER BY a, b;
+ ROLLBACK;
+} {1 2 3 4 5 6 7 1 9 10 11 12 13 14 15 16}
+do_eqp_test 16.3 {
+ INSERT INTO t1 VALUES(1,2),(3,4),(5,6),
+ (7,row_number()OVER()),
+ (9,10), (11,12), (13,14), (15,16);
+} {
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN 3-ROW VALUES CLAUSE
+ |--UNION ALL
+ | |--CO-ROUTINE (subquery-xxxxxx)
+ | | `--SCAN CONSTANT ROW
+ | `--SCAN (subquery-xxxxxx)
+ `--UNION ALL
+ `--SCAN 4-ROW VALUES CLAUSE
+}
+do_execsql_test 16.4 {
+ BEGIN;
+ INSERT INTO t1 VALUES
+ (1,row_number()OVER()),
+ (2,3), (4,5), (6,7);
+ SELECT * FROM t1 ORDER BY a, b;
+ ROLLBACK;
+} {1 1 2 3 4 5 6 7}
+do_eqp_test 16.5 {
+ INSERT INTO t1 VALUES
+ (1,row_number()OVER()),
+ (2,3), (4,5), (6,7);
+} {
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | |--CO-ROUTINE (subquery-xxxxxx)
+ | | `--SCAN CONSTANT ROW
+ | `--SCAN (subquery-xxxxxx)
+ `--UNION ALL
+ `--SCAN 3-ROW VALUES CLAUSE
+}
+do_execsql_test 16.6 {
+ BEGIN;
+ INSERT INTO t1 VALUES
+ (1,2),(3,4),
+ (5,row_number()OVER()),
+ (7,8),(9,10),(11,12),
+ (13,row_number()OVER()),
+ (15,16),(17,18),(19,20),(21,22);
+ SELECT * FROM t1 ORDER BY a, b;
+ ROLLBACK;
+} { 1 2 3 4 5 1 7 8 9 10 11 12 13 1 15 16 17 18 19 20 21 22}
+do_eqp_test 16.7 {
+ INSERT INTO t1 VALUES
+ (1,2),(3,4),
+ (5,row_number()OVER()),
+ (7,8),(9,10),(11,12),
+ (13,row_number()OVER()),
+ (15,16),(17,18),(19,20),(21,22);
+} {
+ QUERY PLAN
+ `--COMPOUND QUERY
+ |--LEFT-MOST SUBQUERY
+ | `--SCAN 2-ROW VALUES CLAUSE
+ |--UNION ALL
+ | |--CO-ROUTINE (subquery-xxxxxx)
+ | | `--SCAN CONSTANT ROW
+ | `--SCAN (subquery-xxxxxx)
+ |--UNION ALL
+ | `--SCAN 3-ROW VALUES CLAUSE
+ |--UNION ALL
+ | |--CO-ROUTINE (subquery-xxxxxx)
+ | | `--SCAN CONSTANT ROW
+ | `--SCAN (subquery-xxxxxx)
+ `--UNION ALL
+ `--SCAN 4-ROW VALUES CLAUSE
+}
+
+finish_test
ADDED test/valuesfault.test
Index: test/valuesfault.test
==================================================================
--- /dev/null
+++ 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
Index: test/whereL.test
==================================================================
--- test/whereL.test
+++ test/whereL.test
@@ -47,10 +47,37 @@
QUERY PLAN
|--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
|--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
`--SCAN t3
}
+do_eqp_test 121 {
+ SELECT * FROM t1, t2, t3
+ WHERE t1.a=t2.a AND t2.a=t3.j AND t3.j=abs(5)
+ ORDER BY t1.a;
+} {
+ QUERY PLAN
+ |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
+ `--SCAN t3
+}
+
+# The sqlite3ExprIsConstant() routine does not believe that
+# the expression "coalesce(5,random())" is constant. So the
+# optimization does not apply in this case.
+#
+sqlite3_create_function db
+do_eqp_test 122 {
+ SELECT * FROM t1, t2, t3
+ WHERE t1.a=t2.a AND t2.a=t3.j AND t3.j=coalesce(5,random())
+ ORDER BY t1.a;
+} {
+ QUERY PLAN
+ |--SCAN t3
+ |--SEARCH t1 USING INDEX sqlite_autoindex_t1_1 (a=?)
+ |--SEARCH t2 USING INDEX sqlite_autoindex_t2_1 (a=?)
+ `--USE TEMP B-TREE FOR ORDER BY
+}
# Constant propagation in the face of collating sequences:
#
do_execsql_test 200 {
CREATE TABLE c3(x COLLATE binary, y COLLATE nocase, z COLLATE binary);
Index: tool/lemon.c
==================================================================
--- tool/lemon.c
+++ tool/lemon.c
@@ -56,10 +56,86 @@
** Compilers are getting increasingly pedantic about type conversions
** as C evolves ever closer to Ada.... To work around the latest problems
** we have to define the following variant of strlen().
*/
#define lemonStrlen(X) ((int)strlen(X))
+
+/*
+** Header on the linked list of memory allocations.
+*/
+typedef struct MemChunk MemChunk;
+struct MemChunk {
+ MemChunk *pNext;
+ size_t sz;
+ /* Actually memory follows */
+};
+
+/*
+** Global linked list of all memory allocations.
+*/
+static MemChunk *memChunkList = 0;
+
+/*
+** Wrappers around malloc(), calloc(), realloc() and free().
+**
+** All memory allocations are kept on a doubly-linked list. The
+** lemon_free_all() function can be called prior to exit to clean
+** up any memory leaks.
+**
+** This is not necessary. But compilers and getting increasingly
+** fussy about memory leaks, even in command-line programs like Lemon
+** where they do not matter. So this code is provided to hush the
+** warnings.
+*/
+static void *lemon_malloc(size_t nByte){
+ MemChunk *p;
+ if( nByte<0 ) return 0;
+ p = malloc( nByte + sizeof(MemChunk) );
+ if( p==0 ){
+ fprintf(stderr, "Out of memory. Failed to allocate %lld bytes.\n",
+ (long long int)nByte);
+ exit(1);
+ }
+ p->pNext = memChunkList;
+ p->sz = nByte;
+ memChunkList = p;
+ return (void*)&p[1];
+}
+static void *lemon_calloc(size_t nElem, size_t sz){
+ void *p = lemon_malloc(nElem*sz);
+ memset(p, 0, nElem*sz);
+ return p;
+}
+static void lemon_free(void *pOld){
+ if( pOld ){
+ MemChunk *p = (MemChunk*)pOld;
+ p--;
+ memset(pOld, 0, p->sz);
+ }
+}
+static void *lemon_realloc(void *pOld, size_t nNew){
+ void *pNew;
+ MemChunk *p;
+ if( pOld==0 ) return lemon_malloc(nNew);
+ p = (MemChunk*)pOld;
+ p--;
+ if( p->sz>=nNew ) return pOld;
+ pNew = lemon_malloc( nNew );
+ memcpy(pNew, pOld, p->sz);
+ return pNew;
+}
+
+/* Free all outstanding memory allocations.
+** Do this right before exiting.
+*/
+static void lemon_free_all(void){
+ while( memChunkList ){
+ MemChunk *pNext = memChunkList->pNext;
+ free( memChunkList );
+ memChunkList = pNext;
+ }
+}
/*
** Compilers are starting to complain about the use of sprintf() and strcpy(),
** saying they are unsafe. So we define our own versions of those routines too.
**
@@ -495,11 +571,11 @@
struct action *newaction;
if( actionfreelist==0 ){
int i;
int amt = 100;
- actionfreelist = (struct action *)calloc(amt, sizeof(struct action));
+ actionfreelist = (struct action *)lemon_calloc(amt, sizeof(struct action));
if( actionfreelist==0 ){
fprintf(stderr,"Unable to allocate memory for a new parser action.");
exit(1);
}
for(i=0; iaAction[N].lookahead)
/* Free all memory associated with the given acttab */
void acttab_free(acttab *p){
- free( p->aAction );
- free( p->aLookahead );
- free( p );
+ lemon_free( p->aAction );
+ lemon_free( p->aLookahead );
+ lemon_free( p );
}
/* Allocate a new acttab structure */
acttab *acttab_alloc(int nsymbol, int nterminal){
- acttab *p = (acttab *) calloc( 1, sizeof(*p) );
+ acttab *p = (acttab *) lemon_calloc( 1, sizeof(*p) );
if( p==0 ){
fprintf(stderr,"Unable to allocate memory for a new acttab.");
exit(1);
}
memset(p, 0, sizeof(*p));
@@ -640,11 +716,11 @@
** state.
*/
void acttab_action(acttab *p, int lookahead, int action){
if( p->nLookahead>=p->nLookaheadAlloc ){
p->nLookaheadAlloc += 25;
- p->aLookahead = (struct lookahead_action *) realloc( p->aLookahead,
+ p->aLookahead = (struct lookahead_action *) lemon_realloc( p->aLookahead,
sizeof(p->aLookahead[0])*p->nLookaheadAlloc );
if( p->aLookahead==0 ){
fprintf(stderr,"malloc failed\n");
exit(1);
}
@@ -690,11 +766,11 @@
*/
n = p->nsymbol + 1;
if( p->nAction + n >= p->nActionAlloc ){
int oldAlloc = p->nActionAlloc;
p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20;
- p->aAction = (struct lookahead_action *) realloc( p->aAction,
+ p->aAction = (struct lookahead_action *) lemon_realloc( p->aAction,
sizeof(p->aAction[0])*p->nActionAlloc);
if( p->aAction==0 ){
fprintf(stderr,"malloc failed\n");
exit(1);
}
@@ -1312,11 +1388,11 @@
static struct config *basis = 0; /* Top of list of basis configs */
static struct config **basisend = 0; /* End of list of basis configs */
/* Return a pointer to a new configuration */
PRIVATE struct config *newconfig(void){
- return (struct config*)calloc(1, sizeof(struct config));
+ return (struct config*)lemon_calloc(1, sizeof(struct config));
}
/* The configuration "old" is no longer used */
PRIVATE void deleteconfig(struct config *old)
{
@@ -1528,23 +1604,23 @@
** Add the macro defined to the azDefine array.
*/
static void handle_D_option(char *z){
char **paz;
nDefine++;
- azDefine = (char **) realloc(azDefine, sizeof(azDefine[0])*nDefine);
+ azDefine = (char **) lemon_realloc(azDefine, sizeof(azDefine[0])*nDefine);
if( azDefine==0 ){
fprintf(stderr,"out of memory\n");
exit(1);
}
- bDefineUsed = (char*)realloc(bDefineUsed, nDefine);
+ bDefineUsed = (char*)lemon_realloc(bDefineUsed, nDefine);
if( bDefineUsed==0 ){
fprintf(stderr,"out of memory\n");
exit(1);
}
bDefineUsed[nDefine-1] = 0;
paz = &azDefine[nDefine-1];
- *paz = (char *) malloc( lemonStrlen(z)+1 );
+ *paz = (char *) lemon_malloc( lemonStrlen(z)+1 );
if( *paz==0 ){
fprintf(stderr,"out of memory\n");
exit(1);
}
lemon_strcpy(*paz, z);
@@ -1554,21 +1630,21 @@
/* Rember the name of the output directory
*/
static char *outputDir = NULL;
static void handle_d_option(char *z){
- outputDir = (char *) malloc( lemonStrlen(z)+1 );
+ outputDir = (char *) lemon_malloc( lemonStrlen(z)+1 );
if( outputDir==0 ){
fprintf(stderr,"out of memory\n");
exit(1);
}
lemon_strcpy(outputDir, z);
}
static char *user_templatename = NULL;
static void handle_T_option(char *z){
- user_templatename = (char *) malloc( lemonStrlen(z)+1 );
+ user_templatename = (char *) lemon_malloc( lemonStrlen(z)+1 );
if( user_templatename==0 ){
memory_error();
}
lemon_strcpy(user_templatename, z);
}
@@ -1801,10 +1877,11 @@
fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict);
}
/* return 0 on success, 1 on failure. */
exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0;
+ lemon_free_all();
exit(exitcode);
return (exitcode);
}
/******************** From the file "msort.c" *******************************/
/*
@@ -2389,11 +2466,11 @@
}
break;
case IN_RHS:
if( x[0]=='.' ){
struct rule *rp;
- rp = (struct rule *)calloc( sizeof(struct rule) +
+ rp = (struct rule *)lemon_calloc( sizeof(struct rule) +
sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs, 1);
if( rp==0 ){
ErrorMsg(psp->filename,psp->tokenlineno,
"Can't allocate enough memory for this rule.");
psp->errorcnt++;
@@ -2441,21 +2518,21 @@
}
}else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 && ISUPPER(x[1]) ){
struct symbol *msp = psp->rhs[psp->nrhs-1];
if( msp->type!=MULTITERMINAL ){
struct symbol *origsp = msp;
- msp = (struct symbol *) calloc(1,sizeof(*msp));
+ msp = (struct symbol *) lemon_calloc(1,sizeof(*msp));
memset(msp, 0, sizeof(*msp));
msp->type = MULTITERMINAL;
msp->nsubsym = 1;
- msp->subsym = (struct symbol **) calloc(1,sizeof(struct symbol*));
+ msp->subsym = (struct symbol**)lemon_calloc(1,sizeof(struct symbol*));
msp->subsym[0] = origsp;
msp->name = origsp->name;
psp->rhs[psp->nrhs-1] = msp;
}
msp->nsubsym++;
- msp->subsym = (struct symbol **) realloc(msp->subsym,
+ msp->subsym = (struct symbol **) lemon_realloc(msp->subsym,
sizeof(struct symbol*)*msp->nsubsym);
msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]);
if( ISLOWER(x[1]) || ISLOWER(msp->subsym[0]->name[0]) ){
ErrorMsg(psp->filename,psp->tokenlineno,
"Cannot form a compound containing a non-terminal");
@@ -2667,11 +2744,11 @@
}
lemon_sprintf(zLine, "#line %d ", psp->tokenlineno);
nLine = lemonStrlen(zLine);
n += nLine + lemonStrlen(psp->filename) + nBack;
}
- *psp->declargslot = (char *) realloc(*psp->declargslot, n);
+ *psp->declargslot = (char *) lemon_realloc(*psp->declargslot, n);
zBuf = *psp->declargslot + nOld;
if( addLineMacro ){
if( nOld && zBuf[-1]!='\n' ){
*(zBuf++) = '\n';
}
@@ -2781,11 +2858,11 @@
if( x[0]=='.' ){
psp->state = WAITING_FOR_DECL_OR_RULE;
}else if( ISUPPER(x[0]) || ((x[0]=='|' || x[0]=='/') && ISUPPER(x[1])) ){
struct symbol *msp = psp->tkclass;
msp->nsubsym++;
- msp->subsym = (struct symbol **) realloc(msp->subsym,
+ msp->subsym = (struct symbol **) lemon_realloc(msp->subsym,
sizeof(struct symbol*)*msp->nsubsym);
if( !ISUPPER(x[0]) ) x++;
msp->subsym[msp->nsubsym-1] = Symbol_new(x);
}else{
ErrorMsg(psp->filename, psp->tokenlineno,
@@ -2996,22 +3073,22 @@
return;
}
fseek(fp,0,2);
filesize = ftell(fp);
rewind(fp);
- filebuf = (char *)malloc( filesize+1 );
+ filebuf = (char *)lemon_malloc( filesize+1 );
if( filesize>100000000 || filebuf==0 ){
ErrorMsg(ps.filename,0,"Input file too large.");
- free(filebuf);
+ lemon_free(filebuf);
gp->errorcnt++;
fclose(fp);
return;
}
if( fread(filebuf,1,filesize,fp)!=filesize ){
ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.",
filesize);
- free(filebuf);
+ lemon_free(filebuf);
gp->errorcnt++;
fclose(fp);
return;
}
fclose(fp);
@@ -3119,11 +3196,11 @@
*cp = 0; /* Null terminate the token */
parseonetoken(&ps); /* Parse the token */
*cp = (char)c; /* Restore the buffer */
cp = nextcp;
}
- free(filebuf); /* Release the buffer after parsing */
+ lemon_free(filebuf); /* Release the buffer after parsing */
gp->rule = ps.firstrule;
gp->errorcnt = ps.errorcnt;
}
/*************************** From the file "plink.c" *********************/
/*
@@ -3137,11 +3214,11 @@
struct plink *newlink;
if( plink_freelist==0 ){
int i;
int amt = 100;
- plink_freelist = (struct plink *)calloc( amt, sizeof(struct plink) );
+ plink_freelist = (struct plink *)lemon_calloc( amt, sizeof(struct plink) );
if( plink_freelist==0 ){
fprintf(stderr,
"Unable to allocate memory for a new follow-set propagation link.\n");
exit(1);
}
@@ -3190,13 +3267,11 @@
/*********************** From the file "report.c" **************************/
/*
** Procedures for generating reports and tables in the LEMON parser generator.
*/
-/* Generate a filename with the given suffix. Space to hold the
-** name comes from malloc() and must be freed by the calling
-** function.
+/* Generate a filename with the given suffix.
*/
PRIVATE char *file_makename(struct lemon *lemp, const char *suffix)
{
char *name;
char *cp;
@@ -3209,11 +3284,11 @@
}
sz = lemonStrlen(filename);
sz += lemonStrlen(suffix);
if( outputDir ) sz += lemonStrlen(outputDir) + 1;
sz += 5;
- name = (char*)malloc( sz );
+ name = (char*)lemon_malloc( sz );
if( name==0 ){
fprintf(stderr,"Can't allocate space for a filename.\n");
exit(1);
}
name[0] = 0;
@@ -3236,11 +3311,11 @@
const char *suffix,
const char *mode
){
FILE *fp;
- if( lemp->outname ) free(lemp->outname);
+ if( lemp->outname ) lemon_free(lemp->outname);
lemp->outname = file_makename(lemp, suffix);
fp = fopen(lemp->outname,mode);
if( fp==0 && *mode=='w' ){
fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname);
lemp->errorcnt++;
@@ -3552,18 +3627,18 @@
cp = strrchr(argv0,'/');
#endif
if( cp ){
c = *cp;
*cp = 0;
- path = (char *)malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 );
+ path = (char *)lemon_malloc( lemonStrlen(argv0) + lemonStrlen(name) + 2 );
if( path ) lemon_sprintf(path,"%s/%s",argv0,name);
*cp = c;
}else{
pathlist = getenv("PATH");
if( pathlist==0 ) pathlist = ".:/bin:/usr/bin";
- pathbuf = (char *) malloc( lemonStrlen(pathlist) + 1 );
- path = (char *)malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 );
+ pathbuf = (char *) lemon_malloc( lemonStrlen(pathlist) + 1 );
+ path = (char *)lemon_malloc( lemonStrlen(pathlist)+lemonStrlen(name)+2 );
if( (pathbuf != 0) && (path!=0) ){
pathbufptr = pathbuf;
lemon_strcpy(pathbuf, pathlist);
while( *pathbuf ){
cp = strchr(pathbuf,':');
@@ -3575,11 +3650,11 @@
if( c==0 ) pathbuf[0] = 0;
else pathbuf = &cp[1];
if( access(path,modemask)==0 ) break;
}
}
- free(pathbufptr);
+ lemon_free(pathbufptr);
}
return path;
}
/* Given an action, compute the integer value for that action
@@ -3706,11 +3781,11 @@
in = fopen(tpltname,"rb");
if( in==0 ){
fprintf(stderr,"Can't open the template file \"%s\".\n",tpltname);
lemp->errorcnt++;
}
- free(toFree);
+ lemon_free(toFree);
return in;
}
/* Print a #line directive line to the output file. */
PRIVATE void tplt_linedir(FILE *out, int lineno, char *filename)
@@ -3835,11 +3910,11 @@
}
n = lemonStrlen(zText);
}
if( (int) (n+sizeof(zInt)*2+used) >= alloced ){
alloced = n + sizeof(zInt)*2 + used + 200;
- z = (char *) realloc(z, alloced);
+ z = (char *) lemon_realloc(z, alloced);
}
if( z==0 ) return empty;
while( n-- > 0 ){
c = *(zText++);
if( c=='%' && n>0 && zText[0]=='d' ){
@@ -4125,11 +4200,11 @@
unsigned hash; /* For hashing the name of a type */
const char *name; /* Name of the parser */
/* Allocate and initialize types[] and allocate stddt[] */
arraysize = lemp->nsymbol * 2;
- types = (char**)calloc( arraysize, sizeof(char*) );
+ types = (char**)lemon_calloc( arraysize, sizeof(char*) );
if( types==0 ){
fprintf(stderr,"Out of memory.\n");
exit(1);
}
for(i=0; isymbols[i];
if( sp->datatype==0 ) continue;
len = lemonStrlen(sp->datatype);
if( len>maxdtlength ) maxdtlength = len;
}
- stddt = (char*)malloc( maxdtlength*2 + 1 );
+ stddt = (char*)lemon_malloc( maxdtlength*2 + 1 );
if( stddt==0 ){
fprintf(stderr,"Out of memory.\n");
exit(1);
}
@@ -4191,11 +4266,11 @@
hash++;
if( hash>=(unsigned)arraysize ) hash = 0;
}
if( types[hash]==0 ){
sp->dtnum = hash + 1;
- types[hash] = (char*)malloc( lemonStrlen(stddt)+1 );
+ types[hash] = (char*)lemon_malloc( lemonStrlen(stddt)+1 );
if( types[hash]==0 ){
fprintf(stderr,"Out of memory.\n");
exit(1);
}
lemon_strcpy(types[hash],stddt);
@@ -4213,17 +4288,17 @@
fprintf(out," int yyinit;\n"); lineno++;
fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++;
for(i=0; ierrsym && lemp->errsym->useCnt ){
fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++;
}
- free(stddt);
- free(types);
+ lemon_free(stddt);
+ lemon_free(types);
fprintf(out,"} YYMINORTYPE;\n"); lineno++;
*plineno = lineno;
}
/*
@@ -4448,11 +4523,11 @@
/* Generate the include code, if any */
tplt_print(out,lemp,lemp->include,&lineno);
if( mhflag ){
char *incName = file_makename(lemp, ".h");
fprintf(out,"#include \"%s\"\n", incName); lineno++;
- free(incName);
+ lemon_free(incName);
}
tplt_xfer(lemp->name,in,out,&lineno);
/* Generate #defines for all tokens */
if( lemp->tokenprefix ) prefix = lemp->tokenprefix;
@@ -4555,11 +4630,11 @@
/* Compute the action table, but do not output it yet. The action
** table must be computed before generating the YYNSTATE macro because
** we need to know how many states can be eliminated.
*/
- ax = (struct axset *) calloc(lemp->nxstate*2, sizeof(ax[0]));
+ ax = (struct axset *) lemon_calloc(lemp->nxstate*2, sizeof(ax[0]));
if( ax==0 ){
fprintf(stderr,"malloc failed\n");
exit(1);
}
for(i=0; inxstate; i++){
@@ -4613,11 +4688,11 @@
i, stp->statenum, ax[i].isTkn ? "Token" : "Var ",
ax[i].nAction, pActtab->nAction, nn);
}
#endif
}
- free(ax);
+ lemon_free(ax);
/* Mark rules that are actually used for reduce actions after all
** optimizations have been applied
*/
for(rp=lemp->rule; rp; rp=rp->next) rp->doesReduce = LEMON_FALSE;
@@ -5239,21 +5314,21 @@
}
/* Allocate a new set */
char *SetNew(void){
char *s;
- s = (char*)calloc( size, 1);
+ s = (char*)lemon_calloc( size, 1);
if( s==0 ){
memory_error();
}
return s;
}
/* Deallocate a set */
void SetFree(char *s)
{
- free(s);
+ lemon_free(s);
}
/* Add a new element to the set. Return TRUE if the element was added
** and FALSE if it was already there. */
int SetAdd(char *s, int e)
@@ -5308,11 +5383,11 @@
const char *z;
char *cpy;
if( y==0 ) return 0;
z = Strsafe_find(y);
- if( z==0 && (cpy=(char *)malloc( lemonStrlen(y)+1 ))!=0 ){
+ if( z==0 && (cpy=(char *)lemon_malloc( lemonStrlen(y)+1 ))!=0 ){
lemon_strcpy(cpy,y);
z = cpy;
Strsafe_insert(z);
}
MemoryCheck(z);
@@ -5344,17 +5419,17 @@
static struct s_x1 *x1a;
/* Allocate a new associative array */
void Strsafe_init(void){
if( x1a ) return;
- x1a = (struct s_x1*)malloc( sizeof(struct s_x1) );
+ x1a = (struct s_x1*)lemon_malloc( sizeof(struct s_x1) );
if( x1a ){
x1a->size = 1024;
x1a->count = 0;
- x1a->tbl = (x1node*)calloc(1024, sizeof(x1node) + sizeof(x1node*));
+ x1a->tbl = (x1node*)lemon_calloc(1024, sizeof(x1node) + sizeof(x1node*));
if( x1a->tbl==0 ){
- free(x1a);
+ lemon_free(x1a);
x1a = 0;
}else{
int i;
x1a->ht = (x1node**)&(x1a->tbl[1024]);
for(i=0; i<1024; i++) x1a->ht[i] = 0;
@@ -5385,11 +5460,11 @@
/* Need to make the hash table bigger */
int i,arrSize;
struct s_x1 array;
array.size = arrSize = x1a->size*2;
array.count = x1a->count;
- array.tbl = (x1node*)calloc(arrSize, sizeof(x1node) + sizeof(x1node*));
+ array.tbl = (x1node*)lemon_calloc(arrSize, sizeof(x1node)+sizeof(x1node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x1node**)&(array.tbl[arrSize]);
for(i=0; icount; i++){
x1node *oldnp, *newnp;
@@ -5400,11 +5475,11 @@
newnp->next = array.ht[h];
newnp->data = oldnp->data;
newnp->from = &(array.ht[h]);
array.ht[h] = newnp;
}
- /* free(x1a->tbl); // This program was originally for 16-bit machines.
+ /* lemon_free(x1a->tbl); // This program was originally for 16-bit machines.
** Don't worry about freeing memory on modern platforms. */
*x1a = array;
}
/* Insert the new data */
h = ph & (x1a->size-1);
@@ -5441,11 +5516,11 @@
{
struct symbol *sp;
sp = Symbol_find(x);
if( sp==0 ){
- sp = (struct symbol *)calloc(1, sizeof(struct symbol) );
+ sp = (struct symbol *)lemon_calloc(1, sizeof(struct symbol) );
MemoryCheck(sp);
sp->name = Strsafe(x);
sp->type = ISUPPER(*x) ? TERMINAL : NONTERMINAL;
sp->rule = 0;
sp->fallback = 0;
@@ -5512,17 +5587,17 @@
static struct s_x2 *x2a;
/* Allocate a new associative array */
void Symbol_init(void){
if( x2a ) return;
- x2a = (struct s_x2*)malloc( sizeof(struct s_x2) );
+ x2a = (struct s_x2*)lemon_malloc( sizeof(struct s_x2) );
if( x2a ){
x2a->size = 128;
x2a->count = 0;
- x2a->tbl = (x2node*)calloc(128, sizeof(x2node) + sizeof(x2node*));
+ x2a->tbl = (x2node*)lemon_calloc(128, sizeof(x2node) + sizeof(x2node*));
if( x2a->tbl==0 ){
- free(x2a);
+ lemon_free(x2a);
x2a = 0;
}else{
int i;
x2a->ht = (x2node**)&(x2a->tbl[128]);
for(i=0; i<128; i++) x2a->ht[i] = 0;
@@ -5553,11 +5628,11 @@
/* Need to make the hash table bigger */
int i,arrSize;
struct s_x2 array;
array.size = arrSize = x2a->size*2;
array.count = x2a->count;
- array.tbl = (x2node*)calloc(arrSize, sizeof(x2node) + sizeof(x2node*));
+ array.tbl = (x2node*)lemon_calloc(arrSize, sizeof(x2node)+sizeof(x2node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x2node**)&(array.tbl[arrSize]);
for(i=0; icount; i++){
x2node *oldnp, *newnp;
@@ -5569,11 +5644,11 @@
newnp->key = oldnp->key;
newnp->data = oldnp->data;
newnp->from = &(array.ht[h]);
array.ht[h] = newnp;
}
- /* free(x2a->tbl); // This program was originally written for 16-bit
+ /* lemon_free(x2a->tbl); // This program was originally written for 16-bit
** machines. Don't worry about freeing this trivial amount of memory
** on modern platforms. Just leak it. */
*x2a = array;
}
/* Insert the new data */
@@ -5630,11 +5705,11 @@
{
struct symbol **array;
int i,arrSize;
if( x2a==0 ) return 0;
arrSize = x2a->count;
- array = (struct symbol **)calloc(arrSize, sizeof(struct symbol *));
+ array = (struct symbol **)lemon_calloc(arrSize, sizeof(struct symbol *));
if( array ){
for(i=0; itbl[i].data;
}
return array;
}
@@ -5678,11 +5753,11 @@
/* Allocate a new state structure */
struct state *State_new()
{
struct state *newstate;
- newstate = (struct state *)calloc(1, sizeof(struct state) );
+ newstate = (struct state *)lemon_calloc(1, sizeof(struct state) );
MemoryCheck(newstate);
return newstate;
}
/* There is one instance of the following structure for each
@@ -5711,17 +5786,17 @@
static struct s_x3 *x3a;
/* Allocate a new associative array */
void State_init(void){
if( x3a ) return;
- x3a = (struct s_x3*)malloc( sizeof(struct s_x3) );
+ x3a = (struct s_x3*)lemon_malloc( sizeof(struct s_x3) );
if( x3a ){
x3a->size = 128;
x3a->count = 0;
- x3a->tbl = (x3node*)calloc(128, sizeof(x3node) + sizeof(x3node*));
+ x3a->tbl = (x3node*)lemon_calloc(128, sizeof(x3node) + sizeof(x3node*));
if( x3a->tbl==0 ){
- free(x3a);
+ lemon_free(x3a);
x3a = 0;
}else{
int i;
x3a->ht = (x3node**)&(x3a->tbl[128]);
for(i=0; i<128; i++) x3a->ht[i] = 0;
@@ -5752,11 +5827,11 @@
/* Need to make the hash table bigger */
int i,arrSize;
struct s_x3 array;
array.size = arrSize = x3a->size*2;
array.count = x3a->count;
- array.tbl = (x3node*)calloc(arrSize, sizeof(x3node) + sizeof(x3node*));
+ array.tbl = (x3node*)lemon_calloc(arrSize, sizeof(x3node)+sizeof(x3node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x3node**)&(array.tbl[arrSize]);
for(i=0; icount; i++){
x3node *oldnp, *newnp;
@@ -5768,11 +5843,11 @@
newnp->key = oldnp->key;
newnp->data = oldnp->data;
newnp->from = &(array.ht[h]);
array.ht[h] = newnp;
}
- free(x3a->tbl);
+ lemon_free(x3a->tbl);
*x3a = array;
}
/* Insert the new data */
h = ph & (x3a->size-1);
np = &(x3a->tbl[x3a->count++]);
@@ -5809,11 +5884,11 @@
{
struct state **array;
int i,arrSize;
if( x3a==0 ) return 0;
arrSize = x3a->count;
- array = (struct state **)calloc(arrSize, sizeof(struct state *));
+ array = (struct state **)lemon_calloc(arrSize, sizeof(struct state *));
if( array ){
for(i=0; itbl[i].data;
}
return array;
}
@@ -5851,17 +5926,17 @@
static struct s_x4 *x4a;
/* Allocate a new associative array */
void Configtable_init(void){
if( x4a ) return;
- x4a = (struct s_x4*)malloc( sizeof(struct s_x4) );
+ x4a = (struct s_x4*)lemon_malloc( sizeof(struct s_x4) );
if( x4a ){
x4a->size = 64;
x4a->count = 0;
- x4a->tbl = (x4node*)calloc(64, sizeof(x4node) + sizeof(x4node*));
+ x4a->tbl = (x4node*)lemon_calloc(64, sizeof(x4node) + sizeof(x4node*));
if( x4a->tbl==0 ){
- free(x4a);
+ lemon_free(x4a);
x4a = 0;
}else{
int i;
x4a->ht = (x4node**)&(x4a->tbl[64]);
for(i=0; i<64; i++) x4a->ht[i] = 0;
@@ -5892,11 +5967,12 @@
/* Need to make the hash table bigger */
int i,arrSize;
struct s_x4 array;
array.size = arrSize = x4a->size*2;
array.count = x4a->count;
- array.tbl = (x4node*)calloc(arrSize, sizeof(x4node) + sizeof(x4node*));
+ array.tbl = (x4node*)lemon_calloc(arrSize,
+ sizeof(x4node) + sizeof(x4node*));
if( array.tbl==0 ) return 0; /* Fail due to malloc failure */
array.ht = (x4node**)&(array.tbl[arrSize]);
for(i=0; icount; i++){
x4node *oldnp, *newnp;
@@ -5907,13 +5983,10 @@
newnp->next = array.ht[h];
newnp->data = oldnp->data;
newnp->from = &(array.ht[h]);
array.ht[h] = newnp;
}
- /* free(x4a->tbl); // This code was originall written for 16-bit machines.
- ** on modern machines, don't worry about freeing this trival amount of
- ** memory. */
*x4a = array;
}
/* Insert the new data */
h = ph & (x4a->size-1);
np = &(x4a->tbl[x4a->count++]);