aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/alter.c5
-rw-r--r--src/build.c72
-rw-r--r--src/expr.c1
-rw-r--r--src/fkey.c4
-rw-r--r--src/insert.c14
-rw-r--r--src/journal.c2
-rw-r--r--src/main.c11
-rw-r--r--src/memjournal.c362
-rw-r--r--src/os.c2
-rw-r--r--src/os_unix.c13
-rw-r--r--src/pager.c39
-rw-r--r--src/parse.y30
-rw-r--r--src/pragma.c17
-rw-r--r--src/select.c10
-rw-r--r--src/shell.c47
-rw-r--r--src/sqlite.h.in28
-rw-r--r--src/sqliteInt.h24
-rw-r--r--src/sqliteLimit.h11
-rw-r--r--src/tclsqlite.c2
-rw-r--r--src/test1.c48
-rw-r--r--src/test_bestindex.c564
-rw-r--r--src/test_config.c6
-rw-r--r--src/treeview.c6
-rw-r--r--src/util.c15
-rw-r--r--src/vdbeInt.h2
-rw-r--r--src/vdbemem.c2
-rw-r--r--src/vtab.c4
-rw-r--r--src/where.c15
-rw-r--r--src/wherecode.c45
29 files changed, 1140 insertions, 261 deletions
diff --git a/src/alter.c b/src/alter.c
index f10a85022..642c1fb67 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -628,7 +628,8 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** literal NULL, then set pDflt to 0. This simplifies checking
** for an SQL NULL default below.
*/
- if( pDflt && pDflt->op==TK_NULL ){
+ assert( pDflt==0 || pDflt->op==TK_SPAN );
+ if( pDflt && pDflt->pLeft->op==TK_NULL ){
pDflt = 0;
}
@@ -785,9 +786,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
Column *pCol = &pNew->aCol[i];
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
pCol->zColl = 0;
- pCol->zType = 0;
pCol->pDflt = 0;
- pCol->zDflt = 0;
}
pNew->pSchema = db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
diff --git a/src/build.c b/src/build.c
index b14d45f6d..a32dfbd02 100644
--- a/src/build.c
+++ b/src/build.c
@@ -571,8 +571,6 @@ void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
for(i=0; i<pTable->nCol; i++, pCol++){
sqlite3DbFree(db, pCol->zName);
sqlite3ExprDelete(db, pCol->pDflt);
- sqlite3DbFree(db, pCol->zDflt);
- sqlite3DbFree(db, pCol->zType);
sqlite3DbFree(db, pCol->zColl);
}
sqlite3DbFree(db, pTable->aCol);
@@ -1039,10 +1037,11 @@ void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
** first to get things going. Then this routine is called for each
** column.
*/
-void sqlite3AddColumn(Parse *pParse, Token *pName){
+void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
Table *p;
int i;
char *z;
+ char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
if( (p = pParse->pNewTable)==0 ) return;
@@ -1052,8 +1051,14 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
return;
}
#endif
- z = sqlite3NameFromToken(db, pName);
+ z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
if( z==0 ) return;
+ memcpy(z, pName->z, pName->n);
+ z[pName->n] = 0;
+ sqlite3Dequote(z);
+ zType = z + sqlite3Strlen30(z) + 1;
+ memcpy(zType, pType->z, pType->n);
+ zType[pType->n] = 0;
for(i=0; i<p->nCol; i++){
if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
@@ -1075,13 +1080,16 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
pCol->zName = z;
sqlite3ColumnPropertiesFromName(p, pCol);
- /* If there is no type specified, columns have the default affinity
- ** 'BLOB'. If there is a type specified, then sqlite3AddColumnType() will
- ** be called next to set pCol->affinity correctly.
- */
- pCol->affinity = SQLITE_AFF_BLOB;
- pCol->szEst = 1;
+ if( pType->n==0 ){
+ /* If there is no type specified, columns have the default affinity
+ ** 'BLOB'. */
+ pCol->affinity = SQLITE_AFF_BLOB;
+ pCol->szEst = 1;
+ }else{
+ pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
+ }
p->nCol++;
+ pParse->constraintName.n = 0;
}
/*
@@ -1185,28 +1193,6 @@ char sqlite3AffinityType(const char *zIn, u8 *pszEst){
}
/*
-** This routine is called by the parser while in the middle of
-** parsing a CREATE TABLE statement. The pFirst token is the first
-** token in the sequence of tokens that describe the type of the
-** column currently under construction. pLast is the last token
-** in the sequence. Use this information to construct a string
-** that contains the typename of the column and store that string
-** in zType.
-*/
-void sqlite3AddColumnType(Parse *pParse, Token *pType){
- Table *p;
- Column *pCol;
-
- p = pParse->pNewTable;
- if( p==0 || NEVER(p->nCol<1) ) return;
- pCol = &p->aCol[p->nCol-1];
- assert( pCol->zType==0 || CORRUPT_DB );
- sqlite3DbFree(pParse->db, pCol->zType);
- pCol->zType = sqlite3NameFromToken(pParse->db, pType);
- pCol->affinity = sqlite3AffinityType(pCol->zType, &pCol->szEst);
-}
-
-/*
** The expression is the default value for the most recently added column
** of the table currently under construction.
**
@@ -1231,11 +1217,16 @@ void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
** tokens that point to volatile memory. The 'span' of the expression
** is required by pragma table_info.
*/
+ Expr x;
sqlite3ExprDelete(db, pCol->pDflt);
- pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE);
- sqlite3DbFree(db, pCol->zDflt);
- pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
- (int)(pSpan->zEnd - pSpan->zStart));
+ memset(&x, 0, sizeof(x));
+ x.op = TK_SPAN;
+ x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
+ (int)(pSpan->zEnd - pSpan->zStart));
+ x.pLeft = pSpan->pExpr;
+ x.flags = EP_Skip;
+ pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
+ sqlite3DbFree(db, x.u.zToken);
}
}
sqlite3ExprDelete(db, pSpan->pExpr);
@@ -1291,7 +1282,7 @@ void sqlite3AddPrimaryKey(
int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */
){
Table *pTab = pParse->pNewTable;
- char *zType = 0;
+ const char *zName = 0;
int iCol = -1, i;
int nTerm;
if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
@@ -1304,7 +1295,7 @@ void sqlite3AddPrimaryKey(
if( pList==0 ){
iCol = pTab->nCol - 1;
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
- zType = pTab->aCol[iCol].zType;
+ zName = pTab->aCol[iCol].zName;
nTerm = 1;
}else{
nTerm = pList->nExpr;
@@ -1317,7 +1308,7 @@ void sqlite3AddPrimaryKey(
for(iCol=0; iCol<pTab->nCol; iCol++){
if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
pTab->aCol[iCol].colFlags |= COLFLAG_PRIMKEY;
- zType = pTab->aCol[iCol].zType;
+ zName = pTab->aCol[iCol].zName;
break;
}
}
@@ -1325,7 +1316,8 @@ void sqlite3AddPrimaryKey(
}
}
if( nTerm==1
- && zType && sqlite3StrICmp(zType, "INTEGER")==0
+ && zName
+ && sqlite3StrICmp(sqlite3StrNext(zName), "INTEGER")==0
&& sortOrder!=SQLITE_SO_DESC
){
pTab->iPKey = iCol;
diff --git a/src/expr.c b/src/expr.c
index 3070de96f..8a6973219 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -3070,6 +3070,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
sqlite3ReleaseTempReg(pParse, r4);
break;
}
+ case TK_SPAN:
case TK_COLLATE:
case TK_UPLUS: {
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
diff --git a/src/fkey.c b/src/fkey.c
index 97eba1ddd..4f2740ec3 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -1162,6 +1162,10 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
+ return 0;
+ }
+
pTrigger = pFKey->apTrigger[iAction];
if( (db->flags & SQLITE_DeferFKs) && action==OE_Restrict ){
return 0;
diff --git a/src/insert.c b/src/insert.c
index 43e94817f..5dc045ab6 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -2008,11 +2008,15 @@ static int xferOptimization(
return 0; /* tab2 must be NOT NULL if tab1 is */
}
/* Default values for second and subsequent columns need to match. */
- if( i>0
- && ((pDestCol->zDflt==0)!=(pSrcCol->zDflt==0)
- || (pDestCol->zDflt && strcmp(pDestCol->zDflt, pSrcCol->zDflt)!=0))
- ){
- return 0; /* Default values must be the same for all columns */
+ if( i>0 ){
+ assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
+ assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
+ if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
+ || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken,
+ pSrcCol->pDflt->u.zToken)!=0)
+ ){
+ return 0; /* Default values must be the same for all columns */
+ }
}
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
diff --git a/src/journal.c b/src/journal.c
index a5cf8c8e2..da59db0e5 100644
--- a/src/journal.c
+++ b/src/journal.c
@@ -24,6 +24,7 @@
** buffer, or
** 2) The sqlite3JournalCreate() function is called.
*/
+#if 0
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
#include "sqliteInt.h"
@@ -254,3 +255,4 @@ int sqlite3JournalSize(sqlite3_vfs *pVfs){
return (pVfs->szOsFile+sizeof(JournalFile));
}
#endif
+#endif
diff --git a/src/main.c b/src/main.c
index 9a8c156c6..116353abe 100644
--- a/src/main.c
+++ b/src/main.c
@@ -796,8 +796,9 @@ int sqlite3_db_config(sqlite3 *db, int op, ...){
int op; /* The opcode */
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
} aFlagOp[] = {
- { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
- { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
+ { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -2833,6 +2834,9 @@ static int openDatabase(
#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
| SQLITE_CellSizeCk
#endif
+#if defined(SQLITE_ENABLE_FTS3_TOKENIZER)
+ | SQLITE_Fts3Tokenizer
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -3352,7 +3356,8 @@ int sqlite3_table_column_metadata(
** explicitly declared column. Copy meta information from *pCol.
*/
if( pCol ){
- zDataType = pCol->zType;
+ zDataType = sqlite3StrNext(pCol->zName);
+ if( zDataType[0]==0 ) zDataType = 0;
zCollSeq = pCol->zColl;
notnull = pCol->notNull!=0;
primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0;
diff --git a/src/memjournal.c b/src/memjournal.c
index 62594530e..9ecd2a9ba 100644
--- a/src/memjournal.c
+++ b/src/memjournal.c
@@ -21,25 +21,29 @@ typedef struct MemJournal MemJournal;
typedef struct FilePoint FilePoint;
typedef struct FileChunk FileChunk;
-/* Space to hold the rollback journal is allocated in increments of
-** this many bytes.
-**
-** The size chosen is a little less than a power of two. That way,
-** the FileChunk object will have a size that almost exactly fills
-** a power-of-two allocation. This minimizes wasted space in power-of-two
-** memory allocators.
-*/
-#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
-
/*
** The rollback journal is composed of a linked list of these structures.
+**
+** The zChunk array is always at least 8 bytes in size - usually much more.
+** Its actual size is stored in the MemJournal.nChunkSize variable.
*/
struct FileChunk {
FileChunk *pNext; /* Next chunk in the journal */
- u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
+ u8 zChunk[8]; /* Content of this chunk */
};
/*
+** By default, allocate this many bytes of memory for each FileChunk object.
+*/
+#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
+
+/*
+** For chunk size nChunkSize, return the number of bytes that should
+** be allocated for each FileChunk structure.
+*/
+#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
+
+/*
** An instance of this object serves as a cursor into the rollback journal.
** The cursor can be either for reading or writing.
*/
@@ -49,14 +53,23 @@ struct FilePoint {
};
/*
-** This subclass is a subclass of sqlite3_file. Each open memory-journal
+** This structure is a subclass of sqlite3_file. Each open memory-journal
** is an instance of this class.
*/
struct MemJournal {
- sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ int nChunkSize; /* In-memory chunk-size */
+
+ int nBuf; /* Bytes of data before flushing */
+ int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
+
+ int flags; /* xOpen flags */
+ sqlite3_vfs *pVfs; /* The "real" underlying VFS */
+ const char *zJournal; /* Name of the journal file */
+ sqlite3_file *pReal; /* The "real" underlying file descriptor */
};
/*
@@ -70,42 +83,97 @@ static int memjrnlRead(
sqlite_int64 iOfst /* Begin reading at this offset */
){
MemJournal *p = (MemJournal *)pJfd;
- u8 *zOut = zBuf;
- int nRead = iAmt;
- int iChunkOffset;
- FileChunk *pChunk;
-
- /* SQLite never tries to read past the end of a rollback journal file */
- assert( iOfst+iAmt<=p->endpoint.iOffset );
-
- if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
- sqlite3_int64 iOff = 0;
- for(pChunk=p->pFirst;
- ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
- pChunk=pChunk->pNext
- ){
- iOff += JOURNAL_CHUNKSIZE;
- }
+ if( p->pReal ){
+ return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+ }else if( (iAmt+iOfst)>p->endpoint.iOffset ){
+ return SQLITE_IOERR_SHORT_READ;
}else{
- pChunk = p->readpoint.pChunk;
- }
+ u8 *zOut = zBuf;
+ int nRead = iAmt;
+ int iChunkOffset;
+ FileChunk *pChunk;
+
+ if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
+ sqlite3_int64 iOff = 0;
+ for(pChunk=p->pFirst;
+ ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
+ pChunk=pChunk->pNext
+ ){
+ iOff += p->nChunkSize;
+ }
+ }else{
+ pChunk = p->readpoint.pChunk;
+ }
- iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
- do {
- int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
- int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
- memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
- zOut += nCopy;
- nRead -= iSpace;
- iChunkOffset = 0;
- } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
- p->readpoint.iOffset = iOfst+iAmt;
- p->readpoint.pChunk = pChunk;
+ iChunkOffset = (int)(iOfst%p->nChunkSize);
+ do {
+ int iSpace = p->nChunkSize - iChunkOffset;
+ int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
+ memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
+ zOut += nCopy;
+ nRead -= iSpace;
+ iChunkOffset = 0;
+ } while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
+ p->readpoint.iOffset = iOfst+iAmt;
+ p->readpoint.pChunk = pChunk;
+ }
return SQLITE_OK;
}
/*
+** Free the list of FileChunk structures headed at MemJournal.pFirst.
+*/
+static void memjrnlFreeChunks(MemJournal *p){
+ FileChunk *pIter;
+ FileChunk *pNext;
+ for(pIter=p->pFirst; pIter; pIter=pNext){
+ pNext = pIter->pNext;
+ sqlite3_free(pIter);
+ }
+ p->pFirst = 0;
+}
+
+/*
+** Flush the contents of memory to a real file on disk.
+*/
+static int createFile(MemJournal *p){
+ int rc = SQLITE_OK;
+ if( !p->pReal ){
+ sqlite3_file *pReal = (sqlite3_file *)&p[1];
+ rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
+ if( rc==SQLITE_OK ){
+ int nChunk = p->nChunkSize;
+ i64 iOff = 0;
+ FileChunk *pIter;
+ p->pReal = pReal;
+ for(pIter=p->pFirst; pIter && rc==SQLITE_OK; pIter=pIter->pNext){
+ int nWrite = nChunk;
+ if( pIter==p->endpoint.pChunk ){
+ nWrite = p->endpoint.iOffset % p->nChunkSize;
+ if( nWrite==0 ) nWrite = p->nChunkSize;
+ }
+ rc = sqlite3OsWrite(pReal, pIter->zChunk, nWrite, iOff);
+ iOff += nWrite;
+ }
+ if( rc!=SQLITE_OK ){
+ /* If an error occurred while writing to the file, close it before
+ ** returning. This way, SQLite uses the in-memory journal data to
+ ** roll back changes made to the internal page-cache before this
+ ** function was called. */
+ sqlite3OsClose(pReal);
+ p->pReal = 0;
+ }else{
+ /* No error has occurred. Free the in-memory buffers. */
+ memjrnlFreeChunks(p);
+ }
+ }
+ }
+ return rc;
+}
+
+
+/*
** Write data to the file.
*/
static int memjrnlWrite(
@@ -118,38 +186,61 @@ static int memjrnlWrite(
int nWrite = iAmt;
u8 *zWrite = (u8 *)zBuf;
- /* An in-memory journal file should only ever be appended to. Random
- ** access writes are not required by sqlite.
- */
- assert( iOfst==p->endpoint.iOffset );
- UNUSED_PARAMETER(iOfst);
-
- while( nWrite>0 ){
- FileChunk *pChunk = p->endpoint.pChunk;
- int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
- int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
-
- if( iChunkOffset==0 ){
- /* New chunk is required to extend the file. */
- FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
- if( !pNew ){
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- pNew->pNext = 0;
- if( pChunk ){
- assert( p->pFirst );
- pChunk->pNext = pNew;
- }else{
- assert( !p->pFirst );
- p->pFirst = pNew;
- }
- p->endpoint.pChunk = pNew;
+ /* If the file has already been created on disk. */
+ if( p->pReal ){
+ return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+ }
+
+ /* If the file should be created now. */
+ else if( p->nBuf>0 && (iAmt+iOfst)>p->nBuf ){
+ int rc = createFile(p);
+ if( rc==SQLITE_OK ){
+ rc = memjrnlWrite(pJfd, zBuf, iAmt, iOfst);
}
+ return rc;
+ }
+
+ /* If the contents of this write should be stored in memory */
+ else{
+ /* An in-memory journal file should only ever be appended to. Random
+ ** access writes are not required. The only exception to this is when
+ ** the in-memory journal is being used by a connection using the
+ ** atomic-write optimization. In this case the first 28 bytes of the
+ ** journal file may be written as part of committing the transaction. */
+ assert( iOfst==p->endpoint.iOffset || iOfst==0 );
+ if( iOfst==0 && p->pFirst ){
+ assert( p->nChunkSize>iAmt );
+ memcpy(p->pFirst->zChunk, zBuf, iAmt);
+ }else{
+ while( nWrite>0 ){
+ FileChunk *pChunk = p->endpoint.pChunk;
+ int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
+ int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
- memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
- zWrite += iSpace;
- nWrite -= iSpace;
- p->endpoint.iOffset += iSpace;
+ if( iChunkOffset==0 ){
+ /* New chunk is required to extend the file. */
+ FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
+ if( !pNew ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ pNew->pNext = 0;
+ if( pChunk ){
+ assert( p->pFirst );
+ pChunk->pNext = pNew;
+ }else{
+ assert( !p->pFirst );
+ p->pFirst = pNew;
+ }
+ p->endpoint.pChunk = pNew;
+ }
+
+ memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
+ zWrite += iSpace;
+ nWrite -= iSpace;
+ p->endpoint.iOffset += iSpace;
+ }
+ p->nSize = iAmt + iOfst;
+ }
}
return SQLITE_OK;
@@ -157,19 +248,23 @@ static int memjrnlWrite(
/*
** Truncate the file.
+**
+** If the journal file is already on disk, truncate it there. Or, if it
+** is still in main memory but is being truncated to zero bytes in size,
+** ignore
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- FileChunk *pChunk;
- assert(size==0);
- UNUSED_PARAMETER(size);
- pChunk = p->pFirst;
- while( pChunk ){
- FileChunk *pTmp = pChunk;
- pChunk = pChunk->pNext;
- sqlite3_free(pTmp);
+ if( p->pReal ){
+ return sqlite3OsTruncate(p->pReal, size);
+ }else if( size==0 ){
+ memjrnlFreeChunks(p);
+ p->nSize = 0;
+ p->endpoint.pChunk = 0;
+ p->endpoint.iOffset = 0;
+ p->readpoint.pChunk = 0;
+ p->readpoint.iOffset = 0;
}
- sqlite3MemJournalOpen(pJfd);
return SQLITE_OK;
}
@@ -177,21 +272,23 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
** Close the file.
*/
static int memjrnlClose(sqlite3_file *pJfd){
- memjrnlTruncate(pJfd, 0);
+ MemJournal *p = (MemJournal *)pJfd;
+ memjrnlFreeChunks(p);
+ if( p->pReal ) sqlite3OsClose(p->pReal);
return SQLITE_OK;
}
-
/*
** Sync the file.
**
-** Syncing an in-memory journal is a no-op. And, in fact, this routine
-** is never called in a working implementation. This implementation
-** exists purely as a contingency, in case some malfunction in some other
-** part of SQLite causes Sync to be called by mistake.
+** If the real file has been created, call its xSync method. Otherwise,
+** syncing an in-memory journal is a no-op.
*/
-static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
+static int memjrnlSync(sqlite3_file *pJfd, int flags){
+ MemJournal *p = (MemJournal *)pJfd;
+ if( p->pReal ){
+ return sqlite3OsSync(p->pReal, flags);
+ }
return SQLITE_OK;
}
@@ -200,6 +297,9 @@ static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
*/
static int memjrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
MemJournal *p = (MemJournal *)pJfd;
+ if( p->pReal ){
+ return sqlite3OsFileSize(p->pReal, pSize);
+ }
*pSize = (sqlite_int64) p->endpoint.iOffset;
return SQLITE_OK;
}
@@ -230,26 +330,86 @@ static const struct sqlite3_io_methods MemJournalMethods = {
};
/*
-** Open a journal file.
+** Open a journal file.
+**
+** The behaviour of the journal file depends on the value of parameter
+** nBuf. If nBuf is 0, then the journal file is always create and
+** accessed using the underlying VFS. If nBuf is less than zero, then
+** all content is always stored in main-memory. Finally, if nBuf is a
+** positive value, then the journal file is initially created in-memory
+** but may be flushed to disk later on. In this case the journal file is
+** flushed to disk either when it grows larger than nBuf bytes in size,
+** or when sqlite3JournalCreate() is called.
+*/
+int sqlite3JournalOpen(
+ sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
+ const char *zName, /* Name of the journal file */
+ sqlite3_file *pJfd, /* Preallocated, blank file handle */
+ int flags, /* Opening flags */
+ int nBuf /* Bytes buffered before opening the file */
+){
+ MemJournal *p = (MemJournal*)pJfd;
+
+ /* Zero the file-handle object. If nBuf was passed zero, initialize
+ ** it using the sqlite3OsOpen() function of the underlying VFS. In this
+ ** case none of the code in this module is executed as a result of calls
+ ** made on the journal file-handle. */
+ memset(p, 0, sizeof(MemJournal) + (pVfs ? pVfs->szOsFile : 0));
+ if( nBuf==0 ){
+ return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ }
+
+ if( nBuf>0 ){
+ p->nChunkSize = nBuf;
+ }else{
+ p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
+ assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
+ }
+
+ p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
+ p->nBuf = nBuf;
+ p->flags = flags;
+ p->zJournal = zName;
+ p->pVfs = pVfs;
+ return SQLITE_OK;
+}
+
+/*
+** Open an in-memory journal file.
*/
void sqlite3MemJournalOpen(sqlite3_file *pJfd){
- MemJournal *p = (MemJournal *)pJfd;
- assert( EIGHT_BYTE_ALIGNMENT(p) );
- memset(p, 0, sqlite3MemJournalSize());
- p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
+ sqlite3JournalOpen(0, 0, pJfd, 0, -1);
+}
+
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+/*
+** If the argument p points to a MemJournal structure that is not an
+** in-memory-only journal file (i.e. is one that was opened with a +ve
+** nBuf parameter), and the underlying file has not yet been created,
+** create it now.
+*/
+int sqlite3JournalCreate(sqlite3_file *p){
+ int rc = SQLITE_OK;
+ if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nBuf>0 ){
+ rc = createFile((MemJournal*)p);
+ }
+ return rc;
}
+#endif
/*
-** Return true if the file-handle passed as an argument is
-** an in-memory journal
+** The file-handle passed as the only argument is open on a journal file.
+** Return true if this "journal file" is currently stored in heap memory,
+** or false otherwise.
*/
-int sqlite3IsMemJournal(sqlite3_file *pJfd){
- return pJfd->pMethods==&MemJournalMethods;
+int sqlite3JournalIsInMemory(sqlite3_file *p){
+ return p->pMethods==&MemJournalMethods && ((MemJournal*)p)->pReal==0;
}
/*
-** Return the number of bytes required to store a MemJournal file descriptor.
+** Return the number of bytes required to store a JournalFile that uses vfs
+** pVfs to create the underlying on-disk files.
*/
-int sqlite3MemJournalSize(void){
- return sizeof(MemJournal);
+int sqlite3JournalSize(sqlite3_vfs *pVfs){
+ return pVfs->szOsFile + sizeof(MemJournal);
}
diff --git a/src/os.c b/src/os.c
index 2fd44abf8..eed782853 100644
--- a/src/os.c
+++ b/src/os.c
@@ -66,7 +66,7 @@ int sqlite3_open_file_count = 0;
#if defined(SQLITE_TEST)
int sqlite3_memdebug_vfs_oom_test = 1;
#define DO_OS_MALLOC_TEST(x) \
- if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \
+ if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
void *pTstAlloc = sqlite3Malloc(10); \
if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \
sqlite3_free(pTstAlloc); \
diff --git a/src/os_unix.c b/src/os_unix.c
index ea07bd99f..d6fbd8058 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -71,6 +71,19 @@
# endif
#endif
+/* Use pread() and pwrite() if they are available */
+#if defined(__APPLE__)
+# define HAVE_PREAD 1
+# define HAVE_PWRITE 1
+#endif
+#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
+# undef USE_PREAD
+# define USE_PREAD64 1
+#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE)
+# undef USE_PREAD64
+# define USE_PREAD 1
+#endif
+
/*
** standard include files.
*/
diff --git a/src/pager.c b/src/pager.c
index 51bd45e48..c74748c8e 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -1343,6 +1343,7 @@ static i64 journalHdrOffset(Pager *pPager){
static int zeroJournalHdr(Pager *pPager, int doTruncate){
int rc = SQLITE_OK; /* Return code */
assert( isOpen(pPager->jfd) );
+ assert( !sqlite3JournalIsInMemory(pPager->jfd) );
if( pPager->journalOff ){
const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */
@@ -1724,7 +1725,7 @@ static void releaseAllSavepoints(Pager *pPager){
for(ii=0; ii<pPager->nSavepoint; ii++){
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
}
- if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
+ if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){
sqlite3OsClose(pPager->sjfd);
}
sqlite3_free(pPager->aSavepoint);
@@ -1962,8 +1963,8 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
assert( !pagerUseWal(pPager) );
/* Finalize the journal file. */
- if( sqlite3IsMemJournal(pPager->jfd) ){
- assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
+ if( sqlite3JournalIsInMemory(pPager->jfd) ){
+ /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */
sqlite3OsClose(pPager->jfd);
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
if( pPager->journalOff==0 ){
@@ -1989,9 +1990,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
/* This branch may be executed with Pager.journalMode==MEMORY if
** a hot-journal was just rolled back. In this case the journal
** file should be closed and deleted. If this connection writes to
- ** the database file, it will do so using an in-memory journal.
+ ** the database file, it will do so using an in-memory journal.
*/
- int bDelete = (!pPager->tempFile && sqlite3JournalExists(pPager->jfd));
+ int bDelete = !pPager->tempFile;
+ assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
@@ -2729,7 +2731,7 @@ static int pager_playback(Pager *pPager, int isHot){
** TODO: Technically the following is an error because it assumes that
** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
- ** mxPathname is 512, which is the same as the minimum allowable value
+ ** mxPathname is 512, which is the same as the minimum allowable value
** for pageSize.
*/
zMaster = pPager->pTmpSpace;
@@ -4353,11 +4355,14 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
static int openSubJournal(Pager *pPager){
int rc = SQLITE_OK;
if( !isOpen(pPager->sjfd) ){
+ const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
+ | SQLITE_OPEN_DELETEONCLOSE;
+ int nBuf = 64*1024;
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
- sqlite3MemJournalOpen(pPager->sjfd);
- }else{
- rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
+ nBuf = -1;
}
+ rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nBuf);
}
return rc;
}
@@ -4578,18 +4583,8 @@ int sqlite3PagerOpen(
int nUri = 0; /* Number of bytes of URI args at *zUri */
/* Figure out how much space is required for each journal file-handle
- ** (there are two of them, the main journal and the sub-journal). This
- ** is the maximum space required for an in-memory journal file handle
- ** and a regular journal file-handle. Note that a "regular journal-handle"
- ** may be a wrapper capable of caching the first portion of the journal
- ** file in memory to implement the atomic-write optimization (see
- ** source file journal.c).
- */
- if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
- journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
- }else{
- journalFileSize = ROUND8(sqlite3MemJournalSize());
- }
+ ** (there are two of them, the main journal and the sub-journal). */
+ journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
/* Set the output variable to NULL in case an error occurs. */
*ppPager = 0;
@@ -6667,7 +6662,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
if( op==SAVEPOINT_RELEASE ){
if( nNew==0 && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
- if( sqlite3IsMemJournal(pPager->sjfd) ){
+ if( sqlite3JournalIsInMemory(pPager->sjfd) ){
rc = sqlite3OsTruncate(pPager->sjfd, 0);
assert( rc==SQLITE_OK );
}
diff --git a/src/parse.y b/src/parse.y
index e7e0d1d95..20492edb8 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -190,22 +190,9 @@ table_options(A) ::= WITHOUT nm(X). {
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", X.n, X.z);
}
}
-columnlist ::= columnlist COMMA column.
-columnlist ::= column.
-
-// A "column" is a complete description of a single column in a
-// CREATE TABLE statement. This includes the column name, its
-// datatype, and other keywords such as PRIMARY KEY, UNIQUE, REFERENCES,
-// NOT NULL and so forth.
-//
-column(A) ::= columnid(A) type carglist. {
- A.n = (int)(pParse->sLastToken.z-A.z) + pParse->sLastToken.n;
-}
-columnid(A) ::= nm(A). {
- sqlite3AddColumn(pParse,&A);
- pParse->constraintName.n = 0;
-}
-
+columnlist ::= columnlist COMMA columnname carglist.
+columnlist ::= columnname carglist.
+columnname(A) ::= nm(A) typetoken(Y). {sqlite3AddColumn(pParse,&A,&Y);}
// An IDENTIFIER can be a generic identifier, or one of several
// keywords. Any non-standard keyword can also be an identifier.
@@ -264,13 +251,12 @@ nm(A) ::= id(A).
nm(A) ::= STRING(A).
nm(A) ::= JOIN_KW(A).
-// A typetoken is really one or more tokens that form a type name such
+// A typetoken is really zero or more tokens that form a type name such
// as can be found after the column name in a CREATE TABLE statement.
// Multiple tokens are concatenated to form the value of the typetoken.
//
%type typetoken {Token}
-type ::= .
-type ::= typetoken(X). {sqlite3AddColumnType(pParse,&X);}
+typetoken(A) ::= . {A.n = 0; A.z = 0;}
typetoken(A) ::= typename(A).
typetoken(A) ::= typename(A) LP signed RP(Y). {
A.n = (int)(&Y.z[Y.n] - A.z);
@@ -580,7 +566,7 @@ selcollist(A) ::= sclp(A) nm(X) DOT STAR(Y). {
%type as {Token}
as(X) ::= AS nm(Y). {X = Y;}
as(X) ::= ids(X).
-as(X) ::= . {X.n = 0;}
+as(X) ::= . {X.n = 0; X.z = 0;}
%type seltablist {SrcList*}
@@ -1499,7 +1485,9 @@ cmd ::= ANALYZE nm(X) dbnm(Y). {sqlite3Analyze(pParse, &X, &Y);}
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
sqlite3AlterRenameTable(pParse,X,&Z);
}
-cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column(Y). {
+cmd ::= ALTER TABLE add_column_fullname
+ ADD kwcolumn_opt columnname(Y) carglist. {
+ Y.n = (int)(pParse->sLastToken.z-Y.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &Y);
}
add_column_fullname ::= fullname(X). {
diff --git a/src/pragma.c b/src/pragma.c
index c34d5421c..1d6291431 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1065,6 +1065,7 @@ void sqlite3Pragma(
setAllColumnNames(v, 6, azCol); assert( 6==ArraySize(azCol) );
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
+ const char *zName;
if( IsHiddenColumn(pCol) ){
nHidden++;
continue;
@@ -1076,12 +1077,14 @@ void sqlite3Pragma(
}else{
for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
}
+ assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
+ zName = pCol->zName;
sqlite3VdbeMultiLoad(v, 1, "issisi",
i-nHidden,
- pCol->zName,
- pCol->zType ? pCol->zType : "",
+ zName,
+ sqlite3StrNext(zName),
pCol->notNull ? 1 : 0,
- pCol->zDflt,
+ pCol->pDflt ? pCol->pDflt->u.zToken : 0,
k);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
@@ -1102,14 +1105,14 @@ void sqlite3Pragma(
sqlite3VdbeMultiLoad(v, 1, "ssii",
pTab->zName,
0,
- (int)sqlite3LogEstToInt(pTab->szTabRow),
- (int)sqlite3LogEstToInt(pTab->nRowLogEst));
+ pTab->szTabRow,
+ pTab->nRowLogEst);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
sqlite3VdbeMultiLoad(v, 2, "sii",
pIdx->zName,
- (int)sqlite3LogEstToInt(pIdx->szIdxRow),
- (int)sqlite3LogEstToInt(pIdx->aiRowLogEst[0]));
+ pIdx->szIdxRow,
+ pIdx->aiRowLogEst[0]);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
}
}
diff --git a/src/select.c b/src/select.c
index bde278b43..c9bc389b2 100644
--- a/src/select.c
+++ b/src/select.c
@@ -1429,8 +1429,8 @@ static const char *columnTypeImpl(
zType = "INTEGER";
zOrigCol = "rowid";
}else{
- zType = pTab->aCol[iCol].zType;
zOrigCol = pTab->aCol[iCol].zName;
+ zType = sqlite3StrNext(zOrigCol);
estWidth = pTab->aCol[iCol].szEst;
}
zOrigTab = pTab->zName;
@@ -1442,7 +1442,7 @@ static const char *columnTypeImpl(
if( iCol<0 ){
zType = "INTEGER";
}else{
- zType = pTab->aCol[iCol].zType;
+ zType = sqlite3StrNext(pTab->aCol[iCol].zName);
estWidth = pTab->aCol[iCol].szEst;
}
#endif
@@ -1727,10 +1727,7 @@ static void selectAddColumnTypeAndCollation(
a = pSelect->pEList->a;
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
p = a[i].pExpr;
- if( pCol->zType==0 ){
- pCol->zType = sqlite3DbStrDup(db,
- columnType(&sNC, p,0,0,0, &pCol->szEst));
- }
+ columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
szAll += pCol->szEst;
pCol->affinity = sqlite3ExprAffinity(p);
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
@@ -2225,7 +2222,6 @@ static int multiSelect(
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
- sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
dest.eDest = SRT_Table;
}
diff --git a/src/shell.c b/src/shell.c
index 1cf9eb447..2ce2f292d 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1301,6 +1301,43 @@ static char *save_err_msg(
return zErrMsg;
}
+#ifdef __linux__
+/*
+** Attempt to display I/O stats on Linux using /proc/PID/io
+*/
+static void displayLinuxIoStats(FILE *out){
+ FILE *in;
+ char z[200];
+ sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
+ in = fopen(z, "rb");
+ if( in==0 ) return;
+ while( fgets(z, sizeof(z), in)!=0 ){
+ static const struct {
+ const char *zPattern;
+ const char *zDesc;
+ } aTrans[] = {
+ { "rchar: ", "Bytes received by read():" },
+ { "wchar: ", "Bytes sent to write():" },
+ { "syscr: ", "Read() system calls:" },
+ { "syscw: ", "Write() system calls:" },
+ { "read_bytes: ", "Bytes read from storage:" },
+ { "write_bytes: ", "Bytes written to storage:" },
+ { "cancelled_write_bytes: ", "Cancelled write bytes:" },
+ };
+ int i;
+ for(i=0; i<ArraySize(aTrans); i++){
+ int n = (int)strlen(aTrans[i].zPattern);
+ if( strncmp(aTrans[i].zPattern, z, n)==0 ){
+ raw_printf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
+ break;
+ }
+ }
+ }
+ fclose(in);
+}
+#endif
+
+
/*
** Display memory stats.
*/
@@ -1423,6 +1460,10 @@ static int display_stats(
raw_printf(pArg->out, "Virtual Machine Steps: %d\n", iCur);
}
+#ifdef __linux__
+ displayLinuxIoStats(pArg->out);
+#endif
+
/* Do not remove this machine readable comment: extra-stats-output-here */
return 0;
@@ -1977,7 +2018,7 @@ static char zHelp[] =
#endif
".shell CMD ARGS... Run CMD ARGS... in a system shell\n"
".show Show the current values for various settings\n"
- ".stats on|off Turn stats on or off\n"
+ ".stats ?on|off? Show stats or turn stats on or off\n"
".system CMD ARGS... Run CMD ARGS... in a system shell\n"
".tables ?TABLE? List names of tables\n"
" If TABLE specified, only list tables matching\n"
@@ -4116,8 +4157,10 @@ static int do_meta_command(char *zLine, ShellState *p){
if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
if( nArg==2 ){
p->statsOn = booleanValue(azArg[1]);
+ }else if( nArg==1 ){
+ display_stats(p->db, p, 0);
}else{
- raw_printf(stderr, "Usage: .stats on|off\n");
+ raw_printf(stderr, "Usage: .stats ?on|off?\n");
rc = 1;
}
}else
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index a790d7797..31202ecc2 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -1228,7 +1228,7 @@ struct sqlite3_vfs {
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
- ** New fields may be appended in figure versions. The iVersion
+ ** New fields may be appended in future versions. The iVersion
** value will increment whenever this happens.
*/
};
@@ -1904,11 +1904,25 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
+** <dd> ^This option is used to enable or disable the two-argument
+** version of the [fts3_tokenizer()] function which is part of the
+** [FTS3] full-text search engine extension.
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable fts3_tokenizer() or
+** positive to enable fts3_tokenizer() or negative to leave the setting
+** unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the new setting is not reported back. </dd>
+**
** </dl>
*/
-#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
-#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
-#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
/*
@@ -8049,7 +8063,11 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
** the first operation, apart from other sqlite3_snapshot_open() calls,
** following the [BEGIN] that starts a new read transaction.
** ^A [snapshot] will fail to open if it has been overwritten by a
-** [checkpoint].
+** [checkpoint].
+** ^A [snapshot] will fail to open if the database connection D has not
+** previously completed at least one read operation against the database
+** file. (Hint: Run "[PRAGMA application_id]" against a newly opened
+** database connection in order to make it ready to use snapshots.)
**
** The [sqlite3_snapshot_open()] interface is only available when the
** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 5ac33b4de..55e751574 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1335,6 +1335,7 @@ struct sqlite3 {
#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
#define SQLITE_Vacuum 0x08000000 /* Currently in a VACUUM */
#define SQLITE_CellSizeCk 0x10000000 /* Check btree cell sizes on load */
+#define SQLITE_Fts3Tokenizer 0x20000000 /* Enable fts3_tokenizer(2) */
/*
@@ -1550,10 +1551,8 @@ struct Module {
** of this structure.
*/
struct Column {
- char *zName; /* Name of this column */
+ char *zName; /* Name of this column, \000, then the type */
Expr *pDflt; /* Default value of this column */
- char *zDflt; /* Original text of the default value */
- char *zType; /* Data type for this column */
char *zColl; /* Collating sequence. If NULL, use the default */
u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
@@ -3270,6 +3269,7 @@ int sqlite3IsIdChar(u8);
*/
int sqlite3StrICmp(const char*,const char*);
int sqlite3Strlen30(const char*);
+const char *sqlite3StrNext(const char*);
#define sqlite3StrNICmp sqlite3_strnicmp
int sqlite3MallocInit(void);
@@ -3425,11 +3425,10 @@ void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
#else
# define sqlite3ColumnPropertiesFromName(T,C) /* no-op */
#endif
-void sqlite3AddColumn(Parse*,Token*);
+void sqlite3AddColumn(Parse*,Token*,Token*);
void sqlite3AddNotNull(Parse*, int);
void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
void sqlite3AddCheckConstraint(Parse*, Expr*);
-void sqlite3AddColumnType(Parse*,Token*);
void sqlite3AddDefaultValue(Parse*,ExprSpan*);
void sqlite3AddCollateType(Parse*, Token*);
void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
@@ -3698,7 +3697,11 @@ LogEst sqlite3LogEstAdd(LogEst,LogEst);
#ifndef SQLITE_OMIT_VIRTUALTABLE
LogEst sqlite3LogEstFromDouble(double);
#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTAT) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
u64 sqlite3LogEstToInt(LogEst);
+#endif
/*
** Routines to read and write variable-length integers. These used to
@@ -4007,19 +4010,14 @@ const char *sqlite3JournalModename(int);
#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
int sqlite3FindInIndex(Parse *, Expr *, u32, int*);
+int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
+int sqlite3JournalSize(sqlite3_vfs *);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
- int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
- int sqlite3JournalSize(sqlite3_vfs *);
int sqlite3JournalCreate(sqlite3_file *);
- int sqlite3JournalExists(sqlite3_file *p);
-#else
- #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
- #define sqlite3JournalExists(p) 1
#endif
+int sqlite3JournalIsInMemory(sqlite3_file *p);
void sqlite3MemJournalOpen(sqlite3_file *);
-int sqlite3MemJournalSize(void);
-int sqlite3IsMemJournal(sqlite3_file *);
void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p);
#if SQLITE_MAX_EXPR_DEPTH>0
diff --git a/src/sqliteLimit.h b/src/sqliteLimit.h
index 75cad1274..4b5ddaade 100644
--- a/src/sqliteLimit.h
+++ b/src/sqliteLimit.h
@@ -102,12 +102,12 @@
** the main database table and for temporary tables.
**
** IMPLEMENTATION-OF: R-31093-59126 The default suggested cache size
-** is 2000 pages.
+** is 2000*1024 bytes.
** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be
** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options.
*/
#ifndef SQLITE_DEFAULT_CACHE_SIZE
-# define SQLITE_DEFAULT_CACHE_SIZE 2000
+# define SQLITE_DEFAULT_CACHE_SIZE -2000
#endif
/*
@@ -120,8 +120,9 @@
/*
** The maximum number of attached databases. This must be between 0
-** and 62. The upper bound on 62 is because a 64-bit integer bitmap
-** is used internally to track attached databases.
+** and 125. The upper bound of 125 is because the attached databases are
+** counted using a signed 8-bit integer which has a maximum value of 127
+** and we have to allow 2 extra counts for the "main" and "temp" databases.
*/
#ifndef SQLITE_MAX_ATTACHED
# define SQLITE_MAX_ATTACHED 10
@@ -156,7 +157,7 @@
** The default size of a database page.
*/
#ifndef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE 1024
+# define SQLITE_DEFAULT_PAGE_SIZE 4096
#endif
#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
# undef SQLITE_DEFAULT_PAGE_SIZE
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index e555cd6de..164664a6e 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -3923,6 +3923,7 @@ static void init_all(Tcl_Interp *interp){
#endif
extern int Fts5tcl_Init(Tcl_Interp *);
extern int SqliteRbu_Init(Tcl_Interp*);
+ extern int Sqlitetesttcl_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
#endif
@@ -3970,6 +3971,7 @@ static void init_all(Tcl_Interp *interp){
#endif
Fts5tcl_Init(interp);
SqliteRbu_Init(interp);
+ Sqlitetesttcl_Init(interp);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp);
diff --git a/src/test1.c b/src/test1.c
index 713152b82..744b400b2 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -6921,6 +6921,53 @@ static int test_register_dbstat_vtab(
}
/*
+** tclcmd: sqlite3_db_config DB SETTING VALUE
+**
+** Invoke sqlite3_db_config() for one of the setting values.
+*/
+static int test_sqlite3_db_config(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ static const struct {
+ const char *zName;
+ int eVal;
+ } aSetting[] = {
+ { "FKEY", SQLITE_DBCONFIG_ENABLE_FKEY },
+ { "TRIGGER", SQLITE_DBCONFIG_ENABLE_TRIGGER },
+ { "FTS3_TOKENIZER", SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER },
+ };
+ int i;
+ int v;
+ const char *zSetting;
+ sqlite3 *db;
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB SETTING VALUE");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zSetting = Tcl_GetString(objv[2]);
+ if( sqlite3_strglob("SQLITE_*", zSetting)==0 ) zSetting += 7;
+ if( sqlite3_strglob("DBCONFIG_*", zSetting)==0 ) zSetting += 9;
+ if( sqlite3_strglob("ENABLE_*", zSetting)==0 ) zSetting += 7;
+ for(i=0; i<ArraySize(aSetting); i++){
+ if( strcmp(zSetting, aSetting[i].zName)==0 ) break;
+ }
+ if( i>=ArraySize(aSetting) ){
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj("unknown sqlite3_db_config setting", -1));
+ return TCL_ERROR;
+ }
+ if( Tcl_GetIntFromObj(interp, objv[3], &v) ) return TCL_ERROR;
+ sqlite3_db_config(db, aSetting[i].eVal, v, &v);
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(v));
+ return TCL_OK;
+}
+
+/*
** Register commands with the TCL interpreter.
*/
int Sqlitetest1_Init(Tcl_Interp *interp){
@@ -6989,6 +7036,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
Tcl_ObjCmdProc *xProc;
void *clientData;
} aObjCmd[] = {
+ { "sqlite3_db_config", test_sqlite3_db_config, 0 },
{ "bad_behavior", test_bad_behavior, (void*)&iZero },
{ "register_dbstat_vtab", test_register_dbstat_vtab },
{ "sqlite3_connection_pointer", get_sqlite_pointer, 0 },
diff --git a/src/test_bestindex.c b/src/test_bestindex.c
new file mode 100644
index 000000000..ab10463fd
--- /dev/null
+++ b/src/test_bestindex.c
@@ -0,0 +1,564 @@
+/*
+** 2016-03-01
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Code for testing the virtual table xBestIndex method and the query
+** planner.
+*/
+
+
+/*
+** INSTRUCTIONS
+**
+** This module exports a single tcl command - [register_tcl_module]. When
+** invoked, it registers a special virtual table module with a database
+** connection.
+**
+** The virtual table is currently read-only. And always returns zero rows.
+** It is created with a single argument - the name of a Tcl command - as
+** follows:
+**
+** CREATE VIRTUAL TABLE x1 USING tcl(tcl_command);
+**
+** The command [tcl_command] is invoked when the table is first created (or
+** connected), when the xBestIndex() method is invoked and when the xFilter()
+** method is called. When it is created (or connected), it is invoked as
+** follows:
+**
+** tcl_command xConnect
+**
+** In this case the return value of the script is passed to the
+** sqlite3_declare_vtab() function to create the virtual table schema.
+**
+** When the xBestIndex() method is called by SQLite, the Tcl command is
+** invoked as:
+**
+** tcl_command xBestIndex CONSTRAINTS ORDERBY MASK
+**
+** where CONSTRAINTS is a tcl representation of the aConstraints[] array,
+** ORDERBY is a representation of the contents of the aOrderBy[] array and
+** MASK is a copy of sqlite3_index_info.colUsed. For example if the virtual
+** table is declared as:
+**
+** CREATE TABLE x1(a, b, c)
+**
+** and the query is:
+**
+** SELECT * FROM x1 WHERE a=? AND c<? ORDER BY b, c;
+**
+** then the Tcl command is:
+**
+** tcl_command xBestIndex \
+** {{op eq column 0 usable 1} {op lt column 2 usable 1}} \
+** {{column 1 desc 0} {column 2 desc 0}} \
+** 7
+**
+** The return value of the script is a list of key-value pairs used to
+** populate the output fields of the sqlite3_index_info structure. Possible
+** keys and the usage of the accompanying values are:
+**
+** "orderby" (value of orderByConsumed flag)
+** "cost" (value of estimatedCost field)
+** "rows" (value of estimatedRows field)
+** "use" (index of used constraint in aConstraint[])
+** "omit" (like "use", but also sets omit flag)
+** "idxnum" (value of idxNum field)
+** "idxstr" (value of idxStr field)
+**
+** Refer to code below for further details.
+**
+** When SQLite calls the xFilter() method, this module invokes the following
+** Tcl script:
+**
+** tcl_command xFilter IDXNUM IDXSTR ARGLIST
+**
+** IDXNUM and IDXSTR are the values of the idxNum and idxStr parameters
+** passed to xFilter. ARGLIST is a Tcl list containing each of the arguments
+** passed to xFilter in text form.
+**
+** As with xBestIndex(), the return value of the script is interpreted as a
+** list of key-value pairs. There is currently only one key defined - "sql".
+** The value must be the full text of an SQL statement that returns the data
+** for the current scan. The leftmost column returned by the SELECT is assumed
+** to contain the rowid. Other columns must follow, in order from left to
+** right.
+*/
+
+
+#include "sqliteInt.h"
+#include "tcl.h"
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
+typedef struct tcl_vtab tcl_vtab;
+typedef struct tcl_cursor tcl_cursor;
+
+/*
+** A fs virtual-table object
+*/
+struct tcl_vtab {
+ sqlite3_vtab base;
+ Tcl_Interp *interp;
+ Tcl_Obj *pCmd;
+ sqlite3 *db;
+};
+
+/* A tcl cursor object */
+struct tcl_cursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pStmt; /* Read data from here */
+};
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the fs virtual table.
+**
+** The argv[] array contains the following:
+**
+** argv[0] -> module name ("fs")
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> other module argument fields.
+*/
+static int tclConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ Tcl_Interp *interp = (Tcl_Interp*)pAux;
+ tcl_vtab *pTab;
+ const char *zCmd;
+ Tcl_Obj *pScript = 0;
+ int rc;
+
+ if( argc!=4 ){
+ *pzErr = sqlite3_mprintf("wrong number of arguments");
+ return SQLITE_ERROR;
+ }
+ zCmd = argv[3];
+
+ pTab = (tcl_vtab*)sqlite3_malloc(sizeof(tcl_vtab));
+ if( pTab==0 ) return SQLITE_NOMEM;
+ memset(pTab, 0, sizeof(tcl_vtab));
+
+ pTab->pCmd = Tcl_NewStringObj(zCmd, -1);
+ pTab->interp = interp;
+ pTab->db = db;
+ Tcl_IncrRefCount(pTab->pCmd);
+
+ pScript = Tcl_DuplicateObj(pTab->pCmd);
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xConnect", -1));
+
+ rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ){
+ *pzErr = sqlite3_mprintf("%s", Tcl_GetStringResult(interp));
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3_declare_vtab(db, Tcl_GetStringResult(interp));
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pTab);
+ pTab = 0;
+ }
+
+ *ppVtab = &pTab->base;
+ return rc;
+}
+
+/* The xDisconnect and xDestroy methods are also the same */
+static int tclDisconnect(sqlite3_vtab *pVtab){
+ tcl_vtab *pTab = (tcl_vtab*)pVtab;
+ Tcl_DecrRefCount(pTab->pCmd);
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** Open a new tcl cursor.
+*/
+static int tclOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ tcl_cursor *pCur;
+ pCur = sqlite3_malloc(sizeof(tcl_cursor));
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(tcl_cursor));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/*
+** Close a tcl cursor.
+*/
+static int tclClose(sqlite3_vtab_cursor *cur){
+ tcl_cursor *pCur = (tcl_cursor *)cur;
+ if( pCur ){
+ sqlite3_finalize(pCur->pStmt);
+ sqlite3_free(pCur);
+ }
+ return SQLITE_OK;
+}
+
+static int tclNext(sqlite3_vtab_cursor *pVtabCursor){
+ tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
+ if( pCsr->pStmt ){
+ tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
+ int rc = sqlite3_step(pCsr->pStmt);
+ if( rc!=SQLITE_ROW ){
+ const char *zErr;
+ rc = sqlite3_finalize(pCsr->pStmt);
+ pCsr->pStmt = 0;
+ if( rc!=SQLITE_OK ){
+ zErr = sqlite3_errmsg(pTab->db);
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int tclFilter(
+ sqlite3_vtab_cursor *pVtabCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
+ tcl_vtab *pTab = (tcl_vtab*)(pVtabCursor->pVtab);
+ Tcl_Interp *interp = pTab->interp;
+ Tcl_Obj *pScript;
+ Tcl_Obj *pArg;
+ int ii;
+ int rc;
+
+ pScript = Tcl_DuplicateObj(pTab->pCmd);
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xFilter", -1));
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(idxNum));
+ if( idxStr ){
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(idxStr, -1));
+ }else{
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("", -1));
+ }
+
+ pArg = Tcl_NewObj();
+ Tcl_IncrRefCount(pArg);
+ for(ii=0; ii<argc; ii++){
+ const char *zVal = (const char*)sqlite3_value_text(argv[ii]);
+ Tcl_Obj *pVal;
+ if( zVal==0 ){
+ pVal = Tcl_NewObj();
+ }else{
+ pVal = Tcl_NewStringObj(zVal, -1);
+ }
+ Tcl_ListObjAppendElement(interp, pArg, pVal);
+ }
+ Tcl_ListObjAppendElement(interp, pScript, pArg);
+ Tcl_DecrRefCount(pArg);
+
+ rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ){
+ const char *zErr = Tcl_GetStringResult(interp);
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }else{
+ /* Analyze the scripts return value. The return value should be a tcl
+ ** list object with an even number of elements. The first element of each
+ ** pair must be one of:
+ **
+ ** "sql" (SQL statement to return data)
+ */
+ Tcl_Obj *pRes = Tcl_GetObjResult(interp);
+ Tcl_Obj **apElem = 0;
+ int nElem;
+ rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
+ if( rc!=TCL_OK ){
+ const char *zErr = Tcl_GetStringResult(interp);
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }else{
+ for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
+ const char *zCmd = Tcl_GetString(apElem[ii]);
+ Tcl_Obj *p = apElem[ii+1];
+ if( sqlite3_stricmp("sql", zCmd)==0 ){
+ const char *zSql = Tcl_GetString(p);
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ const char *zErr = sqlite3_errmsg(pTab->db);
+ pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zErr);
+ }
+ }else{
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = tclNext(pVtabCursor);
+ }
+ return rc;
+}
+
+static int tclColumn(
+ sqlite3_vtab_cursor *pVtabCursor,
+ sqlite3_context *ctx,
+ int i
+){
+ tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
+ sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1));
+ return SQLITE_OK;
+}
+
+static int tclRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
+ tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
+ return SQLITE_OK;
+}
+
+static int tclEof(sqlite3_vtab_cursor *pVtabCursor){
+ tcl_cursor *pCsr = (tcl_cursor*)pVtabCursor;
+ return (pCsr->pStmt==0);
+}
+
+static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ tcl_vtab *pTab = (tcl_vtab*)tab;
+ Tcl_Interp *interp = pTab->interp;
+ Tcl_Obj *pArg;
+ Tcl_Obj *pScript;
+ int ii;
+ int rc = SQLITE_OK;
+
+ pScript = Tcl_DuplicateObj(pTab->pCmd);
+ Tcl_IncrRefCount(pScript);
+ Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
+
+ pArg = Tcl_NewObj();
+ Tcl_IncrRefCount(pArg);
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
+ Tcl_Obj *pElem = Tcl_NewObj();
+ const char *zOp = "?";
+
+ Tcl_IncrRefCount(pElem);
+
+ switch( pCons->op ){
+ case SQLITE_INDEX_CONSTRAINT_EQ:
+ zOp = "eq"; break;
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ zOp = "gt"; break;
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ zOp = "le"; break;
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ zOp = "lt"; break;
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ zOp = "ge"; break;
+ case SQLITE_INDEX_CONSTRAINT_MATCH:
+ zOp = "match"; break;
+ case SQLITE_INDEX_CONSTRAINT_LIKE:
+ zOp = "like"; break;
+ case SQLITE_INDEX_CONSTRAINT_GLOB:
+ zOp = "glob"; break;
+ case SQLITE_INDEX_CONSTRAINT_REGEXP:
+ zOp = "regexp"; break;
+ }
+
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
+
+ Tcl_ListObjAppendElement(0, pArg, pElem);
+ Tcl_DecrRefCount(pElem);
+ }
+
+ Tcl_ListObjAppendElement(0, pScript, pArg);
+ Tcl_DecrRefCount(pArg);
+
+ pArg = Tcl_NewObj();
+ Tcl_IncrRefCount(pArg);
+ for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
+ struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
+ Tcl_Obj *pElem = Tcl_NewObj();
+ Tcl_IncrRefCount(pElem);
+
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->iColumn));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
+ Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
+
+ Tcl_ListObjAppendElement(0, pArg, pElem);
+ Tcl_DecrRefCount(pElem);
+ }
+
+ Tcl_ListObjAppendElement(0, pScript, pArg);
+ Tcl_DecrRefCount(pArg);
+
+ Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
+
+ rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+ Tcl_DecrRefCount(pScript);
+ if( rc!=TCL_OK ){
+ const char *zErr = Tcl_GetStringResult(interp);
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }else{
+ /* Analyze the scripts return value. The return value should be a tcl
+ ** list object with an even number of elements. The first element of each
+ ** pair must be one of:
+ **
+ ** "orderby" (value of orderByConsumed flag)
+ ** "cost" (value of estimatedCost field)
+ ** "rows" (value of estimatedRows field)
+ ** "use" (index of used constraint in aConstraint[])
+ ** "idxnum" (value of idxNum field)
+ ** "idxstr" (value of idxStr field)
+ ** "omit" (index of omitted constraint in aConstraint[])
+ */
+ Tcl_Obj *pRes = Tcl_GetObjResult(interp);
+ Tcl_Obj **apElem = 0;
+ int nElem;
+ rc = Tcl_ListObjGetElements(interp, pRes, &nElem, &apElem);
+ if( rc!=TCL_OK ){
+ const char *zErr = Tcl_GetStringResult(interp);
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }else{
+ int iArgv = 1;
+ for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
+ const char *zCmd = Tcl_GetString(apElem[ii]);
+ Tcl_Obj *p = apElem[ii+1];
+ if( sqlite3_stricmp("cost", zCmd)==0 ){
+ rc = Tcl_GetDoubleFromObj(interp, p, &pIdxInfo->estimatedCost);
+ }else
+ if( sqlite3_stricmp("orderby", zCmd)==0 ){
+ rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->orderByConsumed);
+ }else
+ if( sqlite3_stricmp("idxnum", zCmd)==0 ){
+ rc = Tcl_GetIntFromObj(interp, p, &pIdxInfo->idxNum);
+ }else
+ if( sqlite3_stricmp("idxstr", zCmd)==0 ){
+ sqlite3_free(pIdxInfo->idxStr);
+ pIdxInfo->idxStr = sqlite3_mprintf("%s", Tcl_GetString(p));
+ pIdxInfo->needToFreeIdxStr = 1;
+ }else
+ if( sqlite3_stricmp("rows", zCmd)==0 ){
+ rc = Tcl_GetWideIntFromObj(interp, p, &pIdxInfo->estimatedRows);
+ }else
+ if( sqlite3_stricmp("use", zCmd)==0
+ || sqlite3_stricmp("omit", zCmd)==0
+ ){
+ int iCons;
+ rc = Tcl_GetIntFromObj(interp, p, &iCons);
+ if( rc==SQLITE_OK ){
+ if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %d", iCons);
+ }else{
+ int bOmit = (zCmd[0]=='o' || zCmd[0]=='O');
+ pIdxInfo->aConstraintUsage[iCons].argvIndex = iArgv++;
+ pIdxInfo->aConstraintUsage[iCons].omit = bOmit;
+ }
+ }
+ }else{
+ rc = SQLITE_ERROR;
+ pTab->base.zErrMsg = sqlite3_mprintf("unexpected: %s", zCmd);
+ }
+ if( rc!=SQLITE_OK && pTab->base.zErrMsg==0 ){
+ const char *zErr = Tcl_GetStringResult(interp);
+ pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** A virtual table module that provides read-only access to a
+** Tcl global variable namespace.
+*/
+static sqlite3_module tclModule = {
+ 0, /* iVersion */
+ tclConnect,
+ tclConnect,
+ tclBestIndex,
+ tclDisconnect,
+ tclDisconnect,
+ tclOpen, /* xOpen - open a cursor */
+ tclClose, /* xClose - close a cursor */
+ tclFilter, /* xFilter - configure scan constraints */
+ tclNext, /* xNext - advance a cursor */
+ tclEof, /* xEof - check for end of scan */
+ tclColumn, /* xColumn - read data */
+ tclRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+};
+
+/*
+** Decode a pointer to an sqlite3 object.
+*/
+extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
+
+/*
+** Register the echo virtual table module.
+*/
+static int register_tcl_module(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_create_module(db, "tcl", &tclModule, (void *)interp);
+#endif
+ return TCL_OK;
+}
+
+#endif
+
+
+/*
+** Register commands with the TCL interpreter.
+*/
+int Sqlitetesttcl_Init(Tcl_Interp *interp){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ void *clientData;
+ } aObjCmd[] = {
+ { "register_tcl_module", register_tcl_module, 0 },
+ };
+ int i;
+ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aObjCmd[i].zName,
+ aObjCmd[i].xProc, aObjCmd[i].clientData, 0);
+ }
+#endif
+ return TCL_OK;
+}
diff --git a/src/test_config.c b/src/test_config.c
index a4aca1c72..f712ebac6 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -376,12 +376,6 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
#endif
-#ifdef SQLITE_ENABLE_FTS3_TOKENIZER
- Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "1", TCL_GLOBAL_ONLY);
-#else
- Tcl_SetVar2(interp, "sqlite_options", "fts3_tokenizer", "0", TCL_GLOBAL_ONLY);
-#endif
-
#ifdef SQLITE_ENABLE_FTS5
Tcl_SetVar2(interp, "sqlite_options", "fts5", "1", TCL_GLOBAL_ONLY);
#else
diff --git a/src/treeview.c b/src/treeview.c
index ff3b4be5a..907159c06 100644
--- a/src/treeview.c
+++ b/src/treeview.c
@@ -339,6 +339,12 @@ void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
case TK_ISNULL: zUniOp = "ISNULL"; break;
case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+ case TK_SPAN: {
+ sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+
case TK_COLLATE: {
sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
diff --git a/src/util.c b/src/util.c
index e325a7311..81274260f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -110,6 +110,14 @@ int sqlite3Strlen30(const char *z){
}
/*
+** The string z[] is followed immediately by another string. Return
+** a poiner to that other string.
+*/
+const char *sqlite3StrNext(const char *z){
+ return z + strlen(z) + 1;
+}
+
+/*
** Set the current error code to err_code and clear any prior error message.
*/
void sqlite3Error(sqlite3 *db, int err_code){
@@ -1400,8 +1408,14 @@ LogEst sqlite3LogEstFromDouble(double x){
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+#if defined(SQLITE_ENABLE_STMT_SCANSTAT) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
** Convert a LogEst into an integer.
+**
+** Note that this routine is only used when one or more of various
+** non-standard compile-time options is enabled.
*/
u64 sqlite3LogEstToInt(LogEst x){
u64 n;
@@ -1415,3 +1429,4 @@ u64 sqlite3LogEstToInt(LogEst x){
}
return (n+8)>>(3-x);
}
+#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 4dab3ba49..448163302 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -87,7 +87,7 @@ struct VdbeCursor {
#endif
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
- Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */
+ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
Pgno pgnoRoot; /* Root page of the open btree cursor */
i16 nField; /* Number of fields in the header */
u16 nHdrParsed; /* Number of header fields parsed so far */
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 8930c76d4..abe2dd251 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -1281,7 +1281,7 @@ static int valueFromExpr(
*ppVal = 0;
return SQLITE_OK;
}
- while( (op = pExpr->op)==TK_UPLUS ) pExpr = pExpr->pLeft;
+ while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
/* Compressed expressions only appear when parsing the DEFAULT clause
diff --git a/src/vtab.c b/src/vtab.c
index fa1954819..ad8caef3b 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -564,10 +564,10 @@ static int vtabCallConstructor(
pTab->pVTable = pVTable;
for(iCol=0; iCol<pTab->nCol; iCol++){
- char *zType = pTab->aCol[iCol].zType;
+ char *zType = (char*)sqlite3StrNext(pTab->aCol[iCol].zName);
int nType;
int i = 0;
- if( !zType ){
+ if( !zType[0] ){
pTab->tabFlags |= oooHidden;
continue;
}
diff --git a/src/where.c b/src/where.c
index a68dcf5bf..ed90f6144 100644
--- a/src/where.c
+++ b/src/where.c
@@ -1561,7 +1561,8 @@ static int whereEqualScanEst(
pBuilder->nRecValid = nEq;
whereKeyStats(pParse, p, pRec, 0, a);
- WHERETRACE(0x10,("equality scan regions: %d\n", (int)a[1]));
+ WHERETRACE(0x10,("equality scan regions %s(%d): %d\n",
+ p->zName, nEq-1, (int)a[1]));
*pnRow = a[1];
return rc;
@@ -2897,13 +2898,6 @@ static int whereLoopAddVirtual(
testcase( iTerm==16 );
if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
if( (pTerm->eOperator & WO_IN)!=0 ){
- if( pUsage[i].omit==0 ){
- /* Do not attempt to use an IN constraint if the virtual table
- ** says that the equivalent EQ constraint cannot be safely omitted.
- ** If we do attempt to use such a constraint, some rows might be
- ** repeated in the output. */
- break;
- }
/* A virtual table that is constrained by an IN clause may not
** consume the ORDER BY clause because (1) the order of IN terms
** is not necessarily related to the order of output terms and
@@ -3463,9 +3457,8 @@ static LogEst whereSortingCost(
/* Multiple by log(M) where M is the number of output rows.
** Use the LIMIT for M if it is smaller */
- if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 ){
- LogEst m = sqlite3LogEst(pWInfo->iLimit);
- if( m<nRow ) nRow = m;
+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
+ nRow = pWInfo->iLimit;
}
rSortCost += estLog(nRow);
return rSortCost;
diff --git a/src/wherecode.c b/src/wherecode.c
index accc14086..bfc52fb47 100644
--- a/src/wherecode.c
+++ b/src/wherecode.c
@@ -874,6 +874,7 @@ Bitmask sqlite3WhereCodeOneLoopStart(
int iReg; /* P3 Value for OP_VFilter */
int addrNotFound;
int nConstraint = pLoop->nLTerm;
+ int iIn; /* Counter for IN constraints */
sqlite3ExprCachePush(pParse);
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
@@ -896,14 +897,48 @@ Bitmask sqlite3WhereCodeOneLoopStart(
pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
VdbeCoverage(v);
pLoop->u.vtab.needFree = 0;
- for(j=0; j<nConstraint && j<16; j++){
- if( (pLoop->u.vtab.omitMask>>j)&1 ){
- disableTerm(pLevel, pLoop->aLTerm[j]);
- }
- }
pLevel->p1 = iCur;
pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext;
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+ iIn = pLevel->u.in.nIn;
+ for(j=nConstraint-1; j>=0; j--){
+ pTerm = pLoop->aLTerm[j];
+ if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){
+ disableTerm(pLevel, pTerm);
+ }else if( (pTerm->eOperator & WO_IN)!=0 ){
+ Expr *pCompare; /* The comparison operator */
+ Expr *pRight; /* RHS of the comparison */
+ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */
+
+ /* Reload the constraint value into reg[iReg+j+2]. The same value
+ ** was loaded into the same register prior to the OP_VFilter, but
+ ** the xFilter implementation might have changed the datatype or
+ ** encoding of the value in the register, so it *must* be reloaded. */
+ assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed );
+ if( pLevel->u.in.aInLoop!=0 ){
+ assert( iIn>0 );
+ pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[--iIn].addrInTop);
+ assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid );
+ assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 );
+ assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 );
+ testcase( pOp->opcode==OP_Rowid );
+ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3);
+ }
+
+ /* Generate code that will continue to the next row if
+ ** the IN constraint is not satisfied */
+ pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0, 0);
+ assert( pCompare!=0 || db->mallocFailed );
+ if( pCompare ){
+ pCompare->pLeft = pTerm->pExpr->pLeft;
+ pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pRight ) pRight->iTable = iReg+j+2;
+ sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0);
+ pCompare->pLeft = 0;
+ sqlite3ExprDelete(db, pCompare);
+ }
+ }
+ }
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
sqlite3ExprCachePop(pParse);
}else