Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -472,10 +472,11 @@ $(TOP)/src/date.c \ $(TOP)/src/dbpage.c \ $(TOP)/src/dbstat.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ + $(TOP)/src/global.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ @@ -688,11 +689,11 @@ -DSQLITE_ENABLE_RTREE \ -DSQLITE_ENABLE_FTS4 \ -DSQLITE_EANBLE_FTS5 dbfuzz2: $(TOP)/test/dbfuzz2.c sqlite3.c sqlite3.h - clang-6.0 -I. -g -O0 -fsanitize=fuzzer,undefined -o dbfuzz2 \ + clang-6.0 -I. -g -O0 -fsanitize=fuzzer,undefined,address -o dbfuzz2 \ $(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c mkdir -p dbfuzz2-dir cp $(TOP)/test/dbfuzz2-seed* dbfuzz2-dir mptester$(TEXE): sqlite3.lo $(TOP)/mptest/mptest.c Index: ext/fts5/fts5_index.c ================================================================== --- ext/fts5/fts5_index.c +++ ext/fts5/fts5_index.c @@ -5400,11 +5400,11 @@ sqlite3Fts5IterClose((Fts5IndexIter*)pRet); pRet = 0; fts5CloseReader(p); } - *ppIter = &pRet->base; + *ppIter = (Fts5IndexIter*)pRet; sqlite3Fts5BufferFree(&buf); } return fts5IndexReturn(p); } Index: ext/fts5/fts5_vocab.c ================================================================== --- ext/fts5/fts5_vocab.c +++ ext/fts5/fts5_vocab.c @@ -429,20 +429,22 @@ int rc = SQLITE_OK; Fts5IndexIter *pIter = pCsr->pIter; i64 *pp = &pCsr->iInstPos; int *po = &pCsr->iInstOff; + assert( sqlite3Fts5IterEof(pIter)==0 ); + assert( pCsr->bEof==0 ); while( eDetail==FTS5_DETAIL_NONE || sqlite3Fts5PoslistNext64(pIter->pData, pIter->nData, po, pp) ){ pCsr->iInstPos = 0; pCsr->iInstOff = 0; rc = sqlite3Fts5IterNextScan(pCsr->pIter); if( rc==SQLITE_OK ){ rc = fts5VocabInstanceNewTerm(pCsr); - if( eDetail==FTS5_DETAIL_NONE ) break; + if( pCsr->bEof || eDetail==FTS5_DETAIL_NONE ) break; } if( rc ){ pCsr->bEof = 1; break; } Index: ext/fts5/test/fts5vocab2.test ================================================================== --- ext/fts5/test/fts5vocab2.test +++ ext/fts5/test/fts5vocab2.test @@ -11,11 +11,11 @@ # # The tests in this file focus on testing the fts5vocab module. # source [file join [file dirname [info script]] fts5_common.tcl] -set testprefix fts5vocab +set testprefix fts5vocab2 # If SQLITE_ENABLE_FTS5 is defined, omit this file. ifcapable !fts5 { finish_test return Index: ext/misc/csv.c ================================================================== --- ext/misc/csv.c +++ ext/misc/csv.c @@ -17,13 +17,13 @@ ** ** .load ./csv ** CREATE VIRTUAL TABLE temp.csv USING csv(filename=FILENAME); ** SELECT * FROM csv; ** -** The columns are named "c1", "c2", "c3", ... by default. But the -** application can define its own CREATE TABLE statement as an additional -** parameter. For example: +** The columns are named "c1", "c2", "c3", ... by default. Or the +** application can define its own CREATE TABLE statement using the +** schema= parameter, like this: ** ** CREATE VIRTUAL TABLE temp.csv2 USING csv( ** filename = "../http.log", ** schema = "CREATE TABLE x(date,ipaddr,url,referrer,userAgent)" ** ); @@ -30,13 +30,13 @@ ** ** Instead of specifying a file, the text of the CSV can be loaded using ** the data= parameter. ** ** If the columns=N parameter is supplied, then the CSV file is assumed to have -** N columns. If the columns parameter is omitted, the CSV file is opened -** as soon as the virtual table is constructed and the first row of the CSV -** is read in order to count the tables. +** N columns. If both the columns= and schema= parameters are omitted, then +** the number and names of the columns is determined by the first line of +** the CSV input. ** ** Some extra debugging features (used for testing virtual tables) are available ** if this module is compiled with -DSQLITE_TEST. */ #include @@ -434,10 +434,38 @@ return 0; } return -1; } +/* Check to see if the string is of the form: "TAG = BOOLEAN" or just "TAG". +** If it is, set *pValue to be the value of the boolean ("true" if there is +** not "= BOOLEAN" component) and return non-zero. If the input string +** does not begin with TAG, return zero. +*/ +static int csv_boolean_parameter( + const char *zTag, /* Tag we are looking for */ + int nTag, /* Size of the tag in bytes */ + const char *z, /* Input parameter */ + int *pValue /* Write boolean value here */ +){ + int b; + z = csv_skip_whitespace(z); + if( strncmp(zTag, z, nTag)!=0 ) return 0; + z = csv_skip_whitespace(z + nTag); + if( z[0]==0 ){ + *pValue = 1; + return 1; + } + if( z[0]!='=' ) return 0; + z = csv_skip_whitespace(z+1); + b = csv_boolean(z); + if( b>=0 ){ + *pValue = b; + return 1; + } + return 0; +} /* ** Parameters: ** filename=FILENAME Name of file containing CSV content ** data=TEXT Direct CSV content. @@ -467,10 +495,11 @@ int rc = SQLITE_OK; /* Result code from this routine */ int i, j; /* Loop counters */ #ifdef SQLITE_TEST int tstFlags = 0; /* Value for testflags=N parameter */ #endif + int b; /* Value of a boolean parameter */ int nCol = -99; /* Value of the columns= parameter */ CsvReader sRdr; /* A CSV file reader used to store an error ** message and/or to count the number of columns */ static const char *azParam[] = { "filename", "data", "schema", @@ -491,25 +520,16 @@ if( csv_string_parameter(&sRdr, azParam[j], z, &azPValue[j]) ) break; } if( j=0 ){ csv_errmsg(&sRdr, "more than one 'header' parameter"); goto csvtab_connect_error; } - x = csv_boolean(zValue); - if( x==1 ){ - bHeader = 1; - }else if( x==0 ){ - bHeader = 0; - }else{ - csv_errmsg(&sRdr, "unrecognized argument to 'header': %s", zValue); - goto csvtab_connect_error; - } + bHeader = b; }else #ifdef SQLITE_TEST if( (zValue = csv_parameter("testflags",9,z))!=0 ){ tstFlags = (unsigned int)atoi(zValue); }else @@ -519,57 +539,98 @@ csv_errmsg(&sRdr, "more than one 'columns' parameter"); goto csvtab_connect_error; } nCol = atoi(zValue); if( nCol<=0 ){ - csv_errmsg(&sRdr, "must have at least one column"); + csv_errmsg(&sRdr, "column= value must be positive"); goto csvtab_connect_error; } }else { - csv_errmsg(&sRdr, "unrecognized parameter '%s'", z); + csv_errmsg(&sRdr, "bad parameter: '%s'", z); goto csvtab_connect_error; } } if( (CSV_FILENAME==0)==(CSV_DATA==0) ){ - csv_errmsg(&sRdr, "must either filename= or data= but not both"); + csv_errmsg(&sRdr, "must specify either filename= or data= but not both"); goto csvtab_connect_error; } - if( nCol<=0 && csv_reader_open(&sRdr, CSV_FILENAME, CSV_DATA) ){ + + if( (nCol<=0 || bHeader==1) + && csv_reader_open(&sRdr, CSV_FILENAME, CSV_DATA) + ){ goto csvtab_connect_error; } pNew = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = (sqlite3_vtab*)pNew; if( pNew==0 ) goto csvtab_connect_oom; memset(pNew, 0, sizeof(*pNew)); - if( nCol>0 ){ + if( CSV_SCHEMA==0 ){ + sqlite3_str *pStr = sqlite3_str_new(0); + char *zSep = ""; + int iCol = 0; + sqlite3_str_appendf(pStr, "CREATE TABLE x("); + if( nCol<0 && bHeader<1 ){ + nCol = 0; + do{ + csv_read_one_field(&sRdr); + nCol++; + }while( sRdr.cTerm==',' ); + } + if( nCol>0 && bHeader<1 ){ + for(iCol=0; iCol0 && iColnCol = nCol; - }else{ + sqlite3_str_appendf(pStr, ")"); + CSV_SCHEMA = sqlite3_str_finish(pStr); + if( CSV_SCHEMA==0 ) goto csvtab_connect_oom; + }else if( nCol<0 ){ do{ csv_read_one_field(&sRdr); pNew->nCol++; }while( sRdr.cTerm==',' ); + }else{ + pNew->nCol = nCol; } pNew->zFilename = CSV_FILENAME; CSV_FILENAME = 0; pNew->zData = CSV_DATA; CSV_DATA = 0; #ifdef SQLITE_TEST pNew->tstFlags = tstFlags; #endif - pNew->iStart = bHeader==1 ? ftell(sRdr.in) : 0; - csv_reader_reset(&sRdr); - if( CSV_SCHEMA==0 ){ - char *zSep = ""; - CSV_SCHEMA = sqlite3_mprintf("CREATE TABLE x("); - if( CSV_SCHEMA==0 ) goto csvtab_connect_oom; - for(i=0; inCol; i++){ - CSV_SCHEMA = sqlite3_mprintf("%z%sc%d TEXT",CSV_SCHEMA, zSep, i); - zSep = ","; - } - CSV_SCHEMA = sqlite3_mprintf("%z);", CSV_SCHEMA); - } + if( bHeader!=1 ){ + pNew->iStart = 0; + }else if( pNew->zData ){ + pNew->iStart = (int)sRdr.iIn; + }else{ + pNew->iStart = ftell(sRdr.in); + } + csv_reader_reset(&sRdr); rc = sqlite3_declare_vtab(db, CSV_SCHEMA); - if( rc ) goto csvtab_connect_error; + if( rc ){ + csv_errmsg(&sRdr, "bad schema: '%s' - %s", CSV_SCHEMA, sqlite3_errmsg(db)); + goto csvtab_connect_error; + } for(i=0; iestimatedCost = (double)1000000; pIdxInfo->estimatedRows = 500; for(i=0; inConstraint; i++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i]; - if( p->usable - && p->iColumn==EXPLN_COLUMN_SQL - && p->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - pIdxInfo->estimatedCost = 10.0; - pIdxInfo->idxNum = 1; - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - pIdxInfo->aConstraintUsage[i].omit = 1; - break; - } + if( p->iColumn!=EXPLN_COLUMN_SQL ) continue; + if( !p->usable ){ + unusable = 1; + }else if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + idx = i; + } + } + if( idx>=0 ){ + /* There exists a usable == constraint against the SQL column */ + pIdxInfo->estimatedCost = 10.0; + pIdxInfo->idxNum = 1; + pIdxInfo->aConstraintUsage[idx].argvIndex = 1; + pIdxInfo->aConstraintUsage[idx].omit = 1; + }else if( unusable ){ + /* There are unusable constraints against the SQL column. Do not allow + ** this plan to continue forward. */ + return SQLITE_CONSTRAINT; } return SQLITE_OK; } /* Index: ext/misc/fileio.c ================================================================== --- ext/misc/fileio.c +++ ext/misc/fileio.c @@ -104,11 +104,22 @@ #endif #include #include +/* +** Structure of the fsdir() table-valued function +*/ + /* 0 1 2 3 4 5 */ #define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)" +#define FSDIR_COLUMN_NAME 0 /* Name of the file */ +#define FSDIR_COLUMN_MODE 1 /* Access mode */ +#define FSDIR_COLUMN_MTIME 2 /* Last modification time */ +#define FSDIR_COLUMN_DATA 3 /* File content */ +#define FSDIR_COLUMN_PATH 4 /* Path to top of search */ +#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */ + /* ** Set the result stored by context ctx to a blob containing the ** contents of file zName. */ @@ -693,24 +704,24 @@ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ int i /* Which column to return */ ){ fsdir_cursor *pCur = (fsdir_cursor*)cur; switch( i ){ - case 0: { /* name */ + case FSDIR_COLUMN_NAME: { sqlite3_result_text(ctx, &pCur->zPath[pCur->nBase], -1, SQLITE_TRANSIENT); break; } - case 1: /* mode */ + case FSDIR_COLUMN_MODE: sqlite3_result_int64(ctx, pCur->sStat.st_mode); break; - case 2: /* mtime */ + case FSDIR_COLUMN_MTIME: sqlite3_result_int64(ctx, pCur->sStat.st_mtime); break; - case 3: { /* data */ + case FSDIR_COLUMN_DATA: { mode_t m = pCur->sStat.st_mode; if( S_ISDIR(m) ){ sqlite3_result_null(ctx); #if !defined(_WIN32) && !defined(WIN32) }else if( S_ISLNK(m) ){ @@ -736,10 +747,16 @@ #endif }else{ readFileContents(ctx, pCur->zPath); } } + case FSDIR_COLUMN_PATH: + default: { + /* The FSDIR_COLUMN_PATH and FSDIR_COLUMN_DIR are input parameters. + ** always return their values as NULL */ + break; + } } return SQLITE_OK; } /* @@ -762,10 +779,13 @@ return (pCur->zPath==0); } /* ** xFilter callback. +** +** idxNum==1 PATH parameter only +** idxNum==2 Both PATH and DIR supplied */ static int fsdirFilter( sqlite3_vtab_cursor *cur, int idxNum, const char *idxStr, int argc, sqlite3_value **argv @@ -814,44 +834,67 @@ ** plan. ** ** In this implementation idxNum is used to represent the ** query plan. idxStr is unused. ** -** The query plan is represented by bits in idxNum: +** The query plan is represented by values of idxNum: ** -** (1) start = $value -- constraint exists -** (2) stop = $value -- constraint exists -** (4) step = $value -- constraint exists -** (8) output in descending order +** (1) The path value is supplied by argv[0] +** (2) Path is in argv[0] and dir is in argv[1] */ static int fsdirBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; /* Loop over constraints */ - int idx4 = -1; - int idx5 = -1; + int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */ + int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */ + int seenPath = 0; /* True if an unusable PATH= constraint is seen */ + int seenDir = 0; /* True if an unusable DIR= constraint is seen */ const struct sqlite3_index_constraint *pConstraint; (void)tab; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - if( pConstraint->iColumn==4 ) idx4 = i; - if( pConstraint->iColumn==5 ) idx5 = i; - } - - if( idx4<0 ){ - pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50); - }else{ - pIdxInfo->aConstraintUsage[idx4].omit = 1; - pIdxInfo->aConstraintUsage[idx4].argvIndex = 1; - if( idx5>=0 ){ - pIdxInfo->aConstraintUsage[idx5].omit = 1; - pIdxInfo->aConstraintUsage[idx5].argvIndex = 2; + if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + switch( pConstraint->iColumn ){ + case FSDIR_COLUMN_PATH: { + if( pConstraint->usable ){ + idxPath = i; + seenPath = 0; + }else if( idxPath<0 ){ + seenPath = 1; + } + break; + } + case FSDIR_COLUMN_DIR: { + if( pConstraint->usable ){ + idxDir = i; + seenDir = 0; + }else if( idxDir<0 ){ + seenDir = 1; + } + break; + } + } + } + if( seenPath || seenDir ){ + /* If input parameters are unusable, disallow this plan */ + return SQLITE_CONSTRAINT; + } + + if( idxPath<0 ){ + pIdxInfo->idxNum = 0; + /* The pIdxInfo->estimatedCost should have been initialized to a huge + ** number. Leave it unchanged. */ + pIdxInfo->estimatedRows = 0x7fffffff; + }else{ + pIdxInfo->aConstraintUsage[idxPath].omit = 1; + pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1; + if( idxDir>=0 ){ + pIdxInfo->aConstraintUsage[idxDir].omit = 1; + pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2; pIdxInfo->idxNum = 2; pIdxInfo->estimatedCost = 10.0; }else{ pIdxInfo->idxNum = 1; pIdxInfo->estimatedCost = 100.0; Index: ext/misc/json1.c ================================================================== --- ext/misc/json1.c +++ ext/misc/json1.c @@ -1992,10 +1992,13 @@ #define JEACH_ATOM 3 #define JEACH_ID 4 #define JEACH_PARENT 5 #define JEACH_FULLKEY 6 #define JEACH_PATH 7 +/* The xBestIndex method assumes that the JSON and ROOT columns are +** the last two columns in the table. Should this ever changes, be +** sure to update the xBestIndex method. */ #define JEACH_JSON 8 #define JEACH_ROOT 9 UNUSED_PARAM(pzErr); UNUSED_PARAM(argv); @@ -2249,39 +2252,58 @@ */ static int jsonEachBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ - int i; - int jsonIdx = -1; - int rootIdx = -1; + int i; /* Loop counter or computed array index */ + int aIdx[2]; /* Index of constraints for JSON and ROOT */ + int unusableMask = 0; /* Mask of unusable JSON and ROOT constraints */ + int idxMask = 0; /* Mask of usable == constraints JSON and ROOT */ const struct sqlite3_index_constraint *pConstraint; - UNUSED_PARAM(tab); - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - switch( pConstraint->iColumn ){ - case JEACH_JSON: jsonIdx = i; break; - case JEACH_ROOT: rootIdx = i; break; - default: /* no-op */ break; - } - } - if( jsonIdx<0 ){ - pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = 1e99; - }else{ - pIdxInfo->estimatedCost = 1.0; - pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1; - pIdxInfo->aConstraintUsage[jsonIdx].omit = 1; - if( rootIdx<0 ){ - pIdxInfo->idxNum = 1; - }else{ - pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2; - pIdxInfo->aConstraintUsage[rootIdx].omit = 1; - pIdxInfo->idxNum = 3; + /* This implementation assumes that JSON and ROOT are the last two + ** columns in the table */ + assert( JEACH_ROOT == JEACH_JSON+1 ); + UNUSED_PARAM(tab); + aIdx[0] = aIdx[1] = -1; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++){ + int iCol; + int iMask; + if( pConstraint->iColumn < JEACH_JSON ) continue; + iCol = pConstraint->iColumn - JEACH_JSON; + assert( iCol==0 || iCol==1 ); + iMask = 1 << iCol; + if( pConstraint->usable==0 ){ + unusableMask |= iMask; + }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + aIdx[iCol] = i; + idxMask |= iMask; + } + } + if( (unusableMask & ~idxMask)!=0 ){ + /* If there are any unusable constraints on JSON or ROOT, then reject + ** this entire plan */ + return SQLITE_CONSTRAINT; + } + if( aIdx[0]<0 ){ + /* No JSON input. Leave estimatedCost at the huge value that it was + ** initialized to to discourage the query planner from selecting this + ** plan. */ + pIdxInfo->idxNum = 0; + }else{ + pIdxInfo->estimatedCost = 1.0; + i = aIdx[0]; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + if( aIdx[1]<0 ){ + pIdxInfo->idxNum = 1; /* Only JSON supplied. Plan 1 */ + }else{ + i = aIdx[1]; + pIdxInfo->aConstraintUsage[i].argvIndex = 2; + pIdxInfo->aConstraintUsage[i].omit = 1; + pIdxInfo->idxNum = 3; /* Both JSON and ROOT are supplied. Plan 3 */ } } return SQLITE_OK; } Index: ext/misc/series.c ================================================================== --- ext/misc/series.c +++ ext/misc/series.c @@ -311,48 +311,49 @@ */ static int seriesBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ - int i; /* Loop over constraints */ - int idxNum = 0; /* The query plan bitmask */ - int startIdx = -1; /* Index of the start= constraint, or -1 if none */ - int stopIdx = -1; /* Index of the stop= constraint, or -1 if none */ - int stepIdx = -1; /* Index of the step= constraint, or -1 if none */ - int nArg = 0; /* Number of arguments that seriesFilter() expects */ - - const struct sqlite3_index_constraint *pConstraint; + int i, j; /* Loop over constraints */ + int idxNum = 0; /* The query plan bitmask */ + int unusableMask = 0; /* Mask of unusable constraints */ + int nArg = 0; /* Number of arguments that seriesFilter() expects */ + int aIdx[3]; /* Constraints on start, stop, and step */ + const struct sqlite3_index_constraint *pConstraint; + + /* This implementation assumes that the start, stop, and step columns + ** are the last three columns in the virtual table. */ + assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 ); + assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 ); + aIdx[0] = aIdx[1] = aIdx[2] = -1; pConstraint = pIdxInfo->aConstraint; for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; - switch( pConstraint->iColumn ){ - case SERIES_COLUMN_START: - startIdx = i; - idxNum |= 1; - break; - case SERIES_COLUMN_STOP: - stopIdx = i; - idxNum |= 2; - break; - case SERIES_COLUMN_STEP: - stepIdx = i; - idxNum |= 4; - break; - } - } - if( startIdx>=0 ){ - pIdxInfo->aConstraintUsage[startIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[startIdx].omit= !SQLITE_SERIES_CONSTRAINT_VERIFY; - } - if( stopIdx>=0 ){ - pIdxInfo->aConstraintUsage[stopIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[stopIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; - } - if( stepIdx>=0 ){ - pIdxInfo->aConstraintUsage[stepIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[stepIdx].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; + int iCol; /* 0 for start, 1 for stop, 2 for step */ + int iMask; /* bitmask for those column */ + if( pConstraint->iColumniColumn - SERIES_COLUMN_START; + assert( iCol>=0 && iCol<=2 ); + iMask = 1 << iCol; + if( pConstraint->usable==0 ){ + unusableMask |= iMask; + continue; + }else if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + idxNum |= iMask; + aIdx[iCol] = i; + } + } + for(i=0; i<3; i++){ + if( (j = aIdx[i])>=0 ){ + pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg; + pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY; + } + } + if( (unusableMask & ~idxNum)!=0 ){ + /* The start, stop, and step columns are inputs. Therefore if there + ** are unusable constraints on any of start, stop, or step then + ** this plan is unusable */ + return SQLITE_CONSTRAINT; } if( (idxNum & 3)==3 ){ /* Both start= and stop= boundaries are available. This is the ** the preferred case */ pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0)); @@ -363,11 +364,10 @@ } }else{ /* If either boundary is missing, we have to generate a huge span ** of numbers. Make this case very expensive so that the query ** planner will work hard to avoid it. */ - pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedRows = 2147483647; } pIdxInfo->idxNum = idxNum; return SQLITE_OK; } Index: ext/misc/zipfile.c ================================================================== --- ext/misc/zipfile.c +++ ext/misc/zipfile.c @@ -1294,29 +1294,30 @@ static int zipfileBestIndex( sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo ){ int i; + int idx = -1; + int unusable = 0; for(i=0; inConstraint; i++){ const struct sqlite3_index_constraint *pCons = &pIdxInfo->aConstraint[i]; - if( pCons->usable==0 ) continue; - if( pCons->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pCons->iColumn!=ZIPFILE_F_COLUMN_IDX ) continue; - break; + if( pCons->usable==0 ){ + unusable = 1; + }else if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ ){ + idx = i; + } } - - if( inConstraint ){ - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - pIdxInfo->aConstraintUsage[i].omit = 1; + if( idx>=0 ){ + pIdxInfo->aConstraintUsage[idx].argvIndex = 1; + pIdxInfo->aConstraintUsage[idx].omit = 1; pIdxInfo->estimatedCost = 1000.0; pIdxInfo->idxNum = 1; - }else{ - pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 50); - pIdxInfo->idxNum = 0; + }else if( unusable ){ + return SQLITE_CONSTRAINT; } - return SQLITE_OK; } static ZipfileEntry *zipfileNewEntry(const char *zPath){ ZipfileEntry *pNew; Index: ext/rtree/geopoly.c ================================================================== --- ext/rtree/geopoly.c +++ ext/rtree/geopoly.c @@ -525,11 +525,11 @@ ){ GeoPoly *p = geopolyFuncParam(context, argv[0], 0); if( p ){ if( geopolyArea(p)<0.0 ){ int ii, jj; - for(ii=2, jj=p->nVertex*2 - 4; iinVertex*2 - 2; iia[ii]; p->a[ii] = p->a[jj]; p->a[jj] = t; t = p->a[ii+1]; p->a[ii+1] = p->a[jj+1]; Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -397,10 +397,11 @@ $(TOP)/src/date.c \ $(TOP)/src/dbpage.c \ $(TOP)/src/dbstat.c \ $(TOP)/src/expr.c \ $(TOP)/src/func.c \ + $(TOP)/src/global.c \ $(TOP)/src/insert.c \ $(TOP)/src/wal.c \ $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ Index: src/alter.c ================================================================== --- src/alter.c +++ src/alter.c @@ -665,14 +665,20 @@ #else # define renameTokenCheckAll(x,y) #endif /* -** Add a new RenameToken object mapping parse tree element pPtr into -** token *pToken to the Parse object currently under construction. +** Remember that the parser tree element pPtr was created using +** the token pToken. +** +** In other words, construct a new RenameToken object and add it +** to the list of RenameToken objects currently being built up +** in pParse->pRename. ** -** Return a copy of pPtr. +** The pPtr argument is returned so that this routine can be used +** with tail recursion in tokenExpr() routine, for a small performance +** improvement. */ void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){ RenameToken *pNew; assert( pPtr || pParse->db->mallocFailed ); renameTokenCheckAll(pParse, pPtr); @@ -1180,19 +1186,12 @@ ** Do a column rename operation on the CREATE statement given in zSql. ** The iCol-th column (left-most is 0) of table zTable is renamed from zCol ** into zNew. The name should be quoted if bQuote is true. ** ** This function is used internally by the ALTER TABLE RENAME COLUMN command. -** Though accessible to application code, it is not intended for use by -** applications. The existance of this function, and the way it works, -** is subject to change without notice. -** -** If any of the parameters are out-of-bounds, then simply return NULL. -** An out-of-bounds parameter can only occur when the application calls -** this function directly. The parameters will always be well-formed when -** this routine is invoked by the bytecode for a legitimate ALTER TABLE -** statement. +** It is only accessible to SQL created using sqlite3NestedParse(). It is +** not reachable from ordinary SQL passed into sqlite3_prepare(). */ static void renameColumnFunc( sqlite3_context *context, int NotUsed, sqlite3_value **argv @@ -1596,12 +1595,12 @@ /* ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { - FUNCTION(sqlite_rename_column, 9, 0, 0, renameColumnFunc), - FUNCTION(sqlite_rename_table, 7, 0, 0, renameTableFunc), - FUNCTION(sqlite_rename_test, 5, 0, 0, renameTableTest), + INTERNAL_FUNCTION(sqlite_rename_column, 9, renameColumnFunc), + INTERNAL_FUNCTION(sqlite_rename_table, 7, renameTableFunc), + INTERNAL_FUNCTION(sqlite_rename_test, 5, renameTableTest), }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } #endif /* SQLITE_ALTER_TABLE */ Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -98,11 +98,11 @@ if( pVfs==0 ) return; pNew = &db->aDb[db->init.iDb]; if( pNew->pBt ) sqlite3BtreeClose(pNew->pBt); pNew->pBt = 0; pNew->pSchema = 0; - rc = sqlite3BtreeOpen(pVfs, "x", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); + rc = sqlite3BtreeOpen(pVfs, "x\0", db, &pNew->pBt, 0, SQLITE_OPEN_MAIN_DB); }else{ /* This is a real ATTACH ** ** Check for the following errors: ** Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -4762,13 +4762,10 @@ offset -= ovflSize; }else{ /* Need to read this page properly. It contains some of the ** range of data that is being read (eOp==0) or written (eOp!=0). */ -#ifdef SQLITE_DIRECT_OVERFLOW_READ - sqlite3_file *fd; /* File from which to do direct overflow read */ -#endif int a = amt; if( a + offset > ovflSize ){ a = ovflSize - offset; } @@ -4775,11 +4772,11 @@ #ifdef SQLITE_DIRECT_OVERFLOW_READ /* If all the following are true: ** ** 1) this is a read operation, and ** 2) data is required from the start of this overflow page, and - ** 3) there is no open write-transaction, and + ** 3) there are no dirty pages in the page-cache ** 4) the database is file-backed, and ** 5) the page is not in the WAL file ** 6) at least 4 bytes have already been read into the output buffer ** ** then data can be read directly from the database file into the @@ -4786,15 +4783,14 @@ ** output buffer, bypassing the page-cache altogether. This speeds ** up loading large records that span many overflow pages. */ if( eOp==0 /* (1) */ && offset==0 /* (2) */ - && pBt->inTransaction==TRANS_READ /* (3) */ - && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */ - && 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */ + && sqlite3PagerDirectReadOk(pBt->pPager, nextPage) /* (3,4,5) */ && &pBuf[-4]>=pBufStart /* (6) */ ){ + sqlite3_file *fd = sqlite3PagerFile(pBt->pPager); u8 aSave[4]; u8 *aWrite = &pBuf[-4]; assert( aWrite>=pBufStart ); /* due to (6) */ memcpy(aSave, aWrite, 4); rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1)); Index: src/dbpage.c ================================================================== --- src/dbpage.c +++ src/dbpage.c @@ -117,13 +117,12 @@ for(i=0; inConstraint; i++){ struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i]; if( p->iColumn!=DBPAGE_COLUMN_SCHEMA ) continue; if( p->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( !p->usable ){ - /* No solution. Use the default SQLITE_BIG_DBL cost */ - pIdxInfo->estimatedRows = 0x7fffffff; - return SQLITE_OK; + /* No solution. */ + return SQLITE_CONSTRAINT; } iPlan = 2; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; break; Index: src/dbstat.c ================================================================== --- src/dbstat.c +++ src/dbstat.c @@ -192,21 +192,19 @@ ** idxNum is normally 0, but will be 1 if a schema=? constraint exists. */ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ int i; - pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */ - /* Look for a valid schema=? constraint. If found, change the idxNum to ** 1 and request the value of that constraint be sent to xFilter. And ** lower the cost estimate to encourage the constrained version to be ** used. */ for(i=0; inConstraint; i++){ - if( pIdxInfo->aConstraint[i].usable==0 ) continue; - if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue; + if( pIdxInfo->aConstraint[i].usable==0 ) return SQLITE_CONSTRAINT; + if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; pIdxInfo->idxNum = 1; pIdxInfo->estimatedCost = 1.0; pIdxInfo->aConstraintUsage[i].argvIndex = 1; pIdxInfo->aConstraintUsage[i].omit = 1; break; @@ -395,10 +393,11 @@ assert( nPayload>=(u32)nLocal ); assert( nLocal<=(nUsable-35) ); if( nPayload>(u32)nLocal ){ int j; int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); + if( iOff+nLocal>nUsable ) goto statPageIsCorrupt; pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); pCell->nOvfl = nOvfl; pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT; pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); Index: src/global.c ================================================================== --- src/global.c +++ src/global.c @@ -238,10 +238,11 @@ #endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ + 0, /* bInternalFunctions */ 0x7ffffffe, /* iOnceResetThreshold */ SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */ }; /* Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -4080,18 +4080,29 @@ break; } /* sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff); ** - ** If parameter onoff is non-zero, configure the wrappers so that all - ** subsequent calls to localtime() and variants fail. If onoff is zero, - ** undo this setting. + ** If parameter onoff is non-zero, subsequent calls to localtime() + ** and its variants fail. If onoff is zero, undo this setting. */ case SQLITE_TESTCTRL_LOCALTIME_FAULT: { sqlite3GlobalConfig.bLocaltimeFault = va_arg(ap, int); break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_INTERNAL_FUNCS, int onoff); + ** + ** If parameter onoff is non-zero, internal-use-only SQL functions + ** are visible to ordinary SQL. This is useful for testing but is + ** unsafe because invalid parameters to those internal-use-only functions + ** can result in crashes or segfaults. + */ + case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: { + sqlite3GlobalConfig.bInternalFunctions = va_arg(ap, int); + break; + } /* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int); ** ** Set or clear a flag that indicates that the database file is always well- ** formed and never corrupt. This flag is clear by default, indicating that Index: src/os_win.c ================================================================== --- src/os_win.c +++ src/os_win.c @@ -282,12 +282,11 @@ #endif #if SQLITE_MAX_MMAP_SIZE>0 int nFetchOut; /* Number of outstanding xFetch references */ HANDLE hMap; /* Handle for accessing memory mapping */ void *pMapRegion; /* Area memory mapped */ - sqlite3_int64 mmapSize; /* Usable size of mapped region */ - sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */ + sqlite3_int64 mmapSize; /* Size of mapped region */ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ #endif }; /* @@ -2904,10 +2903,30 @@ winFile *pFile = (winFile*)id; /* File handle object */ int rc = SQLITE_OK; /* Return code for this function */ DWORD lastErrno; #if SQLITE_MAX_MMAP_SIZE>0 sqlite3_int64 oldMmapSize; + if( pFile->nFetchOut>0 ){ + /* File truncation is a no-op if there are outstanding memory mapped + ** pages. This is because truncating the file means temporarily unmapping + ** the file, and that might delete memory out from under existing cursors. + ** + ** This can result in incremental vacuum not truncating the file, + ** if there is an active read cursor when the incremental vacuum occurs. + ** No real harm comes of this - the database file is not corrupted, + ** though some folks might complain that the file is bigger than it + ** needs to be. + ** + ** The only feasible work-around is to defer the truncation until after + ** all references to memory-mapped content are closed. That is doable, + ** but involves adding a few branches in the common write code path which + ** could slow down normal operations slightly. Hence, we have decided for + ** now to simply make trancations a no-op if there are pending reads. We + ** can maybe revisit this decision in the future. + */ + return SQLITE_OK; + } #endif assert( pFile ); SimulateIOError(return SQLITE_IOERR_TRUNCATE); OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", @@ -4341,13 +4360,13 @@ */ #if SQLITE_MAX_MMAP_SIZE>0 static int winUnmapfile(winFile *pFile){ assert( pFile!=0 ); OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " - "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n", + "mmapSize=%lld, mmapSizeMax=%lld\n", osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, - pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax)); + pFile->mmapSize, pFile->mmapSizeMax)); if( pFile->pMapRegion ){ if( !osUnmapViewOfFile(pFile->pMapRegion) ){ pFile->lastErrno = osGetLastError(); OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, " "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, @@ -4355,11 +4374,10 @@ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, "winUnmapfile1", pFile->zPath); } pFile->pMapRegion = 0; pFile->mmapSize = 0; - pFile->mmapSizeActual = 0; } if( pFile->hMap!=NULL ){ if( !osCloseHandle(pFile->hMap) ){ pFile->lastErrno = osGetLastError(); OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", @@ -4466,11 +4484,10 @@ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); return SQLITE_OK; } pFd->pMapRegion = pNew; pFd->mmapSize = nMap; - pFd->mmapSizeActual = nMap; } OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", osGetCurrentProcessId(), pFd)); return SQLITE_OK; @@ -5268,11 +5285,10 @@ pFile->zPath = zName; #if SQLITE_MAX_MMAP_SIZE>0 pFile->hMap = NULL; pFile->pMapRegion = 0; pFile->mmapSize = 0; - pFile->mmapSizeActual = 0; pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap; #endif OpenCounter(+1); return rc; Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -822,10 +822,34 @@ ** instead of ** ** if( pPager->jfd->pMethods ){ ... */ #define isOpen(pFd) ((pFd)->pMethods!=0) + +#ifdef SQLITE_DIRECT_OVERFLOW_READ +/* +** Return true if page pgno can be read directly from the database file +** by the b-tree layer. This is the case if: +** +** * the database file is open, +** * there are no dirty pages in the cache, and +** * the desired page is not currently in the wal file. +*/ +int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){ + if( pPager->fd->pMethods==0 ) return 0; + if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; +#ifndef SQLITE_OMIT_WAL + if( pPager->pWal ){ + u32 iRead = 0; + int rc; + rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead); + return (rc==SQLITE_OK && iRead==0); + } +#endif + return 1; +} +#endif /* ** Return true if this pager uses a write-ahead log to read page pgno. ** Return false if the pager reads pgno directly from the database. */ Index: src/pager.h ================================================================== --- src/pager.h +++ src/pager.h @@ -190,10 +190,14 @@ void sqlite3PagerSnapshotUnlock(Pager *pPager); # endif #else # define sqlite3PagerUseWal(x,y) 0 #endif + +#ifdef SQLITE_DIRECT_OVERFLOW_READ + int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno); +#endif #ifdef SQLITE_ENABLE_ZIPVFS int sqlite3PagerWalFramesize(Pager *pPager); #endif Index: src/pcache.c ================================================================== --- src/pcache.c +++ src/pcache.c @@ -853,10 +853,19 @@ int nDirty = 0; int nCache = numberOfCachePages(pCache); for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++; return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0; } + +#ifdef SQLITE_DIRECT_OVERFLOW_READ +/* +** Return true if there are one or more dirty pages in the cache. Else false. +*/ +int sqlite3PCacheIsDirty(PCache *pCache){ + return (pCache->pDirty!=0); +} +#endif #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* ** For all dirty pages currently in the cache, invoke the specified ** callback. This is only used if the SQLITE_CHECK_PAGES macro is Index: src/pcache.h ================================================================== --- src/pcache.h +++ src/pcache.h @@ -180,7 +180,11 @@ int sqlite3HeaderSizePcache(void); int sqlite3HeaderSizePcache1(void); /* Number of dirty pages as a percentage of the configured cache size */ int sqlite3PCachePercentDirty(PCache*); + +#ifdef SQLITE_DIRECT_OVERFLOW_READ +int sqlite3PCacheIsDirty(PCache *pCache); +#endif #endif /* _PCACHE_H_ */ Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -1084,16 +1084,17 @@ */ case PragTyp_TABLE_INFO: if( zRight ){ Table *pTab; pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb); if( pTab ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int i, k; int nHidden = 0; Column *pCol; Index *pPk = sqlite3PrimaryKeyIndex(pTab); pParse->nMem = 7; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iTabDb); sqlite3ViewGetColumnNames(pParse, pTab); for(i=0, pCol=pTab->aCol; inCol; i++, pCol++){ int isHidden = IsHiddenColumn(pCol); if( isHidden && pPragma->iArg==0 ){ nHidden++; @@ -1150,10 +1151,11 @@ case PragTyp_INDEX_INFO: if( zRight ){ Index *pIdx; Table *pTab; pIdx = sqlite3FindIndex(db, zRight, zDb); if( pIdx ){ + int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema); int i; int mx; if( pPragma->iArg ){ /* PRAGMA index_xinfo (newer version with more rows and columns) */ mx = pIdx->nColumn; @@ -1162,11 +1164,11 @@ /* PRAGMA index_info (legacy version) */ mx = pIdx->nKeyCol; pParse->nMem = 3; } pTab = pIdx->pTable; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iIdxDb); assert( pParse->nMem<=pPragma->nPragCName ); for(i=0; iaiColumn[i]; sqlite3VdbeMultiLoad(v, 1, "iisX", i, cnum, cnum<0 ? 0 : pTab->aCol[cnum].zName); @@ -1186,12 +1188,13 @@ Index *pIdx; Table *pTab; int i; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); pParse->nMem = 5; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iTabDb); for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){ const char *azOrigin[] = { "c", "u", "pk" }; sqlite3VdbeMultiLoad(v, 1, "isisi", i, pIdx->zName, @@ -1234,10 +1237,11 @@ HashElem *j; FuncDef *p; pParse->nMem = 2; for(i=0; iu.pHash ){ + if( p->funcFlags & SQLITE_FUNC_INTERNAL ) continue; sqlite3VdbeMultiLoad(v, 1, "si", p->zName, 1); } } for(j=sqliteHashFirst(&db->aFunc); j; j=sqliteHashNext(j)){ p = (FuncDef*)sqliteHashData(j); @@ -1275,13 +1279,14 @@ Table *pTab; pTab = sqlite3FindTable(db, zRight, zDb); if( pTab ){ pFK = pTab->pFKey; if( pFK ){ + int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); int i = 0; pParse->nMem = 8; - sqlite3CodeVerifySchema(pParse, iDb); + sqlite3CodeVerifySchema(pParse, iTabDb); while(pFK){ int j; for(j=0; jnCol; j++){ sqlite3VdbeMultiLoad(v, 1, "iissssss", i, @@ -1322,36 +1327,38 @@ regResult = pParse->nMem+1; pParse->nMem += 4; regKey = ++pParse->nMem; regRow = ++pParse->nMem; - sqlite3CodeVerifySchema(pParse, iDb); k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash); while( k ){ + int iTabDb; if( zRight ){ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb); k = 0; }else{ pTab = (Table*)sqliteHashData(k); k = sqliteHashNext(k); } if( pTab==0 || pTab->pFKey==0 ) continue; - sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); + iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema); + sqlite3CodeVerifySchema(pParse, iTabDb); + sqlite3TableLock(pParse, iTabDb, pTab->tnum, 0, pTab->zName); if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow; - sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); + sqlite3OpenTable(pParse, 0, iTabDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regResult, pTab->zName); for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; - sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName); + sqlite3TableLock(pParse, iTabDb, pParent->tnum, 0, pParent->zName); x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0); if( x==0 ){ if( pIdx==0 ){ - sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead); + sqlite3OpenTable(pParse, i, iTabDb, pParent, OP_OpenRead); }else{ - sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb); + sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iTabDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); } }else{ k = 0; break; Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -768,10 +768,19 @@ /* Date/time functions that use 'now', and other functions like ** sqlite_version() that might change over time cannot be used ** in an index. */ notValid(pParse, pNC, "non-deterministic functions", NC_IdxExpr|NC_PartIdx); + } + if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0 + && pParse->nested==0 + && sqlite3Config.bInternalFunctions==0 + ){ + /* Internal-use-only functions are disallowed unless the + ** SQL is being compiled using sqlite3NestedParse() */ + no_such_func = 1; + pDef = 0; } } if( 0==IN_RENAME_OBJECT ){ #ifndef SQLITE_OMIT_WINDOWFUNC Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -7675,10 +7675,11 @@ /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" }, */ { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, + { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "BOOLEAN" }, { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" }, { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" }, #ifdef YYCOVERAGE { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" }, @@ -7769,10 +7770,11 @@ break; /* sqlite3_test_control(int, int) */ case SQLITE_TESTCTRL_ASSERT: case SQLITE_TESTCTRL_ALWAYS: + case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: if( nArg==3 ){ int opt = booleanValue(azArg[2]); rc2 = sqlite3_test_control(testctrl, opt); isOk = 1; } @@ -8063,11 +8065,11 @@ ** the shell is compiled with SQLITE_OMIT_COMPLETE. The default assumes ** any arbitrary text is a complete SQL statement. This is not very ** user-friendly, but it does seem to work. */ #ifdef SQLITE_OMIT_COMPLETE -int sqlite3_complete(const char *zSql){ return 1; } +#define sqlite3_complete(x) 1 #endif /* ** Return true if zSql is a complete SQL statement. Return false if it ** ends in the middle of a string literal or C-style comment. Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -2016,10 +2016,11 @@ ** the call worked. ^The [sqlite3_db_config()] interface will return a ** non-zero [error code] if a discontinued or unsupported configuration option ** is invoked. ** **
+** [[SQLITE_DBCONFIG_LOOKASIDE]] **
SQLITE_DBCONFIG_LOOKASIDE
**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** pointer to a memory buffer to use for lookaside memory. @@ -2038,10 +2039,11 @@ ** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. ** Any attempt to change the lookaside memory configuration when lookaside ** memory is in use leaves the configuration unchanged and returns ** [SQLITE_BUSY].)^
** +** [[SQLITE_DBCONFIG_ENABLE_FKEY]] **
SQLITE_DBCONFIG_ENABLE_FKEY
**
^This option is used to enable or disable the enforcement of ** [foreign key constraints]. There should be two additional arguments. ** The first argument is an integer which is 0 to disable FK enforcement, ** positive to enable FK enforcement or negative to leave FK enforcement @@ -2048,10 +2050,11 @@ ** unchanged. The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether FK enforcement is off or on ** following this call. The second parameter may be a NULL pointer, in ** which case the FK enforcement setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]] **
SQLITE_DBCONFIG_ENABLE_TRIGGER
**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable triggers, ** positive to enable triggers or negative to leave the setting unchanged. @@ -2058,10 +2061,11 @@ ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back.
** +** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] **
SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER
**
^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. @@ -2071,10 +2075,11 @@ ** 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.
** +** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] **
SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION
**
^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. @@ -2088,19 +2093,20 @@ ** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface ** 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. **
** -**
SQLITE_DBCONFIG_MAINDBNAME
+** [[SQLITE_DBCONFIG_MAINDBNAME]]
SQLITE_DBCONFIG_MAINDBNAME
**
^This option is used to change the name of the "main" database ** schema. ^The sole argument is a pointer to a constant UTF8 string ** which will become the new schema name in place of "main". ^SQLite ** does not make a copy of the new main schema name string, so the application ** must ensure that the argument passed into this DBCONFIG option is unchanged ** until after the database connection closes. **
** +** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]] **
SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE
**
Usually, when a database in wal mode is closed or detached from a ** database handle, SQLite checks if this will mean that there are now no ** connections at all to the database. If so, it performs a checkpoint ** operation before closing the connection. This option may be used to @@ -2110,11 +2116,11 @@ ** The second parameter is a pointer to an integer ** into which is written 0 or 1 to indicate whether checkpoints-on-close ** have been disabled - 0 if they are not disabled, 1 if they are. **
** -**
SQLITE_DBCONFIG_ENABLE_QPSG
+** [[SQLITE_DBCONFIG_ENABLE_QPSG]]
SQLITE_DBCONFIG_ENABLE_QPSG
**
^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates ** the [query planner stability guarantee] (QPSG). When the QPSG is active, ** a single SQL query statement will always use the same algorithm regardless ** of values of [bound parameters].)^ The QPSG disables some query optimizations ** that look at the values of bound parameters, which can make some queries @@ -2126,11 +2132,11 @@ ** unchanged. The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether the QPSG is disabled or enabled ** following this call. **
** -**
SQLITE_DBCONFIG_TRIGGER_EQP
+** [[SQLITE_DBCONFIG_TRIGGER_EQP]]
SQLITE_DBCONFIG_TRIGGER_EQP
**
By default, the output of EXPLAIN QUERY PLAN commands does not ** include output for any operations performed by trigger programs. This ** option is used to set or clear (the default) a flag that governs this ** behavior. The first parameter passed to this operation is an integer - ** positive to enable output for trigger programs, or zero to disable it, @@ -2138,11 +2144,11 @@ ** The second parameter is a pointer to an integer into which is written ** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if ** it is not disabled, 1 if it is. **
** -**
SQLITE_DBCONFIG_RESET_DATABASE
+** [[SQLITE_DBCONFIG_RESET_DATABASE]]
SQLITE_DBCONFIG_RESET_DATABASE
**
Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run ** [VACUUM] in order to reset a database back to an empty database ** with no schema and no content. The following process works even for ** a badly corrupted database file: **
    @@ -2158,20 +2164,20 @@ **
