aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorstephan <stephan@noemail.net>2024-02-27 00:58:51 +0000
committerstephan <stephan@noemail.net>2024-02-27 00:58:51 +0000
commit32888c2b30cefad8b1bfec91b2b7dba2e2b1513a (patch)
tree0350a76297a861080ef1e99c9cac523a1352b567 /src
parent45cf8ad02a30f76212f7b1c00fa45b00e8626eb6 (diff)
parentf79b0bdcbfb46164cfd665d256f2862bf3f42a7c (diff)
downloadsqlite-32888c2b30cefad8b1bfec91b2b7dba2e2b1513a.tar.gz
sqlite-32888c2b30cefad8b1bfec91b2b7dba2e2b1513a.zip
Bring the extra-src branch up to date with the trunk.
FossilOrigin-Name: 12ff5c5c4162951a29b638a5bc6cffa50e057c5a5e8f5e9c627af5f4ab1e4cdb
Diffstat (limited to 'src')
-rw-r--r--src/alter.c7
-rw-r--r--src/analyze.c100
-rw-r--r--src/btree.c151
-rw-r--r--src/btreeInt.h4
-rw-r--r--src/build.c61
-rw-r--r--src/ctime.c3
-rw-r--r--src/date.c138
-rw-r--r--src/dbpage.c3
-rw-r--r--src/dbstat.c3
-rw-r--r--src/expr.c377
-rw-r--r--src/fkey.c10
-rw-r--r--src/func.c16
-rw-r--r--src/global.c3
-rw-r--r--src/json.c5456
-rw-r--r--src/loadext.c16
-rw-r--r--src/main.c167
-rw-r--r--src/malloc.c22
-rw-r--r--src/memdb.c8
-rw-r--r--src/mutex.c2
-rw-r--r--src/mutex_unix.c2
-rw-r--r--src/mutex_w32.c2
-rw-r--r--src/notify.c3
-rw-r--r--src/os_unix.c346
-rw-r--r--src/os_win.c7
-rw-r--r--src/pager.c88
-rw-r--r--src/pager.h2
-rw-r--r--src/parse.y23
-rw-r--r--src/pragma.c231
-rw-r--r--src/prepare.c3
-rw-r--r--src/printf.c8
-rw-r--r--src/resolve.c141
-rw-r--r--src/select.c311
-rw-r--r--src/shell.c.in2326
-rw-r--r--src/sqlite.h.in211
-rw-r--r--src/sqlite3ext.h6
-rw-r--r--src/sqliteInt.h120
-rw-r--r--src/sqliteLimit.h2
-rw-r--r--src/status.c4
-rw-r--r--src/test1.c72
-rw-r--r--src/test8.c11
-rw-r--r--src/test_bestindex.c5
-rw-r--r--src/test_fs.c15
-rw-r--r--src/test_func.c3
-rw-r--r--src/test_intarray.c5
-rw-r--r--src/test_osinst.c7
-rw-r--r--src/test_schema.c5
-rw-r--r--src/test_tclsh.c2
-rw-r--r--src/test_tclvar.c5
-rw-r--r--src/treeview.c21
-rw-r--r--src/trigger.c15
-rw-r--r--src/utf.c33
-rw-r--r--src/util.c143
-rw-r--r--src/vdbe.c209
-rw-r--r--src/vdbe.h1
-rw-r--r--src/vdbeapi.c201
-rw-r--r--src/vdbeaux.c79
-rw-r--r--src/vdbeblob.c5
-rw-r--r--src/vdbemem.c51
-rw-r--r--src/vdbesort.c8
-rw-r--r--src/vdbevtab.c3
-rw-r--r--src/vtab.c7
-rw-r--r--src/wal.c136
-rw-r--r--src/where.c319
-rw-r--r--src/whereInt.h4
-rw-r--r--src/window.c5
65 files changed, 7743 insertions, 4010 deletions
diff --git a/src/alter.c b/src/alter.c
index d0b1f7f69..ec45e1433 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -446,14 +446,19 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
/* Verify that constraints are still satisfied */
if( pNew->pCheck!=0
|| (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0)
+ || (pTab->tabFlags & TF_Strict)!=0
){
sqlite3NestedParse(pParse,
"SELECT CASE WHEN quick_check GLOB 'CHECK*'"
" THEN raise(ABORT,'CHECK constraint failed')"
+ " WHEN quick_check GLOB 'non-* value in*'"
+ " THEN raise(ABORT,'type mismatch on DEFAULT')"
" ELSE raise(ABORT,'NOT NULL constraint failed')"
" END"
" FROM pragma_quick_check(%Q,%Q)"
- " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'",
+ " WHERE quick_check GLOB 'CHECK*'"
+ " OR quick_check GLOB 'NULL*'"
+ " OR quick_check GLOB 'non-* value in*'",
zTab, zDb
);
}
diff --git a/src/analyze.c b/src/analyze.c
index a7a8b6d66..e7c1068ae 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -264,9 +264,9 @@ static void openStatTable(
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
- tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -424,9 +424,9 @@ static void statInit(
/* Allocate the space required for the StatAccum object */
n = sizeof(*p)
- + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
- + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
+ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
@@ -447,9 +447,9 @@ static void statInit(
p->nKeyCol = nKeyCol;
p->nSkipAhead = 0;
p->current.anDLt = (tRowcnt*)&p[1];
- p->current.anEq = &p->current.anDLt[nColUp];
#ifdef SQLITE_ENABLE_STAT4
+ p->current.anEq = &p->current.anDLt[nColUp];
p->mxSample = p->nLimit==0 ? mxSample : 0;
if( mxSample ){
u8 *pSpace; /* Allocated space not yet assigned */
@@ -716,7 +716,9 @@ static void statPush(
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+#endif
}else{
/* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
@@ -725,15 +727,17 @@ static void statPush(
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
+#endif
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
-#endif
p->current.anEq[i] = 1;
+#endif
}
}
@@ -867,7 +871,9 @@ static void statGet(
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
- assert( p->current.anEq[i] );
+#ifdef SQLITE_ENABLE_STAT4
+ assert( p->current.anEq[i] || p->nRow==0 );
+#endif
}
sqlite3ResultStrAccum(context, &sStat);
}
@@ -1051,7 +1057,7 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol; /* Number of columns in pIdx. "N" */
- int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */
int addrNextRow; /* Address of "next_row:" */
const char *zIdxName; /* Name of the index */
int nColTest; /* Number of columns to test for changes */
@@ -1075,9 +1081,14 @@ static void analyzeOneTable(
/*
** Pseudo-code for loop that calls stat_push():
**
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
** goto chng_addr_0;
**
** next_row:
@@ -1116,41 +1127,36 @@ static void analyzeOneTable(
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
- /* Invoke the stat_init() function. The arguments are:
- **
+ /* Implementation of the following:
+ **
+ ** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
+ ** goto chng_addr_0;
+ */
+ assert( regTemp2==regStat+4 );
+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+
+ /* Arguments to stat_init():
** (1) the number of columns in the index including the rowid
** (or for a WITHOUT ROWID table, the number of PK columns),
** (2) the number of columns in the key without the rowid/pk
- ** (3) estimated number of rows in the index,
- */
+ ** (3) estimated number of rows in the index. */
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
assert( regRowid==regStat+2 );
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
-#ifdef SQLITE_ENABLE_STAT4
- if( OptimizationEnabled(db, SQLITE_Stat4) ){
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- }else
-#endif
- {
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
- }
- assert( regTemp2==regStat+4 );
- sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp,
+ OptimizationDisabled(db, SQLITE_Stat4));
sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
&statInitFuncdef, 0);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
- /* Implementation of the following:
- **
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
- ** regChng = 0
- ** goto next_push_0;
- **
- */
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
addrNextRow = sqlite3VdbeCurrentAddr(v);
@@ -1257,6 +1263,12 @@ static void analyzeOneTable(
}
/* Add the entry to the stat1 table. */
+ if( pIdx->pPartIdxWhere ){
+ /* Partial indexes might get a zero-entry in sqlite_stat1. But
+ ** an empty table is omitted from sqlite_stat1. */
+ sqlite3VdbeJumpHere(v, addrGotoEnd);
+ addrGotoEnd = 0;
+ }
callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
assert( "BBB"[0]==SQLITE_AFF_TEXT );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
@@ -1280,6 +1292,12 @@ static void analyzeOneTable(
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+ /* No STAT4 data is generated if the number of rows is zero */
+ if( addrGotoEnd==0 ){
+ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
+ }
+
if( doOnce ){
int mxCol = nCol;
Index *pX;
@@ -1332,7 +1350,7 @@ static void analyzeOneTable(
#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
- sqlite3VdbeJumpHere(v, addrRewind);
+ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd);
}
@@ -1556,6 +1574,16 @@ static void decodeIntArray(
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
+
+ /* Set the bLowQual flag if the peak number of rows obtained
+ ** from a full equality match is so large that a full table scan
+ ** seems likely to be faster than using the index.
+ */
+ if( aLog[0] > 66 /* Index has more than 100 rows */
+ && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
+ ){
+ pIndex->bLowQual = 1;
+ }
}
}
diff --git a/src/btree.c b/src/btree.c
index c23f86e1d..3e08dc6a1 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -1,4 +1,3 @@
-
/*
** 2004 April 6
**
@@ -152,8 +151,47 @@ int corruptPageError(int lineno, MemPage *p){
# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
#endif
+/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled
+** or if the lock tracking is disabled. This is always the value for
+** release builds.
+*/
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/
+
#ifndef SQLITE_OMIT_SHARED_CACHE
+#if 0
+/* ^---- Change to 1 and recompile to enable shared-lock tracing
+** for debugging purposes.
+**
+** Print all shared-cache locks on a BtShared. Debugging use only.
+*/
+static void sharedLockTrace(
+ BtShared *pBt,
+ const char *zMsg,
+ int iRoot,
+ int eLockType
+){
+ BtLock *pLock;
+ if( iRoot>0 ){
+ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W");
+ }else{
+ printf("%s-%p:", zMsg, pBt);
+ }
+ for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
+ printf(" %p/%u%s", pLock->pBtree, pLock->iTable,
+ pLock->eLock==READ_LOCK ? "R" : "W");
+ while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){
+ pLock = pLock->pNext;
+ printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W");
+ }
+ }
+ printf("\n");
+ fflush(stdout);
+}
+#undef SHARED_LOCK_TRACE
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE)
+#endif /* Shared-lock tracing */
+
#ifdef SQLITE_DEBUG
/*
**** This function is only used as part of an assert() statement. ***
@@ -230,6 +268,8 @@ static int hasSharedCacheTableLock(
iTab = iRoot;
}
+ SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType);
+
/* Search for the required lock. Either a write-lock on root-page iTab, a
** write-lock on the schema table, or (if the client is reading) a
** read-lock on iTab will suffice. Return 1 if any of these are found. */
@@ -363,6 +403,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock);
+
assert( sqlite3BtreeHoldsMutex(p) );
assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
assert( p->db!=0 );
@@ -430,6 +472,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){
assert( p->sharable || 0==*ppIter );
assert( p->inTrans>0 );
+ SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0);
+
while( *ppIter ){
BtLock *pLock = *ppIter;
assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
@@ -468,6 +512,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){
*/
static void downgradeAllSharedCacheTableLocks(Btree *p){
BtShared *pBt = p->pBt;
+
+ SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0);
+
if( pBt->pWriter==p ){
BtLock *pLock;
pBt->pWriter = 0;
@@ -5081,9 +5128,12 @@ static int accessPayload(
if( pCur->aOverflow==0
|| nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
){
- Pgno *aNew = (Pgno*)sqlite3Realloc(
- pCur->aOverflow, nOvfl*2*sizeof(Pgno)
- );
+ Pgno *aNew;
+ if( sqlite3FaultSim(413) ){
+ aNew = 0;
+ }else{
+ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno));
+ }
if( aNew==0 ){
return SQLITE_NOMEM_BKPT;
}else{
@@ -5162,7 +5212,6 @@ static int accessPayload(
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
- if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
@@ -6133,10 +6182,10 @@ i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- /* Currently this interface is only called by the OP_IfSmaller
- ** opcode, and it that case the cursor will always be valid and
- ** will always point to a leaf node. */
- if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
+ /* Currently this interface is only called by the OP_IfSizeBetween
+ ** opcode and the OP_Count opcode with P3=1. In either case,
+ ** the cursor will always be valid unless the btree is empty. */
+ if( pCur->eState!=CURSOR_VALID ) return 0;
if( NEVER(pCur->pPage->leaf==0) ) return -1;
n = pCur->pPage->nCell;
@@ -6282,7 +6331,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
}
pPage = pCur->pPage;
- assert( pPage->isInit );
+ if( sqlite3FaultSim(412) ) pPage->isInit = 0;
+ if( !pPage->isInit ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( !pPage->leaf ){
int idx = pCur->ix;
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
@@ -6955,7 +7007,10 @@ static int fillInCell(
n = nHeader + nPayload;
testcase( n==3 );
testcase( n==4 );
- if( n<4 ) n = 4;
+ if( n<4 ){
+ n = 4;
+ pPayload[nPayload] = 0;
+ }
*pnSize = n;
assert( nSrc<=nPayload );
testcase( nSrc<nPayload );
@@ -7493,9 +7548,10 @@ static int rebuildPage(
int k; /* Current slot in pCArray->apEnd[] */
u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */
+ assert( nCell>0 );
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
- if( NEVER(j>(u32)usableSize) ){ j = 0; }
+ if( j>(u32)usableSize ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; ALWAYS(k<NB*2) && pCArray->ixNx[k]<=i; k++){}
@@ -7799,6 +7855,7 @@ static int editPage(
return SQLITE_OK;
editpage_fail:
/* Unable to edit this page. Rebuild it from scratch instead. */
+ if( nNew<1 ) return SQLITE_CORRUPT_BKPT;
populateCellCache(pCArray, iNew, nNew);
return rebuildPage(pCArray, iNew, nNew, pPg);
}
@@ -8259,7 +8316,7 @@ static int balance_nonroot(
** table-interior, index-leaf, or index-interior).
*/
if( pOld->aData[0]!=apOld[0]->aData[0] ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
@@ -8283,7 +8340,7 @@ static int balance_nonroot(
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
if( NEVER(limit<pOld->aiOvfl[0]) ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
limit = pOld->aiOvfl[0];
@@ -8926,7 +8983,7 @@ static int anotherValidCursor(BtCursor *pCur){
&& pOther->eState==CURSOR_VALID
&& pOther->pPage==pCur->pPage
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pCur->pPage);
}
}
return SQLITE_OK;
@@ -8986,7 +9043,7 @@ static int balance(BtCursor *pCur){
/* The page being written is not a root page, and there is currently
** more than one reference to it. This only happens if the page is one
** of its own ancestor pages. Corruption. */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -9150,7 +9207,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
ovflPgno = get4byte(pPage->aData);
@@ -9178,7 +9235,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCur->info.nLocal==nTotal ){
/* The entire cell is local */
@@ -9259,7 +9316,7 @@ int sqlite3BtreeInsert(
** Which can only happen if the SQLITE_NoSchemaError flag was set when
** the schema was loaded. This cannot be asserted though, as a user might
** set the flag, load the schema, and then unset the flag. */
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
@@ -9382,7 +9439,7 @@ int sqlite3BtreeInsert(
if( pPage->nFree<0 ){
if( NEVER(pCur->eState>CURSOR_INVALID) ){
/* ^^^^^--- due to the moveToRoot() call above */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
rc = btreeComputeFreeSpace(pPage);
}
@@ -9399,7 +9456,10 @@ int sqlite3BtreeInsert(
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
szNew = p->pBt->nPreformatSize;
- if( szNew<4 ) szNew = 4;
+ if( szNew<4 ){
+ szNew = 4;
+ newCell[3] = 0;
+ }
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
@@ -9421,7 +9481,7 @@ int sqlite3BtreeInsert(
CellInfo info;
assert( idx>=0 );
if( idx>=pPage->nCell ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ){
@@ -9448,10 +9508,10 @@ int sqlite3BtreeInsert(
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( oldCell+szNew > pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
@@ -9553,7 +9613,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
nIn = pSrc->info.nLocal;
aIn = pSrc->info.pPayload;
if( aIn+nIn>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
nRem = pSrc->info.nPayload;
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
@@ -9578,7 +9638,7 @@ int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
if( nRem>nIn ){
if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
}
@@ -9674,7 +9734,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID );
if( rc || pCur->eState!=CURSOR_VALID ) return rc;
}else{
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
assert( pCur->eState==CURSOR_VALID );
@@ -9683,14 +9743,14 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
iCellIdx = pCur->ix;
pPage = pCur->pPage;
if( pPage->nCell<=iCellIdx ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
pCell = findCell(pPage, iCellIdx);
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCell<&pPage->aCellIdx[pPage->nCell] ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
@@ -9781,7 +9841,7 @@ int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
n = pCur->pPage->pgno;
}
pCell = findCell(pLeaf, pLeaf->nCell-1);
- if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf);
nCell = pLeaf->xCellSize(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
pTmp = pBt->pTmpSpace;
@@ -9897,7 +9957,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
*/
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
if( pgnoRoot>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgnoRoot);
}
pgnoRoot++;
@@ -9945,7 +10005,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
}
rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage);
if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PGNO(pgnoRoot);
}
if( rc!=SQLITE_OK ){
releasePage(pRoot);
@@ -10035,14 +10095,14 @@ static int clearDatabasePage(
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgno);
}
rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
if( (pBt->openFlags & BTREE_SINGLE)==0
&& sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
goto cleardatabasepage_out;
}
hdr = pPage->hdrOffset;
@@ -10146,7 +10206,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
assert( p->inTrans==TRANS_WRITE );
assert( iTable>=2 );
if( iTable>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(iTable);
}
rc = sqlite3BtreeClearTable(p, iTable, 0);
@@ -10458,7 +10518,8 @@ static void checkAppendMsg(
** corresponds to page iPg is already set.
*/
static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
}
@@ -10466,7 +10527,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
*/
static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
}
@@ -10480,7 +10542,7 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
- if( iPage>pCheck->nPage || iPage==0 ){
+ if( iPage>pCheck->nCkPage || iPage==0 ){
checkAppendMsg(pCheck, "invalid page number %u", iPage);
return 1;
}
@@ -10707,6 +10769,7 @@ static int checkTreePage(
if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
+ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM;
goto end_of_check;
}
@@ -10977,15 +11040,15 @@ int sqlite3BtreeIntegrityCheck(
sCheck.db = db;
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = btreePagecount(sCheck.pBt);
+ sCheck.nCkPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
- if( sCheck.nPage==0 ){
+ if( sCheck.nCkPage==0 ){
goto integrity_ck_cleanup;
}
- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1);
if( !sCheck.aPgRef ){
checkOom(&sCheck);
goto integrity_ck_cleanup;
@@ -10997,7 +11060,7 @@ int sqlite3BtreeIntegrityCheck(
}
i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
+ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
@@ -11048,7 +11111,7 @@ int sqlite3BtreeIntegrityCheck(
/* Make sure every page in the file is referenced
*/
if( !bPartial ){
- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
+ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, "Page %u: never used", i);
diff --git a/src/btreeInt.h b/src/btreeInt.h
index 26a0bc686..67a7db25c 100644
--- a/src/btreeInt.h
+++ b/src/btreeInt.h
@@ -64,7 +64,7 @@
** 22 1 Min embedded payload fraction (must be 32)
** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
-** 28 4 Reserved for future use
+** 28 4 The size of the database in pages
** 32 4 First freelist page
** 36 4 Number of freelist pages in the file
** 40 60 15 4-byte meta values passed to higher layers
@@ -695,7 +695,7 @@ struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
u8 *aPgRef; /* 1 bit per page in the db (see above) */
- Pgno nPage; /* Number of pages in the database */
+ Pgno nCkPage; /* Pages in the database. 0 for partial check */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */
diff --git a/src/build.c b/src/build.c
index fe3a781d6..15f8fe1d2 100644
--- a/src/build.c
+++ b/src/build.c
@@ -189,7 +189,7 @@ void sqlite3FinishCoding(Parse *pParse){
}
sqlite3VdbeAddOp0(v, OP_Halt);
-#if SQLITE_USER_AUTHENTICATION
+#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
if( pParse->nTableLock>0 && db->init.busy==0 ){
sqlite3UserAuthInit(db);
if( db->auth.authLevel<UAUTH_User ){
@@ -246,19 +246,14 @@ void sqlite3FinishCoding(Parse *pParse){
*/
if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse);
- /* Code constant expressions that where factored out of inner loops.
- **
- ** The pConstExpr list might also contain expressions that we simply
- ** want to keep around until the Parse object is deleted. Such
- ** expressions have iConstExprReg==0. Do not generate code for
- ** those expressions, of course.
+ /* Code constant expressions that were factored out of inner loops.
*/
if( pParse->pConstExpr ){
ExprList *pEL = pParse->pConstExpr;
pParse->okConstFactor = 0;
for(i=0; i<pEL->nExpr; i++){
- int iReg = pEL->a[i].u.iConstExprReg;
- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
+ assert( pEL->a[i].u.iConstExprReg>0 );
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
}
}
@@ -725,7 +720,7 @@ void sqlite3ColumnSetExpr(
*/
Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
if( pCol->iDflt==0 ) return 0;
- if( NEVER(!IsOrdinaryTable(pTab)) ) return 0;
+ if( !IsOrdinaryTable(pTab) ) return 0;
if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
@@ -878,6 +873,9 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
+void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
+ sqlite3DeleteTable(db, (Table*)pTable);
+}
/*
@@ -1413,19 +1411,13 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
#endif
/*
-** Name of the special TEMP trigger used to implement RETURNING. The
-** name begins with "sqlite_" so that it is guaranteed not to collide
-** with any application-generated triggers.
-*/
-#define RETURNING_TRIGGER_NAME "sqlite_returning"
-
-/*
** Clean up the data structures associated with the RETURNING clause.
*/
-static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
+static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
+ Returning *pRet = (Returning*)pArg;
Hash *pHash;
pHash = &(db->aDb[1].pSchema->trigHash);
- sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
+ sqlite3HashInsert(pHash, pRet->zName, 0);
sqlite3ExprListDelete(db, pRet->pReturnEL);
sqlite3DbFree(db, pRet);
}
@@ -1464,11 +1456,12 @@ void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pParse->u1.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
+ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
testcase( pParse->earlyCleanup );
if( db->mallocFailed ) return;
- pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
+ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
+ "sqlite_returning_%p", pParse);
+ pRet->retTrig.zName = pRet->zName;
pRet->retTrig.op = TK_RETURNING;
pRet->retTrig.tr_tm = TRIGGER_AFTER;
pRet->retTrig.bReturning = 1;
@@ -1479,9 +1472,9 @@ void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pRet->retTStep.pTrig = &pRet->retTrig;
pRet->retTStep.pExprList = pList;
pHash = &(db->aDb[1].pSchema->trigHash);
- assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0
+ assert( sqlite3HashFind(pHash, pRet->zName)==0
|| pParse->nErr || pParse->ifNotExists );
- if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
+ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig)
==&pRet->retTrig ){
sqlite3OomFault(db);
}
@@ -1662,7 +1655,8 @@ char sqlite3AffinityType(const char *zIn, Column *pCol){
assert( zIn!=0 );
while( zIn[0] ){
- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
+ u8 x = *(u8*)zIn;
+ h = (h<<8) + sqlite3UpperToLower[x];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
aff = SQLITE_AFF_TEXT;
@@ -2925,6 +2919,17 @@ void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
+
+ /* Test for cycles in generated columns and illegal expressions
+ ** in CHECK constraints and in DEFAULT clauses. */
+ if( p->tabFlags & TF_HasGenerated ){
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0,
+ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
+ }
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0,
+ sqlite3MPrintf(db, "PRAGMA \"%w\".integrity_check(%Q)",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
}
/* Add the table to the in-memory representation of the database.
@@ -5516,7 +5521,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zDbSName;
+ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -5526,6 +5531,7 @@ void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
pIndex = sqlite3FindIndex(db, z, zDb);
sqlite3DbFree(db, z);
if( pIndex ){
+ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
return;
@@ -5691,4 +5697,7 @@ void sqlite3WithDelete(sqlite3 *db, With *pWith){
sqlite3DbFree(db, pWith);
}
}
+void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
+ sqlite3WithDelete(db, (With*)pWith);
+}
#endif /* !defined(SQLITE_OMIT_CTE) */
diff --git a/src/ctime.c b/src/ctime.c
index 03c89ff72..cf761299f 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -639,6 +639,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
"OMIT_SCHEMA_VERSION_PRAGMAS",
#endif
+#ifdef SQLITE_OMIT_SEH
+ "OMIT_SEH",
+#endif
#ifdef SQLITE_OMIT_SHARED_CACHE
"OMIT_SHARED_CACHE",
#endif
diff --git a/src/date.c b/src/date.c
index f16308544..026d83391 100644
--- a/src/date.c
+++ b/src/date.c
@@ -1043,6 +1043,12 @@ static int isDate(
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
+ if( argc==1 && p->validYMD && p->D>28 ){
+ /* Make sure a YYYY-MM-DD is normalized.
+ ** Example: 2023-02-31 -> 2023-03-03 */
+ assert( p->validJD );
+ p->validYMD = 0;
+ }
return 0;
}
@@ -1231,21 +1237,82 @@ static void dateFunc(
}
/*
+** Compute the number of days after the most recent January 1.
+**
+** In other words, compute the zero-based day number for the
+** current year:
+**
+** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ...
+** Dec31 = 364 or 365.
+*/
+static int daysAfterJan01(DateTime *pDate){
+ DateTime jan01 = *pDate;
+ assert( jan01.validYMD );
+ assert( jan01.validHMS );
+ assert( pDate->validJD );
+ jan01.validJD = 0;
+ jan01.M = 1;
+ jan01.D = 1;
+ computeJD(&jan01);
+ return (int)((pDate->iJD-jan01.iJD+43200000)/86400000);
+}
+
+/*
+** Return the number of days after the most recent Monday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday.
+*/
+static int daysAfterMonday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+43200000)/86400000) % 7;
+}
+
+/*
+** Return the number of days after the most recent Sunday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
+*/
+static int daysAfterSunday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+129600000)/86400000) % 7;
+}
+
+/*
** strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
**
** Return a string described by FORMAT. Conversions as follows:
**
-** %d day of month
+** %d day of month 01-31
+** %e day of month 1-31
** %f ** fractional seconds SS.SSS
+** %F ISO date. YYYY-MM-DD
+** %G ISO year corresponding to %V 0000-9999.
+** %g 2-digit ISO year corresponding to %V 00-99
** %H hour 00-24
-** %j day of year 000-366
+** %k hour 0-24 (leading zero converted to space)
+** %I hour 01-12
+** %j day of year 001-366
** %J ** julian day number
+** %l hour 1-12 (leading zero converted to space)
** %m month 01-12
** %M minute 00-59
+** %p "am" or "pm"
+** %P "AM" or "PM"
+** %R time as HH:MM
** %s seconds since 1970-01-01
** %S seconds 00-59
-** %w day of week 0-6 Sunday==0
-** %W week of year 00-53
+** %T time as HH:MM:SS
+** %u day of week 1-7 Monday==1, Sunday==7
+** %w day of week 0-6 Sunday==0, Monday==1
+** %U week of year 00-53 (First Sunday is start of week 01)
+** %V week of year 01-53 (First week containing Thursday is week 01)
+** %W week of year 00-53 (First Monday is start of week 01)
** %Y year 0000-9999
** %% %
*/
@@ -1282,7 +1349,7 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D);
break;
}
- case 'f': {
+ case 'f': { /* Fractional seconds. (Non-standard) */
double s = x.s;
if( s>59.999 ) s = 59.999;
sqlite3_str_appendf(&sRes, "%06.3f", s);
@@ -1292,6 +1359,21 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D);
break;
}
+ case 'G': /* Fall thru */
+ case 'g': {
+ DateTime y = x;
+ assert( y.validJD );
+ /* Move y so that it is the Thursday in the same week as x */
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ if( cf=='g' ){
+ sqlite3_str_appendf(&sRes, "%02d", y.Y%100);
+ }else{
+ sqlite3_str_appendf(&sRes, "%04d", y.Y);
+ }
+ break;
+ }
case 'H':
case 'k': {
sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h);
@@ -1305,25 +1387,11 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h);
break;
}
- case 'W': /* Fall thru */
- case 'j': {
- int nDay; /* Number of days since 1st day of year */
- DateTime y = x;
- y.validJD = 0;
- y.M = 1;
- y.D = 1;
- computeJD(&y);
- nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
- if( cf=='W' ){
- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
- wd = (int)(((x.iJD+43200000)/86400000)%7);
- sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
- }else{
- sqlite3_str_appendf(&sRes,"%03d",nDay+1);
- }
+ case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */
+ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1);
break;
}
- case 'J': {
+ case 'J': { /* Julian day number. (Non-standard) */
sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0);
break;
}
@@ -1366,13 +1434,33 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s);
break;
}
- case 'u': /* Fall thru */
- case 'w': {
- char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0';
+ case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */
+ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */
+ char c = (char)daysAfterSunday(&x) + '0';
if( c=='0' && cf=='u' ) c = '7';
sqlite3_str_appendchar(&sRes, 1, c);
break;
}
+ case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7);
+ break;
+ }
+ case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */
+ DateTime y = x;
+ /* Adjust y so that is the Thursday in the same week as x */
+ assert( y.validJD );
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1);
+ break;
+ }
+ case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7);
+ break;
+ }
case 'Y': {
sqlite3_str_appendf(&sRes,"%04d",x.Y);
break;
diff --git a/src/dbpage.c b/src/dbpage.c
index 32a9ce55b..73c31f0da 100644
--- a/src/dbpage.c
+++ b/src/dbpage.c
@@ -425,7 +425,8 @@ int sqlite3DbpageRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
diff --git a/src/dbstat.c b/src/dbstat.c
index 0a89d0524..c70d80637 100644
--- a/src/dbstat.c
+++ b/src/dbstat.c
@@ -895,7 +895,8 @@ int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
diff --git a/src/expr.c b/src/expr.c
index d96f36285..f9b280bbc 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -591,6 +591,7 @@ Expr *sqlite3ExprForVectorField(
*/
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
+ ExprSetProperty(pRet, EP_FullSize);
pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
@@ -1182,6 +1183,67 @@ Expr *sqlite3ExprFunction(
}
/*
+** Report an error when attempting to use an ORDER BY clause within
+** the arguments of a non-aggregate function.
+*/
+void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY may not be used with non-aggregate %#T()", p
+ );
+}
+
+/*
+** Attach an ORDER BY clause to a function call.
+**
+** functionname( arguments ORDER BY sortlist )
+** \_____________________/ \______/
+** pExpr pOrderBy
+**
+** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER
+** and added to the Expr.pLeft field of the parent TK_FUNCTION node.
+*/
+void sqlite3ExprAddFunctionOrderBy(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The function call to which ORDER BY is to be added */
+ ExprList *pOrderBy /* The ORDER BY clause to add */
+){
+ Expr *pOB;
+ sqlite3 *db = pParse->db;
+ if( NEVER(pOrderBy==0) ){
+ assert( db->mallocFailed );
+ return;
+ }
+ if( pExpr==0 ){
+ assert( db->mallocFailed );
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ assert( pExpr->op==TK_FUNCTION );
+ assert( pExpr->pLeft==0 );
+ assert( ExprUseXList(pExpr) );
+ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
+ /* Ignore ORDER BY on zero-argument aggregates */
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);
+ return;
+ }
+ if( IsWindowFunc(pExpr) ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+
+ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
+ if( pOB==0 ){
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ pOB->x.pList = pOrderBy;
+ assert( ExprUseXList(pOB) );
+ pExpr->pLeft = pOB;
+ ExprSetProperty(pOB, EP_FullSize);
+}
+
+/*
** Check to see if a function is usable according to current access
** rules:
**
@@ -1342,6 +1404,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
+void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
+}
/*
** Clear both elements of an OnOrUsing object
@@ -1367,9 +1432,7 @@ void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprDelete,
- pExpr);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
@@ -1434,11 +1497,7 @@ static int dupedExprStructSize(const Expr *p, int flags){
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
- if( 0==flags || p->op==TK_SELECT_COLUMN
-#ifndef SQLITE_OMIT_WINDOWFUNC
- || ExprHasProperty(p, EP_WinFunc)
-#endif
- ){
+ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){
nSize = EXPR_FULLSIZE;
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
@@ -1469,56 +1528,93 @@ static int dupedExprNodeSize(const Expr *p, int flags){
/*
** Return the number of bytes required to create a duplicate of the
-** expression passed as the first argument. The second argument is a
-** mask containing EXPRDUP_XXX flags.
+** expression passed as the first argument.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.u.zToken, if any.
**
-** If the EXPRDUP_REDUCE flag is set, then the return value includes
-** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
-** and Expr.pRight variables (but not for any structures pointed to or
-** descended from the Expr.x.pList or Expr.x.pSelect variables).
+** The return value includes space to duplicate all Expr nodes in the
+** tree formed by Expr.pLeft and Expr.pRight, but not any other
+** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin.
*/
-static int dupedExprSize(const Expr *p, int flags){
- int nByte = 0;
- if( p ){
- nByte = dupedExprNodeSize(p, flags);
- if( flags&EXPRDUP_REDUCE ){
- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags);
- }
- }
+static int dupedExprSize(const Expr *p){
+ int nByte;
+ assert( p!=0 );
+ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE);
+ if( p->pLeft ) nByte += dupedExprSize(p->pLeft);
+ if( p->pRight ) nByte += dupedExprSize(p->pRight);
+ assert( nByte==ROUND8(nByte) );
return nByte;
}
/*
-** This function is similar to sqlite3ExprDup(), except that if pzBuffer
-** is not NULL then *pzBuffer is assumed to point to a buffer large enough
-** to store the copy of expression p, the copies of p->u.zToken
-** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
-** if any. Before returning, *pzBuffer is set to the first byte past the
-** portion of the buffer copied into by this function.
+** An EdupBuf is a memory allocation used to stored multiple Expr objects
+** together with their Expr.zToken content. This is used to help implement
+** compression while doing sqlite3ExprDup(). The top-level Expr does the
+** allocation for itself and many of its decendents, then passes an instance
+** of the structure down into exprDup() so that they decendents can have
+** access to that memory.
*/
-static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
+typedef struct EdupBuf EdupBuf;
+struct EdupBuf {
+ u8 *zAlloc; /* Memory space available for storage */
+#ifdef SQLITE_DEBUG
+ u8 *zEnd; /* First byte past the end of memory */
+#endif
+};
+
+/*
+** This function is similar to sqlite3ExprDup(), except that if pEdupBuf
+** is not NULL then it points to memory that can be used to store a copy
+** of the input Expr p together with its p->u.zToken (if any). pEdupBuf
+** is updated with the new buffer tail prior to returning.
+*/
+static Expr *exprDup(
+ sqlite3 *db, /* Database connection (for memory allocation) */
+ const Expr *p, /* Expr tree to be duplicated */
+ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */
+ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */
+){
Expr *pNew; /* Value to return */
- u8 *zAlloc; /* Memory space from which to build Expr object */
+ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */
u32 staticFlag; /* EP_Static if space not obtained from malloc */
+ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */
assert( db!=0 );
assert( p );
assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE );
/* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
+ if( pEdupBuf ){
+ sEdupBuf.zAlloc = pEdupBuf->zAlloc;
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = pEdupBuf->zEnd;
+#endif
staticFlag = EP_Static;
- assert( zAlloc!=0 );
+ assert( sEdupBuf.zAlloc!=0 );
+ assert( dupFlags==EXPRDUP_REDUCE );
}else{
- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ int nAlloc;
+ if( dupFlags ){
+ nAlloc = dupedExprSize(p);
+ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30NN(p->u.zToken)+1;
+ nAlloc = ROUND8(EXPR_FULLSIZE + nToken);
+ }else{
+ nToken = 0;
+ nAlloc = ROUND8(EXPR_FULLSIZE);
+ }
+ assert( nAlloc==ROUND8(nAlloc) );
+ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc);
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0;
+#endif
+
staticFlag = 0;
}
- pNew = (Expr *)zAlloc;
+ pNew = (Expr *)sEdupBuf.zAlloc;
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) );
if( pNew ){
/* Set nNewSize to the size allocated for the structure pointed to
@@ -1527,22 +1623,27 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
** by the copy of the p->u.zToken string (if any).
*/
const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
+ int nNewSize = nStructSize & 0xfff;
+ if( nToken<0 ){
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
+ }else{
+ nToken = 0;
+ }
}
if( dupFlags ){
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken );
assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
+ memcpy(sEdupBuf.zAlloc, p, nNewSize);
}else{
u32 nSize = (u32)exprStructSize(p);
- memcpy(zAlloc, p, nSize);
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >=
+ (int)EXPR_FULLSIZE+nToken );
+ memcpy(sEdupBuf.zAlloc, p, nSize);
if( nSize<EXPR_FULLSIZE ){
- memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
+ memset(&sEdupBuf.zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ nNewSize = EXPR_FULLSIZE;
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
@@ -1555,44 +1656,50 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ assert( nToken>=0 );
+ if( nToken>0 ){
+ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize];
memcpy(zToken, p->u.zToken, nToken);
+ nNewSize += nToken;
}
+ sEdupBuf.zAlloc += ROUND8(nNewSize);
+
+ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){
- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
if( ExprUseXSelect(p) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
}else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList,
+ p->op!=TK_ORDER ? dupFlags : 0);
}
- }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
- zAlloc += dupedExprNodeSize(p, dupFlags);
- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
- pNew->pLeft = p->pLeft ?
- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
- pNew->pRight = p->pRight ?
- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
- }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(p, EP_WinFunc) ){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
- }else{
- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
- if( pNew->op==TK_SELECT_COLUMN ){
+
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( dupFlags ){
+ if( p->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
+ }else{
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }else{
+ if( p->op==TK_SELECT_COLUMN ){
pNew->pLeft = p->pLeft;
- assert( p->pRight==0 || p->pRight==p->pLeft
- || ExprHasProperty(p->pLeft, EP_Subquery) );
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
}else{
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
}
@@ -1600,6 +1707,8 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
}
}
+ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf));
+ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd );
return pNew;
}
@@ -1864,11 +1973,7 @@ Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags){
** initially NULL, then create a new expression list.
**
** The pList argument must be either NULL or a pointer to an ExprList
-** obtained from a prior call to sqlite3ExprListAppend(). This routine
-** may not be used with an ExprList obtained from sqlite3ExprListDup().
-** Reason: This routine assumes that the number of slots in pList->a[]
-** is a power of two. That is true for sqlite3ExprListAppend() returns
-** but is not necessarily true from the return value of sqlite3ExprListDup().
+** obtained from a prior call to sqlite3ExprListAppend().
**
** If a memory allocation error occurs, the entire list is freed and
** NULL is returned. If non-NULL is returned, then it is guaranteed
@@ -2133,6 +2238,9 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
+void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
+ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
+}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
@@ -2632,9 +2740,10 @@ int sqlite3ExprCanBeNull(const Expr *p){
case TK_COLUMN:
assert( ExprUseYTab(p) );
return ExprHasProperty(p, EP_CanBeNull) ||
- p->y.pTab==0 || /* Reference to column of index on expression */
+ NEVER(p->y.pTab==0) || /* Reference to column of index on expr */
(p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
+ && ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
@@ -2695,6 +2804,27 @@ int sqlite3IsRowid(const char *z){
}
/*
+** Return a pointer to a buffer containing a usable rowid alias for table
+** pTab. An alias is usable if there is not an explicit user-defined column
+** of the same name.
+*/
+const char *sqlite3RowidAlias(Table *pTab){
+ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"};
+ int ii;
+ assert( VisibleRowid(pTab) );
+ for(ii=0; ii<ArraySize(azOpt); ii++){
+ int iCol;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
+ }
+ if( iCol==pTab->nCol ){
+ return azOpt[ii];
+ }
+ }
+ return 0;
+}
+
+/*
** pX is the RHS of an IN operator. If pX is a SELECT statement
** that can be simplified to a direct table access, then return
** a pointer to the SELECT statement. If pX is not a SELECT statement,
@@ -4232,6 +4362,41 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
/*
+** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
+** function checks the Parse.pIdxPartExpr list to see if this column
+** can be replaced with a constant value. If so, it generates code to
+** put the constant value in a register (ideally, but not necessarily,
+** register iTarget) and returns the register number.
+**
+** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
+** returned.
+*/
+static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){
+ IndexedExpr *p;
+ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){
+ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){
+ Vdbe *v = pParse->pVdbe;
+ int addr = 0;
+ int ret;
+
+ if( p->bMaybeNullRow ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur);
+ }
+ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0,
+ (const char*)&p->aff, 1);
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeChangeP3(v, addr, ret);
+ }
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -4267,6 +4432,7 @@ expr_code_doover:
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
op = pExpr->op;
}
+ assert( op!=TK_ORDER );
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
@@ -4280,7 +4446,7 @@ expr_code_doover:
#ifdef SQLITE_VDBE_COVERAGE
/* Verify that the OP_Null above is exercised by tests
** tag-20230325-2 */
- sqlite3VdbeAddOp2(v, OP_NotNull, target, 1);
+ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325);
VdbeCoverageNeverTaken(v);
#endif
break;
@@ -4388,6 +4554,11 @@ expr_code_doover:
iTab = pParse->iSelfTab - 1;
}
}
+ else if( pParse->pIdxPartExpr
+ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target))
+ ){
+ return r1;
+ }
assert( ExprUseYTab(pExpr) );
assert( pExpr->y.pTab!=0 );
iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
@@ -5048,7 +5219,7 @@ expr_code_doover:
** once. If no functions are involved, then factor the code out and put it at
** the end of the prepared statement in the initialization section.
**
-** If regDest>=0 then the result is always stored in that register and the
+** If regDest>0 then the result is always stored in that register and the
** result is not reusable. If regDest<0 then this routine is free to
** store the value wherever it wants. The register where the expression
** is stored is returned. When regDest<0, two identical expressions might
@@ -5063,6 +5234,7 @@ int sqlite3ExprCodeRunJustOnce(
){
ExprList *p;
assert( ConstFactorOk(pParse) );
+ assert( regDest!=0 );
p = pParse->pConstExpr;
if( regDest<0 && p ){
struct ExprList_item *pItem;
@@ -5153,8 +5325,10 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
- if( ALWAYS(pExpr)
- && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER)
+ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
+ testcase( pX!=pExpr );
+ if( ALWAYS(pX)
+ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
){
op = OP_Copy;
}else{
@@ -5874,8 +6048,8 @@ int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB, int iTab){
*/
int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
- sqlite3ExprSkipCollateAndLikely(pA),
- sqlite3ExprSkipCollateAndLikely(pB),
+ sqlite3ExprSkipCollate(pA),
+ sqlite3ExprSkipCollate(pB),
iTab);
}
@@ -6347,6 +6521,12 @@ int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList *pSrcList){
assert( pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3WalkExprList(&w, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ assert( pExpr->pLeft->x.pList!=0 );
+ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
@@ -6594,13 +6774,14 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
+ && pExpr->pAggInfo==0
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( pItem->pFExpr==pExpr ) break;
+ if( NEVER(pItem->pFExpr==pExpr) ) break;
if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
break;
}
@@ -6611,14 +6792,44 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db);
i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){
+ int nArg;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
pItem->pFExpr = pExpr;
assert( ExprUseUToken(pExpr) );
+ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken,
- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
- if( pExpr->flags & EP_Distinct ){
+ pExpr->u.zToken, nArg, enc, 0);
+ assert( pItem->bOBUnique==0 );
+ if( pExpr->pLeft
+ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0
+ ){
+ /* The NEEDCOLL test above causes any ORDER BY clause on
+ ** aggregate min() or max() to be ignored. */
+ ExprList *pOBList;
+ assert( nArg>0 );
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ pItem->iOBTab = pParse->nTab++;
+ pOBList = pExpr->pLeft->x.pList;
+ assert( pOBList->nExpr>0 );
+ assert( pItem->bOBUnique==0 );
+ if( pOBList->nExpr==1
+ && nArg==1
+ && sqlite3ExprCompare(0,pOBList->a[0].pExpr,
+ pExpr->x.pList->a[0].pExpr,0)==0
+ ){
+ pItem->bOBPayload = 0;
+ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
+ }else{
+ pItem->bOBPayload = 1;
+ }
+ pItem->bUseSubtype =
+ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
+ }else{
+ pItem->iOBTab = -1;
+ }
+ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
diff --git a/src/fkey.c b/src/fkey.c
index 3142e0ca6..bace1ae5e 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -858,6 +858,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|| (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
){
+ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 );
return 1;
}
}
@@ -1052,6 +1053,8 @@ void sqlite3FkCheck(
}
if( regOld!=0 ){
int eAction = pFKey->aAction[aChange!=0];
+ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None;
+
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
/* If this is a deferred FK constraint, or a CASCADE or SET NULL
** action applies, then any foreign key violations caused by
@@ -1167,7 +1170,11 @@ int sqlite3FkRequired(
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
- if( p->aAction[1]!=OE_None ) return 2;
+ if( (pParse->db->flags & SQLITE_FkNoAction)==0
+ && p->aAction[1]!=OE_None
+ ){
+ return 2;
+ }
bHaveFK = 1;
}
}
@@ -1217,6 +1224,7 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None;
if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
return 0;
}
diff --git a/src/func.c b/src/func.c
index 8739035b5..9fbd1e9e1 100644
--- a/src/func.c
+++ b/src/func.c
@@ -1101,13 +1101,13 @@ void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
double r1, r2;
const char *zVal;
r1 = sqlite3_value_double(pValue);
- sqlite3_str_appendf(pStr, "%!.15g", r1);
+ sqlite3_str_appendf(pStr, "%!0.15g", r1);
zVal = sqlite3_str_value(pStr);
if( zVal ){
sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
if( r1!=r2 ){
sqlite3_str_reset(pStr);
- sqlite3_str_appendf(pStr, "%!.20e", r1);
+ sqlite3_str_appendf(pStr, "%!0.20e", r1);
}
}
break;
@@ -1256,7 +1256,8 @@ static void hexFunc(
*(z++) = hexdigits[c&0xf];
}
*z = 0;
- sqlite3_result_text(context, zHex, n*2, sqlite3_free);
+ sqlite3_result_text64(context, zHex, (u64)(z-zHex),
+ sqlite3_free, SQLITE_UTF8);
}
}
@@ -1408,7 +1409,7 @@ static void replaceFunc(
}
if( zPattern[0]==0 ){
assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
- sqlite3_result_value(context, argv[0]);
+ sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT);
return;
}
nPattern = sqlite3_value_bytes(argv[1]);
@@ -1580,7 +1581,7 @@ static void concatFuncCore(
k = sqlite3_value_bytes(argv[i]);
if( k>0 ){
const char *v = (const char*)sqlite3_value_text(argv[i]);
- if( ALWAYS(v!=0) ){
+ if( v!=0 ){
if( j>0 && nSep>0 ){
memcpy(&z[j], zSep, nSep);
j += nSep;
@@ -1592,7 +1593,7 @@ static void concatFuncCore(
}
z[j] = 0;
assert( j<=n );
- sqlite3_result_text64(context, z, n, sqlite3_free, SQLITE_UTF8);
+ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
}
/*
@@ -2046,6 +2047,7 @@ static void minMaxFinalize(sqlite3_context *context){
/*
** group_concat(EXPR, ?SEPARATOR?)
+** string_agg(EXPR, SEPARATOR)
**
** The SEPARATOR goes before the EXPR string. This is tragic. The
** groupConcatInverse() implementation would have been easier if the
@@ -2670,6 +2672,8 @@ void sqlite3RegisterBuiltinFunctions(void){
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
+ WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
diff --git a/src/global.c b/src/global.c
index 60cd13e2a..7f27d91d1 100644
--- a/src/global.c
+++ b/src/global.c
@@ -244,6 +244,9 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* bSmallMalloc */
1, /* bExtraSchemaChecks */
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
+#ifdef SQLITE_DEBUG
+ 0, /* bJsonSelfcheck */
+#endif
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
diff --git a/src/json.c b/src/json.c
index 253fce9f4..1227ada78 100644
--- a/src/json.c
+++ b/src/json.c
@@ -10,24 +10,145 @@
**
******************************************************************************
**
-** This SQLite JSON functions.
+** SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015. That
** extension proved so useful that it has now been moved into the core.
**
-** For the time being, all JSON is stored as pure text. (We might add
-** a JSONB type in the future which stores a binary encoding of JSON in
-** a BLOB, but there is no support for JSONB in the current implementation.
-** This implementation parses JSON text at 250 MB/s, so it is hard to see
-** how JSONB might improve on that.)
+** The original design stored all JSON as pure text, canonical RFC-8259.
+** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
+** All generated JSON text still conforms strictly to RFC-8259, but text
+** with JSON-5 extensions is accepted as input.
+**
+** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
+** accept BLOB values that have JSON encoded using a binary representation
+** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
+** format SQLite JSONB is completely different and incompatible with
+** PostgreSQL JSONB.
+**
+** Decoding and interpreting JSONB is still O(N) where N is the size of
+** the input, the same as text JSON. However, the constant of proportionality
+** for JSONB is much smaller due to faster parsing. The size of each
+** element in JSONB is encoded in its header, so there is no need to search
+** for delimiters using persnickety syntax rules. JSONB seems to be about
+** 3x faster than text JSON as a result. JSONB is also tends to be slightly
+** smaller than text JSON, by 5% or 10%, but there are corner cases where
+** JSONB can be slightly larger. So you are not far mistaken to say that
+** a JSONB blob is the same size as the equivalent RFC-8259 text.
+**
+**
+** THE JSONB ENCODING:
+**
+** Every JSON element is encoded in JSONB as a header and a payload.
+** The header is between 1 and 9 bytes in size. The payload is zero
+** or more bytes.
+**
+** The lower 4 bits of the first byte of the header determines the
+** element type:
+**
+** 0: NULL
+** 1: TRUE
+** 2: FALSE
+** 3: INT -- RFC-8259 integer literal
+** 4: INT5 -- JSON5 integer literal
+** 5: FLOAT -- RFC-8259 floating point literal
+** 6: FLOAT5 -- JSON5 floating point literal
+** 7: TEXT -- Text literal acceptable to both SQL and JSON
+** 8: TEXTJ -- Text containing RFC-8259 escapes
+** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes
+** 10: TEXTRAW -- Text containing unescaped syntax characters
+** 11: ARRAY
+** 12: OBJECT
+**
+** The other three possible values (13-15) are reserved for future
+** enhancements.
+**
+** The upper 4 bits of the first byte determine the size of the header
+** and sometimes also the size of the payload. If X is the first byte
+** of the element and if X>>4 is between 0 and 11, then the payload
+** will be that many bytes in size and the header is exactly one byte
+** in size. Other four values for X>>4 (12-15) indicate that the header
+** is more than one byte in size and that the payload size is determined
+** by the remainder of the header, interpreted as a unsigned big-endian
+** integer.
+**
+** Value of X>>4 Size integer Total header size
+** ------------- -------------------- -----------------
+** 12 1 byte (0-255) 2
+** 13 2 byte (0-65535) 3
+** 14 4 byte (0-4294967295) 5
+** 15 8 byte (0-1.8e19) 9
+**
+** The payload size need not be expressed in its minimal form. For example,
+** if the payload size is 10, the size can be expressed in any of 5 different
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
+** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
+** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
+** a single byte of 0x0a. The shorter forms are preferred, of course, but
+** sometimes when generating JSONB, the payload size is not known in advance
+** and it is convenient to reserve sufficient header space to cover the
+** largest possible payload size and then come back later and patch up
+** the size when it becomes known, resulting in a non-minimal encoding.
+**
+** The value (X>>4)==15 is not actually used in the current implementation
+** (as SQLite is currently unable handle BLOBs larger than about 2GB)
+** but is included in the design to allow for future enhancements.
+**
+** The payload follows the header. NULL, TRUE, and FALSE have no payload and
+** their payload size must always be zero. The payload for INT, INT5,
+** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the
+** "..." or '...' delimiters are omitted from the various text encodings.
+** The payload for ARRAY and OBJECT is a list of additional elements that
+** are the content for the array or object. The payload for an OBJECT
+** must be an even number of elements. The first element of each pair is
+** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
+**
+** A valid JSONB blob consists of a single element, as described above.
+** Usually this will be an ARRAY or OBJECT element which has many more
+** elements as its content. But the overall blob is just a single element.
+**
+** Input validation for JSONB blobs simply checks that the element type
+** code is between 0 and 12 and that the total size of the element
+** (header plus payload) is the same as the size of the BLOB. If those
+** checks are true, the BLOB is assumed to be JSONB and processing continues.
+** Errors are only raised if some other miscoding is discovered during
+** processing.
+**
+** Additional information can be found in the doc/jsonb.md file of the
+** canonical SQLite source tree.
*/
#ifndef SQLITE_OMIT_JSON
#include "sqliteInt.h"
+/* JSONB element types
+*/
+#define JSONB_NULL 0 /* "null" */
+#define JSONB_TRUE 1 /* "true" */
+#define JSONB_FALSE 2 /* "false" */
+#define JSONB_INT 3 /* integer acceptable to JSON and SQL */
+#define JSONB_INT5 4 /* integer in 0x000 notation */
+#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */
+#define JSONB_FLOAT5 6 /* float with JSON5 extensions */
+#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */
+#define JSONB_TEXTJ 8 /* Text with JSON escapes */
+#define JSONB_TEXT5 9 /* Text with JSON-5 escape */
+#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */
+#define JSONB_ARRAY 11 /* An array */
+#define JSONB_OBJECT 12 /* An object */
+
+/* Human-readable names for the JSONB values. The index for each
+** string must correspond to the JSONB_* integer above.
+*/
+static const char * const jsonbType[] = {
+ "null", "true", "false", "integer", "integer",
+ "real", "real", "text", "text", "text",
+ "text", "array", "object", "", "", "", ""
+};
+
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
@@ -48,11 +169,19 @@ static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
-#define fast_isspace(x) (jsonIsSpace[(unsigned char)x])
+#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
/*
-** Characters that are special to JSON. Control charaters,
-** '"' and '\\'.
+** The set of all space characters recognized by jsonIsspace().
+** Useful as the second argument to strspn().
+*/
+static const char jsonSpaces[] = "\011\012\015\040";
+
+/*
+** Characters that are special to JSON. Control characters,
+** '"' and '\\' and '\''. Actually, '\'' is not special to
+** canonical JSON, but it is special in JSON-5, so we include
+** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -74,22 +203,49 @@ static const char jsonIsOk[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
-
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST)
-# define VVA(X)
-#else
-# define VVA(X) X
-#endif
-
/* Objects */
+typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
-typedef struct JsonNode JsonNode;
typedef struct JsonParse JsonParse;
-typedef struct JsonCleanup JsonCleanup;
+
+/*
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+*/
+#define JSON_CACHE_ID (-429938) /* Cache entry */
+#define JSON_CACHE_SIZE 4 /* Max number of cache entries */
+
+/*
+** jsonUnescapeOneChar() returns this invalid code point if it encounters
+** a syntax error.
+*/
+#define JSON_INVALID_CHAR 0x99999
+
+/* A cache mapping JSON text into JSONB blobs.
+**
+** Each cache entry is a JsonParse object with the following restrictions:
+**
+** * The bReadOnly flag must be set
+**
+** * The aBlob[] array must be owned by the JsonParse object. In other
+** words, nBlobAlloc must be non-zero.
+**
+** * eEdit and delta must be zero.
+**
+** * zJson must be an RCStr. In other words bJsonIsRCStr must be true.
+*/
+struct JsonCache {
+ sqlite3 *db; /* Database connection */
+ int nUsed; /* Number of active entries in the cache */
+ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */
+};
/* An instance of this object represents a JSON string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
+**
+** If the generated string is longer than will fit into the zSpace[] buffer,
+** then it will be an RCStr string. This aids with caching of large
+** JSON strings.
*/
struct JsonString {
sqlite3_context *pCtx; /* Function context - put error messages here */
@@ -97,121 +253,75 @@ struct JsonString {
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
- u8 bErr; /* True if an error has been encountered */
+ u8 eErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
-/* A deferred cleanup task. A list of JsonCleanup objects might be
-** run when the JsonParse object is destroyed.
-*/
-struct JsonCleanup {
- JsonCleanup *pJCNext; /* Next in a list */
- void (*xOp)(void*); /* Routine to run */
- void *pArg; /* Argument to xOp() */
-};
+/* Allowed values for JsonString.eErr */
+#define JSTRING_OOM 0x01 /* Out of memory */
+#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */
+#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */
-/* JSON type values
+/* The "subtype" set for text JSON values passed through using
+** sqlite3_result_subtype() and sqlite3_value_subtype().
*/
-#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */
-#define JSON_NULL 1
-#define JSON_TRUE 2
-#define JSON_FALSE 3
-#define JSON_INT 4
-#define JSON_REAL 5
-#define JSON_STRING 6
-#define JSON_ARRAY 7
-#define JSON_OBJECT 8
-
-/* The "subtype" set for JSON values */
#define JSON_SUBTYPE 74 /* Ascii for "J" */
/*
-** Names of the various JSON types:
+** Bit values for the flags passed into various SQL function implementations
+** via the sqlite3_user_data() value.
*/
-static const char * const jsonType[] = {
- "subst",
- "null", "true", "false", "integer", "real", "text", "array", "object"
-};
-
-/* Bit values for the JsonNode.jnFlag field
-*/
-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
-#define JNODE_REMOVE 0x04 /* Do not output */
-#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */
-#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
-#define JNODE_LABEL 0x20 /* Is a label of an object */
-#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */
-
-
-/* A single node of parsed JSON. An array of these nodes describes
-** a parse of JSON + edits.
-**
-** Use the json_parse() SQL function (available when compiled with
-** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including
-** a complete listing and decoding of the array of JsonNodes.
-*/
-struct JsonNode {
- u8 eType; /* One of the JSON_ type values */
- u8 jnFlags; /* JNODE flags */
- u8 eU; /* Which union element to use */
- u32 n; /* Bytes of content for INT, REAL or STRING
- ** Number of sub-nodes for ARRAY and OBJECT
- ** Node that SUBST applies to */
- union {
- const char *zJContent; /* 1: Content for INT, REAL, and STRING */
- u32 iAppend; /* 2: More terms for ARRAY and OBJECT */
- u32 iKey; /* 3: Key for ARRAY objects in json_tree() */
- u32 iPrev; /* 4: Previous SUBST node, or 0 */
- } u;
-};
+#define JSON_JSON 0x01 /* Result is always JSON */
+#define JSON_SQL 0x02 /* Result is always SQL */
+#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
+#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+#define JSON_BLOB 0x08 /* Use the BLOB output format */
-/* A parsed and possibly edited JSON string. Lifecycle:
-**
-** 1. JSON comes in and is parsed into an array aNode[]. The original
-** JSON text is stored in zJson.
+/* A parsed JSON value. Lifecycle:
**
-** 2. Zero or more changes are made (via json_remove() or json_replace()
-** or similar) to the aNode[] array.
+** 1. JSON comes in and is parsed into a JSONB value in aBlob. The
+** original text is stored in zJson. This step is skipped if the
+** input is JSONB instead of text JSON.
**
-** 3. A new, edited and mimified JSON string is generated from aNode
-** and stored in zAlt. The JsonParse object always owns zAlt.
+** 2. The aBlob[] array is searched using the JSON path notation, if needed.
+**
+** 3. Zero or more changes are made to aBlob[] (via json_remove() or
+** json_replace() or json_patch() or similar).
**
-** Step 1 always happens. Step 2 and 3 may or may not happen, depending
-** on the operation.
-**
-** aNode[].u.zJContent entries typically point into zJson. Hence zJson
-** must remain valid for the lifespan of the parse. For edits,
-** aNode[].u.zJContent might point to malloced space other than zJson.
-** Entries in pClup are responsible for freeing that extra malloced space.
-**
-** When walking the parse tree in aNode[], edits are ignored if useMod is
-** false.
+** 4. New JSON text is generated from the aBlob[] for output. This step
+** is skipped if the function is one of the jsonb_* functions that
+** returns JSONB instead of text JSON.
*/
struct JsonParse {
- u32 nNode; /* Number of slots of aNode[] used */
- u32 nAlloc; /* Number of slots of aNode[] allocated */
- JsonNode *aNode; /* Array of nodes containing the parse */
- char *zJson; /* Original JSON string (before edits) */
- char *zAlt; /* Revised and/or mimified JSON */
- u32 *aUp; /* Index of parent of each node */
- JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */
+ u8 *aBlob; /* JSONB representation of JSON value */
+ u32 nBlob; /* Bytes of aBlob[] actually used */
+ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */
+ char *zJson; /* Json text used for parsing */
+ sqlite3 *db; /* The database connection to which this object belongs */
+ int nJson; /* Length of the zJson string in bytes */
+ u32 nJPRef; /* Number of references to this object */
+ u32 iErr; /* Error location in zJson[] */
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
u8 bJsonIsRCStr; /* True if zJson is an RCStr */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
- u8 useMod; /* Actually use the edits contain inside aNode */
- u8 hasMod; /* aNode contains edits from the original zJson */
- u32 nJPRef; /* Number of references to this object */
- int nJson; /* Length of the zJson string in bytes */
- int nAlt; /* Length of alternative JSON string zAlt, in bytes */
- u32 iErr; /* Error location in zJson[] */
- u32 iSubst; /* Last JSON_SUBST entry in aNode[] */
- u32 iHold; /* Age of this entry in the cache for LRU replacement */
+ u8 bReadOnly; /* Do not modify. */
+ /* Search and edit information. See jsonLookupStep() */
+ u8 eEdit; /* Edit operation to apply */
+ int delta; /* Size change due to the edit */
+ u32 nIns; /* Number of bytes to insert */
+ u32 iLabel; /* Location of label if search landed on an object value */
+ u8 *aIns; /* Content to be inserted */
};
+/* Allowed values for JsonParse.eEdit */
+#define JEDIT_DEL 1 /* Delete if exists */
+#define JEDIT_REPL 2 /* Overwrite if exists */
+#define JEDIT_INS 3 /* Insert if not exists */
+#define JEDIT_SET 4 /* Insert or overwrite */
+
/*
** Maximum nesting depth of JSON for this implementation.
**
@@ -219,15 +329,151 @@ struct JsonParse {
** descent parser. A depth of 1000 is far deeper than any sane JSON
** should go. Historical note: This limit was 2000 prior to version 3.42.0
*/
-#define JSON_MAX_DEPTH 1000
+#ifndef SQLITE_JSON_MAX_DEPTH
+# define JSON_MAX_DEPTH 1000
+#else
+# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
+#endif
+
+/*
+** Allowed values for the flgs argument to jsonParseFuncArg();
+*/
+#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */
+#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */
+
+/**************************************************************************
+** Forward references
+**************************************************************************/
+static void jsonReturnStringAsBlob(JsonString*);
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
+static void jsonReturnParse(sqlite3_context*,JsonParse*);
+static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
+static void jsonParseFree(JsonParse*);
+static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
+static u32 jsonUnescapeOneChar(const char*, u32, u32*);
+
+/**************************************************************************
+** Utility routines for dealing with JsonCache objects
+**************************************************************************/
+
+/*
+** Free a JsonCache object.
+*/
+static void jsonCacheDelete(JsonCache *p){
+ int i;
+ for(i=0; i<p->nUsed; i++){
+ jsonParseFree(p->a[i]);
+ }
+ sqlite3DbFree(p->db, p);
+}
+static void jsonCacheDeleteGeneric(void *p){
+ jsonCacheDelete((JsonCache*)p);
+}
+
+/*
+** Insert a new entry into the cache. If the cache is full, expel
+** the least recently used entry. Return SQLITE_OK on success or a
+** result code otherwise.
+**
+** Cache entries are stored in age order, oldest first.
+*/
+static int jsonCacheInsert(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ JsonParse *pParse /* The parse object to be added to the cache */
+){
+ JsonCache *p;
+
+ assert( pParse->zJson!=0 );
+ assert( pParse->bJsonIsRCStr );
+ assert( pParse->delta==0 );
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ p->db = db;
+ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ) return SQLITE_NOMEM;
+ }
+ if( p->nUsed >= JSON_CACHE_SIZE ){
+ jsonParseFree(p->a[0]);
+ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
+ p->nUsed = JSON_CACHE_SIZE-1;
+ }
+ assert( pParse->nBlobAlloc>0 );
+ pParse->eEdit = 0;
+ pParse->nJPRef++;
+ pParse->bReadOnly = 1;
+ p->a[p->nUsed] = pParse;
+ p->nUsed++;
+ return SQLITE_OK;
+}
+
+/*
+** Search for a cached translation the json text supplied by pArg. Return
+** the JsonParse object if found. Return NULL if not found.
+**
+** When a match if found, the matching entry is moved to become the
+** most-recently used entry if it isn't so already.
+**
+** The JsonParse object returned still belongs to the Cache and might
+** be deleted at any moment. If the caller whants the JsonParse to
+** linger, it needs to increment the nPJRef reference counter.
+*/
+static JsonParse *jsonCacheSearch(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ sqlite3_value *pArg /* Function argument containing SQL text */
+){
+ JsonCache *p;
+ int i;
+ const char *zJson;
+ int nJson;
+
+ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
+ return 0;
+ }
+ zJson = (const char*)sqlite3_value_text(pArg);
+ if( zJson==0 ) return 0;
+ nJson = sqlite3_value_bytes(pArg);
+
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ return 0;
+ }
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->zJson==zJson ) break;
+ }
+ if( i>=p->nUsed ){
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->nJson!=nJson ) continue;
+ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
+ }
+ }
+ if( i<p->nUsed ){
+ if( i<p->nUsed-1 ){
+ /* Make the matching entry the most recently used entry */
+ JsonParse *tmp = p->a[i];
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
+ p->a[p->nUsed-1] = tmp;
+ i = p->nUsed - 1;
+ }
+ assert( p->a[i]->delta==0 );
+ return p->a[i];
+ }else{
+ return 0;
+ }
+}
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
-/* Set the JsonString object to an empty string
+/* Turn uninitialized bulk memory into a valid JsonString object
+** holding a zero-length string.
*/
-static void jsonZero(JsonString *p){
+static void jsonStringZero(JsonString *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
@@ -236,39 +482,39 @@ static void jsonZero(JsonString *p){
/* Initialize the JsonString object
*/
-static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
- p->bErr = 0;
- jsonZero(p);
+ p->eErr = 0;
+ jsonStringZero(p);
}
/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
-static void jsonReset(JsonString *p){
+static void jsonStringReset(JsonString *p){
if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
- jsonZero(p);
+ jsonStringZero(p);
}
/* Report an out-of-memory (OOM) condition
*/
-static void jsonOom(JsonString *p){
- p->bErr = 1;
- sqlite3_result_error_nomem(p->pCtx);
- jsonReset(p);
+static void jsonStringOom(JsonString *p){
+ p->eErr |= JSTRING_OOM;
+ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
+ jsonStringReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
-static int jsonGrow(JsonString *p, u32 N){
+static int jsonStringGrow(JsonString *p, u32 N){
u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
- if( p->bErr ) return 1;
+ if( p->eErr ) return 1;
zNew = sqlite3RCStrNew(nTotal);
if( zNew==0 ){
- jsonOom(p);
+ jsonStringOom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
@@ -277,8 +523,8 @@ static int jsonGrow(JsonString *p, u32 N){
}else{
p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
if( p->zBuf==0 ){
- p->bErr = 1;
- jsonZero(p);
+ p->eErr |= JSTRING_OOM;
+ jsonStringZero(p);
return SQLITE_NOMEM;
}
}
@@ -288,20 +534,20 @@ static int jsonGrow(JsonString *p, u32 N){
/* Append N bytes from zIn onto the end of the JsonString string.
*/
-static SQLITE_NOINLINE void jsonAppendExpand(
+static SQLITE_NOINLINE void jsonStringExpandAndAppend(
JsonString *p,
const char *zIn,
u32 N
){
assert( N>0 );
- if( jsonGrow(p,N) ) return;
+ if( jsonStringGrow(p,N) ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
if( N==0 ) return;
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -310,7 +556,7 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
assert( N>0 );
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -322,7 +568,7 @@ static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
va_list ap;
- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
@@ -332,7 +578,7 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
- if( jsonGrow(p,1) ) return;
+ if( jsonStringGrow(p,1) ) return;
p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
@@ -343,24 +589,27 @@ static void jsonAppendChar(JsonString *p, char c){
}
}
-/* Try to force the string to be a zero-terminated RCStr string.
+/* Remove a single character from the end of the string
+*/
+static void jsonStringTrimOneChar(JsonString *p){
+ if( p->eErr==0 ){
+ assert( p->nUsed>0 );
+ p->nUsed--;
+ }
+}
+
+
+/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success. Return false if an OOM prevents this
** from happening.
*/
-static int jsonForceRCStr(JsonString *p){
+static int jsonStringTerminate(JsonString *p){
jsonAppendChar(p, 0);
- if( p->bErr ) return 0;
- p->nUsed--;
- if( p->bStatic==0 ) return 1;
- p->nAlloc = 0;
- p->nUsed++;
- jsonGrow(p, p->nUsed);
- p->nUsed--;
- return p->bStatic==0;
+ jsonStringTrimOneChar(p);
+ return p->eErr==0;
}
-
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
@@ -372,184 +621,120 @@ static void jsonAppendSeparator(JsonString *p){
jsonAppendChar(p, ',');
}
-/* Append the N-byte string in zIn to the end of the JsonString string
-** under construction. Enclose the string in "..." and escape
-** any double-quotes or backslash characters contained within the
-** string.
+/* c is a control character. Append the canonical JSON representation
+** of that control character to p.
+**
+** This routine assumes that the output buffer has already been enlarged
+** sufficiently to hold the worst-case encoding plus a nul terminator.
*/
-static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return;
- p->zBuf[p->nUsed++] = '"';
- for(i=0; i<N; i++){
- unsigned char c = ((unsigned const char*)zIn)[i];
- if( jsonIsOk[c] ){
- p->zBuf[p->nUsed++] = c;
- }else if( c=='"' || c=='\\' ){
- json_simple_escape:
- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
- p->zBuf[p->nUsed++] = '\\';
- p->zBuf[p->nUsed++] = c;
- }else if( c=='\'' ){
- p->zBuf[p->nUsed++] = c;
- }else{
- static const char aSpecial[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- assert( sizeof(aSpecial)==32 );
- assert( aSpecial['\b']=='b' );
- assert( aSpecial['\f']=='f' );
- assert( aSpecial['\n']=='n' );
- assert( aSpecial['\r']=='r' );
- assert( aSpecial['\t']=='t' );
- assert( c>=0 && c<sizeof(aSpecial) );
- if( aSpecial[c] ){
- c = aSpecial[c];
- goto json_simple_escape;
- }
- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
- p->zBuf[p->nUsed++] = '\\';
- p->zBuf[p->nUsed++] = 'u';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
- }
+static void jsonAppendControlChar(JsonString *p, u8 c){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ assert( c>=0 && c<sizeof(aSpecial) );
+ assert( p->nUsed+7 <= p->nAlloc );
+ if( aSpecial[c] ){
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = aSpecial[c];
+ p->nUsed += 2;
+ }else{
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = 'u';
+ p->zBuf[p->nUsed+2] = '0';
+ p->zBuf[p->nUsed+3] = '0';
+ p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4];
+ p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf];
+ p->nUsed += 6;
}
- p->zBuf[p->nUsed++] = '"';
- assert( p->nUsed<p->nAlloc );
}
-/*
-** The zIn[0..N] string is a JSON5 string literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
+/* Append the N-byte string in zIn to the end of the JsonString string
+** under construction. Enclose the string in double-quotes ("...") and
+** escape any double-quotes or backslash characters contained within the
+** string.
+**
+** This routine is a high-runner. There is a measurable performance
+** increase associated with unwinding the jsonIsOk[] loop.
*/
-static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- jsonAppendChar(p, '"');
- zIn++;
- N -= 2;
- while( N>0 ){
- for(i=0; i<N && zIn[i]!='\\'; i++){}
- if( i>0 ){
- jsonAppendRawNZ(p, zIn, i);
- zIn += i;
- N -= i;
- if( N==0 ) break;
- }
- assert( zIn[0]=='\\' );
- switch( (u8)zIn[1] ){
- case '\'':
- jsonAppendChar(p, '\'');
- break;
- case 'v':
- jsonAppendRawNZ(p, "\\u0009", 6);
- break;
- case 'x':
- jsonAppendRawNZ(p, "\\u00", 4);
- jsonAppendRawNZ(p, &zIn[2], 2);
- zIn += 2;
- N -= 2;
- break;
- case '0':
- jsonAppendRawNZ(p, "\\u0000", 6);
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
+ u32 k;
+ u8 c;
+ const u8 *z = (const u8*)zIn;
+ if( z==0 ) return;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
+ p->zBuf[p->nUsed++] = '"';
+ while( 1 /*exit-by-break*/ ){
+ k = 0;
+ /* The following while() is the 4-way unwound equivalent of
+ **
+ ** while( k<N && jsonIsOk[z[k]] ){ k++; }
+ */
+ while( 1 /* Exit by break */ ){
+ if( k+3>=N ){
+ while( k<N && jsonIsOk[z[k]] ){ k++; }
break;
- case '\r':
- if( zIn[2]=='\n' ){
- zIn++;
- N--;
- }
+ }
+ if( !jsonIsOk[z[k]] ){
break;
- case '\n':
+ }
+ if( !jsonIsOk[z[k+1]] ){
+ k += 1;
break;
- case 0xe2:
- assert( N>=4 );
- assert( 0x80==(u8)zIn[2] );
- assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] );
- zIn += 2;
- N -= 2;
+ }
+ if( !jsonIsOk[z[k+2]] ){
+ k += 2;
break;
- default:
- jsonAppendRawNZ(p, zIn, 2);
+ }
+ if( !jsonIsOk[z[k+3]] ){
+ k += 3;
break;
+ }else{
+ k += 4;
+ }
}
- zIn += 2;
- N -= 2;
- }
- jsonAppendChar(p, '"');
-}
-
-/*
-** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
- N--;
- }
- if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){
- sqlite3_int64 i = 0;
- int rc = sqlite3DecOrHexToI64(zIn, &i);
- if( rc<=1 ){
- jsonPrintf(100,p,"%lld",i);
+ if( k>=N ){
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ }
+ break;
+ }
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ z += k;
+ N -= k;
+ }
+ c = z[0];
+ if( c=='"' || c=='\\' ){
+ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ p->zBuf[p->nUsed++] = c;
+ }else if( c=='\'' ){
+ p->zBuf[p->nUsed++] = c;
}else{
- assert( rc==2 );
- jsonAppendRawNZ(p, "9.0e999", 7);
+ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
+ jsonAppendControlChar(p, c);
}
- return;
- }
- assert( N>0 );
- jsonAppendRawNZ(p, zIn, N);
-}
-
-/*
-** The zIn[0..N] string is a JSON5 real literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
+ z++;
N--;
}
- if( zIn[0]=='.' ){
- jsonAppendChar(p, '0');
- }
- for(i=0; i<N; i++){
- if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
- i++;
- jsonAppendRaw(p, zIn, i);
- zIn += i;
- N -= i;
- jsonAppendChar(p, '0');
- break;
- }
- }
- if( N>0 ){
- jsonAppendRawNZ(p, zIn, N);
- }
+ p->zBuf[p->nUsed++] = '"';
+ assert( p->nUsed<p->nAlloc );
}
-
-
/*
-** Append a function parameter value to the JSON string under
-** construction.
+** Append an sqlite3_value (such as a function parameter) to the JSON
+** string under construction in p.
*/
-static void jsonAppendValue(
+static void jsonAppendSqlValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
@@ -579,290 +764,127 @@ static void jsonAppendValue(
break;
}
default: {
- if( p->bErr==0 ){
+ if( jsonFuncArgMightBeBinary(pValue) ){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(pValue);
+ px.nBlob = sqlite3_value_bytes(pValue);
+ jsonTranslateBlobToText(&px, 0, p);
+ }else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
- p->bErr = 2;
- jsonReset(p);
+ p->eErr = JSTRING_ERR;
+ jsonStringReset(p);
}
break;
}
}
}
-
-/* Make the JSON in p the result of the SQL function.
+/* Make the text in p (which is probably a generated JSON text string)
+** the result of the SQL function.
+**
+** The JsonString is reset.
**
-** The JSON string is reset.
+** If pParse and ctx are both non-NULL, then the SQL string in p is
+** loaded into the zJson field of the pParse object as a RCStr and the
+** pParse is added to the cache.
*/
-static void jsonResult(JsonString *p){
- if( p->bErr==0 ){
- if( p->bStatic ){
+static void jsonReturnString(
+ JsonString *p, /* String to return */
+ JsonParse *pParse, /* JSONB source or NULL */
+ sqlite3_context *ctx /* Where to cache */
+){
+ assert( (pParse!=0)==(ctx!=0) );
+ assert( ctx==0 || ctx==p->pCtx );
+ if( p->eErr==0 ){
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
+ if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(p);
+ }else if( p->bStatic ){
sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
SQLITE_TRANSIENT, SQLITE_UTF8);
- }else if( jsonForceRCStr(p) ){
- sqlite3RCStrRef(p->zBuf);
- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
- (void(*)(void*))sqlite3RCStrUnref,
+ }else if( jsonStringTerminate(p) ){
+ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
+ int rc;
+ pParse->zJson = sqlite3RCStrRef(p->zBuf);
+ pParse->nJson = p->nUsed;
+ pParse->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, pParse);
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_result_error_nomem(ctx);
+ jsonStringReset(p);
+ return;
+ }
+ }
+ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
+ sqlite3RCStrUnref,
SQLITE_UTF8);
+ }else{
+ sqlite3_result_error_nomem(p->pCtx);
}
- }
- if( p->bErr==1 ){
+ }else if( p->eErr & JSTRING_OOM ){
sqlite3_result_error_nomem(p->pCtx);
+ }else if( p->eErr & JSTRING_MALFORMED ){
+ sqlite3_result_error(p->pCtx, "malformed JSON", -1);
}
- jsonReset(p);
+ jsonStringReset(p);
}
/**************************************************************************
-** Utility routines for dealing with JsonNode and JsonParse objects
+** Utility routines for dealing with JsonParse objects
**************************************************************************/
/*
-** Return the number of consecutive JsonNode slots need to represent
-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
-** OBJECT types, the number might be larger.
-**
-** Appended elements are not counted. The value returned is the number
-** by which the JsonNode counter should increment in order to go to the
-** next peer value.
-*/
-static u32 jsonNodeSize(JsonNode *pNode){
- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
-}
-
-/*
** Reclaim all memory allocated by a JsonParse object. But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
- while( pParse->pClup ){
- JsonCleanup *pTask = pParse->pClup;
- pParse->pClup = pTask->pJCNext;
- pTask->xOp(pTask->pArg);
- sqlite3_free(pTask);
- }
assert( pParse->nJPRef<=1 );
- if( pParse->aNode ){
- sqlite3_free(pParse->aNode);
- pParse->aNode = 0;
- }
- pParse->nNode = 0;
- pParse->nAlloc = 0;
- if( pParse->aUp ){
- sqlite3_free(pParse->aUp);
- pParse->aUp = 0;
- }
if( pParse->bJsonIsRCStr ){
sqlite3RCStrUnref(pParse->zJson);
pParse->zJson = 0;
+ pParse->nJson = 0;
pParse->bJsonIsRCStr = 0;
}
- if( pParse->zAlt ){
- sqlite3RCStrUnref(pParse->zAlt);
- pParse->zAlt = 0;
+ if( pParse->nBlobAlloc ){
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ pParse->aBlob = 0;
+ pParse->nBlob = 0;
+ pParse->nBlobAlloc = 0;
}
}
/*
-** Free a JsonParse object that was obtained from sqlite3_malloc().
-**
-** Note that destroying JsonParse might call sqlite3RCStrUnref() to
-** destroy the zJson value. The RCStr object might recursively invoke
-** JsonParse to destroy this pParse object again. Take care to ensure
-** that this recursive destructor sequence terminates harmlessly.
+** Decrement the reference count on the JsonParse object. When the
+** count reaches zero, free the object.
*/
static void jsonParseFree(JsonParse *pParse){
- if( pParse->nJPRef>1 ){
- pParse->nJPRef--;
- }else{
- jsonParseReset(pParse);
- sqlite3_free(pParse);
- }
-}
-
-/*
-** Add a cleanup task to the JsonParse object.
-**
-** If an OOM occurs, the cleanup operation happens immediately
-** and this function returns SQLITE_NOMEM.
-*/
-static int jsonParseAddCleanup(
- JsonParse *pParse, /* Add the cleanup task to this parser */
- void(*xOp)(void*), /* The cleanup task */
- void *pArg /* Argument to the cleanup */
-){
- JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) );
- if( pTask==0 ){
- pParse->oom = 1;
- xOp(pArg);
- return SQLITE_ERROR;
- }
- pTask->pJCNext = pParse->pClup;
- pParse->pClup = pTask;
- pTask->xOp = xOp;
- pTask->pArg = pArg;
- return SQLITE_OK;
-}
-
-/*
-** Convert the JsonNode pNode into a pure JSON string and
-** append to pOut. Subsubstructure is also included. Return
-** the number of JsonNode objects that are encoded.
-*/
-static void jsonRenderNode(
- JsonParse *pParse, /* the complete parse of the JSON */
- JsonNode *pNode, /* The node to render */
- JsonString *pOut /* Write JSON here */
-){
- assert( pNode!=0 );
- while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){
- u32 idx = (u32)(pNode - pParse->aNode);
- u32 i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pNode = &pParse->aNode[i+1];
- break;
- }
- i = pParse->aNode[i].u.iPrev;
- }
- }
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- jsonAppendRawNZ(pOut, "null", 4);
- break;
- }
- case JSON_TRUE: {
- jsonAppendRawNZ(pOut, "true", 4);
- break;
- }
- case JSON_FALSE: {
- jsonAppendRawNZ(pOut, "false", 5);
- break;
- }
- case JSON_STRING: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->jnFlags & JNODE_LABEL ){
- jsonAppendChar(pOut, '"');
- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
- jsonAppendChar(pOut, '"');
- }else{
- jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
- }
- }else if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_REAL: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_INT: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_ARRAY: {
- u32 j = 1;
- jsonAppendChar(pOut, '[');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- }
- j += jsonNodeSize(&pNode[j]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, ']');
- break;
- }
- case JSON_OBJECT: {
- u32 j = 1;
- jsonAppendChar(pOut, '{');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- jsonAppendChar(pOut, ':');
- jsonRenderNode(pParse, &pNode[j+1], pOut);
- }
- j += 1 + jsonNodeSize(&pNode[j+1]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, '}');
- break;
+ if( pParse ){
+ if( pParse->nJPRef>1 ){
+ pParse->nJPRef--;
+ }else{
+ jsonParseReset(pParse);
+ sqlite3DbFree(pParse->db, pParse);
}
}
}
-/*
-** Return a JsonNode and all its descendants as a JSON string.
-*/
-static void jsonReturnJson(
- JsonParse *pParse, /* The complete JSON */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx, /* Return value for this function */
- int bGenerateAlt /* Also store the rendered text in zAlt */
-){
- JsonString s;
- if( pParse->oom ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- if( pParse->nErr==0 ){
- jsonInit(&s, pCtx);
- jsonRenderNode(pParse, pNode, &s);
- if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){
- pParse->zAlt = sqlite3RCStrRef(s.zBuf);
- pParse->nAlt = s.nUsed;
- }
- jsonResult(&s);
- sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
- }
-}
+/**************************************************************************
+** Utility routines for the JSON text parser
+**************************************************************************/
/*
** Translate a single byte of Hex into an integer.
-** This routine only works if h really is a valid hexadecimal
-** character: 0..9a..fA..F
+** This routine only gives a correct answer if h really is a valid hexadecimal
+** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not
+** assert() if the digit is not hex.
*/
static u8 jsonHexToInt(int h){
- assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
+#ifdef SQLITE_ASCII
+ h += 9*(1&(h>>6));
+#endif
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
-#else
- h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
@@ -872,10 +894,6 @@ static u8 jsonHexToInt(int h){
*/
static u32 jsonHexToInt4(const char *z){
u32 v;
- assert( sqlite3Isxdigit(z[0]) );
- assert( sqlite3Isxdigit(z[1]) );
- assert( sqlite3Isxdigit(z[2]) );
- assert( sqlite3Isxdigit(z[3]) );
v = (jsonHexToInt(z[0])<<12)
+ (jsonHexToInt(z[1])<<8)
+ (jsonHexToInt(z[2])<<4)
@@ -884,281 +902,6 @@ static u32 jsonHexToInt4(const char *z){
}
/*
-** Make the JsonNode the return value of the function.
-*/
-static void jsonReturn(
- JsonParse *pParse, /* Complete JSON parse tree */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx /* Return value for this function */
-){
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- sqlite3_result_null(pCtx);
- break;
- }
- case JSON_TRUE: {
- sqlite3_result_int(pCtx, 1);
- break;
- }
- case JSON_FALSE: {
- sqlite3_result_int(pCtx, 0);
- break;
- }
- case JSON_INT: {
- sqlite3_int64 i = 0;
- int rc;
- int bNeg = 0;
- const char *z;
-
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- if( z[0]=='-' ){ z++; bNeg = 1; }
- else if( z[0]=='+' ){ z++; }
- rc = sqlite3DecOrHexToI64(z, &i);
- if( rc<=1 ){
- sqlite3_result_int64(pCtx, bNeg ? -i : i);
- }else if( rc==3 && bNeg ){
- sqlite3_result_int64(pCtx, SMALLEST_INT64);
- }else{
- goto to_double;
- }
- break;
- }
- case JSON_REAL: {
- double r;
- const char *z;
- assert( pNode->eU==1 );
- to_double:
- z = pNode->u.zJContent;
- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
- sqlite3_result_double(pCtx, r);
- break;
- }
- case JSON_STRING: {
- if( pNode->jnFlags & JNODE_RAW ){
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
- SQLITE_TRANSIENT);
- }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
- /* JSON formatted without any backslash-escapes */
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
- SQLITE_TRANSIENT);
- }else{
- /* Translate JSON formatted string into raw text */
- u32 i;
- u32 n = pNode->n;
- const char *z;
- char *zOut;
- u32 j;
- u32 nOut = n;
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- zOut = sqlite3_malloc( nOut+1 );
- if( zOut==0 ){
- sqlite3_result_error_nomem(pCtx);
- break;
- }
- for(i=1, j=0; i<n-1; i++){
- char c = z[i];
- if( c=='\\' ){
- c = z[++i];
- if( c=='u' ){
- u32 v = jsonHexToInt4(z+i+1);
- i += 4;
- if( v==0 ) break;
- if( v<=0x7f ){
- zOut[j++] = (char)v;
- }else if( v<=0x7ff ){
- zOut[j++] = (char)(0xc0 | (v>>6));
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- u32 vlo;
- if( (v&0xfc00)==0xd800
- && i<n-6
- && z[i+1]=='\\'
- && z[i+2]=='u'
- && ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
- ){
- /* We have a surrogate pair */
- v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
- i += 6;
- zOut[j++] = 0xf0 | (v>>18);
- zOut[j++] = 0x80 | ((v>>12)&0x3f);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- zOut[j++] = 0xe0 | (v>>12);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }
- }
- continue;
- }else if( c=='b' ){
- c = '\b';
- }else if( c=='f' ){
- c = '\f';
- }else if( c=='n' ){
- c = '\n';
- }else if( c=='r' ){
- c = '\r';
- }else if( c=='t' ){
- c = '\t';
- }else if( c=='v' ){
- c = '\v';
- }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
- /* pass through unchanged */
- }else if( c=='0' ){
- c = 0;
- }else if( c=='x' ){
- c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
- i += 2;
- }else if( c=='\r' && z[i+1]=='\n' ){
- i++;
- continue;
- }else if( 0xe2==(u8)c ){
- assert( 0x80==(u8)z[i+1] );
- assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
- i += 2;
- continue;
- }else{
- continue;
- }
- } /* end if( c=='\\' ) */
- zOut[j++] = c;
- } /* end for() */
- zOut[j] = 0;
- sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
- }
- break;
- }
- case JSON_ARRAY:
- case JSON_OBJECT: {
- jsonReturnJson(pParse, pNode, pCtx, 0);
- break;
- }
- }
-}
-
-/* Forward reference */
-static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
-
-/*
-** A macro to hint to the compiler that a function should not be
-** inlined.
-*/
-#if defined(__GNUC__)
-# define JSON_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER) && _MSC_VER>=1310
-# define JSON_NOINLINE __declspec(noinline)
-#else
-# define JSON_NOINLINE
-#endif
-
-
-/*
-** Add a single node to pParse->aNode after first expanding the
-** size of the aNode array. Return the index of the new node.
-**
-** If an OOM error occurs, set pParse->oom and return -1.
-*/
-static JSON_NOINLINE int jsonParseAddNodeExpand(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- u32 nNew;
- JsonNode *pNew;
- assert( pParse->nNode>=pParse->nAlloc );
- if( pParse->oom ) return -1;
- nNew = pParse->nAlloc*2 + 10;
- pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew);
- if( pNew==0 ){
- pParse->oom = 1;
- return -1;
- }
- pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode);
- pParse->aNode = pNew;
- assert( pParse->nNode<pParse->nAlloc );
- return jsonParseAddNode(pParse, eType, n, zContent);
-}
-
-/*
-** Create a new JsonNode instance based on the arguments and append that
-** instance to the JsonParse. Return the index in pParse->aNode[] of the
-** new node, or -1 if a memory allocation fails.
-*/
-static int jsonParseAddNode(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- JsonNode *p;
- assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc );
- if( pParse->nNode>=pParse->nAlloc ){
- return jsonParseAddNodeExpand(pParse, eType, n, zContent);
- }
- assert( pParse->aNode!=0 );
- p = &pParse->aNode[pParse->nNode];
- assert( p!=0 );
- p->eType = (u8)(eType & 0xff);
- p->jnFlags = (u8)(eType >> 8);
- VVA( p->eU = zContent ? 1 : 0 );
- p->n = n;
- p->u.zJContent = zContent;
- return pParse->nNode++;
-}
-
-/*
-** Add an array of new nodes to the current pParse->aNode array.
-** Return the index of the first node added.
-**
-** If an OOM error occurs, set pParse->oom.
-*/
-static void jsonParseAddNodeArray(
- JsonParse *pParse, /* Append the node to this object */
- JsonNode *aNode, /* Array of nodes to add */
- u32 nNode /* Number of elements in aNew */
-){
- assert( aNode!=0 );
- assert( nNode>=1 );
- if( pParse->nNode + nNode > pParse->nAlloc ){
- u32 nNew = pParse->nNode + nNode;
- JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode));
- if( aNew==0 ){
- pParse->oom = 1;
- return;
- }
- pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode);
- pParse->aNode = aNew;
- }
- memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode));
- pParse->nNode += nNode;
-}
-
-/*
-** Add a new JSON_SUBST node. The node immediately following
-** this new node will be the substitute content for iNode.
-*/
-static int jsonParseAddSubstNode(
- JsonParse *pParse, /* Add the JSON_SUBST here */
- u32 iNode /* References this node */
-){
- int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0);
- if( pParse->oom ) return -1;
- pParse->aNode[iNode].jnFlags |= JNODE_REPLACE;
- pParse->aNode[idx].eU = 4;
- pParse->aNode[idx].u.iPrev = pParse->iSubst;
- pParse->iSubst = idx;
- pParse->hasMod = 1;
- pParse->useMod = 1;
- return idx;
-}
-
-/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
@@ -1311,63 +1054,503 @@ static const struct NanInfName {
char *zMatch;
char *zRepl;
} aNanInfName[] = {
- { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" },
- { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
- { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },
- { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },
- { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },
+ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
+ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
+ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
+ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
+ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
};
+
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
+){
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+/****************************************************************************
+** Utility routines for dealing with the binary BLOB representation of JSON
+****************************************************************************/
+
/*
-** Parse a single JSON value which begins at pParse->zJson[i]. Return the
-** index of the first character past the end of the value parsed.
+** Expand pParse->aBlob so that it holds at least N bytes.
**
-** Special return values:
+** Return the number of errors.
+*/
+static int jsonBlobExpand(JsonParse *pParse, u32 N){
+ u8 *aNew;
+ u32 t;
+ assert( N>pParse->nBlobAlloc );
+ if( pParse->nBlobAlloc==0 ){
+ t = 100;
+ }else{
+ t = pParse->nBlobAlloc*2;
+ }
+ if( t<N ) t = N+100;
+ aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
+ if( aNew==0 ){ pParse->oom = 1; return 1; }
+ pParse->aBlob = aNew;
+ pParse->nBlobAlloc = t;
+ return 0;
+}
+
+/*
+** If pParse->aBlob is not previously editable (because it is taken
+** from sqlite3_value_blob(), as indicated by the fact that
+** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
+** by making a copy into space obtained from malloc.
+**
+** Return true on success. Return false on OOM.
+*/
+static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
+ u8 *aOld;
+ u32 nSize;
+ assert( !pParse->bReadOnly );
+ if( pParse->oom ) return 0;
+ if( pParse->nBlobAlloc>0 ) return 1;
+ aOld = pParse->aBlob;
+ nSize = pParse->nBlob + nExtra;
+ pParse->aBlob = 0;
+ if( jsonBlobExpand(pParse, nSize) ){
+ return 0;
+ }
+ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
+ memcpy(pParse->aBlob, aOld, pParse->nBlob);
+ return 1;
+}
+
+/* Expand pParse->aBlob and append one bytes.
+*/
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
+ JsonParse *pParse,
+ u8 c
+){
+ jsonBlobExpand(pParse, pParse->nBlob+1);
+ if( pParse->oom==0 ){
+ assert( pParse->nBlob+1<=pParse->nBlobAlloc );
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Append a single character.
+*/
+static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
+ if( pParse->nBlob >= pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendOneByte(pParse, c);
+ }else{
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Slow version of jsonBlobAppendNode() that first resizes the
+** pParse->aBlob structure.
+*/
+static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
+ JsonParse *pParse,
+ u8 eType,
+ u32 szPayload,
+ const void *aPayload
+){
+ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
+ jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
+}
+
+
+/* Append an node type byte together with the payload size and
+** possibly also the payload.
+**
+** If aPayload is not NULL, then it is a pointer to the payload which
+** is also appended. If aPayload is NULL, the pParse->aBlob[] array
+** is resized (if necessary) so that it is big enough to hold the
+** payload, but the payload is not appended and pParse->nBlob is left
+** pointing to where the first byte of payload will eventually be.
+*/
+static void jsonBlobAppendNode(
+ JsonParse *pParse, /* The JsonParse object under construction */
+ u8 eType, /* Node type. One of JSONB_* */
+ u32 szPayload, /* Number of bytes of payload */
+ const void *aPayload /* The payload. Might be NULL */
+){
+ u8 *a;
+ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
+ return;
+ }
+ assert( pParse->aBlob!=0 );
+ a = &pParse->aBlob[pParse->nBlob];
+ if( szPayload<=11 ){
+ a[0] = eType | (szPayload<<4);
+ pParse->nBlob += 1;
+ }else if( szPayload<=0xff ){
+ a[0] = eType | 0xc0;
+ a[1] = szPayload & 0xff;
+ pParse->nBlob += 2;
+ }else if( szPayload<=0xffff ){
+ a[0] = eType | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ pParse->nBlob += 3;
+ }else{
+ a[0] = eType | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ pParse->nBlob += 5;
+ }
+ if( aPayload ){
+ pParse->nBlob += szPayload;
+ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
+ }
+}
+
+/* Change the payload size for the node at index i to be szPayload.
+*/
+static int jsonBlobChangePayloadSize(
+ JsonParse *pParse,
+ u32 i,
+ u32 szPayload
+){
+ u8 *a;
+ u8 szType;
+ u8 nExtra;
+ u8 nNeeded;
+ int delta;
+ if( pParse->oom ) return 0;
+ a = &pParse->aBlob[i];
+ szType = a[0]>>4;
+ if( szType<=11 ){
+ nExtra = 0;
+ }else if( szType==12 ){
+ nExtra = 1;
+ }else if( szType==13 ){
+ nExtra = 2;
+ }else{
+ nExtra = 4;
+ }
+ if( szPayload<=11 ){
+ nNeeded = 0;
+ }else if( szPayload<=0xff ){
+ nNeeded = 1;
+ }else if( szPayload<=0xffff ){
+ nNeeded = 2;
+ }else{
+ nNeeded = 4;
+ }
+ delta = nNeeded - nExtra;
+ if( delta ){
+ u32 newSize = pParse->nBlob + delta;
+ if( delta>0 ){
+ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
+ return 0; /* OOM error. Error state recorded in pParse->oom. */
+ }
+ a = &pParse->aBlob[i];
+ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
+ }else{
+ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
+ }
+ pParse->nBlob = newSize;
+ }
+ if( nNeeded==0 ){
+ a[0] = (a[0] & 0x0f) | (szPayload<<4);
+ }else if( nNeeded==1 ){
+ a[0] = (a[0] & 0x0f) | 0xc0;
+ a[1] = szPayload & 0xff;
+ }else if( nNeeded==2 ){
+ a[0] = (a[0] & 0x0f) | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ }else{
+ a[0] = (a[0] & 0x0f) | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ }
+ return delta;
+}
+
+/*
+** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
+** then set *pOp to JSONB_TEXTJ and return true. If not, do not make
+** any changes to *pOp and return false.
+*/
+static int jsonIs4HexB(const char *z, int *pOp){
+ if( z[0]!='u' ) return 0;
+ if( !jsonIs4Hex(&z[1]) ) return 0;
+ *pOp = JSONB_TEXTJ;
+ return 1;
+}
+
+/*
+** Check a single element of the JSONB in pParse for validity.
+**
+** The element to be checked starts at offset i and must end at on the
+** last byte before iEnd.
+**
+** Return 0 if everything is correct. Return the 1-based byte offset of the
+** error if a problem is detected. (In other words, if the error is at offset
+** 0, return 1).
+*/
+static u32 jsonbValidityCheck(
+ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */
+ u32 i, /* Start of element as pParse->aBlob[i] */
+ u32 iEnd, /* One more than the last byte of the element */
+ u32 iDepth /* Current nesting depth */
+){
+ u32 n, sz, j, k;
+ const u8 *z;
+ u8 x;
+ if( iDepth>JSON_MAX_DEPTH ) return i+1;
+ sz = 0;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( NEVER(n==0) ) return i+1; /* Checked by caller */
+ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */
+ z = pParse->aBlob;
+ x = z[i] & 0x0f;
+ switch( x ){
+ case JSONB_NULL:
+ case JSONB_TRUE:
+ case JSONB_FALSE: {
+ return n+sz==1 ? 0 : i+1;
+ }
+ case JSONB_INT: {
+ if( sz<1 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<2 ) return i+1;
+ }
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_INT5: {
+ if( sz<3 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ if( sz<4 ) return i+1;
+ j++;
+ }
+ if( z[j]!='0' ) return i+1;
+ if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
+ j += 2;
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isxdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_FLOAT:
+ case JSONB_FLOAT5: {
+ u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */
+ if( sz<2 ) return i+1;
+ j = i+n;
+ k = j+sz;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<3 ) return i+1;
+ }
+ if( z[j]=='.' ){
+ if( x==JSONB_FLOAT ) return j+1;
+ if( !sqlite3Isdigit(z[j+1]) ) return j+1;
+ j += 2;
+ seen = 1;
+ }else if( z[j]=='0' && x==JSONB_FLOAT ){
+ if( j+3>k ) return j+1;
+ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
+ j++;
+ }
+ for(; j<k; j++){
+ if( sqlite3Isdigit(z[j]) ) continue;
+ if( z[j]=='.' ){
+ if( seen>0 ) return j+1;
+ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
+ return j+1;
+ }
+ seen = 1;
+ continue;
+ }
+ if( z[j]=='e' || z[j]=='E' ){
+ if( seen==2 ) return j+1;
+ if( j==k-1 ) return j+1;
+ if( z[j+1]=='+' || z[j+1]=='-' ){
+ j++;
+ if( j==k-1 ) return j+1;
+ }
+ seen = 2;
+ continue;
+ }
+ return j+1;
+ }
+ if( seen==0 ) return i+1;
+ return 0;
+ }
+ case JSONB_TEXT: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTJ:
+ case JSONB_TEXT5: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ){
+ if( z[j]=='"' ){
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( z[j]<=0x1f ){
+ /* Control characters in JSON5 string literals are ok */
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( NEVER(z[j]!='\\') || j+1>=k ){
+ return j+1;
+ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
+ j++;
+ }else if( z[j+1]=='u' ){
+ if( j+5>=k ) return j+1;
+ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
+ j++;
+ }else if( x!=JSONB_TEXT5 ){
+ return j+1;
+ }else{
+ u32 c = 0;
+ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
+ if( c==JSON_INVALID_CHAR ) return j+1;
+ j += szC - 1;
+ }
+ }
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTRAW: {
+ return 0;
+ }
+ case JSONB_ARRAY: {
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ j += n + sz;
+ }
+ assert( j==k );
+ return 0;
+ }
+ case JSONB_OBJECT: {
+ u32 cnt = 0;
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ if( (cnt & 1)==0 ){
+ x = z[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
+ }
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ cnt++;
+ j += n + sz;
+ }
+ assert( j==k );
+ if( (cnt & 1)!=0 ) return j+1;
+ return 0;
+ }
+ default: {
+ return i+1;
+ }
+ }
+}
+
+/*
+** Translate a single element of JSON text at pParse->zJson[i] into
+** its equivalent binary JSONB representation. Append the translation into
+** pParse->aBlob[] beginning at pParse->nBlob. The size of
+** pParse->aBlob[] is increased as necessary.
+**
+** Return the index of the first character past the end of the element parsed,
+** or one of the following special result codes:
**
** 0 End of input
-** -1 Syntax error
-** -2 '}' seen
-** -3 ']' seen
-** -4 ',' seen
-** -5 ':' seen
+** -1 Syntax error or OOM
+** -2 '}' seen \
+** -3 ']' seen \___ For these returns, pParse->iErr is set to
+** -4 ',' seen / the index in zJson[] of the seen character
+** -5 ':' seen /
*/
-static int jsonParseValue(JsonParse *pParse, u32 i){
+static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
char c;
u32 j;
- int iThis;
+ u32 iThis, iStart;
int x;
- JsonNode *pNode;
+ u8 t;
const char *z = pParse->zJson;
json_parse_restart:
switch( (u8)z[i] ){
case '{': {
/* Parse object */
- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
+ iStart = pParse->nBlob;
for(j=i+1;;j++){
- u32 nNode = pParse->nNode;
- x = jsonParseValue(pParse, j);
+ u32 iBlob = pParse->nBlob;
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
+ int op;
if( x==(-2) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
break;
}
j += json5Whitespace(&z[j]);
- if( sqlite3JsonId1(z[j])
- || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
+ op = JSONB_TEXT;
+ if( sqlite3JsonId1(z[j])
+ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
){
int k = j+1;
while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
- || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
+ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
){
k++;
}
- jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]);
+ assert( iBlob==pParse->nBlob );
+ jsonBlobAppendNode(pParse, op, k-j, &z[j]);
pParse->hasNonstd = 1;
x = k;
}else{
@@ -1376,24 +1559,24 @@ json_parse_restart:
}
}
if( pParse->oom ) return -1;
- pNode = &pParse->aNode[nNode];
- if( pNode->eType!=JSON_STRING ){
+ t = pParse->aBlob[iBlob] & 0x0f;
+ if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
pParse->iErr = j;
return -1;
}
- pNode->jnFlags |= JNODE_LABEL;
j = x;
if( z[j]==':' ){
j++;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ /* strspn() is not helpful here */
+ do{ j++; }while( jsonIsspace(z[j]) );
if( z[j]==':' ){
j++;
goto parse_object_value;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x!=(-5) ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -1401,7 +1584,7 @@ json_parse_restart:
j = pParse->iErr+1;
}
parse_object_value:
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -1412,15 +1595,15 @@ json_parse_restart:
}else if( z[j]=='}' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -1433,25 +1616,27 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '[': {
/* Parse array */
- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ assert( i<=(u32)pParse->nJson );
+ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
+ iStart = pParse->nBlob;
+ if( pParse->oom ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
- memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
for(j=i+1;;j++){
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x==(-3) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
break;
}
if( x!=(-1) ) pParse->iErr = j;
@@ -1463,15 +1648,15 @@ json_parse_restart:
}else if( z[j]==']' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -1484,23 +1669,33 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '\'': {
- u8 jnFlags;
+ u8 opcode;
char cDelim;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ opcode = JSONB_TEXT;
goto parse_string;
case '"':
/* Parse string */
- jnFlags = 0;
+ opcode = JSONB_TEXT;
parse_string:
cDelim = z[i];
- for(j=i+1; 1; j++){
- if( jsonIsOk[(unsigned char)z[j]] ) continue;
+ j = i+1;
+ while( 1 /*exit-by-break*/ ){
+ if( jsonIsOk[(u8)z[j]] ){
+ if( !jsonIsOk[(u8)z[j+1]] ){
+ j += 1;
+ }else if( !jsonIsOk[(u8)z[j+2]] ){
+ j += 2;
+ }else{
+ j += 3;
+ continue;
+ }
+ }
c = z[j];
if( c==cDelim ){
break;
@@ -1509,33 +1704,41 @@ json_parse_restart:
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
- jnFlags |= JNODE_ESCAPE;
+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
}else if( c=='\'' || c=='0' || c=='v' || c=='\n'
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else if( c=='\r' ){
if( z[j+1]=='\n' ) j++;
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else{
pParse->iErr = j;
return -1;
}
}else if( c<=0x1f ){
- /* Control characters are not allowed in strings */
- pParse->iErr = j;
- return -1;
+ if( c==0 ){
+ pParse->iErr = j;
+ return -1;
+ }
+ /* Control characters are not allowed in canonical JSON string
+ ** literals, but are allowed in JSON5 string literals. */
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
+ }else if( c=='"' ){
+ opcode = JSONB_TEXT5;
}
+ j++;
}
- jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]);
+ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
return j+1;
}
case 't': {
if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_TRUE);
return i+4;
}
pParse->iErr = i;
@@ -1543,23 +1746,22 @@ json_parse_restart:
}
case 'f': {
if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
- jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_FALSE);
return i+5;
}
pParse->iErr = i;
return -1;
}
case '+': {
- u8 seenDP, seenE, jnFlags;
+ u8 seenE;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
goto parse_number;
case '.':
if( sqlite3Isdigit(z[i+1]) ){
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
seenE = 0;
- seenDP = JSON_REAL;
goto parse_number_2;
}
pParse->iErr = i;
@@ -1576,9 +1778,8 @@ json_parse_restart:
case '8':
case '9':
/* Parse number */
- jnFlags = 0;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
parse_number:
- seenDP = JSON_INT;
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
@@ -1588,9 +1789,9 @@ json_parse_restart:
if( c<='0' ){
if( c=='0' ){
if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
- assert( seenDP==JSON_INT );
+ assert( t==0x00 );
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t = 0x01;
for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}else if( sqlite3Isdigit(z[i+1]) ){
@@ -1607,15 +1808,15 @@ json_parse_restart:
){
pParse->hasNonstd = 1;
if( z[i]=='-' ){
- jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
}else{
- jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}
return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
}
if( z[i+1]=='.' ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
goto parse_number_2;
}
pParse->iErr = i;
@@ -1627,30 +1828,31 @@ json_parse_restart:
return -1;
}else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}
}
}
}
+
parse_number_2:
for(j=i+1;; j++){
c = z[j];
if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
- if( seenDP==JSON_REAL ){
+ if( (t & 0x02)!=0 ){
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
@@ -1660,7 +1862,7 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
@@ -1678,14 +1880,18 @@ json_parse_restart:
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
}
}
parse_number_finish:
- jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]);
+ assert( JSONB_INT+0x01==JSONB_INT5 );
+ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
+ assert( JSONB_INT+0x02==JSONB_FLOAT );
+ if( z[i]=='+' ) i++;
+ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
return j;
}
case '}': {
@@ -1711,9 +1917,7 @@ json_parse_restart:
case 0x0a:
case 0x0d:
case 0x20: {
- do{
- i++;
- }while( fast_isspace(z[i]) );
+ i += 1 + (u32)strspn(&z[i+1], jsonSpaces);
goto json_parse_restart;
}
case 0x0b:
@@ -1735,10 +1939,11 @@ json_parse_restart:
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
return i+4;
}
/* fall-through into the default case that checks for NaN */
+ /* no break */ deliberate_fall_through
}
default: {
u32 k;
@@ -1751,8 +1956,11 @@ json_parse_restart:
continue;
}
if( sqlite3Isalnum(z[i+nn]) ) continue;
- jsonParseAddNode(pParse, aNanInfName[k].eType,
- aNanInfName[k].nRepl, aNanInfName[k].zRepl);
+ if( aNanInfName[k].eType==JSONB_FLOAT ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ }
pParse->hasNonstd = 1;
return i + nn;
}
@@ -1762,6 +1970,7 @@ json_parse_restart:
} /* End switch(z[i]) */
}
+
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory held by pParse,
@@ -1770,20 +1979,26 @@ json_parse_restart:
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
-static int jsonParse(
+static int jsonConvertTextToBlob(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
sqlite3_context *pCtx /* Report errors here */
){
int i;
const char *zJson = pParse->zJson;
- i = jsonParseValue(pParse, 0);
+ i = jsonTranslateTextToBlob(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
+#ifdef SQLITE_DEBUG
assert( pParse->iDepth==0 );
- while( fast_isspace(zJson[i]) ) i++;
+ if( sqlite3Config.bJsonSelfcheck ){
+ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
+ }
+#endif
+ while( jsonIsspace(zJson[i]) ) i++;
if( zJson[i] ){
i += json5Whitespace(&zJson[i]);
if( zJson[i] ){
+ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
jsonParseReset(pParse);
return 1;
}
@@ -1804,248 +2019,726 @@ static int jsonParse(
return 0;
}
+/*
+** The input string pStr is a well-formed JSON text string. Convert
+** this into the JSONB format and make it the return value of the
+** SQL function.
+*/
+static void jsonReturnStringAsBlob(JsonString *pStr){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ jsonStringTerminate(pStr);
+ if( pStr->eErr ){
+ sqlite3_result_error_nomem(pStr->pCtx);
+ return;
+ }
+ px.zJson = pStr->zBuf;
+ px.nJson = pStr->nUsed;
+ px.db = sqlite3_context_db_handle(pStr->pCtx);
+ (void)jsonTranslateTextToBlob(&px, 0);
+ if( px.oom ){
+ sqlite3DbFree(px.db, px.aBlob);
+ sqlite3_result_error_nomem(pStr->pCtx);
+ }else{
+ assert( px.nBlobAlloc>0 );
+ assert( !px.bReadOnly );
+ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
+ }
+}
-/* Mark node i of pParse as being a child of iParent. Call recursively
-** to fill in all the descendants of node i.
+/* The byte at index i is a node type-code. This routine
+** determines the payload size for that node and writes that
+** payload size in to *pSz. It returns the offset from i to the
+** beginning of the payload. Return 0 on error.
*/
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
- JsonNode *pNode = &pParse->aNode[i];
- u32 j;
- pParse->aUp[i] = iParent;
- switch( pNode->eType ){
- case JSON_ARRAY: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
- jsonParseFillInParentage(pParse, i+j, i);
+static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
+ u8 x;
+ u32 sz;
+ u32 n;
+ if( NEVER(i>pParse->nBlob) ){
+ *pSz = 0;
+ return 0;
+ }
+ x = pParse->aBlob[i]>>4;
+ if( x<=11 ){
+ sz = x;
+ n = 1;
+ }else if( x==12 ){
+ if( i+1>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = pParse->aBlob[i+1];
+ n = 2;
+ }else if( x==13 ){
+ if( i+2>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
+ n = 3;
+ }else if( x==14 ){
+ if( i+4>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
+ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
+ n = 5;
+ }else{
+ if( i+8>=pParse->nBlob
+ || pParse->aBlob[i+1]!=0
+ || pParse->aBlob[i+2]!=0
+ || pParse->aBlob[i+3]!=0
+ || pParse->aBlob[i+4]!=0
+ ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
+ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
+ n = 9;
+ }
+ if( (i64)i+sz+n > pParse->nBlob
+ && (i64)i+sz+n > pParse->nBlob-pParse->delta
+ ){
+ sz = 0;
+ n = 0;
+ }
+ *pSz = sz;
+ return n;
+}
+
+
+/*
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
+*/
+static u32 jsonTranslateBlobToText(
+ const JsonParse *pParse, /* the complete parse of the JSON */
+ u32 i, /* Start rendering at this index */
+ JsonString *pOut /* Write JSON here */
+){
+ u32 sz, n, j, iEnd;
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ jsonAppendRawNZ(pOut, "null", 4);
+ return i+1;
+ }
+ case JSONB_TRUE: {
+ jsonAppendRawNZ(pOut, "true", 4);
+ return i+1;
+ }
+ case JSONB_FALSE: {
+ jsonAppendRawNZ(pOut, "false", 5);
+ return i+1;
+ }
+ case JSONB_INT:
+ case JSONB_FLOAT: {
+ if( sz==0 ) goto malformed_jsonb;
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_INT5: { /* Integer literal in hexadecimal notation */
+ u32 k = 2;
+ sqlite3_uint64 u = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ int bOverflow = 0;
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }else if( zIn[0]=='+' ){
+ k++;
+ }
+ for(; k<sz; k++){
+ if( !sqlite3Isxdigit(zIn[k]) ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }else if( (u>>60)!=0 ){
+ bOverflow = 1;
+ }else{
+ u = u*16 + sqlite3HexToInt(zIn[k]);
+ }
}
+ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
break;
}
- case JSON_OBJECT: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
- pParse->aUp[i+j] = i;
- jsonParseFillInParentage(pParse, i+j+1, i);
+ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
+ u32 k = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
}
+ if( zIn[k]=='.' ){
+ jsonAppendChar(pOut, '0');
+ }
+ for(; k<sz; k++){
+ jsonAppendChar(pOut, zIn[k]);
+ if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
+ jsonAppendChar(pOut, '0');
+ }
+ }
+ break;
+ }
+ case JSONB_TEXT:
+ case JSONB_TEXTJ: {
+ jsonAppendChar(pOut, '"');
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXT5: {
+ const char *zIn;
+ u32 k;
+ u32 sz2 = sz;
+ zIn = (const char*)&pParse->aBlob[i+n];
+ jsonAppendChar(pOut, '"');
+ while( sz2>0 ){
+ for(k=0; k<sz2 && (jsonIsOk[(u8)zIn[k]] || zIn[k]=='\''); k++){}
+ if( k>0 ){
+ jsonAppendRawNZ(pOut, zIn, k);
+ if( k>=sz2 ){
+ break;
+ }
+ zIn += k;
+ sz2 -= k;
+ }
+ if( zIn[0]=='"' ){
+ jsonAppendRawNZ(pOut, "\\\"", 2);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ if( zIn[0]<=0x1f ){
+ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break;
+ jsonAppendControlChar(pOut, zIn[0]);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ assert( zIn[0]=='\\' );
+ assert( sz2>=1 );
+ if( sz2<2 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ switch( (u8)zIn[1] ){
+ case '\'':
+ jsonAppendChar(pOut, '\'');
+ break;
+ case 'v':
+ jsonAppendRawNZ(pOut, "\\u0009", 6);
+ break;
+ case 'x':
+ if( sz2<4 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ jsonAppendRawNZ(pOut, "\\u00", 4);
+ jsonAppendRawNZ(pOut, &zIn[2], 2);
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ case '0':
+ jsonAppendRawNZ(pOut, "\\u0000", 6);
+ break;
+ case '\r':
+ if( sz2>2 && zIn[2]=='\n' ){
+ zIn++;
+ sz2--;
+ }
+ break;
+ case '\n':
+ break;
+ case 0xe2:
+ /* '\' followed by either U+2028 or U+2029 is ignored as
+ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
+ ** U+2029 is the same except for the last byte */
+ if( sz2<4
+ || 0x80!=(u8)zIn[2]
+ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
+ ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ default:
+ jsonAppendRawNZ(pOut, zIn, 2);
+ break;
+ }
+ assert( sz2>=2 );
+ zIn += 2;
+ sz2 -= 2;
+ }
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXTRAW: {
+ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
break;
}
+ case JSONB_ARRAY: {
+ jsonAppendChar(pOut, '[');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, ',');
+ }
+ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, ']');
+ break;
+ }
+ case JSONB_OBJECT: {
+ int x = 0;
+ jsonAppendChar(pOut, '{');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
+ }
+ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, '}');
+ break;
+ }
+
default: {
+ malformed_jsonb:
+ pOut->eErr |= JSTRING_MALFORMED;
break;
}
}
+ return i+n+sz;
+}
+
+/* Return true if the input pJson
+**
+** For performance reasons, this routine does not do a detailed check of the
+** input BLOB to ensure that it is well-formed. Hence, false positives are
+** possible. False negatives should never occur, however.
+*/
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
+ u32 sz, n;
+ const u8 *aBlob;
+ int nBlob;
+ JsonParse s;
+ if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
+ aBlob = sqlite3_value_blob(pJson);
+ nBlob = sqlite3_value_bytes(pJson);
+ if( nBlob<1 ) return 0;
+ if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
+ memset(&s, 0, sizeof(s));
+ s.aBlob = (u8*)aBlob;
+ s.nBlob = nBlob;
+ n = jsonbPayloadSize(&s, 0, &sz);
+ if( n==0 ) return 0;
+ if( sz+n!=(u32)nBlob ) return 0;
+ if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
+ return sz+n==(u32)nBlob;
}
/*
-** Compute the parentage of all nodes in a completed parse.
+** Given that a JSONB_ARRAY object starts at offset i, return
+** the number of entries in that array.
*/
-static int jsonParseFindParents(JsonParse *pParse){
- u32 *aUp;
- assert( pParse->aUp==0 );
- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
- if( aUp==0 ){
- pParse->oom = 1;
- return SQLITE_NOMEM;
+static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
+ u32 n, sz, i, iEnd;
+ u32 k = 0;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ iEnd = iRoot+n+sz;
+ for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
+ n = jsonbPayloadSize(pParse, i, &sz);
}
- jsonParseFillInParentage(pParse, 0, 0);
- return SQLITE_OK;
+ return k;
}
/*
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+** Edit the payload size of the element at iRoot by the amount in
+** pParse->delta.
*/
-#define JSON_CACHE_ID (-429938) /* First cache entry */
-#define JSON_CACHE_SZ 4 /* Max number of cache entries */
+static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
+ u32 sz = 0;
+ u32 nBlob;
+ assert( pParse->delta!=0 );
+ assert( pParse->nBlobAlloc >= pParse->nBlob );
+ nBlob = pParse->nBlob;
+ pParse->nBlob = pParse->nBlobAlloc;
+ (void)jsonbPayloadSize(pParse, iRoot, &sz);
+ pParse->nBlob = nBlob;
+ sz += pParse->delta;
+ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
+}
/*
-** Obtain a complete parse of the JSON found in the pJson argument
-**
-** Use the sqlite3_get_auxdata() cache to find a preexisting parse
-** if it is available. If the cache is not available or if it
-** is no longer valid, parse the JSON again and return the new parse.
-** Also register the new parse so that it will be available for
-** future sqlite3_get_auxdata() calls.
-**
-** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
-** and return NULL.
-**
-** The returned pointer (if it is not NULL) is owned by the cache in
-** most cases, not the caller. The caller does NOT need to invoke
-** jsonParseFree(), in most cases.
+** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
+** content beginning at iDel, and replacing them with nIns bytes of
+** content given by aIns.
**
-** Except, if an error occurs and pErrCtx==0 then return the JsonParse
-** object with JsonParse.nErr non-zero and the caller will own the JsonParse
-** object. In that case, it will be the responsibility of the caller to
-** invoke jsonParseFree(). To summarize:
+** nDel may be zero, in which case no bytes are removed. But iDel is
+** still important as new bytes will be insert beginning at iDel.
**
-** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the
-** cache. Call does not need to
-** free it.
+** aIns may be zero, in which case space is created to hold nIns bytes
+** beginning at iDel, but that space is uninitialized.
**
-** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller
-** and so the caller must free it.
+** Set pParse->oom if an OOM occurs.
*/
-static JsonParse *jsonParseCached(
- sqlite3_context *pCtx, /* Context to use for cache search */
- sqlite3_value *pJson, /* Function param containing JSON text */
- sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */
- int bUnedited /* No prior edits allowed */
+static void jsonBlobEdit(
+ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */
+ u32 iDel, /* First byte to be removed */
+ u32 nDel, /* Number of bytes to remove */
+ const u8 *aIns, /* Content to insert */
+ u32 nIns /* Bytes of content to insert */
){
- char *zJson = (char*)sqlite3_value_text(pJson);
- int nJson = sqlite3_value_bytes(pJson);
- JsonParse *p;
- JsonParse *pMatch = 0;
- int iKey;
- int iMinKey = 0;
- u32 iMinHold = 0xffffffff;
- u32 iMaxHold = 0;
- int bJsonRCStr;
+ i64 d = (i64)nIns - (i64)nDel;
+ if( d!=0 ){
+ if( pParse->nBlob + d > pParse->nBlobAlloc ){
+ jsonBlobExpand(pParse, pParse->nBlob+d);
+ if( pParse->oom ) return;
+ }
+ memmove(&pParse->aBlob[iDel+nIns],
+ &pParse->aBlob[iDel+nDel],
+ pParse->nBlob - (iDel+nDel));
+ pParse->nBlob += d;
+ pParse->delta += d;
+ }
+ if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+}
- if( zJson==0 ) return 0;
- for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
- if( p==0 ){
- iMinKey = iKey;
- break;
+/*
+** Return the number of escaped newlines to be ignored.
+** An escaped newline is a one of the following byte sequences:
+**
+** 0x5c 0x0a
+** 0x5c 0x0d
+** 0x5c 0x0d 0x0a
+** 0x5c 0xe2 0x80 0xa8
+** 0x5c 0xe2 0x80 0xa9
+*/
+static u32 jsonBytesToBypass(const char *z, u32 n){
+ u32 i = 0;
+ while( i+1<n ){
+ if( z[i]!='\\' ) return i;
+ if( z[i+1]=='\n' ){
+ i += 2;
+ continue;
}
- if( pMatch==0
- && p->nJson==nJson
- && (p->hasMod==0 || bUnedited==0)
- && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
- ){
- p->nErr = 0;
- p->useMod = 0;
- pMatch = p;
- }else
- if( pMatch==0
- && p->zAlt!=0
- && bUnedited==0
- && p->nAlt==nJson
- && memcmp(p->zAlt, zJson, nJson)==0
+ if( z[i+1]=='\r' ){
+ if( i+2<n && z[i+2]=='\n' ){
+ i += 3;
+ }else{
+ i += 2;
+ }
+ continue;
+ }
+ if( 0xe2==(u8)z[i+1]
+ && i+3<n
+ && 0x80==(u8)z[i+2]
+ && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
){
- p->nErr = 0;
- p->useMod = 1;
- pMatch = p;
- }else if( p->iHold<iMinHold ){
- iMinHold = p->iHold;
- iMinKey = iKey;
- }
- if( p->iHold>iMaxHold ){
- iMaxHold = p->iHold;
- }
- }
- if( pMatch ){
- /* The input JSON text was found in the cache. Use the preexisting
- ** parse of this JSON */
- pMatch->nErr = 0;
- pMatch->iHold = iMaxHold+1;
- assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
- return pMatch;
- }
-
- /* The input JSON was not found anywhere in the cache. We will need
- ** to parse it ourselves and generate a new JsonParse object.
- */
- bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
- p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
- if( p==0 ){
- sqlite3_result_error_nomem(pCtx);
- return 0;
+ i += 4;
+ continue;
+ }
+ break;
}
- memset(p, 0, sizeof(*p));
- if( bJsonRCStr ){
- p->zJson = sqlite3RCStrRef(zJson);
- p->bJsonIsRCStr = 1;
- }else{
- p->zJson = (char*)&p[1];
- memcpy(p->zJson, zJson, nJson+1);
+ return i;
+}
+
+/*
+** Input z[0..n] defines JSON escape sequence including the leading '\\'.
+** Decode that escape sequence into a single character. Write that
+** character into *piOut. Return the number of bytes in the escape sequence.
+**
+** If there is a syntax error of some kind (for example too few characters
+** after the '\\' to complete the encoding) then *piOut is set to
+** JSON_INVALID_CHAR.
+*/
+static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
+ assert( n>0 );
+ assert( z[0]=='\\' );
+ if( n<2 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
}
- p->nJPRef = 1;
- if( jsonParse(p, pErrCtx) ){
- if( pErrCtx==0 ){
- p->nErr = 1;
- assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
- return p;
+ switch( (u8)z[1] ){
+ case 'u': {
+ u32 v, vlo;
+ if( n<6 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ v = jsonHexToInt4(&z[2]);
+ if( (v & 0xfc00)==0xd800
+ && n>=12
+ && z[6]=='\\'
+ && z[7]=='u'
+ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
+ ){
+ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
+ return 12;
+ }else{
+ *piOut = v;
+ return 6;
+ }
+ }
+ case 'b': { *piOut = '\b'; return 2; }
+ case 'f': { *piOut = '\f'; return 2; }
+ case 'n': { *piOut = '\n'; return 2; }
+ case 'r': { *piOut = '\r'; return 2; }
+ case 't': { *piOut = '\t'; return 2; }
+ case 'v': { *piOut = '\v'; return 2; }
+ case '0': { *piOut = 0; return 2; }
+ case '\'':
+ case '"':
+ case '/':
+ case '\\':{ *piOut = z[1]; return 2; }
+ case 'x': {
+ if( n<4 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
+ return 4;
+ }
+ case 0xe2:
+ case '\r':
+ case '\n': {
+ u32 nSkip = jsonBytesToBypass(z, n);
+ if( nSkip==0 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }else if( nSkip==n ){
+ *piOut = 0;
+ return n;
+ }else if( z[nSkip]=='\\' ){
+ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
+ }else{
+ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
+ return nSkip + sz;
+ }
+ }
+ default: {
+ *piOut = JSON_INVALID_CHAR;
+ return 2;
}
- jsonParseFree(p);
- return 0;
}
- p->nJson = nJson;
- p->iHold = iMaxHold+1;
- /* Transfer ownership of the new JsonParse to the cache */
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
- (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
+
/*
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
-** a match.
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ.
+**
+** In this version, we know that one or the other or both of the
+** two comparands contains an escape sequence.
*/
-static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->n!=nKey ) return 0;
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
- }else{
- if( pNode->n!=nKey+2 ) return 0;
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+static SQLITE_NOINLINE int jsonLabelCompareEscaped(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ u32 cLeft, cRight;
+ assert( rawLeft==0 || rawRight==0 );
+ while( 1 /*exit-by-return*/ ){
+ if( nLeft==0 ){
+ cLeft = 0;
+ }else if( rawLeft || zLeft[0]!='\\' ){
+ cLeft = ((u8*)zLeft)[0];
+ if( cLeft>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
+ zLeft += sz;
+ nLeft -= sz;
+ }else{
+ zLeft++;
+ nLeft--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
+ zLeft += n;
+ assert( n<=nLeft );
+ nLeft -= n;
+ }
+ if( nRight==0 ){
+ cRight = 0;
+ }else if( rawRight || zRight[0]!='\\' ){
+ cRight = ((u8*)zRight)[0];
+ if( cRight>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
+ zRight += sz;
+ nRight -= sz;
+ }else{
+ zRight++;
+ nRight--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
+ zRight += n;
+ assert( n<=nRight );
+ nRight -= n;
+ }
+ if( cLeft!=cRight ) return 0;
+ if( cLeft==0 ) return 1;
}
}
-static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
- if( p1->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
- }else if( p2->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
+
+/*
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ. Return -1 if an OOM occurs.
+*/
+static int jsonLabelCompare(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ if( rawLeft && rawRight ){
+ /* Simpliest case: Neither label contains escapes. A simple
+ ** memcmp() is sufficient. */
+ if( nLeft!=nRight ) return 0;
+ return memcmp(zLeft, zRight, nLeft)==0;
}else{
- return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
+ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
+ zRight, nRight, rawRight);
}
}
-/* forward declaration */
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+/*
+** Error returns from jsonLookupStep()
+*/
+#define JSON_LOOKUP_ERROR 0xffffffff
+#define JSON_LOOKUP_NOTFOUND 0xfffffffe
+#define JSON_LOOKUP_PATHERROR 0xfffffffd
+#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
+
+/* Forward declaration */
+static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);
+
+
+/* This helper routine for jsonLookupStep() populates pIns with
+** binary data that is to be inserted into pParse.
+**
+** In the common case, pIns just points to pParse->aIns and pParse->nIns.
+** But if the zPath of the original edit operation includes path elements
+** that go deeper, additional substructure must be created.
+**
+** For example:
+**
+** json_insert('{}', '$.a.b.c', 123);
+**
+** The search stops at '$.a' But additional substructure must be
+** created for the ".b.c" part of the patch so that the final result
+** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with
+** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
+**
+** The caller is responsible for resetting pIns when it has finished
+** using the substructure.
+*/
+static u32 jsonCreateEditSubstructure(
+ JsonParse *pParse, /* The original JSONB that is being edited */
+ JsonParse *pIns, /* Populate this with the blob data to insert */
+ const char *zTail /* Tail of the path that determins substructure */
+){
+ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
+ int rc;
+ memset(pIns, 0, sizeof(*pIns));
+ pIns->db = pParse->db;
+ if( zTail[0]==0 ){
+ /* No substructure. Just insert what is given in pParse. */
+ pIns->aBlob = pParse->aIns;
+ pIns->nBlob = pParse->nIns;
+ rc = 0;
+ }else{
+ /* Construct the binary substructure */
+ pIns->nBlob = 1;
+ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
+ pIns->eEdit = pParse->eEdit;
+ pIns->nIns = pParse->nIns;
+ pIns->aIns = pParse->aIns;
+ rc = jsonLookupStep(pIns, 0, zTail, 0);
+ pParse->oom |= pIns->oom;
+ }
+ return rc; /* Error code only */
+}
/*
-** Search along zPath to find the node specified. Return a pointer
-** to that node, or NULL if zPath is malformed or if there is no such
-** node.
+** Search along zPath to find the Json element specified. Return an
+** index into pParse->aBlob[] for the start of that element's value.
**
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
-** possible to do so and if no existing node corresponds to zPath. If
-** new nodes are appended *pApnd is set to 1.
+** If the value found by this routine is the value half of label/value pair
+** within an object, then set pPath->iLabel to the start of the corresponding
+** label, before returning.
+**
+** Return one of the JSON_LOOKUP error codes if problems are seen.
+**
+** This routine will also modify the blob. If pParse->eEdit is one of
+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
+** made to the selected value. If an edit is performed, then the return
+** value does not necessarily point to the select element. If an edit
+** is performed, the return value is only useful for detecting error
+** conditions.
*/
-static JsonNode *jsonLookupStep(
+static u32 jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
- u32 iRoot, /* Begin the search at this node */
+ u32 iRoot, /* Begin the search at this element of aBlob[] */
const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+ u32 iLabel /* Label if iRoot is a value of in an object */
){
- u32 i, j, nKey;
+ u32 i, j, k, nKey, sz, n, iEnd, rc;
const char *zKey;
- JsonNode *pRoot;
- if( pParse->oom ) return 0;
- pRoot = &pParse->aNode[iRoot];
- if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
- while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
- u32 idx = (u32)(pRoot - pParse->aNode);
- i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pRoot = &pParse->aNode[i+1];
- iRoot = i+1;
- break;
+ u8 x;
+
+ if( zPath[0]==0 ){
+ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ sz += n;
+ if( pParse->eEdit==JEDIT_DEL ){
+ if( iLabel>0 ){
+ sz += iRoot - iLabel;
+ iRoot = iLabel;
}
- i = pParse->aNode[i].u.iPrev;
+ jsonBlobEdit(pParse, iRoot, sz, 0, 0);
+ }else if( pParse->eEdit==JEDIT_INS ){
+ /* Already exists, so json_insert() is a no-op */
+ }else{
+ /* json_set() or json_replace() */
+ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
}
}
- if( pRoot->jnFlags & JNODE_REMOVE ){
- return 0;
- }
+ pParse->iLabel = iLabel;
+ return iRoot;
}
- if( zPath[0]==0 ) return pRoot;
if( zPath[0]=='.' ){
- if( pRoot->eType!=JSON_OBJECT ) return 0;
+ int rawKey = 1;
+ x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
@@ -2054,250 +2747,705 @@ static JsonNode *jsonLookupStep(
if( zPath[i] ){
i++;
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
testcase( nKey==0 );
+ rawKey = memchr(zKey, '\\', nKey)==0;
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
if( nKey==0 ){
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
+ }
+ }
+ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ j = iRoot + n; /* j is the index of a label */
+ iEnd = j+sz;
+ while( j<iEnd ){
+ int rawLabel;
+ const char *zLabel;
+ x = pParse->aBlob[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ k = j+n; /* k is the index of the label text */
+ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
+ zLabel = (const char*)&pParse->aBlob[k];
+ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
+ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
+ u32 v = k+sz; /* v is the index of the value */
+ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, v, &sz);
+ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
+ assert( j>0 );
+ rc = jsonLookupStep(pParse, v, &zPath[i], j);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
+ j = k+sz;
+ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
}
- j = 1;
- for(;;){
- while( j<=pRoot->n ){
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( pParse->eEdit>=JEDIT_INS ){
+ u32 nIns; /* Total bytes to insert (label+value) */
+ JsonParse v; /* BLOB encoding of the value to be inserted */
+ JsonParse ix; /* Header of the label to be inserted */
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ memset(&ix, 0, sizeof(ix));
+ ix.db = pParse->db;
+ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
+ pParse->oom |= ix.oom;
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
+ ){
+ assert( !pParse->oom );
+ nIns = ix.nBlob + nKey + v.nBlob;
+ jsonBlobEdit(pParse, j, 0, 0, nIns);
+ if( !pParse->oom ){
+ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
+ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */
+ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
+ k = j + ix.nBlob;
+ memcpy(&pParse->aBlob[k], zKey, nKey);
+ k += nKey;
+ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
+ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
}
- j++;
- j += jsonNodeSize(&pRoot[j]);
- }
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( pApnd ){
- u32 iStart, iLabel;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- zPath += i;
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
- }
- return pNode;
+ }
+ jsonParseReset(&v);
+ jsonParseReset(&ix);
+ return rc;
}
}else if( zPath[0]=='[' ){
- i = 0;
- j = 1;
- while( sqlite3Isdigit(zPath[j]) ){
- i = i*10 + zPath[j] - '0';
- j++;
+ x = pParse->aBlob[iRoot] & 0x0f;
+ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ k = 0;
+ i = 1;
+ while( sqlite3Isdigit(zPath[i]) ){
+ k = k*10 + zPath[i] - '0';
+ i++;
}
- if( j<2 || zPath[j]!=']' ){
+ if( i<2 || zPath[i]!=']' ){
if( zPath[1]=='#' ){
- JsonNode *pBase = pRoot;
- int iBase = iRoot;
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- for(;;){
- while( j<=pBase->n ){
- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
- j += jsonNodeSize(&pBase[j]);
- }
- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pBase->eU==2 );
- iBase = pBase->u.iAppend;
- pBase = &pParse->aNode[iBase];
- j = 1;
- }
- j = 2;
+ k = jsonbArrayCount(pParse, iRoot);
+ i = 2;
if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
- unsigned int x = 0;
- j = 3;
+ unsigned int nn = 0;
+ i = 3;
do{
- x = x*10 + zPath[j] - '0';
- j++;
- }while( sqlite3Isdigit(zPath[j]) );
- if( x>i ) return 0;
- i -= x;
+ nn = nn*10 + zPath[i] - '0';
+ i++;
+ }while( sqlite3Isdigit(zPath[i]) );
+ if( nn>k ) return JSON_LOOKUP_NOTFOUND;
+ k -= nn;
}
- if( zPath[j]!=']' ){
- *pzErr = zPath;
- return 0;
+ if( zPath[i]!=']' ){
+ return JSON_LOOKUP_PATHERROR;
}
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
}
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- zPath += j + 1;
- j = 1;
- for(;;){
- while( j<=pRoot->n
- && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))
+ j = iRoot+n;
+ iEnd = j+sz;
+ while( j<iEnd ){
+ if( k==0 ){
+ rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ k--;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( k>0 ) return JSON_LOOKUP_NOTFOUND;
+ if( pParse->eEdit>=JEDIT_INS ){
+ JsonParse v;
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, v.nBlob)
){
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
- j += jsonNodeSize(&pRoot[j]);
- }
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( j<=pRoot->n ){
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
- }
- if( i==0 && pApnd ){
- u32 iStart;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
- }
- return pNode;
+ assert( !pParse->oom );
+ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
+ }
+ jsonParseReset(&v);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
}else{
- *pzErr = zPath;
+ return JSON_LOOKUP_PATHERROR;
}
- return 0;
+ return JSON_LOOKUP_NOTFOUND;
}
/*
-** Append content to pParse that will complete zPath. Return a pointer
-** to the inserted node, or return NULL if the append fails.
+** Convert a JSON BLOB into text and make that text the return value
+** of an SQL function.
*/
-static JsonNode *jsonLookupAppend(
- JsonParse *pParse, /* Append content to the JSON parse */
- const char *zPath, /* Description of content to append */
- int *pApnd, /* Set this flag to 1 */
- const char **pzErr /* Make this point to any syntax error */
+static void jsonReturnTextJsonFromBlob(
+ sqlite3_context *ctx,
+ const u8 *aBlob,
+ u32 nBlob
){
- *pApnd = 1;
- if( zPath[0]==0 ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
+ JsonParse x;
+ JsonString s;
+
+ if( NEVER(aBlob==0) ) return;
+ memset(&x, 0, sizeof(x));
+ x.aBlob = (u8*)aBlob;
+ x.nBlob = nBlob;
+ jsonStringInit(&s, ctx);
+ jsonTranslateBlobToText(&x, 0, &s);
+ jsonReturnString(&s, 0, 0);
+}
+
+
+/*
+** Return the value of the BLOB node at index i.
+**
+** If the value is a primitive, return it as an SQL value.
+** If the value is an array or object, return it as either
+** JSON text or the BLOB encoding, depending on the JSON_B flag
+** on the userdata.
+*/
+static void jsonReturnFromBlob(
+ JsonParse *pParse, /* Complete JSON parse tree */
+ u32 i, /* Index of the node */
+ sqlite3_context *pCtx, /* Return value for this function */
+ int textOnly /* return text JSON. Disregard user-data */
+){
+ u32 n, sz;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
}
- if( zPath[0]=='.' ){
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- }else if( strncmp(zPath,"[0]",3)==0 ){
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- }else{
- return 0;
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_null(pCtx);
+ break;
+ }
+ case JSONB_TRUE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSONB_FALSE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSONB_INT5:
+ case JSONB_INT: {
+ sqlite3_int64 iRes = 0;
+ char *z;
+ int bNeg = 0;
+ char x;
+ if( sz==0 ) goto returnfromblob_malformed;
+ x = (char)pParse->aBlob[i+n];
+ if( x=='-' ){
+ if( sz<2 ) goto returnfromblob_malformed;
+ n++;
+ sz--;
+ bNeg = 1;
+ }
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3DecOrHexToI64(z, &iRes);
+ sqlite3DbFree(db, z);
+ if( rc==0 ){
+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
+ }else if( rc==3 && bNeg ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ }else if( rc==1 ){
+ goto returnfromblob_malformed;
+ }else{
+ if( bNeg ){ n--; sz++; }
+ goto to_double;
+ }
+ break;
+ }
+ case JSONB_FLOAT5:
+ case JSONB_FLOAT: {
+ double r;
+ char *z;
+ if( sz==0 ) goto returnfromblob_malformed;
+ to_double:
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+ sqlite3DbFree(db, z);
+ if( rc<=0 ) goto returnfromblob_malformed;
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSONB_TEXTRAW:
+ case JSONB_TEXT: {
+ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
+ SQLITE_TRANSIENT);
+ break;
+ }
+ case JSONB_TEXT5:
+ case JSONB_TEXTJ: {
+ /* Translate JSON formatted string into raw text */
+ u32 iIn, iOut;
+ const char *z;
+ char *zOut;
+ u32 nOut = sz;
+ z = (const char*)&pParse->aBlob[i+n];
+ zOut = sqlite3DbMallocRaw(db, nOut+1);
+ if( zOut==0 ) goto returnfromblob_oom;
+ for(iIn=iOut=0; iIn<sz; iIn++){
+ char c = z[iIn];
+ if( c=='\\' ){
+ u32 v;
+ u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
+ if( v<=0x7f ){
+ zOut[iOut++] = (char)v;
+ }else if( v<=0x7ff ){
+ assert( szEscape>=2 );
+ zOut[iOut++] = (char)(0xc0 | (v>>6));
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v<0x10000 ){
+ assert( szEscape>=3 );
+ zOut[iOut++] = 0xe0 | (v>>12);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v==JSON_INVALID_CHAR ){
+ /* Silently ignore illegal unicode */
+ }else{
+ assert( szEscape>=4 );
+ zOut[iOut++] = 0xf0 | (v>>18);
+ zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }
+ iIn += szEscape - 1;
+ }else{
+ zOut[iOut++] = c;
+ }
+ } /* end for() */
+ assert( iOut<=nOut );
+ zOut[iOut] = 0;
+ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
+ break;
+ }
+ case JSONB_ARRAY:
+ case JSONB_OBJECT: {
+ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
+ if( flags & JSON_BLOB ){
+ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
+ }else{
+ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
+ }
+ break;
+ }
+ default: {
+ goto returnfromblob_malformed;
+ }
}
- if( pParse->oom ) return 0;
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+ return;
+
+returnfromblob_oom:
+ sqlite3_result_error_nomem(pCtx);
+ return;
+
+returnfromblob_malformed:
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
}
/*
-** Return the text of a syntax error message on a JSON path. Space is
-** obtained from sqlite3_malloc().
+** pArg is a function argument that might be an SQL value or a JSON
+** value. Figure out what it is and encode it as a JSONB blob.
+** Return the results in pParse.
+**
+** pParse is uninitialized upon entry. This routine will handle the
+** initialization of pParse. The result will be contained in
+** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically
+** allocated (if pParse->nBlobAlloc is greater than zero) in which case
+** the caller is responsible for freeing the space allocated to pParse->aBlob
+** when it has finished with it. Or pParse->aBlob might be a static string
+** or a value obtained from sqlite3_value_blob(pArg).
+**
+** If the argument is a BLOB that is clearly not a JSONB, then this
+** function might set an error message in ctx and return non-zero.
+** It might also set an error message and return non-zero on an OOM error.
*/
-static char *jsonPathSyntaxError(const char *zErr){
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
+static int jsonFunctionArgToBlob(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ JsonParse *pParse
+){
+ int eType = sqlite3_value_type(pArg);
+ static u8 aNull[] = { 0x00 };
+ memset(pParse, 0, sizeof(pParse[0]));
+ pParse->db = sqlite3_context_db_handle(ctx);
+ switch( eType ){
+ default: {
+ pParse->aBlob = aNull;
+ pParse->nBlob = 1;
+ return 0;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(pArg) ){
+ pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
+ pParse->nBlob = sqlite3_value_bytes(pArg);
+ }else{
+ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
+ return 1;
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *zJson = (const char*)sqlite3_value_text(pArg);
+ int nJson = sqlite3_value_bytes(pArg);
+ if( zJson==0 ) return 1;
+ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
+ pParse->zJson = (char*)zJson;
+ pParse->nJson = nJson;
+ if( jsonConvertTextToBlob(pParse, ctx) ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ memset(pParse, 0, sizeof(pParse[0]));
+ return 1;
+ }
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double r = sqlite3_value_double(pArg);
+ if( NEVER(sqlite3IsNaN(r)) ){
+ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);
+ }else{
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ if( z[0]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else if( z[0]=='-' && z[1]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
+ }
+ }
+ break;
+ }
+ case SQLITE_INTEGER: {
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ jsonBlobAppendNode(pParse, JSONB_INT, n, z);
+ break;
+ }
+ }
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return 1;
+ }else{
+ return 0;
+ }
}
/*
-** Do a node lookup using zPath. Return a pointer to the node on success.
-** Return NULL if not found or if there is an error.
+** Generate a bad path error.
**
-** On an error, write an error message into pCtx and increment the
-** pParse->nErr counter.
-**
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
-** nodes are appended.
+** If ctx is not NULL then push the error message into ctx and return NULL.
+** If ctx is NULL, then return the text of the error message.
*/
-static JsonNode *jsonLookup(
- JsonParse *pParse, /* The JSON to search */
- const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- sqlite3_context *pCtx /* Report errors here, if not NULL */
+static char *jsonBadPathError(
+ sqlite3_context *ctx, /* The function call containing the error */
+ const char *zPath /* The path with the problem */
){
- const char *zErr = 0;
- JsonNode *pNode = 0;
- char *zMsg;
-
- if( zPath==0 ) return 0;
- if( zPath[0]!='$' ){
- zErr = zPath;
- goto lookup_err;
- }
- zPath++;
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- if( zErr==0 ) return pNode;
-
-lookup_err:
- pParse->nErr++;
- assert( zErr!=0 && pCtx!=0 );
- zMsg = jsonPathSyntaxError(zErr);
+ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+ if( ctx==0 ) return zMsg;
if( zMsg ){
- sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_result_error(ctx, zMsg, -1);
sqlite3_free(zMsg);
}else{
- sqlite3_result_error_nomem(pCtx);
+ sqlite3_result_error_nomem(ctx);
}
return 0;
}
+/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
+** arguments come in parse where each pair contains a JSON path and
+** content to insert or set at that patch. Do the updates
+** and return the result.
+**
+** The specific operation is determined by eEdit, which can be one
+** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
+*/
+static void jsonInsertIntoBlob(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv,
+ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
+){
+ int i;
+ u32 rc = 0;
+ const char *zPath = 0;
+ int flgs;
+ JsonParse *p;
+ JsonParse ax;
+
+ assert( (argc&1)==1 );
+ flgs = argc==1 ? 0 : JSON_EDITABLE;
+ p = jsonParseFuncArg(ctx, argv[0], flgs);
+ if( p==0 ) return;
+ for(i=1; i<argc-1; i+=2){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ){
+ sqlite3_result_error_nomem(ctx);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
+ if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){
+ jsonParseReset(&ax);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[1]==0 ){
+ if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
+ jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
+ }
+ rc = 0;
+ }else{
+ p->eEdit = eEdit;
+ p->nIns = ax.nBlob;
+ p->aIns = ax.aBlob;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ }
+ jsonParseReset(&ax);
+ if( rc==JSON_LOOKUP_NOTFOUND ) continue;
+ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
+ }
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+jsonInsertIntoBlob_patherror:
+ jsonParseFree(p);
+ if( rc==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ }
+ return;
+}
/*
-** Report the wrong number of arguments for json_insert(), json_replace()
-** or json_set().
+** If pArg is a blob that seems like a JSONB blob, then initialize
+** p to point to that JSONB and return TRUE. If pArg does not seem like
+** a JSONB blob, then return FALSE;
+**
+** This routine is only called if it is already known that pArg is a
+** blob. The only open question is whether or not the blob appears
+** to be a JSONB blob.
*/
-static void jsonWrongNumArgs(
- sqlite3_context *pCtx,
- const char *zFuncName
-){
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
- zFuncName);
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
+static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
+ u32 n, sz = 0;
+ p->aBlob = (u8*)sqlite3_value_blob(pArg);
+ p->nBlob = (u32)sqlite3_value_bytes(pArg);
+ if( p->nBlob==0 ){
+ p->aBlob = 0;
+ return 0;
+ }
+ if( NEVER(p->aBlob==0) ){
+ return 0;
+ }
+ if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ && (n = jsonbPayloadSize(p, 0, &sz))>0
+ && sz+n==p->nBlob
+ && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ ){
+ return 1;
+ }
+ p->aBlob = 0;
+ p->nBlob = 0;
+ return 0;
}
/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
+** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
+** from the SQL function argument pArg. Return a pointer to the new
+** JsonParse object.
+**
+** Ownership of the new JsonParse object is passed to the caller. The
+** caller should invoke jsonParseFree() on the return value when it
+** has finished using it.
+**
+** If any errors are detected, an appropriate error messages is set
+** using sqlite3_result_error() or the equivalent and this routine
+** returns NULL. This routine also returns NULL if the pArg argument
+** is an SQL NULL value, but no error message is set in that case. This
+** is so that SQL functions that are given NULL arguments will return
+** a NULL value.
*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
- int i, n;
- assert( pNode->eType==JSON_OBJECT );
- n = pNode->n;
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
- switch( pNode[i].eType ){
- case JSON_NULL:
- pNode[i].jnFlags |= JNODE_REMOVE;
- break;
- case JSON_OBJECT:
- jsonRemoveAllNulls(&pNode[i]);
- break;
+static JsonParse *jsonParseFuncArg(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ u32 flgs
+){
+ int eType; /* Datatype of pArg */
+ JsonParse *p = 0; /* Value to be returned */
+ JsonParse *pFromCache = 0; /* Value taken from cache */
+ sqlite3 *db; /* The database connection */
+
+ assert( ctx!=0 );
+ eType = sqlite3_value_type(pArg);
+ if( eType==SQLITE_NULL ){
+ return 0;
+ }
+ pFromCache = jsonCacheSearch(ctx, pArg);
+ if( pFromCache ){
+ pFromCache->nJPRef++;
+ if( (flgs & JSON_EDITABLE)==0 ){
+ return pFromCache;
+ }
+ }
+ db = sqlite3_context_db_handle(ctx);
+rebuild_from_cache:
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) goto json_pfa_oom;
+ memset(p, 0, sizeof(*p));
+ p->db = db;
+ p->nJPRef = 1;
+ if( pFromCache!=0 ){
+ u32 nBlob = pFromCache->nBlob;
+ p->aBlob = sqlite3DbMallocRaw(db, nBlob);
+ if( p->aBlob==0 ) goto json_pfa_oom;
+ memcpy(p->aBlob, pFromCache->aBlob, nBlob);
+ p->nBlobAlloc = p->nBlob = nBlob;
+ p->hasNonstd = pFromCache->hasNonstd;
+ jsonParseFree(pFromCache);
+ return p;
+ }
+ if( eType==SQLITE_BLOB ){
+ if( jsonArgIsJsonb(pArg,p) ){
+ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
+ goto json_pfa_oom;
+ }
+ return p;
}
+ /* If the blob is not valid JSONB, fall through into trying to cast
+ ** the blob into text which is then interpreted as JSON. (tag-20240123-a)
+ **
+ ** This goes against all historical documentation about how the SQLite
+ ** JSON functions were suppose to work. From the beginning, blob was
+ ** reserved for expansion and a blob value should have raised an error.
+ ** But it did not, due to a bug. And many applications came to depend
+ ** upon this buggy behavior, espeically when using the CLI and reading
+ ** JSON text using readfile(), which returns a blob. For this reason
+ ** we will continue to support the bug moving forward.
+ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
+ */
}
+ p->zJson = (char*)sqlite3_value_text(pArg);
+ p->nJson = sqlite3_value_bytes(pArg);
+ if( db->mallocFailed ) goto json_pfa_oom;
+ if( p->nJson==0 ) goto json_pfa_malformed;
+ assert( p->zJson!=0 );
+ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ return 0;
+ }
+ }else{
+ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
+ int rc;
+ if( !isRCStr ){
+ char *zNew = sqlite3RCStrNew( p->nJson );
+ if( zNew==0 ) goto json_pfa_oom;
+ memcpy(zNew, p->zJson, p->nJson);
+ p->zJson = zNew;
+ p->zJson[p->nJson] = 0;
+ }else{
+ sqlite3RCStrRef(p->zJson);
+ }
+ p->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, p);
+ if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
+ if( flgs & JSON_EDITABLE ){
+ pFromCache = p;
+ p = 0;
+ goto rebuild_from_cache;
+ }
+ }
+ return p;
+
+json_pfa_malformed:
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ return 0;
+ }
+
+json_pfa_oom:
+ jsonParseFree(pFromCache);
+ jsonParseFree(p);
+ sqlite3_result_error_nomem(ctx);
+ return 0;
}
+/*
+** Make the return value of a JSON function either the raw JSONB blob
+** or make it JSON text, depending on whether the JSON_BLOB flag is
+** set on the function.
+*/
+static void jsonReturnParse(
+ sqlite3_context *ctx,
+ JsonParse *p
+){
+ int flgs;
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return;
+ }
+ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( flgs & JSON_BLOB ){
+ if( p->nBlobAlloc>0 && !p->bReadOnly ){
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
+ p->nBlobAlloc = 0;
+ }else{
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
+ }
+ }else{
+ JsonString s;
+ jsonStringInit(&s, ctx);
+ p->delta = 0;
+ jsonTranslateBlobToText(p, 0, &s);
+ jsonReturnString(&s, p, ctx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+}
/****************************************************************************
** SQL functions used for testing and debugging
@@ -2305,63 +3453,124 @@ static void jsonRemoveAllNulls(JsonNode *pNode){
#if SQLITE_DEBUG
/*
-** Print N node entries.
+** Decode JSONB bytes in aBlob[] starting at iStart through but not
+** including iEnd. Indent the
+** content by nIndent spaces.
*/
-static void jsonDebugPrintNodeEntries(
- JsonNode *aNode, /* First node entry to print */
- int N /* Number of node entries to print */
+static void jsonDebugPrintBlob(
+ JsonParse *pParse, /* JSON content */
+ u32 iStart, /* Start rendering here */
+ u32 iEnd, /* Do not render this byte or any byte after this one */
+ int nIndent, /* Indent by this many spaces */
+ sqlite3_str *pOut /* Generate output into this sqlite3_str object */
){
- int i;
- for(i=0; i<N; i++){
- const char *zType;
- if( aNode[i].jnFlags & JNODE_LABEL ){
- zType = "label";
- }else{
- zType = jsonType[aNode[i].eType];
+ while( iStart<iEnd ){
+ u32 i, n, nn, sz = 0;
+ int showContent = 1;
+ u8 x = pParse->aBlob[iStart] & 0x0f;
+ u32 savedNBlob = pParse->nBlob;
+ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
+ if( pParse->nBlobAlloc>pParse->nBlob ){
+ pParse->nBlob = pParse->nBlobAlloc;
}
- printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
- if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
- u8 f = aNode[i].jnFlags;
- if( f & JNODE_RAW ) printf(" RAW");
- if( f & JNODE_ESCAPE ) printf(" ESCAPE");
- if( f & JNODE_REMOVE ) printf(" REMOVE");
- if( f & JNODE_REPLACE ) printf(" REPLACE");
- if( f & JNODE_APPEND ) printf(" APPEND");
- if( f & JNODE_JSON5 ) printf(" JSON5");
+ nn = n = jsonbPayloadSize(pParse, iStart, &sz);
+ if( nn==0 ) nn = 1;
+ if( sz>0 && x<JSONB_ARRAY ){
+ nn += sz;
}
- switch( aNode[i].eU ){
- case 1: printf(" zJContent=[%.*s]\n",
- aNode[i].n, aNode[i].u.zJContent); break;
- case 2: printf(" iAppend=%u\n", aNode[i].u.iAppend); break;
- case 3: printf(" iKey=%u\n", aNode[i].u.iKey); break;
- case 4: printf(" iPrev=%u\n", aNode[i].u.iPrev); break;
- default: printf("\n");
+ for(i=0; i<nn; i++){
+ sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
}
+ if( n==0 ){
+ sqlite3_str_appendf(pOut, " ERROR invalid node size\n");
+ iStart = n==0 ? iStart+1 : iEnd;
+ continue;
+ }
+ pParse->nBlob = savedNBlob;
+ if( iStart+n+sz>iEnd ){
+ iEnd = iStart+n+sz;
+ if( iEnd>pParse->nBlob ){
+ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
+ iEnd = pParse->nBlobAlloc;
+ }else{
+ iEnd = pParse->nBlob;
+ }
+ }
+ }
+ sqlite3_str_appendall(pOut," <-- ");
+ switch( x ){
+ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break;
+ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break;
+ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break;
+ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break;
+ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break;
+ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break;
+ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break;
+ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break;
+ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break;
+ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break;
+ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break;
+ case JSONB_ARRAY: {
+ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ case JSONB_OBJECT: {
+ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ default: {
+ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
+ showContent = 0;
+ break;
+ }
+ }
+ if( showContent ){
+ if( sz==0 && x<=JSONB_FALSE ){
+ sqlite3_str_append(pOut, "\n", 1);
+ }else{
+ u32 j;
+ sqlite3_str_appendall(pOut, ": \"");
+ for(j=iStart+n; j<iStart+n+sz; j++){
+ u8 c = pParse->aBlob[j];
+ if( c<0x20 || c>=0x7f ) c = '.';
+ sqlite3_str_append(pOut, (char*)&c, 1);
+ }
+ sqlite3_str_append(pOut, "\"\n", 2);
+ }
+ }
+ iStart += n + sz;
}
}
-#endif /* SQLITE_DEBUG */
-
-
-#if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */
-static void jsonDebugPrintParse(JsonParse *p){
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
-}
-static void jsonDebugPrintNode(JsonNode *pNode){
- jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
+static void jsonShowParse(JsonParse *pParse){
+ sqlite3_str out;
+ char zBuf[1000];
+ if( pParse==0 ){
+ printf("NULL pointer\n");
+ return;
+ }else{
+ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
+ printf("nBlob = %u\n", pParse->nBlob);
+ printf("delta = %d\n", pParse->delta);
+ if( pParse->nBlob==0 ) return;
+ printf("content (bytes 0..%u):\n", pParse->nBlob-1);
+ }
+ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
+ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
+ printf("%s", sqlite3_str_value(&out));
+ sqlite3_str_reset(&out);
}
-#else
- /* The usual case */
-# define jsonDebugPrintNode(X)
-# define jsonDebugPrintParse(X)
-#endif
+#endif /* SQLITE_DEBUG */
#ifdef SQLITE_DEBUG
/*
** SQL function: json_parse(JSON)
**
-** Parse JSON using jsonParseCached(). Then print a dump of that
-** parse on standard output. Return the mimified JSON result, just
-** like the json() function.
+** Parse JSON using jsonParseFuncArg(). Return text that is a
+** human-readable dump of the binary JSONB for the input parameter.
*/
static void jsonParseFunc(
sqlite3_context *ctx,
@@ -2369,38 +3578,20 @@ static void jsonParseFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
+ sqlite3_str out;
- assert( argc==1 );
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ assert( argc>=1 );
+ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- printf("nNode = %u\n", p->nNode);
- printf("nAlloc = %u\n", p->nAlloc);
- printf("nJson = %d\n", p->nJson);
- printf("nAlt = %d\n", p->nAlt);
- printf("nErr = %u\n", p->nErr);
- printf("oom = %u\n", p->oom);
- printf("hasNonstd = %u\n", p->hasNonstd);
- printf("useMod = %u\n", p->useMod);
- printf("hasMod = %u\n", p->hasMod);
- printf("nJPRef = %u\n", p->nJPRef);
- printf("iSubst = %u\n", p->iSubst);
- printf("iHold = %u\n", p->iHold);
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
- jsonReturnJson(p, p->aNode, ctx, 1);
-}
-
-/*
-** The json_test1(JSON) function return true (1) if the input is JSON
-** text generated by another json function. It returns (0) if the input
-** is not known to be JSON.
-*/
-static void jsonTest1Func(
- sqlite3_context *ctx,
- int argc,
- sqlite3_value **argv
-){
- UNUSED_PARAMETER(argc);
- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+ if( argc==1 ){
+ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
+ sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8);
+ }else{
+ jsonShowParse(p);
+ }
+ jsonParseFree(p);
+ sqlite3_str_reset(&out);
}
#endif /* SQLITE_DEBUG */
@@ -2409,7 +3600,7 @@ static void jsonTest1Func(
****************************************************************************/
/*
-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
+** Implementation of the json_quote(VALUE) function. Return a JSON value
** corresponding to the SQL value input. Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
@@ -2422,9 +3613,9 @@ static void jsonQuoteFunc(
JsonString jx;
UNUSED_PARAMETER(argc);
- jsonInit(&jx, ctx);
- jsonAppendValue(&jx, argv[0]);
- jsonResult(&jx);
+ jsonStringInit(&jx, ctx);
+ jsonAppendSqlValue(&jx, argv[0]);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -2441,18 +3632,17 @@ static void jsonArrayFunc(
int i;
JsonString jx;
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
- jsonAppendValue(&jx, argv[i]);
+ jsonAppendSqlValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
-
/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
@@ -2466,46 +3656,53 @@ static void jsonArrayLengthFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- sqlite3_int64 n = 0;
+ sqlite3_int64 cnt = 0;
u32 i;
- JsonNode *pNode;
+ u8 eErr = 0;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- }else{
- pNode = p->aNode;
- }
- if( pNode==0 ){
- return;
- }
- if( pNode->eType==JSON_ARRAY ){
- while( 1 /*exit-by-break*/ ){
- i = 1;
- while( i<=pNode->n ){
- if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++;
- i += jsonNodeSize(&pNode[i]);
+ if( zPath==0 ){
+ jsonParseFree(p);
+ return;
+ }
+ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
}
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( p->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &p->aNode[pNode->u.iAppend];
+ eErr = 1;
+ i = 0;
}
+ }else{
+ i = 0;
+ }
+ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
+ cnt = jsonbArrayCount(p, i);
}
- sqlite3_result_int64(ctx, n);
+ if( !eErr ) sqlite3_result_int64(ctx, cnt);
+ jsonParseFree(p);
}
-/*
-** Bit values for the flags passed into jsonExtractFunc() or
-** jsonSetFunc() via the user-data value.
-*/
-#define JSON_JSON 0x01 /* Result is always JSON */
-#define JSON_SQL 0x02 /* Result is always SQL */
-#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
-#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+/* True if the string is all digits */
+static int jsonAllDigits(const char *z, int n){
+ int i;
+ for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
+ return i==n;
+}
+
+/* True if the string is all alphanumerics and underscores */
+static int jsonAllAlphanum(const char *z, int n){
+ int i;
+ for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
+ return i==n;
+}
/*
** json_extract(JSON, PATH, ...)
@@ -2532,152 +3729,307 @@ static void jsonExtractFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
- JsonString jx;
+ JsonParse *p = 0; /* The parse */
+ int flags; /* Flags associated with the function */
+ int i; /* Loop counter */
+ JsonString jx; /* String for array result */
if( argc<2 ) return;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- if( argc==2 ){
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ jsonStringInit(&jx, ctx);
+ if( argc>2 ){
+ jsonAppendChar(&jx, '[');
+ }
+ for(i=1; i<argc; i++){
/* With a single PATH argument */
- zPath = (const char*)sqlite3_value_text(argv[1]);
- if( zPath==0 ) return;
- if( flags & JSON_ABPATH ){
- if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
- /* The -> and ->> operators accept abbreviated PATH arguments. This
- ** is mostly for compatibility with PostgreSQL, but also for
- ** convenience.
- **
- ** NUMBER ==> $[NUMBER] // PG compatible
- ** LABEL ==> $.LABEL // PG compatible
- ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
- */
- jsonInit(&jx, ctx);
- if( sqlite3Isdigit(zPath[0]) ){
- jsonAppendRawNZ(&jx, "$[", 2);
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendRawNZ(&jx, "]", 2);
- }else{
- jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='['));
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendChar(&jx, 0);
- }
- pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx);
- jsonReset(&jx);
+ const char *zPath = (const char*)sqlite3_value_text(argv[i]);
+ int nPath;
+ u32 j;
+ if( zPath==0 ) goto json_extract_error;
+ nPath = sqlite3Strlen30(zPath);
+ if( zPath[0]=='$' ){
+ j = jsonLookupStep(p, 0, zPath+1, 0);
+ }else if( (flags & JSON_ABPATH) ){
+ /* The -> and ->> operators accept abbreviated PATH arguments. This
+ ** is mostly for compatibility with PostgreSQL, but also for
+ ** convenience.
+ **
+ ** NUMBER ==> $[NUMBER] // PG compatible
+ ** LABEL ==> $.LABEL // PG compatible
+ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
+ */
+ jsonStringInit(&jx, ctx);
+ if( jsonAllDigits(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, "[", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "]", 2);
+ }else if( jsonAllAlphanum(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, ".", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
+ jsonAppendRaw(&jx, zPath, nPath);
}else{
- pNode = jsonLookup(p, zPath, 0, ctx);
+ jsonAppendRawNZ(&jx, ".\"", 2);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "\"", 1);
}
- if( pNode ){
+ jsonStringTerminate(&jx);
+ j = jsonLookupStep(p, 0, jx.zBuf, 0);
+ jsonStringReset(&jx);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
+ }
+ if( j<p->nBlob ){
+ if( argc==2 ){
if( flags & JSON_JSON ){
- jsonReturnJson(p, pNode, ctx, 0);
+ jsonStringInit(&jx, ctx);
+ jsonTranslateBlobToText(p, j, &jx);
+ jsonReturnString(&jx, 0, 0);
+ jsonStringReset(&jx);
+ assert( (flags & JSON_BLOB)==0 );
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}else{
- jsonReturn(p, pNode, ctx);
- sqlite3_result_subtype(ctx, 0);
+ jsonReturnFromBlob(p, j, ctx, 0);
+ if( (flags & (JSON_SQL|JSON_BLOB))==0
+ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY
+ ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
}
+ }else{
+ jsonAppendSeparator(&jx);
+ jsonTranslateBlobToText(p, j, &jx);
}
- }else{
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx);
- }
- }else{
- /* Two or more PATH arguments results in a JSON array with each
- ** element of the array being the value selected by one of the PATHs */
- int i;
- jsonInit(&jx, ctx);
- jsonAppendChar(&jx, '[');
- for(i=1; i<argc; i++){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr ) break;
- jsonAppendSeparator(&jx);
- if( pNode ){
- jsonRenderNode(p, pNode, &jx);
+ }else if( j==JSON_LOOKUP_NOTFOUND ){
+ if( argc==2 ){
+ goto json_extract_error; /* Return NULL if not found */
}else{
+ jsonAppendSeparator(&jx);
jsonAppendRawNZ(&jx, "null", 4);
}
+ }else if( j==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ goto json_extract_error;
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
}
- if( i==argc ){
- jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ }
+ if( argc>2 ){
+ jsonAppendChar(&jx, ']');
+ jsonReturnString(&jx, 0, 0);
+ if( (flags & JSON_BLOB)==0 ){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
- jsonReset(&jx);
}
+json_extract_error:
+ jsonStringReset(&jx);
+ jsonParseFree(p);
+ return;
}
-/* This is the RFC 7396 MergePatch algorithm.
+/*
+** Return codes for jsonMergePatch()
*/
-static JsonNode *jsonMergePatch(
- JsonParse *pParse, /* The JSON parser that contains the TARGET */
- u32 iTarget, /* Node of the TARGET in pParse */
- JsonNode *pPatch /* The PATCH */
+#define JSON_MERGE_OK 0 /* Success */
+#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */
+#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */
+#define JSON_MERGE_OOM 3 /* Out-of-memory condition */
+
+/*
+** RFC-7396 MergePatch for two JSONB blobs.
+**
+** pTarget is the target. pPatch is the patch. The target is updated
+** in place. The patch is read-only.
+**
+** The original RFC-7396 algorithm is this:
+**
+** define MergePatch(Target, Patch):
+** if Patch is an Object:
+** if Target is not an Object:
+** Target = {} # Ignore the contents and set it to an empty Object
+** for each Name/Value pair in Patch:
+** if Value is null:
+** if Name exists in Target:
+** remove the Name/Value pair from Target
+** else:
+** Target[Name] = MergePatch(Target[Name], Value)
+** return Target
+** else:
+** return Patch
+**
+** Here is an equivalent algorithm restructured to show the actual
+** implementation:
+**
+** 01 define MergePatch(Target, Patch):
+** 02 if Patch is not an Object:
+** 03 return Patch
+** 04 else: // if Patch is an Object
+** 05 if Target is not an Object:
+** 06 Target = {}
+** 07 for each Name/Value pair in Patch:
+** 08 if Name exists in Target:
+** 09 if Value is null:
+** 10 remove the Name/Value pair from Target
+** 11 else
+** 12 Target[name] = MergePatch(Target[Name], Value)
+** 13 else if Value is not NULL:
+** 14 if Value is not an Object:
+** 15 Target[name] = Value
+** 16 else:
+** 17 Target[name] = MergePatch('{}',value)
+** 18 return Target
+** |
+** ^---- Line numbers referenced in comments in the implementation
+*/
+static int jsonMergePatch(
+ JsonParse *pTarget, /* The JSON parser that contains the TARGET */
+ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */
+ const JsonParse *pPatch, /* The PATCH */
+ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */
){
- u32 i, j;
- u32 iRoot;
- JsonNode *pTarget;
- if( pPatch->eType!=JSON_OBJECT ){
- return pPatch;
- }
- assert( iTarget<pParse->nNode );
- pTarget = &pParse->aNode[iTarget];
- assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
- if( pTarget->eType!=JSON_OBJECT ){
- jsonRemoveAllNulls(pPatch);
- return pPatch;
- }
- iRoot = iTarget;
- for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
- u32 nKey;
- const char *zKey;
- assert( pPatch[i].eType==JSON_STRING );
- assert( pPatch[i].jnFlags & JNODE_LABEL );
- assert( pPatch[i].eU==1 );
- nKey = pPatch[i].n;
- zKey = pPatch[i].u.zJContent;
- for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
- assert( pTarget[j].eType==JSON_STRING );
- assert( pTarget[j].jnFlags & JNODE_LABEL );
- if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){
- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break;
- if( pPatch[i+1].eType==JSON_NULL ){
- pTarget[j+1].jnFlags |= JNODE_REMOVE;
- }else{
- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
- if( pNew==0 ) return 0;
- if( pNew!=&pParse->aNode[iTarget+j+1] ){
- jsonParseAddSubstNode(pParse, iTarget+j+1);
- jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew));
- }
- pTarget = &pParse->aNode[iTarget];
- }
- break;
+ u8 x; /* Type of a single node */
+ u32 n, sz=0; /* Return values from jsonbPayloadSize() */
+ u32 iTCursor; /* Cursor position while scanning the target object */
+ u32 iTStart; /* First label in the target object */
+ u32 iTEndBE; /* Original first byte past end of target, before edit */
+ u32 iTEnd; /* Current first byte past end of target */
+ u8 eTLabel; /* Node type of the target label */
+ u32 iTLabel = 0; /* Index of the label */
+ u32 nTLabel = 0; /* Header size in bytes for the target label */
+ u32 szTLabel = 0; /* Size of the target label payload */
+ u32 iTValue = 0; /* Index of the target value */
+ u32 nTValue = 0; /* Header size of the target value */
+ u32 szTValue = 0; /* Payload size for the target value */
+
+ u32 iPCursor; /* Cursor position while scanning the patch */
+ u32 iPEnd; /* First byte past the end of the patch */
+ u8 ePLabel; /* Node type of the patch label */
+ u32 iPLabel; /* Start of patch label */
+ u32 nPLabel; /* Size of header on the patch label */
+ u32 szPLabel; /* Payload size of the patch label */
+ u32 iPValue; /* Start of patch value */
+ u32 nPValue; /* Header size for the patch value */
+ u32 szPValue; /* Payload size of the patch value */
+
+ assert( iTarget>=0 && iTarget<pTarget->nBlob );
+ assert( iPatch>=0 && iPatch<pPatch->nBlob );
+ x = pPatch->aBlob[iPatch] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */
+ u32 szPatch; /* Total size of the patch, header+payload */
+ u32 szTarget; /* Total size of the target, header+payload */
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ szPatch = n+sz;
+ sz = 0;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ szTarget = n+sz;
+ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */
+ }
+ x = pTarget->aBlob[iTarget] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
+ x = pTarget->aBlob[iTarget];
+ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
+ }
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPatch+n;
+ iPEnd = iPCursor+sz;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
+ iTStart = iTarget+n;
+ iTEndBE = iTStart+sz;
+
+ while( iPCursor<iPEnd ){ /* Algorithm line 07 */
+ iPLabel = iPCursor;
+ ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
+ if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADPATCH;
+ }
+ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
+ if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
+ iPValue = iPCursor + nPLabel + szPLabel;
+ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
+ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
+ if( nPValue==0 ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPValue + nPValue + szPValue;
+ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;
+
+ iTCursor = iTStart;
+ iTEnd = iTEndBE + pTarget->delta;
+ while( iTCursor<iTEnd ){
+ int isEqual; /* true if the patch and target labels match */
+ iTLabel = iTCursor;
+ eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
+ if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADTARGET;
}
+ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
+ if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
+ iTValue = iTLabel + nTLabel + szTLabel;
+ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
+ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
+ if( nTValue==0 ) return JSON_MERGE_BADTARGET;
+ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
+ isEqual = jsonLabelCompare(
+ (const char*)&pPatch->aBlob[iPLabel+nPLabel],
+ szPLabel,
+ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
+ (const char*)&pTarget->aBlob[iTLabel+nTLabel],
+ szTLabel,
+ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
+ if( isEqual ) break;
+ iTCursor = iTValue + nTValue + szTValue;
}
- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
- int iStart;
- JsonNode *pApnd;
- u32 nApnd;
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- pApnd = &pPatch[i+1];
- if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd);
- nApnd = jsonNodeSize(pApnd);
- jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd));
- if( pParse->oom ) return 0;
- pParse->aNode[iStart].n = 1+nApnd;
- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
- pParse->aNode[iRoot].u.iAppend = iStart;
- VVA( pParse->aNode[iRoot].eU = 2 );
- iRoot = iStart;
- pTarget = &pParse->aNode[iTarget];
+ x = pPatch->aBlob[iPValue] & 0x0f;
+ if( iTCursor<iTEnd ){
+ /* A match was found. Algorithm line 08 */
+ if( x==0 ){
+ /* Patch value is NULL. Algorithm line 09 */
+ jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
+ /* vvvvvv----- No OOM on a delete-only edit */
+ if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
+ }else{
+ /* Algorithm line 12 */
+ int rc, savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
+ }
+ }else if( x>0 ){ /* Algorithm line 13 */
+ /* No match and patch value is not NULL */
+ u32 szNew = szPLabel+nPLabel;
+ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ memcpy(&pTarget->aBlob[iTEnd+szNew],
+ &pPatch->aBlob[iPValue], szPValue+nPValue);
+ }else{
+ int rc, savedDelta;
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ pTarget->aBlob[iTEnd+szNew] = 0x00;
+ savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
+ }
}
}
- return pTarget;
+ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}
+
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
@@ -2688,28 +4040,27 @@ static void jsonPatchFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pX; /* The JSON that is being patched */
- JsonParse *pY; /* The patch */
- JsonNode *pResult; /* The result of the merge */
+ JsonParse *pTarget; /* The TARGET */
+ JsonParse *pPatch; /* The PATCH */
+ int rc; /* Result code */
UNUSED_PARAMETER(argc);
- pX = jsonParseCached(ctx, argv[0], ctx, 1);
- if( pX==0 ) return;
- assert( pX->hasMod==0 );
- pX->hasMod = 1;
- pY = jsonParseCached(ctx, argv[1], ctx, 1);
- if( pY==0 ) return;
- pX->useMod = 1;
- pY->useMod = 1;
- pResult = jsonMergePatch(pX, 0, pY->aNode);
- assert( pResult!=0 || pX->oom );
- if( pResult && pX->oom==0 ){
- jsonDebugPrintParse(pX);
- jsonDebugPrintNode(pResult);
- jsonReturnJson(pX, pResult, ctx, 0);
- }else{
- sqlite3_result_error_nomem(ctx);
+ assert( argc==2 );
+ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
+ if( pTarget==0 ) return;
+ pPatch = jsonParseFuncArg(ctx, argv[1], 0);
+ if( pPatch ){
+ rc = jsonMergePatch(pTarget, 0, pPatch, 0);
+ if( rc==JSON_MERGE_OK ){
+ jsonReturnParse(ctx, pTarget);
+ }else if( rc==JSON_MERGE_OOM ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ jsonParseFree(pPatch);
}
+ jsonParseFree(pTarget);
}
@@ -2733,23 +4084,23 @@ static void jsonObjectFunc(
"of arguments", -1);
return;
}
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; i<argc; i+=2){
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
- jsonReset(&jx);
+ jsonStringReset(&jx);
return;
}
jsonAppendSeparator(&jx);
z = (const char*)sqlite3_value_text(argv[i]);
- n = (u32)sqlite3_value_bytes(argv[i]);
+ n = sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
- jsonAppendValue(&jx, argv[i+1]);
+ jsonAppendSqlValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -2765,118 +4116,50 @@ static void jsonRemoveFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
+ JsonParse *p; /* The parse */
+ const char *zPath = 0; /* Path of element to be removed */
+ int i; /* Loop counter */
+ u32 rc; /* Subroutine return code */
if( argc<1 ) return;
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i++){
+ p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
+ if( p==0 ) return;
+ for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
- if( zPath==0 ) goto remove_done;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto remove_done;
- if( pNode ){
- pNode->jnFlags |= JNODE_REMOVE;
- pParse->hasMod = 1;
- pParse->useMod = 1;
- }
- }
- if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
- }
-remove_done:
- jsonDebugPrintParse(p);
-}
-
-/*
-** Substitute the value at iNode with the pValue parameter.
-*/
-static void jsonReplaceNode(
- sqlite3_context *pCtx,
- JsonParse *p,
- int iNode,
- sqlite3_value *pValue
-){
- int idx = jsonParseAddSubstNode(p, iNode);
- if( idx<=0 ){
- assert( p->oom );
- return;
- }
- switch( sqlite3_value_type(pValue) ){
- case SQLITE_NULL: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- break;
+ if( zPath==0 ){
+ goto json_remove_done;
}
- case SQLITE_FLOAT: {
- char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_REAL, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
- break;
+ if( zPath[0]!='$' ){
+ goto json_remove_patherror;
}
- case SQLITE_INTEGER: {
- char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_INT, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
-
- break;
+ if( zPath[1]==0 ){
+ /* json_remove(j,'$') returns NULL */
+ goto json_remove_done;
}
- case SQLITE_TEXT: {
- const char *z = (const char*)sqlite3_value_text(pValue);
- u32 n = (u32)sqlite3_value_bytes(pValue);
- if( z==0 ){
- p->oom = 1;
- break;
- }
- if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){
- char *zCopy = sqlite3DbStrDup(0, z);
- int k;
- if( zCopy ){
- jsonParseAddCleanup(p, sqlite3_free, zCopy);
- }else{
- p->oom = 1;
- sqlite3_result_error_nomem(pCtx);
- }
- k = jsonParseAddNode(p, JSON_STRING, n, zCopy);
- assert( k>0 || p->oom );
- if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW;
+ p->eEdit = JEDIT_DEL;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(rc) ){
+ if( rc==JSON_LOOKUP_NOTFOUND ){
+ continue; /* No-op */
+ }else if( rc==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
}else{
- JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1);
- if( pPatch==0 ){
- p->oom = 1;
- break;
- }
- jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode);
- /* The nodes copied out of pPatch and into p likely contain
- ** u.zJContent pointers into pPatch->zJson. So preserve the
- ** content of pPatch until p is destroyed. */
- assert( pPatch->nJPRef>=1 );
- pPatch->nJPRef++;
- jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch);
+ sqlite3_result_error(ctx, "malformed JSON", -1);
}
- break;
- }
- default: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1);
- p->nErr++;
- break;
+ goto json_remove_done;
}
}
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+json_remove_patherror:
+ jsonBadPathError(ctx, zPath);
+
+json_remove_done:
+ jsonParseFree(p);
+ return;
}
/*
@@ -2890,30 +4173,12 @@ static void jsonReplaceFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
-
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto replace_err;
- if( pNode ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-replace_err:
- jsonDebugPrintParse(pParse);
+ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);
}
@@ -2934,39 +4199,16 @@ static void jsonSetFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
- int bApnd;
- int bIsSet = sqlite3_user_data(ctx)!=0;
+
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ int bIsSet = (flags&JSON_ISSET)!=0;
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- bApnd = 0;
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, &bApnd, ctx);
- if( pParse->oom ){
- sqlite3_result_error_nomem(ctx);
- goto jsonSetDone;
- }else if( pParse->nErr ){
- goto jsonSetDone;
- }else if( pNode && (bApnd || bIsSet) ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonDebugPrintParse(pParse);
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-
-jsonSetDone:
- /* no cleanup required */;
+ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
}
/*
@@ -2982,27 +4224,93 @@ static void jsonTypeFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- const char *zPath;
- JsonNode *pNode;
+ const char *zPath = 0;
+ u32 i;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
+ if( zPath==0 ) goto json_type_done;
+ if( zPath[0]!='$' ){
+ jsonBadPathError(ctx, zPath);
+ goto json_type_done;
+ }
+ i = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_type_done;
+ }
}else{
- pNode = p->aNode;
- }
- if( pNode ){
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ i = 0;
}
+ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
+json_type_done:
+ jsonParseFree(p);
}
/*
** json_valid(JSON)
+** json_valid(JSON, FLAGS)
+**
+** Check the JSON argument to see if it is well-formed. The FLAGS argument
+** encodes the various constraints on what is meant by "well-formed":
+**
+** 0x01 Canonical RFC-8259 JSON text
+** 0x02 JSON text with optional JSON-5 extensions
+** 0x04 Superficially appears to be JSONB
+** 0x08 Strictly well-formed JSONB
+**
+** If the FLAGS argument is omitted, it defaults to 1. Useful values for
+** FLAGS include:
+**
+** 1 Strict canonical JSON text
+** 2 JSON text perhaps with JSON-5 extensions
+** 4 Superficially appears to be JSONB
+** 5 Canonical JSON text or superficial JSONB
+** 6 JSON-5 text or superficial JSONB
+** 8 Strict JSONB
+** 9 Canonical JSON text or strict JSONB
+** 10 JSON-5 text or strict JSONB
+**
+** Other flag combinations are redundant. For example, every canonical
+** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
+** are the same. Similarly, any input that passes a strict JSONB validation
+** will also pass the superficial validation so 12 through 15 are the same
+** as 8 through 11 respectively.
**
-** Return 1 if JSON is a well-formed canonical JSON string according
-** to RFC-7159. Return 0 otherwise.
+** This routine runs in linear time to validate text and when doing strict
+** JSONB validation. Superficial JSONB validation is constant time,
+** assuming the BLOB is already in memory. The performance advantage
+** of superficial JSONB validation is why that option is provided.
+** Application developers can choose to do fast superficial validation or
+** slower strict validation, according to their specific needs.
+**
+** Only the lower four bits of the FLAGS argument are currently used.
+** Higher bits are reserved for future expansion. To facilitate
+** compatibility, the current implementation raises an error if any bit
+** in FLAGS is set other than the lower four bits.
+**
+** The original circa 2015 implementation of the JSON routines in
+** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
+** function only accepted one argument. That is why the default value
+** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
+** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS
+** argument was added when the JSON routines were extended to support
+** JSON5-like extensions and binary JSONB stored in BLOBs.
+**
+** Return Values:
+**
+** * Raise an error if FLAGS is outside the range of 1 to 15.
+** * Return NULL if the input is NULL
+** * Return 1 if the input is well-formed.
+** * Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
@@ -3010,79 +4318,128 @@ static void jsonValidFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
+ u8 flags = 1;
+ u8 res = 0;
+ if( argc==2 ){
+ i64 f = sqlite3_value_int64(argv[1]);
+ if( f<1 || f>15 ){
+ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
+ " between 1 and 15", -1);
+ return;
+ }
+ flags = f & 0x0f;
+ }
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: {
#ifdef SQLITE_LEGACY_JSON_VALID
- /* Incorrect legacy behavior was to return FALSE for a NULL input */
- sqlite3_result_int(ctx, 0);
+ /* Incorrect legacy behavior was to return FALSE for a NULL input */
+ sqlite3_result_int(ctx, 0);
#endif
- return;
- }
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
- sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else{
- sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod));
- if( p->nErr ) jsonParseFree(p);
+ return;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ if( flags & 0x04 ){
+ /* Superficial checking only - accomplished by the
+ ** jsonFuncArgMightBeBinary() call above. */
+ res = 1;
+ }else if( flags & 0x08 ){
+ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
+ ** no errors occur, call that a "strict check". */
+ JsonParse px;
+ u32 iErr;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ px.nBlob = sqlite3_value_bytes(argv[0]);
+ iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
+ res = iErr==0;
+ }
+ break;
+ }
+ /* Fall through into interpreting the input as text. See note
+ ** above at tag-20240123-a. */
+ /* no break */ deliberate_fall_through
+ }
+ default: {
+ JsonParse px;
+ if( (flags & 0x3)==0 ) break;
+ memset(&px, 0, sizeof(px));
+
+ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
+ if( p ){
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ }else if( p->nErr ){
+ /* no-op */
+ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
+ res = 1;
+ }
+ jsonParseFree(p);
+ }else{
+ sqlite3_result_error_nomem(ctx);
+ }
+ break;
+ }
}
+ sqlite3_result_int(ctx, res);
}
/*
** json_error_position(JSON)
**
-** If the argument is not an interpretable JSON string, then return the 1-based
-** character position at which the parser first recognized that the input
-** was in error. The left-most character is 1. If the string is valid
-** JSON, then return 0.
-**
-** Note that json_valid() is only true for strictly conforming canonical JSON.
-** But this routine returns zero if the input contains extension. Thus:
-**
-** (1) If the input X is strictly conforming canonical JSON:
+** If the argument is NULL, return NULL
**
-** json_valid(X) returns true
-** json_error_position(X) returns 0
+** If the argument is BLOB, do a full validity check and return non-zero
+** if the check fails. The return value is the approximate 1-based offset
+** to the byte of the element that contains the first error.
**
-** (2) If the input X is JSON but it includes extension (such as JSON5) that
-** are not part of RFC-8259:
-**
-** json_valid(X) returns false
-** json_error_position(X) return 0
-**
-** (3) If the input X cannot be interpreted as JSON even taking extensions
-** into account:
-**
-** json_valid(X) return false
-** json_error_position(X) returns 1 or more
+** Otherwise interpret the argument is TEXT (even if it is numeric) and
+** return the 1-based character position for where the parser first recognized
+** that the input was not valid JSON, or return 0 if the input text looks
+** ok. JSON-5 extensions are accepted.
*/
static void jsonErrorFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
+ i64 iErrPos = 0; /* Error position to be returned */
+ JsonParse s;
+
+ assert( argc==1 );
UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
- sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else if( p->nErr==0 ){
- sqlite3_result_int(ctx, 0);
+ memset(&s, 0, sizeof(s));
+ s.db = sqlite3_context_db_handle(ctx);
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ s.nBlob = sqlite3_value_bytes(argv[0]);
+ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
}else{
- int n = 1;
- u32 i;
- const char *z = (const char*)sqlite3_value_text(argv[0]);
- for(i=0; i<p->iErr && ALWAYS(z[i]); i++){
- if( (z[i]&0xc0)!=0x80 ) n++;
+ s.zJson = (char*)sqlite3_value_text(argv[0]);
+ if( s.zJson==0 ) return; /* NULL input or OOM */
+ s.nJson = sqlite3_value_bytes(argv[0]);
+ if( jsonConvertTextToBlob(&s,0) ){
+ if( s.oom ){
+ iErrPos = -1;
+ }else{
+ /* Convert byte-offset s.iErr into a character offset */
+ u32 k;
+ assert( s.zJson!=0 ); /* Because s.oom is false */
+ for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
+ if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
+ }
+ iErrPos++;
+ }
}
- sqlite3_result_int(ctx, n);
- jsonParseFree(p);
+ }
+ jsonParseReset(&s);
+ if( iErrPos<0 ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_int64(ctx, iErrPos);
}
}
-
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
@@ -3101,32 +4458,42 @@ static void jsonArrayStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '[');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
- jsonAppendValue(pStr, argv[0]);
+ jsonAppendSqlValue(pStr, argv[0]);
}
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
@@ -3207,35 +4574,46 @@ static void jsonObjectStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
z = (const char*)sqlite3_value_text(argv[0]);
- n = (u32)sqlite3_value_bytes(argv[0]);
+ n = sqlite3Strlen30(z);
jsonAppendString(pStr, z, n);
jsonAppendChar(pStr, ':');
- jsonAppendValue(pStr, argv[1]);
+ jsonAppendSqlValue(pStr, argv[1]);
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
jsonAppendChar(pStr, '}');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ pStr->pCtx = ctx;
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
@@ -3255,19 +4633,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){
/****************************************************************************
** The json_each virtual table
****************************************************************************/
+typedef struct JsonParent JsonParent;
+struct JsonParent {
+ u32 iHead; /* Start of object or array */
+ u32 iValue; /* Start of the value */
+ u32 iEnd; /* First byte past the end */
+ u32 nPath; /* Length of path */
+ i64 iKey; /* Key for JSONB_ARRAY */
+};
+
typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
- u32 iBegin; /* The first node of the scan */
- u32 i; /* Index in sParse.aNode[] of current row */
+ u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
- u8 eType; /* Type of top-level element */
+ u32 nRoot; /* Size of the root path in bytes */
+ u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
- char *zJson; /* Input JSON */
- char *zRoot; /* Path by which to filter zJson */
+ u32 nParent; /* Current nesting depth */
+ u32 nParentAlloc; /* Space allocated for aParent[] */
+ JsonParent *aParent; /* Parent elements of i */
+ sqlite3 *db; /* Database connection */
+ JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
+typedef struct JsonEachConnection JsonEachConnection;
+struct JsonEachConnection {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3 *db; /* Database connection */
+};
+
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
@@ -3277,7 +4673,7 @@ static int jsonEachConnect(
sqlite3_vtab **ppVtab,
char **pzErr
){
- sqlite3_vtab *pNew;
+ JsonEachConnection *pNew;
int rc;
/* Column numbers */
@@ -3303,28 +4699,32 @@ static int jsonEachConnect(
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
+ JsonEachConnection *p = (JsonEachConnection*)pVtab;
+ sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
UNUSED_PARAMETER(p);
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
- memset(pCur, 0, sizeof(*pCur));
+ pCur->db = pVtab->db;
+ jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
@@ -3342,21 +4742,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
- sqlite3_free(p->zRoot);
jsonParseReset(&p->sParse);
+ jsonStringReset(&p->path);
+ sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
p->i = 0;
+ p->aParent = 0;
+ p->nParent = 0;
+ p->nParentAlloc = 0;
p->iEnd = 0;
p->eType = 0;
- p->zJson = 0;
- p->zRoot = 0;
}
/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
jsonEachCursorReset(p);
- sqlite3_free(cur);
+
+ sqlite3DbFree(p->db, cur);
return SQLITE_OK;
}
@@ -3367,200 +4770,233 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){
return p->i >= p->iEnd;
}
+/*
+** If the cursor is currently pointing at the label of a object entry,
+** then return the index of the value. For all other cases, return the
+** current pointer position, which is the value.
+*/
+static int jsonSkipLabel(JsonEachCursor *p){
+ if( p->eType==JSONB_OBJECT ){
+ u32 sz = 0;
+ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ return p->i + n + sz;
+ }else{
+ return p->i;
+ }
+}
+
+/*
+** Append the path name for the current element.
+*/
+static void jsonAppendPathName(JsonEachCursor *p){
+ assert( p->nParent>0 );
+ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
+ if( p->eType==JSONB_ARRAY ){
+ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
+ }else{
+ u32 n, sz = 0, k, i;
+ const char *z;
+ int needQuote = 0;
+ n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ k = p->i + n;
+ z = (const char*)&p->sParse.aBlob[k];
+ if( sz==0 || !sqlite3Isalpha(z[0]) ){
+ needQuote = 1;
+ }else{
+ for(i=0; i<sz; i++){
+ if( !sqlite3Isalnum(z[i]) ){
+ needQuote = 1;
+ break;
+ }
+ }
+ }
+ if( needQuote ){
+ jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
+ }else{
+ jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
+ }
+ }
+}
+
/* Advance the cursor to the next element for json_tree() */
static int jsonEachNext(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
+ int rc = SQLITE_OK;
if( p->bRecursive ){
- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
- p->i++;
- p->iRowid++;
- if( p->i<p->iEnd ){
- u32 iUp = p->sParse.aUp[p->i];
- JsonNode *pUp = &p->sParse.aNode[iUp];
- p->eType = pUp->eType;
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==0 || pUp->eU==3 );
- testcase( pUp->eU==3 );
- VVA( pUp->eU = 3 );
- if( iUp==p->i-1 ){
- pUp->u.iKey = 0;
- }else{
- pUp->u.iKey++;
- }
+ u8 x;
+ u8 levelChange = 0;
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ x = p->sParse.aBlob[i] & 0x0f;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
+ JsonParent *pParent;
+ if( p->nParent>=p->nParentAlloc ){
+ JsonParent *pNew;
+ u64 nNew;
+ nNew = p->nParentAlloc*2 + 3;
+ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ p->nParentAlloc = (u32)nNew;
+ p->aParent = pNew;
}
- }
- }else{
- switch( p->eType ){
- case JSON_ARRAY: {
- p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
- p->iRowid++;
- break;
+ levelChange = 1;
+ pParent = &p->aParent[p->nParent];
+ pParent->iHead = p->i;
+ pParent->iValue = i;
+ pParent->iEnd = i + n + sz;
+ pParent->iKey = -1;
+ pParent->nPath = (u32)p->path.nUsed;
+ if( p->eType && p->nParent ){
+ jsonAppendPathName(p);
+ if( p->path.eErr ) rc = SQLITE_NOMEM;
}
- case JSON_OBJECT: {
- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
- p->iRowid++;
- break;
- }
- default: {
- p->i = p->iEnd;
- break;
+ p->nParent++;
+ p->i = i + n;
+ }else{
+ p->i = i + n + sz;
+ }
+ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
+ p->nParent--;
+ p->path.nUsed = p->aParent[p->nParent].nPath;
+ levelChange = 1;
+ }
+ if( levelChange ){
+ if( p->nParent>0 ){
+ JsonParent *pParent = &p->aParent[p->nParent-1];
+ u32 iVal = pParent->iValue;
+ p->eType = p->sParse.aBlob[iVal] & 0x0f;
+ }else{
+ p->eType = 0;
}
}
+ }else{
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->i = i + n + sz;
}
- return SQLITE_OK;
+ if( p->eType==JSONB_ARRAY && p->nParent ){
+ p->aParent[p->nParent-1].iKey++;
+ }
+ p->iRowid++;
+ return rc;
}
-/* Append an object label to the JSON Path being constructed
-** in pStr.
+/* Length of the path for rowid==0 in bRecursive mode.
*/
-static void jsonAppendObjectPathElement(
- JsonString *pStr,
- JsonNode *pNode
-){
- int jj, nn;
- const char *z;
- assert( pNode->eType==JSON_STRING );
- assert( pNode->jnFlags & JNODE_LABEL );
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- nn = pNode->n;
- if( (pNode->jnFlags & JNODE_RAW)==0 ){
- assert( nn>=2 );
- assert( z[0]=='"' || z[0]=='\'' );
- assert( z[nn-1]=='"' || z[0]=='\'' );
- if( nn>2 && sqlite3Isalpha(z[1]) ){
- for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
- if( jj==nn-1 ){
- z++;
- nn -= 2;
+static int jsonEachPathLength(JsonEachCursor *p){
+ u32 n = p->path.nUsed;
+ char *z = p->path.zBuf;
+ if( p->iRowid==0 && p->bRecursive && n>=2 ){
+ while( n>1 ){
+ n--;
+ if( z[n]=='[' || z[n]=='.' ){
+ u32 x, sz = 0;
+ char cSaved = z[n];
+ z[n] = 0;
+ assert( p->sParse.eEdit==0 );
+ x = jsonLookupStep(&p->sParse, 0, z+1, 0);
+ z[n] = cSaved;
+ if( JSON_LOOKUP_ISERROR(x) ) continue;
+ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
}
}
}
- jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
-}
-
-/* Append the name of the path for element i to pStr
-*/
-static void jsonEachComputePath(
- JsonEachCursor *p, /* The cursor */
- JsonString *pStr, /* Write the path here */
- u32 i /* Path to this element */
-){
- JsonNode *pNode, *pUp;
- u32 iUp;
- if( i==0 ){
- jsonAppendChar(pStr, '$');
- return;
- }
- iUp = p->sParse.aUp[i];
- jsonEachComputePath(p, pStr, iUp);
- pNode = &p->sParse.aNode[i];
- pUp = &p->sParse.aNode[iUp];
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) );
- testcase( pUp->eU==0 );
- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
- }else{
- assert( pUp->eType==JSON_OBJECT );
- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
- jsonAppendObjectPathElement(pStr, pNode);
- }
+ return n;
}
/* Return the value of a column */
static int jsonEachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
- int i /* Which column to return */
+ int iColumn /* Which column to return */
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- JsonNode *pThis = &p->sParse.aNode[p->i];
- switch( i ){
+ switch( iColumn ){
case JEACH_KEY: {
- if( p->i==0 ) break;
- if( p->eType==JSON_OBJECT ){
- jsonReturn(&p->sParse, pThis, ctx);
- }else if( p->eType==JSON_ARRAY ){
- u32 iKey;
- if( p->bRecursive ){
- if( p->iRowid==0 ) break;
- assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 );
- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ if( p->nParent==0 ){
+ u32 n, j;
+ if( p->nRoot==1 ) break;
+ j = jsonEachPathLength(p);
+ n = p->nRoot - j;
+ if( n==0 ){
+ break;
+ }else if( p->path.zBuf[j]=='[' ){
+ i64 x;
+ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
+ sqlite3_result_int64(ctx, x);
+ }else if( p->path.zBuf[j+1]=='"' ){
+ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
}else{
- iKey = p->iRowid;
+ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
}
- sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ break;
+ }
+ if( p->eType==JSONB_OBJECT ){
+ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
+ }else{
+ assert( p->eType==JSONB_ARRAY );
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
break;
}
case JEACH_TYPE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ u32 i = jsonSkipLabel(p);
+ u8 eType = p->sParse.aBlob[i] & 0x0f;
+ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
break;
}
case JEACH_ATOM: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- if( pThis->eType>=JSON_ARRAY ) break;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ }
break;
}
case JEACH_ID: {
- sqlite3_result_int64(ctx,
- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->i);
break;
}
case JEACH_PARENT: {
- if( p->i>p->iBegin && p->bRecursive ){
- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ if( p->nParent>0 && p->bRecursive ){
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
}
break;
}
case JEACH_FULLKEY: {
- JsonString x;
- jsonInit(&x, ctx);
- if( p->bRecursive ){
- jsonEachComputePath(p, &x, p->i);
- }else{
- if( p->zRoot ){
- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
- }else{
- jsonAppendChar(&x, '$');
- }
- if( p->eType==JSON_ARRAY ){
- jsonPrintf(30, &x, "[%d]", p->iRowid);
- }else if( p->eType==JSON_OBJECT ){
- jsonAppendObjectPathElement(&x, pThis);
- }
- }
- jsonResult(&x);
+ u64 nBase = p->path.nUsed;
+ if( p->nParent ) jsonAppendPathName(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ p->path.nUsed = nBase;
break;
}
case JEACH_PATH: {
- if( p->bRecursive ){
- JsonString x;
- jsonInit(&x, ctx);
- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
- jsonResult(&x);
- break;
- }
- /* For json_each() path and root are the same so fall through
- ** into the root case */
- /* no break */ deliberate_fall_through
+ u32 n = jsonEachPathLength(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, n,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ break;
}
default: {
- const char *zRoot = p->zRoot;
- if( zRoot==0 ) zRoot = "$";
- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
break;
}
case JEACH_JSON: {
- assert( i==JEACH_JSON );
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ if( p->sParse.zJson==0 ){
+ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
+ SQLITE_STATIC);
+ }else{
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ }
break;
}
}
@@ -3651,86 +5087,97 @@ static int jsonEachFilter(
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- const char *z;
const char *zRoot = 0;
- sqlite3_int64 n;
+ u32 i, n, sz;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
- z = (const char*)sqlite3_value_text(argv[0]);
- if( z==0 ) return SQLITE_OK;
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
- if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){
- p->sParse.zJson = sqlite3RCStrRef((char*)z);
- }else{
- n = sqlite3_value_bytes(argv[0]);
- p->sParse.zJson = sqlite3RCStrNew( n+1 );
- if( p->sParse.zJson==0 ) return SQLITE_NOMEM;
- memcpy(p->sParse.zJson, z, (size_t)n+1);
- }
- p->sParse.bJsonIsRCStr = 1;
- p->zJson = p->sParse.zJson;
- if( jsonParse(&p->sParse, 0) ){
- int rc = SQLITE_NOMEM;
- if( p->sParse.oom==0 ){
- sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
- }
- jsonEachCursorReset(p);
- return rc;
- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
- jsonEachCursorReset(p);
- return SQLITE_NOMEM;
+ p->sParse.db = p->db;
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
+ p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
}else{
- JsonNode *pNode = 0;
- if( idxNum==3 ){
- const char *zErr = 0;
- zRoot = (const char*)sqlite3_value_text(argv[1]);
- if( zRoot==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[1]);
- p->zRoot = sqlite3_malloc64( n+1 );
- if( p->zRoot==0 ) return SQLITE_NOMEM;
- memcpy(p->zRoot, zRoot, (size_t)n+1);
- if( zRoot[0]!='$' ){
- zErr = zRoot;
- }else{
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
+ p->sParse.nJson = sqlite3_value_bytes(argv[0]);
+ if( p->sParse.zJson==0 ){
+ p->i = p->iEnd = 0;
+ return SQLITE_OK;
+ }
+ if( jsonConvertTextToBlob(&p->sParse, 0) ){
+ if( p->sParse.oom ){
+ return SQLITE_NOMEM;
}
- if( zErr ){
+ goto json_each_malformed_input;
+ }
+ }
+ if( idxNum==3 ){
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ if( zRoot[0]!='$' ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }
+ p->nRoot = sqlite3Strlen30(zRoot);
+ if( zRoot[1]==0 ){
+ i = p->i = 0;
+ p->eType = 0;
+ }else{
+ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ p->i = 0;
+ p->eType = 0;
+ p->iEnd = 0;
+ return SQLITE_OK;
+ }
sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
- }else if( pNode==0 ){
- return SQLITE_OK;
}
- }else{
- pNode = p->sParse.aNode;
- }
- p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
- p->eType = pNode->eType;
- if( p->eType>=JSON_ARRAY ){
- assert( pNode->eU==0 );
- VVA( pNode->eU = 3 );
- pNode->u.iKey = 0;
- p->iEnd = p->i + pNode->n + 1;
- if( p->bRecursive ){
- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
- p->i--;
- }
+ if( p->sParse.iLabel ){
+ p->i = p->sParse.iLabel;
+ p->eType = JSONB_OBJECT;
}else{
- p->i++;
+ p->i = i;
+ p->eType = JSONB_ARRAY;
}
- }else{
- p->iEnd = p->i+1;
}
+ jsonAppendRaw(&p->path, zRoot, p->nRoot);
+ }else{
+ i = p->i = 0;
+ p->eType = 0;
+ p->nRoot = 1;
+ jsonAppendRaw(&p->path, "$", 1);
+ }
+ p->nParent = 0;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->iEnd = i+n+sz;
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
+ p->i = i + n;
+ p->eType = p->sParse.aBlob[i] & 0x0f;
+ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
+ if( p->aParent==0 ) return SQLITE_NOMEM;
+ p->nParent = 1;
+ p->nParentAlloc = 1;
+ p->aParent[0].iKey = 0;
+ p->aParent[0].iEnd = p->iEnd;
+ p->aParent[0].iHead = p->i;
+ p->aParent[0].iValue = i;
}
return SQLITE_OK;
+
+json_each_malformed_input:
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
/* The methods of the json_each virtual table */
@@ -3758,7 +5205,8 @@ static sqlite3_module jsonEachModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/* The methods of the json_tree virtual table. */
@@ -3786,7 +5234,8 @@ static sqlite3_module jsonTreeModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#endif /* !defined(SQLITE_OMIT_JSON) */
@@ -3797,34 +5246,57 @@ static sqlite3_module jsonTreeModule = {
void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
- JFUNCTION(json, 1, 0, jsonRemoveFunc),
- JFUNCTION(json_array, -1, 0, jsonArrayFunc),
- JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
- JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
- JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
- JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
- JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
- JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
- JFUNCTION(json_insert, -1, 0, jsonSetFunc),
- JFUNCTION(json_object, -1, 0, jsonObjectFunc),
- JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
- JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
- JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
- JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
- JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
- JFUNCTION(json_type, 1, 0, jsonTypeFunc),
- JFUNCTION(json_type, 2, 0, jsonTypeFunc),
- JFUNCTION(json_valid, 1, 0, jsonValidFunc),
+ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */
+ /* | | */
+ /* Uses cache ------, | | ,---- Returns JSONB */
+ /* | | | | */
+ /* Number of arguments ---, | | | | ,--- Flags */
+ /* | | | | | | */
+ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc),
+ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc),
+ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc),
+ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc),
+ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc),
+ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc),
+ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc),
+ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc),
+ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc),
+ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc),
+ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
+ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
+ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
+ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
+ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc),
+ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc),
+ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc),
+ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc),
#if SQLITE_DEBUG
- JFUNCTION(json_parse, 1, 0, jsonParseFunc),
- JFUNCTION(json_test1, 1, 0, jsonTest1Func),
+ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
diff --git a/src/loadext.c b/src/loadext.c
index e792fa5a9..7e0ae2543 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -514,7 +514,10 @@ static const sqlite3_api_routines sqlite3Apis = {
/* Version 3.41.0 and later */
sqlite3_is_interrupted,
/* Version 3.43.0 and later */
- sqlite3_stmt_explain
+ sqlite3_stmt_explain,
+ /* Version 3.44.0 and later */
+ sqlite3_get_clientdata,
+ sqlite3_set_clientdata
};
/* True if x is the directory separator character
@@ -730,6 +733,9 @@ void sqlite3CloseExtensions(sqlite3 *db){
** default so as not to open security holes in older applications.
*/
int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
if( onoff ){
db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
@@ -751,7 +757,7 @@ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
*/
typedef struct sqlite3AutoExtList sqlite3AutoExtList;
static SQLITE_WSD struct sqlite3AutoExtList {
- u32 nExt; /* Number of entries in aExt[] */
+ u32 nExt; /* Number of entries in aExt[] */
void (**aExt)(void); /* Pointers to the extension init functions */
} sqlite3Autoext = { 0, 0 };
@@ -779,6 +785,9 @@ int sqlite3_auto_extension(
void (*xInit)(void)
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return SQLITE_MISUSE_BKPT;
+#endif
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc ){
@@ -831,6 +840,9 @@ int sqlite3_cancel_auto_extension(
int i;
int n = 0;
wsdAutoextInit;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return 0;
+#endif
sqlite3_mutex_enter(mutex);
for(i=(int)wsdAutoext.nExt-1; i>=0; i--){
if( wsdAutoext.aExt[i]==xInit ){
diff --git a/src/main.c b/src/main.c
index b8f262261..03429983d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -160,6 +160,32 @@ char *sqlite3_temp_directory = 0;
char *sqlite3_data_directory = 0;
/*
+** Determine whether or not high-precision (long double) floating point
+** math works correctly on CPU currently running.
+*/
+static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){
+ if( sizeof(LONGDOUBLE_TYPE)<=8 ){
+ /* If the size of "long double" is not more than 8, then
+ ** high-precision math is not possible. */
+ return 0;
+ }else{
+ /* Just because sizeof(long double)>8 does not mean that the underlying
+ ** hardware actually supports high-precision floating point. For example,
+ ** clearing the 0x100 bit in the floating-point control word on Intel
+ ** processors will make long double work like double, even though long
+ ** double takes up more space. The only way to determine if long double
+ ** actually works is to run an experiment. */
+ LONGDOUBLE_TYPE a, b, c;
+ rc++;
+ a = 1.0+rc*0.1;
+ b = 1.0e+18+rc*25.0;
+ c = a+b;
+ return b!=c;
+ }
+}
+
+
+/*
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
@@ -354,6 +380,12 @@ int sqlite3_initialize(void){
}
#endif
+ /* Experimentally determine if high-precision floating point is
+ ** available. */
+#ifndef SQLITE_OMIT_WSD
+ sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc);
+#endif
+
return rc;
}
@@ -924,6 +956,10 @@ int sqlite3_db_cacheflush(sqlite3 *db){
int sqlite3_db_config(sqlite3 *db, int op, ...){
va_list ap;
int rc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
@@ -1253,6 +1289,14 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
}
#endif
+ while( db->pDbData ){
+ DbClientData *p = db->pDbData;
+ db->pDbData = p->pNext;
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ sqlite3_free(p);
+ }
+
/* Convert the connection into a zombie and then close it.
*/
db->eOpenState = SQLITE_STATE_ZOMBIE;
@@ -1870,7 +1914,7 @@ int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
- SQLITE_SUBTYPE|SQLITE_INNOCUOUS);
+ SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
@@ -2327,6 +2371,12 @@ void *sqlite3_preupdate_hook(
void *pArg /* First callback argument */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 ){
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pPreUpdateArg;
db->xPreUpdateCallback = xCallback;
@@ -2473,7 +2523,7 @@ int sqlite3_wal_checkpoint_v2(
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
/* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
** mode: */
- return SQLITE_MISUSE;
+ return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
@@ -3710,6 +3760,69 @@ int sqlite3_collation_needed16(
}
#endif /* SQLITE_OMIT_UTF16 */
+/*
+** Find existing client data.
+*/
+void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){
+ DbClientData *p;
+ sqlite3_mutex_enter(db->mutex);
+ for(p=db->pDbData; p; p=p->pNext){
+ if( strcmp(p->zName, zName)==0 ){
+ void *pResult = p->pData;
+ sqlite3_mutex_leave(db->mutex);
+ return pResult;
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return 0;
+}
+
+/*
+** Add new client data to a database connection.
+*/
+int sqlite3_set_clientdata(
+ sqlite3 *db, /* Attach client data to this connection */
+ const char *zName, /* Name of the client data */
+ void *pData, /* The client data itself */
+ void (*xDestructor)(void*) /* Destructor */
+){
+ DbClientData *p, **pp;
+ sqlite3_mutex_enter(db->mutex);
+ pp = &db->pDbData;
+ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){
+ pp = &p->pNext;
+ }
+ if( p ){
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ if( pData==0 ){
+ *pp = p->pNext;
+ sqlite3_free(p);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }
+ }else if( pData==0 ){
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }else{
+ size_t n = strlen(zName);
+ p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
+ if( p==0 ){
+ if( xDestructor ) xDestructor(pData);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_NOMEM;
+ }
+ memcpy(p->zName, zName, n+1);
+ p->pNext = db->pDbData;
+ db->pDbData = p;
+ }
+ p->pData = pData;
+ p->xDestructor = xDestructor;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+
#ifndef SQLITE_OMIT_DEPRECATED
/*
** This function is now an anachronism. It used to be used to recover from a
@@ -4059,6 +4172,28 @@ int sqlite3_test_control(int op, ...){
}
#endif
+ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
+ **
+ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
+ ** false then clearn that setting. If the SQLITE_FkNoAction setting is
+ ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
+ ** they were NO ACTION, regardless of how they are defined.
+ **
+ ** NB: One must usually run "PRAGMA writable_schema=RESET" after
+ ** using this test-control, before it will take full effect. failing
+ ** to reset the schema can result in some unexpected behavior.
+ */
+ case SQLITE_TESTCTRL_FK_NO_ACTION: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ int b = va_arg(ap, int);
+ if( b ){
+ db->flags |= SQLITE_FkNoAction;
+ }else{
+ db->flags &= ~SQLITE_FkNoAction;
+ }
+ break;
+ }
+
/*
** sqlite3_test_control(BITVEC_TEST, size, program)
**
@@ -4483,11 +4618,11 @@ int sqlite3_test_control(int op, ...){
** X<0 Make no changes to the bUseLongDouble. Just report value.
** X==0 Disable bUseLongDouble
** X==1 Enable bUseLongDouble
- ** X==2 Set bUseLongDouble to its default value for this platform
+ ** X>=2 Set bUseLongDouble to its default value for this platform
*/
case SQLITE_TESTCTRL_USELONGDOUBLE: {
int b = va_arg(ap, int);
- if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8;
+ if( b>=2 ) b = hasHighPrecisionDouble(b);
if( b>=0 ) sqlite3Config.bUseLongDouble = b>0;
rc = sqlite3Config.bUseLongDouble!=0;
break;
@@ -4524,6 +4659,28 @@ int sqlite3_test_control(int op, ...){
break;
}
#endif
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
+ **
+ ** Activate or deactivate validation of JSONB that is generated from
+ ** text. Off by default, as the validation is slow. Validation is
+ ** only available if compiled using SQLITE_DEBUG.
+ **
+ ** If onOff is initially 1, then turn it on. If onOff is initially
+ ** off, turn it off. If onOff is initially -1, then change onOff
+ ** to be the current setting.
+ */
+ case SQLITE_TESTCTRL_JSON_SELFCHECK: {
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
+ int *pOnOff = va_arg(ap, int*);
+ if( *pOnOff<0 ){
+ *pOnOff = sqlite3Config.bJsonSelfcheck;
+ }else{
+ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
+ }
+#endif
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
@@ -4901,7 +5058,7 @@ int sqlite3_compileoption_used(const char *zOptName){
int nOpt;
const char **azCompileOpt;
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( zOptName==0 ){
(void)SQLITE_MISUSE_BKPT;
return 0;
diff --git a/src/malloc.c b/src/malloc.c
index 48c460060..9a635e716 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -221,6 +221,24 @@ static void sqlite3MallocAlarm(int nByte){
sqlite3_mutex_enter(mem0.mutex);
}
+#ifdef SQLITE_DEBUG
+/*
+** This routine is called whenever an out-of-memory condition is seen,
+** It's only purpose to to serve as a breakpoint for gdb or similar
+** code debuggers when working on out-of-memory conditions, for example
+** caused by PRAGMA hard_heap_limit=N.
+*/
+static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){
+ static u64 nOomFault = 0;
+ nOomFault += n;
+ /* The assert() is never reached in a human lifetime. It is here mostly
+ ** to prevent code optimizers from optimizing out this function. */
+ assert( (nOomFault>>32) < 0xffffffff );
+}
+#else
+# define test_oom_breakpoint(X) /* No-op for production builds */
+#endif
+
/*
** Do a memory allocation with statistics and alarms. Assume the
** lock is already held.
@@ -247,6 +265,7 @@ static void mallocWithAlarm(int n, void **pp){
if( mem0.hardLimit ){
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.hardLimit - nFull ){
+ test_oom_breakpoint(1);
*pp = 0;
return;
}
@@ -535,6 +554,7 @@ void *sqlite3Realloc(void *pOld, u64 nBytes){
sqlite3MallocAlarm(nDiff);
if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){
sqlite3_mutex_leave(mem0.mutex);
+ test_oom_breakpoint(1);
return 0;
}
}
@@ -896,5 +916,5 @@ int sqlite3ApiExit(sqlite3* db, int rc){
if( db->mallocFailed || rc ){
return apiHandleError(db, rc);
}
- return rc & db->errMask;
+ return 0;
}
diff --git a/src/memdb.c b/src/memdb.c
index 657cb9ca6..d83a51d54 100644
--- a/src/memdb.c
+++ b/src/memdb.c
@@ -799,6 +799,14 @@ unsigned char *sqlite3_serialize(
pOut = 0;
}else{
sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ if( sz==0 ){
+ sqlite3_reset(pStmt);
+ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ }
+ }
if( piSize ) *piSize = sz;
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
pOut = 0;
diff --git a/src/mutex.c b/src/mutex.c
index 13a9fca15..381ffbdfd 100644
--- a/src/mutex.c
+++ b/src/mutex.c
@@ -133,7 +133,7 @@ static void checkMutexFree(sqlite3_mutex *p){
assert( SQLITE_MUTEX_FAST<2 );
assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( ((CheckMutex*)p)->iType<2 )
#endif
{
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index ac4331a67..beae877f9 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -223,7 +223,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
*/
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
#endif
{
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index e0e0dfb06..7eb5b50be 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -87,7 +87,7 @@ void sqlite3MemoryBarrier(void){
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__)
__sync_synchronize();
-#elif MSVC_VERSION>=1300
+#elif MSVC_VERSION>=1400
_ReadWriteBarrier();
#elif defined(MemoryBarrier)
MemoryBarrier();
diff --git a/src/notify.c b/src/notify.c
index 4960ab76b..6a4cab875 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -152,6 +152,9 @@ int sqlite3_unlock_notify(
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
enterMutex();
diff --git a/src/os_unix.c b/src/os_unix.c
index 59f67d142..4663c22d9 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -1295,8 +1295,12 @@ static int unixLogErrorAtLine(
** available, the error message will often be an empty string. Not a
** huge problem. Incorrectly concluding that the GNU version is available
** could lead to a segfault though.
+ **
+ ** Forum post 3f13857fa4062301 reports that the Android SDK may use
+ ** int-type return, depending on its version.
*/
-#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
+#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \
+ && !defined(ANDROID) && !defined(__ANDROID__)
zErr =
# endif
strerror_r(iErrno, aErr, sizeof(aErr)-1);
@@ -3150,9 +3154,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unixInodeInfo *pInode;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
int skipShared = 0;
-#ifdef SQLITE_TEST
- int h = pFile->h;
-#endif
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
@@ -3168,9 +3169,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
@@ -3219,9 +3217,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
pInode->nShared--;
if( pInode->nShared==0 ){
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( !skipShared ){
rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
}
@@ -4063,7 +4058,13 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
+ pFile->iBusyTimeout = !!(*(int*)pArg);
+#else
+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
+#endif
*(int*)pArg = iOld;
return SQLITE_OK;
}
@@ -4316,6 +4317,25 @@ static int unixGetpagesize(void){
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
+**
+** aLock[SQLITE_SHM_NLOCK]:
+** This array records the various locks held by clients on each of the
+** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
+** locks are held by the process on this slot. If it is set to -1, then
+** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
+** value is set to a positive value, then it is the number of shared
+** locks currently held on the slot.
+**
+** aMutex[SQLITE_SHM_NLOCK]:
+** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
+** pShmMutex is used to protect the aLock[] array and the right to
+** call fcntl() on unixShmNode.hShm to obtain or release locks.
+**
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
+** of mutexes - one for each locking slot. To read or write locking
+** slot aLock[iSlot], the caller must hold the corresponding mutex
+** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
+** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
@@ -4329,10 +4349,11 @@ struct unixShmNode {
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
+#endif
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
- u8 exclMask; /* Mask of exclusive locks held */
- u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
@@ -4415,16 +4436,35 @@ static int unixShmSystemLock(
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
- /* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode;
- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
- assert( pShmNode->nRef>0 || unixMutexHeld() );
+
+ /* Assert that the parameters are within expected range and that the
+ ** correct mutex or mutexes are held. */
+ assert( pShmNode->nRef>=0 );
+ assert( (ofst==UNIX_SHM_DMS && n==1)
+ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
+ );
+ if( ofst==UNIX_SHM_DMS ){
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+ }else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int ii;
+ for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
+ assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
+ }
+#else
+ assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
+ assert( pShmNode->nRef>0 );
+#endif
+ }
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
+ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){
int res;
@@ -4435,7 +4475,7 @@ static int unixShmSystemLock(
f.l_len = n;
res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
if( res==-1 ){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
rc = SQLITE_BUSY;
@@ -4443,39 +4483,28 @@ static int unixShmSystemLock(
}
}
- /* Update the global lock state and do debug tracing */
+ /* Do debug tracing */
#ifdef SQLITE_DEBUG
- { u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask |= mask;
+ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d ok", ofst));
- pShmNode->exclMask |= mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
}
}else{
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d failed", ofst));
+ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock failed"));
+ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d failed", ofst));
+ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
}
}
- OSTRACE((" - afterwards %03x,%03x\n",
- pShmNode->sharedMask, pShmNode->exclMask));
- }
#endif
return rc;
@@ -4512,6 +4541,11 @@ static void unixShmPurge(unixFile *pFd){
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ for(i=0; i<SQLITE_SHM_NLOCK; i++){
+ sqlite3_mutex_free(p->aMutex[i]);
+ }
+#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
@@ -4571,7 +4605,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* Do not use a blocking lock here. If the lock cannot be obtained
+ ** immediately, it means some other connection is truncating the
+ ** *-shm file. And after it has done so, it will not release its
+ ** lock, but only downgrade it to a shared lock. So no point in
+ ** blocking here. The call below to obtain the shared DMS lock may
+ ** use a blocking lock. */
+ int iSaveTimeout = pDbFd->iBusyTimeout;
+ pDbFd->iBusyTimeout = 0;
+#endif
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ pDbFd->iBusyTimeout = iSaveTimeout;
+#endif
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
@@ -4692,6 +4739,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ {
+ int ii;
+ for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
+ pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->aMutex[ii]==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
+ }
+ }
+#endif
}
if( pInode->bProcessLock==0 ){
@@ -4913,9 +4972,11 @@ shmpage_out:
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ return 1;
+#else
unixShm *pX;
int aLock[SQLITE_SHM_NLOCK];
- assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
memset(aLock, 0, sizeof(aLock));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
@@ -4933,13 +4994,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
+#endif
}
#endif
/*
** Change the lock state for a shared-memory segment.
**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
+** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared.
@@ -4954,7 +5016,7 @@ static int unixShmLock(
unixShm *p; /* The shared memory being locked */
unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
- u16 mask; /* Mask of locks to take or release */
+ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
int *aLock;
p = pDbFd->pShm;
@@ -4989,88 +5051,151 @@ static int unixShmLock(
** It is not permitted to block on the RECOVER lock.
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
- (ofst!=2) /* not RECOVER */
- && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
- && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
- && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
- ));
+ {
+ u16 lockMask = (p->exclMask|p->sharedMask);
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (ofst!=1 || lockMask==0 || lockMask==2)
+ && (ofst!=0 || lockMask<3)
+ && (ofst<3 || lockMask<(1<<ofst))
+ ));
+ }
#endif
- mask = (1<<(ofst+n)) - (1<<ofst);
- assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->pShmMutex);
- assert( assertLockingArrayOk(pShmNode) );
- if( flags & SQLITE_SHM_UNLOCK ){
- if( (p->exclMask|p->sharedMask) & mask ){
- int ii;
- int bUnlock = 1;
-
- for(ii=ofst; ii<ofst+n; ii++){
- if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){
- bUnlock = 0;
- }
- }
-
- if( bUnlock ){
- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- memset(&aLock[ofst], 0, sizeof(int)*n);
- }
- }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
- assert( n==1 && aLock[ofst]>1 );
- aLock[ofst]--;
- }
-
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- assert( n==1 );
- assert( (p->exclMask & (1<<ofst))==0 );
- if( (p->sharedMask & mask)==0 ){
- if( aLock[ofst]<0 ){
- rc = SQLITE_BUSY;
- }else if( aLock[ofst]==0 ){
- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
- }
+ /* Check if there is any work to do. There are three cases:
+ **
+ ** a) An unlock operation where there are locks to unlock,
+ ** b) An shared lock where the requested lock is not already held
+ ** c) An exclusive lock where the requested lock is not already held
+ **
+ ** The SQLite core never requests an exclusive lock that it already holds.
+ ** This is assert()ed below.
+ */
+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
+ || 0==(p->exclMask & mask)
+ );
+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
+ ){
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- aLock[ofst]++;
- }
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away. */
- int ii;
- for(ii=ofst; ii<ofst+n; ii++){
- assert( (p->sharedMask & mask)==0 );
- if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
- rc = SQLITE_BUSY;
- break;
+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
+ ** other thread is holding this mutex, then it is either holding or about
+ ** to hold a lock exclusive to the one being requested, and we may
+ ** therefore return SQLITE_BUSY to the caller.
+ **
+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may
+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
+ ** may be a normal SQL client upgrading to a write transaction. In this
+ ** case thread 2 does a non-blocking request for the WRITER lock. But -
+ ** if it were to use sqlite3_mutex_enter() then it would effectively
+ ** become a (doomed) blocking request, as thread 2 would block until thread
+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
+ ** a lock on a read-locking slot at this point, this breaks the
+ ** anti-deadlock rules (see above). */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int iMutex;
+ for(iMutex=ofst; iMutex<ofst+n; iMutex++){
+ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
+ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
+ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
+ }else{
+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
}
}
+#else
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
+#endif
- /* Get the exclusive locks at the system level. Then if successful
- ** also update the in-memory values. */
- if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
+ if( ALWAYS(rc==SQLITE_OK) ){
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
+ int bUnlock = 1;
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
+
+ /* If this is a SHARED lock being unlocked, it is possible that other
+ ** clients within this process are holding the same SHARED lock. In
+ ** this case, set bUnlock to 0 so that the posix lock is not removed
+ ** from the file-descriptor below. */
+ if( flags & SQLITE_SHM_SHARED ){
+ assert( n==1 );
+ assert( aLock[ofst]>=1 );
+ if( aLock[ofst]>1 ){
+ bUnlock = 0;
+ aLock[ofst]--;
+ p->sharedMask &= ~mask;
+ }
+ }
+
+ if( bUnlock ){
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ memset(&aLock[ofst], 0, sizeof(int)*n);
+ p->sharedMask &= ~mask;
+ p->exclMask &= ~mask;
+ }
+ }
+ }else if( flags & SQLITE_SHM_SHARED ){
+ /* Case (b) - a shared lock. */
+
+ if( aLock[ofst]<0 ){
+ /* An exclusive lock is held by some other connection. BUSY. */
+ rc = SQLITE_BUSY;
+ }else if( aLock[ofst]==0 ){
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ }
+
+ /* Get the local shared locks */
+ if( rc==SQLITE_OK ){
+ p->sharedMask |= mask;
+ aLock[ofst]++;
+ }
+ }else{
+ /* Case (c) - an exclusive lock. */
+ int ii;
+
+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
+ assert( (p->exclMask & mask)==0 );
+
+ /* Make sure no sibling connections hold locks that will block this
+ ** lock. If any do, return SQLITE_BUSY right away. */
for(ii=ofst; ii<ofst+n; ii++){
- aLock[ii] = -1;
+ if( aLock[ii] ){
+ rc = SQLITE_BUSY;
+ break;
+ }
+ }
+
+ /* Get the exclusive locks at the system level. Then if successful
+ ** also update the in-memory values. */
+ if( rc==SQLITE_OK ){
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ p->exclMask |= mask;
+ for(ii=ofst; ii<ofst+n; ii++){
+ aLock[ii] = -1;
+ }
+ }
}
}
+ assert( assertLockingArrayOk(pShmNode) );
}
+
+ /* Drop the mutexes acquired above. */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ leave_shmnode_mutexes:
+ for(iMutex--; iMutex>=ofst; iMutex--){
+ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
+ }
+#else
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
+#endif
}
- assert( assertLockingArrayOk(pShmNode) );
- sqlite3_mutex_leave(pShmNode->pShmMutex);
+
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
@@ -5320,11 +5445,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
diff --git a/src/os_win.c b/src/os_win.c
index dc16c08b5..442c108e9 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -4521,6 +4521,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
@@ -4529,7 +4534,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
return rc;
}
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
diff --git a/src/pager.c b/src/pager.c
index a53dc1889..37588f0b2 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -688,7 +688,7 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
- int aStat[4]; /* Total cache hits, misses, writes, spills */
+ u32 aStat[4]; /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
int nRead; /* Database pages read */
#endif
@@ -818,9 +818,8 @@ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
- int rc;
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return (rc==SQLITE_OK && iRead==0);
+ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+ return iRead==0;
}
#endif
return 1;
@@ -1492,9 +1491,32 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
+
+
/* The random check-hash initializer */
- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ }
+#ifdef SQLITE_DEBUG
+ else{
+ /* The Pager.cksumInit variable is usually randomized above to protect
+ ** against there being existing records in the journal file. This is
+ ** dangerous, as following a crash they may be mistaken for records
+ ** written by the current transaction and rolled back into the database
+ ** file, causing corruption. The following assert statements verify
+ ** that this is not required in "journal_mode=memory" mode, as in that
+ ** case the journal file is always 0 bytes in size at this point.
+ ** It is advantageous to avoid the sqlite3_randomness() call if possible
+ ** as it takes the global PRNG mutex. */
+ i64 sz = 0;
+ sqlite3OsFileSize(pPager->jfd, &sz);
+ assert( sz==0 );
+ assert( pPager->journalOff==journalHdrOffset(pPager) );
+ assert( sqlite3JournalIsInMemory(pPager->jfd) );
+ }
+#endif
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
+
/* The initial database size */
put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize);
/* The assumed sector size for this process */
@@ -2138,6 +2160,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
return (rc==SQLITE_OK?rc2:rc);
}
+/* Forward reference */
+static int pager_playback(Pager *pPager, int isHot);
+
/*
** Execute a rollback if a transaction is active and unlock the
** database file.
@@ -2166,6 +2191,21 @@ static void pagerUnlockAndRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0, 0);
}
+ }else if( pPager->eState==PAGER_ERROR
+ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ && isOpen(pPager->jfd)
+ ){
+ /* Special case for a ROLLBACK due to I/O error with an in-memory
+ ** journal: We have to rollback immediately, before the journal is
+ ** closed, because once it is closed, all content is forgotten. */
+ int errCode = pPager->errCode;
+ u8 eLock = pPager->eLock;
+ pPager->eState = PAGER_OPEN;
+ pPager->errCode = SQLITE_OK;
+ pPager->eLock = EXCLUSIVE_LOCK;
+ pager_playback(pPager, 1);
+ pPager->errCode = errCode;
+ pPager->eLock = eLock;
}
pager_unlock(pPager);
}
@@ -5021,10 +5061,13 @@ act_like_temp_file:
*/
sqlite3_file *sqlite3_database_file_object(const char *zName){
Pager *pPager;
+ const char *p;
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
zName--;
}
- pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
+ p = zName - 4 - sizeof(Pager*);
+ assert( EIGHT_BYTE_ALIGNMENT(p) );
+ pPager = *(Pager**)p;
return pPager->fd;
}
@@ -5658,8 +5701,20 @@ int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
- /* printf("PAGE %u\n", pgno); fflush(stdout); */
+#if 0 /* Trace page fetch by setting to 1 */
+ int rc;
+ printf("PAGE %u\n", pgno);
+ fflush(stdout);
+ rc = pPager->xGet(pPager, pgno, ppPage, flags);
+ if( rc ){
+ printf("PAGE %u failed with 0x%02x\n", pgno, rc);
+ fflush(stdout);
+ }
+ return rc;
+#else
+ /* Normal, high-speed version of sqlite3PagerGet() */
return pPager->xGet(pPager, pgno, ppPage, flags);
+#endif
}
/*
@@ -6535,6 +6590,13 @@ int sqlite3PagerCommitPhaseOne(
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
if( rc==SQLITE_OK ){
rc = pager_write_pagelist(pPager, pList);
+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){
+ char *pTmp = pPager->pTmpSpace;
+ int szPage = (int)pPager->pageSize;
+ memset(pTmp, 0, szPage);
+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage,
+ ((i64)pPager->dbSize*pPager->pageSize)-szPage);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
}
@@ -6769,11 +6831,11 @@ int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->aStat[PAGER_STAT_HIT];
- a[7] = pPager->aStat[PAGER_STAT_MISS];
+ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
+ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->aStat[PAGER_STAT_WRITE];
+ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
return a;
}
#endif
@@ -6789,7 +6851,7 @@ int *sqlite3PagerStats(Pager *pPager){
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
-void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
@@ -7346,7 +7408,7 @@ int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
assert( state==pPager->eState );
}
- }else if( eMode==PAGER_JOURNALMODE_OFF ){
+ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3OsClose(pPager->jfd);
}
}
@@ -7729,7 +7791,7 @@ int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
int sqlite3PagerWalSystemErrno(Pager *pPager){
return sqlite3WalSystemErrno(pPager->pWal);
}
diff --git a/src/pager.h b/src/pager.h
index 044c2573e..7ef9a237a 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -216,7 +216,7 @@ sqlite3_file *sqlite3PagerJrnlFile(Pager*);
const char *sqlite3PagerJournalname(Pager*);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
-void sqlite3PagerCacheStat(Pager *, int, int, int *);
+void sqlite3PagerCacheStat(Pager *, int, int, u64*);
void sqlite3PagerClearCache(Pager*);
int sqlite3SectorSize(sqlite3_file *);
diff --git a/src/parse.y b/src/parse.y
index 867b62aa7..12621b434 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -21,6 +21,10 @@
*/
}
+// Function used to enlarge the parser stack, if needed
+%realloc parserStackRealloc
+%free sqlite3_free
+
// All token codes are small integers with #defines that begin with "TK_"
%token_prefix TK_
@@ -45,7 +49,7 @@
}
}
%stack_overflow {
- sqlite3ErrorMsg(pParse, "parser stack overflow");
+ sqlite3OomFault(pParse->db);
}
// The name of the generated procedure that implements the parser
@@ -547,6 +551,14 @@ cmd ::= select(X). {
}
return pSelect;
}
+
+ /* Memory allocator for parser stack resizing. This is a thin wrapper around
+ ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
+ ** testing.
+ */
+ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
+ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
+ }
}
%ifndef SQLITE_OMIT_CTE
@@ -1144,6 +1156,10 @@ expr(A) ::= CAST LP expr(E) AS typetoken(T) RP. {
expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP. {
A = sqlite3ExprFunction(pParse, Y, &X, D);
}
+expr(A) ::= idj(X) LP distinct(D) exprlist(Y) ORDER BY sortlist(O) RP. {
+ A = sqlite3ExprFunction(pParse, Y, &X, D);
+ sqlite3ExprAddFunctionOrderBy(pParse, A, O);
+}
expr(A) ::= idj(X) LP STAR RP. {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
}
@@ -1153,6 +1169,11 @@ expr(A) ::= idj(X) LP distinct(D) exprlist(Y) RP filter_over(Z). {
A = sqlite3ExprFunction(pParse, Y, &X, D);
sqlite3WindowAttach(pParse, A, Z);
}
+expr(A) ::= idj(X) LP distinct(D) exprlist(Y) ORDER BY sortlist(O) RP filter_over(Z). {
+ A = sqlite3ExprFunction(pParse, Y, &X, D);
+ sqlite3WindowAttach(pParse, A, Z);
+ sqlite3ExprAddFunctionOrderBy(pParse, A, O);
+}
expr(A) ::= idj(X) LP STAR RP filter_over(Z). {
A = sqlite3ExprFunction(pParse, 0, &X, 0);
sqlite3WindowAttach(pParse, A, Z);
diff --git a/src/pragma.c b/src/pragma.c
index f15c1be27..4ff640a65 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -31,6 +31,34 @@
#include "pragma.h"
/*
+** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
+** will be run with an analysis_limit set to the lessor of the value of
+** the following macro or to the actual analysis_limit if it is non-zero,
+** in order to prevent PRAGMA optimize from running for too long.
+**
+** The value of 2000 is chosen emperically so that the worst-case run-time
+** for PRAGMA optimize does not exceed 100 milliseconds against a variety
+** of test databases on a RaspberryPI-4 compiled using -Os and without
+** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
+** this paragraph, "worst-case" means that ANALYZE ends up being
+** run on every table in the database. The worst case typically only
+** happens if PRAGMA optimize is run on a database file for which ANALYZE
+** has not been previously run and the 0x10000 flag is included so that
+** all tables are analyzed. The usual case for PRAGMA optimize is that
+** no ANALYZE commands will be run at all, or if any ANALYZE happens it
+** will be against a single table, so that expected timing for PRAGMA
+** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000
+** flag or less than 100 microseconds without the 0x10000 flag.
+**
+** An analysis limit of 2000 is almost always sufficient for the query
+** planner to fully characterize an index. The additional accuracy from
+** a larger analysis is not usually helpful.
+*/
+#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT
+# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000
+#endif
+
+/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
** unrecognized string argument. The FULL and EXTRA option is disallowed
@@ -1124,7 +1152,11 @@ void sqlite3Pragma(
#endif
if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
+ if( (mask & SQLITE_WriteSchema)==0
+ || (db->flags & SQLITE_Defensive)==0
+ ){
+ db->flags |= mask;
+ }
}else{
db->flags &= ~mask;
if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
@@ -1757,8 +1789,8 @@ void sqlite3Pragma(
int r2; /* Previous key for WITHOUT ROWID tables */
int mxCol; /* Maximum non-virtual column number */
- if( !IsOrdinaryTable(pTab) ) continue;
if( pObjTab && pObjTab!=pTab ) continue;
+ if( !IsOrdinaryTable(pTab) ) continue;
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
@@ -1893,6 +1925,7 @@ void sqlite3Pragma(
** is REAL, we have to load the actual data using OP_Column
** to reliably determine if the value is a NULL. */
sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+ sqlite3ColumnDefault(v, pTab, j, 3);
jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
VdbeCoverage(v);
}
@@ -2083,6 +2116,38 @@ void sqlite3Pragma(
}
}
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Second pass to invoke the xIntegrity method on all virtual
+ ** tables.
+ */
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( IsOrdinaryTable(pTab) ) continue;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ continue;
+ }
+#endif
}
{
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -2346,44 +2411,63 @@ void sqlite3Pragma(
**
** The optional argument is a bitmask of optimizations to perform:
**
- ** 0x0001 Debugging mode. Do not actually perform any optimizations
- ** but instead return one line of text for each optimization
- ** that would have been done. Off by default.
+ ** 0x00001 Debugging mode. Do not actually perform any optimizations
+ ** but instead return one line of text for each optimization
+ ** that would have been done. Off by default.
**
- ** 0x0002 Run ANALYZE on tables that might benefit. On by default.
- ** See below for additional information.
+ ** 0x00002 Run ANALYZE on tables that might benefit. On by default.
+ ** See below for additional information.
**
- ** 0x0004 (Not yet implemented) Record usage and performance
- ** information from the current session in the
- ** database file so that it will be available to "optimize"
- ** pragmas run by future database connections.
+ ** 0x00010 Run all ANALYZE operations using an analysis_limit that
+ ** is the lessor of the current analysis_limit and the
+ ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option.
+ ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is
+ ** currently (2024-02-19) set to 2000, which is such that
+ ** the worst case run-time for PRAGMA optimize on a 100MB
+ ** database will usually be less than 100 milliseconds on
+ ** a RaspberryPI-4 class machine. On by default.
**
- ** 0x0008 (Not yet implemented) Create indexes that might have
- ** been helpful to recent queries
+ ** 0x10000 Look at tables to see if they need to be reanalyzed
+ ** due to growth or shrinkage even if they have not been
+ ** queried during the current connection. Off by default.
**
- ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all
- ** of the optimizations listed above except Debug Mode, including new
- ** optimizations that have not yet been invented. If new optimizations are
- ** ever added that should be off by default, those off-by-default
- ** optimizations will have bitmasks of 0x10000 or larger.
+ ** The default MASK is and always shall be 0x0fffe. In the current
+ ** implementation, the default mask only covers the 0x00002 optimization,
+ ** though additional optimizations that are covered by 0x0fffe might be
+ ** added in the future. Optimizations that are off by default and must
+ ** be explicitly requested have masks of 0x10000 or greater.
**
** DETERMINATION OF WHEN TO RUN ANALYZE
**
** In the current implementation, a table is analyzed if only if all of
** the following are true:
**
- ** (1) MASK bit 0x02 is set.
+ ** (1) MASK bit 0x00002 is set.
+ **
+ ** (2) The table is an ordinary table, not a virtual table or view.
**
- ** (2) The query planner used sqlite_stat1-style statistics for one or
- ** more indexes of the table at some point during the lifetime of
- ** the current connection.
+ ** (3) The table name does not begin with "sqlite_".
**
- ** (3) One or more indexes of the table are currently unanalyzed OR
- ** the number of rows in the table has increased by 25 times or more
- ** since the last time ANALYZE was run.
+ ** (4) One or more of the following is true:
+ ** (4a) The 0x10000 MASK bit is set.
+ ** (4b) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table.
+ ** (4c) The query planner used sqlite_stat1-style statistics for one
+ ** or more indexes of the table at some point during the lifetime
+ ** of the current connection.
+ **
+ ** (5) One or more of the following is true:
+ ** (5a) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table. (Same as 4a)
+ ** (5b) The number of rows in the table has increased or decreased by
+ ** 10-fold. In other words, the current size of the table is
+ ** 10 times larger than the size in sqlite_stat1 or else the
+ ** current size is less than 1/10th the size in sqlite_stat1.
**
** The rules for when tables are analyzed are likely to change in
- ** future releases.
+ ** future releases. Future versions of SQLite might accept a string
+ ** literal argument to this pragma that contains a mnemonic description
+ ** of the options rather than a bitmap.
*/
case PragTyp_OPTIMIZE: {
int iDbLast; /* Loop termination point for the schema loop */
@@ -2395,6 +2479,10 @@ void sqlite3Pragma(
LogEst szThreshold; /* Size threshold above which reanalysis needed */
char *zSubSql; /* SQL statement for the OP_SqlExec opcode */
u32 opMask; /* Mask of operations to perform */
+ int nLimit; /* Analysis limit to use */
+ int nCheck = 0; /* Number of tables to be optimized */
+ int nBtree = 0; /* Number of btrees to scan */
+ int nIndex; /* Number of indexes on the current table */
if( zRight ){
opMask = (u32)sqlite3Atoi(zRight);
@@ -2402,6 +2490,14 @@ void sqlite3Pragma(
}else{
opMask = 0xfffe;
}
+ if( (opMask & 0x10)==0 ){
+ nLimit = 0;
+ }else if( db->nAnalysisLimit>0
+ && db->nAnalysisLimit<SQLITE_DEFAULT_OPTIMIZE_LIMIT ){
+ nLimit = 0;
+ }else{
+ nLimit = SQLITE_DEFAULT_OPTIMIZE_LIMIT;
+ }
iTabCur = pParse->nTab++;
for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){
if( iDb==1 ) continue;
@@ -2410,23 +2506,61 @@ void sqlite3Pragma(
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
pTab = (Table*)sqliteHashData(k);
- /* If table pTab has not been used in a way that would benefit from
- ** having analysis statistics during the current session, then skip it.
- ** This also has the effect of skipping virtual tables and views */
- if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue;
+ /* This only works for ordinary tables */
+ if( !IsOrdinaryTable(pTab) ) continue;
- /* Reanalyze if the table is 25 times larger than the last analysis */
- szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 );
+ /* Do not scan system tables */
+ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue;
+
+ /* Find the size of the table as last recorded in sqlite_stat1.
+ ** If any index is unanalyzed, then the threshold is -1 to
+ ** indicate a new, unanalyzed index
+ */
+ szThreshold = pTab->nRowLogEst;
+ nIndex = 0;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ nIndex++;
if( !pIdx->hasStat1 ){
- szThreshold = 0; /* Always analyze if any index lacks statistics */
- break;
+ szThreshold = -1; /* Always analyze if any index lacks statistics */
}
}
- if( szThreshold ){
- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur,
- sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold);
+
+ /* If table pTab has not been used in a way that would benefit from
+ ** having analysis statistics during the current session, then skip it,
+ ** unless the 0x10000 MASK bit is set. */
+ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){
+ /* Check for size change if stat1 has been used for a query */
+ }else if( opMask & 0x10000 ){
+ /* Check for size change if 0x10000 is set */
+ }else if( pTab->pIndex!=0 && szThreshold<0 ){
+ /* Do analysis if unanalyzed indexes exists */
+ }else{
+ /* Otherwise, we can skip this table */
+ continue;
+ }
+
+ nCheck++;
+ if( nCheck==2 ){
+ /* If ANALYZE might be invoked two or more times, hold a write
+ ** transaction for efficiency */
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ }
+ nBtree += nIndex+1;
+
+ /* Reanalyze if the table is 10 times larger or smaller than
+ ** the last analysis. Unconditional reanalysis if there are
+ ** unanalyzed indexes. */
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
+ if( szThreshold>=0 ){
+ const LogEst iRange = 33; /* 10x size change */
+ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1),
+ szThreshold>=iRange ? szThreshold-iRange : -1,
+ szThreshold+iRange);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1));
VdbeCoverage(v);
}
zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"",
@@ -2436,11 +2570,27 @@ void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1);
}else{
- sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0,
+ zSubSql, P4_DYNAMIC);
}
}
}
sqlite3VdbeAddOp0(v, OP_Expire);
+
+ /* In a schema with a large number of tables and indexes, scale back
+ ** the analysis_limit to avoid excess run-time in the worst case.
+ */
+ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){
+ int iAddr, iEnd;
+ VdbeOp *aOp;
+ nLimit = 100*nLimit/nBtree;
+ if( nLimit<100 ) nLimit = 100;
+ aOp = sqlite3VdbeGetOp(v, 0);
+ iEnd = sqlite3VdbeCurrentAddr(v);
+ for(iAddr=0; iAddr<iEnd; iAddr++){
+ if( aOp[iAddr].opcode==OP_SqlExec ) aOp[iAddr].p2 = nLimit;
+ }
+ }
break;
}
@@ -2884,7 +3034,8 @@ static const sqlite3_module pragmaVtabModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/prepare.c b/src/prepare.c
index 9f843faa8..87569ee91 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -598,8 +598,6 @@ void sqlite3ParseObjectReset(Parse *pParse){
db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
assert( pParse->db->pParse==pParse );
db->pParse = pParse->pOuterParse;
- pParse->db = 0;
- pParse->disableLookaside = 0;
}
/*
@@ -870,6 +868,7 @@ static int sqlite3LockAndPrepare(
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
+ assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
diff --git a/src/printf.c b/src/printf.c
index 87ad91f79..186e95bb8 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -498,6 +498,7 @@ void sqlite3_str_vappendf(
if( xtype==etFLOAT ){
iRound = -precision;
}else if( xtype==etGENERIC ){
+ if( precision==0 ) precision = 1;
iRound = precision;
}else{
iRound = precision+1;
@@ -533,13 +534,14 @@ void sqlite3_str_vappendf(
}
exp = s.iDP-1;
- if( xtype==etGENERIC && precision>0 ) precision--;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
if( xtype==etGENERIC ){
+ assert( precision>0 );
+ precision--;
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
@@ -1369,7 +1371,7 @@ void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
/*****************************************************************************
-** Reference counted string storage
+** Reference counted string/blob storage
*****************************************************************************/
/*
@@ -1389,7 +1391,7 @@ char *sqlite3RCStrRef(char *z){
** Decrease the reference count by one. Free the string when the
** reference count reaches zero.
*/
-void sqlite3RCStrUnref(char *z){
+void sqlite3RCStrUnref(void *z){
RCStr *p = (RCStr*)z;
assert( p!=0 );
p--;
diff --git a/src/resolve.c b/src/resolve.c
index 7fc0151ad..5d0801e82 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -79,6 +79,8 @@ static void resolveAlias(
assert( iCol>=0 && iCol<pEList->nExpr );
pOrig = pEList->a[iCol].pExpr;
assert( pOrig!=0 );
+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+ if( pExpr->pAggInfo ) return;
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( db->mallocFailed ){
@@ -104,21 +106,36 @@ static void resolveAlias(
}
/*
-** Subqueries stores the original database, table and column names for their
-** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
-** Check to see if the zSpan given to this routine matches the zDb, zTab,
-** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
-** match anything.
+** Subqueries store the original database, table and column names for their
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN",
+** and mark the expression-list item by setting ExprList.a[].fg.eEName
+** to ENAME_TAB.
+**
+** Check to see if the zSpan/eEName of the expression-list item passed to this
+** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are
+** NULL then those fields will match anything. Return true if there is a match,
+** or false otherwise.
+**
+** SF_NestedFrom subqueries also store an entry for the implicit rowid (or
+** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID,
+** and setting zSpan to "DATABASE.TABLE.<rowid-alias>". This type of pItem
+** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid)
+** is set to 1 if there is this kind of match.
*/
int sqlite3MatchEName(
const struct ExprList_item *pItem,
const char *zCol,
const char *zTab,
- const char *zDb
+ const char *zDb,
+ int *pbRowid
){
int n;
const char *zSpan;
- if( pItem->fg.eEName!=ENAME_TAB ) return 0;
+ int eEName = pItem->fg.eEName;
+ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){
+ return 0;
+ }
+ assert( pbRowid==0 || *pbRowid==0 );
zSpan = pItem->zEName;
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
@@ -130,9 +147,11 @@ int sqlite3MatchEName(
return 0;
}
zSpan += n+1;
- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
- return 0;
+ if( zCol ){
+ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0;
+ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0;
}
+ if( eEName==ENAME_ROWID ) *pbRowid = 1;
return 1;
}
@@ -165,6 +184,7 @@ Bitmask sqlite3ExprColUsed(Expr *pExpr){
assert( ExprUseYTab(pExpr) );
pExTab = pExpr->y.pTab;
assert( pExTab!=0 );
+ assert( n < pExTab->nCol );
if( (pExTab->tabFlags & TF_HasGenerated)!=0
&& (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
){
@@ -259,13 +279,13 @@ static int lookupName(
Parse *pParse, /* The parsing context */
const char *zDb, /* Name of the database containing table, or NULL */
const char *zTab, /* Name of table containing column, or NULL */
- const char *zCol, /* Name of the column. */
+ const Expr *pRight, /* Name of the column. */
NameContext *pNC, /* The name context used to resolve the name */
Expr *pExpr /* Make this EXPR node point to the selected column */
){
int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
- int cntTab = 0; /* Number of matching table names */
+ int cntTab = 0; /* Number of potential "rowid" matches */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
SrcItem *pItem; /* Use for looping over pSrcList items */
@@ -276,6 +296,7 @@ static int lookupName(
Table *pTab = 0; /* Table holding the row */
Column *pCol; /* A column of pTab */
ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
+ const char *zCol = pRight->u.zToken;
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
@@ -342,39 +363,49 @@ static int lookupName(
assert( pEList!=0 );
assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
- if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
+ int bRowid = 0; /* True if possible rowid match */
+ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){
continue;
}
- if( cnt>0 ){
- if( pItem->fg.isUsing==0
- || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
- ){
- /* Two or more tables have the same column name which is
- ** not joined by USING. This is an error. Signal as much
- ** by clearing pFJMatch and letting cnt go above 1. */
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else
- if( (pItem->fg.jointype & JT_RIGHT)==0 ){
- /* An INNER or LEFT JOIN. Use the left-most table */
- continue;
- }else
- if( (pItem->fg.jointype & JT_LEFT)==0 ){
- /* A RIGHT JOIN. Use the right-most table */
- cnt = 0;
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else{
- /* For a FULL JOIN, we must construct a coalesce() func */
- extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ if( bRowid==0 ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ }
}
+ cnt++;
+ hit = 1;
+ }else if( cnt>0 ){
+ /* This is a potential rowid match, but there has already been
+ ** a real match found. So this can be ignored. */
+ continue;
}
- cnt++;
- cntTab = 2;
+ cntTab++;
pMatch = pItem;
pExpr->iColumn = j;
pEList->a[j].fg.bUsed = 1;
- hit = 1;
+
+ /* rowid cannot be part of a USING clause - assert() this. */
+ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 );
if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
@@ -569,10 +600,10 @@ static int lookupName(
&& pMatch
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
- && ALWAYS(VisibleRowid(pMatch->pTab))
+ && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
cnt = 1;
- pExpr->iColumn = -1;
+ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -725,12 +756,17 @@ static int lookupName(
sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
}else if( zTab ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
+ }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){
+ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a"
+ " string literal in single-quotes?",
+ zErr, zCol);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
pTopNC->nNcErr++;
+ eNewExprOp = TK_NULL;
}
assert( pFJMatch==0 );
@@ -757,7 +793,7 @@ static int lookupName(
** If a generated column is referenced, set bits for every column
** of the table.
*/
- if( pExpr->iColumn>=0 && pMatch!=0 ){
+ if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
}
@@ -971,7 +1007,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/
case TK_ID:
case TK_DOT: {
- const char *zColumn;
const char *zTable;
const char *zDb;
Expr *pRight;
@@ -980,7 +1015,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
zDb = 0;
zTable = 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- zColumn = pExpr->u.zToken;
+ pRight = pExpr;
}else{
Expr *pLeft = pExpr->pLeft;
testcase( pNC->ncFlags & NC_IdxExpr );
@@ -999,14 +1034,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) );
zTable = pLeft->u.zToken;
- zColumn = pRight->u.zToken;
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
}
}
- return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
+ return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr);
}
/* Resolve function names
@@ -1025,6 +1059,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
+ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER );
zId = pExpr->u.zToken;
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
@@ -1166,6 +1201,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pNC->nNcErr++;
}
#endif
+ else if( is_agg==0 && pExpr->pLeft ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ pNC->nNcErr++;
+ }
if( is_agg ){
/* Window functions may not be arguments of aggregate functions.
** Or arguments of other window functions. But aggregate functions
@@ -1184,6 +1223,11 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
Select *pSel = pNC->pWinSelect;
@@ -1212,11 +1256,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
while( pNC2
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
){
- pExpr->op2++;
+ pExpr->op2 += (1 + pNC2->nNestedSelect);
pNC2 = pNC2->pNext;
}
assert( pDef!=0 || IN_RENAME_OBJECT );
if( pNC2 && pDef ){
+ pExpr->op2 += pNC2->nNestedSelect;
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
@@ -1747,10 +1792,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
while( p ){
assert( (p->selFlags & SF_Expanded)!=0 );
assert( (p->selFlags & SF_Resolved)==0 );
- assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */
p->selFlags |= SF_Resolved;
-
/* Resolve the expressions in the LIMIT and OFFSET clauses. These
** are not allowed to refer to any names, so pass an empty NameContext.
*/
@@ -1777,6 +1820,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Recursively resolve names in all subqueries in the FROM clause
*/
+ if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
@@ -1801,6 +1845,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
}
+ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
+ pOuterNC->nNestedSelect--;
+ }
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
diff --git a/src/select.c b/src/select.c
index 90b284036..81e802d6e 100644
--- a/src/select.c
+++ b/src/select.c
@@ -184,6 +184,9 @@ Select *sqlite3SelectNew(
void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
+void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
+}
/*
** Return a pointer to the right-most SELECT statement in a compound.
@@ -454,6 +457,7 @@ static void unsetJoinExpr(Expr *p, int iTable, int nullable){
}
if( p->op==TK_FUNCTION ){
assert( ExprUseXList(p) );
+ assert( p->pLeft==0 );
if( p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
@@ -2318,7 +2322,8 @@ void sqlite3SubqueryColumnTypes(
NameContext sNC;
assert( pSelect!=0 );
- assert( (pSelect->selFlags & SF_Resolved)!=0 );
+ testcase( (pSelect->selFlags & SF_Resolved)==0 );
+ assert( (pSelect->selFlags & SF_Resolved)!=0 || IN_RENAME_OBJECT );
assert( pTab->nCol==pSelect->pEList->nExpr || pParse->nErr>0 );
assert( aff==SQLITE_AFF_NONE || aff==SQLITE_AFF_BLOB );
if( db->mallocFailed || IN_RENAME_OBJECT ) return;
@@ -3202,9 +3207,7 @@ multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete,
- pDelete);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
}
return rc;
}
@@ -3755,8 +3758,7 @@ static int multiSelectOrderBy(
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);
}
pSplit->pPrior = pPrior;
pPrior->pNext = pSplit;
@@ -4577,9 +4579,7 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- sqlite3ParserAddCleanup(pToplevel,
- (void(*)(sqlite3*,void*))sqlite3DeleteTable,
- pTabToDel);
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
@@ -5626,8 +5626,7 @@ static struct Cte *searchWith(
With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
if( bFree ){
- pWith = (With*)sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3WithDelete,
+ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,
pWith);
if( pWith==0 ) return 0;
}
@@ -6114,6 +6113,7 @@ static int selectExpander(Walker *pWalker, Select *p){
char *zTName = 0; /* text of name of TABLE */
int iErrOfst;
if( pE->op==TK_DOT ){
+ assert( (selFlags & SF_NestedFrom)==0 );
assert( pE->pLeft!=0 );
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
zTName = pE->pLeft->u.zToken;
@@ -6124,6 +6124,7 @@ static int selectExpander(Walker *pWalker, Select *p){
iErrOfst = pE->w.iOfst;
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ int nAdd; /* Number of cols including rowid */
Table *pTab = pFrom->pTab; /* Table for this data source */
ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
char *zTabName; /* AS name for this data source */
@@ -6141,6 +6142,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
+ assert( VisibleRowid(pTab)==0 );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -6171,33 +6173,48 @@ static int selectExpander(Walker *pWalker, Select *p){
}else{
pUsing = 0;
}
- for(j=0; j<pTab->nCol; j++){
- char *zName = pTab->aCol[j].zCnName;
+
+ nAdd = pTab->nCol + (VisibleRowid(pTab) && (selFlags&SF_NestedFrom));
+ for(j=0; j<nAdd; j++){
+ const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
- assert( zName );
- if( zTName
- && pNestedFrom
- && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0
- ){
- continue;
- }
+ if( j==pTab->nCol ){
+ zName = sqlite3RowidAlias(pTab);
+ if( zName==0 ) continue;
+ }else{
+ zName = pTab->aCol[j].zCnName;
- /* If a column is marked as 'hidden', omit it from the expanded
- ** result-set list unless the SELECT has the SF_IncludeHidden
- ** bit set.
- */
- if( (p->selFlags & SF_IncludeHidden)==0
- && IsHiddenColumn(&pTab->aCol[j])
- ){
- continue;
- }
- if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
- && zTName==0
- && (selFlags & (SF_NestedFrom))==0
- ){
- continue;
+ /* If pTab is actually an SF_NestedFrom sub-select, do not
+ ** expand any ENAME_ROWID columns. */
+ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){
+ continue;
+ }
+
+ if( zTName
+ && pNestedFrom
+ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0
+ ){
+ continue;
+ }
+
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
+ */
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
+ continue;
+ }
+ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ && zTName==0
+ && (selFlags & (SF_NestedFrom))==0
+ ){
+ continue;
+ }
}
+ assert( zName );
tableSeen = 1;
if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){
@@ -6247,11 +6264,11 @@ static int selectExpander(Walker *pWalker, Select *p){
zSchemaName, zTabName, zName);
testcase( pX->zEName==0 );
}
- pX->fg.eEName = ENAME_TAB;
+ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB);
if( (pFrom->fg.isUsing
&& sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0)
|| (pUsing && sqlite3IdListIndex(pUsing, zName)>=0)
- || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ || (j<pTab->nCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND))
){
pX->fg.bNoExpand = 1;
}
@@ -6353,10 +6370,11 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
SrcList *pTabList;
SrcItem *pFrom;
- assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
+ testcase( (p->selFlags & SF_Resolved)==0 );
+ assert( (p->selFlags & SF_Resolved) || IN_RENAME_OBJECT );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -6426,6 +6444,8 @@ void sqlite3SelectPrep(
*/
static void printAggInfo(AggInfo *pAggInfo){
int ii;
+ sqlite3DebugPrintf("AggInfo %d/%p:\n",
+ pAggInfo->selId, pAggInfo);
for(ii=0; ii<pAggInfo->nColumn; ii++){
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
sqlite3DebugPrintf(
@@ -6472,8 +6492,14 @@ static void analyzeAggFuncArgs(
pNC->ncFlags |= NC_InAggFunc;
for(i=0; i<pAggInfo->nFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !IsWindowFunc(pExpr) );
if( ExprHasProperty(pExpr, EP_WinFunc) ){
@@ -6628,6 +6654,36 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
pFunc->pFunc->zName));
}
}
+ if( pFunc->iOBTab>=0 ){
+ ExprList *pOBList;
+ KeyInfo *pKeyInfo;
+ int nExtra = 0;
+ assert( pFunc->pFExpr->pLeft!=0 );
+ assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pFunc->pFExpr->pLeft) );
+ assert( pFunc->pFunc!=0 );
+ pOBList = pFunc->pFExpr->pLeft->x.pList;
+ if( !pFunc->bOBUnique ){
+ nExtra++; /* One extra column for the OP_Sequence */
+ }
+ if( pFunc->bOBPayload ){
+ /* extra columns for the function arguments */
+ assert( ExprUseXList(pFunc->pFExpr) );
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ if( pFunc->bUseSubtype ){
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
+ if( !pFunc->bOBUnique && pParse->nErr==0 ){
+ pKeyInfo->nKeyField++;
+ }
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)",
+ pFunc->pFunc->zName));
+ }
}
}
@@ -6643,13 +6699,56 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
+ if( pF->iOBTab>=0 ){
+ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs
+ ** were stored in emphermal table pF->iOBTab. Here, we extract those
+ ** inputs (in ORDER BY order) and make all calls to OP_AggStep
+ ** before doing the OP_AggFinal call. */
+ int iTop; /* Start of loop for extracting columns */
+ int nArg; /* Number of columns to extract */
+ int nKey; /* Key columns to be skipped */
+ int regAgg; /* Extract into this array */
+ int j; /* Loop counter */
+
+ assert( pF->pFunc!=0 );
+ nArg = pList->nExpr;
+ regAgg = sqlite3GetTempRange(pParse, nArg);
+
+ if( pF->bOBPayload==0 ){
+ nKey = 0;
+ }else{
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ assert( pF->pFExpr->pLeft->x.pList!=0 );
+ nKey = pF->pFExpr->pLeft->x.pList->nExpr;
+ if( ALWAYS(!pF->bOBUnique) ) nKey++;
+ }
+ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
+ }
+ if( pF->bUseSubtype ){
+ int regSubtype = sqlite3GetTempReg(pParse);
+ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
+ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
+ }
+ sqlite3ReleaseTempReg(pParse, regSubtype);
+ }
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, iTop);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
+ }
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
-
/*
** Generate code that will update the accumulator memory cells for an
** aggregate based on the current cursor position.
@@ -6658,6 +6757,13 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
** registers if register regAcc contains 0. The caller will take care
** of setting and clearing regAcc.
+**
+** For an ORDER BY aggregate, the actual accumulator memory cell update
+** is deferred until after all input rows have been received, so that they
+** can be run in the requested order. In that case, instead of invoking
+** OP_AggStep to update the accumulator, just add the arguments that would
+** have been passed into OP_AggStep into the sorting ephemeral table
+** (along with the appropriate sort key).
*/
static void updateAccumulator(
Parse *pParse,
@@ -6679,9 +6785,12 @@ static void updateAccumulator(
int nArg;
int addrNext = 0;
int regAgg;
+ int regAggSz = 0;
+ int regDistinct = 0;
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
assert( !IsWindowFunc(pF->pFExpr) );
+ assert( pF->pFunc!=0 );
pList = pF->pFExpr->x.pList;
if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
@@ -6705,9 +6814,55 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
}
- if( pList ){
+ if( pF->iOBTab>=0 ){
+ /* Instead of invoking AggStep, we must push the arguments that would
+ ** have been passed to AggStep onto the sorting table. */
+ int jj; /* Registered used so far in building the record */
+ ExprList *pOBList; /* The ORDER BY clause */
+ assert( pList!=0 );
+ nArg = pList->nExpr;
+ assert( nArg>0 );
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( pF->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ pOBList = pF->pFExpr->pLeft->x.pList;
+ assert( pOBList!=0 );
+ assert( pOBList->nExpr>0 );
+ regAggSz = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ regAggSz++; /* One register for OP_Sequence */
+ }
+ if( pF->bOBPayload ){
+ regAggSz += nArg;
+ }
+ if( pF->bUseSubtype ){
+ regAggSz += nArg;
+ }
+ regAggSz++; /* One extra register to hold result of MakeRecord */
+ regAgg = sqlite3GetTempRange(pParse, regAggSz);
+ regDistinct = regAgg;
+ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
+ jj = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
+ jj++;
+ }
+ if( pF->bOBPayload ){
+ regDistinct = regAgg+jj;
+ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
+ jj += nArg;
+ }
+ if( pF->bUseSubtype ){
+ int kk;
+ int regBase = pF->bOBPayload ? regDistinct : regAgg;
+ for(kk=0; kk<nArg; kk++, jj++){
+ sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
+ }
+ }
+ }else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
+ regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
@@ -6718,26 +6873,37 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
}
pF->iDistinct = codeDistinct(pParse, eDistinctType,
- pF->iDistinct, addrNext, pList, regAgg);
- }
- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
- CollSeq *pColl = 0;
- struct ExprList_item *pItem;
- int j;
- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
- for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
- pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- }
- if( !pColl ){
- pColl = pParse->db->pDfltColl;
+ pF->iDistinct, addrNext, pList, regDistinct);
+ }
+ if( pF->iOBTab>=0 ){
+ /* Insert a new record into the ORDER BY table */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1,
+ regAgg+regAggSz-1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1,
+ regAgg, regAggSz-1);
+ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz);
+ }else{
+ /* Invoke the AggStep function */
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ CollSeq *pColl = 0;
+ struct ExprList_item *pItem;
+ int j;
+ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
+ for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
+ pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
+ (char *)pColl, P4_COLLSEQ);
}
- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
- sqlite3ReleaseTempRange(pParse, regAgg, nArg);
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
@@ -6896,7 +7062,8 @@ static SrcItem *isSelfJoinView(
/*
** Deallocate a single AggInfo object
*/
-static void agginfoFree(sqlite3 *db, AggInfo *p){
+static void agginfoFree(sqlite3 *db, void *pArg){
+ AggInfo *p = (AggInfo*)pArg;
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
@@ -6970,7 +7137,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
- sqlite3ExprListDelete(db, pSub->pEList);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
@@ -7150,9 +7317,8 @@ int sqlite3Select(
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- p->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ p->pOrderBy);
testcase( pParse->earlyCleanup );
p->pOrderBy = 0;
}
@@ -7258,6 +7424,7 @@ int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pItem->iCursor, 0);
}
}
if( pItem->fg.jointype & JT_LTORJ ){
@@ -7272,17 +7439,15 @@ int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("RIGHT-JOIN simplifies to JOIN on term %d\n",j));
pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pI2->iCursor, 1);
}
}
}
- for(j=pTabList->nSrc-1; j>=i; j--){
+ for(j=pTabList->nSrc-1; j>=0; j--){
pTabList->a[j].fg.jointype &= ~JT_LTORJ;
if( pTabList->a[j].fg.jointype & JT_RIGHT ) break;
}
}
- assert( pItem->iCursor>=0 );
- unsetJoinExpr(p->pWhere, pItem->iCursor,
- pTabList->a[0].fg.jointype & JT_LTORJ);
}
/* No further action if this term of the FROM clause is not a subquery */
@@ -7345,9 +7510,8 @@ int sqlite3Select(
){
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- pSub->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ pSub->pOrderBy);
pSub->pOrderBy = 0;
}
@@ -7876,8 +8040,7 @@ int sqlite3Select(
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
if( pAggInfo ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
+ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);
testcase( pParse->earlyCleanup );
}
if( db->mallocFailed ){
@@ -8385,6 +8548,12 @@ select_end:
sqlite3ExprListDelete(db, pMinMaxOrderBy);
#ifdef SQLITE_DEBUG
if( pAggInfo && !db->mallocFailed ){
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n"));
+ printAggInfo(pAggInfo);
+ }
+#endif
for(i=0; i<pAggInfo->nColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
if( pExpr==0 ) continue;
diff --git a/src/shell.c.in b/src/shell.c.in
index 07d92d014..07e4b201c 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -234,29 +234,67 @@ typedef unsigned char u8;
/* string conversion routines only needed on Win32 */
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
-extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
-extern char *sqlite3_win32_utf8_to_mbcs_v2(const char *, int);
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
#endif
-/* On Windows, we normally run with output mode of TEXT so that \n characters
-** are automatically translated into \r\n. However, this behavior needs
-** to be disabled in some cases (ex: when generating CSV output and when
-** rendering quoted strings that contain \n characters). The following
-** routines take care of that.
-*/
-#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
-static void setBinaryMode(FILE *file, int isOutput){
- if( isOutput ) fflush(file);
- _setmode(_fileno(file), _O_BINARY);
-}
-static void setTextMode(FILE *file, int isOutput){
- if( isOutput ) fflush(file);
- _setmode(_fileno(file), _O_TEXT);
-}
+/* Use console I/O package as a direct INCLUDE. */
+#define SQLITE_INTERNAL_LINKAGE static
+
+#ifdef SQLITE_SHELL_FIDDLE
+/* Deselect most features from the console I/O package for Fiddle. */
+# define SQLITE_CIO_NO_REDIRECT
+# define SQLITE_CIO_NO_CLASSIFY
+# define SQLITE_CIO_NO_TRANSLATE
+# define SQLITE_CIO_NO_SETMODE
+#endif
+INCLUDE ../ext/consio/console_io.h
+INCLUDE ../ext/consio/console_io.c
+
+#ifndef SQLITE_SHELL_FIDDLE
+
+/* From here onward, fgets() is redirected to the console_io library. */
+# define fgets(b,n,f) fGetsUtf8(b,n,f)
+/*
+ * Define macros for emitting output text in various ways:
+ * sputz(s, z) => emit 0-terminated string z to given stream s
+ * sputf(s, f, ...) => emit varargs per format f to given stream s
+ * oputz(z) => emit 0-terminated string z to default stream
+ * oputf(f, ...) => emit varargs per format f to default stream
+ * eputz(z) => emit 0-terminated string z to error stream
+ * eputf(f, ...) => emit varargs per format f to error stream
+ * oputb(b, n) => emit char buffer b[0..n-1] to default stream
+ *
+ * Note that the default stream is whatever has been last set via:
+ * setOutputStream(FILE *pf)
+ * This is normally the stream that CLI normal output goes to.
+ * For the stand-alone CLI, it is stdout with no .output redirect.
+ *
+ * The ?putz(z) forms are required for the Fiddle builds for string literal
+ * output, in aid of enforcing format string to argument correspondence.
+ */
+# define sputz(s,z) fPutsUtf8(z,s)
+# define sputf fPrintfUtf8
+# define oputz(z) oPutsUtf8(z)
+# define oputf oPrintfUtf8
+# define eputz(z) ePutsUtf8(z)
+# define eputf ePrintfUtf8
+# define oputb(buf,na) oPutbUtf8(buf,na)
+
#else
-# define setBinaryMode(X,Y)
-# define setTextMode(X,Y)
+/* For Fiddle, all console handling and emit redirection is omitted. */
+/* These next 3 macros are for emitting formatted output. When complaints
+ * from the WASM build are issued for non-formatted output, (when a mere
+ * string literal is to be emitted, the ?putz(z) forms should be used.
+ * (This permits compile-time checking of format string / argument mismatch.)
+ */
+# define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
+# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__)
+# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
+/* These next 3 macros are for emitting simple string literals. */
+# define oputz(z) fputs(z,stdout)
+# define eputz(z) fputs(z,stderr)
+# define sputz(fp,z) fputs(z,fp)
+# define oputb(buf,na) fwrite(buf,1,na,stdout)
#endif
/* True if the timer is enabled */
@@ -331,10 +369,10 @@ static void endTimer(void){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
- printf("Run Time: real %.3f user %f sys %f\n",
- (iEnd - iBegin)*0.001,
- timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
- timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
+ sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
+ (iEnd - iBegin)*0.001,
+ timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
+ timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
}
@@ -410,10 +448,10 @@ static void endTimer(void){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
- printf("Run Time: real %.3f user %f sys %f\n",
- (ftWallEnd - ftWallBegin)*0.001,
- timeDiff(&ftUserBegin, &ftUserEnd),
- timeDiff(&ftKernelBegin, &ftKernelEnd));
+ sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
+ (ftWallEnd - ftWallBegin)*0.001,
+ timeDiff(&ftUserBegin, &ftUserEnd),
+ timeDiff(&ftKernelBegin, &ftKernelEnd));
}
}
@@ -449,24 +487,10 @@ static int bail_on_error = 0;
*/
static int stdin_is_interactive = 1;
-#if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \
- && !defined(SHELL_OMIT_WIN_UTF8)
-# define SHELL_WIN_UTF8_OPT 1
-#else
-# define SHELL_WIN_UTF8_OPT 0
-#endif
-
-#if SHELL_WIN_UTF8_OPT
/*
-** Setup console for UTF-8 input/output when following variable true.
-*/
-static int console_utf8 = 0;
-#endif
-
-/*
-** On Windows systems we have to know if standard output is a console
-** in order to translate UTF-8 into MBCS. The following variable is
-** true if translation is required.
+** On Windows systems we need to know if standard output is a console
+** in order to show that UTF-16 translation is done in the sign-on
+** banner. The following variable is true if it is the console.
*/
static int stdout_is_console = 1;
@@ -588,182 +612,17 @@ static char *dynamicContinuePrompt(void){
shell_strncpy(dynPrompt.dynamicPrompt, "(x.", 4);
dynPrompt.dynamicPrompt[2] = (char)('0'+dynPrompt.inParenLevel);
}
- shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4);
+ shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3,
+ PROMPT_LEN_MAX-4);
}
}
return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */
-#if SHELL_WIN_UTF8_OPT
-/* Following struct is used for -utf8 operation. */
-static struct ConsoleState {
- int stdinEof; /* EOF has been seen on console input */
- int infsMode; /* Input file stream mode upon shell start */
- UINT inCodePage; /* Input code page upon shell start */
- UINT outCodePage; /* Output code page upon shell start */
- HANDLE hConsoleIn; /* Console input handle */
- DWORD consoleMode; /* Console mode upon shell start */
-} conState = { 0, 0, 0, 0, INVALID_HANDLE_VALUE, 0 };
-
-#ifndef _O_U16TEXT /* For build environments lacking this constant: */
-# define _O_U16TEXT 0x20000
-#endif
-
-/*
-** Prepare console, (if known to be a WIN32 console), for UTF-8
-** input (from either typing or suitable paste operations) and for
-** UTF-8 rendering. This may "fail" with a message to stderr, where
-** the preparation is not done and common "code page" issues occur.
-*/
-static void console_prepare(void){
- HANDLE hCI = GetStdHandle(STD_INPUT_HANDLE);
- DWORD consoleMode = 0;
- if( isatty(0) && GetFileType(hCI)==FILE_TYPE_CHAR
- && GetConsoleMode( hCI, &consoleMode) ){
- if( !IsValidCodePage(CP_UTF8) ){
- fprintf(stderr, "Cannot use UTF-8 code page.\n");
- console_utf8 = 0;
- return;
- }
- conState.hConsoleIn = hCI;
- conState.consoleMode = consoleMode;
- conState.inCodePage = GetConsoleCP();
- conState.outCodePage = GetConsoleOutputCP();
- SetConsoleCP(CP_UTF8);
- SetConsoleOutputCP(CP_UTF8);
- consoleMode |= ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
- SetConsoleMode(conState.hConsoleIn, consoleMode);
- conState.infsMode = _setmode(_fileno(stdin), _O_U16TEXT);
- console_utf8 = 1;
- }else{
- console_utf8 = 0;
- }
-}
-
-/*
-** Undo the effects of console_prepare(), if any.
-*/
-static void SQLITE_CDECL console_restore(void){
- if( console_utf8 && conState.inCodePage!=0
- && conState.hConsoleIn!=INVALID_HANDLE_VALUE ){
- _setmode(_fileno(stdin), conState.infsMode);
- SetConsoleCP(conState.inCodePage);
- SetConsoleOutputCP(conState.outCodePage);
- SetConsoleMode(conState.hConsoleIn, conState.consoleMode);
- /* Avoid multiple calls. */
- conState.hConsoleIn = INVALID_HANDLE_VALUE;
- conState.consoleMode = 0;
- console_utf8 = 0;
- }
-}
-
-/*
-** Collect input like fgets(...) with special provisions for input
-** from the Windows console to get around its strange coding issues.
-** Defers to plain fgets() when input is not interactive or when the
-** startup option, -utf8, has not been provided or taken effect.
-*/
-static char* utf8_fgets(char *buf, int ncmax, FILE *fin){
- if( fin==0 ) fin = stdin;
- if( fin==stdin && stdin_is_interactive && console_utf8 ){
-# define SQLITE_IALIM 150
- wchar_t wbuf[SQLITE_IALIM];
- int lend = 0;
- int noc = 0;
- if( ncmax==0 || conState.stdinEof ) return 0;
- buf[0] = 0;
- while( noc<ncmax-7-1 && !lend ){
- /* There is room for at least 2 more characters and a 0-terminator. */
- int na = (ncmax > SQLITE_IALIM*4+1 + noc)
- ? SQLITE_IALIM : (ncmax-1 - noc)/4;
-# undef SQLITE_IALIM
- DWORD nbr = 0;
- BOOL bRC = ReadConsoleW(conState.hConsoleIn, wbuf, na, &nbr, 0);
- if( !bRC || (noc==0 && nbr==0) ) return 0;
- if( nbr > 0 ){
- int nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR,
- wbuf,nbr,0,0,0,0);
- if( nmb !=0 && noc+nmb <= ncmax ){
- int iseg = noc;
- nmb = WideCharToMultiByte(CP_UTF8,WC_COMPOSITECHECK|WC_DEFAULTCHAR,
- wbuf,nbr,buf+noc,nmb,0,0);
- noc += nmb;
- /* Fixup line-ends as coded by Windows for CR (or "Enter".)*/
- if( noc > 0 ){
- if( buf[noc-1]=='\n' ){
- lend = 1;
- if( noc > 1 && buf[noc-2]=='\r' ){
- buf[noc-2] = '\n';
- --noc;
- }
- }
- }
- /* Check for ^Z (anywhere in line) too. */
- while( iseg < noc ){
- if( buf[iseg]==0x1a ){
- conState.stdinEof = 1;
- noc = iseg; /* Chop ^Z and anything following. */
- break;
- }
- ++iseg;
- }
- }else break; /* Drop apparent garbage in. (Could assert.) */
- }else break;
- }
- /* If got nothing, (after ^Z chop), must be at end-of-file. */
- if( noc == 0 ) return 0;
- buf[noc] = 0;
- return buf;
- }else{
- return fgets(buf, ncmax, fin);
- }
-}
-
-# define fgets(b,n,f) utf8_fgets(b,n,f)
-#endif /* SHELL_WIN_UTF8_OPT */
-
-/*
-** Render output like fprintf(). Except, if the output is going to the
-** console and if this is running on a Windows machine, and if the -utf8
-** option is unavailable or (available and inactive), translate the
-** output from UTF-8 into MBCS for output through 8-bit stdout stream.
-** (With -utf8 active, no translation is needed and must not be done.)
-*/
-#if defined(_WIN32) || defined(WIN32)
-void utf8_printf(FILE *out, const char *zFormat, ...){
- va_list ap;
- va_start(ap, zFormat);
- if( stdout_is_console && (out==stdout || out==stderr)
-# if SHELL_WIN_UTF8_OPT
- && !console_utf8
-# endif
- ){
- char *z1 = sqlite3_vmprintf(zFormat, ap);
- char *z2 = sqlite3_win32_utf8_to_mbcs_v2(z1, 0);
- sqlite3_free(z1);
- fputs(z2, out);
- sqlite3_free(z2);
- }else{
- vfprintf(out, zFormat, ap);
- }
- va_end(ap);
-}
-#elif !defined(utf8_printf)
-# define utf8_printf fprintf
-#endif
-
-/*
-** Render output like fprintf(). This should not be used on anything that
-** includes string formatting (e.g. "%s").
-*/
-#if !defined(raw_printf)
-# define raw_printf fprintf
-#endif
-
/* Indicate out-of-memory and exit. */
static void shell_out_of_memory(void){
- raw_printf(stderr,"Error: out of memory\n");
+ eputz("Error: out of memory\n");
exit(1);
}
@@ -795,18 +654,18 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
- utf8_printf(iotrace, "%s", z);
+ sputf(iotrace, "%s", z);
sqlite3_free(z);
}
#endif
/*
-** Output string zUtf to stream pOut as w characters. If w is negative,
+** Output string zUtf to Out stream as w characters. If w is negative,
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
*/
-static void utf8_width_print(FILE *pOut, int w, const char *zUtf){
+static void utf8_width_print(int w, const char *zUtf){
int i;
int n;
int aw = w<0 ? -w : w;
@@ -821,11 +680,11 @@ static void utf8_width_print(FILE *pOut, int w, const char *zUtf){
}
}
if( n>=aw ){
- utf8_printf(pOut, "%.*s", i, zUtf);
+ oputf("%.*s", i, zUtf);
}else if( w<0 ){
- utf8_printf(pOut, "%*s%s", aw-n, "", zUtf);
+ oputf("%*s%s", aw-n, "", zUtf);
}else{
- utf8_printf(pOut, "%s%*s", zUtf, aw-n, "");
+ oputf("%s%*s", zUtf, aw-n, "");
}
}
@@ -885,15 +744,15 @@ static int strlenChar(const char *z){
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
-#ifdef _WIN32
- struct _stat x = {0};
+#if defined(_WIN32) || defined(WIN32)
+ struct __stat64 x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
/* On Windows, open first, then check the stream nature. This order
** is necessary because _stat() and sibs, when checking a named pipe,
** effectively break the pipe as its supplier sees it. */
FILE *rv = fopen(zFile, "rb");
if( rv==0 ) return 0;
- if( _fstat(_fileno(rv), &x) != 0
+ if( _fstat64(_fileno(rv), &x) != 0
|| !STAT_CHR_SRC(x.st_mode)){
fclose(rv);
rv = 0;
@@ -948,27 +807,6 @@ static char *local_getline(char *zLine, FILE *in){
break;
}
}
-#if defined(_WIN32) || defined(WIN32)
- /* For interactive input on Windows systems, without -utf8,
- ** translate the multi-byte characterset characters into UTF-8.
- ** This is the translation that predates the -utf8 option. */
- if( stdin_is_interactive && in==stdin
-# if SHELL_WIN_UTF8_OPT
- && !console_utf8
-# endif /* SHELL_WIN_UTF8_OPT */
- ){
- char *zTrans = sqlite3_win32_mbcs_to_utf8_v2(zLine, 0);
- if( zTrans ){
- i64 nTrans = strlen(zTrans)+1;
- if( nTrans>nLine ){
- zLine = realloc(zLine, nTrans);
- shell_check_oom(zLine);
- }
- memcpy(zLine, zTrans, nTrans);
- sqlite3_free(zTrans);
- }
- }
-#endif /* defined(_WIN32) || defined(WIN32) */
return zLine;
}
@@ -995,7 +833,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){
}else{
zPrompt = isContinuation ? CONTINUATION_PROMPT : mainPrompt;
#if SHELL_USE_LOCAL_GETLINE
- printf("%s", zPrompt);
+ sputz(stdout, zPrompt);
fflush(stdout);
do{
zResult = local_getline(zPrior, stdin);
@@ -1242,7 +1080,7 @@ static void shellDtostr(
char z[400];
if( n<1 ) n = 1;
if( n>350 ) n = 350;
- sprintf(z, "%#+.*e", n, r);
+ sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r);
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
}
@@ -1384,6 +1222,9 @@ INCLUDE ../ext/misc/sqlar.c
INCLUDE ../ext/expert/sqlite3expert.h
INCLUDE ../ext/expert/sqlite3expert.c
+INCLUDE ../ext/intck/sqlite3intck.h
+INCLUDE ../ext/intck/sqlite3intck.c
+
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
#define SQLITE_SHELL_HAVE_RECOVER 1
#else
@@ -1463,6 +1304,7 @@ struct ShellState {
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
u8 bSafeMode; /* True to prohibit unsafe operations */
u8 bSafeModePersist; /* The long-term value of bSafeMode */
+ u8 eRestoreState; /* See comments above doAutoDetectRestore() */
ColModeOpts cmOpts; /* Option values affecting columnar mode output */
unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */
@@ -1656,7 +1498,7 @@ static const char *modeDescr[] = {
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
ShellState *p = (ShellState*)pArg;
if( p->pLog==0 ) return;
- utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
+ sputf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
fflush(p->pLog);
}
@@ -1671,9 +1513,9 @@ static void shellPutsFunc(
int nVal,
sqlite3_value **apVal
){
- ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
+ /* Unused: (ShellState*)sqlite3_user_data(pCtx); */
(void)nVal;
- utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
+ oputf("%s\n", sqlite3_value_text(apVal[0]));
sqlite3_result_value(pCtx, apVal[0]);
}
@@ -1692,8 +1534,7 @@ static void failIfSafeMode(
va_start(ap, zErrMsg);
zMsg = sqlite3_vmprintf(zErrMsg, ap);
va_end(ap);
- raw_printf(stderr, "line %d: ", p->lineno);
- utf8_printf(stderr, "%s\n", zMsg);
+ eputf("line %d: %s\n", p->lineno, zMsg);
exit(1);
}
}
@@ -1861,7 +1702,7 @@ static void outputModePop(ShellState *p){
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
-static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
+static void output_hex_blob(const void *pBlob, int nBlob){
int i;
unsigned char *aBlob = (unsigned char*)pBlob;
@@ -1878,7 +1719,7 @@ static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
}
zStr[i*2] = '\0';
- raw_printf(out,"X'%s'", zStr);
+ oputf("X'%s'", zStr);
sqlite3_free(zStr);
}
@@ -1908,25 +1749,28 @@ static const char *unused_string(
**
** See also: output_quoted_escaped_string()
*/
-static void output_quoted_string(FILE *out, const char *z){
+static void output_quoted_string(const char *z){
int i;
char c;
- setBinaryMode(out, 1);
+#ifndef SQLITE_SHELL_FIDDLE
+ FILE *pfO = setOutputStream(invalidFileStream);
+ setBinaryMode(pfO, 1);
+#endif
if( z==0 ) return;
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
- utf8_printf(out,"'%s'",z);
+ oputf("'%s'",z);
}else{
- raw_printf(out, "'");
+ oputz("'");
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c=='\'' ) i++;
if( i ){
- utf8_printf(out, "%.*s", i, z);
+ oputf("%.*s", i, z);
z += i;
}
if( c=='\'' ){
- raw_printf(out, "'");
+ oputz("'");
continue;
}
if( c==0 ){
@@ -1934,9 +1778,13 @@ static void output_quoted_string(FILE *out, const char *z){
}
z++;
}
- raw_printf(out, "'");
+ oputz("'");
}
- setTextMode(out, 1);
+#ifndef SQLITE_SHELL_FIDDLE
+ setTextMode(pfO, 1);
+#else
+ setTextMode(stdout, 1);
+#endif
}
/*
@@ -1948,13 +1796,16 @@ static void output_quoted_string(FILE *out, const char *z){
** This is like output_quoted_string() but with the addition of the \r\n
** escape mechanism.
*/
-static void output_quoted_escaped_string(FILE *out, const char *z){
+static void output_quoted_escaped_string(const char *z){
int i;
char c;
- setBinaryMode(out, 1);
+#ifndef SQLITE_SHELL_FIDDLE
+ FILE *pfO = setOutputStream(invalidFileStream);
+ setBinaryMode(pfO, 1);
+#endif
for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
if( c==0 ){
- utf8_printf(out,"'%s'",z);
+ oputf("'%s'",z);
}else{
const char *zNL = 0;
const char *zCR = 0;
@@ -1966,23 +1817,23 @@ static void output_quoted_escaped_string(FILE *out, const char *z){
if( z[i]=='\r' ) nCR++;
}
if( nNL ){
- raw_printf(out, "replace(");
+ oputz("replace(");
zNL = unused_string(z, "\\n", "\\012", zBuf1);
}
if( nCR ){
- raw_printf(out, "replace(");
+ oputz("replace(");
zCR = unused_string(z, "\\r", "\\015", zBuf2);
}
- raw_printf(out, "'");
+ oputz("'");
while( *z ){
for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){}
if( c=='\'' ) i++;
if( i ){
- utf8_printf(out, "%.*s", i, z);
+ oputf("%.*s", i, z);
z += i;
}
if( c=='\'' ){
- raw_printf(out, "'");
+ oputz("'");
continue;
}
if( c==0 ){
@@ -1990,93 +1841,139 @@ static void output_quoted_escaped_string(FILE *out, const char *z){
}
z++;
if( c=='\n' ){
- raw_printf(out, "%s", zNL);
+ oputz(zNL);
continue;
}
- raw_printf(out, "%s", zCR);
+ oputz(zCR);
}
- raw_printf(out, "'");
+ oputz("'");
if( nCR ){
- raw_printf(out, ",'%s',char(13))", zCR);
+ oputf(",'%s',char(13))", zCR);
}
if( nNL ){
- raw_printf(out, ",'%s',char(10))", zNL);
+ oputf(",'%s',char(10))", zNL);
}
}
- setTextMode(out, 1);
+#ifndef SQLITE_SHELL_FIDDLE
+ setTextMode(pfO, 1);
+#else
+ setTextMode(stdout, 1);
+#endif
}
/*
+** Find earliest of chars within s specified in zAny.
+** With ns == ~0, is like strpbrk(s,zAny) and s must be 0-terminated.
+*/
+static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){
+ const char *pcFirst = 0;
+ if( ns == ~(size_t)0 ) ns = strlen(s);
+ while(*zAny){
+ const char *pc = (const char*)memchr(s, *zAny&0xff, ns);
+ if( pc ){
+ pcFirst = pc;
+ ns = pcFirst - s;
+ }
+ ++zAny;
+ }
+ return pcFirst;
+}
+/*
** Output the given string as a quoted according to C or TCL quoting rules.
*/
-static void output_c_string(FILE *out, const char *z){
- unsigned int c;
- fputc('"', out);
- while( (c = *(z++))!=0 ){
- if( c=='\\' ){
- fputc(c, out);
- fputc(c, out);
- }else if( c=='"' ){
- fputc('\\', out);
- fputc('"', out);
- }else if( c=='\t' ){
- fputc('\\', out);
- fputc('t', out);
- }else if( c=='\n' ){
- fputc('\\', out);
- fputc('n', out);
- }else if( c=='\r' ){
- fputc('\\', out);
- fputc('r', out);
+static void output_c_string(const char *z){
+ char c;
+ static const char *zq = "\"";
+ static long ctrlMask = ~0L;
+ static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */
+ char ace[3] = "\\?";
+ char cbsSay;
+ oputz(zq);
+ while( *z!=0 ){
+ const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0);
+ const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask);
+ const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast;
+ if( pcEnd > z ) oputb(z, (int)(pcEnd-z));
+ if( (c = *pcEnd)==0 ) break;
+ ++pcEnd;
+ switch( c ){
+ case '\\': case '"':
+ cbsSay = (char)c;
+ break;
+ case '\t': cbsSay = 't'; break;
+ case '\n': cbsSay = 'n'; break;
+ case '\r': cbsSay = 'r'; break;
+ case '\f': cbsSay = 'f'; break;
+ default: cbsSay = 0; break;
+ }
+ if( cbsSay ){
+ ace[1] = cbsSay;
+ oputz(ace);
}else if( !isprint(c&0xff) ){
- raw_printf(out, "\\%03o", c&0xff);
+ oputf("\\%03o", c&0xff);
}else{
- fputc(c, out);
+ ace[1] = (char)c;
+ oputz(ace+1);
}
+ z = pcEnd;
}
- fputc('"', out);
+ oputz(zq);
}
/*
** Output the given string as a quoted according to JSON quoting rules.
*/
-static void output_json_string(FILE *out, const char *z, i64 n){
- unsigned int c;
+static void output_json_string(const char *z, i64 n){
+ char c;
+ static const char *zq = "\"";
+ static long ctrlMask = ~0L;
+ static const char *zDQBS = "\"\\";
+ const char *pcLimit;
+ char ace[3] = "\\?";
+ char cbsSay;
+
if( z==0 ) z = "";
- if( n<0 ) n = strlen(z);
- fputc('"', out);
- while( n-- ){
+ pcLimit = z + ((n<0)? strlen(z) : (size_t)n);
+ oputz(zq);
+ while( z < pcLimit ){
+ const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z);
+ const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask);
+ const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast;
+ if( pcEnd > z ){
+ oputb(z, (int)(pcEnd-z));
+ z = pcEnd;
+ }
+ if( z >= pcLimit ) break;
c = *(z++);
- if( c=='\\' || c=='"' ){
- fputc('\\', out);
- fputc(c, out);
+ switch( c ){
+ case '"': case '\\':
+ cbsSay = (char)c;
+ break;
+ case '\b': cbsSay = 'b'; break;
+ case '\f': cbsSay = 'f'; break;
+ case '\n': cbsSay = 'n'; break;
+ case '\r': cbsSay = 'r'; break;
+ case '\t': cbsSay = 't'; break;
+ default: cbsSay = 0; break;
+ }
+ if( cbsSay ){
+ ace[1] = cbsSay;
+ oputz(ace);
}else if( c<=0x1f ){
- fputc('\\', out);
- if( c=='\b' ){
- fputc('b', out);
- }else if( c=='\f' ){
- fputc('f', out);
- }else if( c=='\n' ){
- fputc('n', out);
- }else if( c=='\r' ){
- fputc('r', out);
- }else if( c=='\t' ){
- fputc('t', out);
- }else{
- raw_printf(out, "u%04x",c);
- }
+ oputf("u%04x", c);
}else{
- fputc(c, out);
+ ace[1] = (char)c;
+ oputz(ace+1);
}
}
- fputc('"', out);
+ oputz(zq);
}
/*
** Output the given string with characters that are special to
** HTML escaped.
*/
-static void output_html_string(FILE *out, const char *z){
+static void output_html_string(const char *z){
int i;
if( z==0 ) z = "";
while( *z ){
@@ -2088,18 +1985,18 @@ static void output_html_string(FILE *out, const char *z){
&& z[i]!='\'';
i++){}
if( i>0 ){
- utf8_printf(out,"%.*s",i,z);
+ oputf("%.*s",i,z);
}
if( z[i]=='<' ){
- raw_printf(out,"&lt;");
+ oputz("&lt;");
}else if( z[i]=='&' ){
- raw_printf(out,"&amp;");
+ oputz("&amp;");
}else if( z[i]=='>' ){
- raw_printf(out,"&gt;");
+ oputz("&gt;");
}else if( z[i]=='\"' ){
- raw_printf(out,"&quot;");
+ oputz("&quot;");
}else if( z[i]=='\'' ){
- raw_printf(out,"&#39;");
+ oputz("&#39;");
}else{
break;
}
@@ -2137,9 +2034,8 @@ static const char needCsvQuote[] = {
** is only issued if bSep is true.
*/
static void output_csv(ShellState *p, const char *z, int bSep){
- FILE *out = p->out;
if( z==0 ){
- utf8_printf(out,"%s",p->nullValue);
+ oputf("%s",p->nullValue);
}else{
unsigned i;
for(i=0; z[i]; i++){
@@ -2151,14 +2047,14 @@ static void output_csv(ShellState *p, const char *z, int bSep){
if( i==0 || strstr(z, p->colSeparator)!=0 ){
char *zQuoted = sqlite3_mprintf("\"%w\"", z);
shell_check_oom(zQuoted);
- utf8_printf(out, "%s", zQuoted);
+ oputz(zQuoted);
sqlite3_free(zQuoted);
}else{
- utf8_printf(out, "%s", z);
+ oputz(z);
}
}
if( bSep ){
- utf8_printf(p->out, "%s", p->colSeparator);
+ oputz(p->colSeparator);
}
}
@@ -2266,16 +2162,16 @@ static int shellAuth(
az[1] = zA2;
az[2] = zA3;
az[3] = zA4;
- utf8_printf(p->out, "authorizer: %s", azAction[op]);
+ oputf("authorizer: %s", azAction[op]);
for(i=0; i<4; i++){
- raw_printf(p->out, " ");
+ oputz(" ");
if( az[i] ){
- output_c_string(p->out, az[i]);
+ output_c_string(az[i]);
}else{
- raw_printf(p->out, "NULL");
+ oputz("NULL");
}
}
- raw_printf(p->out, "\n");
+ oputz("\n");
if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
return SQLITE_OK;
}
@@ -2291,7 +2187,7 @@ static int shellAuth(
** sqlite3_complete() returns false, try to terminate the comment before
** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c
*/
-static void printSchemaLine(FILE *out, const char *z, const char *zTail){
+static void printSchemaLine(const char *z, const char *zTail){
char *zToFree = 0;
if( z==0 ) return;
if( zTail==0 ) return;
@@ -2313,16 +2209,16 @@ static void printSchemaLine(FILE *out, const char *z, const char *zTail){
}
}
if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){
- utf8_printf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
+ oputf("CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail);
}else{
- utf8_printf(out, "%s%s", z, zTail);
+ oputf("%s%s", z, zTail);
}
sqlite3_free(zToFree);
}
-static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){
+static void printSchemaLineN(char *z, int n, const char *zTail){
char c = z[n];
z[n] = 0;
- printSchemaLine(out, z, zTail);
+ printSchemaLine(z, zTail);
z[n] = c;
}
@@ -2350,7 +2246,7 @@ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
if( zText==0 ) return;
nText = strlen(zText);
if( p->autoEQPtest ){
- utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
+ oputf("%d,%d,%s\n", iEqpId, p2, zText);
}
pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
shell_check_oom(pNew);
@@ -2398,8 +2294,7 @@ static void eqp_render_level(ShellState *p, int iEqpId){
for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
pNext = eqp_next_row(p, iEqpId, pRow);
z = pRow->zText;
- utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix,
- pNext ? "|--" : "`--", z);
+ oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){
memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
eqp_render_level(p, pRow->iEqpId);
@@ -2419,13 +2314,13 @@ static void eqp_render(ShellState *p, i64 nCycle){
eqp_reset(p);
return;
}
- utf8_printf(p->out, "%s\n", pRow->zText+3);
+ oputf("%s\n", pRow->zText+3);
p->sGraph.pRow = pRow->pNext;
sqlite3_free(pRow);
}else if( nCycle>0 ){
- utf8_printf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
+ oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle);
}else{
- utf8_printf(p->out, "QUERY PLAN\n");
+ oputz("QUERY PLAN\n");
}
p->sGraph.zPrefix[0] = 0;
eqp_render_level(p, 0);
@@ -2441,13 +2336,13 @@ static int progress_handler(void *pClientData) {
ShellState *p = (ShellState*)pClientData;
p->nProgress++;
if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){
- raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress);
+ oputf("Progress limit reached (%u)\n", p->nProgress);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0;
return 1;
}
if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
- raw_printf(p->out, "Progress %u\n", p->nProgress);
+ oputf("Progress %u\n", p->nProgress);
}
return 0;
}
@@ -2456,14 +2351,14 @@ static int progress_handler(void *pClientData) {
/*
** Print N dashes
*/
-static void print_dashes(FILE *out, int N){
+static void print_dashes(int N){
const char zDash[] = "--------------------------------------------------";
const int nDash = sizeof(zDash) - 1;
while( N>nDash ){
- fputs(zDash, out);
+ oputz(zDash);
N -= nDash;
}
- raw_printf(out, "%.*s", N, zDash);
+ oputf("%.*s", N, zDash);
}
/*
@@ -2476,15 +2371,15 @@ static void print_row_separator(
){
int i;
if( nArg>0 ){
- fputs(zSep, p->out);
- print_dashes(p->out, p->actualWidth[0]+2);
+ oputz(zSep);
+ print_dashes(p->actualWidth[0]+2);
for(i=1; i<nArg; i++){
- fputs(zSep, p->out);
- print_dashes(p->out, p->actualWidth[i]+2);
+ oputz(zSep);
+ print_dashes(p->actualWidth[i]+2);
}
- fputs(zSep, p->out);
+ oputz(zSep);
}
- fputs("\n", p->out);
+ oputz("\n");
}
/*
@@ -2514,10 +2409,10 @@ static int shell_callback(
int len = strlen30(azCol[i] ? azCol[i] : "");
if( len>w ) w = len;
}
- if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator);
+ if( p->cnt++>0 ) oputz(p->rowSeparator);
for(i=0; i<nArg; i++){
- utf8_printf(p->out,"%*s = %s%s", w, azCol[i],
- azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
+ oputf("%*s = %s%s", w, azCol[i],
+ azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator);
}
break;
}
@@ -2544,12 +2439,12 @@ static int shell_callback(
/* If this is the first row seen, print out the headers */
if( p->cnt++==0 ){
for(i=0; i<nArg; i++){
- utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]);
- fputs(i==nArg-1 ? "\n" : " ", p->out);
+ utf8_width_print(aWidth[i], azCol[ aMap[i] ]);
+ oputz(i==nArg-1 ? "\n" : " ");
}
for(i=0; i<nArg; i++){
- print_dashes(p->out, aWidth[i]);
- fputs(i==nArg-1 ? "\n" : " ", p->out);
+ print_dashes(aWidth[i]);
+ oputz(i==nArg-1 ? "\n" : " ");
}
}
@@ -2567,17 +2462,17 @@ static int shell_callback(
}
if( i==iIndent && p->aiIndent && p->pStmt ){
if( p->iIndent<p->nIndent ){
- utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
+ oputf("%*.s", p->aiIndent[p->iIndent], "");
}
p->iIndent++;
}
- utf8_width_print(p->out, w, zVal ? zVal : p->nullValue);
- fputs(i==nArg-1 ? "\n" : zSep, p->out);
+ utf8_width_print(w, zVal ? zVal : p->nullValue);
+ oputz(i==nArg-1 ? "\n" : zSep);
}
break;
}
case MODE_Semi: { /* .schema and .fullschema output */
- printSchemaLine(p->out, azArg[0], ";\n");
+ printSchemaLine(azArg[0], ";\n");
break;
}
case MODE_Pretty: { /* .schema and .fullschema with --indent */
@@ -2592,7 +2487,7 @@ static int shell_callback(
if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
|| sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
){
- utf8_printf(p->out, "%s;\n", azArg[0]);
+ oputf("%s;\n", azArg[0]);
break;
}
z = sqlite3_mprintf("%s", azArg[0]);
@@ -2625,7 +2520,7 @@ static int shell_callback(
}else if( c==')' ){
nParen--;
if( nLine>0 && nParen==0 && j>0 ){
- printSchemaLineN(p->out, z, j, "\n");
+ printSchemaLineN(z, j, "\n");
j = 0;
}
}
@@ -2634,7 +2529,7 @@ static int shell_callback(
&& (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
){
if( c=='\n' ) j--;
- printSchemaLineN(p->out, z, j, "\n ");
+ printSchemaLineN(z, j, "\n ");
j = 0;
nLine++;
while( IsSpace(z[i+1]) ){ i++; }
@@ -2642,64 +2537,59 @@ static int shell_callback(
}
z[j] = 0;
}
- printSchemaLine(p->out, z, ";\n");
+ printSchemaLine(z, ";\n");
sqlite3_free(z);
break;
}
case MODE_List: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
- utf8_printf(p->out,"%s%s",azCol[i],
- i==nArg-1 ? p->rowSeparator : p->colSeparator);
+ oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator);
}
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
char *z = azArg[i];
if( z==0 ) z = p->nullValue;
- utf8_printf(p->out, "%s", z);
- if( i<nArg-1 ){
- utf8_printf(p->out, "%s", p->colSeparator);
- }else{
- utf8_printf(p->out, "%s", p->rowSeparator);
- }
+ oputz(z);
+ oputz((i<nArg-1)? p->colSeparator : p->rowSeparator);
}
break;
}
case MODE_Html: {
if( p->cnt++==0 && p->showHeader ){
- raw_printf(p->out,"<TR>");
+ oputz("<TR>");
for(i=0; i<nArg; i++){
- raw_printf(p->out,"<TH>");
- output_html_string(p->out, azCol[i]);
- raw_printf(p->out,"</TH>\n");
+ oputz("<TH>");
+ output_html_string(azCol[i]);
+ oputz("</TH>\n");
}
- raw_printf(p->out,"</TR>\n");
+ oputz("</TR>\n");
}
if( azArg==0 ) break;
- raw_printf(p->out,"<TR>");
+ oputz("<TR>");
for(i=0; i<nArg; i++){
- raw_printf(p->out,"<TD>");
- output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
- raw_printf(p->out,"</TD>\n");
+ oputz("<TD>");
+ output_html_string(azArg[i] ? azArg[i] : p->nullValue);
+ oputz("</TD>\n");
}
- raw_printf(p->out,"</TR>\n");
+ oputz("</TR>\n");
break;
}
case MODE_Tcl: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
- output_c_string(p->out,azCol[i] ? azCol[i] : "");
- if(i<nArg-1) utf8_printf(p->out, "%s", p->colSeparator);
+ output_c_string(azCol[i] ? azCol[i] : "");
+ if(i<nArg-1) oputz(p->colSeparator);
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
- output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue);
- if(i<nArg-1) utf8_printf(p->out, "%s", p->colSeparator);
+ output_c_string(azArg[i] ? azArg[i] : p->nullValue);
+ if(i<nArg-1) oputz(p->colSeparator);
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
break;
}
case MODE_Csv: {
@@ -2708,57 +2598,57 @@ static int shell_callback(
for(i=0; i<nArg; i++){
output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1);
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
}
if( nArg>0 ){
for(i=0; i<nArg; i++){
output_csv(p, azArg[i], i<nArg-1);
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
}
setTextMode(p->out, 1);
break;
}
case MODE_Insert: {
if( azArg==0 ) break;
- utf8_printf(p->out,"INSERT INTO %s",p->zDestTable);
+ oputf("INSERT INTO %s",p->zDestTable);
if( p->showHeader ){
- raw_printf(p->out,"(");
+ oputz("(");
for(i=0; i<nArg; i++){
- if( i>0 ) raw_printf(p->out, ",");
+ if( i>0 ) oputz(",");
if( quoteChar(azCol[i]) ){
char *z = sqlite3_mprintf("\"%w\"", azCol[i]);
shell_check_oom(z);
- utf8_printf(p->out, "%s", z);
+ oputz(z);
sqlite3_free(z);
}else{
- raw_printf(p->out, "%s", azCol[i]);
+ oputf("%s", azCol[i]);
}
}
- raw_printf(p->out,")");
+ oputz(")");
}
p->cnt++;
for(i=0; i<nArg; i++){
- raw_printf(p->out, i>0 ? "," : " VALUES(");
+ oputz(i>0 ? "," : " VALUES(");
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- utf8_printf(p->out,"NULL");
+ oputz("NULL");
}else if( aiType && aiType[i]==SQLITE_TEXT ){
if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p->out, azArg[i]);
+ output_quoted_string(azArg[i]);
}else{
- output_quoted_escaped_string(p->out, azArg[i]);
+ output_quoted_escaped_string(azArg[i]);
}
}else if( aiType && aiType[i]==SQLITE_INTEGER ){
- utf8_printf(p->out,"%s", azArg[i]);
+ oputz(azArg[i]);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
- raw_printf(p->out, "9.0e+999");
+ oputz("9.0e+999");
}else if( ur==0xfff0000000000000LL ){
- raw_printf(p->out, "-9.0e+999");
+ oputz("-9.0e+999");
}else{
sqlite3_int64 ir = (sqlite3_int64)r;
if( r==(double)ir ){
@@ -2766,21 +2656,21 @@ static int shell_callback(
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
}
- raw_printf(p->out, "%s", z);
+ oputz(z);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
+ output_hex_blob(pBlob, nBlob);
}else if( isNumber(azArg[i], 0) ){
- utf8_printf(p->out,"%s", azArg[i]);
+ oputz(azArg[i]);
}else if( ShellHasFlag(p, SHFLG_Newlines) ){
- output_quoted_string(p->out, azArg[i]);
+ output_quoted_string(azArg[i]);
}else{
- output_quoted_escaped_string(p->out, azArg[i]);
+ output_quoted_escaped_string(azArg[i]);
}
}
- raw_printf(p->out,");\n");
+ oputz(");\n");
break;
}
case MODE_Json: {
@@ -2792,37 +2682,37 @@ static int shell_callback(
}
p->cnt++;
for(i=0; i<nArg; i++){
- output_json_string(p->out, azCol[i], -1);
- putc(':', p->out);
+ output_json_string(azCol[i], -1);
+ oputz(":");
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- fputs("null",p->out);
+ oputz("null");
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_uint64 ur;
memcpy(&ur,&r,sizeof(r));
if( ur==0x7ff0000000000000LL ){
- raw_printf(p->out, "9.0e+999");
+ oputz("9.0e+999");
}else if( ur==0xfff0000000000000LL ){
- raw_printf(p->out, "-9.0e+999");
+ oputz("-9.0e+999");
}else{
sqlite3_snprintf(50,z,"%!.20g", r);
- raw_printf(p->out, "%s", z);
+ oputz(z);
}
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_json_string(p->out, pBlob, nBlob);
+ output_json_string(pBlob, nBlob);
}else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_json_string(p->out, azArg[i], -1);
+ output_json_string(azArg[i], -1);
}else{
- utf8_printf(p->out,"%s", azArg[i]);
+ oputz(azArg[i]);
}
if( i<nArg-1 ){
- putc(',', p->out);
+ oputz(",");
}
}
- putc('}', p->out);
+ oputz("}");
break;
}
case MODE_Quote: {
@@ -2830,7 +2720,7 @@ static int shell_callback(
if( p->cnt==0 && p->showHeader ){
for(i=0; i<nArg; i++){
if( i>0 ) fputs(p->colSeparator, p->out);
- output_quoted_string(p->out, azCol[i]);
+ output_quoted_string(azCol[i]);
}
fputs(p->rowSeparator, p->out);
}
@@ -2838,24 +2728,24 @@ static int shell_callback(
for(i=0; i<nArg; i++){
if( i>0 ) fputs(p->colSeparator, p->out);
if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){
- utf8_printf(p->out,"NULL");
+ oputz("NULL");
}else if( aiType && aiType[i]==SQLITE_TEXT ){
- output_quoted_string(p->out, azArg[i]);
+ output_quoted_string(azArg[i]);
}else if( aiType && aiType[i]==SQLITE_INTEGER ){
- utf8_printf(p->out,"%s", azArg[i]);
+ oputz(azArg[i]);
}else if( aiType && aiType[i]==SQLITE_FLOAT ){
char z[50];
double r = sqlite3_column_double(p->pStmt, i);
sqlite3_snprintf(50,z,"%!.20g", r);
- raw_printf(p->out, "%s", z);
+ oputz(z);
}else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){
const void *pBlob = sqlite3_column_blob(p->pStmt, i);
int nBlob = sqlite3_column_bytes(p->pStmt, i);
- output_hex_blob(p->out, pBlob, nBlob);
+ output_hex_blob(pBlob, nBlob);
}else if( isNumber(azArg[i], 0) ){
- utf8_printf(p->out,"%s", azArg[i]);
+ oputz(azArg[i]);
}else{
- output_quoted_string(p->out, azArg[i]);
+ output_quoted_string(azArg[i]);
}
}
fputs(p->rowSeparator, p->out);
@@ -2864,17 +2754,17 @@ static int shell_callback(
case MODE_Ascii: {
if( p->cnt++==0 && p->showHeader ){
for(i=0; i<nArg; i++){
- if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator);
- utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : "");
+ if( i>0 ) oputz(p->colSeparator);
+ oputz(azCol[i] ? azCol[i] : "");
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
}
if( azArg==0 ) break;
for(i=0; i<nArg; i++){
- if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator);
- utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue);
+ if( i>0 ) oputz(p->colSeparator);
+ oputz(azArg[i] ? azArg[i] : p->nullValue);
}
- utf8_printf(p->out, "%s", p->rowSeparator);
+ oputz(p->rowSeparator);
break;
}
case MODE_EQP: {
@@ -2953,7 +2843,7 @@ static void createSelftestTable(ShellState *p){
"DROP TABLE [_shell$self];"
,0,0,&zErrMsg);
if( zErrMsg ){
- utf8_printf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg);
+ eputf("SELFTEST initialization failure: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0);
@@ -3056,8 +2946,8 @@ static int run_table_dump_query(
rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0);
if( rc!=SQLITE_OK || !pSelect ){
char *zContext = shell_error_context(zSelect, p->db);
- utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc,
- sqlite3_errmsg(p->db), zContext);
+ oputf("/**** ERROR: (%d) %s *****/\n%s",
+ rc, sqlite3_errmsg(p->db), zContext);
sqlite3_free(zContext);
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
return rc;
@@ -3066,23 +2956,22 @@ static int run_table_dump_query(
nResult = sqlite3_column_count(pSelect);
while( rc==SQLITE_ROW ){
z = (const char*)sqlite3_column_text(pSelect, 0);
- utf8_printf(p->out, "%s", z);
+ oputf("%s", z);
for(i=1; i<nResult; i++){
- utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
+ oputf(",%s", sqlite3_column_text(pSelect, i));
}
if( z==0 ) z = "";
while( z[0] && (z[0]!='-' || z[1]!='-') ) z++;
if( z[0] ){
- raw_printf(p->out, "\n;\n");
+ oputz("\n;\n");
}else{
- raw_printf(p->out, ";\n");
+ oputz(";\n");
}
rc = sqlite3_step(pSelect);
}
rc = sqlite3_finalize(pSelect);
if( rc!=SQLITE_OK ){
- utf8_printf(p->out, "/**** ERROR: (%d) %s *****/\n", rc,
- sqlite3_errmsg(p->db));
+ oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db));
if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++;
}
return rc;
@@ -3118,7 +3007,7 @@ static char *save_err_msg(
/*
** Attempt to display I/O stats on Linux using /proc/PID/io
*/
-static void displayLinuxIoStats(FILE *out){
+static void displayLinuxIoStats(void){
FILE *in;
char z[200];
sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
@@ -3141,7 +3030,7 @@ static void displayLinuxIoStats(FILE *out){
for(i=0; i<ArraySize(aTrans); i++){
int n = strlen30(aTrans[i].zPattern);
if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){
- utf8_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
+ oputf("%-36s %s", aTrans[i].zDesc, &z[n]);
break;
}
}
@@ -3154,7 +3043,6 @@ static void displayLinuxIoStats(FILE *out){
** Display a single line of status using 64-bit values.
*/
static void displayStatLine(
- ShellState *p, /* The shell context */
char *zLabel, /* Label for this one line */
char *zFormat, /* Format for the result */
int iStatusCtrl, /* Which status to display */
@@ -3173,7 +3061,7 @@ static void displayStatLine(
}else{
sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr);
}
- raw_printf(p->out, "%-36s %s\n", zLabel, zLine);
+ oputf("%-36s %s\n", zLabel, zLine);
}
/*
@@ -3186,30 +3074,28 @@ static int display_stats(
){
int iCur;
int iHiwtr;
- FILE *out;
if( pArg==0 || pArg->out==0 ) return 0;
- out = pArg->out;
if( pArg->pStmt && pArg->statsOn==2 ){
int nCol, i, x;
sqlite3_stmt *pStmt = pArg->pStmt;
char z[100];
nCol = sqlite3_column_count(pStmt);
- raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
+ oputf("%-36s %d\n", "Number of output columns:", nCol);
for(i=0; i<nCol; i++){
sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
- utf8_printf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i));
+ oputf("%-36s %s\n", z, sqlite3_column_name(pStmt,i));
#ifndef SQLITE_OMIT_DECLTYPE
sqlite3_snprintf(30, z+x, "declared type:");
- utf8_printf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
+ oputf("%-36s %s\n", z, sqlite3_column_decltype(pStmt, i));
#endif
#ifdef SQLITE_ENABLE_COLUMN_METADATA
sqlite3_snprintf(30, z+x, "database name:");
- utf8_printf(out, "%-36s %s\n", z, sqlite3_column_database_name(pStmt,i));
+ oputf("%-36s %s\n", z, sqlite3_column_database_name(pStmt,i));
sqlite3_snprintf(30, z+x, "table name:");
- utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
+ oputf("%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
sqlite3_snprintf(30, z+x, "origin name:");
- utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
+ oputf("%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
#endif
}
}
@@ -3217,27 +3103,27 @@ static int display_stats(
if( pArg->statsOn==3 ){
if( pArg->pStmt ){
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset);
- raw_printf(pArg->out, "VM-steps: %d\n", iCur);
+ oputf("VM-steps: %d\n", iCur);
}
return 0;
}
- displayStatLine(pArg, "Memory Used:",
+ displayStatLine("Memory Used:",
"%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
- displayStatLine(pArg, "Number of Outstanding Allocations:",
+ displayStatLine("Number of Outstanding Allocations:",
"%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
if( pArg->shellFlgs & SHFLG_Pagecache ){
- displayStatLine(pArg, "Number of Pcache Pages Used:",
+ displayStatLine("Number of Pcache Pages Used:",
"%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset);
}
- displayStatLine(pArg, "Number of Pcache Overflow Bytes:",
+ displayStatLine("Number of Pcache Overflow Bytes:",
"%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset);
- displayStatLine(pArg, "Largest Allocation:",
+ displayStatLine("Largest Allocation:",
"%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset);
- displayStatLine(pArg, "Largest Pcache Allocation:",
+ displayStatLine("Largest Pcache Allocation:",
"%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset);
#ifdef YYTRACKMAXSTACKDEPTH
- displayStatLine(pArg, "Deepest Parser Stack:",
+ displayStatLine("Deepest Parser Stack:",
"%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset);
#endif
@@ -3246,77 +3132,68 @@ static int display_stats(
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED,
&iCur, &iHiwtr, bReset);
- raw_printf(pArg->out,
- "Lookaside Slots Used: %d (max %d)\n",
- iCur, iHiwtr);
+ oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT,
&iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Successful lookaside attempts: %d\n",
- iHiwtr);
+ oputf("Successful lookaside attempts: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE,
&iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Lookaside failures due to size: %d\n",
- iHiwtr);
+ oputf("Lookaside failures due to size: %d\n", iHiwtr);
sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL,
&iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Lookaside failures due to OOM: %d\n",
- iHiwtr);
+ oputf("Lookaside failures due to OOM: %d\n", iHiwtr);
}
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Pager Heap Usage: %d bytes\n",
- iCur);
+ oputf("Pager Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
- raw_printf(pArg->out, "Page cache hits: %d\n", iCur);
+ oputf("Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
- raw_printf(pArg->out, "Page cache misses: %d\n", iCur);
+ oputf("Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
- raw_printf(pArg->out, "Page cache writes: %d\n", iCur);
+ oputf("Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
- raw_printf(pArg->out, "Page cache spills: %d\n", iCur);
+ oputf("Page cache spills: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Schema Heap Usage: %d bytes\n",
- iCur);
+ oputf("Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
- raw_printf(pArg->out, "Statement Heap/Lookaside Usage: %d bytes\n",
- iCur);
+ oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur);
}
if( pArg->pStmt ){
int iHit, iMiss;
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP,
bReset);
- raw_printf(pArg->out, "Fullscan Steps: %d\n", iCur);
+ oputf("Fullscan Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset);
- raw_printf(pArg->out, "Sort Operations: %d\n", iCur);
+ oputf("Sort Operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset);
- raw_printf(pArg->out, "Autoindex Inserts: %d\n", iCur);
+ oputf("Autoindex Inserts: %d\n", iCur);
iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT,
bReset);
iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS,
bReset);
if( iHit || iMiss ){
- raw_printf(pArg->out, "Bloom filter bypass taken: %d/%d\n",
- iHit, iHit+iMiss);
+ oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss);
}
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
- raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
+ oputf("Virtual Machine Steps: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset);
- raw_printf(pArg->out, "Reprepare operations: %d\n", iCur);
+ oputf("Reprepare operations: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset);
- raw_printf(pArg->out, "Number of times run: %d\n", iCur);
+ oputf("Number of times run: %d\n", iCur);
iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset);
- raw_printf(pArg->out, "Memory used by prepared stmt: %d\n", iCur);
+ oputf("Memory used by prepared stmt: %d\n", iCur);
}
#ifdef __linux__
- displayLinuxIoStats(pArg->out);
+ displayLinuxIoStats();
#endif
/* Do not remove this machine readable comment: extra-stats-output-here */
@@ -3371,7 +3248,7 @@ static void display_explain_scanstats(
if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
break;
}
- n = strlen(z) + scanStatsHeight(p, ii)*3;
+ n = (int)strlen(z) + scanStatsHeight(p, ii)*3;
if( n>nWidth ) nWidth = n;
}
nWidth += 4;
@@ -3383,12 +3260,12 @@ static void display_explain_scanstats(
i64 nCycle = 0;
int iId = 0;
int iPid = 0;
- const char *z = 0;
+ const char *zo = 0;
const char *zName = 0;
char *zText = 0;
double rEst = 0.0;
- if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&z) ){
+ if( sqlite3_stmt_scanstatus_v2(p,ii,SQLITE_SCANSTAT_EXPLAIN,f,(void*)&zo) ){
break;
}
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_EST,f,(void*)&rEst);
@@ -3399,7 +3276,7 @@ static void display_explain_scanstats(
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_PARENTID,f,(void*)&iPid);
sqlite3_stmt_scanstatus_v2(p, ii, SQLITE_SCANSTAT_NAME,f,(void*)&zName);
- zText = sqlite3_mprintf("%s", z);
+ zText = sqlite3_mprintf("%s", zo);
if( nCycle>=0 || nLoop>=0 || nRow>=0 ){
char *z = 0;
if( nCycle>=0 && nTotal>0 ){
@@ -3551,7 +3428,7 @@ static void display_scanstats(
UNUSED_PARAMETER(pArg);
#else
if( pArg->scanstatsOn==3 ){
- const char *zSql =
+ const char *zSql =
" SELECT addr, opcode, p1, p2, p3, p4, p5, comment, nexec,"
" round(ncycle*100.0 / (sum(ncycle) OVER ()), 2)||'%' AS cycles"
" FROM bytecode(?)";
@@ -3697,17 +3574,17 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
/* Draw horizontal line N characters long using unicode box
** characters
*/
-static void print_box_line(FILE *out, int N){
+static void print_box_line(int N){
const char zDash[] =
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24
BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24;
const int nDash = sizeof(zDash) - 1;
N *= 3;
while( N>nDash ){
- utf8_printf(out, zDash);
+ oputz(zDash);
N -= nDash;
}
- utf8_printf(out, "%.*s", N, zDash);
+ oputf("%.*s", N, zDash);
}
/*
@@ -3722,15 +3599,15 @@ static void print_box_row_separator(
){
int i;
if( nArg>0 ){
- utf8_printf(p->out, "%s", zSep1);
- print_box_line(p->out, p->actualWidth[0]+2);
+ oputz(zSep1);
+ print_box_line(p->actualWidth[0]+2);
for(i=1; i<nArg; i++){
- utf8_printf(p->out, "%s", zSep2);
- print_box_line(p->out, p->actualWidth[i]+2);
+ oputz(zSep2);
+ print_box_line(p->actualWidth[i]+2);
}
- utf8_printf(p->out, "%s", zSep3);
+ oputz(zSep3);
}
- fputs("\n", p->out);
+ oputz("\n");
}
/*
@@ -3993,11 +3870,11 @@ static void exec_prepared_stmt_columnar(
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
if( p->colWidth[i]<0 ) w = -w;
- utf8_width_print(p->out, w, azData[i]);
+ utf8_width_print(w, azData[i]);
fputs(i==nColumn-1?"\n":" ", p->out);
}
for(i=0; i<nColumn; i++){
- print_dashes(p->out, p->actualWidth[i]);
+ print_dashes(p->actualWidth[i]);
fputs(i==nColumn-1?"\n":" ", p->out);
}
}
@@ -4011,8 +3888,8 @@ static void exec_prepared_stmt_columnar(
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
- utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
- fputs(i==nColumn-1?" |\n":" | ", p->out);
+ oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
+ oputz(i==nColumn-1?" |\n":" | ");
}
print_row_separator(p, nColumn, "+");
break;
@@ -4024,8 +3901,8 @@ static void exec_prepared_stmt_columnar(
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
- utf8_printf(p->out, "%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
- fputs(i==nColumn-1?" |\n":" | ", p->out);
+ oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, "");
+ oputz(i==nColumn-1?" |\n":" | ");
}
print_row_separator(p, nColumn, "|");
break;
@@ -4034,13 +3911,13 @@ static void exec_prepared_stmt_columnar(
colSep = " " BOX_13 " ";
rowSep = " " BOX_13 "\n";
print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34);
- utf8_printf(p->out, BOX_13 " ");
+ oputz(BOX_13 " ");
for(i=0; i<nColumn; i++){
w = p->actualWidth[i];
n = strlenChar(azData[i]);
- utf8_printf(p->out, "%*s%s%*s%s",
- (w-n)/2, "", azData[i], (w-n+1)/2, "",
- i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
+ oputf("%*s%s%*s%s",
+ (w-n)/2, "", azData[i], (w-n+1)/2, "",
+ i==nColumn-1?" "BOX_13"\n":" "BOX_13" ");
}
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
break;
@@ -4048,28 +3925,28 @@ static void exec_prepared_stmt_columnar(
}
for(i=nColumn, j=0; i<nTotal; i++, j++){
if( j==0 && p->cMode!=MODE_Column ){
- utf8_printf(p->out, "%s", p->cMode==MODE_Box?BOX_13" ":"| ");
+ oputz(p->cMode==MODE_Box?BOX_13" ":"| ");
}
z = azData[i];
if( z==0 ) z = p->nullValue;
w = p->actualWidth[j];
if( p->colWidth[j]<0 ) w = -w;
- utf8_width_print(p->out, w, z);
+ utf8_width_print(w, z);
if( j==nColumn-1 ){
- utf8_printf(p->out, "%s", rowSep);
+ oputz(rowSep);
if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){
if( p->cMode==MODE_Table ){
print_row_separator(p, nColumn, "+");
}else if( p->cMode==MODE_Box ){
print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134);
}else if( p->cMode==MODE_Column ){
- raw_printf(p->out, "\n");
+ oputz("\n");
}
}
j = -1;
if( seenInterrupt ) goto columnar_end;
}else{
- utf8_printf(p->out, "%s", colSep);
+ oputz(colSep);
}
}
if( p->cMode==MODE_Table ){
@@ -4079,7 +3956,7 @@ static void exec_prepared_stmt_columnar(
}
columnar_end:
if( seenInterrupt ){
- utf8_printf(p->out, "Interrupt\n");
+ oputz("Interrupt\n");
}
nData = (nRow+1)*nColumn;
for(i=0; i<nData; i++){
@@ -4218,7 +4095,6 @@ static int expertFinish(
assert( p );
assert( bCancel || pzErr==0 || *pzErr==0 );
if( bCancel==0 ){
- FILE *out = pState->out;
int bVerbose = pState->expert.bVerbose;
rc = sqlite3_expert_analyze(p, pzErr);
@@ -4228,8 +4104,8 @@ static int expertFinish(
if( bVerbose ){
const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES);
- raw_printf(out, "-- Candidates -----------------------------\n");
- raw_printf(out, "%s\n", zCand);
+ oputz("-- Candidates -----------------------------\n");
+ oputf("%s\n", zCand);
}
for(i=0; i<nQuery; i++){
const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL);
@@ -4237,11 +4113,11 @@ static int expertFinish(
const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN);
if( zIdx==0 ) zIdx = "(no new indexes)\n";
if( bVerbose ){
- raw_printf(out, "-- Query %d --------------------------------\n",i+1);
- raw_printf(out, "%s\n\n", zSql);
+ oputf("-- Query %d --------------------------------\n",i+1);
+ oputf("%s\n\n", zSql);
}
- raw_printf(out, "%s\n", zIdx);
- raw_printf(out, "%s\n", zEQP);
+ oputf("%s\n", zIdx);
+ oputf("%s\n", zEQP);
}
}
}
@@ -4276,18 +4152,18 @@ static int expertDotCommand(
}
else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){
if( i==(nArg-1) ){
- raw_printf(stderr, "option requires an argument: %s\n", z);
+ eputf("option requires an argument: %s\n", z);
rc = SQLITE_ERROR;
}else{
iSample = (int)integerValue(azArg[++i]);
if( iSample<0 || iSample>100 ){
- raw_printf(stderr, "value out of range: %s\n", azArg[i]);
+ eputf("value out of range: %s\n", azArg[i]);
rc = SQLITE_ERROR;
}
}
}
else{
- raw_printf(stderr, "unknown option: %s\n", z);
+ eputf("unknown option: %s\n", z);
rc = SQLITE_ERROR;
}
}
@@ -4295,8 +4171,7 @@ static int expertDotCommand(
if( rc==SQLITE_OK ){
pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr);
if( pState->expert.pExpert==0 ){
- raw_printf(stderr, "sqlite3_expert_new: %s\n",
- zErr ? zErr : "out of memory");
+ eputf("sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory");
rc = SQLITE_ERROR;
}else{
sqlite3_expert_config(
@@ -4623,9 +4498,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0;
if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){
- if( !dataOnly ) raw_printf(p->out, "DELETE FROM sqlite_sequence;\n");
+ if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n");
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){
- if( !dataOnly ) raw_printf(p->out, "ANALYZE sqlite_schema;\n");
+ if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n");
}else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){
return 0;
}else if( dataOnly ){
@@ -4633,7 +4508,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
}else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){
char *zIns;
if( !p->writableSchema ){
- raw_printf(p->out, "PRAGMA writable_schema=ON;\n");
+ oputz("PRAGMA writable_schema=ON;\n");
p->writableSchema = 1;
}
zIns = sqlite3_mprintf(
@@ -4641,11 +4516,11 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
"VALUES('table','%q','%q',0,'%q');",
zTable, zTable, zSql);
shell_check_oom(zIns);
- utf8_printf(p->out, "%s\n", zIns);
+ oputf("%s\n", zIns);
sqlite3_free(zIns);
return 0;
}else{
- printSchemaLine(p->out, zSql, ";\n");
+ printSchemaLine(zSql, ";\n");
}
if( cli_strcmp(zType, "table")==0 ){
@@ -4703,7 +4578,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
p->mode = p->cMode = MODE_Insert;
rc = shell_exec(p, sSelect.z, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
- raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n");
+ oputz("/****** CORRUPTION ERROR *******/\n");
toggleSelectOrder(p->db);
shell_exec(p, sSelect.z, 0);
toggleSelectOrder(p->db);
@@ -4734,9 +4609,9 @@ static int run_schema_dump_query(
if( rc==SQLITE_CORRUPT ){
char *zQ2;
int len = strlen30(zQuery);
- raw_printf(p->out, "/****** CORRUPTION ERROR *******/\n");
+ oputz("/****** CORRUPTION ERROR *******/\n");
if( zErr ){
- utf8_printf(p->out, "/****** %s ******/\n", zErr);
+ oputf("/****** %s ******/\n", zErr);
sqlite3_free(zErr);
zErr = 0;
}
@@ -4745,7 +4620,7 @@ static int run_schema_dump_query(
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
- utf8_printf(p->out, "/****** ERROR: %s ******/\n", zErr);
+ oputf("/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
@@ -4869,6 +4744,7 @@ static const char *(azHelp[]) = {
".indexes ?TABLE? Show names of indexes",
" If TABLE is specified, only show indexes for",
" tables matching TABLE using the LIKE operator.",
+ ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db",
#ifdef SQLITE_ENABLE_IOTRACE
",iotrace FILE Enable I/O diagnostic logging to FILE",
#endif
@@ -5101,10 +4977,10 @@ static int showHelp(FILE *out, const char *zPattern){
}
if( ((hw^hh)&HH_Undoc)==0 ){
if( (hh&HH_Summary)!=0 ){
- utf8_printf(out, ".%s\n", azHelp[i]+1);
+ sputf(out, ".%s\n", azHelp[i]+1);
++n;
}else if( (hw&HW_SummaryOnly)==0 ){
- utf8_printf(out, "%s\n", azHelp[i]);
+ sputf(out, "%s\n", azHelp[i]);
}
}
}
@@ -5114,7 +4990,7 @@ static int showHelp(FILE *out, const char *zPattern){
shell_check_oom(zPat);
for(i=0; i<ArraySize(azHelp); i++){
if( sqlite3_strglob(zPat, azHelp[i])==0 ){
- utf8_printf(out, "%s\n", azHelp[i]);
+ sputf(out, "%s\n", azHelp[i]);
j = i+1;
n++;
}
@@ -5125,7 +5001,7 @@ static int showHelp(FILE *out, const char *zPattern){
/* when zPattern is a prefix of exactly one command, then include
** the details of that command, which should begin at offset j */
while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
- utf8_printf(out, "%s\n", azHelp[j]);
+ sputf(out, "%s\n", azHelp[j]);
j++;
}
}
@@ -5142,10 +5018,10 @@ static int showHelp(FILE *out, const char *zPattern){
}
if( azHelp[i][0]=='.' ) j = i;
if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
- utf8_printf(out, "%s\n", azHelp[j]);
+ sputf(out, "%s\n", azHelp[j]);
while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
j++;
- utf8_printf(out, "%s\n", azHelp[j]);
+ sputf(out, "%s\n", azHelp[j]);
}
i = j;
n++;
@@ -5183,7 +5059,7 @@ static char *readFile(const char *zName, int *pnByte){
if( in==0 ) return 0;
rc = fseek(in, 0, SEEK_END);
if( rc!=0 ){
- raw_printf(stderr, "Error: '%s' not seekable\n", zName);
+ eputf("Error: '%s' not seekable\n", zName);
fclose(in);
return 0;
}
@@ -5191,7 +5067,7 @@ static char *readFile(const char *zName, int *pnByte){
rewind(in);
pBuf = sqlite3_malloc64( nIn+1 );
if( pBuf==0 ){
- raw_printf(stderr, "Error: out of memory\n");
+ eputz("Error: out of memory\n");
fclose(in);
return 0;
}
@@ -5199,7 +5075,7 @@ static char *readFile(const char *zName, int *pnByte){
fclose(in);
if( nRead!=1 ){
sqlite3_free(pBuf);
- raw_printf(stderr, "Error: cannot read '%s'\n", zName);
+ eputf("Error: cannot read '%s'\n", zName);
return 0;
}
pBuf[nIn] = 0;
@@ -5320,7 +5196,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
if( zDbFilename ){
in = fopen(zDbFilename, "r");
if( in==0 ){
- utf8_printf(stderr, "cannot open \"%s\" for reading\n", zDbFilename);
+ eputf("cannot open \"%s\" for reading\n", zDbFilename);
return 0;
}
nLine = 0;
@@ -5341,7 +5217,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
shell_check_oom(a);
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
- utf8_printf(stderr, "invalid pagesize\n");
+ eputz("invalid pagesize\n");
goto readHexDb_error;
}
for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){
@@ -5383,7 +5259,7 @@ readHexDb_error:
p->lineno = nLine;
}
sqlite3_free(a);
- utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine);
+ eputf("Error on line %d of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_OMIT_DESERIALIZE */
@@ -5457,26 +5333,23 @@ static void open_db(ShellState *p, int openFlags){
break;
}
}
- globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
- zDbFilename, sqlite3_errmsg(p->db));
+ eputf("Error: unable to open database \"%s\": %s\n",
+ zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
exit(1);
}
sqlite3_close(p->db);
sqlite3_open(":memory:", &p->db);
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
- utf8_printf(stderr,
- "Also: unable to open substitute in-memory database.\n"
- );
+ eputz("Also: unable to open substitute in-memory database.\n");
exit(1);
}else{
- utf8_printf(stderr,
- "Notice: using substitute in-memory database instead of \"%s\"\n",
- zDbFilename);
+ eputf("Notice: using substitute in-memory database instead of \"%s\"\n",
+ zDbFilename);
}
}
+ globalDb = p->db;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);
/* Reflect the use or absence of --unsafe-testing invocation. */
@@ -5581,7 +5454,7 @@ static void open_db(ShellState *p, int openFlags){
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
- utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc);
+ eputf("Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
@@ -5605,8 +5478,7 @@ static void open_db(ShellState *p, int openFlags){
void close_db(sqlite3 *db){
int rc = sqlite3_close(db);
if( rc ){
- utf8_printf(stderr, "Error: sqlite3_close() returns %d: %s\n",
- rc, sqlite3_errmsg(db));
+ eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
@@ -5767,8 +5639,7 @@ static int booleanValue(const char *zArg){
if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){
return 0;
}
- utf8_printf(stderr, "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n",
- zArg);
+ eputf("ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg);
return 0;
}
@@ -5806,7 +5677,7 @@ static FILE *output_file_open(const char *zFile, int bTextMode){
}else{
f = fopen(zFile, bTextMode ? "w" : "wb");
if( f==0 ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ eputf("Error: cannot open \"%s\"\n", zFile);
}
}
return f;
@@ -5828,7 +5699,7 @@ static int sql_trace_callback(
i64 nSql;
if( p->traceOut==0 ) return 0;
if( mType==SQLITE_TRACE_CLOSE ){
- utf8_printf(p->traceOut, "-- closing database connection\n");
+ sputz(p->traceOut, "-- closing database connection\n");
return 0;
}
if( mType!=SQLITE_TRACE_ROW && pX!=0 && ((const char*)pX)[0]=='-' ){
@@ -5859,12 +5730,12 @@ static int sql_trace_callback(
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
- utf8_printf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
+ sputf(p->traceOut, "%.*s;\n", (int)nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0;
- utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
+ sputf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec);
break;
}
}
@@ -5971,12 +5842,11 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){
break;
}
if( pc==cQuote && c!='\r' ){
- utf8_printf(stderr, "%s:%d: unescaped %c character\n",
- p->zFile, p->nLine, cQuote);
+ eputf("%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote);
}
if( c==EOF ){
- utf8_printf(stderr, "%s:%d: unterminated %c-quoted field\n",
- p->zFile, startLine, cQuote);
+ eputf("%s:%d: unterminated %c-quoted field\n",
+ p->zFile, startLine, cQuote);
p->cTerm = c;
break;
}
@@ -6074,9 +5944,8 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- utf8_printf(stderr, "Error %d: %s on [%s]\n",
- sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
- zQuery);
+ eputf("Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_data_xfer;
}
n = sqlite3_column_count(pQuery);
@@ -6092,9 +5961,8 @@ static void tryToCloneData(
memcpy(zInsert+i, ");", 3);
rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0);
if( rc ){
- utf8_printf(stderr, "Error %d: %s on [%s]\n",
- sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb),
- zInsert);
+ eputf("Error %d: %s on [%s]\n",
+ sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert);
goto end_data_xfer;
}
for(k=0; k<2; k++){
@@ -6129,8 +5997,8 @@ static void tryToCloneData(
} /* End for */
rc = sqlite3_step(pInsert);
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
- utf8_printf(stderr, "Error %d: %s\n", sqlite3_extended_errcode(newDb),
- sqlite3_errmsg(newDb));
+ eputf("Error %d: %s\n",
+ sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb));
}
sqlite3_reset(pInsert);
cnt++;
@@ -6147,7 +6015,7 @@ static void tryToCloneData(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- utf8_printf(stderr, "Warning: cannot step \"%s\" backwards", zTable);
+ eputf("Warning: cannot step \"%s\" backwards", zTable);
break;
}
} /* End for(k=0...) */
@@ -6184,9 +6052,8 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
- sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
- zQuery);
+ eputf("Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db),
+ sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){
@@ -6194,10 +6061,10 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){
- printf("%s... ", zName); fflush(stdout);
+ sputf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
@@ -6205,7 +6072,7 @@ static void tryToCloneSchema(
if( xForEach ){
xForEach(p, newDb, (const char*)zName);
}
- printf("done\n");
+ sputz(stdout, "done\n");
}
if( rc!=SQLITE_DONE ){
sqlite3_finalize(pQuery);
@@ -6215,9 +6082,8 @@ static void tryToCloneSchema(
shell_check_oom(zQuery);
rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0);
if( rc ){
- utf8_printf(stderr, "Error: (%d) %s on [%s]\n",
- sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db),
- zQuery);
+ eputf("Error: (%d) %s on [%s]\n",
+ sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery);
goto end_schema_xfer;
}
while( sqlite3_step(pQuery)==SQLITE_ROW ){
@@ -6225,17 +6091,17 @@ static void tryToCloneSchema(
zSql = sqlite3_column_text(pQuery, 1);
if( zName==0 || zSql==0 ) continue;
if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue;
- printf("%s... ", zName); fflush(stdout);
+ sputf(stdout, "%s... ", zName); fflush(stdout);
sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg);
if( zErrMsg ){
- utf8_printf(stderr, "Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
+ eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql);
sqlite3_free(zErrMsg);
zErrMsg = 0;
}
if( xForEach ){
xForEach(p, newDb, (const char*)zName);
}
- printf("done\n");
+ sputz(stdout, "done\n");
}
}
end_schema_xfer:
@@ -6252,13 +6118,12 @@ static void tryToClone(ShellState *p, const char *zNewDb){
int rc;
sqlite3 *newDb = 0;
if( access(zNewDb,0)==0 ){
- utf8_printf(stderr, "File \"%s\" already exists.\n", zNewDb);
+ eputf("File \"%s\" already exists.\n", zNewDb);
return;
}
rc = sqlite3_open(zNewDb, &newDb);
if( rc ){
- utf8_printf(stderr, "Cannot create output database: %s\n",
- sqlite3_errmsg(newDb));
+ eputf("Cannot create output database: %s\n", sqlite3_errmsg(newDb));
}else{
sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0);
sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0);
@@ -6270,6 +6135,18 @@ static void tryToClone(ShellState *p, const char *zNewDb){
close_db(newDb);
}
+#ifndef SQLITE_SHELL_FIDDLE
+/*
+** Change the output stream (file or pipe or console) to something else.
+*/
+static void output_redir(ShellState *p, FILE *pfNew){
+ if( p->out != stdout ) eputz("Output already redirected.\n");
+ else{
+ p->out = pfNew;
+ setOutputStream(pfNew);
+ }
+}
+
/*
** Change the output file back to stdout.
**
@@ -6297,7 +6174,7 @@ static void output_reset(ShellState *p){
char *zCmd;
zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
if( system(zCmd) ){
- utf8_printf(stderr, "Failed: [%s]\n", zCmd);
+ eputf("Failed: [%s]\n", zCmd);
}else{
/* Give the start/open/xdg-open command some time to get
** going before we continue, and potential delete the
@@ -6312,7 +6189,12 @@ static void output_reset(ShellState *p){
}
p->outfile[0] = 0;
p->out = stdout;
+ setOutputStream(stdout);
}
+#else
+# define output_redir(SS,pfO)
+# define output_reset(SS)
+#endif
/*
** Run an SQL command and return the single integer result.
@@ -6383,7 +6265,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
-1, &pStmt, 0);
if( rc ){
- utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
+ eputf("error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
return 1;
}
@@ -6396,28 +6278,28 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
memcpy(aHdr, pb, 100);
sqlite3_finalize(pStmt);
}else{
- raw_printf(stderr, "unable to read database header\n");
+ eputz("unable to read database header\n");
sqlite3_finalize(pStmt);
return 1;
}
i = get2byteInt(aHdr+16);
if( i==1 ) i = 65536;
- utf8_printf(p->out, "%-20s %d\n", "database page size:", i);
- utf8_printf(p->out, "%-20s %d\n", "write format:", aHdr[18]);
- utf8_printf(p->out, "%-20s %d\n", "read format:", aHdr[19]);
- utf8_printf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]);
+ oputf("%-20s %d\n", "database page size:", i);
+ oputf("%-20s %d\n", "write format:", aHdr[18]);
+ oputf("%-20s %d\n", "read format:", aHdr[19]);
+ oputf("%-20s %d\n", "reserved bytes:", aHdr[20]);
for(i=0; i<ArraySize(aField); i++){
int ofst = aField[i].ofst;
unsigned int val = get4byteInt(aHdr + ofst);
- utf8_printf(p->out, "%-20s %u", aField[i].zName, val);
+ oputf("%-20s %u", aField[i].zName, val);
switch( ofst ){
case 56: {
- if( val==1 ) raw_printf(p->out, " (utf8)");
- if( val==2 ) raw_printf(p->out, " (utf16le)");
- if( val==3 ) raw_printf(p->out, " (utf16be)");
+ if( val==1 ) oputz(" (utf8)");
+ if( val==2 ) oputz(" (utf16le)");
+ if( val==3 ) oputz(" (utf16be)");
}
}
- raw_printf(p->out, "\n");
+ oputz("\n");
}
if( zDb==0 ){
zSchemaTab = sqlite3_mprintf("main.sqlite_schema");
@@ -6430,11 +6312,11 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
int val = db_int(p->db, zSql);
sqlite3_free(zSql);
- utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val);
+ oputf("%-20s %d\n", aQuery[i].zName, val);
}
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
- utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion);
+ oputf("%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
@@ -6444,7 +6326,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){
*/
static int shellDatabaseError(sqlite3 *db){
const char *zErr = sqlite3_errmsg(db);
- utf8_printf(stderr, "Error: %s\n", zErr);
+ eputf("Error: %s\n", zErr);
return 1;
}
@@ -6679,7 +6561,6 @@ static int lintFkeyIndexes(
int nArg /* Number of entries in azArg[] */
){
sqlite3 *db = pState->db; /* Database handle to query "main" db of */
- FILE *out = pState->out; /* Stream to write non-error output to */
int bVerbose = 0; /* If -verbose is present */
int bGroupByParent = 0; /* If -groupbyparent is present */
int i; /* To iterate through azArg[] */
@@ -6761,9 +6642,7 @@ static int lintFkeyIndexes(
zIndent = " ";
}
else{
- raw_printf(stderr, "Usage: %s %s ?-verbose? ?-groupbyparent?\n",
- azArg[0], azArg[1]
- );
+ eputf("Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]);
return SQLITE_ERROR;
}
}
@@ -6807,23 +6686,23 @@ static int lintFkeyIndexes(
if( rc!=SQLITE_OK ) break;
if( res<0 ){
- raw_printf(stderr, "Error: internal error");
+ eputz("Error: internal error");
break;
}else{
if( bGroupByParent
&& (bVerbose || res==0)
&& (zPrev==0 || sqlite3_stricmp(zParent, zPrev))
){
- raw_printf(out, "-- Parent table %s\n", zParent);
+ oputf("-- Parent table %s\n", zParent);
sqlite3_free(zPrev);
zPrev = sqlite3_mprintf("%s", zParent);
}
if( res==0 ){
- raw_printf(out, "%s%s --> %s\n", zIndent, zCI, zTarget);
+ oputf("%s%s --> %s\n", zIndent, zCI, zTarget);
}else if( bVerbose ){
- raw_printf(out, "%s/* no extra indexes required for %s -> %s */\n",
- zIndent, zFrom, zTarget
+ oputf("%s/* no extra indexes required for %s -> %s */\n",
+ zIndent, zFrom, zTarget
);
}
}
@@ -6831,16 +6710,16 @@ static int lintFkeyIndexes(
sqlite3_free(zPrev);
if( rc!=SQLITE_OK ){
- raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
+ eputf("%s\n", sqlite3_errmsg(db));
}
rc2 = sqlite3_finalize(pSql);
if( rc==SQLITE_OK && rc2!=SQLITE_OK ){
rc = rc2;
- raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
+ eputf("%s\n", sqlite3_errmsg(db));
}
}else{
- raw_printf(stderr, "%s\n", sqlite3_errmsg(db));
+ eputf("%s\n", sqlite3_errmsg(db));
}
return rc;
@@ -6860,13 +6739,12 @@ static int lintDotCommand(
return lintFkeyIndexes(pState, azArg, nArg);
usage:
- raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]);
- raw_printf(stderr, "Where sub-commands are:\n");
- raw_printf(stderr, " fkey-indexes\n");
+ eputf("Usage %s sub-command ?switches...?\n", azArg[0]);
+ eputz("Where sub-commands are:\n");
+ eputz(" fkey-indexes\n");
return SQLITE_ERROR;
}
-#if !defined SQLITE_OMIT_VIRTUALTABLE
static void shellPrepare(
sqlite3 *db,
int *pRc,
@@ -6877,9 +6755,7 @@ static void shellPrepare(
if( *pRc==SQLITE_OK ){
int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
- raw_printf(stderr, "sql error: %s (%d)\n",
- sqlite3_errmsg(db), sqlite3_errcode(db)
- );
+ eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
*pRc = rc;
}
}
@@ -6887,12 +6763,8 @@ static void shellPrepare(
/*
** Create a prepared statement using printf-style arguments for the SQL.
-**
-** This routine is could be marked "static". But it is not always used,
-** depending on compile-time options. By omitting the "static", we avoid
-** nuisance compiler warnings about "defined but not used".
*/
-void shellPreparePrintf(
+static void shellPreparePrintf(
sqlite3 *db,
int *pRc,
sqlite3_stmt **ppStmt,
@@ -6915,13 +6787,10 @@ void shellPreparePrintf(
}
}
-/* Finalize the prepared statement created using shellPreparePrintf().
-**
-** This routine is could be marked "static". But it is not always used,
-** depending on compile-time options. By omitting the "static", we avoid
-** nuisance compiler warnings about "defined but not used".
+/*
+** Finalize the prepared statement created using shellPreparePrintf().
*/
-void shellFinalize(
+static void shellFinalize(
int *pRc,
sqlite3_stmt *pStmt
){
@@ -6930,13 +6799,14 @@ void shellFinalize(
int rc = sqlite3_finalize(pStmt);
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
- raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
+ eputf("SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
}
}
+#if !defined SQLITE_OMIT_VIRTUALTABLE
/* Reset the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static". But it is not always used,
@@ -6951,7 +6821,7 @@ void shellReset(
if( *pRc==SQLITE_OK ){
if( rc!=SQLITE_OK ){
sqlite3 *db = sqlite3_db_handle(pStmt);
- raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
+ eputf("SQL error: %s\n", sqlite3_errmsg(db));
}
*pRc = rc;
}
@@ -7001,11 +6871,11 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){
va_start(ap, zFmt);
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
- utf8_printf(stderr, "Error: %s\n", z);
+ eputf("Error: %s\n", z);
if( pAr->fromCmdLine ){
- utf8_printf(stderr, "Use \"-A\" for more help\n");
+ eputz("Use \"-A\" for more help\n");
}else{
- utf8_printf(stderr, "Use \".archive --help\" for more help\n");
+ eputz("Use \".archive --help\" for more help\n");
}
sqlite3_free(z);
return SQLITE_ERROR;
@@ -7105,7 +6975,7 @@ static int arParseCommand(
struct ArSwitch *pEnd = &aSwitch[nSwitch];
if( nArg<=1 ){
- utf8_printf(stderr, "Wrong number of arguments. Usage:\n");
+ eputz("Wrong number of arguments. Usage:\n");
return arUsage(stderr);
}else{
char *z = azArg[1];
@@ -7211,7 +7081,7 @@ static int arParseCommand(
}
}
if( pAr->eCmd==0 ){
- utf8_printf(stderr, "Required argument missing. Usage:\n");
+ eputz("Required argument missing. Usage:\n");
return arUsage(stderr);
}
return SQLITE_OK;
@@ -7254,7 +7124,7 @@ static int arCheckEntries(ArCommand *pAr){
}
shellReset(&rc, pTest);
if( rc==SQLITE_OK && bOk==0 ){
- utf8_printf(stderr, "not found in archive: %s\n", z);
+ eputf("not found in archive: %s\n", z);
rc = SQLITE_ERROR;
}
}
@@ -7321,18 +7191,15 @@ static int arListCommand(ArCommand *pAr){
shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose],
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
- utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql));
+ oputf("%s\n", sqlite3_sql(pSql));
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( pAr->bVerbose ){
- utf8_printf(pAr->p->out, "%s % 10d %s %s\n",
- sqlite3_column_text(pSql, 0),
- sqlite3_column_int(pSql, 1),
- sqlite3_column_text(pSql, 2),
- sqlite3_column_text(pSql, 3)
- );
+ oputf("%s % 10d %s %s\n",
+ sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1),
+ sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3));
}else{
- utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
+ oputf("%s\n", sqlite3_column_text(pSql, 0));
}
}
}
@@ -7341,7 +7208,6 @@ static int arListCommand(ArCommand *pAr){
return rc;
}
-
/*
** Implementation of .ar "Remove" command.
*/
@@ -7360,7 +7226,7 @@ static int arRemoveCommand(ArCommand *pAr){
zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;",
pAr->zSrcTable, zWhere);
if( pAr->bDryRun ){
- utf8_printf(pAr->p->out, "%s\n", zSql);
+ oputf("%s\n", zSql);
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0);
@@ -7373,7 +7239,7 @@ static int arRemoveCommand(ArCommand *pAr){
}
}
if( zErr ){
- utf8_printf(stdout, "ERROR: %s\n", zErr);
+ sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */
sqlite3_free(zErr);
}
}
@@ -7437,11 +7303,11 @@ static int arExtractCommand(ArCommand *pAr){
j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
sqlite3_bind_int(pSql, j, i);
if( pAr->bDryRun ){
- utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql));
+ oputf("%s\n", sqlite3_sql(pSql));
}else{
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
if( i==0 && pAr->bVerbose ){
- utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
+ oputf("%s\n", sqlite3_column_text(pSql, 0));
}
}
}
@@ -7461,13 +7327,13 @@ static int arExtractCommand(ArCommand *pAr){
static int arExecSql(ArCommand *pAr, const char *zSql){
int rc;
if( pAr->bDryRun ){
- utf8_printf(pAr->p->out, "%s\n", zSql);
+ oputf("%s\n", zSql);
rc = SQLITE_OK;
}else{
char *zErr = 0;
rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
if( zErr ){
- utf8_printf(stdout, "ERROR: %s\n", zErr);
+ sputf(stdout, "ERROR: %s\n", zErr);
sqlite3_free(zErr);
}
}
@@ -7642,15 +7508,13 @@ static int arDotCommand(
}
cmd.db = 0;
if( cmd.bDryRun ){
- utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
- eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
+ oputf("-- open database '%s'%s\n", cmd.zFile,
+ eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
}
rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "cannot open file: %s (%s)\n",
- cmd.zFile, sqlite3_errmsg(cmd.db)
- );
+ eputf("cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db));
goto end_ar_command;
}
sqlite3_fileio_init(cmd.db, 0, 0);
@@ -7663,7 +7527,7 @@ static int arDotCommand(
if( cmd.eCmd!=AR_CMD_CREATE
&& sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
){
- utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
+ eputz("database does not contain an 'sqlar' table\n");
rc = SQLITE_ERROR;
goto end_ar_command;
}
@@ -7721,7 +7585,7 @@ end_ar_command:
*/
static int recoverSqlCb(void *pCtx, const char *zSql){
ShellState *pState = (ShellState*)pCtx;
- utf8_printf(pState->out, "%s;\n", zSql);
+ sputf(pState->out, "%s;\n", zSql);
return SQLITE_OK;
}
@@ -7764,7 +7628,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
bRowids = 0;
}
else{
- utf8_printf(stderr, "unexpected option: %s\n", azArg[i]);
+ eputf("unexpected option: %s\n", azArg[i]);
showHelp(pState->out, azArg[0]);
return 1;
}
@@ -7783,13 +7647,47 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
const char *zErr = sqlite3_recover_errmsg(p);
int errCode = sqlite3_recover_errcode(p);
- raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode);
+ eputf("sql error: %s (%d)\n", zErr, errCode);
}
rc = sqlite3_recover_finish(p);
return rc;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
+/*
+** Implementation of ".intck STEPS_PER_UNLOCK" command.
+*/
+static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){
+ sqlite3_intck *p = 0;
+ int rc = SQLITE_OK;
+
+ rc = sqlite3_intck_open(pState->db, "main", &p);
+ if( rc==SQLITE_OK ){
+ i64 nStep = 0;
+ i64 nError = 0;
+ const char *zErr = 0;
+ while( SQLITE_OK==sqlite3_intck_step(p) ){
+ const char *zMsg = sqlite3_intck_message(p);
+ if( zMsg ){
+ oputf("%s\n", zMsg);
+ nError++;
+ }
+ nStep++;
+ if( nStepPerUnlock && (nStep % nStepPerUnlock)==0 ){
+ sqlite3_intck_unlock(p);
+ }
+ }
+ rc = sqlite3_intck_error(p, &zErr);
+ if( zErr ){
+ eputf("%s\n", zErr);
+ }
+ sqlite3_intck_close(p);
+
+ oputf("%lld steps, %lld errors\n", nStep, nError);
+ }
+
+ return rc;
+}
/*
* zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it.
@@ -7808,7 +7706,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
#define rc_err_oom_die(rc) \
if( rc==SQLITE_NOMEM ) shell_check_oom(0); \
else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \
- fprintf(stderr,"E:%d\n",rc), assert(0)
+ eputf("E:%d\n",rc), assert(0)
#else
static void rc_err_oom_die(int rc){
if( rc==SQLITE_NOMEM ) shell_check_oom(0);
@@ -7948,6 +7846,7 @@ FROM (\
sqlite3_exec(*pDb,"drop table if exists ColNames;"
"drop view if exists RepeatedNames;",0,0,0);
#endif
+#undef SHELL_COLFIX_DB
rc = sqlite3_exec(*pDb, zTabMake, 0, 0, 0);
rc_err_oom_die(rc);
}
@@ -8009,6 +7908,62 @@ FROM (\
}
/*
+** Check if the sqlite_schema table contains one or more virtual tables. If
+** parameter zLike is not NULL, then it is an SQL expression that the
+** sqlite_schema row must also match. If one or more such rows are found,
+** print the following warning to the output:
+**
+** WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled
+*/
+static int outputDumpWarning(ShellState *p, const char *zLike){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pStmt = 0;
+ shellPreparePrintf(p->db, &rc, &pStmt,
+ "SELECT 1 FROM sqlite_schema o WHERE "
+ "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
+ );
+ if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
+ oputz("/* WARNING: "
+ "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
+ );
+ }
+ shellFinalize(&rc, pStmt);
+ return rc;
+}
+
+/*
+** Fault-Simulator state and logic.
+*/
+static struct {
+ int iId; /* ID that triggers a simulated fault. -1 means "any" */
+ int iErr; /* The error code to return on a fault */
+ int iCnt; /* Trigger the fault only if iCnt is already zero */
+ int iInterval; /* Reset iCnt to this value after each fault */
+ int eVerbose; /* When to print output */
+} faultsim_state = {-1, 0, 0, 0, 0};
+
+/*
+** This is the fault-sim callback
+*/
+static int faultsim_callback(int iArg){
+ if( faultsim_state.iId>0 && faultsim_state.iId!=iArg ){
+ return SQLITE_OK;
+ }
+ if( faultsim_state.iCnt>0 ){
+ faultsim_state.iCnt--;
+ if( faultsim_state.eVerbose>=2 ){
+ oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt);
+ }
+ return SQLITE_OK;
+ }
+ if( faultsim_state.eVerbose>=1 ){
+ oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr);
+ }
+ faultsim_state.iCnt = faultsim_state.iInterval;
+ return faultsim_state.iErr;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -8047,7 +8002,6 @@ static int do_meta_command(char *zLine, ShellState *p){
azArg[nArg++] = &zLine[h];
while( zLine[h] && !IsSpace(zLine[h]) ){ h++; }
if( zLine[h] ) zLine[h++] = 0;
- resolve_backslashes(azArg[nArg-1]);
}
}
azArg[nArg] = 0;
@@ -8062,7 +8016,7 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_OMIT_AUTHORIZATION
if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){
if( nArg!=2 ){
- raw_printf(stderr, "Usage: .auth ON|OFF\n");
+ eputz("Usage: .auth ON|OFF\n");
rc = 1;
goto meta_command_exit;
}
@@ -8109,7 +8063,7 @@ static int do_meta_command(char *zLine, ShellState *p){
bAsync = 1;
}else
{
- utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
+ eputf("unknown option: %s\n", azArg[j]);
return 1;
}
}else if( zDestFile==0 ){
@@ -8118,19 +8072,19 @@ static int do_meta_command(char *zLine, ShellState *p){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
- raw_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
+ eputz("Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
return 1;
}
}
if( zDestFile==0 ){
- raw_printf(stderr, "missing FILENAME argument on .backup\n");
+ eputz("missing FILENAME argument on .backup\n");
return 1;
}
if( zDb==0 ) zDb = "main";
rc = sqlite3_open_v2(zDestFile, &pDest,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
+ eputf("Error: cannot open \"%s\"\n", zDestFile);
close_db(pDest);
return 1;
}
@@ -8141,7 +8095,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
+ eputf("Error: %s\n", sqlite3_errmsg(pDest));
close_db(pDest);
return 1;
}
@@ -8150,7 +8104,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( rc==SQLITE_DONE ){
rc = 0;
}else{
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
+ eputf("Error: %s\n", sqlite3_errmsg(pDest));
rc = 1;
}
close_db(pDest);
@@ -8161,7 +8115,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==2 ){
bail_on_error = booleanValue(azArg[1]);
}else{
- raw_printf(stderr, "Usage: .bail on|off\n");
+ eputz("Usage: .bail on|off\n");
rc = 1;
}
}else
@@ -8175,9 +8129,8 @@ static int do_meta_command(char *zLine, ShellState *p){
setTextMode(p->out, 1);
}
}else{
- raw_printf(stderr, "The \".binary\" command is deprecated."
- " Use \".crnl\" instead.\n");
- raw_printf(stderr, "Usage: .binary on|off\n");
+ eputz("The \".binary\" command is deprecated. Use \".crnl\" instead.\n"
+ "Usage: .binary on|off\n");
rc = 1;
}
}else
@@ -8201,11 +8154,11 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = chdir(azArg[1]);
#endif
if( rc ){
- utf8_printf(stderr, "Cannot change to directory \"%s\"\n", azArg[1]);
+ eputf("Cannot change to directory \"%s\"\n", azArg[1]);
rc = 1;
}
}else{
- raw_printf(stderr, "Usage: .cd DIRECTORY\n");
+ eputz("Usage: .cd DIRECTORY\n");
rc = 1;
}
}else
@@ -8215,7 +8168,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==2 ){
setOrClearFlag(p, SHFLG_CountChanges, azArg[1]);
}else{
- raw_printf(stderr, "Usage: .changes on|off\n");
+ eputz("Usage: .changes on|off\n");
rc = 1;
}
}else
@@ -8229,17 +8182,16 @@ static int do_meta_command(char *zLine, ShellState *p){
char *zRes = 0;
output_reset(p);
if( nArg!=2 ){
- raw_printf(stderr, "Usage: .check GLOB-PATTERN\n");
+ eputz("Usage: .check GLOB-PATTERN\n");
rc = 2;
}else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
rc = 2;
}else if( testcase_glob(azArg[1],zRes)==0 ){
- utf8_printf(stderr,
- "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
- p->zTestcase, azArg[1], zRes);
+ eputf("testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n",
+ p->zTestcase, azArg[1], zRes);
rc = 1;
}else{
- utf8_printf(stdout, "testcase-%s ok\n", p->zTestcase);
+ oputf("testcase-%s ok\n", p->zTestcase);
p->nCheck++;
}
sqlite3_free(zRes);
@@ -8252,7 +8204,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==2 ){
tryToClone(p, azArg[1]);
}else{
- raw_printf(stderr, "Usage: .clone FILENAME\n");
+ eputz("Usage: .clone FILENAME\n");
rc = 1;
}
}else
@@ -8272,9 +8224,9 @@ static int do_meta_command(char *zLine, ShellState *p){
zFile = "(temporary-file)";
}
if( p->pAuxDb == &p->aAuxDb[i] ){
- utf8_printf(stdout, "ACTIVE %d: %s\n", i, zFile);
+ sputf(stdout, "ACTIVE %d: %s\n", i, zFile);
}else if( p->aAuxDb[i].db!=0 ){
- utf8_printf(stdout, " %d: %s\n", i, zFile);
+ sputf(stdout, " %d: %s\n", i, zFile);
}
}
}else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
@@ -8291,7 +8243,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( i<0 || i>=ArraySize(p->aAuxDb) ){
/* No-op */
}else if( p->pAuxDb == &p->aAuxDb[i] ){
- raw_printf(stderr, "cannot close the active database connection\n");
+ eputz("cannot close the active database connection\n");
rc = 1;
}else if( p->aAuxDb[i].db ){
session_close_all(p, i);
@@ -8299,7 +8251,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->aAuxDb[i].db = 0;
}
}else{
- raw_printf(stderr, "Usage: .connection [close] [CONNECTION-NUMBER]\n");
+ eputz("Usage: .connection [close] [CONNECTION-NUMBER]\n");
rc = 1;
}
}else
@@ -8313,9 +8265,9 @@ static int do_meta_command(char *zLine, ShellState *p){
}
}else{
#if !defined(_WIN32) && !defined(WIN32)
- raw_printf(stderr, "The \".crnl\" is a no-op on non-Windows machines.\n");
+ eputz("The \".crnl\" is a no-op on non-Windows machines.\n");
#endif
- raw_printf(stderr, "Usage: .crnl on|off\n");
+ eputz("Usage: .crnl on|off\n");
rc = 1;
}
}else
@@ -8328,7 +8280,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ){
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
rc = 1;
}else{
while( sqlite3_step(pStmt)==SQLITE_ROW ){
@@ -8347,11 +8299,9 @@ static int do_meta_command(char *zLine, ShellState *p){
int eTxn = sqlite3_txn_state(p->db, azName[i*2]);
int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]);
const char *z = azName[i*2+1];
- utf8_printf(p->out, "%s: %s %s%s\n",
- azName[i*2],
- z && z[0] ? z : "\"\"",
- bRdonly ? "r/o" : "r/w",
- eTxn==SQLITE_TXN_NONE ? "" :
+ oputf("%s: %s %s%s\n",
+ azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w",
+ eTxn==SQLITE_TXN_NONE ? "" :
eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn");
free(azName[i*2]);
free(azName[i*2+1]);
@@ -8391,12 +8341,12 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0);
}
sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v);
- utf8_printf(p->out, "%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off");
+ oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off");
if( nArg>1 ) break;
}
if( nArg>1 && ii==ArraySize(aDbConfig) ){
- utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]);
- utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n");
+ eputf("Error: unknown dbconfig \"%s\"\n", azArg[1]);
+ eputz("Enter \".dbconfig\" with no arguments for a list\n");
}
}else
@@ -8426,8 +8376,8 @@ static int do_meta_command(char *zLine, ShellState *p){
if( z[0]=='-' ) z++;
if( cli_strcmp(z,"preserve-rowids")==0 ){
#ifdef SQLITE_OMIT_VIRTUALTABLE
- raw_printf(stderr, "The --preserve-rowids option is not compatible"
- " with SQLITE_OMIT_VIRTUALTABLE\n");
+ eputz("The --preserve-rowids option is not compatible"
+ " with SQLITE_OMIT_VIRTUALTABLE\n");
rc = 1;
sqlite3_free(zLike);
goto meta_command_exit;
@@ -8445,7 +8395,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ShellSetFlag(p, SHFLG_DumpNoSys);
}else
{
- raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
+ eputf("Unknown option \"%s\" on \".dump\"\n", azArg[i]);
rc = 1;
sqlite3_free(zLike);
goto meta_command_exit;
@@ -8475,12 +8425,13 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
+ outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */
- raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
- raw_printf(p->out, "BEGIN TRANSACTION;\n");
+ oputz("PRAGMA foreign_keys=OFF;\n");
+ oputz("BEGIN TRANSACTION;\n");
}
p->writableSchema = 0;
p->showHeader = 0;
@@ -8503,7 +8454,8 @@ static int do_meta_command(char *zLine, ShellState *p){
zSql = sqlite3_mprintf(
"SELECT sql FROM sqlite_schema AS o "
"WHERE (%s) AND sql NOT NULL"
- " AND type IN ('index','trigger','view')",
+ " AND type IN ('index','trigger','view') "
+ "ORDER BY type COLLATE NOCASE DESC",
zLike
);
run_table_dump_query(p, zSql);
@@ -8511,13 +8463,13 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_free(zLike);
if( p->writableSchema ){
- raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
+ oputz("PRAGMA writable_schema=OFF;\n");
p->writableSchema = 0;
}
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
- raw_printf(p->out, p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
+ oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n");
}
p->showHeader = savedShowHeader;
p->shellFlgs = savedShellFlags;
@@ -8527,7 +8479,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==2 ){
setOrClearFlag(p, SHFLG_Echo, azArg[1]);
}else{
- raw_printf(stderr, "Usage: .echo on|off\n");
+ eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
@@ -8558,7 +8510,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->autoEQP = (u8)booleanValue(azArg[1]);
}
}else{
- raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n");
+ eputz("Usage: .eqp off|on|trace|trigger|full\n");
rc = 1;
}
}else
@@ -8597,9 +8549,8 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
- raw_printf(stderr,
- "Cannot run experimental commands such as \"%s\" in safe mode\n",
- azArg[0]);
+ eputf("Cannot run experimental commands such as \"%s\" in safe mode\n",
+ azArg[0]);
rc = 1;
}else{
open_db(p, 0);
@@ -8655,10 +8606,9 @@ static int do_meta_command(char *zLine, ShellState *p){
/* --help lists all file-controls */
if( cli_strcmp(zCmd,"help")==0 ){
- utf8_printf(p->out, "Available file-controls:\n");
+ oputz("Available file-controls:\n");
for(i=0; i<ArraySize(aCtrl); i++){
- utf8_printf(p->out, " .filectrl %s %s\n",
- aCtrl[i].zCtrlName, aCtrl[i].zUsage);
+ oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
goto meta_command_exit;
@@ -8673,16 +8623,16 @@ static int do_meta_command(char *zLine, ShellState *p){
filectrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
- utf8_printf(stderr, "Error: ambiguous file-control: \"%s\"\n"
- "Use \".filectrl --help\" for help\n", zCmd);
+ eputf("Error: ambiguous file-control: \"%s\"\n"
+ "Use \".filectrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
}
}
}
if( filectrl<0 ){
- utf8_printf(stderr,"Error: unknown file-control: %s\n"
- "Use \".filectrl --help\" for help\n", zCmd);
+ eputf("Error: unknown file-control: %s\n"
+ "Use \".filectrl --help\" for help\n", zCmd);
}else{
switch(filectrl){
case SQLITE_FCNTL_SIZE_LIMIT: {
@@ -8725,7 +8675,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg!=2 ) break;
sqlite3_file_control(p->db, zSchema, filectrl, &z);
if( z ){
- utf8_printf(p->out, "%s\n", z);
+ oputf("%s\n", z);
sqlite3_free(z);
}
isOk = 2;
@@ -8739,19 +8689,19 @@ static int do_meta_command(char *zLine, ShellState *p){
}
x = -1;
sqlite3_file_control(p->db, zSchema, filectrl, &x);
- utf8_printf(p->out,"%d\n", x);
+ oputf("%d\n", x);
isOk = 2;
break;
}
}
}
if( isOk==0 && iCtrl>=0 ){
- utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
+ oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
char zBuf[100];
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes);
- raw_printf(p->out, "%s\n", zBuf);
+ oputf("%s\n", zBuf);
}
}else
@@ -8766,7 +8716,7 @@ static int do_meta_command(char *zLine, ShellState *p){
nArg = 1;
}
if( nArg!=1 ){
- raw_printf(stderr, "Usage: .fullschema ?--indent?\n");
+ eputz("Usage: .fullschema ?--indent?\n");
rc = 1;
goto meta_command_exit;
}
@@ -8786,19 +8736,21 @@ static int do_meta_command(char *zLine, ShellState *p){
"SELECT rowid FROM sqlite_schema"
" WHERE name GLOB 'sqlite_stat[134]'",
-1, &pStmt, 0);
- doStats = sqlite3_step(pStmt)==SQLITE_ROW;
- sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ doStats = sqlite3_step(pStmt)==SQLITE_ROW;
+ sqlite3_finalize(pStmt);
+ }
}
if( doStats==0 ){
- raw_printf(p->out, "/* No STAT tables available */\n");
+ oputz("/* No STAT tables available */\n");
}else{
- raw_printf(p->out, "ANALYZE sqlite_schema;\n");
+ oputz("ANALYZE sqlite_schema;\n");
data.cMode = data.mode = MODE_Insert;
data.zDestTable = "sqlite_stat1";
shell_exec(&data, "SELECT * FROM sqlite_stat1", 0);
data.zDestTable = "sqlite_stat4";
shell_exec(&data, "SELECT * FROM sqlite_stat4", 0);
- raw_printf(p->out, "ANALYZE sqlite_schema;\n");
+ oputz("ANALYZE sqlite_schema;\n");
}
}else
@@ -8807,7 +8759,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->showHeader = booleanValue(azArg[1]);
p->shellFlgs |= SHFLG_HeaderSet;
}else{
- raw_printf(stderr, "Usage: .headers on|off\n");
+ eputz("Usage: .headers on|off\n");
rc = 1;
}
}else
@@ -8816,7 +8768,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg>=2 ){
n = showHelp(p->out, azArg[1]);
if( n==0 ){
- utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
+ oputf("Nothing matches '%s'\n", azArg[1]);
}
}else{
showHelp(p->out, 0);
@@ -8860,7 +8812,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( zTable==0 ){
zTable = z;
}else{
- utf8_printf(p->out, "ERROR: extra argument: \"%s\". Usage:\n", z);
+ oputf("ERROR: extra argument: \"%s\". Usage:\n", z);
showHelp(p->out, "import");
goto meta_command_exit;
}
@@ -8881,14 +8833,14 @@ static int do_meta_command(char *zLine, ShellState *p){
xRead = csv_read_one_field;
useOutputMode = 0;
}else{
- utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z);
+ oputf("ERROR: unknown option: \"%s\". Usage:\n", z);
showHelp(p->out, "import");
goto meta_command_exit;
}
}
if( zTable==0 ){
- utf8_printf(p->out, "ERROR: missing %s argument. Usage:\n",
- zFile==0 ? "FILE" : "TABLE");
+ oputf("ERROR: missing %s argument. Usage:\n",
+ zFile==0 ? "FILE" : "TABLE");
showHelp(p->out, "import");
goto meta_command_exit;
}
@@ -8899,20 +8851,17 @@ static int do_meta_command(char *zLine, ShellState *p){
** the column and row separator characters from the output mode. */
nSep = strlen30(p->colSeparator);
if( nSep==0 ){
- raw_printf(stderr,
- "Error: non-null column separator required for import\n");
+ eputz("Error: non-null column separator required for import\n");
goto meta_command_exit;
}
if( nSep>1 ){
- raw_printf(stderr,
- "Error: multi-character column separators not allowed"
+ eputz("Error: multi-character column separators not allowed"
" for import\n");
goto meta_command_exit;
}
nSep = strlen30(p->rowSeparator);
if( nSep==0 ){
- raw_printf(stderr,
- "Error: non-null row separator required for import\n");
+ eputz("Error: non-null row separator required for import\n");
goto meta_command_exit;
}
if( nSep==2 && p->mode==MODE_Csv
@@ -8926,8 +8875,8 @@ static int do_meta_command(char *zLine, ShellState *p){
nSep = strlen30(p->rowSeparator);
}
if( nSep>1 ){
- raw_printf(stderr, "Error: multi-character row separators not allowed"
- " for import\n");
+ eputz("Error: multi-character row separators not allowed"
+ " for import\n");
goto meta_command_exit;
}
sCtx.cColSep = (u8)p->colSeparator[0];
@@ -8937,7 +8886,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sCtx.nLine = 1;
if( sCtx.zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
- raw_printf(stderr, "Error: pipes are not supported in this OS\n");
+ eputz("Error: pipes are not supported in this OS\n");
goto meta_command_exit;
#else
sCtx.in = popen(sCtx.zFile+1, "r");
@@ -8949,19 +8898,19 @@ static int do_meta_command(char *zLine, ShellState *p){
sCtx.xCloser = fclose;
}
if( sCtx.in==0 ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ eputf("Error: cannot open \"%s\"\n", zFile);
goto meta_command_exit;
}
if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
char zSep[2];
zSep[1] = 0;
zSep[0] = sCtx.cColSep;
- utf8_printf(p->out, "Column separator ");
- output_c_string(p->out, zSep);
- utf8_printf(p->out, ", row separator ");
+ oputz("Column separator ");
+ output_c_string(zSep);
+ oputz(", row separator ");
zSep[0] = sCtx.cRowSep;
- output_c_string(p->out, zSep);
- utf8_printf(p->out, "\n");
+ output_c_string(zSep);
+ oputz("\n");
}
sCtx.z = sqlite3_malloc64(120);
if( sCtx.z==0 ){
@@ -8996,14 +8945,14 @@ static int do_meta_command(char *zLine, ShellState *p){
}
zColDefs = zAutoColumn(0, &dbCols, &zRenames);
if( zRenames!=0 ){
- utf8_printf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
- "Columns renamed during .import %s due to duplicates:\n"
- "%s\n", sCtx.zFile, zRenames);
+ sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr,
+ "Columns renamed during .import %s due to duplicates:\n"
+ "%s\n", sCtx.zFile, zRenames);
sqlite3_free(zRenames);
}
assert(dbCols==0);
if( zColDefs==0 ){
- utf8_printf(stderr,"%s: empty file\n", sCtx.zFile);
+ eputf("%s: empty file\n", sCtx.zFile);
import_fail:
sqlite3_free(zCreate);
sqlite3_free(zSql);
@@ -9014,11 +8963,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}
zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
if( eVerbose>=1 ){
- utf8_printf(p->out, "%s\n", zCreate);
+ oputf("%s\n", zCreate);
}
rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
if( rc ){
- utf8_printf(stderr, "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
+ eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db));
goto import_fail;
}
sqlite3_free(zCreate);
@@ -9027,7 +8976,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
- utf8_printf(stderr,"Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
goto import_fail;
}
sqlite3_free(zSql);
@@ -9049,11 +8998,11 @@ static int do_meta_command(char *zLine, ShellState *p){
zSql[j++] = ')';
zSql[j] = 0;
if( eVerbose>=2 ){
- utf8_printf(p->out, "Insert using: %s\n", zSql);
+ oputf("Insert using: %s\n", zSql);
}
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
if( rc ){
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
if (pStmt) sqlite3_finalize(pStmt);
goto import_fail;
}
@@ -9076,11 +9025,19 @@ static int do_meta_command(char *zLine, ShellState *p){
** the remaining columns.
*/
if( p->mode==MODE_Ascii && (z==0 || z[0]==0) && i==0 ) break;
+ /*
+ ** For CSV mode, per RFC 4180, accept EOF in lieu of final
+ ** record terminator but only for last field of multi-field row.
+ ** (If there are too few fields, it's not valid CSV anyway.)
+ */
+ if( z==0 && (xRead==csv_read_one_field) && i==nCol-1 && i>0 ){
+ z = "";
+ }
sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){
- utf8_printf(stderr, "%s:%d: expected %d columns but found %d - "
- "filling the rest with NULL\n",
- sCtx.zFile, startLine, nCol, i+1);
+ eputf("%s:%d: expected %d columns but found %d"
+ " - filling the rest with NULL\n",
+ sCtx.zFile, startLine, nCol, i+1);
i += 2;
while( i<=nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
@@ -9090,16 +9047,15 @@ static int do_meta_command(char *zLine, ShellState *p){
xRead(&sCtx);
i++;
}while( sCtx.cTerm==sCtx.cColSep );
- utf8_printf(stderr, "%s:%d: expected %d columns but found %d - "
- "extras ignored\n",
- sCtx.zFile, startLine, nCol, i);
+ eputf("%s:%d: expected %d columns but found %d - extras ignored\n",
+ sCtx.zFile, startLine, nCol, i);
}
if( i>=nCol ){
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "%s:%d: INSERT failed: %s\n", sCtx.zFile,
- startLine, sqlite3_errmsg(p->db));
+ eputf("%s:%d: INSERT failed: %s\n",
+ sCtx.zFile, startLine, sqlite3_errmsg(p->db));
sCtx.nErr++;
}else{
sCtx.nRow++;
@@ -9111,9 +9067,8 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
if( eVerbose>0 ){
- utf8_printf(p->out,
- "Added %d rows with %d errors using %d lines of input\n",
- sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
+ oputf("Added %d rows with %d errors using %d lines of input\n",
+ sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
}
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
@@ -9128,14 +9083,14 @@ static int do_meta_command(char *zLine, ShellState *p){
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
if( !ShellHasFlag(p,SHFLG_TestingMode) ){
- utf8_printf(stderr, ".%s unavailable without --unsafe-testing\n",
- "imposter");
+ eputf(".%s unavailable without --unsafe-testing\n",
+ "imposter");
rc = 1;
goto meta_command_exit;
}
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
- utf8_printf(stderr, "Usage: .imposter INDEX IMPOSTER\n"
- " .imposter off\n");
+ eputz("Usage: .imposter INDEX IMPOSTER\n"
+ " .imposter off\n");
/* Also allowed, but not documented:
**
** .imposter TABLE IMPOSTER
@@ -9194,7 +9149,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
sqlite3_finalize(pStmt);
if( i==0 || tnum==0 ){
- utf8_printf(stderr, "no such index: \"%s\"\n", azArg[1]);
+ eputf("no such index: \"%s\"\n", azArg[1]);
rc = 1;
sqlite3_free(zCollist);
goto meta_command_exit;
@@ -9209,22 +9164,35 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
if( rc ){
- utf8_printf(stderr, "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
+ eputf("Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
}else{
- utf8_printf(stdout, "%s;\n", zSql);
- raw_printf(stdout,
- "WARNING: writing to an imposter table will corrupt the \"%s\" %s!\n",
- azArg[1], isWO ? "table" : "index"
- );
+ sputf(stdout, "%s;\n", zSql);
+ sputf(stdout, "WARNING: writing to an imposter table will corrupt"
+ " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index");
}
}else{
- raw_printf(stderr, "SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
+ eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
#endif /* !defined(SQLITE_OMIT_TEST_CONTROL) */
+ if( c=='i' && cli_strncmp(azArg[0], "intck", n)==0 ){
+ i64 iArg = 0;
+ if( nArg==2 ){
+ iArg = integerValue(azArg[1]);
+ if( iArg==0 ) iArg = -1;
+ }
+ if( (nArg!=1 && nArg!=2) || iArg<0 ){
+ eputf("Usage: .intck STEPS_PER_UNLOCK\n");
+ rc = 1;
+ goto meta_command_exit;
+ }
+ open_db(p, 0);
+ rc = intckDatabaseCmd(p, iArg);
+ }else
+
#ifdef SQLITE_ENABLE_IOTRACE
if( c=='i' && cli_strncmp(azArg[0], "iotrace", n)==0 ){
SQLITE_API extern void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...);
@@ -9238,7 +9206,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else{
iotrace = fopen(azArg[1], "w");
if( iotrace==0 ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
+ eputf("Error: cannot open \"%s\"\n", azArg[1]);
sqlite3IoTrace = 0;
rc = 1;
}else{
@@ -9270,11 +9238,11 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
if( nArg==1 ){
for(i=0; i<ArraySize(aLimit); i++){
- printf("%20s %d\n", aLimit[i].zLimitName,
- sqlite3_limit(p->db, aLimit[i].limitCode, -1));
+ sputf(stdout, "%20s %d\n", aLimit[i].zLimitName,
+ sqlite3_limit(p->db, aLimit[i].limitCode, -1));
}
}else if( nArg>3 ){
- raw_printf(stderr, "Usage: .limit NAME ?NEW-VALUE?\n");
+ eputz("Usage: .limit NAME ?NEW-VALUE?\n");
rc = 1;
goto meta_command_exit;
}else{
@@ -9285,16 +9253,16 @@ static int do_meta_command(char *zLine, ShellState *p){
if( iLimit<0 ){
iLimit = i;
}else{
- utf8_printf(stderr, "ambiguous limit: \"%s\"\n", azArg[1]);
+ eputf("ambiguous limit: \"%s\"\n", azArg[1]);
rc = 1;
goto meta_command_exit;
}
}
}
if( iLimit<0 ){
- utf8_printf(stderr, "unknown limit: \"%s\"\n"
- "enter \".limits\" with no arguments for a list.\n",
- azArg[1]);
+ eputf("unknown limit: \"%s\"\n"
+ "enter \".limits\" with no arguments for a list.\n",
+ azArg[1]);
rc = 1;
goto meta_command_exit;
}
@@ -9302,8 +9270,8 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_limit(p->db, aLimit[iLimit].limitCode,
(int)integerValue(azArg[2]));
}
- printf("%20s %d\n", aLimit[iLimit].zLimitName,
- sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1));
+ sputf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName,
+ sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1));
}
}else
@@ -9319,7 +9287,7 @@ static int do_meta_command(char *zLine, ShellState *p){
failIfSafeMode(p, "cannot run .load in safe mode");
if( nArg<2 || azArg[1][0]==0 ){
/* Must have a non-empty FILE. (Will not load self.) */
- raw_printf(stderr, "Usage: .load FILE ?ENTRYPOINT?\n");
+ eputz("Usage: .load FILE ?ENTRYPOINT?\n");
rc = 1;
goto meta_command_exit;
}
@@ -9328,7 +9296,7 @@ static int do_meta_command(char *zLine, ShellState *p){
open_db(p, 0);
rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg);
if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "Error: %s\n", zErrMsg);
+ eputf("Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}
@@ -9337,7 +9305,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='l' && cli_strncmp(azArg[0], "log", n)==0 ){
if( nArg!=2 ){
- raw_printf(stderr, "Usage: .log FILENAME\n");
+ eputz("Usage: .log FILENAME\n");
rc = 1;
}else{
const char *zFile = azArg[1];
@@ -9345,8 +9313,8 @@ static int do_meta_command(char *zLine, ShellState *p){
&& cli_strcmp(zFile,"on")!=0
&& cli_strcmp(zFile,"off")!=0
){
- raw_printf(stdout, "cannot set .log to anything other "
- "than \"on\" or \"off\"\n");
+ sputz(stdout, "cannot set .log to anything other"
+ " than \"on\" or \"off\"\n");
zFile = "off";
}
output_file_close(p->pLog);
@@ -9385,17 +9353,17 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( zTabname==0 ){
zTabname = z;
}else if( z[0]=='-' ){
- utf8_printf(stderr, "unknown option: %s\n", z);
- utf8_printf(stderr, "options:\n"
- " --noquote\n"
- " --quote\n"
- " --wordwrap on/off\n"
- " --wrap N\n"
- " --ww\n");
+ eputf("unknown option: %s\n", z);
+ eputz("options:\n"
+ " --noquote\n"
+ " --quote\n"
+ " --wordwrap on/off\n"
+ " --wrap N\n"
+ " --ww\n");
rc = 1;
goto meta_command_exit;
}else{
- utf8_printf(stderr, "extra argument: \"%s\"\n", z);
+ eputf("extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}
@@ -9404,14 +9372,12 @@ static int do_meta_command(char *zLine, ShellState *p){
if( p->mode==MODE_Column
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
){
- raw_printf
- (p->out,
- "current output mode: %s --wrap %d --wordwrap %s --%squote\n",
- modeDescr[p->mode], p->cmOpts.iWrap,
- p->cmOpts.bWordWrap ? "on" : "off",
- p->cmOpts.bQuote ? "" : "no");
+ oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n",
+ modeDescr[p->mode], p->cmOpts.iWrap,
+ p->cmOpts.bWordWrap ? "on" : "off",
+ p->cmOpts.bQuote ? "" : "no");
}else{
- raw_printf(p->out, "current output mode: %s\n", modeDescr[p->mode]);
+ oputf("current output mode: %s\n", modeDescr[p->mode]);
}
zMode = modeDescr[p->mode];
}
@@ -9470,9 +9436,9 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( cli_strncmp(zMode,"json",n2)==0 ){
p->mode = MODE_Json;
}else{
- raw_printf(stderr, "Error: mode should be one of: "
- "ascii box column csv html insert json line list markdown "
- "qbox quote table tabs tcl\n");
+ eputz("Error: mode should be one of: "
+ "ascii box column csv html insert json line list markdown "
+ "qbox quote table tabs tcl\n");
rc = 1;
}
p->cMode = p->mode;
@@ -9481,11 +9447,11 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifndef SQLITE_SHELL_FIDDLE
if( c=='n' && cli_strcmp(azArg[0], "nonce")==0 ){
if( nArg!=2 ){
- raw_printf(stderr, "Usage: .nonce NONCE\n");
+ eputz("Usage: .nonce NONCE\n");
rc = 1;
}else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){
- raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n",
- p->lineno, azArg[1]);
+ eputf("line %d: incorrect nonce: \"%s\"\n",
+ p->lineno, azArg[1]);
exit(1);
}else{
p->bSafeMode = 0;
@@ -9500,7 +9466,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
"%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
}else{
- raw_printf(stderr, "Usage: .nullvalue STRING\n");
+ eputz("Usage: .nullvalue STRING\n");
rc = 1;
}
}else
@@ -9539,11 +9505,11 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
#endif /* !SQLITE_SHELL_FIDDLE */
if( z[0]=='-' ){
- utf8_printf(stderr, "unknown option: %s\n", z);
+ eputf("unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
}else if( zFN ){
- utf8_printf(stderr, "extra argument: \"%s\"\n", z);
+ eputf("extra argument: \"%s\"\n", z);
rc = 1;
goto meta_command_exit;
}else{
@@ -9585,7 +9551,7 @@ static int do_meta_command(char *zLine, ShellState *p){
p->pAuxDb->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
- utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
+ eputf("Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
p->pAuxDb->zFreeOnClose = zNewFilename;
@@ -9609,9 +9575,9 @@ static int do_meta_command(char *zLine, ShellState *p){
int i;
int eMode = 0;
int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
- unsigned char zBOM[4]; /* Byte-order mark to using if --bom is present */
+ static const char *zBomUtf8 = "\xef\xbb\xbf";
+ const char *zBom = 0;
- zBOM[0] = 0;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( c=='e' ){
eMode = 'x';
@@ -9624,17 +9590,13 @@ static int do_meta_command(char *zLine, ShellState *p){
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( cli_strcmp(z,"-bom")==0 ){
- zBOM[0] = 0xef;
- zBOM[1] = 0xbb;
- zBOM[2] = 0xbf;
- zBOM[3] = 0;
+ zBom = zBomUtf8;
}else if( c!='e' && cli_strcmp(z,"-x")==0 ){
eMode = 'x'; /* spreadsheet */
}else if( c!='e' && cli_strcmp(z,"-e")==0 ){
eMode = 'e'; /* text editor */
}else{
- utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n",
- azArg[i]);
+ oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
goto meta_command_exit;
@@ -9646,8 +9608,7 @@ static int do_meta_command(char *zLine, ShellState *p){
break;
}
}else{
- utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n",
- azArg[i]);
+ oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]);
showHelp(p->out, azArg[0]);
rc = 1;
sqlite3_free(zFile);
@@ -9686,30 +9647,30 @@ static int do_meta_command(char *zLine, ShellState *p){
shell_check_oom(zFile);
if( zFile[0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
- raw_printf(stderr, "Error: pipes are not supported in this OS\n");
+ eputz("Error: pipes are not supported in this OS\n");
rc = 1;
- p->out = stdout;
+ output_redir(p, stdout);
#else
- p->out = popen(zFile + 1, "w");
- if( p->out==0 ){
- utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
- p->out = stdout;
+ FILE *pfPipe = popen(zFile + 1, "w");
+ if( pfPipe==0 ){
+ eputf("Error: cannot open pipe \"%s\"\n", zFile + 1);
rc = 1;
}else{
- if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out);
+ output_redir(p, pfPipe);
+ if( zBom ) oputz(zBom);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
#endif
}else{
- p->out = output_file_open(zFile, bTxtMode);
- if( p->out==0 ){
+ FILE *pfFile = output_file_open(zFile, bTxtMode);
+ if( pfFile==0 ){
if( cli_strcmp(zFile,"off")!=0 ){
- utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
+ eputf("Error: cannot write to \"%s\"\n", zFile);
}
- p->out = stdout;
rc = 1;
} else {
- if( zBOM[0] ) fwrite(zBOM, 1, 3, p->out);
+ output_redir(p, pfFile);
+ if( zBom ) oputz(zBom);
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
@@ -9750,8 +9711,8 @@ static int do_meta_command(char *zLine, ShellState *p){
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
- utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
- sqlite3_column_text(pStmt,1));
+ oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0),
+ sqlite3_column_text(pStmt,1));
}
sqlite3_finalize(pStmt);
}
@@ -9795,7 +9756,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
- utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
+ oputf("Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
@@ -9824,10 +9785,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
- if( i>1 ) raw_printf(p->out, " ");
- utf8_printf(p->out, "%s", azArg[i]);
+ if( i>1 ) oputz(" ");
+ oputz(azArg[i]);
}
- raw_printf(p->out, "\n");
+ oputz("\n");
}else
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
@@ -9856,7 +9817,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
if( cli_strcmp(z,"limit")==0 ){
if( i+1>=nArg ){
- utf8_printf(stderr, "Error: missing argument on --limit\n");
+ eputz("Error: missing argument on --limit\n");
rc = 1;
goto meta_command_exit;
}else{
@@ -9864,7 +9825,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
continue;
}
- utf8_printf(stderr, "Error: unknown option: \"%s\"\n", azArg[i]);
+ eputf("Error: unknown option: \"%s\"\n", azArg[i]);
rc = 1;
goto meta_command_exit;
}else{
@@ -9897,19 +9858,19 @@ static int do_meta_command(char *zLine, ShellState *p){
int savedLineno = p->lineno;
failIfSafeMode(p, "cannot run .read in safe mode");
if( nArg!=2 ){
- raw_printf(stderr, "Usage: .read FILE\n");
+ eputz("Usage: .read FILE\n");
rc = 1;
goto meta_command_exit;
}
if( azArg[1][0]=='|' ){
#ifdef SQLITE_OMIT_POPEN
- raw_printf(stderr, "Error: pipes are not supported in this OS\n");
+ eputz("Error: pipes are not supported in this OS\n");
rc = 1;
p->out = stdout;
#else
p->in = popen(azArg[1]+1, "r");
if( p->in==0 ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", azArg[1]);
+ eputf("Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
@@ -9917,7 +9878,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
#endif
}else if( (p->in = openChrSource(azArg[1]))==0 ){
- utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
+ eputf("Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
@@ -9944,20 +9905,20 @@ static int do_meta_command(char *zLine, ShellState *p){
zSrcFile = azArg[2];
zDb = azArg[1];
}else{
- raw_printf(stderr, "Usage: .restore ?DB? FILE\n");
+ eputz("Usage: .restore ?DB? FILE\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_open(zSrcFile, &pSrc);
if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "Error: cannot open \"%s\"\n", zSrcFile);
+ eputf("Error: cannot open \"%s\"\n", zSrcFile);
close_db(pSrc);
return 1;
}
open_db(p, 0);
pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main");
if( pBackup==0 ){
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
close_db(pSrc);
return 1;
}
@@ -9972,10 +9933,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( rc==SQLITE_DONE ){
rc = 0;
}else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ){
- raw_printf(stderr, "Error: source database is busy\n");
+ eputz("Error: source database is busy\n");
rc = 1;
}else{
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
rc = 1;
}
close_db(pSrc);
@@ -9996,11 +9957,15 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_db_config(
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
);
-#ifndef SQLITE_ENABLE_STMT_SCANSTATUS
- raw_printf(stderr, "Warning: .scanstats not available in this build.\n");
+#if !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ eputz("Warning: .scanstats not available in this build.\n");
+#elif !defined(SQLITE_ENABLE_BYTECODE_VTAB)
+ if( p->scanstatsOn==3 ){
+ eputz("Warning: \".scanstats vm\" not available in this build.\n");
+ }
#endif
}else{
- raw_printf(stderr, "Usage: .scanstats on|off|est\n");
+ eputz("Usage: .scanstats on|off|est\n");
rc = 1;
}
}else
@@ -10029,14 +9994,13 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( optionMatch(azArg[ii],"nosys") ){
bNoSystemTabs = 1;
}else if( azArg[ii][0]=='-' ){
- utf8_printf(stderr, "Unknown option: \"%s\"\n", azArg[ii]);
+ eputf("Unknown option: \"%s\"\n", azArg[ii]);
rc = 1;
goto meta_command_exit;
}else if( zName==0 ){
zName = azArg[ii];
}else{
- raw_printf(stderr,
- "Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n");
+ eputz("Usage: .schema ?--indent? ?--nosys? ?LIKE-PATTERN?\n");
rc = 1;
goto meta_command_exit;
}
@@ -10069,7 +10033,7 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list",
-1, &pStmt, 0);
if( rc ){
- utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(p->db));
+ eputf("Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
rc = 1;
goto meta_command_exit;
@@ -10131,18 +10095,18 @@ static int do_meta_command(char *zLine, ShellState *p){
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
- utf8_printf(p->out, "SQL: %s;\n", sSelect.z);
+ oputf("SQL: %s;\n", sSelect.z);
}else{
rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg);
}
freeText(&sSelect);
}
if( zErrMsg ){
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
+ eputf("Error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
}else if( rc != SQLITE_OK ){
- raw_printf(stderr,"Error: querying schema information\n");
+ eputz("Error: querying schema information\n");
rc = 1;
}else{
rc = 0;
@@ -10188,11 +10152,11 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=2 ) goto session_syntax_error;
if( pSession->p==0 ){
session_not_open:
- raw_printf(stderr, "ERROR: No sessions are open\n");
+ eputz("ERROR: No sessions are open\n");
}else{
rc = sqlite3session_attach(pSession->p, azCmd[1]);
if( rc ){
- raw_printf(stderr, "ERROR: sqlite3session_attach() returns %d\n", rc);
+ eputf("ERROR: sqlite3session_attach() returns %d\n",rc);
rc = 0;
}
}
@@ -10211,8 +10175,8 @@ static int do_meta_command(char *zLine, ShellState *p){
if( pSession->p==0 ) goto session_not_open;
out = fopen(azCmd[1], "wb");
if( out==0 ){
- utf8_printf(stderr, "ERROR: cannot open \"%s\" for writing\n",
- azCmd[1]);
+ eputf("ERROR: cannot open \"%s\" for writing\n",
+ azCmd[1]);
}else{
int szChng;
void *pChng;
@@ -10222,13 +10186,12 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3session_patchset(pSession->p, &szChng, &pChng);
}
if( rc ){
- printf("Error: error code %d\n", rc);
+ sputf(stdout, "Error: error code %d\n", rc);
rc = 0;
}
if( pChng
&& fwrite(pChng, szChng, 1, out)!=1 ){
- raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n",
- szChng);
+ eputf("ERROR: Failed to write entire %d-byte output\n", szChng);
}
sqlite3_free(pChng);
fclose(out);
@@ -10255,8 +10218,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_enable(pSession->p, ii);
- utf8_printf(p->out, "session %s enable flag = %d\n",
- pSession->zName, ii);
+ oputf("session %s enable flag = %d\n", pSession->zName, ii);
}
}else
@@ -10273,10 +10235,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_free(pSession->azFilter);
nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
pSession->azFilter = sqlite3_malloc( nByte );
- if( pSession->azFilter==0 ){
- raw_printf(stderr, "Error: out or memory\n");
- exit(1);
- }
+ shell_check_oom( pSession->azFilter );
for(ii=1; ii<nCmd; ii++){
char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
shell_check_oom(x);
@@ -10294,8 +10253,7 @@ static int do_meta_command(char *zLine, ShellState *p){
ii = nCmd==1 ? -1 : booleanValue(azCmd[1]);
if( pAuxDb->nSession ){
ii = sqlite3session_indirect(pSession->p, ii);
- utf8_printf(p->out, "session %s indirect flag = %d\n",
- pSession->zName, ii);
+ oputf("session %s indirect flag = %d\n", pSession->zName, ii);
}
}else
@@ -10307,8 +10265,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nCmd!=1 ) goto session_syntax_error;
if( pAuxDb->nSession ){
ii = sqlite3session_isempty(pSession->p);
- utf8_printf(p->out, "session %s isempty flag = %d\n",
- pSession->zName, ii);
+ oputf("session %s isempty flag = %d\n", pSession->zName, ii);
}
}else
@@ -10317,7 +10274,7 @@ static int do_meta_command(char *zLine, ShellState *p){
*/
if( cli_strcmp(azCmd[0],"list")==0 ){
for(i=0; i<pAuxDb->nSession; i++){
- utf8_printf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName);
+ oputf("%d %s\n", i, pAuxDb->aSession[i].zName);
}
}else
@@ -10332,19 +10289,18 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zName[0]==0 ) goto session_syntax_error;
for(i=0; i<pAuxDb->nSession; i++){
if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
- utf8_printf(stderr, "Session \"%s\" already exists\n", zName);
+ eputf("Session \"%s\" already exists\n", zName);
goto meta_command_exit;
}
}
if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
- raw_printf(stderr,
- "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
+ eputf("Maximum of %d sessions\n", ArraySize(pAuxDb->aSession));
goto meta_command_exit;
}
pSession = &pAuxDb->aSession[pAuxDb->nSession];
rc = sqlite3session_create(p->db, azCmd[1], &pSession->p);
if( rc ){
- raw_printf(stderr, "Cannot open session: error code=%d\n", rc);
+ eputf("Cannot open session: error code=%d\n", rc);
rc = 0;
goto meta_command_exit;
}
@@ -10368,7 +10324,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int i, v;
for(i=1; i<nArg; i++){
v = booleanValue(azArg[i]);
- utf8_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v);
+ oputf("%s: %d 0x%x\n", azArg[i], v, v);
}
}
if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){
@@ -10377,7 +10333,7 @@ static int do_meta_command(char *zLine, ShellState *p){
char zBuf[200];
v = integerValue(azArg[i]);
sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
- utf8_printf(p->out, "%s", zBuf);
+ oputz(zBuf);
}
}
}else
@@ -10404,9 +10360,8 @@ static int do_meta_command(char *zLine, ShellState *p){
bVerbose++;
}else
{
- utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n",
- azArg[i], azArg[0]);
- raw_printf(stderr, "Should be one of: --init -v\n");
+ eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
+ eputz("Should be one of: --init -v\n");
rc = 1;
goto meta_command_exit;
}
@@ -10435,7 +10390,7 @@ static int do_meta_command(char *zLine, ShellState *p){
-1, &pStmt, 0);
}
if( rc ){
- raw_printf(stderr, "Error querying the selftest table\n");
+ eputz("Error querying the selftest table\n");
rc = 1;
sqlite3_finalize(pStmt);
goto meta_command_exit;
@@ -10451,10 +10406,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( zAns==0 ) continue;
k = 0;
if( bVerbose>0 ){
- printf("%d: %s %s\n", tno, zOp, zSql);
+ sputf(stdout, "%d: %s %s\n", tno, zOp, zSql);
}
if( cli_strcmp(zOp,"memo")==0 ){
- utf8_printf(p->out, "%s\n", zSql);
+ oputf("%s\n", zSql);
}else
if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
@@ -10463,23 +10418,22 @@ static int do_meta_command(char *zLine, ShellState *p){
rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
nTest++;
if( bVerbose ){
- utf8_printf(p->out, "Result: %s\n", str.z);
+ oputf("Result: %s\n", str.z);
}
if( rc || zErrMsg ){
nErr++;
rc = 1;
- utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg);
+ oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg);
sqlite3_free(zErrMsg);
}else if( cli_strcmp(zAns,str.z)!=0 ){
nErr++;
rc = 1;
- utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns);
- utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z);
+ oputf("%d: Expected: [%s]\n", tno, zAns);
+ oputf("%d: Got: [%s]\n", tno, str.z);
}
- }else
- {
- utf8_printf(stderr,
- "Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
+ }
+ else{
+ eputf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
rc = 1;
break;
}
@@ -10487,12 +10441,12 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
} /* End loop over k */
freeText(&str);
- utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest);
+ oputf("%d errors out of %d tests\n", nErr, nTest);
}else
if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){
if( nArg<2 || nArg>3 ){
- raw_printf(stderr, "Usage: .separator COL ?ROW?\n");
+ eputz("Usage: .separator COL ?ROW?\n");
rc = 1;
}
if( nArg>=2 ){
@@ -10535,14 +10489,13 @@ static int do_meta_command(char *zLine, ShellState *p){
bDebug = 1;
}else
{
- utf8_printf(stderr, "Unknown option \"%s\" on \"%s\"\n",
- azArg[i], azArg[0]);
+ eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
showHelp(p->out, azArg[0]);
rc = 1;
goto meta_command_exit;
}
}else if( zLike ){
- raw_printf(stderr, "Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n");
+ eputz("Usage: .sha3sum ?OPTIONS? ?LIKE-PATTERN?\n");
rc = 1;
goto meta_command_exit;
}else{
@@ -10614,7 +10567,7 @@ static int do_meta_command(char *zLine, ShellState *p){
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
- utf8_printf(p->out, "%s\n", zSql);
+ oputf("%s\n", zSql);
}else{
shell_exec(p, zSql, 0);
}
@@ -10644,7 +10597,7 @@ static int do_meta_command(char *zLine, ShellState *p){
"' OR ') as query, tname from tabcols group by tname)"
, zRevText);
shell_check_oom(zRevText);
- if( bDebug ) utf8_printf(p->out, "%s\n", zRevText);
+ if( bDebug ) oputf("%s\n", zRevText);
lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0);
if( lrc!=SQLITE_OK ){
/* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the
@@ -10657,7 +10610,7 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
sqlite3_stmt *pCheckStmt;
lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0);
- if( bDebug ) utf8_printf(p->out, "%s\n", zGenQuery);
+ if( bDebug ) oputf("%s\n", zGenQuery);
if( lrc!=SQLITE_OK ){
rc = 1;
}else{
@@ -10665,9 +10618,8 @@ static int do_meta_command(char *zLine, ShellState *p){
double countIrreversible = sqlite3_column_double(pCheckStmt, 0);
if( countIrreversible>0 ){
int sz = (int)(countIrreversible + 0.5);
- utf8_printf(stderr,
- "Digest includes %d invalidly encoded text field%s.\n",
- sz, (sz>1)? "s": "");
+ eputf("Digest includes %d invalidly encoded text field%s.\n",
+ sz, (sz>1)? "s": "");
}
}
sqlite3_finalize(pCheckStmt);
@@ -10675,7 +10627,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
}
}
- if( rc ) utf8_printf(stderr, ".sha3sum failed.\n");
+ if( rc ) eputz(".sha3sum failed.\n");
sqlite3_free(zRevText);
}
#endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
@@ -10691,7 +10643,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int i, x;
failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
if( nArg<2 ){
- raw_printf(stderr, "Usage: .system COMMAND\n");
+ eputz("Usage: .system COMMAND\n");
rc = 1;
goto meta_command_exit;
}
@@ -10700,9 +10652,11 @@ static int do_meta_command(char *zLine, ShellState *p){
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
zCmd, azArg[i]);
}
+ consoleRestore();
x = zCmd!=0 ? system(zCmd) : 1;
+ consoleRenewSetup();
sqlite3_free(zCmd);
- if( x ) raw_printf(stderr, "System command returns %d\n", x);
+ if( x ) eputf("System command returns %d\n", x);
}else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */
@@ -10711,52 +10665,51 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zOut;
int i;
if( nArg!=1 ){
- raw_printf(stderr, "Usage: .show\n");
+ eputz("Usage: .show\n");
rc = 1;
goto meta_command_exit;
}
- utf8_printf(p->out, "%12.12s: %s\n","echo",
- azBool[ShellHasFlag(p, SHFLG_Echo)]);
- utf8_printf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]);
- utf8_printf(p->out, "%12.12s: %s\n","explain",
- p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
- utf8_printf(p->out,"%12.12s: %s\n","headers", azBool[p->showHeader!=0]);
+ oputf("%12.12s: %s\n","echo",
+ azBool[ShellHasFlag(p, SHFLG_Echo)]);
+ oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]);
+ oputf("%12.12s: %s\n","explain",
+ p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off");
+ oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]);
if( p->mode==MODE_Column
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
){
- utf8_printf
- (p->out, "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
- modeDescr[p->mode], p->cmOpts.iWrap,
- p->cmOpts.bWordWrap ? "on" : "off",
- p->cmOpts.bQuote ? "" : "no");
+ oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode",
+ modeDescr[p->mode], p->cmOpts.iWrap,
+ p->cmOpts.bWordWrap ? "on" : "off",
+ p->cmOpts.bQuote ? "" : "no");
}else{
- utf8_printf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]);
- }
- utf8_printf(p->out, "%12.12s: ", "nullvalue");
- output_c_string(p->out, p->nullValue);
- raw_printf(p->out, "\n");
- utf8_printf(p->out,"%12.12s: %s\n","output",
- strlen30(p->outfile) ? p->outfile : "stdout");
- utf8_printf(p->out,"%12.12s: ", "colseparator");
- output_c_string(p->out, p->colSeparator);
- raw_printf(p->out, "\n");
- utf8_printf(p->out,"%12.12s: ", "rowseparator");
- output_c_string(p->out, p->rowSeparator);
- raw_printf(p->out, "\n");
+ oputf("%12.12s: %s\n","mode", modeDescr[p->mode]);
+ }
+ oputf("%12.12s: ", "nullvalue");
+ output_c_string(p->nullValue);
+ oputz("\n");
+ oputf("%12.12s: %s\n","output",
+ strlen30(p->outfile) ? p->outfile : "stdout");
+ oputf("%12.12s: ", "colseparator");
+ output_c_string(p->colSeparator);
+ oputz("\n");
+ oputf("%12.12s: ", "rowseparator");
+ output_c_string(p->rowSeparator);
+ oputz("\n");
switch( p->statsOn ){
case 0: zOut = "off"; break;
default: zOut = "on"; break;
case 2: zOut = "stmt"; break;
case 3: zOut = "vmstep"; break;
}
- utf8_printf(p->out, "%12.12s: %s\n","stats", zOut);
- utf8_printf(p->out, "%12.12s: ", "width");
+ oputf("%12.12s: %s\n","stats", zOut);
+ oputf("%12.12s: ", "width");
for (i=0;i<p->nWidth;i++) {
- raw_printf(p->out, "%d ", p->colWidth[i]);
+ oputf("%d ", p->colWidth[i]);
}
- raw_printf(p->out, "\n");
- utf8_printf(p->out, "%12.12s: %s\n", "filename",
- p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
+ oputz("\n");
+ oputf("%12.12s: %s\n", "filename",
+ p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : "");
}else
if( c=='s' && cli_strncmp(azArg[0], "stats", n)==0 ){
@@ -10771,7 +10724,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}else if( nArg==1 ){
display_stats(p->db, p, 0);
}else{
- raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n");
+ eputz("Usage: .stats ?on|off|stmt|vmstep?\n");
rc = 1;
}
}else
@@ -10797,7 +10750,7 @@ static int do_meta_command(char *zLine, ShellState *p){
/* It is an historical accident that the .indexes command shows an error
** when called with the wrong number of arguments whereas the .tables
** command does not. */
- raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n");
+ eputz("Usage: .indexes ?LIKE-PATTERN?\n");
rc = 1;
sqlite3_finalize(pStmt);
goto meta_command_exit;
@@ -10873,10 +10826,9 @@ static int do_meta_command(char *zLine, ShellState *p){
for(i=0; i<nPrintRow; i++){
for(j=i; j<nRow; j+=nPrintRow){
char *zSp = j<nPrintRow ? "" : " ";
- utf8_printf(p->out, "%s%-*s", zSp, maxlen,
- azResult[j] ? azResult[j]:"");
+ oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:"");
}
- raw_printf(p->out, "\n");
+ oputz("\n");
}
}
@@ -10890,7 +10842,7 @@ static int do_meta_command(char *zLine, ShellState *p){
output_reset(p);
p->out = output_file_open("testcase-out.txt", 0);
if( p->out==0 ){
- raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n");
+ eputz("Error: cannot open 'testcase-out.txt'\n");
}
if( nArg>=2 ){
sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]);
@@ -10914,9 +10866,11 @@ static int do_meta_command(char *zLine, ShellState *p){
/*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/
{"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" },
{"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" },
- /*{"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/
+ {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." },
+ {"fk_no_action", SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN" },
{"imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
{"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" },
+ {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" },
{"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" },
{"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" },
{"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" },
@@ -10930,7 +10884,7 @@ static int do_meta_command(char *zLine, ShellState *p){
{"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\"?"},
+ {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"},
};
int testctrl = -1;
int iCtrl = -1;
@@ -10950,11 +10904,11 @@ static int do_meta_command(char *zLine, ShellState *p){
/* --help lists all test-controls */
if( cli_strcmp(zCmd,"help")==0 ){
- utf8_printf(p->out, "Available test-controls:\n");
+ oputz("Available test-controls:\n");
for(i=0; i<ArraySize(aCtrl); i++){
if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue;
- utf8_printf(p->out, " .testctrl %s %s\n",
- aCtrl[i].zCtrlName, aCtrl[i].zUsage);
+ oputf(" .testctrl %s %s\n",
+ aCtrl[i].zCtrlName, aCtrl[i].zUsage);
}
rc = 1;
goto meta_command_exit;
@@ -10970,21 +10924,22 @@ static int do_meta_command(char *zLine, ShellState *p){
testctrl = aCtrl[i].ctrlCode;
iCtrl = i;
}else{
- utf8_printf(stderr, "Error: ambiguous test-control: \"%s\"\n"
- "Use \".testctrl --help\" for help\n", zCmd);
+ eputf("Error: ambiguous test-control: \"%s\"\n"
+ "Use \".testctrl --help\" for help\n", zCmd);
rc = 1;
goto meta_command_exit;
}
}
}
if( testctrl<0 ){
- utf8_printf(stderr,"Error: unknown test-control: %s\n"
- "Use \".testctrl --help\" for help\n", zCmd);
+ eputf("Error: unknown test-control: %s\n"
+ "Use \".testctrl --help\" for help\n", zCmd);
}else{
switch(testctrl){
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_OPTIMIZATIONS:
+ case SQLITE_TESTCTRL_FK_NO_ACTION:
if( nArg==3 ){
unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0);
rc2 = sqlite3_test_control(testctrl, p->db, opt);
@@ -11018,7 +10973,7 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3 *db;
if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){
sqlite3_randomness(sizeof(ii),&ii);
- printf("-- random seed: %d\n", ii);
+ sputf(stdout, "-- random seed: %d\n", ii);
}
if( nArg==3 ){
db = 0;
@@ -11086,7 +11041,7 @@ static int do_meta_command(char *zLine, ShellState *p){
case SQLITE_TESTCTRL_SEEK_COUNT: {
u64 x = 0;
rc2 = sqlite3_test_control(testctrl, p->db, &x);
- utf8_printf(p->out, "%llu\n", x);
+ oputf("%llu\n", x);
isOk = 3;
break;
}
@@ -11117,11 +11072,11 @@ static int do_meta_command(char *zLine, ShellState *p){
int val = 0;
rc2 = sqlite3_test_control(testctrl, -id, &val);
if( rc2!=SQLITE_OK ) break;
- if( id>1 ) utf8_printf(p->out, " ");
- utf8_printf(p->out, "%d: %d", id, val);
+ if( id>1 ) oputz(" ");
+ oputf("%d: %d", id, val);
id++;
}
- if( id>1 ) utf8_printf(p->out, "\n");
+ if( id>1 ) oputz("\n");
isOk = 3;
}
break;
@@ -11134,15 +11089,83 @@ static int do_meta_command(char *zLine, ShellState *p){
isOk = 3;
}
break;
+ case SQLITE_TESTCTRL_JSON_SELFCHECK:
+ if( nArg==2 ){
+ rc2 = -1;
+ isOk = 1;
+ }else{
+ rc2 = booleanValue(azArg[2]);
+ isOk = 3;
+ }
+ sqlite3_test_control(testctrl, &rc2);
+ break;
+ case SQLITE_TESTCTRL_FAULT_INSTALL: {
+ int kk;
+ int bShowHelp = nArg<=2;
+ isOk = 3;
+ for(kk=2; kk<nArg; kk++){
+ const char *z = azArg[kk];
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ if( cli_strcmp(z,"off")==0 ){
+ sqlite3_test_control(testctrl, 0);
+ }else if( cli_strcmp(z,"on")==0 ){
+ faultsim_state.iCnt = faultsim_state.iInterval;
+ if( faultsim_state.iErr==0 ) faultsim_state.iErr = 1;
+ sqlite3_test_control(testctrl, faultsim_callback);
+ }else if( cli_strcmp(z,"reset")==0 ){
+ faultsim_state.iCnt = faultsim_state.iInterval;
+ }else if( cli_strcmp(z,"status")==0 ){
+ oputf("faultsim.iId: %d\n", faultsim_state.iId);
+ oputf("faultsim.iErr: %d\n", faultsim_state.iErr);
+ oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt);
+ oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval);
+ oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose);
+ }else if( cli_strcmp(z,"-v")==0 ){
+ if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++;
+ }else if( cli_strcmp(z,"-q")==0 ){
+ if( faultsim_state.eVerbose>0 ) faultsim_state.eVerbose--;
+ }else if( cli_strcmp(z,"-id")==0 && kk+1<nArg ){
+ faultsim_state.iId = atoi(azArg[++kk]);
+ }else if( cli_strcmp(z,"-errcode")==0 && kk+1<nArg ){
+ faultsim_state.iErr = atoi(azArg[++kk]);
+ }else if( cli_strcmp(z,"-interval")==0 && kk+1<nArg ){
+ faultsim_state.iInterval = atoi(azArg[++kk]);
+ }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){
+ bShowHelp = 1;
+ }else{
+ eputf("Unrecognized fault_install argument: \"%s\"\n",
+ azArg[kk]);
+ rc = 1;
+ bShowHelp = 1;
+ break;
+ }
+ }
+ if( bShowHelp ){
+ oputz(
+ "Usage: .testctrl fault_install ARGS\n"
+ "Possible arguments:\n"
+ " off Disable faultsim\n"
+ " on Activate faultsim\n"
+ " reset Reset the trigger counter\n"
+ " status Show current status\n"
+ " -v Increase verbosity\n"
+ " -q Decrease verbosity\n"
+ " --errcode N When triggered, return N as error code\n"
+ " --id ID Trigger only for the ID specified\n"
+ " --interval N Trigger only after every N-th call\n"
+ );
+ }
+ break;
+ }
}
}
if( isOk==0 && iCtrl>=0 ){
- utf8_printf(p->out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
+ oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
rc = 1;
}else if( isOk==1 ){
- raw_printf(p->out, "%d\n", rc2);
+ oputf("%d\n", rc2);
}else if( isOk==2 ){
- raw_printf(p->out, "0x%08x\n", rc2);
+ oputf("0x%08x\n", rc2);
}
}else
#endif /* !defined(SQLITE_UNTESTABLE) */
@@ -11156,11 +11179,11 @@ static int do_meta_command(char *zLine, ShellState *p){
if( nArg==2 ){
enableTimer = booleanValue(azArg[1]);
if( enableTimer && !HAS_TIMER ){
- raw_printf(stderr, "Error: timer not available on this system.\n");
+ eputz("Error: timer not available on this system.\n");
enableTimer = 0;
}
}else{
- raw_printf(stderr, "Usage: .timer on|off\n");
+ eputz("Usage: .timer on|off\n");
rc = 1;
}
}else
@@ -11197,7 +11220,7 @@ static int do_meta_command(char *zLine, ShellState *p){
mType |= SQLITE_TRACE_CLOSE;
}
else {
- raw_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z);
+ eputf("Unknown option \"%s\" on \".trace\"\n", z);
rc = 1;
goto meta_command_exit;
}
@@ -11221,7 +11244,7 @@ static int do_meta_command(char *zLine, ShellState *p){
int lenOpt;
char *zOpt;
if( nArg<2 ){
- raw_printf(stderr, "Usage: .unmodule [--allexcept] NAME ...\n");
+ eputz("Usage: .unmodule [--allexcept] NAME ...\n");
rc = 1;
goto meta_command_exit;
}
@@ -11243,60 +11266,60 @@ static int do_meta_command(char *zLine, ShellState *p){
#if SQLITE_USER_AUTHENTICATION
if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){
if( nArg<2 ){
- raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n");
+ eputz("Usage: .user SUBCOMMAND ...\n");
rc = 1;
goto meta_command_exit;
}
open_db(p, 0);
if( cli_strcmp(azArg[1],"login")==0 ){
if( nArg!=4 ){
- raw_printf(stderr, "Usage: .user login USER PASSWORD\n");
+ eputz("Usage: .user login USER PASSWORD\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3],
strlen30(azArg[3]));
if( rc ){
- utf8_printf(stderr, "Authentication failed for user %s\n", azArg[2]);
+ eputf("Authentication failed for user %s\n", azArg[2]);
rc = 1;
}
}else if( cli_strcmp(azArg[1],"add")==0 ){
if( nArg!=5 ){
- raw_printf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n");
+ eputz("Usage: .user add USER PASSWORD ISADMIN\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
booleanValue(azArg[4]));
if( rc ){
- raw_printf(stderr, "User-Add failed: %d\n", rc);
+ eputf("User-Add failed: %d\n", rc);
rc = 1;
}
}else if( cli_strcmp(azArg[1],"edit")==0 ){
if( nArg!=5 ){
- raw_printf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n");
+ eputz("Usage: .user edit USER PASSWORD ISADMIN\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
booleanValue(azArg[4]));
if( rc ){
- raw_printf(stderr, "User-Edit failed: %d\n", rc);
+ eputf("User-Edit failed: %d\n", rc);
rc = 1;
}
}else if( cli_strcmp(azArg[1],"delete")==0 ){
if( nArg!=3 ){
- raw_printf(stderr, "Usage: .user delete USER\n");
+ eputz("Usage: .user delete USER\n");
rc = 1;
goto meta_command_exit;
}
rc = sqlite3_user_delete(p->db, azArg[2]);
if( rc ){
- raw_printf(stderr, "User-Delete failed: %d\n", rc);
+ eputf("User-Delete failed: %d\n", rc);
rc = 1;
}
}else{
- raw_printf(stderr, "Usage: .user login|add|edit|delete ...\n");
+ eputz("Usage: .user login|add|edit|delete ...\n");
rc = 1;
goto meta_command_exit;
}
@@ -11305,21 +11328,21 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){
char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit";
- utf8_printf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
- sqlite3_libversion(), sqlite3_sourceid());
+ oputf("SQLite %s %s\n" /*extra-version-info*/,
+ sqlite3_libversion(), sqlite3_sourceid());
#if SQLITE_HAVE_ZLIB
- utf8_printf(p->out, "zlib version %s\n", zlibVersion());
+ oputf("zlib version %s\n", zlibVersion());
#endif
#define CTIMEOPT_VAL_(opt) #opt
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
#if defined(__clang__) && defined(__clang_major__)
- utf8_printf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "."
- CTIMEOPT_VAL(__clang_minor__) "."
- CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz);
+ oputf("clang-" CTIMEOPT_VAL(__clang_major__) "."
+ CTIMEOPT_VAL(__clang_minor__) "."
+ CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz);
#elif defined(_MSC_VER)
- utf8_printf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz);
+ oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz);
#elif defined(__GNUC__) && defined(__VERSION__)
- utf8_printf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz);
+ oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz);
#endif
}else
@@ -11329,10 +11352,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs);
if( pVfs ){
- utf8_printf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName);
- raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
- raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
- raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
+ oputf("vfs.zName = \"%s\"\n", pVfs->zName);
+ oputf("vfs.iVersion = %d\n", pVfs->iVersion);
+ oputf("vfs.szOsFile = %d\n", pVfs->szOsFile);
+ oputf("vfs.mxPathname = %d\n", pVfs->mxPathname);
}
}
}else
@@ -11344,13 +11367,13 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent);
}
for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){
- utf8_printf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName,
- pVfs==pCurrent ? " <--- CURRENT" : "");
- raw_printf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion);
- raw_printf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile);
- raw_printf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
+ oputf("vfs.zName = \"%s\"%s\n", pVfs->zName,
+ pVfs==pCurrent ? " <--- CURRENT" : "");
+ oputf("vfs.iVersion = %d\n", pVfs->iVersion);
+ oputf("vfs.szOsFile = %d\n", pVfs->szOsFile);
+ oputf("vfs.mxPathname = %d\n", pVfs->mxPathname);
if( pVfs->pNext ){
- raw_printf(p->out, "-----------------------------------\n");
+ oputz("-----------------------------------\n");
}
}
}else
@@ -11361,7 +11384,7 @@ static int do_meta_command(char *zLine, ShellState *p){
if( p->db ){
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
if( zVfsName ){
- utf8_printf(p->out, "%s\n", zVfsName);
+ oputf("%s\n", zVfsName);
sqlite3_free(zVfsName);
}
}
@@ -11385,8 +11408,8 @@ static int do_meta_command(char *zLine, ShellState *p){
}else
{
- utf8_printf(stderr, "Error: unknown command or invalid arguments: "
- " \"%s\". Enter \".help\" for help\n", azArg[0]);
+ eputf("Error: unknown command or invalid arguments: "
+ " \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
}
@@ -11541,6 +11564,88 @@ static int line_is_complete(char *zSql, int nSql){
}
/*
+** This function is called after processing each line of SQL in the
+** runOneSqlLine() function. Its purpose is to detect scenarios where
+** defensive mode should be automatically turned off. Specifically, when
+**
+** 1. The first line of input is "PRAGMA foreign_keys=OFF;",
+** 2. The second line of input is "BEGIN TRANSACTION;",
+** 3. The database is empty, and
+** 4. The shell is not running in --safe mode.
+**
+** The implementation uses the ShellState.eRestoreState to maintain state:
+**
+** 0: Have not seen any SQL.
+** 1: Have seen "PRAGMA foreign_keys=OFF;".
+** 2-6: Currently running .dump transaction. If the "2" bit is set,
+** disable DEFENSIVE when done. If "4" is set, disable DQS_DDL.
+** 7: Nothing left to do. This function becomes a no-op.
+*/
+static int doAutoDetectRestore(ShellState *p, const char *zSql){
+ int rc = SQLITE_OK;
+
+ if( p->eRestoreState<7 ){
+ switch( p->eRestoreState ){
+ case 0: {
+ const char *zExpect = "PRAGMA foreign_keys=OFF;";
+ assert( strlen(zExpect)==24 );
+ if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){
+ p->eRestoreState = 1;
+ }else{
+ p->eRestoreState = 7;
+ }
+ break;
+ };
+
+ case 1: {
+ int bIsDump = 0;
+ const char *zExpect = "BEGIN TRANSACTION;";
+ assert( strlen(zExpect)==18 );
+ if( memcmp(zSql, zExpect, 19)==0 ){
+ /* Now check if the database is empty. */
+ const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1";
+ sqlite3_stmt *pStmt = 0;
+
+ bIsDump = 1;
+ shellPrepare(p->db, &rc, zQuery, &pStmt);
+ if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
+ bIsDump = 0;
+ }
+ shellFinalize(&rc, pStmt);
+ }
+ if( bIsDump && rc==SQLITE_OK ){
+ int bDefense = 0;
+ int bDqsDdl = 0;
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense);
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl);
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0);
+ p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0);
+ }else{
+ p->eRestoreState = 7;
+ }
+ break;
+ }
+
+ default: {
+ if( sqlite3_get_autocommit(p->db) ){
+ if( (p->eRestoreState & 2) ){
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
+ }
+ if( (p->eRestoreState & 4) ){
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0);
+ }
+ p->eRestoreState = 7;
+ }
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
** Run a single line of SQL. Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
@@ -11576,7 +11681,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
}else{
sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType);
}
- utf8_printf(stderr, "%s %s\n", zPrefix, zErrorTail);
+ eputf("%s %s\n", zPrefix, zErrorTail);
sqlite3_free(zErrMsg);
zErrMsg = 0;
return 1;
@@ -11585,13 +11690,15 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
"changes: %lld total_changes: %lld",
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
- raw_printf(p->out, "%s\n", zLineBuf);
+ oputf("%s\n", zLineBuf);
}
+
+ if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
- if( ShellHasFlag(p, SHFLG_Echo) ) utf8_printf(p->out, "%s\n", zDo);
+ if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo);
}
#ifdef SQLITE_SHELL_FIDDLE
@@ -11649,8 +11756,8 @@ static int process_input(ShellState *p){
if( p->inputNesting==MAX_INPUT_NESTING ){
/* This will be more informative in a later version. */
- utf8_printf(stderr,"Input nesting limit (%d) reached at line %d."
- " Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
+ eputf("Input nesting limit (%d) reached at line %d."
+ " Check recursion.\n", MAX_INPUT_NESTING, p->lineno);
return 1;
}
++p->inputNesting;
@@ -11661,7 +11768,7 @@ static int process_input(ShellState *p){
zLine = one_input_line(p->in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
- if( p->in==0 && stdin_is_interactive ) printf("\n");
+ if( p->in==0 && stdin_is_interactive ) oputz("\n");
break;
}
if( seenInterrupt ){
@@ -11871,8 +11978,8 @@ static void process_sqliterc(
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
if( home_dir==0 ){
- raw_printf(stderr, "-- warning: cannot find home directory;"
- " cannot read ~/.sqliterc\n");
+ eputz("-- warning: cannot find home directory;"
+ " cannot read ~/.sqliterc\n");
return;
}
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
@@ -11882,12 +11989,12 @@ static void process_sqliterc(
p->in = fopen(sqliterc,"rb");
if( p->in ){
if( stdin_is_interactive ){
- utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
+ eputf("-- Loading resources from %s\n", sqliterc);
}
if( process_input(p) && bail_on_error ) exit(1);
fclose(p->in);
}else if( sqliterc_override!=0 ){
- utf8_printf(stderr,"cannot open: \"%s\"\n", sqliterc);
+ eputf("cannot open: \"%s\"\n", sqliterc);
if( bail_on_error ) exit(1);
}
p->in = inSaved;
@@ -11953,9 +12060,6 @@ static const char zOptions[] =
" -table set output mode to 'table'\n"
" -tabs set output mode to 'tabs'\n"
" -unsafe-testing allow unsafe commands and modes for testing\n"
-#if SHELL_WIN_UTF8_OPT
- " -utf8 setup interactive console code page for UTF-8\n"
-#endif
" -version show SQLite version\n"
" -vfs NAME use NAME as the default VFS\n"
#ifdef SQLITE_ENABLE_VFSTRACE
@@ -11966,16 +12070,15 @@ static const char zOptions[] =
#endif
;
static void usage(int showDetail){
- utf8_printf(stderr,
- "Usage: %s [OPTIONS] [FILENAME [SQL]]\n"
- "FILENAME is the name of an SQLite database. A new database is created\n"
- "if the file does not previously exist. Defaults to :memory:.\n", Argv0);
+ eputf("Usage: %s [OPTIONS] [FILENAME [SQL]]\n"
+ "FILENAME is the name of an SQLite database. A new database is created\n"
+ "if the file does not previously exist. Defaults to :memory:.\n", Argv0);
if( showDetail ){
- utf8_printf(stderr, "OPTIONS include:\n%s", zOptions);
+ eputf("OPTIONS include:\n%s", zOptions);
}else{
- raw_printf(stderr, "Use the -help option for additional information\n");
+ eputz("Use the -help option for additional information\n");
}
- exit(1);
+ exit(0);
}
/*
@@ -11984,8 +12087,8 @@ static void usage(int showDetail){
*/
static void verify_uninitialized(void){
if( sqlite3_config(-1)==SQLITE_MISUSE ){
- utf8_printf(stdout, "WARNING: attempt to configure SQLite after"
- " initialization.\n");
+ sputz(stdout, "WARNING: attempt to configure SQLite after"
+ " initialization.\n");
}
}
@@ -12014,7 +12117,7 @@ static void main_init(ShellState *data) {
/*
** Output text to the console in a font that attracts extra attention.
*/
-#ifdef _WIN32
+#if defined(_WIN32) || defined(WIN32)
static void printBold(const char *zText){
#if !SQLITE_OS_WINRT
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -12024,14 +12127,14 @@ static void printBold(const char *zText){
FOREGROUND_RED|FOREGROUND_INTENSITY
);
#endif
- printf("%s", zText);
+ sputz(stdout, zText);
#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
- printf("\033[1m%s\033[0m", zText);
+ sputf(stdout, "\033[1m%s\033[0m", zText);
}
#endif
@@ -12041,15 +12144,14 @@ static void printBold(const char *zText){
*/
static char *cmdline_option_value(int argc, char **argv, int i){
if( i==argc ){
- utf8_printf(stderr, "%s: Error: missing argument to %s\n",
- argv[0], argv[argc-1]);
+ eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]);
exit(1);
}
return argv[i];
}
static void sayAbnormalExit(void){
- if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
+ if( seenInterrupt ) eputz("Program interrupted.\n");
}
#ifndef SQLITE_SHELL_IS_UTF8
@@ -12079,6 +12181,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
# define data shellState
#else
ShellState data;
+ StreamsAreConsole consStreams = SAC_NoConsole;
#endif
const char *zInitFile = 0;
int i;
@@ -12100,11 +12203,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
stdout_is_console = 1;
data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
- stdin_is_interactive = isatty(0);
- stdout_is_console = isatty(1);
-#endif
-#if SHELL_WIN_UTF8_OPT
- atexit(console_restore); /* Needs revision for CLI as library call */
+ consStreams = consoleClassifySetup(stdin, stdout, stderr);
+ stdin_is_interactive = (consStreams & SAC_InConsole)!=0;
+ stdout_is_console = (consStreams & SAC_OutConsole)!=0;
+ atexit(consoleRestore);
#endif
atexit(sayAbnormalExit);
#ifdef SQLITE_DEBUG
@@ -12113,9 +12215,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
#if !defined(_WIN32_WCE)
if( getenv("SQLITE_DEBUG_BREAK") ){
if( isatty(0) && isatty(2) ){
- fprintf(stderr,
- "attach debugger to process %d and press any key to continue.\n",
- GETPID());
+ eputf("attach debugger to process %d and press any key to continue.\n",
+ GETPID());
fgetc(stdin);
}else{
#if defined(_WIN32) || defined(WIN32)
@@ -12135,14 +12236,14 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
signal(SIGINT, interrupt_handler);
#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){
- fprintf(stderr, "No ^C handler.\n");
+ eputz("No ^C handler.\n");
}
#endif
#if USE_SYSTEM_SQLITE+0!=1
if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){
- utf8_printf(stderr, "SQLite header and source version mismatch\n%s\n%s\n",
- sqlite3_sourceid(), SQLITE_SOURCE_ID);
+ eputf("SQLite header and source version mismatch\n%s\n%s\n",
+ sqlite3_sourceid(), SQLITE_SOURCE_ID);
exit(1);
}
#endif
@@ -12191,8 +12292,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
/* Do an initial pass through the command-line argument to locate
** the name of the database file, the name of the initialization file,
- ** the size of the alternative malloc heap,
- ** and the first command to execute.
+ ** the size of the alternative malloc heap, options affecting commands
+ ** or SQL run from the command line, and the first command to execute.
*/
#ifndef SQLITE_SHELL_FIDDLE
verify_uninitialized();
@@ -12226,12 +12327,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
(void)cmdline_option_value(argc, argv, ++i);
}else if( cli_strcmp(z,"-init")==0 ){
zInitFile = cmdline_option_value(argc, argv, ++i);
+ }else if( cli_strcmp(z,"-interactive")==0 ){
}else if( cli_strcmp(z,"-batch")==0 ){
/* Need to check for batch mode here to so we can avoid printing
** informational messages (like from process_sqliterc) before
** we do the actual processing of arguments later in a second pass.
*/
stdin_is_interactive = 0;
+ }else if( cli_strcmp(z,"-utf8")==0 ){
+ }else if( cli_strcmp(z,"-no-utf8")==0 ){
}else if( cli_strcmp(z,"-heap")==0 ){
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
const char *zSize;
@@ -12366,7 +12470,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
if( pVfs ){
sqlite3_vfs_register(pVfs, 1);
}else{
- utf8_printf(stderr, "no such VFS: \"%s\"\n", zVfs);
+ eputf("no such VFS: \"%s\"\n", zVfs);
exit(1);
}
}
@@ -12376,7 +12480,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
data.pAuxDb->zDbFilename = ":memory:";
warnInmemoryDb = argc==1;
#else
- utf8_printf(stderr,"%s: Error: no database filename specified\n", Argv0);
+ eputf("%s: Error: no database filename specified\n", Argv0);
return 1;
#endif
}
@@ -12493,17 +12597,20 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-bail")==0 ){
/* No-op. The bail_on_error flag should already be set. */
}else if( cli_strcmp(z,"-version")==0 ){
- printf("%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(),
- 8*(int)sizeof(char*));
+ sputf(stdout, "%s %s (%d-bit)\n",
+ sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
return 0;
}else if( cli_strcmp(z,"-interactive")==0 ){
+ /* Need to check for interactive override here to so that it can
+ ** affect console setup (for Windows only) and testing thereof.
+ */
stdin_is_interactive = 1;
}else if( cli_strcmp(z,"-batch")==0 ){
- stdin_is_interactive = 0;
+ /* already handled */
}else if( cli_strcmp(z,"-utf8")==0 ){
-#if SHELL_WIN_UTF8_OPT
- console_utf8 = 1;
-#endif /* SHELL_WIN_UTF8_OPT */
+ /* already handled */
+ }else if( cli_strcmp(z,"-no-utf8")==0 ){
+ /* already handled */
}else if( cli_strcmp(z,"-heap")==0 ){
i++;
}else if( cli_strcmp(z,"-pagecache")==0 ){
@@ -12550,18 +12657,18 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
open_db(&data, 0);
rc = shell_exec(&data, z, &zErrMsg);
if( zErrMsg!=0 ){
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
+ eputf("Error: %s\n", zErrMsg);
if( bail_on_error ) return rc!=0 ? rc : 1;
}else if( rc!=0 ){
- utf8_printf(stderr,"Error: unable to process SQL \"%s\"\n", z);
+ eputf("Error: unable to process SQL \"%s\"\n", z);
if( bail_on_error ) return rc;
}
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A", 2)==0 ){
if( nCmd>0 ){
- utf8_printf(stderr, "Error: cannot mix regular SQL or dot-commands"
- " with \"%s\"\n", z);
+ eputf("Error: cannot mix regular SQL or dot-commands"
+ " with \"%s\"\n", z);
return 1;
}
open_db(&data, OPEN_DB_ZIPFILE);
@@ -12579,20 +12686,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
/* Acted upon in first pass. */
}else{
- utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
- raw_printf(stderr,"Use -help for a list of options.\n");
+ eputf("%s: Error: unknown option: %s\n", Argv0, z);
+ eputz("Use -help for a list of options.\n");
return 1;
}
data.cMode = data.mode;
}
-#if SHELL_WIN_UTF8_OPT
- if( console_utf8 && stdin_is_interactive ){
- console_prepare();
- }else{
- setBinaryMode(stdin, 0);
- console_utf8 = 0;
- }
-#endif
if( !readStdin ){
/* Run all arguments that do not begin with '-' as if they were separate
@@ -12612,9 +12711,9 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
rc = shell_exec(&data, azCmd[i], &zErrMsg);
if( zErrMsg || rc ){
if( zErrMsg!=0 ){
- utf8_printf(stderr,"Error: %s\n", zErrMsg);
+ eputf("Error: %s\n", zErrMsg);
}else{
- utf8_printf(stderr,"Error: unable to process SQL: %s\n", azCmd[i]);
+ eputf("Error: unable to process SQL: %s\n", azCmd[i]);
}
sqlite3_free(zErrMsg);
free(azCmd);
@@ -12629,16 +12728,19 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
char *zHome;
char *zHistory;
int nHistory;
- printf(
- "SQLite version %s %.19s\n" /*extra-version-info*/
- "Enter \".help\" for usage hints.\n",
- sqlite3_libversion(), sqlite3_sourceid()
- );
+#if CIO_WIN_WC_XLATE
+# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "")
+#else
+# define SHELL_CIO_CHAR_SET ""
+#endif
+ sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/
+ "Enter \".help\" for usage hints.\n",
+ sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET);
if( warnInmemoryDb ){
- printf("Connected to a ");
+ sputz(stdout, "Connected to a ");
printBold("transient in-memory database");
- printf(".\nUse \".open FILENAME\" to reopen on a "
- "persistent database.\n");
+ sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
+ " persistent database.\n");
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
@@ -12698,8 +12800,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
memset(&data, 0, sizeof(data));
#ifdef SQLITE_DEBUG
if( sqlite3_memory_used()>mem_main_enter ){
- utf8_printf(stderr, "Memory leaked: %u bytes\n",
- (unsigned int)(sqlite3_memory_used()-mem_main_enter));
+ eputf("Memory leaked: %u bytes\n",
+ (unsigned int)(sqlite3_memory_used()-mem_main_enter));
}
#endif
#endif /* !SQLITE_SHELL_FIDDLE */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 48009b145..e05d7b231 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
int sqlite3_exec(
@@ -762,11 +764,11 @@ struct sqlite3_file {
** </ul>
** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
-** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
-* If the lock is already at or below the requested lock state, then the call
+** If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
@@ -2127,7 +2129,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
@@ -2302,7 +2304,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -3954,14 +3956,17 @@ void sqlite3_free_filename(sqlite3_filename);
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
@@ -5325,6 +5330,7 @@ int sqlite3_finalize(sqlite3_stmt *pStmt);
*/
int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
@@ -5571,13 +5577,27 @@ int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
-** Specifying this flag makes no difference for scalar or aggregate user
-** functions. However, if it is not specified for a user-defined window
-** function, then any sub-types belonging to arguments passed to the window
-** function may be discarded before the window function is called (i.e.
-** sqlite3_value_subtype() will always return 0).
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5585,6 +5605,7 @@ int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -5781,6 +5802,12 @@ int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -5879,48 +5906,56 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
-** allocation error occurs.)^ </ul>
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -5930,10 +5965,67 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
void *sqlite3_get_auxdata(sqlite3_context*, int N);
void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+void *sqlite3_get_clientdata(sqlite3*,const char*);
+int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6135,6 +6227,20 @@ int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6566,7 +6672,7 @@ int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
int sqlite3_txn_state(sqlite3*,const char *zSchema);
/*
-** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state}
**
** These constants define the current transaction state of a database file.
@@ -6698,7 +6804,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
-** then the autovacuum steps callback is cancelled. The return value
+** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@@ -7217,6 +7323,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -7704,7 +7814,7 @@ int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
+** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the
@@ -7931,9 +8041,11 @@ int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
@@ -8184,6 +8296,7 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8191,6 +8304,7 @@ int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ASSERT 12
#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_SCRATCHMALLOC 17 /* NOT USED */
@@ -9245,8 +9359,8 @@ int sqlite3_backup_pagecount(sqlite3_backup *p);
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is cancelled. ^The blocked connections
-** unlock-notify callback may also be cancelled by closing the blocked
+** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
** The unlock-notify callback is not reentrant. If an application invokes
@@ -10549,6 +10663,13 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
@@ -10597,6 +10718,9 @@ unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
@@ -10605,6 +10729,13 @@ unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h
index 711638099..ae0949baf 100644
--- a/src/sqlite3ext.h
+++ b/src/sqlite3ext.h
@@ -363,6 +363,9 @@ struct sqlite3_api_routines {
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -693,6 +696,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index c2a9f855a..1c1055f42 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -319,6 +319,29 @@
#endif
/*
+** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit
+** SEH support if the -DSQLITE_OMIT_SEH option is given.
+*/
+#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
+# define SQLITE_USE_SEH 1
+#else
+# undef SQLITE_USE_SEH
+#endif
+
+/*
+** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly
+** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0
+*/
+#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1
+ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */
+# undef SQLITE_DIRECT_OVERFLOW_READ
+#else
+ /* In all other cases, enable */
+# define SQLITE_DIRECT_OVERFLOW_READ 1
+#endif
+
+
+/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
@@ -863,7 +886,7 @@ typedef INT16_TYPE LogEst;
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
- (defined(__APPLE__) && defined(__POWERPC__)) || \
+ (defined(__APPLE__) && defined(__ppc__)) || \
(defined(__TOS_AIX__) && !defined(__64BIT__))
# define SQLITE_PTRSIZE 4
# else
@@ -923,16 +946,33 @@ typedef INT16_TYPE LogEst;
** using C-preprocessor macros. If that is unsuccessful, or if
** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined
** at run-time.
+**
+** If you are building SQLite on some obscure platform for which the
+** following ifdef magic does not work, you can always include either:
+**
+** -DSQLITE_BYTEORDER=1234
+**
+** or
+**
+** -DSQLITE_BYTEORDER=4321
+**
+** to cause the build to work for little-endian or big-endian processors,
+** respectively.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__) || \
- defined(__ARMEB__) || defined(__AARCH64EB__)
-# define SQLITE_BYTEORDER 4321
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -1256,6 +1296,7 @@ typedef struct Column Column;
typedef struct Cte Cte;
typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbClientData DbClientData;
typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
@@ -1555,6 +1596,10 @@ struct FuncDefHash {
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
+#if defined(SQLITE_USER_AUTHENTICATION)
+# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \
+ See ext/userauth/user-auth.txt for details."
+#endif
#ifdef SQLITE_USER_AUTHENTICATION
/*
** Information held in the "sqlite3" database connection object and used
@@ -1734,6 +1779,7 @@ struct sqlite3 {
i64 nDeferredCons; /* Net deferred constraints this transaction. */
i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
+ DbClientData *pDbData; /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/* The following variables are all protected by the STATIC_MAIN
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
@@ -1816,6 +1862,7 @@ struct sqlite3 {
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
+#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
@@ -1984,14 +2031,15 @@ struct FuncDestructor {
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
-/* 0x8000 -- available for reuse */
+#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
-#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
+/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */
#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */
+/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */
#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
/* Identifier numbers for each in-line function */
@@ -2083,10 +2131,11 @@ struct FuncDestructor {
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
-#define JFUNCTION(zName, nArg, iArg, xFunc) \
- {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\
- SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
+ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
+ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
+ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
@@ -2427,8 +2476,7 @@ struct Table {
#define TF_HasStored 0x00000040 /* Has one or more STORED columns */
#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */
#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */
-#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by
- ** Index.aiRowLogEst[] values */
+#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */
#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */
#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */
#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */
@@ -2721,6 +2769,7 @@ struct Index {
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
+ unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
@@ -2831,6 +2880,10 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
+ int iOBTab; /* Ephemeral table to implement ORDER BY */
+ u8 bOBPayload; /* iOBTab has payload columns separate from key */
+ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
+ u8 bUseSubtype; /* Transfer subtype info through sorter */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
@@ -3015,7 +3068,7 @@ struct Expr {
#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
#define EP_Win 0x008000 /* Contains window functions */
#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
- /* 0x020000 // Available for reuse */
+#define EP_FullSize 0x020000 /* Expr structure must remain full sized */
#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
@@ -3045,6 +3098,7 @@ struct Expr {
#define ExprClearProperty(E,P) (E)->flags&=~(P)
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
+#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
/* Macros used to ensure that the correct members of unions are accessed
** in Expr.
@@ -3162,6 +3216,7 @@ struct ExprList {
#define ENAME_NAME 0 /* The AS clause of a result set */
#define ENAME_SPAN 1 /* Complete text of the result set expression */
#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */
+#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */
/*
** An instance of this structure can hold a simple list of identifiers,
@@ -3362,6 +3417,7 @@ struct NameContext {
int nRef; /* Number of names resolved by this context */
int nNcErr; /* Number of errors encountered while resolving names */
int ncFlags; /* Zero or more NC_* flags defined below */
+ u32 nNestedSelect; /* Number of nested selects using this NC */
Select *pWinSelect; /* SELECT statement for any window functions */
};
@@ -3770,6 +3826,7 @@ struct Parse {
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
+ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -4041,6 +4098,7 @@ struct Returning {
int iRetCur; /* Transient table holding RETURNING results */
int nRetCol; /* Number of in pReturnEL after expansion */
int iRetReg; /* Register array for holding a row of RETURNING */
+ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */
};
/*
@@ -4076,6 +4134,9 @@ struct sqlite3_str {
**
** 3. Make a (read-only) copy of a read-only RCStr string using
** sqlite3RCStrRef().
+**
+** "String" is in the name, but an RCStr object can also be used to hold
+** binary data.
*/
struct RCStr {
u64 nRCRef; /* Number of references */
@@ -4134,6 +4195,9 @@ struct Sqlite3Config {
u8 bSmallMalloc; /* Avoid large memory allocations if true */
u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
u8 bUseLongDouble; /* Make use of long double */
+#ifdef SQLITE_DEBUG
+ u8 bJsonSelfcheck; /* Double-check JSON parsing */
+#endif
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -4341,6 +4405,16 @@ struct CteUse {
};
+/* Client data associated with sqlite3_set_clientdata() and
+** sqlite3_get_clientdata().
+*/
+struct DbClientData {
+ DbClientData *pNext; /* Next in a linked list */
+ void *pData; /* The data */
+ void (*xDestructor)(void*); /* Destructor. Might be NULL */
+ char zName[1]; /* Name of this client data. MUST BE LAST */
+};
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -4745,9 +4819,12 @@ void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*);
Expr *sqlite3ExprSimplifiedAndOr(Expr*);
Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
+void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
+void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
void sqlite3ExprDelete(sqlite3*, Expr*);
+void sqlite3ExprDeleteGeneric(sqlite3*,void*);
void sqlite3ExprDeferredDelete(Parse*, Expr*);
void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
@@ -4757,6 +4834,7 @@ void sqlite3ExprListSetSortOrder(ExprList*,int,int);
void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
void sqlite3ExprListDelete(sqlite3*, ExprList*);
+void sqlite3ExprListDeleteGeneric(sqlite3*,void*);
u32 sqlite3ExprListFlags(const ExprList*);
int sqlite3IndexHasDuplicateRootPage(Index*);
int sqlite3Init(sqlite3*, char**);
@@ -4847,6 +4925,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int);
void sqlite3DropTable(Parse*, SrcList*, int, int);
void sqlite3CodeDropTable(Parse*, Table*, int, int);
void sqlite3DeleteTable(sqlite3*, Table*);
+void sqlite3DeleteTableGeneric(sqlite3*, void*);
void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
void sqlite3AutoincrementBegin(Parse *pParse);
@@ -4883,6 +4962,7 @@ int sqlite3Select(Parse*, Select*, SelectDest*);
Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
void sqlite3SelectDelete(sqlite3*, Select*);
+void sqlite3SelectDeleteGeneric(sqlite3*,void*);
Table *sqlite3SrcListLookup(Parse*, SrcList*);
int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
@@ -4981,6 +5061,7 @@ int sqlite3ExprIsInteger(const Expr*, int*);
int sqlite3ExprCanBeNull(const Expr*);
int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
int sqlite3IsRowid(const char*);
+const char *sqlite3RowidAlias(Table *pTab);
void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
@@ -5108,6 +5189,7 @@ int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
int sqlite3Utf8CharLen(const char *pData, int nByte);
u32 sqlite3Utf8Read(const u8**);
+int sqlite3Utf8ReadLimited(const u8*, int, u32*);
LogEst sqlite3LogEst(u64);
LogEst sqlite3LogEstAdd(LogEst,LogEst);
LogEst sqlite3LogEstFromDouble(double);
@@ -5252,7 +5334,8 @@ int sqlite3MatchEName(
const struct ExprList_item*,
const char*,
const char*,
- const char*
+ const char*,
+ int*
);
Bitmask sqlite3ExprColUsed(Expr*);
u8 sqlite3StrIHash(const char*);
@@ -5309,7 +5392,7 @@ int sqlite3ApiExit(sqlite3 *db, int);
int sqlite3OpenTempDatabase(Parse *);
char *sqlite3RCStrRef(char*);
-void sqlite3RCStrUnref(char*);
+void sqlite3RCStrUnref(void*);
char *sqlite3RCStrNew(u64);
char *sqlite3RCStrResize(char*,u64);
@@ -5453,6 +5536,7 @@ const char *sqlite3JournalModename(int);
void sqlite3CteDelete(sqlite3*,Cte*);
With *sqlite3WithAdd(Parse*,With*,Cte*);
void sqlite3WithDelete(sqlite3*,With*);
+ void sqlite3WithDeleteGeneric(sqlite3*,void*);
With *sqlite3WithPush(Parse*, With*, u8);
#else
# define sqlite3CteNew(P,T,E,S) ((void*)0)
diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h
index bfb596da2..abf59e1b3 100644
--- a/src/sqliteLimit.h
+++ b/src/sqliteLimit.h
@@ -187,7 +187,7 @@
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
+# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
#endif
/*
diff --git a/src/status.c b/src/status.c
index 784016700..a462c9429 100644
--- a/src/status.c
+++ b/src/status.c
@@ -362,7 +362,7 @@ int sqlite3_db_status(
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
- int nRet = 0;
+ u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
@@ -375,7 +375,7 @@ int sqlite3_db_status(
*pHighwater = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
- *pCurrent = nRet;
+ *pCurrent = (int)nRet & 0x7fffffff;
break;
}
diff --git a/src/test1.c b/src/test1.c
index 145882f08..8faf5a397 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -992,6 +992,39 @@ static void intrealFunction(
}
/*
+** These SQL functions attempt to return a value (their first argument)
+** that has been modified to have multiple datatypes. For example both
+** TEXT and INTEGER.
+*/
+static void addTextTypeFunction(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ (void)sqlite3_value_text(argv[0]);
+ (void)argc;
+ sqlite3_result_value(context, argv[0]);
+}
+static void addIntTypeFunction(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ (void)sqlite3_value_int64(argv[0]);
+ (void)argc;
+ sqlite3_result_value(context, argv[0]);
+}
+static void addRealTypeFunction(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ (void)sqlite3_value_double(argv[0]);
+ (void)argc;
+ sqlite3_result_value(context, argv[0]);
+}
+
+/*
** SQL function: strtod(X)
**
** Use the C-library strtod() function to convert string X into a double.
@@ -1103,6 +1136,22 @@ static int SQLITE_TCLAPI test_create_function(
0, intrealFunction, 0, 0);
}
+ /* The add_text_type(), add_int_type(), and add_real_type() functions
+ ** attempt to return a value that has multiple datatypes.
+ */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "add_text_type", 1, SQLITE_UTF8,
+ 0, addTextTypeFunction, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "add_int_type", 1, SQLITE_UTF8,
+ 0, addIntTypeFunction, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "add_real_type", 1, SQLITE_UTF8,
+ 0, addRealTypeFunction, 0, 0);
+ }
+
/* Functions strtod() and dtostr() work as in the shell. These routines
** use the standard C library to convert between floating point and
** text. This is used to compare SQLite's internal conversion routines
@@ -4222,9 +4271,11 @@ static int SQLITE_TCLAPI test_bind_value_from_preupdate(
sqlite3_stmt *pStmt;
int idx;
int bidx;
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
const char *z3 = 0;
sqlite3 *db = 0;
sqlite3_value *pVal = 0;
+#endif
if( objc!=5 ){
Tcl_WrongNumArgs(interp, 1, objv, "STMT N NEW|OLD IDX");
@@ -4233,11 +4284,11 @@ static int SQLITE_TCLAPI test_bind_value_from_preupdate(
if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR;
if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR;
- z3 = Tcl_GetString(objv[3]);
if( Tcl_GetIntFromObj(interp, objv[4], &bidx) ) return TCL_ERROR;
- db = sqlite3_db_handle(pStmt);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ z3 = Tcl_GetString(objv[3]);
+ db = sqlite3_db_handle(pStmt);
if( z3[0]=='n' ){
sqlite3_preupdate_new(db, bidx, &pVal);
}else if( z3[0]=='o' ){
@@ -7651,6 +7702,7 @@ static int SQLITE_TCLAPI test_test_control(
{ "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP },
{ "SQLITE_TESTCTRL_IMPOSTER", SQLITE_TESTCTRL_IMPOSTER },
{ "SQLITE_TESTCTRL_INTERNAL_FUNCTIONS", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS},
+ { "SQLITE_TESTCTRL_FK_NO_ACTION", SQLITE_TESTCTRL_FK_NO_ACTION},
{ 0, 0 }
};
int iVerb;
@@ -7690,6 +7742,20 @@ static int SQLITE_TCLAPI test_test_control(
break;
}
+ case SQLITE_TESTCTRL_FK_NO_ACTION: {
+ int val = 0;
+ sqlite3 *db = 0;
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "DB BOOLEAN");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[2]), &db) ) return TCL_ERROR;
+ if( Tcl_GetBooleanFromObj(interp, objv[3], &val) ) return TCL_ERROR;
+
+ sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, db, val);
+ break;
+ }
+
case SQLITE_TESTCTRL_SORTER_MMAP: {
int val;
sqlite3 *db;
@@ -8103,6 +8169,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*);
#endif
extern int sqlite3_qpvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
+ extern int sqlite3_randomjson_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*);
extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
@@ -8135,6 +8202,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
{ "prefixes", sqlite3_prefixes_init },
#endif
{ "qpvtab", sqlite3_qpvtab_init },
+ { "randomjson", sqlite3_randomjson_init },
{ "regexp", sqlite3_regexp_init },
{ "remember", sqlite3_remember_init },
{ "series", sqlite3_series_init },
diff --git a/src/test8.c b/src/test8.c
index f0f574310..4aeb555c7 100644
--- a/src/test8.c
+++ b/src/test8.c
@@ -1317,7 +1317,12 @@ static sqlite3_module echoModule = {
echoCommit, /* xCommit - commit transaction */
echoRollback, /* xRollback - rollback transaction */
echoFindFunction, /* xFindFunction - function overloading */
- echoRename /* xRename - rename the table */
+ echoRename, /* xRename - rename the table */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
static sqlite3_module echoModuleV2 = {
@@ -1343,7 +1348,9 @@ static sqlite3_module echoModuleV2 = {
echoRename, /* xRename - rename the table */
echoSavepoint,
echoRelease,
- echoRollbackTo
+ echoRollbackTo,
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/test_bestindex.c b/src/test_bestindex.c
index f6e0678ce..8128530b4 100644
--- a/src/test_bestindex.c
+++ b/src/test_bestindex.c
@@ -814,6 +814,11 @@ static sqlite3_module tclModule = {
0, /* xRollback */
tclFindFunction, /* xFindFunction */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/test_fs.c b/src/test_fs.c
index ddfdc7fb5..f88f3a942 100644
--- a/src/test_fs.c
+++ b/src/test_fs.c
@@ -816,6 +816,11 @@ static sqlite3_module fsModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
static sqlite3_module fsdirModule = {
@@ -839,6 +844,11 @@ static sqlite3_module fsdirModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
static sqlite3_module fstreeModule = {
@@ -862,6 +872,11 @@ static sqlite3_module fstreeModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/test_func.c b/src/test_func.c
index fa5243826..80df48828 100644
--- a/src/test_func.c
+++ b/src/test_func.c
@@ -694,7 +694,8 @@ static int registerTestFunctions(
{ "test_extract", 2, SQLITE_UTF8, test_extract},
{ "test_zeroblob", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, test_zeroblob},
{ "test_getsubtype", 1, SQLITE_UTF8, test_getsubtype},
- { "test_setsubtype", 2, SQLITE_UTF8, test_setsubtype},
+ { "test_setsubtype", 2, SQLITE_UTF8|SQLITE_RESULT_SUBTYPE,
+ test_setsubtype},
{ "test_frombind", -1, SQLITE_UTF8, test_frombind},
};
int i;
diff --git a/src/test_intarray.c b/src/test_intarray.c
index 8c74a0415..a978ed585 100644
--- a/src/test_intarray.c
+++ b/src/test_intarray.c
@@ -205,6 +205,11 @@ static sqlite3_module intarrayModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
diff --git a/src/test_osinst.c b/src/test_osinst.c
index 3e698c032..062e83159 100644
--- a/src/test_osinst.c
+++ b/src/test_osinst.c
@@ -1090,7 +1090,12 @@ int sqlite3_vfslog_register(sqlite3 *db){
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
- };
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
+ };
sqlite3_create_module(db, "vfslog", &vfslog_module, 0);
return SQLITE_OK;
diff --git a/src/test_schema.c b/src/test_schema.c
index d2cae7f2a..2cbc18e2b 100644
--- a/src/test_schema.c
+++ b/src/test_schema.c
@@ -292,6 +292,11 @@ static sqlite3_module schemaModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
diff --git a/src/test_tclsh.c b/src/test_tclsh.c
index 32aee4267..4697c3b85 100644
--- a/src/test_tclsh.c
+++ b/src/test_tclsh.c
@@ -108,6 +108,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
extern int Sqlitetest_window_Init(Tcl_Interp *);
extern int Sqlitetestvdbecov_Init(Tcl_Interp *);
extern int TestRecover_Init(Tcl_Interp*);
+ extern int Sqlitetestintck_Init(Tcl_Interp*);
Tcl_CmdInfo cmdInfo;
@@ -175,6 +176,7 @@ const char *sqlite3TestInit(Tcl_Interp *interp){
Sqlitetest_window_Init(interp);
Sqlitetestvdbecov_Init(interp);
TestRecover_Init(interp);
+ Sqlitetestintck_Init(interp);
Tcl_CreateObjCommand(
interp, "load_testfixture_extensions", load_testfixture_extensions,0,0
diff --git a/src/test_tclvar.c b/src/test_tclvar.c
index bf99a8ead..36165bc27 100644
--- a/src/test_tclvar.c
+++ b/src/test_tclvar.c
@@ -487,6 +487,11 @@ static sqlite3_module tclvarModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/treeview.c b/src/treeview.c
index d55adab38..2576532b6 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -412,6 +412,7 @@ void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
sqlite3TreeViewItem(pView, "FILTER", 1);
sqlite3TreeViewExpr(pView, pWin->pFilter, 0);
sqlite3TreeViewPop(&pView);
+ if( pWin->eFrmType==TK_FILTER ) return;
}
sqlite3TreeViewPush(&pView, more);
if( pWin->zName ){
@@ -421,7 +422,7 @@ void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
}
if( pWin->zBase ) nElement++;
if( pWin->pOrderBy ) nElement++;
- if( pWin->eFrmType ) nElement++;
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++;
if( pWin->eExclude ) nElement++;
if( pWin->zBase ){
sqlite3TreeViewPush(&pView, (--nElement)>0);
@@ -434,7 +435,7 @@ void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){
if( pWin->pOrderBy ){
sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY");
}
- if( pWin->eFrmType ){
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){
char zBuf[30];
const char *zFrmType = "ROWS";
if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE";
@@ -682,7 +683,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
assert( ExprUseXList(pExpr) );
pFarg = pExpr->x.pList;
#ifndef SQLITE_OMIT_WINDOWFUNC
- pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0;
+ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0;
#else
pWin = 0;
#endif
@@ -708,7 +709,13 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0);
+ if( pExpr->pLeft ){
+ Expr *pOB = pExpr->pLeft;
+ assert( pOB->op==TK_ORDER );
+ assert( ExprUseXList(pOB) );
+ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY");
+ }
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
@@ -717,6 +724,10 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
#endif
break;
}
+ case TK_ORDER: {
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY");
+ break;
+ }
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS: {
assert( ExprUseXSelect(pExpr) );
@@ -770,7 +781,7 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
assert( pExpr->x.pList->nExpr==2 );
pY = pExpr->x.pList->a[0].pExpr;
pZ = pExpr->x.pList->a[1].pExpr;
- sqlite3TreeViewLine(pView, "BETWEEN");
+ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
sqlite3TreeViewExpr(pView, pX, 1);
sqlite3TreeViewExpr(pView, pY, 1);
sqlite3TreeViewExpr(pView, pZ, 0);
diff --git a/src/trigger.c b/src/trigger.c
index bcb2132f0..97ca249be 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -183,6 +183,10 @@ void sqlite3BeginTrigger(
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
goto trigger_orphan_error;
}
+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
+ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables");
+ goto trigger_orphan_error;
+ }
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
@@ -966,10 +970,17 @@ static void codeReturningTrigger(
SrcList sFrom;
assert( v!=0 );
- assert( pParse->bReturning );
+ if( !pParse->bReturning ){
+ /* This RETURNING trigger must be for a different statement as
+ ** this statement lacks a RETURNING clause. */
+ return;
+ }
assert( db->pParse==pParse );
pReturning = pParse->u1.pReturning;
- assert( pTrigger == &(pReturning->retTrig) );
+ if( pTrigger != &(pReturning->retTrig) ){
+ /* This RETURNING trigger is for a different statement */
+ return;
+ }
memset(&sSelect, 0, sizeof(sSelect));
memset(&sFrom, 0, sizeof(sFrom));
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
diff --git a/src/utf.c b/src/utf.c
index 5f27babdf..216864f5c 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -164,7 +164,38 @@ u32 sqlite3Utf8Read(
return c;
}
-
+/*
+** Read a single UTF8 character out of buffer z[], but reading no
+** more than n characters from the buffer. z[] is not zero-terminated.
+**
+** Return the number of bytes used to construct the character.
+**
+** Invalid UTF8 might generate a strange result. No effort is made
+** to detect invalid UTF8.
+**
+** At most 4 bytes will be read out of z[]. The return value will always
+** be between 1 and 4.
+*/
+int sqlite3Utf8ReadLimited(
+ const u8 *z,
+ int n,
+ u32 *piOut
+){
+ u32 c;
+ int i = 1;
+ assert( n>0 );
+ c = z[0];
+ if( c>=0xc0 ){
+ c = sqlite3Utf8Trans1[c-0xc0];
+ if( n>4 ) n = 4;
+ while( i<n && (z[i] & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & z[i]);
+ i++;
+ }
+ }
+ *piOut = c;
+ return i;
+}
/*
diff --git a/src/util.c b/src/util.c
index 7f8a33f9c..4aa82d063 100644
--- a/src/util.c
+++ b/src/util.c
@@ -141,7 +141,7 @@ void sqlite3ErrorClear(sqlite3 *db){
*/
void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
if( rc==SQLITE_IOERR_IN_PAGE ){
int ii;
int iErr;
@@ -1029,29 +1029,29 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
double rr[2];
rr[0] = r;
rr[1] = 0.0;
- if( rr[0]>1.84e+19 ){
- while( rr[0]>1.84e+119 ){
+ if( rr[0]>9.223372036854774784e+18 ){
+ while( rr[0]>9.223372036854774784e+118 ){
exp += 100;
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
}
- while( rr[0]>1.84e+29 ){
+ while( rr[0]>9.223372036854774784e+28 ){
exp += 10;
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
}
- while( rr[0]>1.84e+19 ){
+ while( rr[0]>9.223372036854774784e+18 ){
exp += 1;
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}else{
- while( rr[0]<1.84e-82 ){
+ while( rr[0]<9.223372036854774784e-83 ){
exp -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
- while( rr[0]<1.84e+08 ){
+ while( rr[0]<9.223372036854774784e+07 ){
exp -= 10;
dekkerMul2(rr, 1.0e+10, 0.0);
}
- while( rr[0]<1.84e+18 ){
+ while( rr[0]<9.22337203685477478e+17 ){
exp -= 1;
dekkerMul2(rr, 1.0e+01, 0.0);
}
@@ -1069,7 +1069,7 @@ void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRound){
assert( p->n>0 );
assert( p->n<sizeof(p->zBuf) );
p->iDP = p->n + exp;
- if( iRound<0 ){
+ if( iRound<=0 ){
iRound = p->iDP - iRound;
if( iRound==0 && p->zBuf[i+1]>='5' ){
iRound = 1;
@@ -1367,121 +1367,32 @@ u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
** this function assumes the single-byte case has already been handled.
*/
u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
- u32 a,b;
+ u64 v64;
+ u8 n;
- /* The 1-byte case. Overwhelmingly the most common. Handled inline
- ** by the getVarin32() macro */
- a = *p;
- /* a: p0 (unmasked) */
-#ifndef getVarint32
- if (!(a&0x80))
- {
- /* Values between 0 and 127 */
- *v = a;
- return 1;
- }
-#endif
+ /* Assume that the single-byte case has already been handled by
+ ** the getVarint32() macro */
+ assert( (p[0] & 0x80)!=0 );
- /* The 2-byte case */
- p++;
- b = *p;
- /* b: p1 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 128 and 16383 */
- a &= 0x7f;
- a = a<<7;
- *v = a | b;
+ if( (p[1] & 0x80)==0 ){
+ /* This is the two-byte case */
+ *v = ((p[0]&0x7f)<<7) | p[1];
return 2;
}
-
- /* The 3-byte case */
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<14 | p2 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 16384 and 2097151 */
- a &= (0x7f<<14)|(0x7f);
- b &= 0x7f;
- b = b<<7;
- *v = a | b;
+ if( (p[2] & 0x80)==0 ){
+ /* This is the three-byte case */
+ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2];
return 3;
}
-
- /* A 32-bit varint is used to store size information in btrees.
- ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
- ** A 3-byte varint is sufficient, for example, to record the size
- ** of a 1048569-byte BLOB or string.
- **
- ** We only unroll the first 1-, 2-, and 3- byte cases. The very
- ** rare larger cases can be handled by the slower 64-bit varint
- ** routine.
- */
-#if 1
- {
- u64 v64;
- u8 n;
-
- n = sqlite3GetVarint(p-2, &v64);
- assert( n>3 && n<=9 );
- if( (v64 & SQLITE_MAX_U32)!=v64 ){
- *v = 0xffffffff;
- }else{
- *v = (u32)v64;
- }
- return n;
- }
-
-#else
- /* For following code (kept for historical record only) shows an
- ** unrolling for the 3- and 4-byte varint cases. This code is
- ** slightly faster, but it is also larger and much harder to test.
- */
- p++;
- b = b<<14;
- b |= *p;
- /* b: p1<<14 | p3 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 2097152 and 268435455 */
- b &= (0x7f<<14)|(0x7f);
- a &= (0x7f<<14)|(0x7f);
- a = a<<7;
- *v = a | b;
- return 4;
- }
-
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<28 | p2<<14 | p4 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 268435456 and 34359738367 */
- a &= SLOT_4_2_0;
- b &= SLOT_4_2_0;
- b = b<<7;
- *v = a | b;
- return 5;
- }
-
- /* We can only reach this point when reading a corrupt database
- ** file. In that case we are not in any hurry. Use the (relatively
- ** slow) general-purpose sqlite3GetVarint() routine to extract the
- ** value. */
- {
- u64 v64;
- u8 n;
-
- p -= 4;
- n = sqlite3GetVarint(p, &v64);
- assert( n>5 && n<=9 );
+ /* four or more bytes */
+ n = sqlite3GetVarint(p, &v64);
+ assert( n>3 && n<=9 );
+ if( (v64 & SQLITE_MAX_U32)!=v64 ){
+ *v = 0xffffffff;
+ }else{
*v = (u32)v64;
- return n;
}
-#endif
+ return n;
}
/*
diff --git a/src/vdbe.c b/src/vdbe.c
index 5899fa518..48a029dbf 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -132,11 +132,12 @@ int sqlite3_found_count = 0;
** sqlite3CantopenError(lineno)
*/
static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
(void)v;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */
}
#endif
@@ -762,11 +763,11 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
sqlite3RCStrRef(pBuf);
if( t&1 ){
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pDest->flags |= MEM_Term;
}else{
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
}
}else{
rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest);
@@ -2033,7 +2034,7 @@ case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
- pIn1->u.i += pOp->p2;
+ *(u64*)&pIn1->u.i += (u64)pOp->p2;
break;
}
@@ -2085,7 +2086,7 @@ case OP_RealAffinity: { /* in1 */
}
#endif
-#ifndef SQLITE_OMIT_CAST
+#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE)
/* Opcode: Cast P1 P2 * * *
** Synopsis: affinity(r[P1])
**
@@ -2300,7 +2301,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags1 & MEM_Str)!=0 ){
+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
testcase( pIn1->flags & MEM_IntReal );
@@ -2309,7 +2312,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags3 & MEM_Str)!=0 ){
+ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
testcase( pIn3->flags & MEM_IntReal );
@@ -3641,7 +3646,6 @@ case OP_MakeRecord: {
/* NULL value. No change in zPayload */
}else{
u64 v;
- u32 i;
if( serial_type==7 ){
assert( sizeof(v)==sizeof(pRec->u.r) );
memcpy(&v, &pRec->u.r, sizeof(v));
@@ -3649,12 +3653,22 @@ case OP_MakeRecord: {
}else{
v = pRec->u.i;
}
- len = i = sqlite3SmallTypeSizes[serial_type];
- assert( i>0 );
- while( 1 /*exit-by-break*/ ){
- zPayload[--i] = (u8)(v&0xFF);
- if( i==0 ) break;
- v >>= 8;
+ len = sqlite3SmallTypeSizes[serial_type];
+ assert( len>=1 && len<=8 && len!=5 && len!=7 );
+ switch( len ){
+ default: zPayload[7] = (u8)(v&0xff); v >>= 8;
+ zPayload[6] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8;
+ zPayload[4] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 1: zPayload[0] = (u8)(v&0xff);
}
zPayload += len;
}
@@ -5771,8 +5785,13 @@ case OP_RowCell: {
** the "primary" delete. The others are all on OPFLAG_FORDELETE
** cursors or else are marked with the AUXDELETE flag.
**
-** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
-** change count is incremented (otherwise not).
+** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then
+** the row change count is incremented (otherwise not).
+**
+** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the
+** pre-update-hook for deletes is run, but the btree is otherwise unchanged.
+** This happens when the OP_Delete is to be shortly followed by an OP_Insert
+** with the same key, causing the btree entry to be overwritten.
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
@@ -6173,28 +6192,37 @@ case OP_Last: { /* jump, ncycle */
break;
}
-/* Opcode: IfSmaller P1 P2 P3 * *
+/* Opcode: IfSizeBetween P1 P2 P3 P4 *
**
-** Estimate the number of rows in the table P1. Jump to P2 if that
-** estimate is less than approximately 2**(0.1*P3).
+** Let N be the approximate number of rows in the table or index
+** with cursor P1 and let X be 10*log2(N) if N is positive or -1
+** if N is zero. Thus X will be within the range of -1 to 640, inclusive
+** Jump to P2 if X is in between P3 and P4, inclusive.
*/
-case OP_IfSmaller: { /* jump */
+case OP_IfSizeBetween: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
i64 sz;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_INT32 );
+ assert( pOp->p3>=-1 && pOp->p3<=640 );
+ assert( pOp->p4.i>=-1 && pOp->p4.i<=640 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
if( rc ) goto abort_due_to_error;
- if( res==0 ){
+ if( res!=0 ){
+ sz = -1; /* -Infinity encoding */
+ }else{
sz = sqlite3BtreeRowCountEst(pCrsr);
- if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)<pOp->p3 ) res = 1;
+ assert( sz>0 );
+ sz = sqlite3LogEst((u64)sz);
}
+ res = sz>=pOp->p3 && sz<=pOp->p4.i;
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
break;
@@ -6894,16 +6922,57 @@ case OP_CreateBtree: { /* out2 */
break;
}
-/* Opcode: SqlExec * * * P4 *
+/* Opcode: SqlExec P1 P2 * P4 *
**
** Run the SQL statement or statements specified in the P4 string.
+**
+** The P1 parameter is a bitmask of options:
+**
+** 0x0001 Disable Auth and Trace callbacks while the statements
+** in P4 are running.
+**
+** 0x0002 Set db->nAnalysisLimit to P2 while the statements in
+** P4 are running.
+**
*/
case OP_SqlExec: {
+ char *zErr;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth;
+#endif
+ u8 mTrace;
+ int savedAnalysisLimit;
+
sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
+ zErr = 0;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ xAuth = db->xAuth;
+#endif
+ mTrace = db->mTrace;
+ savedAnalysisLimit = db->nAnalysisLimit;
+ if( pOp->p1 & 0x0001 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = 0;
+#endif
+ db->mTrace = 0;
+ }
+ if( pOp->p1 & 0x0002 ){
+ db->nAnalysisLimit = pOp->p2;
+ }
+ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr);
db->nSqlExec--;
- if( rc ) goto abort_due_to_error;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ db->mTrace = mTrace;
+ db->nAnalysisLimit = savedAnalysisLimit;
+ if( zErr || rc ){
+ sqlite3VdbeError(p, "%s", zErr);
+ sqlite3_free(zErr);
+ if( rc==SQLITE_NOMEM ) goto no_mem;
+ goto abort_due_to_error;
+ }
break;
}
@@ -7054,8 +7123,8 @@ case OP_DropTrigger: {
**
** The register P3 contains one less than the maximum number of allowed errors.
** At most reg(P3) errors will be reported.
-** In other words, the analysis stops as soon as reg(P1) errors are
-** seen. Reg(P1) is updated with the number of errors remaining.
+** In other words, the analysis stops as soon as reg(P3) errors are
+** seen. Reg(P3) is updated with the number of errors remaining.
**
** The root page numbers of all tables in the database are integers
** stored in P4_INTARRAY argument.
@@ -8125,6 +8194,52 @@ case OP_VOpen: { /* ncycle */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VCheck P1 P2 P3 P4 *
+**
+** P4 is a pointer to a Table object that is a virtual table in schema P1
+** that supports the xIntegrity() method. This opcode runs the xIntegrity()
+** method for that virtual table, using P3 as the integer argument. If
+** an error is reported back, the table name is prepended to the error
+** message and that message is stored in P2. If no errors are seen,
+** register P2 is set to NULL.
+*/
+case OP_VCheck: { /* out2 */
+ Table *pTab;
+ sqlite3_vtab *pVtab;
+ const sqlite3_module *pModule;
+ char *zErr = 0;
+
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */
+ assert( pOp->p4type==P4_TABLEREF );
+ pTab = pOp->p4.pTab;
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>0 );
+ assert( IsVirtual(pTab) );
+ if( pTab->u.vtab.p==0 ) break;
+ pVtab = pTab->u.vtab.p->pVtab;
+ assert( pVtab!=0 );
+ pModule = pVtab->pModule;
+ assert( pModule!=0 );
+ assert( pModule->iVersion>=4 );
+ assert( pModule->xIntegrity!=0 );
+ sqlite3VtabLock(pTab->u.vtab.p);
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
+ pOp->p3, &zErr);
+ sqlite3VtabUnlock(pTab->u.vtab.p);
+ if( rc ){
+ sqlite3_free(zErr);
+ goto abort_due_to_error;
+ }
+ if( zErr ){
+ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
+ }
+ break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VInitIn P1 P2 P3 * *
** Synopsis: r[P2]=ValueList(P1,P3)
**
@@ -8237,6 +8352,7 @@ case OP_VColumn: { /* ncycle */
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
+ FuncDef nullFunc;
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur!=0 );
@@ -8254,6 +8370,9 @@ case OP_VColumn: { /* ncycle */
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
sContext.enc = encoding;
+ nullFunc.pUserData = 0;
+ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
+ sContext.pFunc = &nullFunc;
assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
@@ -8586,6 +8705,42 @@ case OP_ClrSubtype: { /* in1 */
break;
}
+/* Opcode: GetSubtype P1 P2 * * *
+** Synopsis: r[P2] = r[P1].subtype
+**
+** Extract the subtype value from register P1 and write that subtype
+** into register P2. If P1 has no subtype, then P1 gets a NULL.
+*/
+case OP_GetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Subtype ){
+ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
+ }else{
+ sqlite3VdbeMemSetNull(pOut);
+ }
+ break;
+}
+
+/* Opcode: SetSubtype P1 P2 * * *
+** Synopsis: r[P2].subtype = r[P1]
+**
+** Set the subtype value of register P2 to the integer from register P1.
+** If P1 is NULL, clear the subtype from p2.
+*/
+case OP_SetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Null ){
+ pOut->flags &= ~MEM_Subtype;
+ }else{
+ assert( pIn1->flags & MEM_Int );
+ pOut->flags |= MEM_Subtype;
+ pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
+ }
+ break;
+}
+
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
diff --git a/src/vdbe.h b/src/vdbe.h
index f44f24f93..25bda6be7 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -126,6 +126,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */
#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 79b5de9f0..14c6091e0 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -152,7 +152,15 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+ sqlite3_mutex *mutex;
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+#if SQLITE_THREADSAFE
+ mutex = p->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
for(i=0; i<p->nVar; i++){
@@ -375,7 +383,7 @@ void sqlite3_value_free(sqlite3_value *pOld){
** is too big or if an OOM occurs.
**
** The invokeValueDestructor(P,X) routine invokes destructor function X()
-** on value P is not going to be used and need to be destroyed.
+** on value P if P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -405,7 +413,7 @@ static void setResultStrOrError(
static int invokeValueDestructor(
const void *p, /* Value to destroy */
void (*xDel)(void*), /* The destructor */
- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */
){
assert( xDel!=SQLITE_DYNAMIC );
if( xDel==0 ){
@@ -415,7 +423,14 @@ static int invokeValueDestructor(
}else{
xDel((void*)p);
}
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx!=0 ){
+ sqlite3_result_error_toobig(pCtx);
+ }
+#else
+ assert( pCtx!=0 );
sqlite3_result_error_toobig(pCtx);
+#endif
return SQLITE_TOOBIG;
}
void sqlite3_result_blob(
@@ -424,6 +439,12 @@ void sqlite3_result_blob(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 || n<0 ){
+ invokeValueDestructor(z, xDel, pCtx);
+ return;
+ }
+#endif
assert( n>=0 );
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
@@ -434,8 +455,14 @@ void sqlite3_result_blob64(
sqlite3_uint64 n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
@@ -443,30 +470,48 @@ void sqlite3_result_blob64(
}
}
void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
void sqlite3_result_null(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
}
@@ -476,14 +521,37 @@ void sqlite3_result_pointer(
const char *zPType,
void (*xDestructor)(void*)
){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(pPtr, xDestructor, 0);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
sqlite3VdbeMemRelease(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
}
void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0
+ if( pCtx->pFunc!=0
+ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0
+ ){
+ char zErr[200];
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "misuse of sqlite3_result_subtype() by %s()",
+ pCtx->pFunc->zName);
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+#endif /* SQLITE_STRICT_SUBTYPE */
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
pOut->eSubtype = eSubtype & 0xff;
pOut->flags |= MEM_Subtype;
@@ -494,6 +562,12 @@ void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
@@ -504,6 +578,12 @@ void sqlite3_result_text64(
void (*xDel)(void *),
unsigned char enc
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
if( enc!=SQLITE_UTF8 ){
@@ -547,7 +627,16 @@ void sqlite3_result_text16le(
}
#endif /* SQLITE_OMIT_UTF16 */
void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+ if( pValue==0 ){
+ sqlite3_result_null(pCtx);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemCopy(pOut, pValue);
sqlite3VdbeChangeEncoding(pOut, pCtx->enc);
@@ -559,7 +648,12 @@ void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0);
}
int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
sqlite3_result_error_toobig(pCtx);
@@ -573,6 +667,9 @@ int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
#endif
}
void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
pCtx->isError = errCode ? errCode : -1;
#ifdef SQLITE_DEBUG
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
@@ -585,6 +682,9 @@ void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
/* Force an SQLITE_TOOBIG error. */
void sqlite3_result_error_toobig(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
@@ -593,6 +693,9 @@ void sqlite3_result_error_toobig(sqlite3_context *pCtx){
/* An SQLITE_NOMEM error. */
void sqlite3_result_error_nomem(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM_BKPT;
@@ -845,6 +948,9 @@ int sqlite3_step(sqlite3_stmt *pStmt){
** pointer to it.
*/
void *sqlite3_user_data(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#endif
assert( p && p->pFunc );
return p->pFunc->pUserData;
}
@@ -860,7 +966,11 @@ void *sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p && p->pOut );
+#endif
return p->pOut->db;
}
@@ -879,7 +989,11 @@ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
int sqlite3_vtab_nochange(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p );
+#endif
return sqlite3_value_nochange(p->pOut);
}
@@ -907,7 +1021,7 @@ static int valueFromValueList(
ValueList *pRhs;
*ppOut = 0;
- if( pVal==0 ) return SQLITE_MISUSE;
+ if( pVal==0 ) return SQLITE_MISUSE_BKPT;
if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
return SQLITE_ERROR;
}else{
@@ -1038,6 +1152,9 @@ void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return 0;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
@@ -1070,8 +1187,12 @@ void sqlite3_set_auxdata(
void (*xDelete)(void*)
){
AuxData *pAuxData;
- Vdbe *pVdbe = pCtx->pVdbe;
+ Vdbe *pVdbe;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+ pVdbe= pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
@@ -1508,7 +1629,7 @@ static int vdbeUnbind(Vdbe *p, unsigned int i){
}
sqlite3_mutex_enter(p->db->mutex);
if( p->eVdbeState!=VDBE_READY_STATE ){
- sqlite3Error(p->db, SQLITE_MISUSE);
+ sqlite3Error(p->db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
@@ -1737,6 +1858,9 @@ int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(p->db->mutex);
if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
rc = SQLITE_TOOBIG;
@@ -1863,6 +1987,9 @@ int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){
Vdbe *v = (Vdbe*)pStmt;
int rc;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(v->db->mutex);
if( ((int)v->explain)==eMode ){
rc = SQLITE_OK;
@@ -2029,10 +2156,16 @@ static UnpackedRecord *vdbeUnpackRecord(
** a field of the row currently being updated or deleted.
*/
int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
Mem *pMem;
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
/* Test that this call is being made from within an SQLITE_DELETE or
** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
if( !p || p->op==SQLITE_INSERT ){
@@ -2093,7 +2226,12 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
** the number of columns in the row being updated, deleted or inserted.
*/
int sqlite3_preupdate_count(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -2111,7 +2249,12 @@ int sqlite3_preupdate_count(sqlite3 *db){
** or SET DEFAULT action is considered a trigger.
*/
int sqlite3_preupdate_depth(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->v->nFrame : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -2122,7 +2265,12 @@ int sqlite3_preupdate_depth(sqlite3 *db){
** only.
*/
int sqlite3_preupdate_blobwrite(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->iBlobWrite : -1);
}
#endif
@@ -2133,10 +2281,16 @@ int sqlite3_preupdate_blobwrite(sqlite3 *db){
** a field of the row currently being updated or inserted.
*/
int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
int rc = SQLITE_OK;
Mem *pMem;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
if( !p || p->op==SQLITE_DELETE ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_new_out;
@@ -2215,11 +2369,20 @@ int sqlite3_stmt_scanstatus_v2(
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
- VdbeOp *aOp = p->aOp;
- int nOp = p->nOp;
+ VdbeOp *aOp;
+ int nOp;
ScanStatus *pScan = 0;
int idx;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 || pOut==0
+ || iScanStatusOp<SQLITE_SCANSTAT_NLOOP
+ || iScanStatusOp>SQLITE_SCANSTAT_NCYCLE ){
+ return 1;
+ }
+#endif
+ aOp = p->aOp;
+ nOp = p->nOp;
if( p->pFrame ){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
@@ -2366,7 +2529,7 @@ int sqlite3_stmt_scanstatus(
void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
int ii;
- for(ii=0; ii<p->nOp; ii++){
+ for(ii=0; p!=0 && ii<p->nOp; ii++){
Op *pOp = &p->aOp[ii];
pOp->nExec = 0;
pOp->nCycle = 0;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 225c8d12c..209d02a04 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -211,10 +211,11 @@ static int growOpArray(Vdbe *v, int nOp){
** sqlite3CantopenError(lineno)
*/
static void test_addop_breakpoint(int pc, Op *pOp){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */
}
#endif
@@ -1002,6 +1003,10 @@ void sqlite3VdbeNoJumpsOutsideSubrtn(
int iDest = pOp->p2; /* Jump destination */
if( iDest==0 ) continue;
if( pOp->opcode==OP_Gosub ) continue;
+ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){
+ /* This is a deliberately taken illegal branch. tag-20230325-2 */
+ continue;
+ }
if( iDest<0 ){
int j = ADDR(iDest);
assert( j>=0 );
@@ -1395,6 +1400,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
+ case P4_TABLEREF: {
+ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
+ break;
+ }
}
}
@@ -1522,7 +1531,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full(
int n
){
if( pOp->p4type ){
- freeP4(p->db, pOp->p4type, pOp->p4.p);
+ assert( pOp->p4type > P4_FREE_IF_LE );
pOp->p4type = 0;
pOp->p4.p = 0;
}
@@ -4051,6 +4060,23 @@ static void serialGet(
pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
}
}
+static int serialGet7(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ if( IsNaN(x) ){
+ pMem->flags = MEM_Null;
+ return 1;
+ }
+ pMem->flags = MEM_Real;
+ return 0;
+}
void sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -4461,32 +4487,44 @@ SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
return n1 - n2;
}
+/* The following two functions are used only within testcase() to prove
+** test coverage. These functions do no exist for production builds.
+** We must use separate SQLITE_NOINLINE functions here, since otherwise
+** optimizer code movement causes gcov to become very confused.
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
+static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
+#endif
+
/*
** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
** number. Return negative, zero, or positive if the first (i64) is less than,
** equal to, or greater than the second (double).
*/
int sqlite3IntFloatCompare(i64 i, double r){
- if( sizeof(LONGDOUBLE_TYPE)>8 ){
+ if( sqlite3IsNaN(r) ){
+ /* SQLite considers NaN to be a NULL. And all integer values are greater
+ ** than NULL */
+ return 1;
+ }
+ if( sqlite3Config.bUseLongDouble ){
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
testcase( x<r );
testcase( x>r );
testcase( x==r );
- if( x<r ) return -1;
- if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
- return 0; /*NO_TEST*/ /* work around bugs in gcov */
+ return (x<r) ? -1 : (x>r);
}else{
i64 y;
- double s;
if( r<-9223372036854775808.0 ) return +1;
if( r>=9223372036854775808.0 ) return -1;
y = (i64)r;
if( i<y ) return -1;
if( i>y ) return +1;
- s = (double)i;
- if( s<r ) return -1;
- if( s>r ) return +1;
- return 0;
+ testcase( doubleLt(((double)i),r) );
+ testcase( doubleLt(r,((double)i)) );
+ testcase( doubleEq(r,((double)i)) );
+ return (((double)i)<r) ? -1 : (((double)i)>r);
}
}
@@ -4716,7 +4754,7 @@ int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ serialGet7(&aKey1[d1], &mem1);
rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
}else{
i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
@@ -4741,14 +4779,18 @@ int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else{
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
if( serial_type==7 ){
- if( mem1.u.r<pRhs->u.r ){
+ if( serialGet7(&aKey1[d1], &mem1) ){
+ rc = -1; /* mem1 is a NaN */
+ }else if( mem1.u.r<pRhs->u.r ){
rc = -1;
}else if( mem1.u.r>pRhs->u.r ){
rc = +1;
+ }else{
+ assert( rc==0 );
}
}else{
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
}
}
@@ -4818,7 +4860,14 @@ int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0 && serial_type!=10);
+ if( serial_type==0
+ || serial_type==10
+ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0)
+ ){
+ assert( rc==0 );
+ }else{
+ rc = 1;
+ }
}
if( rc!=0 ){
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 32987da13..522447dbc 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -59,8 +59,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
/* Set the value of register r[1] in the SQL statement to integer iRow.
** This is done directly as a performance optimization
*/
- v->aMem[1].flags = MEM_Int;
- v->aMem[1].u.i = iRow;
+ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);
/* If the statement has been run before (and is paused at the OP_ResultRow)
** then back it up to the point where it does the OP_NotExists. This could
@@ -143,7 +142,7 @@ int sqlite3_blob_open(
#endif
*ppBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
+ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
diff --git a/src/vdbemem.c b/src/vdbemem.c
index e25efc977..cbc6712bf 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -336,7 +336,7 @@ void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
pMem->flags |= MEM_Term;
return;
}
- if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){
+ if( pMem->xDel==sqlite3RCStrUnref ){
/* Blindly assume that all RCStr objects are zero-terminated */
pMem->flags |= MEM_Term;
return;
@@ -1515,7 +1515,7 @@ static int valueFromFunction(
#endif
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0
){
return SQLITE_OK;
}
@@ -1631,14 +1631,20 @@ static int valueFromExpr(
}
/* Handle negative integers in a single step. This is needed in the
- ** case when the value is -9223372036854775808.
- */
- if( op==TK_UMINUS
- && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
- pExpr = pExpr->pLeft;
- op = pExpr->op;
- negInt = -1;
- zNeg = "-";
+ ** case when the value is -9223372036854775808. Except - do not do this
+ ** for hexadecimal literals. */
+ if( op==TK_UMINUS ){
+ Expr *pLeft = pExpr->pLeft;
+ if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){
+ if( ExprHasProperty(pLeft, EP_IntValue)
+ || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X'
+ ){
+ pExpr = pLeft;
+ op = pExpr->op;
+ negInt = -1;
+ zNeg = "-";
+ }
+ }
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
@@ -1647,12 +1653,26 @@ static int valueFromExpr(
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
}else{
- zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
- if( zVal==0 ) goto no_mem;
- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ i64 iVal;
+ if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){
+ sqlite3VdbeMemSetInt64(pVal, iVal*negInt);
+ }else{
+ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
+ if( zVal==0 ) goto no_mem;
+ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ }
}
- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){
- sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ if( affinity==SQLITE_AFF_BLOB ){
+ if( op==TK_FLOAT ){
+ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) );
+ sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8);
+ pVal->flags = MEM_Real;
+ }else if( op==TK_INTEGER ){
+ /* This case is required by -9223372036854775808 and other strings
+ ** that look like integers but cannot be handled by the
+ ** sqlite3DecOrHexToI64() call above. */
+ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ }
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
@@ -1716,6 +1736,7 @@ static int valueFromExpr(
if( pVal ){
pVal->flags = MEM_Int;
pVal->u.i = pExpr->u.zToken[4]==0;
+ sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}
diff --git a/src/vdbesort.c b/src/vdbesort.c
index 2b7da94f7..008369030 100644
--- a/src/vdbesort.c
+++ b/src/vdbesort.c
@@ -186,7 +186,7 @@ struct SorterFile {
struct SorterList {
SorterRecord *pList; /* Linked list of records */
u8 *aMemory; /* If non-NULL, bulk memory to hold pList */
- int szPMA; /* Size of pList as PMA in bytes */
+ i64 szPMA; /* Size of pList as PMA in bytes */
};
/*
@@ -295,10 +295,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
struct SortSubtask {
SQLiteThread *pThread; /* Background thread, if any */
int bDone; /* Set if thread is finished but not joined */
+ int nPMA; /* Number of PMAs currently in file */
VdbeSorter *pSorter; /* Sorter that owns this sub-task */
UnpackedRecord *pUnpacked; /* Space to unpack a record */
SorterList list; /* List for thread to write to a PMA */
- int nPMA; /* Number of PMAs currently in file */
SorterCompare xCompare; /* Compare function to use */
SorterFile file; /* Temp file for level-0 PMAs */
SorterFile file2; /* Space for other PMAs */
@@ -1772,8 +1772,8 @@ int sqlite3VdbeSorterWrite(
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
int bFlush; /* True to flush contents of memory to PMA */
- int nReq; /* Bytes of memory required */
- int nPMA; /* Bytes of PMA space required */
+ i64 nReq; /* Bytes of memory required */
+ i64 nPMA; /* Bytes of PMA space required */
int t; /* serial type of first record field */
assert( pCsr->eCurType==CURTYPE_SORTER );
diff --git a/src/vdbevtab.c b/src/vdbevtab.c
index 59030e0e1..b295dff7b 100644
--- a/src/vdbevtab.c
+++ b/src/vdbevtab.c
@@ -428,7 +428,8 @@ static sqlite3_module bytecodevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
diff --git a/src/vtab.c b/src/vtab.c
index 741518991..67226c63c 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -315,7 +315,6 @@ void sqlite3VtabUnlockList(sqlite3 *db){
if( p ){
db->pDisconnect = 0;
- sqlite3ExpirePreparedStatements(db, 0);
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
@@ -612,6 +611,8 @@ static int vtabCallConstructor(
db->pVtabCtx = &sCtx;
pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>1 || rc!=SQLITE_OK );
sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
@@ -634,7 +635,7 @@ static int vtabCallConstructor(
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
- *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
+ *pzErr = sqlite3MPrintf(db, zFormat, zModuleName);
sqlite3VtabUnlock(pVTable);
rc = SQLITE_ERROR;
}else{
@@ -821,7 +822,7 @@ int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
- sqlite3Error(db, SQLITE_MISUSE);
+ sqlite3Error(db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
diff --git a/src/wal.c b/src/wal.c
index d63c13ffc..fd2eabfd9 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -2004,6 +2004,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+
+
+/*
+** Attempt to enable blocking locks that block for nMs ms. Return 1 if
+** blocking locks are successfully enabled, or 0 otherwise.
+*/
+static int walEnableBlockingMs(Wal *pWal, int nMs){
+ int rc = sqlite3OsFileControl(
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
+ );
+ return (rc==SQLITE_OK);
+}
+
/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
@@ -2015,11 +2028,7 @@ static int walEnableBlocking(Wal *pWal){
if( pWal->db ){
int tmout = pWal->db->busyTimeout;
if( tmout ){
- int rc;
- rc = sqlite3OsFileControl(
- pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
- );
- res = (rc==SQLITE_OK);
+ res = walEnableBlockingMs(pWal, tmout);
}
}
return res;
@@ -2068,20 +2077,10 @@ void sqlite3WalDb(Wal *pWal, sqlite3 *db){
pWal->db = db;
}
-/*
-** Take an exclusive WRITE lock. Blocking if so configured.
-*/
-static int walLockWriter(Wal *pWal){
- int rc;
- walEnableBlocking(pWal);
- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
- walDisableBlocking(pWal);
- return rc;
-}
#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
-# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
+# define walEnableBlockingMs(pWal, ms) 0
# define sqlite3WalDb(pWal, db)
#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
@@ -2682,7 +2681,9 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
}
}else{
int bWriteLock = pWal->writeLock;
- if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
+ if( bWriteLock
+ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
+ ){
pWal->writeLock = 1;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
@@ -2690,7 +2691,8 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
/* If the wal-index header is still malformed even while holding
** a WRITE lock, it can only mean that the header is corrupted and
** needs to be reconstructed. So run recovery to do exactly that.
- */
+ ** Disable blocking locks first. */
+ walDisableBlocking(pWal);
rc = walIndexRecover(pWal);
*pChanged = 1;
}
@@ -2901,6 +2903,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
}
/*
+** The final argument passed to walTryBeginRead() is of type (int*). The
+** caller should invoke walTryBeginRead as follows:
+**
+** int cnt = 0;
+** do {
+** rc = walTryBeginRead(..., &cnt);
+** }while( rc==WAL_RETRY );
+**
+** The final value of "cnt" is of no use to the caller. It is used by
+** the implementation of walTryBeginRead() as follows:
+**
+** + Each time walTryBeginRead() is called, it is incremented. Once
+** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
+** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
+** returns SQLITE_PROTOCOL.
+**
+** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
+** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
+** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
+** the next invocation of walTryBeginRead() may omit an expected call to
+** sqlite3OsSleep(). There has already been a delay when the previous call
+** waited on a lock.
+*/
+#define WAL_RETRY_PROTOCOL_LIMIT 100
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+# define WAL_RETRY_BLOCKED_MASK 0x10000000
+#else
+# define WAL_RETRY_BLOCKED_MASK 0
+#endif
+
+/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
@@ -2950,13 +2983,16 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
-static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
u32 mxFrame; /* Wal frame to lock to */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int nBlockTmout = 0;
+#endif
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -2980,14 +3016,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
** The total delay time before giving up is less than 10 seconds.
*/
- if( cnt>5 ){
+ (*pCnt)++;
+ if( *pCnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
- if( cnt>100 ){
+ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
+ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
+ ** to block for locks for approximately nDelay us. This affects three
+ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
+ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
+ ** first attempted read fails, and (c) the shared lock taken on the
+ ** read-mark.
+ **
+ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
+ ** then sleep for the minimum of 1us. The previous call already provided
+ ** an extra delay while it was blocking on the lock.
+ */
+ nBlockTmout = (nDelay+998) / 1000;
+ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
+ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
+ }
+#endif
sqlite3OsSleep(pWal->pVfs, nDelay);
+ *pCnt &= ~WAL_RETRY_BLOCKED_MASK;
}
if( !useWal ){
@@ -2995,6 +3051,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
if( pWal->bShmUnreliable==0 ){
rc = walIndexReadHdr(pWal, pChanged);
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ walDisableBlocking(pWal);
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ rc = SQLITE_BUSY;
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#endif
if( rc==SQLITE_BUSY ){
/* If there is not a recovery running in another thread or process
** then convert BUSY errors to WAL_RETRY. If recovery is known to
@@ -3109,9 +3172,19 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ walDisableBlocking(pWal);
if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#else
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
+#endif
+ assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
@@ -3299,7 +3372,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){
#endif
do{
- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
+ rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -3480,6 +3553,7 @@ static int walFindFrame(
iRead = iFrame;
}
if( (nCollide--)==0 ){
+ *piRead = 0;
return SQLITE_CORRUPT_BKPT;
}
iKey = walNextHash(iKey);
@@ -3783,7 +3857,7 @@ static int walRestartLog(Wal *pWal){
cnt = 0;
do{
int notUsed;
- rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
+ rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -4204,10 +4278,9 @@ int sqlite3WalCheckpoint(
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
- /* Enable blocking locks, if possible. If blocking locks are successfully
- ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
+ /* Enable blocking locks, if possible. */
sqlite3WalDb(pWal, db);
- (void)walEnableBlocking(pWal);
+ if( xBusy2 ) (void)walEnableBlocking(pWal);
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
@@ -4248,9 +4321,14 @@ int sqlite3WalCheckpoint(
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
+ /* For a passive checkpoint, do not re-enable blocking locks after
+ ** reading the wal-index header. A passive checkpoint should not block
+ ** or invoke the busy handler. The only lock such a checkpoint may
+ ** attempt to obtain is a lock on a read-slot, and it should give up
+ ** immediately and do a partial checkpoint if it cannot obtain it. */
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
- (void)walEnableBlocking(pWal);
+ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
diff --git a/src/where.c b/src/where.c
index 213df4223..9850d22cc 100644
--- a/src/where.c
+++ b/src/where.c
@@ -678,12 +678,22 @@ static void translateColumnToCopy(
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
@@ -1142,13 +1152,17 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
int iCur; /* Cursor for table getting the filter */
IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
+ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */
saved_pIdxEpr = pParse->pIdxEpr;
+ saved_pIdxPartExpr = pParse->pIdxPartExpr;
pParse->pIdxEpr = 0;
+ pParse->pIdxPartExpr = 0;
assert( pLoop!=0 );
assert( v!=0 );
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 );
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
do{
@@ -1238,6 +1252,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
}while( iLevel < pWInfo->nLevel );
sqlite3VdbeJumpHere(v, addrOnce);
pParse->pIdxEpr = saved_pIdxEpr;
+ pParse->pIdxPartExpr = saved_pIdxPartExpr;
}
@@ -2005,7 +2020,8 @@ static int whereRangeScanEst(
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
** if estimated without the use of STAT4 tables. */
- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ if( iLwrIdx==iUprIdx ){ nNew -= 20; }
+ assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
@@ -2229,17 +2245,34 @@ void sqlite3WhereClausePrint(WhereClause *pWC){
#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
+**
+** Format example:
+**
+** .--- Position in WHERE clause rSetup, rRun, nOut ---.
+** | |
+** | .--- selfMask nTerm ------. |
+** | | | |
+** | | .-- prereq Idx wsFlags----. | |
+** | | | Name | | |
+** | | | __|__ nEq ---. ___|__ | __|__
+** | / \ / \ / \ | / \ / \ / \
+** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
*/
-void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
- WhereInfo *pWInfo = pWC->pWInfo;
- int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
- Table *pTab = pItem->pTab;
- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
- sqlite3DebugPrintf(" %12s",
- pItem->zAlias ? pItem->zAlias : pTab->zName);
+void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
+ if( pWC ){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ }else{
+ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
+ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
+ }
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
const char *zName;
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
@@ -2276,6 +2309,15 @@ void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
}
}
}
+void sqlite3ShowWhereLoop(const WhereLoop *p){
+ if( p ) sqlite3WhereLoopPrint(p, 0);
+}
+void sqlite3ShowWhereLoopList(const WhereLoop *p){
+ while( p ){
+ sqlite3ShowWhereLoop(p);
+ p = p->pNextLoop;
+ }
+}
#endif
/*
@@ -2388,46 +2430,60 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
}
/*
-** Return TRUE if all of the following are true:
-**
-** (1) X has the same or lower cost, or returns the same or fewer rows,
-** than Y.
-** (2) X uses fewer WHERE clause terms than Y
-** (3) Every WHERE clause term used by X is also used by Y
-** (4) X skips at least as many columns as Y
-** (5) If X is a covering index, than Y is too
-**
-** Conditions (2) and (3) mean that X is a "proper subset" of Y.
-** If X is a proper subset of Y then Y is a better choice and ought
-** to have a lower cost. This routine returns TRUE when that cost
-** relationship is inverted and needs to be adjusted. Constraint (4)
-** was added because if X uses skip-scan less than Y it still might
-** deserve a lower cost even if it is a proper subset of Y. Constraint (5)
-** was added because a covering index probably deserves to have a lower cost
-** than a non-covering index even if it is a proper subset.
+** Return TRUE if X is a proper subset of Y but is of equal or less cost.
+** In other words, return true if all constraints of X are also part of Y
+** and Y has additional constraints that might speed the search that X lacks
+** but the cost of running X is not more than the cost of running Y.
+**
+** In other words, return true if the cost relationwship between X and Y
+** is inverted and needs to be adjusted.
+**
+** Case 1:
+**
+** (1a) X and Y use the same index.
+** (1b) X has fewer == terms than Y
+** (1c) Neither X nor Y use skip-scan
+** (1d) X does not have a a greater cost than Y
+**
+** Case 2:
+**
+** (2a) X has the same or lower cost, or returns the same or fewer rows,
+** than Y.
+** (2b) X uses fewer WHERE clause terms than Y
+** (2c) Every WHERE clause term used by X is also used by Y
+** (2d) X skips at least as many columns as Y
+** (2e) If X is a covering index, than Y is too
*/
static int whereLoopCheaperProperSubset(
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
+ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
+ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */
+ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */
+ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */
+ ){
+ return 1; /* Case 1 is true */
+ }
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
- return 0; /* X is not a subset of Y */
+ return 0; /* (2b) */
}
- if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0;
- if( pY->nSkip > pX->nSkip ) return 0;
+ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ if( j<0 ) return 0; /* (2c) */
}
if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
- return 0; /* Constraint (5) */
+ return 0; /* (2e) */
}
- return 1; /* All conditions meet */
+ return 1; /* Case 2 is true */
}
/*
@@ -2917,7 +2973,12 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bUnordered || pProbe->bLowQual ){
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
+ opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ }
+ }
assert( pNew->u.btree.nEq<pProbe->nColumn );
assert( pNew->u.btree.nEq<pProbe->nKeyCol
@@ -3303,7 +3364,9 @@ static int indexMightHelpWithOrderBy(
for(ii=0; ii<pOB->nExpr; ii++){
Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
if( NEVER(pExpr==0) ) continue;
- if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN)
+ && pExpr->iTable==iCursor
+ ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
@@ -3498,6 +3561,100 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex(
}
/*
+** This is an sqlite3ParserAddCleanup() callback that is invoked to
+** free the Parse->pIdxEpr list when the Parse object is destroyed.
+*/
+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
+ IndexedExpr **pp = (IndexedExpr**)pObject;
+ while( *pp!=0 ){
+ IndexedExpr *p = *pp;
+ *pp = p->pIENext;
+ sqlite3ExprDelete(db, p->pExpr);
+ sqlite3DbFreeNN(db, p);
+ }
+}
+
+/*
+** This function is called for a partial index - one with a WHERE clause - in
+** two scenarios. In both cases, it determines whether or not the WHERE
+** clause on the index implies that a column of the table may be safely
+** replaced by a constant expression. For example, in the following
+** SELECT:
+**
+** CREATE INDEX i1 ON t1(b, c) WHERE a=<expr>;
+** SELECT a, b, c FROM t1 WHERE a=<expr> AND b=?;
+**
+** The "a" in the select-list may be replaced by <expr>, iff:
+**
+** (a) <expr> is a constant expression, and
+** (b) The (a=<expr>) comparison uses the BINARY collation sequence, and
+** (c) Column "a" has an affinity other than NONE or BLOB.
+**
+** If argument pItem is NULL, then pMask must not be NULL. In this case this
+** function is being called as part of determining whether or not pIdx
+** is a covering index. This function clears any bits in (*pMask)
+** corresponding to columns that may be replaced by constants as described
+** above.
+**
+** Otherwise, if pItem is not NULL, then this function is being called
+** as part of coding a loop that uses index pIdx. In this case, add entries
+** to the Parse.pIdxPartExpr list for each column that can be replaced
+** by a constant.
+*/
+static void wherePartIdxExpr(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Partial index being processed */
+ Expr *pPart, /* WHERE clause being processed */
+ Bitmask *pMask, /* Mask to clear bits in */
+ int iIdxCur, /* Cursor number for index */
+ SrcItem *pItem /* The FROM clause entry for the table */
+){
+ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 );
+ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) );
+
+ if( pPart->op==TK_AND ){
+ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem);
+ pPart = pPart->pLeft;
+ }
+
+ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){
+ Expr *pLeft = pPart->pLeft;
+ Expr *pRight = pPart->pRight;
+ u8 aff;
+
+ if( pLeft->op!=TK_COLUMN ) return;
+ if( !sqlite3ExprIsConstant(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 ){
+ sqlite3 *db = pParse->db;
+ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p));
+ if( p ){
+ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0;
+ p->pExpr = sqlite3ExprDup(db, pRight, 0);
+ p->iDataCur = pItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = pLeft->iColumn;
+ p->bMaybeNullRow = bNullRow;
+ p->pIENext = pParse->pIdxPartExpr;
+ p->aff = aff;
+ pParse->pIdxPartExpr = p;
+ if( p->pIENext==0 ){
+ void *pArg = (void*)&pParse->pIdxPartExpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
+ }
+ }
+ }else if( pLeft->iColumn<(BMS-1) ){
+ *pMask &= ~((Bitmask)1 << pLeft->iColumn);
+ }
+ }
+ }
+}
+
+
+/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
@@ -3700,9 +3857,6 @@ static int whereLoopAddBtree(
#else
pNew->rRun = rSize + 16;
#endif
- if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){
- pNew->wsFlags |= WHERE_VIEWSCAN;
- }
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
rc = whereLoopInsert(pBuilder, pNew);
@@ -3715,6 +3869,11 @@ static int whereLoopAddBtree(
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
+ if( pProbe->pPartIdxWhere ){
+ wherePartIdxExpr(
+ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0
+ );
+ }
pNew->wsFlags = WHERE_INDEXED;
if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){
u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
@@ -3813,7 +3972,7 @@ static int whereLoopAddBtree(
** unique index is used (making the index functionally non-unique)
** then the sqlite_stat1 data becomes important for scoring the
** plan */
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
}
#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
@@ -4097,7 +4256,7 @@ int sqlite3_vtab_rhs_value(
sqlite3_value *pVal = 0;
int rc = SQLITE_OK;
if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
- rc = SQLITE_MISUSE; /* EV: R-30545-25046 */
+ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */
}else{
if( pH->aRhs[iCons]==0 ){
WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
@@ -5121,14 +5280,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
- /* TUNING: A full-scan of a VIEW or subquery in the outer loop
- ** is not so bad. */
- if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){
- rCost += -10;
- nOut += -30;
- WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId));
- }
-
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
@@ -5315,10 +5466,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
- if( pWInfo->pSelect->pOrderBy
- && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){
- pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr;
- }
+ /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */
+ assert( pWInfo->pSelect->pOrderBy==0
+ || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr );
}else{
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
@@ -5657,7 +5807,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
Table *pTab = pItem->pTab;
if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
if( i>=1
&& (pLoop->wsFlags & reqFlags)==reqFlags
/* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
@@ -5679,20 +5829,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
/*
-** This is an sqlite3ParserAddCleanup() callback that is invoked to
-** free the Parse->pIdxEpr list when the Parse object is destroyed.
-*/
-static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
- Parse *pParse = (Parse*)pObject;
- while( pParse->pIdxEpr!=0 ){
- IndexedExpr *p = pParse->pIdxEpr;
- pParse->pIdxEpr = p->pIENext;
- sqlite3ExprDelete(db, p->pExpr);
- sqlite3DbFreeNN(db, p);
- }
-}
-
-/*
** The index pIdx is used by a query and contains one or more expressions.
** In other words pIdx is an index on an expression. iIdxCur is the cursor
** number for the index and iDataCur is the cursor number for the corresponding
@@ -5731,6 +5867,20 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
continue;
}
if( sqlite3ExprIsConstant(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;
+ FuncDef *pDef;
+ sqlite3 *db = pParse->db;
+ assert( ExprUseXList(pExpr) );
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ continue;
+ }
+ }
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxEpr;
@@ -5753,7 +5903,8 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
#endif
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
- sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
+ void *pArg = (void*)&pParse->pIdxEpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
}
}
}
@@ -5908,7 +6059,10 @@ WhereInfo *sqlite3WhereBegin(
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+ if( pOrderBy && pOrderBy->nExpr>=BMS ){
+ pOrderBy = 0;
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -5933,7 +6087,10 @@ WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
+ nByteWInfo = ROUND8P(sizeof(WhereInfo));
+ if( nTabList>1 ){
+ nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
+ }
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
@@ -6143,6 +6300,16 @@ WhereInfo *sqlite3WhereBegin(
wherePathSolver(pWInfo, pWInfo->nRowOut+1);
if( db->mallocFailed ) goto whereBeginError;
}
+
+ /* TUNING: Assume that a DISTINCT clause on a subquery reduces
+ ** the output size by a factor of 8 (LogEst -30).
+ */
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
+ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n",
+ pWInfo->nRowOut, pWInfo->nRowOut-30));
+ pWInfo->nRowOut -= 30;
+ }
+
}
assert( pWInfo->pTabList!=0 );
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
@@ -6355,6 +6522,11 @@ WhereInfo *sqlite3WhereBegin(
if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
}
+ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){
+ wherePartIdxExpr(
+ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem
+ );
+ }
}
pLevel->iIdxCur = iIndexCur;
assert( pIx!=0 );
@@ -6480,6 +6652,11 @@ whereBeginError:
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
+#ifdef WHERETRACE_ENABLED
+ /* Prevent harmless compiler warnings about debugging routines
+ ** being declared but never used */
+ sqlite3ShowWhereLoopList(0);
+#endif /* WHERETRACE_ENABLED */
return 0;
}
diff --git a/src/whereInt.h b/src/whereInt.h
index 759e774e3..f3cc5776a 100644
--- a/src/whereInt.h
+++ b/src/whereInt.h
@@ -502,7 +502,7 @@ Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
void sqlite3WhereClausePrint(WhereClause *pWC);
void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
-void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC);
+void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC);
#endif
WhereTerm *sqlite3WhereFindTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
@@ -633,7 +633,7 @@ void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
-#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */
+ /* 0x02000000 -- available for reuse */
#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */
#endif /* !defined(SQLITE_WHEREINT_H) */
diff --git a/src/window.c b/src/window.c
index d46eabc3b..62df349fb 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1038,7 +1038,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->pWFunc!=0 );
pArgs = pWin->pOwner->x.pList;
- if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
+ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){
selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
pWin->bExprArgs = 1;
@@ -1312,8 +1312,9 @@ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
assert( pWin );
+ assert( ExprIsFullSize(p) );
p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
+ ExprSetProperty(p, EP_WinFunc|EP_FullSize);
pWin->pOwner = p;
if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
sqlite3ErrorMsg(pParse,