aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2013-07-09 13:05:49 +0000
committerdrh <drh@noemail.net>2013-07-09 13:05:49 +0000
commit888e16e7c50efe29c2e48ae7cf8cab942ba496fb (patch)
treef46feb7f6dbedd058a5dc77bd619aca7d0a78d0e /src
parentbded90b4873a5e4e6d513f36dd0edc181004dfaa (diff)
parent1f10c286188947d222cbaf3589245f19d812cf4a (diff)
downloadsqlite-888e16e7c50efe29c2e48ae7cf8cab942ba496fb.tar.gz
sqlite-888e16e7c50efe29c2e48ae7cf8cab942ba496fb.zip
Pull in all the latest changes from trunk.
FossilOrigin-Name: af3ca4c6e557e6bc92584586b5a97d9be41b0b82
Diffstat (limited to 'src')
-rw-r--r--src/btree.c3
-rw-r--r--src/main.c11
-rw-r--r--src/pager.c5
-rw-r--r--src/printf.c4
-rw-r--r--src/shell.c277
-rw-r--r--src/sqlite.h.in2
-rw-r--r--src/sqlite3ext.h3
-rw-r--r--src/sqliteInt.h8
-rw-r--r--src/test1.c25
-rw-r--r--src/test_schema.c5
-rw-r--r--src/test_vfs.c36
-rw-r--r--src/tokenize.c2
-rw-r--r--src/vacuum.c2
-rw-r--r--src/vdbe.c43
-rw-r--r--src/vdbeInt.h3
-rw-r--r--src/vdbeapi.c13
-rw-r--r--src/vdbeaux.c45
-rw-r--r--src/vdbetrace.c6
-rw-r--r--src/wal.c2
-rw-r--r--src/where.c104
20 files changed, 428 insertions, 171 deletions
diff --git a/src/btree.c b/src/btree.c
index f6e564628..9b7837879 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3273,12 +3273,13 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
*/
static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3 *db = p->db;
assert( sqlite3BtreeHoldsMutex(p) );
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->bDoTruncate = 0;
#endif
- if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
+ if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){
/* If there are other active statements that belong to this database
** handle, downgrade to a read-only transaction. The other statements
** may still be reading from the database. */
diff --git a/src/main.c b/src/main.c
index df158b918..ad9422b48 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1065,6 +1065,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
+ case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
@@ -1138,6 +1139,7 @@ const char *sqlite3ErrName(int rc){
case SQLITE_NOTICE_RECOVER_ROLLBACK:
zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
+ case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
case SQLITE_DONE: zName = "SQLITE_DONE"; break;
}
}
@@ -1396,7 +1398,7 @@ int sqlite3CreateFunc(
*/
p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
if( p && p->iPrefEnc==enc && p->nArg==nArg ){
- if( db->activeVdbeCnt ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
@@ -1998,7 +2000,7 @@ static int createCollation(
*/
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0);
if( pColl && pColl->xCmp ){
- if( db->activeVdbeCnt ){
+ if( db->nVdbeActive ){
sqlite3Error(db, SQLITE_BUSY,
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
@@ -2473,7 +2475,10 @@ static int openDatabase(
db->nextAutovac = -1;
db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
- db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger
+ db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger
+#if !defined(SQLITE_DEAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
+ | SQLITE_AutoIndex
+#endif
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
#endif
diff --git a/src/pager.c b/src/pager.c
index 645cc3d9a..b20822896 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -1812,6 +1812,7 @@ static void pager_unlock(Pager *pPager){
pPager->changeCountDone = pPager->tempFile;
pPager->eState = PAGER_OPEN;
pPager->errCode = SQLITE_OK;
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
pPager->journalOff = 0;
@@ -3378,10 +3379,10 @@ void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
static void pagerFixMaplimit(Pager *pPager){
#if SQLITE_MAX_MMAP_SIZE>0
sqlite3_file *fd = pPager->fd;
- if( isOpen(fd) ){
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){
sqlite3_int64 sz;
- pPager->bUseFetch = (fd->pMethods->iVersion>=3) && pPager->szMmap>0;
sz = pPager->szMmap;
+ pPager->bUseFetch = (sz>0);
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
}
#endif
diff --git a/src/printf.c b/src/printf.c
index 8d37d633b..67649b269 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -468,8 +468,8 @@ void sqlite3VXPrintf(
}else{
e2 = exp;
}
- if( e2+precision+width > etBUFSIZE - 15 ){
- bufpt = zExtra = sqlite3Malloc( e2+precision+width+15 );
+ if( MAX(e2,0)+precision+width > etBUFSIZE - 15 ){
+ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
if( bufpt==0 ){
pAccum->mallocFailed = 1;
return;
diff --git a/src/shell.c b/src/shell.c
index 9a8a90694..79548fa1e 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -67,7 +67,7 @@
#undef popen
#define popen(a,b) _popen((a),(b))
#undef pclose
-#define pclose(x) _pclose(x)
+#define pclose _pclose
#else
/* Make sure isatty() has a prototype.
*/
@@ -1492,6 +1492,7 @@ static void open_db(struct callback_data *p){
** \t -> tab
** \n -> newline
** \r -> carriage return
+** \" -> "
** \NNN -> ascii character NNN in octal
** \\ -> backslash
*/
@@ -1507,6 +1508,8 @@ static void resolve_backslashes(char *z){
c = '\t';
}else if( c=='r' ){
c = '\r';
+ }else if( c=='\\' ){
+ c = '\\';
}else if( c>='0' && c<='7' ){
c -= '0';
if( z[i+1]>='0' && z[i+1]<='7' ){
@@ -1651,6 +1654,107 @@ static void test_breakpoint(void){
}
/*
+** An object used to read a CSV file
+*/
+typedef struct CSVReader CSVReader;
+struct CSVReader {
+ const char *zFile; /* Name of the input file */
+ FILE *in; /* Read the CSV text from this input stream */
+ char *z; /* Accumulated text for a field */
+ int n; /* Number of bytes in z */
+ int nAlloc; /* Space allocated for z[] */
+ int nLine; /* Current line number */
+ int cTerm; /* Character that terminated the most recent field */
+ int cSeparator; /* The separator character. (Usually ",") */
+};
+
+/* Append a single byte to z[] */
+static void csv_append_char(CSVReader *p, int c){
+ if( p->n+1>=p->nAlloc ){
+ p->nAlloc += p->nAlloc + 100;
+ p->z = sqlite3_realloc(p->z, p->nAlloc);
+ if( p->z==0 ){
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ }
+ p->z[p->n++] = (char)c;
+}
+
+/* Read a single field of CSV text. Compatible with rfc4180 and extended
+** with the option of having a separator other than ",".
+**
+** + Input comes from p->in.
+** + Store results in p->z of length p->n. Space to hold p->z comes
+** from sqlite3_malloc().
+** + Use p->cSep as the separator. The default is ",".
+** + Keep track of the line number in p->nLine.
+** + Store the character that terminates the field in p->cTerm. Store
+** EOF on end-of-file.
+** + Report syntax errors on stderr
+*/
+static char *csv_read_one_field(CSVReader *p){
+ int c, pc;
+ int cSep = p->cSeparator;
+ p->n = 0;
+ c = fgetc(p->in);
+ if( c==EOF || seenInterrupt ){
+ p->cTerm = EOF;
+ return 0;
+ }
+ if( c=='"' ){
+ int startLine = p->nLine;
+ int cQuote = c;
+ pc = 0;
+ while( 1 ){
+ c = fgetc(p->in);
+ if( c=='\n' ) p->nLine++;
+ if( c==cQuote ){
+ if( pc==cQuote ){
+ pc = 0;
+ continue;
+ }
+ }
+ if( (c==cSep && pc==cQuote)
+ || (c=='\n' && pc==cQuote)
+ || (c=='\n' && pc=='\r' && p->n>2 && p->z[p->n-2]==cQuote)
+ || (c==EOF && pc==cQuote)
+ ){
+ do{ p->n--; }while( p->z[p->n]!=cQuote );
+ p->z[p->n] = 0;
+ p->cTerm = c;
+ break;
+ }
+ if( pc==cQuote && c!='\r' ){
+ fprintf(stderr, "%s:%d: unescaped %c character\n",
+ p->zFile, p->nLine, cQuote);
+ }
+ if( c==EOF ){
+ fprintf(stderr, "%s:%d: unterminated %c-quoted field\n",
+ p->zFile, startLine, cQuote);
+ p->z[p->n] = 0;
+ p->cTerm = EOF;
+ break;
+ }
+ csv_append_char(p, c);
+ pc = c;
+ }
+ }else{
+ while( c!=EOF && c!=cSep && c!='\n' ){
+ csv_append_char(p, c);
+ c = fgetc(p->in);
+ }
+ if( c=='\n' ){
+ p->nLine++;
+ if( p->n>1 && p->z[p->n-1]=='\r' ) p->n--;
+ }
+ p->z[p->n] = 0;
+ p->cTerm = c;
+ }
+ return p->z;
+}
+
+/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
@@ -1671,7 +1775,10 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( zLine[i]=='\'' || zLine[i]=='"' ){
int delim = zLine[i++];
azArg[nArg++] = &zLine[i];
- while( zLine[i] && zLine[i]!=delim ){ i++; }
+ while( zLine[i] && zLine[i]!=delim ){
+ if( zLine[i]=='\\' && delim=='"' && zLine[i+1]!=0 ) i++;
+ i++;
+ }
if( zLine[i]==delim ){
zLine[i++] = 0;
}
@@ -1888,48 +1995,97 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg==3 ){
char *zTable = azArg[2]; /* Insert data into this table */
- char *zFile = azArg[1]; /* The file from which to extract data */
+ char *zFile = azArg[1]; /* Name of file to extra content from */
sqlite3_stmt *pStmt = NULL; /* A statement */
int nCol; /* Number of columns in the table */
int nByte; /* Number of bytes in an SQL string */
int i, j; /* Loop counters */
int nSep; /* Number of bytes in p->separator[] */
char *zSql; /* An SQL statement */
- char *zLine; /* A single line of input from the file */
- char **azCol; /* zLine[] broken up into columns */
- char *zCommit; /* How to commit changes */
- FILE *in; /* The input file */
- int lineno = 0; /* Line number of input file */
+ CSVReader sCsv; /* Reader context */
+ int (*xCloser)(FILE*); /* Procedure to close th3 connection */
+ seenInterrupt = 0;
+ memset(&sCsv, 0, sizeof(sCsv));
open_db(p);
nSep = strlen30(p->separator);
if( nSep==0 ){
fprintf(stderr, "Error: non-null separator required for import\n");
return 1;
}
+ if( nSep>1 ){
+ fprintf(stderr, "Error: multi-character separators not allowed"
+ " for import\n");
+ return 1;
+ }
+ sCsv.zFile = zFile;
+ sCsv.nLine = 1;
+ if( sCsv.zFile[0]=='|' ){
+ sCsv.in = popen(sCsv.zFile+1, "r");
+ sCsv.zFile = "<pipe>";
+ xCloser = pclose;
+ }else{
+ sCsv.in = fopen(sCsv.zFile, "rb");
+ xCloser = fclose;
+ }
+ if( sCsv.in==0 ){
+ fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
+ return 1;
+ }
+ sCsv.cSeparator = p->separator[0];
zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
nByte = strlen30(zSql);
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ if( rc && sqlite3_strglob("no such table: *", sqlite3_errmsg(db))==0 ){
+ char *zCreate = sqlite3_mprintf("CREATE TABLE %s", zTable);
+ char cSep = '(';
+ while( csv_read_one_field(&sCsv) ){
+ zCreate = sqlite3_mprintf("%z%c\n \"%s\" TEXT", zCreate, cSep, sCsv.z);
+ cSep = ',';
+ if( sCsv.cTerm!=sCsv.cSeparator ) break;
+ }
+ if( cSep=='(' ){
+ sqlite3_free(zCreate);
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ fprintf(stderr,"%s: empty file\n", sCsv.zFile);
+ return 1;
+ }
+ zCreate = sqlite3_mprintf("%z\n)", zCreate);
+ rc = sqlite3_exec(p->db, zCreate, 0, 0, 0);
+ sqlite3_free(zCreate);
+ if( rc ){
+ fprintf(stderr, "CREATE TABLE %s(...) failed: %s\n", zTable,
+ sqlite3_errmsg(db));
+ sqlite3_free(sCsv.z);
+ xCloser(sCsv.in);
+ return 1;
+ }
+ rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
+ }
sqlite3_free(zSql);
if( rc ){
if (pStmt) sqlite3_finalize(pStmt);
fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ xCloser(sCsv.in);
return 1;
}
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
- zSql = malloc( nByte + 20 + nCol*2 );
+ zSql = sqlite3_malloc( nByte*2 + 20 + nCol*2 );
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
+ xCloser(sCsv.in);
return 1;
}
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
@@ -1938,79 +2094,50 @@ static int do_meta_command(char *zLine, struct callback_data *p){
zSql[j++] = ')';
zSql[j] = 0;
rc = sqlite3_prepare(p->db, zSql, -1, &pStmt, 0);
- free(zSql);
+ sqlite3_free(zSql);
if( rc ){
fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
if (pStmt) sqlite3_finalize(pStmt);
+ xCloser(sCsv.in);
return 1;
}
- in = fopen(zFile, "rb");
- if( in==0 ){
- fprintf(stderr, "Error: cannot open \"%s\"\n", zFile);
- sqlite3_finalize(pStmt);
- return 1;
- }
- azCol = malloc( sizeof(azCol[0])*(nCol+1) );
- if( azCol==0 ){
- fprintf(stderr, "Error: out of memory\n");
- fclose(in);
- sqlite3_finalize(pStmt);
- return 1;
- }
- sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
- zCommit = "COMMIT";
- while( (zLine = local_getline(0, in, 1))!=0 ){
- char *z, c;
- int inQuote = 0;
- lineno++;
- azCol[0] = zLine;
- for(i=0, z=zLine; (c = *z)!=0; z++){
- if( c=='"' ) inQuote = !inQuote;
- if( c=='\n' ) lineno++;
- if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){
- *z = 0;
+ do{
+ int startLine = sCsv.nLine;
+ for(i=0; i<nCol; i++){
+ char *z = csv_read_one_field(&sCsv);
+ if( z==0 && i==0 ) break;
+ sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT);
+ if( i<nCol-1 && sCsv.cTerm!=sCsv.cSeparator ){
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "filling the rest with NULL\n",
+ sCsv.zFile, startLine, nCol, i+1);
i++;
- if( i<nCol ){
- azCol[i] = &z[nSep];
- z += nSep-1;
- }
+ while( i<nCol ){ sqlite3_bind_null(pStmt, i); i++; }
}
- } /* end for */
- *z = 0;
- if( i+1!=nCol ){
- fprintf(stderr,
- "Error: %s line %d: expected %d columns of data but found %d\n",
- zFile, lineno, nCol, i+1);
- zCommit = "ROLLBACK";
- free(zLine);
- rc = 1;
- break; /* from while */
}
- for(i=0; i<nCol; i++){
- if( azCol[i][0]=='"' ){
- int k;
- for(z=azCol[i], j=1, k=0; z[j]; j++){
- if( z[j]=='"' ){ j++; if( z[j]==0 ) break; }
- z[k++] = z[j];
- }
- z[k] = 0;
+ if( sCsv.cTerm==sCsv.cSeparator ){
+ do{
+ csv_read_one_field(&sCsv);
+ i++;
+ }while( sCsv.cTerm==sCsv.cSeparator );
+ fprintf(stderr, "%s:%d: expected %d columns but found %d - "
+ "extras ignored\n",
+ sCsv.zFile, startLine, nCol, i);
+ }
+ if( i>=nCol ){
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr, "%s:%d: INSERT failed: %s\n", sCsv.zFile, startLine,
+ sqlite3_errmsg(db));
}
- sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
- }
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
- free(zLine);
- if( rc!=SQLITE_OK ){
- fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
- zCommit = "ROLLBACK";
- rc = 1;
- break; /* from while */
}
- } /* end while */
- free(azCol);
- fclose(in);
+ }while( sCsv.cTerm!=EOF );
+
+ xCloser(sCsv.in);
+ sqlite3_free(sCsv.z);
sqlite3_finalize(pStmt);
- sqlite3_exec(p->db, zCommit, 0, 0, 0);
+ sqlite3_exec(p->db, "COMMIT", 0, 0, 0);
}else
if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg<3 ){
@@ -2332,6 +2459,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
}
}else
+#ifdef SQLITE_DEBUG
/* Undocumented commands for internal testing. Subject to change
** without notice. */
if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){
@@ -2345,11 +2473,14 @@ static int do_meta_command(char *zLine, struct callback_data *p){
if( strncmp(azArg[0]+9, "integer", n-9)==0 ){
int i; sqlite3_int64 v;
for(i=1; i<nArg; i++){
+ char zBuf[200];
v = integerValue(azArg[i]);
- fprintf(p->out, "%s: %lld 0x%llx\n", azArg[i], v, v);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%s: %lld 0x%llx\n", azArg[i], v, v);
+ fprintf(p->out, "%s", zBuf);
}
}
}else
+#endif
if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
sqlite3_snprintf(sizeof(p->separator), p->separator,
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index f02e568f3..24ccb8d39 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -475,6 +475,7 @@ int sqlite3_exec(
#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
@@ -494,6 +495,7 @@ int sqlite3_exec(
#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h
index 928bb3bad..ecf93f62f 100644
--- a/src/sqlite3ext.h
+++ b/src/sqlite3ext.h
@@ -474,11 +474,14 @@ struct sqlite3_api_routines {
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* _SQLITE3EXT_H_ */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index dd65c51d9..228d34fc5 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -898,9 +898,10 @@ struct sqlite3 {
u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
} init;
- int activeVdbeCnt; /* Number of VDBEs currently executing */
- int writeVdbeCnt; /* Number of active VDBEs that are writing */
- int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
+ int nVdbeActive; /* Number of VDBEs currently running */
+ int nVdbeRead; /* Number of active VDBEs that read or write */
+ int nVdbeWrite; /* Number of active VDBEs that read and write */
+ int nVdbeExec; /* Number of nested calls to VdbeExec() */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
void (*xTrace)(void*,const char*); /* Trace function */
@@ -1035,6 +1036,7 @@ struct sqlite3 {
#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
#define SQLITE_Transitive 0x0200 /* Transitive constraints */
#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
+#define SQLITE_Stat3 0x0800 /* Use the SQLITE_STAT3 table */
#define SQLITE_AllOpts 0xffff /* All optimizations */
/*
diff --git a/src/test1.c b/src/test1.c
index 58cfd2ca5..9c38b11a6 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -5959,15 +5959,20 @@ static int optimization_control(
const char *zOptName;
int mask;
} aOpt[] = {
- { "all", SQLITE_AllOpts },
- { "query-flattener", SQLITE_QueryFlattener },
- { "column-cache", SQLITE_ColumnCache },
- { "groupby-order", SQLITE_GroupByOrder },
- { "factor-constants", SQLITE_FactorOutConst },
- { "real-as-int", SQLITE_IdxRealAsInt },
- { "distinct-opt", SQLITE_DistinctOpt },
- { "cover-idx-scan", SQLITE_CoverIdxScan },
- { "order-by-idx-join",SQLITE_OrderByIdxJoin },
+ { "all", SQLITE_AllOpts },
+ { "none", 0 },
+ { "query-flattener", SQLITE_QueryFlattener },
+ { "column-cache", SQLITE_ColumnCache },
+ { "groupby-order", SQLITE_GroupByOrder },
+ { "factor-constants", SQLITE_FactorOutConst },
+ { "real-as-int", SQLITE_IdxRealAsInt },
+ { "distinct-opt", SQLITE_DistinctOpt },
+ { "cover-idx-scan", SQLITE_CoverIdxScan },
+ { "order-by-idx-join", SQLITE_OrderByIdxJoin },
+ { "transitive", SQLITE_Transitive },
+ { "subquery-coroutine", SQLITE_SubqCoroutine },
+ { "omit-noop-join", SQLITE_OmitNoopJoin },
+ { "stat3", SQLITE_Stat3 },
};
if( objc!=4 ){
@@ -5988,7 +5993,7 @@ static int optimization_control(
Tcl_AppendResult(interp, "unknown optimization - should be one of:",
(char*)0);
for(i=0; i<sizeof(aOpt)/sizeof(aOpt[0]); i++){
- Tcl_AppendResult(interp, " ", aOpt[i].zOptName);
+ Tcl_AppendResult(interp, " ", aOpt[i].zOptName, (char*)0);
}
return TCL_ERROR;
}
diff --git a/src/test_schema.c b/src/test_schema.c
index 126444676..00a9f4dd9 100644
--- a/src/test_schema.c
+++ b/src/test_schema.c
@@ -344,7 +344,10 @@ int Sqlitetestschema_Init(Tcl_Interp *interp){
/*
** Extension load function.
*/
-int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_schema_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
diff --git a/src/test_vfs.c b/src/test_vfs.c
index fcd577439..8b6b530bb 100644
--- a/src/test_vfs.c
+++ b/src/test_vfs.c
@@ -190,8 +190,11 @@ static int tvfsShmMap(sqlite3_file*,int,int,int, void volatile **);
static void tvfsShmBarrier(sqlite3_file*);
static int tvfsShmUnmap(sqlite3_file*, int);
+static int tvfsFetch(sqlite3_file*, sqlite3_int64, int, void**);
+static int tvfsUnfetch(sqlite3_file*, sqlite3_int64, void*);
+
static sqlite3_io_methods tvfs_io_methods = {
- 2, /* iVersion */
+ 3, /* iVersion */
tvfsClose, /* xClose */
tvfsRead, /* xRead */
tvfsWrite, /* xWrite */
@@ -207,7 +210,9 @@ static sqlite3_io_methods tvfs_io_methods = {
tvfsShmMap, /* xShmMap */
tvfsShmLock, /* xShmLock */
tvfsShmBarrier, /* xShmBarrier */
- tvfsShmUnmap /* xShmUnmap */
+ tvfsShmUnmap, /* xShmUnmap */
+ tvfsFetch,
+ tvfsUnfetch
};
static int tvfsResultCode(Testvfs *p, int *pRc){
@@ -618,7 +623,10 @@ static int tvfsOpen(
pMethods = (sqlite3_io_methods *)ckalloc(nByte);
memcpy(pMethods, &tvfs_io_methods, nByte);
- pMethods->iVersion = pVfs->iVersion;
+ pMethods->iVersion = pFd->pReal->pMethods->iVersion;
+ if( pMethods->iVersion>pVfs->iVersion ){
+ pMethods->iVersion = pVfs->iVersion;
+ }
if( pVfs->iVersion>1 && ((Testvfs *)pVfs->pAppData)->isNoshm ){
pMethods->xShmUnmap = 0;
pMethods->xShmLock = 0;
@@ -993,6 +1001,21 @@ static int tvfsShmUnmap(
return rc;
}
+static int tvfsFetch(
+ sqlite3_file *pFile,
+ sqlite3_int64 iOfst,
+ int iAmt,
+ void **pp
+){
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ return sqlite3OsFetch(pFd->pReal, iOfst, iAmt, pp);
+}
+
+static int tvfsUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *p){
+ TestvfsFd *pFd = tvfsGetFd(pFile);
+ return sqlite3OsUnfetch(pFd->pReal, iOfst, p);
+}
+
static int testvfs_obj_cmd(
ClientData cd,
Tcl_Interp *interp,
@@ -1343,7 +1366,7 @@ static int testvfs_cmd(
Tcl_Obj *CONST objv[]
){
static sqlite3_vfs tvfs_vfs = {
- 2, /* iVersion */
+ 3, /* iVersion */
0, /* szOsFile */
0, /* mxPathname */
0, /* pNext */
@@ -1369,6 +1392,9 @@ static int testvfs_cmd(
tvfsCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
0, /* xCurrentTimeInt64 */
+ 0, /* xSetSystemCall */
+ 0, /* xGetSystemCall */
+ 0, /* xNextSystemCall */
};
Testvfs *p; /* New object */
@@ -1382,7 +1408,7 @@ static int testvfs_cmd(
int isDefault = 0; /* True if -default is passed */
int szOsFile = 0; /* Value passed to -szosfile */
int mxPathname = -1; /* Value passed to -mxpathname */
- int iVersion = 2; /* Value passed to -iversion */
+ int iVersion = 3; /* Value passed to -iversion */
if( objc<2 || 0!=(objc%2) ) goto bad_args;
for(i=2; i<objc; i += 2){
diff --git a/src/tokenize.c b/src/tokenize.c
index faea5f26c..aef1d300f 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -396,7 +396,7 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
pParse->rc = SQLITE_OK;
diff --git a/src/vacuum.c b/src/vacuum.c
index 4afb2cca6..4ba09fd4d 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -111,7 +111,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
return SQLITE_ERROR;
}
- if( db->activeVdbeCnt>1 ){
+ if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
diff --git a/src/vdbe.c b/src/vdbe.c
index 5330c48b5..e5a40b799 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -589,6 +589,7 @@ int sqlite3VdbeExec(
goto no_mem;
}
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
assert( p->explain==0 );
p->pResultSet = 0;
@@ -2695,9 +2696,10 @@ case OP_Savepoint: {
assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
assert( db->pSavepoint || db->isTransactionSavepoint==0 );
assert( checkSavepointCount(db) );
+ assert( p->bIsReader );
if( p1==SAVEPOINT_BEGIN ){
- if( db->writeVdbeCnt>0 ){
+ if( db->nVdbeWrite>0 ){
/* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
@@ -2755,7 +2757,7 @@ case OP_Savepoint: {
if( !pSavepoint ){
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
- }else if( db->writeVdbeCnt>0 && p1==SAVEPOINT_RELEASE ){
+ }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
/* It is not possible to release (commit) a savepoint if there are
** active write statements.
*/
@@ -2857,10 +2859,11 @@ case OP_AutoCommit: {
turnOnAC = desiredAutoCommit && !db->autoCommit;
assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
assert( desiredAutoCommit==1 || iRollback==0 );
- assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
+ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
+ assert( p->bIsReader );
#if 0
- if( turnOnAC && iRollback && db->activeVdbeCnt>1 ){
+ if( turnOnAC && iRollback && db->nVdbeActive>1 ){
/* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating
** that the other VMs must complete first.
@@ -2870,7 +2873,7 @@ case OP_AutoCommit: {
rc = SQLITE_BUSY;
}else
#endif
- if( turnOnAC && !iRollback && db->writeVdbeCnt>0 ){
+ if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
@@ -2946,6 +2949,8 @@ case OP_AutoCommit: {
case OP_Transaction: {
Btree *pBt;
+ assert( p->bIsReader );
+ assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
@@ -2962,7 +2967,7 @@ case OP_Transaction: {
}
if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
assert( sqlite3BtreeIsInTrans(pBt) );
if( p->iStatement==0 ){
@@ -3003,6 +3008,7 @@ case OP_ReadCookie: { /* out2-prerelease */
int iDb;
int iCookie;
+ assert( p->bIsReader );
iDb = pOp->p1;
iCookie = pOp->p3;
assert( pOp->p3<SQLITE_N_BTREE_META );
@@ -3030,6 +3036,7 @@ case OP_SetCookie: { /* in3 */
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
@@ -3080,6 +3087,7 @@ case OP_VerifyCookie: {
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
+ assert( p->bIsReader );
pBt = db->aDb[pOp->p1].pBt;
if( pBt ){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
@@ -3175,6 +3183,8 @@ case OP_OpenWrite: {
assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
+ assert( p->bIsReader );
+ assert( pOp->opcode==OP_OpenRead || p->readOnly==0 );
if( p->expired ){
rc = SQLITE_ABORT;
@@ -4787,15 +4797,18 @@ case OP_Destroy: { /* out2-prerelease */
Vdbe *pVdbe;
int iDb;
+ assert( p->readOnly==0 );
#ifndef SQLITE_OMIT_VIRTUALTABLE
iCnt = 0;
for(pVdbe=db->pVdbe; pVdbe; pVdbe = pVdbe->pNext){
- if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->inVtabMethod<2 && pVdbe->pc>=0 ){
+ if( pVdbe->magic==VDBE_MAGIC_RUN && pVdbe->bIsReader
+ && pVdbe->inVtabMethod<2 && pVdbe->pc>=0
+ ){
iCnt++;
}
}
#else
- iCnt = db->activeVdbeCnt;
+ iCnt = db->nVdbeRead;
#endif
pOut->flags = MEM_Null;
if( iCnt>1 ){
@@ -4842,6 +4855,7 @@ case OP_Clear: {
int nChange;
nChange = 0;
+ assert( p->readOnly==0 );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
rc = sqlite3BtreeClearTable(
db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
@@ -4888,6 +4902,7 @@ case OP_CreateTable: { /* out2-prerelease */
pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
pDb = &db->aDb[pOp->p1];
assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
@@ -5035,7 +5050,8 @@ case OP_IntegrityCk: {
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
-
+
+ assert( p->bIsReader );
nRoot = pOp->p2;
assert( nRoot>0 );
aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(nRoot+1) );
@@ -5543,6 +5559,7 @@ case OP_Checkpoint: {
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
+ assert( p->readOnly==0 );
aRes[0] = 0;
aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
@@ -5592,6 +5609,7 @@ case OP_JournalMode: { /* out2-prerelease */
|| eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( p->readOnly==0 );
pBt = db->aDb[pOp->p1].pBt;
pPager = sqlite3BtreePager(pBt);
@@ -5615,7 +5633,7 @@ case OP_JournalMode: { /* out2-prerelease */
if( (eNew!=eOld)
&& (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
){
- if( !db->autoCommit || db->activeVdbeCnt>1 ){
+ if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db,
"cannot change %s wal mode from within a transaction",
@@ -5674,6 +5692,7 @@ case OP_JournalMode: { /* out2-prerelease */
** a transaction.
*/
case OP_Vacuum: {
+ assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db);
break;
}
@@ -5691,6 +5710,7 @@ case OP_IncrVacuum: { /* jump */
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
+ assert( p->readOnly==0 );
pBt = db->aDb[pOp->p1].pBt;
rc = sqlite3BtreeIncrVacuum(pBt);
if( rc==SQLITE_DONE ){
@@ -5809,6 +5829,7 @@ case OP_VOpen: {
sqlite3_vtab *pVtab;
sqlite3_module *pModule;
+ assert( p->bIsReader );
pCur = 0;
pVtabCursor = 0;
pVtab = pOp->p4.pVtab->pVtab;
@@ -6025,6 +6046,7 @@ case OP_VRename: {
pName = &aMem[pOp->p1];
assert( pVtab->pModule->xRename );
assert( memIsValid(pName) );
+ assert( p->readOnly==0 );
REGISTER_TRACE(pOp->p1, pName);
assert( pName->flags & MEM_Str );
testcase( pName->enc==SQLITE_UTF8 );
@@ -6076,6 +6098,7 @@ case OP_VUpdate: {
assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
+ assert( p->readOnly==0 );
pVtab = pOp->p4.pVtab->pVtab;
pModule = (sqlite3_module *)pVtab->pModule;
nArg = pOp->p2;
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index aec4b422b..dee4ca68d 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -337,7 +337,8 @@ struct Vdbe {
bft expired:1; /* True if the VM needs to be recompiled */
bft runOnlyOnce:1; /* Automatically expire on reset */
bft usesStmtJournal:1; /* True if uses a statement journal */
- bft readOnly:1; /* True for read-only statements */
+ bft readOnly:1; /* True for statements that do not write */
+ bft bIsReader:1; /* True for statements that read */
bft isPrepareV2:1; /* True if prepared with prepare_v2() */
bft doingRerun:1; /* True if rerunning after an auto-reprepare */
int nChange; /* Number of db changes made since last reset */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 6fb45edb8..b97faaf40 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -382,11 +382,11 @@ static int sqlite3Step(Vdbe *p){
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
** from interrupting a statement that has not yet started.
*/
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
- assert( db->writeVdbeCnt>0 || db->autoCommit==0
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
|| (db->nDeferredCons==0 && db->nDeferredImmCons==0)
);
@@ -396,8 +396,9 @@ static int sqlite3Step(Vdbe *p){
}
#endif
- db->activeVdbeCnt++;
- if( p->readOnly==0 ) db->writeVdbeCnt++;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
}
#ifndef SQLITE_OMIT_EXPLAIN
@@ -406,9 +407,9 @@ static int sqlite3Step(Vdbe *p){
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
- db->vdbeExecCnt++;
+ db->nVdbeExec++;
rc = sqlite3VdbeExec(p);
- db->vdbeExecCnt--;
+ db->nVdbeExec--;
}
#ifndef SQLITE_OMIT_TRACE
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 88561ecb0..5ba51dce6 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -404,14 +404,26 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
Op *pOp;
int *aLabel = p->aLabel;
p->readOnly = 1;
+ p->bIsReader = 0;
for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
u8 opcode = pOp->opcode;
pOp->opflags = sqlite3OpcodeProperty[opcode];
if( opcode==OP_Function || opcode==OP_AggStep ){
if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
+ }else if( opcode==OP_Transaction ){
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ p->bIsReader = 1;
+ }else if( opcode==OP_AutoCommit || opcode==OP_Savepoint ){
+ p->bIsReader = 1;
+ }else if( opcode==OP_Vacuum
+ || opcode==OP_JournalMode
+#ifndef SQLITE_OMIT_WAL
+ || opcode==OP_Checkpoint
+#endif
+ ){
p->readOnly = 0;
+ p->bIsReader = 1;
#ifndef SQLITE_OMIT_VIRTUALTABLE
}else if( opcode==OP_VUpdate ){
if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
@@ -437,8 +449,8 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
}
sqlite3DbFree(p->db, p->aLabel);
p->aLabel = 0;
-
*pMaxFuncArgs = nMaxArgs;
+ assert( p->bIsReader!=0 || p->btreeMask==0 );
}
/*
@@ -1964,7 +1976,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/*
-** This routine checks that the sqlite3.activeVdbeCnt count variable
+** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
@@ -1977,16 +1989,19 @@ static void checkActiveVdbeCnt(sqlite3 *db){
Vdbe *p;
int cnt = 0;
int nWrite = 0;
+ int nRead = 0;
p = db->pVdbe;
while( p ){
if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
cnt++;
if( p->readOnly==0 ) nWrite++;
+ if( p->bIsReader ) nRead++;
}
p = p->pNext;
}
- assert( cnt==db->activeVdbeCnt );
- assert( nWrite==db->writeVdbeCnt );
+ assert( cnt==db->nVdbeActive );
+ assert( nWrite==db->nVdbeWrite );
+ assert( nRead==db->nVdbeRead );
}
#else
#define checkActiveVdbeCnt(x)
@@ -2125,8 +2140,9 @@ int sqlite3VdbeHalt(Vdbe *p){
}
checkActiveVdbeCnt(db);
- /* No commit or rollback needed if the program never started */
- if( p->pc>=0 ){
+ /* No commit or rollback needed if the program never started or if the
+ ** SQL statement does not read or write a database file. */
+ if( p->pc>=0 && p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -2179,7 +2195,7 @@ int sqlite3VdbeHalt(Vdbe *p){
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
- && db->writeVdbeCnt==(p->readOnly==0)
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -2262,11 +2278,12 @@ int sqlite3VdbeHalt(Vdbe *p){
/* We have successfully halted and closed the VM. Record this fact. */
if( p->pc>=0 ){
- db->activeVdbeCnt--;
- if( !p->readOnly ){
- db->writeVdbeCnt--;
- }
- assert( db->activeVdbeCnt>=db->writeVdbeCnt );
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
@@ -2282,7 +2299,7 @@ int sqlite3VdbeHalt(Vdbe *p){
sqlite3ConnectionUnlocked(db);
}
- assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
+ assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
diff --git a/src/vdbetrace.c b/src/vdbetrace.c
index 356277e8d..0a767261f 100644
--- a/src/vdbetrace.c
+++ b/src/vdbetrace.c
@@ -47,9 +47,9 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
-** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
+** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
@@ -87,7 +87,7 @@ char *sqlite3VdbeExpandSql(
sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
out.db = db;
- if( db->vdbeExecCnt>1 ){
+ if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
diff --git a/src/wal.c b/src/wal.c
index e642ea210..f41392064 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -2463,7 +2463,7 @@ int sqlite3WalBeginWriteTransaction(Wal *pWal){
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY;
+ rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
diff --git a/src/where.c b/src/where.c
index e20f2b301..e18e88623 100644
--- a/src/where.c
+++ b/src/where.c
@@ -159,7 +159,7 @@ static int whereLoopResize(sqlite3*, WhereLoop*, int);
** Each instance of this object holds a sequence of WhereLoop objects
** that implement some or all of a query plan.
**
-** Think of each WhereLoop objects as a node in a graph, which arcs
+** Think of each WhereLoop object as a node in a graph with arcs
** showing dependences and costs for travelling between nodes. (That is
** not a completely accurate description because WhereLoop costs are a
** vector, not a scalar, and because dependences are many-to-one, not
@@ -429,7 +429,7 @@ struct WhereInfo {
** The particular combination of bits in each WhereLoop help to
** determine the algorithm that WhereLoop represents.
*/
-#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR or x IN (...) or x IS NULL */
+#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR */
#define WHERE_COLUMN_RANGE 0x00000002 /* x<EXPR and/or x>EXPR */
#define WHERE_COLUMN_IN 0x00000004 /* x IN (...) */
#define WHERE_COLUMN_NULL 0x00000008 /* x IS NULL */
@@ -444,7 +444,7 @@ struct WhereInfo {
#define WHERE_IN_ABLE 0x00000800 /* Able to support an IN operator */
#define WHERE_ONEROW 0x00001000 /* Selects no more than one row */
#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */
-#define WHERE_TEMP_INDEX 0x00004000 /* Uses an ephemeral index */
+#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
/* Convert a WhereCost value (10 times log2(X)) into its integer value X.
@@ -822,7 +822,7 @@ static u16 operatorMask(int op){
** established when the pScan object was initialized by whereScanInit().
** Return NULL if there are no more matching WhereTerms.
*/
-WhereTerm *whereScanNext(WhereScan *pScan){
+static WhereTerm *whereScanNext(WhereScan *pScan){
int iCur; /* The cursor on the LHS of the term */
int iColumn; /* The column on the LHS of the term. -1 for IPK */
Expr *pX; /* An expression being tested */
@@ -909,7 +909,7 @@ WhereTerm *whereScanNext(WhereScan *pScan){
** If X is not the INTEGER PRIMARY KEY then X must be compatible with
** index pIdx.
*/
-WhereTerm *whereScanInit(
+static WhereTerm *whereScanInit(
WhereScan *pScan, /* The WhereScan object being initialized */
WhereClause *pWC, /* The WHERE clause to be scanned */
int iCur, /* Cursor to scan for */
@@ -1738,6 +1738,7 @@ static void exprAnalyze(
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
+ && OptimizationEnabled(db, SQLITE_Stat3)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -2041,6 +2042,7 @@ static void constructAutomaticIndex(
WhereLoop *pLoop; /* The Loop object */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
+ u8 sentWarning = 0; /* True if a warnning has been issued */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
@@ -2061,6 +2063,12 @@ static void constructAutomaticIndex(
Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
+ if( !sentWarning ){
+ sqlite3_log(SQLITE_WARNING_AUTOINDEX,
+ "automatic index on %s(%s)", pTable->zName,
+ pTable->aCol[iCol].zName);
+ sentWarning = 1;
+ }
if( (idxCols & cMask)==0 ){
if( whereLoopResize(pParse->db, pLoop, nColumn+1) ) return;
pLoop->aLTerm[nColumn++] = pTerm;
@@ -2071,7 +2079,7 @@ static void constructAutomaticIndex(
assert( nColumn>0 );
pLoop->u.btree.nEq = pLoop->nLTerm = nColumn;
pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
- | WHERE_TEMP_INDEX;
+ | WHERE_AUTO_INDEX;
/* Count the number of additional columns needed to create a
** covering index. A "covering index" is an index that contains all
@@ -2571,7 +2579,7 @@ static int whereRangeScanEst(
#ifdef SQLITE_ENABLE_STAT3
- if( nEq==0 && p->nSample ){
+ if( nEq==0 && p->nSample && OptimizationEnabled(pParse->db, SQLITE_Stat3) ){
sqlite3_value *pRangeVal;
tRowcnt iLower = 0;
tRowcnt iUpper = p->aiRowEst[0];
@@ -3117,13 +3125,12 @@ static void explainOneScan(
&& ALWAYS(pLoop->u.btree.pIndex!=0)
){
char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
- ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
- ((flags & WHERE_IDX_ONLY)?"COVERING ":""),
- ((flags & WHERE_TEMP_INDEX)?"":" "),
- ((flags & WHERE_TEMP_INDEX)?"": pLoop->u.btree.pIndex->zName),
- zWhere
- );
+ zMsg = sqlite3MAppendf(db, zMsg,
+ ((flags & WHERE_AUTO_INDEX) ?
+ "%s USING AUTOMATIC %sINDEX%.0s%s" :
+ "%s USING %sINDEX %s%s"),
+ zMsg, ((flags & WHERE_IDX_ONLY) ? "COVERING " : ""),
+ pLoop->u.btree.pIndex->zName, zWhere);
sqlite3DbFree(db, zWhere);
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
@@ -3328,10 +3335,11 @@ static Bitmask codeOneLoopStart(
assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
+ assert( (pStart->wtFlags & TERM_VNULL)==0 );
testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
pX = pStart->pExpr;
assert( pX!=0 );
- assert( pStart->leftCursor==iCur );
+ testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
VdbeComment((v, "pk"));
@@ -3345,7 +3353,8 @@ static Bitmask codeOneLoopStart(
Expr *pX;
pX = pEnd->pExpr;
assert( pX!=0 );
- assert( pEnd->leftCursor==iCur );
+ assert( (pEnd->wtFlags & TERM_VNULL)==0 );
+ testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
memEndValue = ++pParse->nMem;
sqlite3ExprCode(pParse, pX->pRight, memEndValue);
@@ -3794,7 +3803,7 @@ static Bitmask codeOneLoopStart(
** be available.
*/
pSubLoop = pSubWInfo->a[0].pWLoop;
- assert( (pSubLoop->wsFlags & WHERE_TEMP_INDEX)==0 );
+ assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
&& (ii==0 || pSubLoop->u.btree.pIndex==pCov)
){
@@ -3880,6 +3889,7 @@ static Bitmask codeOneLoopStart(
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue;
if( pTerm->leftCursor!=iCur ) continue;
+ if( pLevel->iLeftJoin ) continue;
pE = pTerm->pExpr;
assert( !ExprHasProperty(pE, EP_FromJoin) );
assert( (pTerm->prereqRight & newNotReady)!=0 );
@@ -3976,12 +3986,12 @@ static void whereLoopInit(WhereLoop *p){
** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact.
*/
static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
- if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_TEMP_INDEX) ){
+ if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){
if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){
sqlite3_free(p->u.vtab.idxStr);
p->u.vtab.needFree = 0;
p->u.vtab.idxStr = 0;
- }else if( (p->wsFlags & WHERE_TEMP_INDEX)!=0 && p->u.btree.pIndex!=0 ){
+ }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
sqlite3DbFree(db, p->u.btree.pIndex);
p->u.btree.pIndex = 0;
@@ -4024,7 +4034,7 @@ static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0]));
if( pFrom->wsFlags & WHERE_VIRTUALTABLE ){
pFrom->u.vtab.needFree = 0;
- }else if( (pFrom->wsFlags & WHERE_TEMP_INDEX)!=0 ){
+ }else if( (pFrom->wsFlags & WHERE_AUTO_INDEX)!=0 ){
pFrom->u.btree.pIndex = 0;
}
return SQLITE_OK;
@@ -4274,6 +4284,11 @@ static int whereLoopAddBtreeIndex(
for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
int nIn = 0;
if( pTerm->prereqRight & pNew->maskSelf ) continue;
+#ifdef SQLITE_ENABLE_STAT3
+ if( (pTerm->wtFlags & TERM_VNULL)!=0 && pSrc->pTab->aCol[iCol].notNull ){
+ continue; /* skip IS NOT NULL constraints on a NOT NULL column */
+ }
+#endif
pNew->wsFlags = saved_wsFlags;
pNew->u.btree.nEq = saved_nEq;
pNew->nLTerm = saved_nLTerm;
@@ -4336,7 +4351,8 @@ static int whereLoopAddBtreeIndex(
pNew->nOut = saved_nOut>rDiv+10 ? saved_nOut - rDiv : 10;
}
#ifdef SQLITE_ENABLE_STAT3
- if( pNew->u.btree.nEq==1 && pProbe->nSample ){
+ if( pNew->u.btree.nEq==1 && pProbe->nSample
+ && OptimizationEnabled(db, SQLITE_Stat3) ){
tRowcnt nOut = 0;
if( (pTerm->eOperator & (WO_EQ|WO_ISNULL))!=0 ){
testcase( pTerm->eOperator & WO_EQ );
@@ -4420,7 +4436,7 @@ static Bitmask columnsInIndex(Index *pIdx){
/*
-** Add all WhereLoop objects a single table of the join were the table
+** Add all WhereLoop objects for a single table of the join where the table
** is idenfied by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
*/
@@ -4496,13 +4512,16 @@ static int whereLoopAddBtree(
pNew->nLTerm = 1;
pNew->aLTerm[0] = pTerm;
/* TUNING: One-time cost for computing the automatic index is
- ** approximately 6*N*log2(N) where N is the number of rows in
+ ** approximately 7*N*log2(N) where N is the number of rows in
** the table being indexed. */
- pNew->rSetup = rLogSize + rSize + 26; assert( 26==whereCost(6) );
- /* TUNING: Each index lookup yields 10 rows in the table */
- pNew->nOut = 33; assert( 33==whereCost(10) );
+ pNew->rSetup = rLogSize + rSize + 28; assert( 28==whereCost(7) );
+ /* TUNING: Each index lookup yields 20 rows in the table. This
+ ** is more than the usual guess of 10 rows, since we have no way
+ ** of knowning how selective the index will ultimately be. It would
+ ** not be unreasonable to make this value much larger. */
+ pNew->nOut = 43; assert( 43==whereCost(20) );
pNew->rRun = whereCostAdd(rLogSize,pNew->nOut);
- pNew->wsFlags = WHERE_TEMP_INDEX;
+ pNew->wsFlags = WHERE_AUTO_INDEX;
pNew->prereq = mExtra | pTerm->prereqRight;
rc = whereLoopInsert(pBuilder, pNew);
}
@@ -4881,12 +4900,19 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
/*
** Examine a WherePath (with the addition of the extra WhereLoop of the 5th
** parameters) to see if it outputs rows in the requested ORDER BY
-** (or GROUP BY) without requiring a separate source operation. Return:
+** (or GROUP BY) without requiring a separate sort operation. Return:
**
** 0: ORDER BY is not satisfied. Sorting required
** 1: ORDER BY is satisfied. Omit sorting
** -1: Unknown at this time
**
+** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
+** strict. With GROUP BY and DISTINCT the only requirement is that
+** equivalent rows appear immediately adjacent to one another. GROUP BY
+** and DISTINT do not require rows to appear in any particular order as long
+** as equivelent rows are grouped together. Thus for GROUP BY and DISTINCT
+** the pOrderBy terms can be matched in any order. With ORDER BY, the
+** pOrderBy terms must be matched in strict left-to-right order.
*/
static int wherePathSatisfiesOrderBy(
WhereInfo *pWInfo, /* The WHERE clause */
@@ -5134,7 +5160,7 @@ static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
/*
-** Given the list of WhereLoop objects on pWInfo->pLoops, this routine
+** Given the list of WhereLoop objects at pWInfo->pLoops, this routine
** attempts to find the lowest cost path that visits each WhereLoop
** once. This path is then loaded into the pWInfo->a[].pWLoop fields.
**
@@ -5551,7 +5577,8 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
**
** ORDER BY CLAUSE PROCESSING
**
-** pOrderBy is a pointer to the ORDER BY clause of a SELECT statement,
+** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause
+** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement
** if there is one. If there is no ORDER BY clause or if this routine
** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
*/
@@ -5643,6 +5670,7 @@ WhereInfo *sqlite3WhereBegin(
whereClauseInit(&pWInfo->sWC, pWInfo);
sqlite3ExprCodeConstants(pParse, pWhere);
whereSplit(&pWInfo->sWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
+ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
@@ -5796,6 +5824,7 @@ WhereInfo *sqlite3WhereBegin(
Bitmask tabUsed = exprListTableUsage(pMaskSet, pResultSet);
if( pOrderBy ) tabUsed |= exprListTableUsage(pMaskSet, pOrderBy);
while( pWInfo->nLevel>=2 ){
+ WhereTerm *pTerm, *pEnd;
pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
if( (pWInfo->pTabList->a[pLoop->iTab].jointype & JT_LEFT)==0 ) break;
if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
@@ -5804,6 +5833,15 @@ WhereInfo *sqlite3WhereBegin(
break;
}
if( (tabUsed & pLoop->maskSelf)!=0 ) break;
+ pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
+ for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ ){
+ break;
+ }
+ }
+ if( pTerm<pEnd ) break;
WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
pWInfo->nLevel--;
nTabList--;
@@ -5827,13 +5865,11 @@ WhereInfo *sqlite3WhereBegin(
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
- sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
notReady = ~(Bitmask)0;
for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
struct SrcList_item *pTabItem;
- WhereLoop *pLoop;
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
@@ -5869,7 +5905,7 @@ WhereInfo *sqlite3WhereBegin(
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- if( (pLoop->wsFlags & WHERE_TEMP_INDEX)!=0 ){
+ if( (pLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
constructAutomaticIndex(pParse, &pWInfo->sWC, pTabItem, notReady, pLevel);
}else
#endif
@@ -5992,7 +6028,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
- if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_TEMP_INDEX))==0 ){
+ if( (ws & WHERE_INDEXED)!=0 && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
}