** Because resetting a database is destructive and irreversible, the ** process requires the use of this obscure API and multiple steps to help ** ensure that it does not happen by accident. ** -**
SQLITE_DBCONFIG_DEFENSIVE
+** [[SQLITE_DBCONFIG_DEFENSIVE]]
SQLITE_DBCONFIG_DEFENSIVE
**
The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the ** "defensive" flag for a database connection. When the defensive ** flag is enabled, language features that allow ordinary SQL to ** deliberately corrupt the database file are disabled. The disabled ** features include but are not limited to the following: **
    **
  • The [PRAGMA writable_schema=ON] statement. **
  • Writes to the [sqlite_dbpage] virtual table. -**
  • Direct writes to shadow tables. +**
  • Direct writes to [shadow tables]. **
**
**
*/ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ @@ -7235,10 +7241,11 @@ #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */ #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */ +#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 #define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */ #define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19 #define SQLITE_TESTCTRL_NEVER_CORRUPT 20 #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 @@ -8647,10 +8654,11 @@ ** These macros define the various options to the ** [sqlite3_vtab_config()] interface that [virtual table] implementations ** can use to customize and optimize their behavior. ** **
+** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]] **
SQLITE_VTAB_CONSTRAINT_SUPPORT **
Calls of the form ** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, ** where X is an integer. If X is zero, then the [virtual table] whose ** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1697,12 +1697,13 @@ #define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */ #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ #define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ -#define SQLITE_FUNC_WINDOW 0x10000 /* Built-in window-only function */ -#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ +#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ +#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ +#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** @@ -1774,14 +1775,17 @@ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,0,#zName, {0}} #define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xFinal,0,#zName, {0}} - #define WAGGREGATE(zName, nArg, arg, nc, xStep, xFinal, xValue, xInverse, f) \ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|f, \ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,xValue,xInverse,#zName, {0}} +#define INTERNAL_FUNCTION(zName, nArg, xFunc) \ + {nArg, SQLITE_FUNC_INTERNAL|SQLITE_UTF8|SQLITE_FUNC_CONSTANT, \ + 0, 0, xFunc, 0, 0, 0, #zName, {0} } + /* ** All current savepoints are stored in a linked list starting at ** sqlite3.pSavepoint. The first element in the list is the most recently ** opened savepoint. Savepoints are added to the list by the vdbe @@ -3428,10 +3432,11 @@ #endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif int bLocaltimeFault; /* True to fail localtime() calls */ + int bInternalFunctions; /* Internal SQL functions are visible */ int iOnceResetThreshold; /* When to reset OP_Once counters */ u32 szSorterRef; /* Min size in bytes to use sorter-refs */ }; /* Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -6518,11 +6518,11 @@ } /* ** tclcmd: database_may_be_corrupt ** -** Indicate that database files might be corrupt. In other words, set the normal +** Indicate that database files might be corrupt. In other words, set the normal ** state of operation. */ static int SQLITE_TCLAPI database_may_be_corrupt( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ @@ -6533,12 +6533,13 @@ return TCL_OK; } /* ** tclcmd: database_never_corrupt ** -** Indicate that database files are always well-formed. This enables extra assert() -** statements that test conditions that are always true for well-formed databases. +** Indicate that database files are always well-formed. This enables +** extra assert() statements that test conditions that are always true +** for well-formed databases. */ static int SQLITE_TCLAPI database_never_corrupt( ClientData clientData, /* Pointer to sqlite3_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ @@ -6900,13 +6901,14 @@ ){ struct Verb { const char *zName; int i; } aVerb[] = { - { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, - { "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP }, - { "SQLITE_TESTCTRL_IMPOSTER", SQLITE_TESTCTRL_IMPOSTER }, + { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_TESTCTRL_LOCALTIME_FAULT }, + { "SQLITE_TESTCTRL_SORTER_MMAP", SQLITE_TESTCTRL_SORTER_MMAP }, + { "SQLITE_TESTCTRL_IMPOSTER", SQLITE_TESTCTRL_IMPOSTER }, + { "SQLITE_TESTCTRL_INTERNAL_FUNCTIONS", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS}, }; int iVerb; int iFlag; int rc; @@ -6920,18 +6922,19 @@ ); if( rc!=TCL_OK ) return rc; iFlag = aVerb[iVerb].i; switch( iFlag ){ + case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: case SQLITE_TESTCTRL_LOCALTIME_FAULT: { int val; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "ONOFF"); return TCL_ERROR; } if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR; - sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, val); + sqlite3_test_control(iFlag, val); break; } case SQLITE_TESTCTRL_SORTER_MMAP: { int val; Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -469,11 +469,13 @@ int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *); int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*); int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*); int sqlite3VdbeExec(Vdbe*); +#ifndef SQLITE_OMIT_EXPLAIN int sqlite3VdbeList(Vdbe*); +#endif int sqlite3VdbeHalt(Vdbe*); int sqlite3VdbeChangeEncoding(Mem *, int); int sqlite3VdbeMemTooBig(Mem*); int sqlite3VdbeMemCopy(Mem*, const Mem*); void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int); @@ -508,11 +510,13 @@ void sqlite3VdbeMemRelease(Mem *p); int sqlite3VdbeMemFinalize(Mem*, FuncDef*); #ifndef SQLITE_OMIT_WINDOWFUNC int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*); #endif +#ifndef SQLITE_OMIT_EXPLAIN const char *sqlite3OpcodeName(int); +#endif int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve); int sqlite3VdbeMemClearAndResize(Mem *pMem, int n); int sqlite3VdbeCloseStatement(Vdbe *, int); #ifdef SQLITE_DEBUG int sqlite3VdbeFrameIsValid(VdbeFrame*); Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -1029,13 +1029,15 @@ ** The table object reference passed as the second argument to this function ** must represent a virtual table. This function invokes the xBestIndex() ** method of the virtual table with the sqlite3_index_info object that ** comes in as the 3rd argument to this function. ** -** If an error occurs, pParse is populated with an error message and a -** non-zero value is returned. Otherwise, 0 is returned and the output -** part of the sqlite3_index_info structure is left populated. +** If an error occurs, pParse is populated with an error message and an +** appropriate error code is returned. A return of SQLITE_CONSTRAINT from +** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that +** the current configuration of "unusable" flags in sqlite3_index_info can +** not result in a valid plan. ** ** Whether or not an error is returned, it is the responsibility of the ** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates ** that this is required. */ @@ -1045,11 +1047,11 @@ TRACE_IDX_INPUTS(p); rc = pVtab->pModule->xBestIndex(pVtab, p); TRACE_IDX_OUTPUTS(p); - if( rc!=SQLITE_OK ){ + if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){ if( rc==SQLITE_NOMEM ){ sqlite3OomFault(pParse->db); }else if( !pVtab->zErrMsg ){ sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc)); }else{ @@ -1056,23 +1058,11 @@ sqlite3ErrorMsg(pParse, "%s", pVtab->zErrMsg); } } sqlite3_free(pVtab->zErrMsg); pVtab->zErrMsg = 0; - -#if 0 - /* This error is now caught by the caller. - ** Search for "xBestIndex malfunction" below */ - for(i=0; inConstraint; i++){ - if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){ - sqlite3ErrorMsg(pParse, - "table %s: xBestIndex returned an invalid plan", pTab->zName); - } - } -#endif - - return pParse->nErr; + return rc; } #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 /* @@ -3141,11 +3131,21 @@ pIdxInfo->idxFlags = 0; pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed; /* Invoke the virtual table xBestIndex() method */ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo); - if( rc ) return rc; + if( rc ){ + if( rc==SQLITE_CONSTRAINT ){ + /* If the xBestIndex method returns SQLITE_CONSTRAINT, that means + ** that the particular combination of parameters provided is unusable. + ** Make no entries in the loop table. + */ + WHERETRACE(0xffff, (" ^^^^--- non-viable plan rejected!\n")); + return SQLITE_OK; + } + return rc; + } mxTerm = -1; assert( pNew->nLSlot>=nConstraint ); for(i=0; iaLTerm[i] = 0; pNew->u.vtab.omitMask = 0; Index: test/alter.test ================================================================== --- test/alter.test +++ test/alter.test @@ -682,10 +682,11 @@ #-------------------------------------------------------------------------- # alter-9.X - Special test: Make sure the sqlite_rename_column() and # rename_table() functions do not crash when handed bad input. # +sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test alter-9.1 { execsql {SELECT SQLITE_RENAME_COLUMN(0,0,0,0,0,0,0,0,0)} } {{}} foreach {tn sql} { 1 { SELECT SQLITE_RENAME_TABLE(0,0,0,0,0,0,0) } @@ -694,10 +695,18 @@ } { do_test alter-9.2.$tn { catch { execsql $sql } } 1 } +sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 + +# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default), +# then the sqlite_rename_table() SQL function is not accessible to ordinary SQL. +# +do_catchsql_test alter-9.3 { + SELECT sqlite_rename_table(0,0,0,0,0,0,0); +} {1 {no such function: sqlite_rename_table}} #------------------------------------------------------------------------ # alter-10.X - Make sure ALTER TABLE works with multi-byte UTF-8 characters # in the names. # Index: test/altercol.test ================================================================== --- test/altercol.test +++ test/altercol.test @@ -616,10 +616,11 @@ } #------------------------------------------------------------------------- # Passing invalid parameters directly to sqlite_rename_column(). # +sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_execsql_test 14.1 { CREATE TABLE ddd(sql, type, object, db, tbl, icol, znew, bquote); INSERT INTO ddd VALUES( 'CREATE TABLE x1(i INTEGER, t TEXT)', 'table', 'x1', 'main', 'x1', -1, 'zzz', 0 @@ -638,10 +639,19 @@ do_execsql_test 14.2 { SELECT sqlite_rename_column(sql, type, object, db, tbl, icol, znew, bquote, 0) FROM ddd; } {{} {} {} {}} +sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 + +# If the INTERNAL_FUNCTIONS test-control is disabled (which is the default) +# then the sqlite_rename_table() SQL function is not accessible to +# ordinary SQL. +# +do_catchsql_test 14.3 { + SELECT sqlite_rename_column(0,0,0,0,0,0,0,0,0); +} {1 {no such function: sqlite_rename_column}} #------------------------------------------------------------------------- # reset_db do_execsql_test 15.0 { Index: test/altertab.test ================================================================== --- test/altertab.test +++ test/altertab.test @@ -238,15 +238,17 @@ ), ( 'main', NULL, 'ddd', 'eee', 0 ); } {} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_execsql_test 7.2 { SELECT sqlite_rename_table(db, 0, 0, sql, zOld, zNew, bTemp) FROM ddd; } {{} {} {}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 } #------------------------------------------------------------------------- # reset_db Index: test/attach.test ================================================================== --- test/attach.test +++ test/attach.test @@ -727,10 +727,11 @@ } {1 {trigger r5 cannot reference objects in database temp}} } ;# endif subquery ifcapable json1 { do_test attach-5.10 { db close + catch {db2 close} forcedelete test.db sqlite3 db test.db db eval { CREATE TABLE t1(x); CREATE TABLE t2(a,b); Index: test/bestindex4.test ================================================================== --- test/bestindex4.test +++ test/bestindex4.test @@ -153,21 +153,23 @@ do_execsql_test 2.0 { CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); CREATE TABLE t1 (x INT PRIMARY KEY); } {} -do_execsql_test 2.1 { - EXPLAIN QUERY PLAN SELECT * FROM t1, x1 WHERE x1.d=t1.x; +do_eqp_test 2.1 { + SELECT * FROM t1, x1 WHERE x1.d=t1.x; } { - 3 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 0:} - 7 0 0 {SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?)} + QUERY PLAN + |--SCAN TABLE x1 VIRTUAL TABLE INDEX 0: + `--SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?) } -do_execsql_test 2.2 { - EXPLAIN QUERY PLAN SELECT * FROM t1, x1(t1.x) +do_eqp_test 2.2 { + SELECT * FROM t1, x1(t1.x) } { - 3 0 0 {SCAN TABLE t1} - 5 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:} + QUERY PLAN + |--SCAN TABLE t1 + `--SCAN TABLE x1 VIRTUAL TABLE INDEX 555: } finish_test Index: test/csv01.test ================================================================== --- test/csv01.test +++ test/csv01.test @@ -35,10 +35,67 @@ SELECT * FROM t1 WHERE c1='10'; } {9 10 11 12} do_execsql_test 1.2 { SELECT rowid FROM t1; } {1 2 3 4} + +do_execsql_test 1.3 { + DROP TABLE temp.t1; + CREATE VIRTUAL TABLE temp.t1 USING csv( + data= +'a,b,"mix-bloom-eel","soft opinion" +1,2,3,4 +5,6,7,8 +9,10,11,12 +13,14,15,16 +', + header=1 + ); + SELECT * FROM t1 WHERE "soft opinion"=12; +} {9 10 11 12} +do_execsql_test 1.4 { + SELECT name FROM pragma_table_xinfo('t1'); +} {a b mix-bloom-eel {soft opinion}} + +do_execsql_test 1.5 { + DROP TABLE temp.t1; + CREATE VIRTUAL TABLE temp.t1 USING csv( + data= +'a,b,"mix-bloom-eel","soft opinion" +1,2,3,4 +5,6,7,8 +9,10,11,12 +13,14,15,16 +', + header=false + ); + SELECT * FROM t1 WHERE c1='b'; +} {a b mix-bloom-eel {soft opinion}} +do_execsql_test 1.6 { + SELECT name FROM pragma_table_xinfo('t1'); +} {c0 c1 c2 c3} + +do_execsql_test 1.7 { + DROP TABLE temp.t1; + CREATE VIRTUAL TABLE temp.t1 USING csv( + data= +'a,b,"mix-bloom-eel","soft opinion" +1,2,3,4 +5,6,7,8 +9,10,11,12 +13,14,15,16 +', + header, + schema='CREATE TABLE x(x0,x1,x2,x3,x4)', + columns=5 + ); + SELECT * FROM t1 WHERE x1='6'; +} {5 6 7 8 {}} +do_execsql_test 1.8 { + SELECT name FROM pragma_table_xinfo('t1'); +} {x0 x1 x2 x3 x4} + do_execsql_test 2.0 { DROP TABLE t1; CREATE VIRTUAL TABLE temp.t2 USING csv( data= @@ -105,11 +162,11 @@ columns=4, schema= 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID', testflags=1 ); -} {1 {vtable constructor failed: t4}} +} {1 {bad schema: 'CREATE TABLE t3(a,b,c,d,PRIMARY KEY(a,b)) WITHOUT ROWID' - not an error}} # WITHOUT ROWID tables with a single-column PRIMARY KEY may be writable. do_catchsql_test 4.1 { DROP TABLE IF EXISTS t4; CREATE VIRTUAL TABLE temp.t4 USING csv_wr( @@ -136,11 +193,11 @@ columns=4, schema= 'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID', testflags=1 ); -} {1 {vtable constructor failed: t5}} +} {1 {bad schema: 'CREATE TABLE t3(a,b,c,d) WITHOUT ROWID' - PRIMARY KEY missing on table t3}} # 2018-04-24 # Memory leak reported on the sqlite-users mailing list by Ralf Junker. # do_catchsql_test 4.3 { Index: test/e_fkey.test ================================================================== --- test/e_fkey.test +++ test/e_fkey.test @@ -2795,16 +2795,18 @@ } } {{CREATE TABLE c(b REFERENCES "parent"(a))}} do_test e_fkey-61.2.2 { execsql { PRAGMA foreign_keys = OFF; + PRAGMA legacy_alter_table = ON; ALTER TABLE p RENAME TO parent; SELECT sql FROM sqlite_master WHERE name = 'c'; } } {{CREATE TABLE c(b REFERENCES p(a))}} do_test e_fkey-61.2.3 { execsql { PRAGMA foreign_keys = ON } + execsql { PRAGMA legacy_alter_table = OFF } } {} do_test e_fkey-61.3.1 { drop_all_tables execsql { Index: test/fkey2.test ================================================================== --- test/fkey2.test +++ test/fkey2.test @@ -985,19 +985,21 @@ proc test_rename_parent {zCreate zOld zNew} { db eval {SELECT sqlite_rename_table( 'main', 'table', 't1', $zCreate, $zOld, $zNew, 0 )} } + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test fkey2-14.2.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test fkey2-14.2.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test fkey2-14.2.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test fkey2-14.2.2.1 { drop_all_tables @@ -1066,19 +1068,21 @@ PRAGMA foreign_keys = on; SELECT sql FROM temp.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test fkey2-14.2tmp.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test fkey2-14.2tmp.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test fkey2-14.2tmp.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test fkey2-14.2tmp.2.1 { drop_all_tables @@ -1148,19 +1152,21 @@ PRAGMA foreign_keys = on; SELECT sql FROM aux.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test fkey2-14.2aux.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test fkey2-14.2aux.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test fkey2-14.2aux.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test fkey2-14.2aux.2.1 { drop_all_tables Index: test/fuzzcheck.c ================================================================== --- test/fuzzcheck.c +++ test/fuzzcheck.c @@ -403,11 +403,14 @@ /* Return the current wall-clock time */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; - if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ){ + clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ) return 0; + } if( clockVfs->iVersion>=1 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); @@ -864,10 +867,11 @@ int ossFuzzThisDb = 0; /* ossFuzz value for this particular database */ int nativeMalloc = 0; /* Turn off MEMSYS3/5 and lookaside if true */ sqlite3_vfs *pDfltVfs; /* The default VFS */ int openFlags4Data; /* Flags for sqlite3_open_v2() */ + sqlite3_initialize(); iBegin = timeOfDay(); #ifdef __unix__ signal(SIGALRM, timeoutHandler); #endif g.zArgv0 = argv[0]; Index: test/fuzzdata7.db ================================================================== --- test/fuzzdata7.db +++ test/fuzzdata7.db cannot compute difference between binary files Index: test/incrvacuum.test ================================================================== --- test/incrvacuum.test +++ test/incrvacuum.test @@ -780,7 +780,55 @@ execsql { COMMIT; PRAGMA integrity_check; } } {ok} + +#------------------------------------------------------------------------- +# At one point it was unsafe to truncate a db file on windows while there +# were outstanding xFetch() references. This test case attempts to hit +# that case. +# +reset_db +do_execsql_test incrvacuum-16.0 { + PRAGMA auto_vacuum = 2; + CREATE TABLE t3(a); + INSERT INTO t3 VALUES(1), (2), (3), (4); + + CREATE TABLE t2(x); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); + INSERT INTO t2 VALUES( randomblob(1000) ); +} {} + +# Reopen db to ensure the page-cache is empty. +# +db close +sqlite3 db test.db + +# Open db in mmap-mode. Open a transaction, delete some data, then run +# incremental-vacuum. Do not commit the transaction. +# +do_execsql_test incrvacuum-16.1 { + PRAGMA mmap_size = 1000000; + BEGIN; + DELETE FROM t2; + PRAGMA incremental_vacuum = 1000; +} {1000000} + +# Scan through table t3 (which is all clean pages - so mmap is used). Then, +# midway through, commit the transaction. This causes the db to be truncated +# while there are outstanding xFetch pages. +# +do_test incrvacuum-16.2 { + set res [list] + db eval { SELECT a FROM t3 } { + if {$a==3} { db eval COMMIT } + lappend res $a + } + set res +} {1 2 3 4} finish_test Index: test/loadext.test ================================================================== --- test/loadext.test +++ test/loadext.test @@ -59,13 +59,13 @@ {%s: cannot open shared object file: No such file or directory} set dlerror_notadll {%s: file too short} set dlerror_nosymbol {%s: undefined symbol: %s} if {$::tcl_platform(os) eq "Darwin"} { - set dlerror_nosuchfile {dlopen(%s, 10): image not found} - set dlerror_notadll {dlopen(%1$s, 10): no suitable image found.*} - set dlerror_nosymbol {dlsym(XXX, %2$s): symbol not found} + set dlerror_nosuchfile {dlopen.%s, 10.: .*image.*found.*} + set dlerror_notadll {dlopen.%1$s, 10.: .*image.*found.*} + set dlerror_nosymbol {dlsym.XXX, %2$s.: symbol not found} } if {$::tcl_platform(platform) eq "windows"} { set dlerror_nosuchfile {The specified module could not be found.*} set dlerror_notadll {%%1 is not a valid Win32 application.*} Index: test/ossfuzz.c ================================================================== --- test/ossfuzz.c +++ test/ossfuzz.c @@ -34,11 +34,14 @@ ** Julian epoch (-4714-11-24). */ static sqlite3_int64 timeOfDay(void){ static sqlite3_vfs *clockVfs = 0; sqlite3_int64 t; - if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ){ + clockVfs = sqlite3_vfs_find(0); + if( clockVfs==0 ) return 0; + } if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){ clockVfs->xCurrentTimeInt64(clockVfs, &t); }else{ double r; clockVfs->xCurrentTime(clockVfs, &r); @@ -54,13 +57,13 @@ sqlite3 *db; /* The database connection */ sqlite3_int64 iCutoffTime; /* Stop processing at this time. */ sqlite3_int64 iLastCb; /* Time recorded for previous progress callback */ sqlite3_int64 mxInterval; /* Longest interval between two progress calls */ unsigned nCb; /* Number of progress callbacks */ + unsigned execCnt; /* Number of calls to the sqlite3_exec callback */ } FuzzCtx; -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* ** Progress handler callback. ** ** The argument is the cutoff-time after which all processing should ** stop. So return non-zero if the cut-off time is exceeded. @@ -72,11 +75,10 @@ sqlite3_int64 iDiff = iNow - p->iLastCb; if( iDiff > p->mxInterval ) p->mxInterval = iDiff; p->nCb++; return rc; } -#endif /* ** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and ** "PRAGMA parser_trace" since they can dramatically increase the ** amount of output without actually testing anything useful. @@ -99,24 +101,24 @@ } /* ** Callback for sqlite3_exec(). */ -static int exec_handler(void *pCnt, int argc, char **argv, char **namev){ +static int exec_handler(void *pClientData, int argc, char **argv, char **namev){ + FuzzCtx *p = (FuzzCtx*)pClientData; int i; if( argv ){ for(i=0; iexecCnt--)<=0 || progress_handler(pClientData); } /* ** Main entry point. The fuzzer invokes this function with each ** fuzzed input. */ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - int execCnt = 0; /* Abort row callback when count reaches zero */ char *zErrMsg = 0; /* Error message returned by sqlite_exec() */ uint8_t uSelector; /* First byte of input data[] */ int rc; /* Return code from various interfaces */ char *zSql; /* Zero-terminated copy of data[] */ FuzzCtx cx; /* Fuzzing context */ @@ -132,22 +134,23 @@ }else{ uSelector = 0xfd; } /* Open the database connection. Only use an in-memory database. */ + if( sqlite3_initialize() ) return 0; rc = sqlite3_open_v2("fuzz.db", &cx.db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY, 0); if( rc ) return 0; -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK /* Invoke the progress handler frequently to check to see if we ** are taking too long. The progress handler will return true ** (which will block further processing) if more than 10 seconds have ** elapsed since the start of the test. */ cx.iLastCb = timeOfDay(); cx.iCutoffTime = cx.iLastCb + 10000; /* Now + 10 seconds */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK sqlite3_progress_handler(cx.db, 10, progress_handler, (void*)&cx); #endif /* Set a limit on the maximum size of a prepared statement */ sqlite3_limit(cx.db, SQLITE_LIMIT_VDBE_OP, 25000); @@ -159,19 +162,19 @@ /* Do not allow debugging pragma statements that might cause excess output */ sqlite3_set_authorizer(cx.db, block_debug_pragmas, 0); /* Remaining bits of the selector determine a limit on the number of ** output rows */ - execCnt = uSelector + 1; + cx.execCnt = uSelector + 1; /* Run the SQL. The sqlite_exec() interface expects a zero-terminated ** string, so make a copy. */ zSql = sqlite3_mprintf("%.*s", (int)size, data); #ifndef SQLITE_OMIT_COMPLETE sqlite3_complete(zSql); #endif - sqlite3_exec(cx.db, zSql, exec_handler, (void*)&execCnt, &zErrMsg); + sqlite3_exec(cx.db, zSql, exec_handler, (void*)&cx, &zErrMsg); /* Show any errors */ if( (mDebug & FUZZ_SHOW_ERRORS)!=0 && zErrMsg ){ printf("Error: %s\n", zErrMsg); } Index: test/pragma4.test ================================================================== --- test/pragma4.test +++ test/pragma4.test @@ -100,6 +100,130 @@ } string map {\[ x \] x \173 {} \175 {}} \ [db eval {EXPLAIN PRAGMA integrity_check}] } {/ IntegrityCk 2 2 1 x[0-9]+,1x /} + +#-------------------------------------------------------------------------- +# +reset_db +forcedelete test.db2 +do_execsql_test 4.1.1 { + CREATE TABLE t1(a, b, c); + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t2(d, e, f); +} +do_execsql_test 4.1.2 { PRAGMA table_info = t1 } { + 0 a {} 0 {} 0 1 b {} 0 {} 0 2 c {} 0 {} 0 +} +do_execsql_test 4.1.3 { PRAGMA table_info = t2 } { + 0 d {} 0 {} 0 1 e {} 0 {} 0 2 f {} 0 {} 0 +} +do_test 4.1.4 { + sqlite3 db3 test.db + sqlite3 db2 test.db2 + execsql { DROP TABLE t1 } db3 + execsql { DROP TABLE t2 } db2 +} {} +do_execsql_test 4.1.5 { PRAGMA table_info = t1 } +do_execsql_test 4.1.6 { PRAGMA table_info = t2 } + +db2 close +db3 close +reset_db +forcedelete test.db2 +do_execsql_test 4.2.1 { + CREATE TABLE t1(a, b, c); + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t2(d, e, f); +} +do_execsql_test 4.2.2 { SELECT * FROM pragma_table_info('t1') } { + 0 a {} 0 {} 0 1 b {} 0 {} 0 2 c {} 0 {} 0 +} +do_execsql_test 4.2.3 { SELECT * FROM pragma_table_info('t2') } { + 0 d {} 0 {} 0 1 e {} 0 {} 0 2 f {} 0 {} 0 +} +do_test 4.2.4 { + sqlite3 db3 test.db + sqlite3 db2 test.db2 + execsql { DROP TABLE t1 } db3 + execsql { DROP TABLE t2 } db2 +} {} +do_execsql_test 4.2.5 { SELECT * FROM pragma_table_info('t1') } +do_execsql_test 4.2.6 { SELECT * FROM pragma_table_info('t2') } + +db2 close +db3 close +reset_db +forcedelete test.db2 +do_execsql_test 4.3.1 { + CREATE TABLE t1(a, b, c); + CREATE INDEX i1 ON t1(b); + ATTACH 'test.db2' AS aux; + CREATE TABLE aux.t2(d, e, f); + CREATE INDEX aux.i2 ON t2(e); +} +do_execsql_test 4.3.2 { SELECT * FROM pragma_index_info('i1') } {0 1 b} +do_execsql_test 4.3.3 { SELECT * FROM pragma_index_info('i2') } {0 1 e} +do_test 4.3.4 { + sqlite3 db3 test.db + sqlite3 db2 test.db2 + execsql { DROP INDEX i1 } db3 + execsql { DROP INDEX i2 } db2 +} {} +do_execsql_test 4.3.5 { SELECT * FROM pragma_index_info('i1') } +do_execsql_test 4.3.6 { SELECT * FROM pragma_index_info('i2') } + +do_execsql_test 4.4.0 { + CREATE INDEX main.i1 ON t1(b, c); + CREATE INDEX aux.i2 ON t2(e, f); +} +do_execsql_test 4.4.1 { SELECT * FROM pragma_index_list('t1') } {0 i1 0 c 0} +do_execsql_test 4.4.2 { SELECT * FROM pragma_index_list('t2') } {0 i2 0 c 0} +do_test 4.4.3 { + execsql { DROP INDEX i1 } db3 + execsql { DROP INDEX i2 } db2 +} {} +do_execsql_test 4.4.5 { SELECT * FROM pragma_index_list('t1') } {} +do_execsql_test 4.4.6 { SELECT * FROM pragma_index_list('t2') } {} + +do_execsql_test 4.5.0 { + CREATE UNIQUE INDEX main.i1 ON t1(a); + CREATE UNIQUE INDEX aux.i2 ON t2(d); + CREATE TABLE main.c1 (a, b, c REFERENCES t1(a)); + CREATE TABLE aux.c2 (d, e, r REFERENCES t2(d)); +} +do_execsql_test 4.5.1 { SELECT * FROM pragma_foreign_key_list('c1') } { + 0 0 t1 c a {NO ACTION} {NO ACTION} NONE +} +do_execsql_test 4.5.2 { SELECT * FROM pragma_foreign_key_list('c2') } { + 0 0 t2 r d {NO ACTION} {NO ACTION} NONE +} +do_test 4.5.3 { + execsql { DROP TABLE c1 } db3 + execsql { DROP TABLE c2 } db2 +} {} +do_execsql_test 4.5.1 { SELECT * FROM pragma_foreign_key_list('c1') } +do_execsql_test 4.5.2 { SELECT * FROM pragma_foreign_key_list('c2') } + +do_execsql_test 4.6.0 { + CREATE TABLE main.c1 (a, b, c REFERENCES t1(a)); + CREATE TABLE aux.c2 (d, e, r REFERENCES t2(d)); + INSERT INTO main.c1 VALUES(1, 2, 3); + INSERT INTO aux.c2 VALUES(4, 5, 6); +} +do_execsql_test 4.6.1 { pragma foreign_key_check('c1') } { + c1 1 t1 0 +} +do_execsql_test 4.6.2 { pragma foreign_key_check('c2') } { + c2 1 t2 0 +} +do_test 4.6.3 { + execsql { DROP TABLE c2 } db2 +} {} +do_execsql_test 4.6.4 { pragma foreign_key_check('c1') } {c1 1 t1 0} +do_catchsql_test 4.6.5 { + pragma foreign_key_check('c2') +} {1 {no such table: c2}} + finish_test + Index: test/tempdb2.test ================================================================== --- test/tempdb2.test +++ test/tempdb2.test @@ -74,6 +74,27 @@ do_execsql_test 1.4 { SELECT b=int2str(2) FROM t1 } {1 1 1} +#------------------------------------------------------------------------- +db close +sqlite3 db "" +db func int2str int2str + +do_execsql_test 2.0 { + PRAGMA cache_size = -100; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + WITH c(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<100 ) + INSERT INTO t1 SELECT x, int2str(x) FROM c; +} + +do_execsql_test 2.1 { + INSERT INTO t1 VALUES(10001, int2str(1001) || int2str(1001) || int2str(1001)); +} + +do_execsql_test 2.2 { + SELECT b FROM t1 WHERE a = 10001; +} "[int2str 1001][int2str 1001][int2str 1001]" + finish_test + Index: test/without_rowid3.test ================================================================== --- test/without_rowid3.test +++ test/without_rowid3.test @@ -951,19 +951,21 @@ proc test_rename_parent {zCreate zOld zNew} { db eval {SELECT sqlite_rename_table( 'main', 'table', 't1', $zCreate, $zOld, $zNew, 0 )} } + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test without_rowid3-14.2.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test without_rowid3-14.2.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test without_rowid3-14.2.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test without_rowid3-14.2.2.1 { drop_all_tables @@ -1035,19 +1037,21 @@ PRAGMA foreign_keys = on; SELECT sql FROM temp.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test without_rowid3-14.2tmp.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test without_rowid3-14.2tmp.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test without_rowid3-14.2tmp.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test without_rowid3-14.2tmp.2.1 { drop_all_tables @@ -1120,19 +1124,21 @@ PRAGMA foreign_keys = on; SELECT sql FROM aux.sqlite_master WHERE name='t2'; } } {{CREATE TABLE t2(a, b, c REFERENCES t1, d DEFAULT NULL REFERENCES t1, e REFERENCES t1 DEFAULT NULL, h DEFAULT 'text' REFERENCES t1)}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 1 do_test without_rowid3-14.2aux.1.1 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} do_test without_rowid3-14.2aux.1.2 { test_rename_parent {CREATE TABLE t1(a REFERENCES t2)} t4 t3 } {{CREATE TABLE t1(a REFERENCES t2)}} do_test without_rowid3-14.2aux.1.3 { test_rename_parent {CREATE TABLE t1(a REFERENCES "t2")} t2 t3 } {{CREATE TABLE t1(a REFERENCES "t3")}} + sqlite3_test_control SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 0 # Test ALTER TABLE RENAME TABLE a bit. # do_test without_rowid3-14.2aux.2.1 { drop_all_tables Index: test/zipfile.test ================================================================== --- test/zipfile.test +++ test/zipfile.test @@ -759,6 +759,40 @@ do_execsql_test 11.11 { UPDATE z SET name = name || 'suffix'; SELECT name, data FROM z ORDER BY name; } {b0suffix two b2suffix one} + +if {$tcl_platform(platform)!="windows"} { + do_test 12.0 { + catch { file delete -force subdir } + foreach {path sz} { + subdir/x1.txt 143 + subdir/x2.txt 153 + } { + set dir [file dirname $path] + catch { file mkdir $dir } + set fd [open $path w] + puts -nonewline $fd [string repeat 1 $sz] + close $fd + } + } {} + + do_execsql_test 12.1 { + SELECT name FROM fsdir('subdir') ORDER BY 1; + } {subdir subdir/x1.txt subdir/x2.txt} + + do_execsql_test 12.2 { + CREATE TABLE d AS SELECT 'subdir' d; + CREATE TABLE x AS SELECT 1 x; + } + + do_execsql_test 12.4 { + SELECT name FROM d JOIN x JOIN fsdir(d) ORDER BY 1; + } {subdir subdir/x1.txt subdir/x2.txt} + + do_execsql_test 12.5 { + SELECT name FROM d JOIN x JOIN fsdir('.', d) ORDER BY 1; + } {. ./x1.txt ./x2.txt} +} + finish_test Index: tool/warnings.sh ================================================================== --- tool/warnings.sh +++ tool/warnings.sh @@ -14,14 +14,14 @@ WARNING_ANDROID_OPTS="-Wshadow -Wall -Wextra" fi rm -f sqlite3.c make sqlite3.c -echo '********** No optimizations. Includes FTS4/5, RTREE, JSON1 ***' +echo '********** No optimizations. Includes FTS4/5, GEOPOLY, JSON1 ***' echo '********** ' Options: $WARNING_OPTS gcc -c $WARNING_OPTS -std=c89 \ - -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \ + -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_GEOPOLY \ -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 \ sqlite3.c if test x`uname` = 'xLinux'; then echo '********** Android configuration ******************************' echo '********** ' Options: $WARNING_ANDROID_OPTS @@ -50,11 +50,11 @@ echo '********** No optimizations. ENABLE_STAT4. THREADSAFE=0 *******' echo '********** ' Options: $WARNING_OPTS gcc -c $WARNING_OPTS -std=c89 \ -ansi -DSQLITE_ENABLE_STAT4 -DSQLITE_THREADSAFE=0 \ sqlite3.c -echo '********** Optimized -O3. Includes FTS4/5, RTREE, JSON1 ******' +echo '********** Optimized -O3. Includes FTS4/5, GEOPOLY, JSON1 ******' echo '********** ' Options: $WARNING_OPTS gcc -O3 -c $WARNING_OPTS -std=c89 \ - -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_RTREE \ + -ansi -DHAVE_STDINT_H -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_GEOPOLY \ -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_JSON1 \ sqlite3.c