diff --git a/ext/rtree/rtree.c b/ext/rtree/rtree.c index 5665e6aa8c..d47c1092ef 100644 --- a/ext/rtree/rtree.c +++ b/ext/rtree/rtree.c @@ -3133,7 +3133,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){ int jj; nodeGetCell(&tree, &node, ii, &cell); - sqlite3_snprintf(512-nCell,&zCell[nCell],"%d", cell.iRowid); + sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid); nCell = strlen(zCell); for(jj=0; jji ){ - int t; MemPage *pT; - t = apNew[i]->pgno; pT = apNew[i]; apNew[i] = apNew[minI]; apNew[minI] = pT; diff --git a/src/ctime.c b/src/ctime.c index a128f61a69..a04c567b40 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -302,6 +302,9 @@ static const char * const azCompileOpt[] = { #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + "OMIT_UNIQUE_ENFORCEMENT", +#endif #ifdef SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif diff --git a/src/expr.c b/src/expr.c index b7b73946cd..f80ae2ba2d 100644 --- a/src/expr.c +++ b/src/expr.c @@ -389,6 +389,7 @@ Expr *sqlite3ExprAlloc( if( op!=TK_INTEGER || pToken->z==0 || sqlite3GetInt32(pToken->z, &iValue)==0 ){ nExtra = pToken->n+1; + assert( iValue>=0 ); } } pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra); @@ -614,6 +615,8 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){ */ void sqlite3ExprDelete(sqlite3 *db, Expr *p){ if( p==0 ) return; + /* Sanity check: Assert that the IntValue is non-negative if it exists */ + assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 ); if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); @@ -1223,13 +1226,6 @@ int sqlite3ExprIsInteger(Expr *p, int *pValue){ } default: break; } - if( rc ){ - assert( ExprHasAnyProperty(p, EP_Reduced|EP_TokenOnly) - || (p->flags2 & EP2_MallocedToken)==0 ); - p->op = TK_INTEGER; - p->flags |= EP_IntValue; - p->u.iValue = *pValue; - } return rc; } @@ -1954,6 +1950,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ Vdbe *v = pParse->pVdbe; if( pExpr->flags & EP_IntValue ){ int i = pExpr->u.iValue; + assert( i>=0 ); if( negFlag ) i = -i; sqlite3VdbeAddOp2(v, OP_Integer, i, iMem); }else{ @@ -1964,7 +1961,7 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){ c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8); if( c==0 || (c==2 && negFlag) ){ char *zV; - if( negFlag ){ value = -value; } + if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; } zV = dup8bytes(v, (char*)&value); sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64); }else{ diff --git a/src/fkey.c b/src/fkey.c index a385b814ec..653cc1833a 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -687,7 +687,6 @@ void sqlite3FkCheck( int regNew /* New row data is stored here */ ){ sqlite3 *db = pParse->db; /* Database handle */ - Vdbe *v; /* VM to write code to */ FKey *pFKey; /* Used to iterate through FKs */ int iDb; /* Index of database containing pTab */ const char *zDb; /* Name of database containing pTab */ @@ -699,7 +698,6 @@ void sqlite3FkCheck( /* If foreign-keys are disabled, this function is a no-op. */ if( (db->flags&SQLITE_ForeignKeys)==0 ) return; - v = sqlite3GetVdbe(pParse); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zName; diff --git a/src/func.c b/src/func.c index 19c6d22510..6a4f7c09c3 100644 --- a/src/func.c +++ b/src/func.c @@ -1239,13 +1239,8 @@ static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ if( type==SQLITE_INTEGER ){ i64 v = sqlite3_value_int64(argv[0]); p->rSum += v; - if( (p->approx|p->overflow)==0 ){ - i64 iNewSum = p->iSum + v; - int s1 = (int)(p->iSum >> (sizeof(i64)*8-1)); - int s2 = (int)(v >> (sizeof(i64)*8-1)); - int s3 = (int)(iNewSum >> (sizeof(i64)*8-1)); - p->overflow = ((s1&s2&~s3) | (~s1&~s2&s3))?1:0; - p->iSum = iNewSum; + if( (p->approx|p->overflow)==0 && sqlite3AddInt64(&p->iSum, v) ){ + p->overflow = 1; } }else{ p->rSum += sqlite3_value_double(argv[0]); diff --git a/src/insert.c b/src/insert.c index 98f00bfe3d..85b19ae3f7 100644 --- a/src/insert.c +++ b/src/insert.c @@ -465,7 +465,6 @@ void sqlite3Insert( int regIns; /* Block of regs holding rowid+data being inserted */ int regRowid; /* registers holding insert rowid */ int regData; /* register holding first column to insert */ - int regRecord; /* Holds the assemblied row record */ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ @@ -794,7 +793,6 @@ void sqlite3Insert( /* Allocate registers for holding the rowid of the new row, ** the content of the new row, and the assemblied row record. */ - regRecord = ++pParse->nMem; regRowid = regIns = pParse->nMem+1; pParse->nMem += pTab->nCol + 1; if( IsVirtual(pTab) ){ @@ -1188,7 +1186,7 @@ void sqlite3GenerateConstraintChecks( case OE_Rollback: case OE_Fail: { char *zMsg; - j1 = sqlite3VdbeAddOp3(v, OP_HaltIfNull, + sqlite3VdbeAddOp3(v, OP_HaltIfNull, SQLITE_CONSTRAINT, onError, regData+i); zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL", pTab->zName, pTab->aCol[i].zName); @@ -1320,8 +1318,9 @@ void sqlite3GenerateConstraintChecks( */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; +#ifndef SQLITE_OMIT_UNIQUE_ENFORCEMENT int regR; - +#endif if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create a key for accessing the index entry */ @@ -1339,6 +1338,11 @@ void sqlite3GenerateConstraintChecks( sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); + continue; /* Treat pIdx as if it is not a UNIQUE index */ +#else + /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ @@ -1412,6 +1416,7 @@ void sqlite3GenerateConstraintChecks( } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); +#endif } if( pbMayReplace ){ diff --git a/src/main.c b/src/main.c index 3976f7ebc3..f3c1777011 100644 --- a/src/main.c +++ b/src/main.c @@ -375,6 +375,13 @@ int sqlite3_config(int op, ...){ sqlite3GlobalConfig.nHeap = va_arg(ap, int); sqlite3GlobalConfig.mnReq = va_arg(ap, int); + if( sqlite3GlobalConfig.mnReq<1 ){ + sqlite3GlobalConfig.mnReq = 1; + }else if( sqlite3GlobalConfig.mnReq>(1<<12) ){ + /* cap min request size at 2^12 */ + sqlite3GlobalConfig.mnReq = (1<<12); + } + if( sqlite3GlobalConfig.pHeap==0 ){ /* If the heap pointer is NULL, then restore the malloc implementation ** back to NULL pointers too. This will cause the malloc to go diff --git a/src/mem5.c b/src/mem5.c index 2fdfac1413..5d6a5b5806 100644 --- a/src/mem5.c +++ b/src/mem5.c @@ -442,7 +442,7 @@ static int memsys5Roundup(int n){ */ static int memsys5Log(int iValue){ int iLog; - for(iLog=0; (1<mem5.szAtom ){ diff --git a/src/mutex_os2.c b/src/mutex_os2.c index 4438c097c4..85129f3e48 100644 --- a/src/mutex_os2.c +++ b/src/mutex_os2.c @@ -31,11 +31,16 @@ struct sqlite3_mutex { HMTX mutex; /* Mutex controlling the lock */ int id; /* Mutex type */ - int nRef; /* Number of references */ - TID owner; /* Thread holding this mutex */ +#ifdef SQLITE_DEBUG + int trace; /* True to trace changes */ +#endif }; -#define OS2_MUTEX_INITIALIZER 0,0,0,0 +#ifdef SQLITE_DEBUG +#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 } +#else +#define SQLITE3_MUTEX_INITIALIZER { 0, 0 } +#endif /* ** Initialize and deinitialize the mutex subsystem. @@ -51,11 +56,14 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** to sqlite3_mutex_alloc() is one of these integer constants: ** ** ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -69,7 +77,7 @@ static int os2MutexEnd(void){ return SQLITE_OK; } ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should @@ -99,13 +107,13 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ } default: { static volatile int isInit = 0; - static sqlite3_mutex staticMutexes[] = { - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, - { OS2_MUTEX_INITIALIZER, }, + static sqlite3_mutex staticMutexes[6] = { + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, + SQLITE3_MUTEX_INITIALIZER, }; if ( !isInit ){ APIRET rc; @@ -151,9 +159,14 @@ static sqlite3_mutex *os2MutexAlloc(int iType){ ** SQLite is careful to deallocate every mutex that it allocates. */ static void os2MutexFree(sqlite3_mutex *p){ - if( p==0 ) return; - assert( p->nRef==0 ); +#ifdef SQLITE_DEBUG + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + assert( ulCount==0 ); assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ); +#endif DosCloseMutexSem( p->mutex ); sqlite3_free( p ); } @@ -168,26 +181,29 @@ static int os2MutexHeld(sqlite3_mutex *p){ PID pid; ULONG ulCount; PTIB ptib; - if( p!=0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || (p->nRef!=0 && p->owner==tid); + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) ) + return 0; + DosGetInfoBlocks(&ptib, NULL); + return tid==ptib->tib_ptib2->tib2_ultid; } static int os2MutexNotheld(sqlite3_mutex *p){ TID tid; PID pid; ULONG ulCount; PTIB ptib; - if( p!= 0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || p->nRef==0 || p->owner!=tid; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + if( ulCount==0 ) + return 1; + DosGetInfoBlocks(&ptib, NULL); + return tid!=ptib->tib_ptib2->tib2_ultid; +} +static void os2MutexTrace(sqlite3_mutex *p, char *pAction){ + TID tid; + PID pid; + ULONG ulCount; + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount); } #endif @@ -203,32 +219,21 @@ static int os2MutexNotheld(sqlite3_mutex *p){ ** more than once, the behavior is undefined. */ static void os2MutexEnter(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "enter"); +#endif } static int os2MutexTry(sqlite3_mutex *p){ - int rc; - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return SQLITE_OK; + int rc = SQLITE_BUSY; assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) ); - if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR) { - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - p->owner = tid; - p->nRef++; + if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) { rc = SQLITE_OK; - } else { - rc = SQLITE_BUSY; +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "try"); +#endif } - return rc; } @@ -239,19 +244,14 @@ static int os2MutexTry(sqlite3_mutex *p){ ** is not currently allocated. SQLite will never do either. */ static void os2MutexLeave(sqlite3_mutex *p){ - TID tid; - PID holder1; - ULONG holder2; - if( p==0 ) return; - assert( p->nRef>0 ); - DosQueryMutexSem(p->mutex, &holder1, &tid, &holder2); - assert( p->owner==tid ); - p->nRef--; - assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); + assert( os2MutexHeld(p) ); DosReleaseMutexSem(p->mutex); +#ifdef SQLITE_DEBUG + if( p->trace ) os2MutexTrace(p, "leave"); +#endif } -sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ +SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ static const sqlite3_mutex_methods sMutex = { os2MutexInit, os2MutexEnd, @@ -263,6 +263,9 @@ sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ #ifdef SQLITE_DEBUG os2MutexHeld, os2MutexNotheld +#else + 0, + 0 #endif }; diff --git a/src/mutex_w32.c b/src/mutex_w32.c index 8e257a91b0..bfd9dacf6c 100644 --- a/src/mutex_w32.c +++ b/src/mutex_w32.c @@ -280,7 +280,7 @@ static int winMutexTry(sqlite3_mutex *p){ #endif #ifdef SQLITE_DEBUG if( rc==SQLITE_OK && p->trace ){ - printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); + printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); } #endif return rc; diff --git a/src/os_os2.c b/src/os_os2.c index df5ad1026b..2a786b4dea 100644 --- a/src/os_os2.c +++ b/src/os_os2.c @@ -646,19 +646,23 @@ char *convertCpPathToUtf8( const char *in ){ ** sqlite3_file for os2. */ static const sqlite3_io_methods os2IoMethod = { - 1, /* iVersion */ - os2Close, - os2Read, - os2Write, - os2Truncate, - os2Sync, - os2FileSize, - os2Lock, - os2Unlock, - os2CheckReservedLock, - os2FileControl, - os2SectorSize, - os2DeviceCharacteristics + 1, /* iVersion */ + os2Close, /* xClose */ + os2Read, /* xRead */ + os2Write, /* xWrite */ + os2Truncate, /* xTruncate */ + os2Sync, /* xSync */ + os2FileSize, /* xFileSize */ + os2Lock, /* xLock */ + os2Unlock, /* xUnlock */ + os2CheckReservedLock, /* xCheckReservedLock */ + os2FileControl, /* xFileControl */ + os2SectorSize, /* xSectorSize */ + os2DeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0 /* xShmUnmap */ }; /*************************************************************************** @@ -750,100 +754,137 @@ static int os2FullPathname( */ static int os2Open( sqlite3_vfs *pVfs, /* Not used */ - const char *zName, /* Name of the file */ + const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */ ){ HFILE h; - ULONG ulFileAttribute = FILE_NORMAL; ULONG ulOpenFlags = 0; ULONG ulOpenMode = 0; + ULONG ulAction = 0; + ULONG rc; os2File *pFile = (os2File*)id; - APIRET rc = NO_ERROR; - ULONG ulAction; + const char *zUtf8Name = zName; char *zNameCp; - char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */ + char zTmpname[CCHMAXPATH]; + + int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); + int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); + int isCreate = (flags & SQLITE_OPEN_CREATE); + int isReadWrite = (flags & SQLITE_OPEN_READWRITE); +#ifndef NDEBUG + int isReadonly = (flags & SQLITE_OPEN_READONLY); + int eType = (flags & 0xFFFFFF00); + int isOpenJournal = (isCreate && ( + eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_MAIN_JOURNAL + || eType==SQLITE_OPEN_WAL + )); +#endif + + UNUSED_PARAMETER(pVfs); + assert( id!=0 ); + + /* Check the following statements are true: + ** + ** (a) Exactly one of the READWRITE and READONLY flags must be set, and + ** (b) if CREATE is set, then READWRITE must also be set, and + ** (c) if EXCLUSIVE is set, then CREATE must also be set. + ** (d) if DELETEONCLOSE is set, then CREATE must also be set. + */ + assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); + assert(isCreate==0 || isReadWrite); + assert(isExclusive==0 || isCreate); + assert(isDelete==0 || isCreate); + + /* The main DB, main journal, WAL file and master journal are never + ** automatically deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); + + /* Assert that the upper layer has set one of the "file-type" flags. */ + assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB + || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL + || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL + || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL + ); + + memset( pFile, 0, sizeof(*pFile) ); + pFile->pMethod = &os2IoMethod; /* If the second argument to this function is NULL, generate a ** temporary file name to use */ - if( !zName ){ - int rc = getTempname(CCHMAXPATH+1, zTmpname); + if( !zUtf8Name ){ + assert(isDelete && !isOpenJournal); + rc = getTempname(CCHMAXPATH, zTmpname); if( rc!=SQLITE_OK ){ return rc; } - zName = zTmpname; + zUtf8Name = zTmpname; } - - memset( pFile, 0, sizeof(*pFile) ); - - OSTRACE(( "OPEN want %d\n", flags )); - - if( flags & SQLITE_OPEN_READWRITE ){ + if( isReadWrite ){ ulOpenMode |= OPEN_ACCESS_READWRITE; - OSTRACE(( "OPEN read/write\n" )); }else{ ulOpenMode |= OPEN_ACCESS_READONLY; - OSTRACE(( "OPEN read only\n" )); } - if( flags & SQLITE_OPEN_CREATE ){ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; - OSTRACE(( "OPEN open new/create\n" )); - }else{ - ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; - OSTRACE(( "OPEN open existing\n" )); - } - - if( flags & SQLITE_OPEN_MAIN_DB ){ - ulOpenMode |= OPEN_SHARE_DENYNONE; - OSTRACE(( "OPEN share read/write\n" )); - }else{ - ulOpenMode |= OPEN_SHARE_DENYWRITE; - OSTRACE(( "OPEN share read only\n" )); - } - - if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - char pathUtf8[CCHMAXPATH]; -#ifdef NDEBUG /* when debugging we want to make sure it is deleted */ - ulFileAttribute = FILE_HIDDEN; -#endif - os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); - pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); - OSTRACE(( "OPEN hidden/delete on close file attributes\n" )); - }else{ - pFile->pathToDel = NULL; - OSTRACE(( "OPEN normal file attribute\n" )); - } - - /* always open in random access mode for possibly better speed */ + /* Open in random access mode for possibly better speed. Allow full + ** sharing because file locks will provide exclusive access when needed. + */ ulOpenMode |= OPEN_FLAGS_RANDOM; ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; ulOpenMode |= OPEN_FLAGS_NOINHERIT; + ulOpenMode |= OPEN_SHARE_DENYNONE; - zNameCp = convertUtf8PathToCp( zName ); + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + ** created. SQLite doesn't use it to indicate "exclusive access" + ** as it is usually understood. + */ + if( isExclusive ){ + /* Creates a new file, only if it does not already exist. */ + /* If the file exists, it fails. */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS; + }else if( isCreate ){ + /* Open existing file, or create if it doesn't exist */ + ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + }else{ + /* Opens a file, only if it exists. */ + ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; + } + + /* For DELETEONCLOSE, save a pointer to the converted filename */ + if( isDelete ){ + char pathUtf8[CCHMAXPATH]; + os2FullPathname( pVfs, zUtf8Name, CCHMAXPATH, pathUtf8 ); + pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); + } + + zNameCp = convertUtf8PathToCp( zUtf8Name ); rc = DosOpen( (PSZ)zNameCp, &h, &ulAction, 0L, - ulFileAttribute, + FILE_NORMAL, ulOpenFlags, ulOpenMode, (PEAOP2)NULL ); free( zNameCp ); + if( rc != NO_ERROR ){ - OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", - rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode )); + OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n", + rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode )); if( pFile->pathToDel ) free( pFile->pathToDel ); pFile->pathToDel = NULL; - if( flags & SQLITE_OPEN_READWRITE ){ - OSTRACE(( "OPEN %d Invalid handle\n", - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) )); + + if( isReadWrite ){ return os2Open( pVfs, zName, id, - ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE), + ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags ); }else{ return SQLITE_CANTOPEN; @@ -851,10 +892,9 @@ static int os2Open( } if( pOutFlags ){ - *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; + *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; } - pFile->pMethod = &os2IoMethod; pFile->h = h; OpenCounter(+1); OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); @@ -869,13 +909,16 @@ static int os2Delete( const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on os2 */ ){ - APIRET rc = NO_ERROR; - char *zFilenameCp = convertUtf8PathToCp( zFilename ); + APIRET rc; + char *zFilenameCp; SimulateIOError( return SQLITE_IOERR_DELETE ); + zFilenameCp = convertUtf8PathToCp( zFilename ); rc = DosDelete( (PSZ)zFilenameCp ); free( zFilenameCp ); OSTRACE(( "DELETE \"%s\"\n", zFilename )); - return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; + return (rc == NO_ERROR || + rc == ERROR_FILE_NOT_FOUND || + rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* @@ -940,7 +983,7 @@ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ /* no-op */ } -static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ +static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ PFN pfn; APIRET rc; rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); @@ -952,7 +995,7 @@ static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ strncat(_zSymbol, zSymbol, 255); rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); } - return rc != NO_ERROR ? 0 : (void*)pfn; + return rc != NO_ERROR ? 0 : (void(*)(void))pfn; } static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ DosFreeModule((HMODULE)pHandle); @@ -1056,10 +1099,11 @@ int sqlite3_current_time = 0; int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ double now; SHORT minute; /* needs to be able to cope with negative timezone offset */ - USHORT second, hour, + USHORT hundredths, second, hour, day, month, year; DATETIME dt; DosGetDateTime( &dt ); + hundredths = (USHORT)dt.hundredths; second = (USHORT)dt.seconds; minute = (SHORT)dt.minutes + dt.timezone; hour = (USHORT)dt.hours; @@ -1079,6 +1123,7 @@ int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ now += (hour + 12.0)/24.0; now += minute/1440.0; now += second/86400.0; + now += hundredths/8640000.0; *prNow = now; #ifdef SQLITE_TEST if( sqlite3_current_time ){ @@ -1088,6 +1133,22 @@ int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ return 0; } +/* +** Find the current time (in Universal Coordinated Time). Write into *piNow +** the current time and date as a Julian Day number times 86_400_000. In +** other words, write into *piNow the number of milliseconds since the Julian +** epoch of noon in Greenwich on November 24, 4714 B.C according to the +** proleptic Gregorian calendar. +** +** On success, return 0. Return 1 if the time and date cannot be found. +*/ +static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ + double now; + os2CurrentTime(pVfs, &now); + *piNow = now * 86400000; + return 0; +} + static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ return 0; } @@ -1097,7 +1158,7 @@ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ int sqlite3_os_init(void){ static sqlite3_vfs os2Vfs = { - 1, /* iVersion */ + 3, /* iVersion */ sizeof(os2File), /* szOsFile */ CCHMAXPATH, /* mxPathname */ 0, /* pNext */ @@ -1116,6 +1177,10 @@ int sqlite3_os_init(void){ os2Sleep, /* xSleep */ os2CurrentTime, /* xCurrentTime */ os2GetLastError, /* xGetLastError */ + os2CurrentTimeInt64 /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ }; sqlite3_vfs_register(&os2Vfs, 1); initUconvObjects(); diff --git a/src/os_unix.c b/src/os_unix.c index 473432be5f..f771efdad5 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -206,10 +206,10 @@ struct unixFile { int h; /* The file descriptor */ int dirfd; /* File descriptor for the directory */ unsigned char eFileLock; /* The type of lock held on this fd */ + unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ int lastErrno; /* The unix errno from last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ - int fileFlags; /* Miscellanous flags */ const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ @@ -244,9 +244,10 @@ struct unixFile { }; /* -** The following macros define bits in unixFile.fileFlags +** Allowed values for the unixFile.ctrlFlags bitmask: */ -#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ +#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ +#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ /* ** Include code that is common to all os_*.c files @@ -270,16 +271,6 @@ struct unixFile { # define O_BINARY 0 #endif -/* -** The DJGPP compiler environment looks mostly like Unix, but it -** lacks the fcntl() system call. So redefine fcntl() to be something -** that always succeeds. This means that locking does not occur under -** DJGPP. But it is DOS - what did you expect? -*/ -#ifdef __DJGPP__ -# define fcntl(A,B,C) 0 -#endif - /* ** The threadid macro resolves to the thread-id or to 0. Used for ** testing and debugging only. @@ -290,6 +281,184 @@ struct unixFile { #define threadid 0 #endif +/* +** Many system calls are accessed through pointer-to-functions so that +** they may be overridden at runtime to facilitate fault injection during +** testing and sandboxing. The following array holds the names and pointers +** to all overrideable system calls. +*/ +static struct unix_syscall { + const char *zName; /* Name of the sytem call */ + void *pCurrent; /* Current value of the system call */ + void *pDefault; /* Default value */ +} aSyscall[] = { + { "open", (void*)open, 0 }, +#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) + + { "close", (void*)close, 0 }, +#define osClose ((int(*)(int))aSyscall[1].pCurrent) + + { "access", (void*)access, 0 }, +#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) + + { "getcwd", (void*)getcwd, 0 }, +#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) + + { "stat", (void*)stat, 0 }, +#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) + +/* +** The DJGPP compiler environment looks mostly like Unix, but it +** lacks the fcntl() system call. So redefine fcntl() to be something +** that always succeeds. This means that locking does not occur under +** DJGPP. But it is DOS - what did you expect? +*/ +#ifdef __DJGPP__ + { "fstat", 0, 0 }, +#define osFstat(a,b,c) 0 +#else + { "fstat", (void*)fstat, 0 }, +#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) +#endif + + { "ftruncate", (void*)ftruncate, 0 }, +#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) + + { "fcntl", (void*)fcntl, 0 }, +#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) + + { "read", (void*)read, 0 }, +#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) + +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) + { "pread", (void*)pread, 0 }, +#else + { "pread", (void*)0, 0 }, +#endif +#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) + +#if defined(USE_PREAD64) + { "pread64", (void*)pread64, 0 }, +#else + { "pread64", (void*)0, 0 }, +#endif +#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) + + { "write", (void*)write, 0 }, +#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) + +#if defined(USE_PREAD) || defined(SQLITE_ENABLE_LOCKING_STYLE) + { "pwrite", (void*)pwrite, 0 }, +#else + { "pwrite", (void*)0, 0 }, +#endif +#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[12].pCurrent) + +#if defined(USE_PREAD64) + { "pwrite64", (void*)pwrite64, 0 }, +#else + { "pwrite64", (void*)0, 0 }, +#endif +#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ + aSyscall[13].pCurrent) + + { "fchmod", (void*)fchmod, 0 }, +#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) + +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + { "fallocate", (void*)posix_fallocate, 0 }, +#else + { "fallocate", (void*)0, 0 }, +#endif +#define osFallocate ((int(*)(int,off_t,off_t)aSyscall[15].pCurrent) + +}; /* End of the overrideable system calls */ + +/* +** This is the xSetSystemCall() method of sqlite3_vfs for all of the +** "unix" VFSes. Return SQLITE_OK opon successfully updating the +** system call pointer, or SQLITE_NOTFOUND if there is no configurable +** system call named zName. +*/ +static int unixSetSystemCall( + sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ + const char *zName, /* Name of system call to override */ + void *pNewFunc /* Pointer to new system call value */ +){ + int i; + int rc = SQLITE_NOTFOUND; + if( zName==0 ){ + /* If no zName is given, restore all system calls to their default + ** settings and return NULL + */ + for(i=0; il_whence==SEEK_SET ); - s = fcntl(fd, op, p); + s = osFcntl(fd, op, p); savedErrno = errno; sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, @@ -376,7 +545,7 @@ static int lockTrace(int fd, int op, struct flock *p){ if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ struct flock l2; l2 = *p; - fcntl(fd, F_GETLK, &l2); + osFcntl(fd, F_GETLK, &l2); if( l2.l_type==F_RDLCK ){ zType = "RDLCK"; }else if( l2.l_type==F_WRLCK ){ @@ -392,23 +561,18 @@ static int lockTrace(int fd, int op, struct flock *p){ errno = savedErrno; return s; } -#define fcntl lockTrace +#undef osFcntl +#define osFcntl lockTrace #endif /* SQLITE_LOCK_TRACE */ - /* ** Retry ftruncate() calls that fail due to EINTR */ -#ifdef EINTR static int robust_ftruncate(int h, sqlite3_int64 sz){ int rc; - do{ rc = ftruncate(h,sz); }while( rc<0 && errno==EINTR ); + do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); return rc; } -#else -# define robust_ftruncate(a,b) ftruncate(a,b) -#endif - /* ** This routine translates a standard POSIX errno code into something @@ -730,7 +894,8 @@ struct unixFileId { struct unixInodeInfo { struct unixFileId fileId; /* The lookup key */ int nShared; /* Number of SHARED locks held */ - int eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ + unsigned char bProcessLock; /* An exclusive process lock is held */ int nRef; /* Number of pointers to this structure */ unixShmNode *pShmNode; /* Shared memory associated with this inode */ int nLock; /* Number of outstanding file locks */ @@ -767,14 +932,15 @@ static unixInodeInfo *inodeList = 0; ** failed (e.g. "unlink", "open") and the the associated file-system path, ** if any. */ -#define unixLogError(a,b,c) unixLogError_x(a,b,c,__LINE__) -static int unixLogError_x( +#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) +static int unixLogErrorAtLine( int errcode, /* SQLite error code */ const char *zFunc, /* Name of OS function that failed */ const char *zPath, /* File path associated with error */ int iLine /* Source line number where error occurred */ ){ char *zErr; /* Message from strerror() or equivalent */ + int iErrno = errno; /* Saved syscall error number */ /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use ** the strerror() function to obtain the human-readable error message @@ -800,54 +966,59 @@ static int unixLogError_x( #if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) zErr = # endif - strerror_r(errno, aErr, sizeof(aErr)-1); + strerror_r(iErrno, aErr, sizeof(aErr)-1); #elif SQLITE_THREADSAFE /* This is a threadsafe build, but strerror_r() is not available. */ zErr = ""; #else /* Non-threadsafe build, use strerror(). */ - zErr = strerror(errno); + zErr = strerror(iErrno); #endif assert( errcode!=SQLITE_OK ); + if( zPath==0 ) zPath = ""; sqlite3_log(errcode, - "os_unix.c: %s() at line %d - \"%s\" errno=%d path=%s", - zFunc, iLine, zErr, errno, (zPath ? zPath : "n/a") + "os_unix.c:%d: (%d) %s(%s) - %s", + iLine, iErrno, zFunc, zPath, zErr ); return errcode; } +/* +** Close a file descriptor. +** +** We assume that close() almost always works, since it is only in a +** very sick application or on a very sick platform that it might fail. +** If it does fail, simply leak the file descriptor, but do log the +** error. +** +** Note that it is not safe to retry close() after EINTR since the +** file descriptor might have already been reused by another thread. +** So we don't even try to recover from an EINTR. Just log the error +** and move on. +*/ +static void robust_close(unixFile *pFile, int h, int lineno){ + if( osClose(h) ){ + unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", + pFile ? pFile->zPath : 0, lineno); + } +} /* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. -** If all such file descriptors are closed without error, the list is -** cleared and SQLITE_OK returned. -** -** Otherwise, if an error occurs, then successfully closed file descriptor -** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. -** not deleted and SQLITE_IOERR_CLOSE returned. */ -static int closePendingFds(unixFile *pFile){ - int rc = SQLITE_OK; +static void closePendingFds(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; - UnixUnusedFd *pError = 0; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; - if( close(p->fd) ){ - pFile->lastErrno = errno; - rc = unixLogError(SQLITE_IOERR_CLOSE, "close", pFile->zPath); - p->pNext = pError; - pError = p; - }else{ - sqlite3_free(p); - } + robust_close(pFile, p->fd, __LINE__); + sqlite3_free(p); } - pInode->pUnused = pError; - return rc; + pInode->pUnused = 0; } /* @@ -906,7 +1077,7 @@ static int findInodeInfo( ** create a unique name for the file. */ fd = pFile->h; - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; #ifdef EOVERFLOW @@ -927,12 +1098,12 @@ static int findInodeInfo( ** the first page of the database, no damage is done. */ if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ - do{ rc = write(fd, "S", 1); }while( rc<0 && errno==EINTR ); + do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); if( rc!=1 ){ pFile->lastErrno = errno; return SQLITE_IOERR; } - rc = fstat(fd, &statbuf); + rc = osFstat(fd, &statbuf); if( rc!=0 ){ pFile->lastErrno = errno; return SQLITE_IOERR; @@ -995,13 +1166,13 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ /* Otherwise see if some other process holds it. */ #ifndef __DJGPP__ - if( !reserved ){ + if( !reserved && !pFile->pInode->bProcessLock ){ struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = RESERVED_BYTE; lock.l_len = 1; lock.l_type = F_WRLCK; - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { + if (-1 == osFcntl(pFile->h, F_GETLK, &lock)) { int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); pFile->lastErrno = tErrno; @@ -1018,6 +1189,50 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ return rc; } +/* +** Attempt to set a system-lock on the file pFile. The lock is +** described by pLock. +** +** If the pFile was opened read/write from unix-excl, then the only lock +** ever obtained is an exclusive lock, and it is obtained exactly once +** the first time any lock is attempted. All subsequent system locking +** operations become no-ops. Locking operations still happen internally, +** in order to coordinate access between separate database connections +** within this process, but all of that is handled in memory and the +** operating system does not participate. +** +** This function is a pass-through to fcntl(F_SETLK) if pFile is using +** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" +** and is read-only. +*/ +static int unixFileLock(unixFile *pFile, struct flock *pLock){ + int rc; + unixInodeInfo *pInode = pFile->pInode; + assert( unixMutexHeld() ); + assert( pInode!=0 ); + if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock) + && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0) + ){ + if( pInode->bProcessLock==0 ){ + struct flock lock; + assert( pInode->nLock==0 ); + lock.l_whence = SEEK_SET; + lock.l_start = SHARED_FIRST; + lock.l_len = SHARED_SIZE; + lock.l_type = F_WRLCK; + rc = osFcntl(pFile->h, F_SETLK, &lock); + if( rc<0 ) return rc; + pInode->bProcessLock = 1; + pInode->nLock++; + }else{ + rc = 0; + } + }else{ + rc = osFcntl(pFile->h, F_SETLK, pLock); + } + return rc; +} + /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: @@ -1154,7 +1369,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ ){ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); lock.l_start = PENDING_BYTE; - s = fcntl(pFile->h, F_SETLK, &lock); + s = unixFileLock(pFile, &lock); if( s==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); @@ -1176,14 +1391,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){ /* Now get the read-lock */ lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ + if( (s = unixFileLock(pFile, &lock))==(-1) ){ tErrno = errno; } /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; lock.l_type = F_UNLCK; - if( fcntl(pFile->h, F_SETLK, &lock)!=0 ){ + if( unixFileLock(pFile, &lock)!=0 ){ if( s != -1 ){ /* This could happen with a network mount */ tErrno = errno; @@ -1226,7 +1441,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){ default: assert(0); } - s = fcntl(pFile->h, F_SETLK, &lock); + s = unixFileLock(pFile, &lock); if( s==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); @@ -1295,7 +1510,7 @@ static void setPendingFd(unixFile *pFile){ ** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to ** remove the write lock on a region when a read lock is set. */ -static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ +static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ unixFile *pFile = (unixFile*)id; unixInodeInfo *pInode; struct flock lock; @@ -1351,6 +1566,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ if( eFileLock==SHARED_LOCK ){ #if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE + (void)handleNFSUnlock; assert( handleNFSUnlock==0 ); #endif #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE @@ -1361,7 +1577,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile,, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -1373,7 +1589,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -1385,7 +1601,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST+divSize; lock.l_len = SHARED_SIZE-divSize; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -1400,7 +1616,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = SHARED_FIRST; lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ + if( unixFileLock(pFile, &lock)==(-1) ){ tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ @@ -1414,7 +1630,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ lock.l_whence = SEEK_SET; lock.l_start = PENDING_BYTE; lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)!=(-1) ){ pInode->eFileLock = SHARED_LOCK; }else{ tErrno = errno; @@ -1438,7 +1654,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ SimulateIOErrorBenign(1); SimulateIOError( h=(-1) ) SimulateIOErrorBenign(0); - if( fcntl(h, F_SETLK, &lock)!=(-1) ){ + if( unixFileLock(pFile, &lock)!=(-1) ){ pInode->eFileLock = NO_LOCK; }else{ tErrno = errno; @@ -1458,10 +1674,7 @@ static int _posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - int rc2 = closePendingFds(pFile); - if( rc==SQLITE_OK ){ - rc = rc2; - } + closePendingFds(pFile); } } @@ -1479,7 +1692,7 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 0); + return posixUnlock(id, eFileLock, 0); } /* @@ -1496,20 +1709,12 @@ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; if( pFile ){ if( pFile->dirfd>=0 ){ - int err = close(pFile->dirfd); - if( err ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_DIR_CLOSE, "close", pFile->zPath); - }else{ - pFile->dirfd=-1; - } + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd=-1; } if( pFile->h>=0 ){ - int err = close(pFile->h); - if( err ){ - pFile->lastErrno = errno; - return unixLogError(SQLITE_IOERR_CLOSE, "close", pFile->zPath); - } + robust_close(pFile, pFile->h, __LINE__); + pFile->h = -1; } #if OS_VXWORKS if( pFile->pId ){ @@ -1537,6 +1742,8 @@ static int unixClose(sqlite3_file *id){ unixFile *pFile = (unixFile *)id; unixUnlock(id, NO_LOCK); unixEnterMutex(); + assert( pFile->pInode==0 || pFile->pInode->nLock>0 + || pFile->pInode->bProcessLock==0 ); if( pFile->pInode && pFile->pInode->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file @@ -1651,7 +1858,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { }else{ /* The lock is held if and only if the lockfile exists */ const char *zLockFile = (const char*)pFile->lockingContext; - reserved = access(zLockFile, 0)==0; + reserved = osAccess(zLockFile, 0)==0; } OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); *pResOut = reserved; @@ -1705,7 +1912,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } /* grab an exclusive lock */ - fd = open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); + fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600); if( fd<0 ){ /* failed to open/create the file, someone else may have stolen the lock */ int tErrno = errno; @@ -1719,10 +1926,7 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) { } return rc; } - if( close(fd) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - } + robust_close(pFile, fd, __LINE__); /* got it, set the type and return ok */ pFile->eFileLock = eFileLock; @@ -2614,7 +2818,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) { pInode->nLock--; assert( pInode->nLock>=0 ); if( pInode->nLock==0 ){ - rc = closePendingFds(pFile); + closePendingFds(pFile); } } } @@ -2671,7 +2875,7 @@ static int afpClose(sqlite3_file *id) { ** the requested locking level, this routine is a no-op. */ static int nfsUnlock(sqlite3_file *id, int eFileLock){ - return _posixUnlock(id, eFileLock, 1); + return posixUnlock(id, eFileLock, 1); } #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ @@ -2713,10 +2917,10 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - do{ got = pread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); + do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) - do{ got = pread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); + do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR); SimulateIOError( got = -1 ); #else newOffset = lseek(id->h, offset, SEEK_SET); @@ -2729,7 +2933,7 @@ static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ } return -1; } - do{ got = read(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); + do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -2791,9 +2995,9 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ #endif TIMER_START; #if defined(USE_PREAD) - do{ got = pwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); + do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); #elif defined(USE_PREAD64) - do{ got = pwrite64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR ); + do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR); #else newOffset = lseek(id->h, offset, SEEK_SET); if( newOffset!=offset ){ @@ -2804,7 +3008,7 @@ static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ } return -1; } - do{ got = write(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); + do{ got = osWrite(id->h, pBuf, cnt); }while( got<0 && errno==EINTR ); #endif TIMER_END; if( got<0 ){ @@ -2972,7 +3176,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ rc = SQLITE_OK; #elif HAVE_FULLFSYNC if( fullSync ){ - rc = fcntl(fd, F_FULLFSYNC, 0); + rc = osFcntl(fd, F_FULLFSYNC, 0); }else{ rc = 1; } @@ -3047,7 +3251,6 @@ static int unixSync(sqlite3_file *id, int flags){ return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); } if( pFile->dirfd>=0 ){ - int err; OSTRACE(("DIRSYNC %-3d (have_fullfsync=%d fullsync=%d)\n", pFile->dirfd, HAVE_FULLFSYNC, isFullsync)); #ifndef SQLITE_DISABLE_DIRSYNC @@ -3066,13 +3269,9 @@ static int unixSync(sqlite3_file *id, int flags){ /* return SQLITE_IOERR; */ } #endif - err = close(pFile->dirfd); /* Only need to sync once, so close the */ - if( err==0 ){ /* directory when we are done */ - pFile->dirfd = -1; - }else{ - pFile->lastErrno = errno; - rc = unixLogError(SQLITE_IOERR_DIR_CLOSE, "close", pFile->zPath); - } + /* Only need to sync once, so close the directory when we are done */ + robust_close(pFile, pFile->dirfd, __LINE__); + pFile->dirfd = -1; } return rc; } @@ -3124,7 +3323,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); - rc = fstat(((unixFile*)id)->h, &buf); + rc = osFstat(((unixFile*)id)->h, &buf); SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; @@ -3165,14 +3364,14 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ i64 nSize; /* Required file size */ struct stat buf; /* Used to hold return values of fstat() */ - if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; if( nSize>(i64)buf.st_size ){ #if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE int rc; do{ - rc = posix_fallocate(pFile-.h, buf.st_size, nSize-buf.st_size; + rc = osFallocate(pFile->.h, buf.st_size, nSize-buf.st_size; }while( rc<0 && errno=EINTR ); if( rc ) return SQLITE_IOERR_WRITE; #else @@ -3373,15 +3572,17 @@ static int unixShmSystemLock( /* Locks are within range */ assert( n>=1 && nh>=0 ){ + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + f.l_start = ofst; + f.l_len = n; - rc = fcntl(pShmNode->h, F_SETLK, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + rc = osFcntl(pShmNode->h, F_SETLK, &f); + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + } /* Update the global lock state and do debug tracing */ #ifdef SQLITE_DEBUG @@ -3436,10 +3637,17 @@ static void unixShmPurge(unixFile *pFd){ assert( p->pInode==pFd->pInode ); if( p->mutex ) sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ - munmap(p->apRegion[i], p->szRegion); + if( p->h>=0 ){ + munmap(p->apRegion[i], p->szRegion); + }else{ + sqlite3_free(p->apRegion[i]); + } } sqlite3_free(p->apRegion); - if( p->h>=0 ) close(p->h); + if( p->h>=0 ){ + robust_close(pFd, p->h, __LINE__); + p->h = -1; + } p->pInode->pShmNode = 0; sqlite3_free(p); } @@ -3473,6 +3681,12 @@ static void unixShmPurge(unixFile *pFd){ ** When opening a new shared-memory file, if no other instances of that ** file are currently open, in this process or in other processes, then ** the file must be truncated to zero length or have its header cleared. +** +** If the original database file (pDbFd) is using the "unix-excl" VFS +** that means that an exclusive lock is held on the database file and +** that no other processes are able to read or write the database. In +** that case, we do not really need shared memory. No shared memory +** file is created. The shared memory will be simulated with heap memory. */ static int unixOpenSharedMemory(unixFile *pDbFd){ struct unixShm *p = 0; /* The connection to be opened */ @@ -3502,7 +3716,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ ** with the same permissions. The actual permissions the file is created ** with are subject to the current umask setting. */ - if( fstat(pDbFd->h, &sStat) ){ + if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){ rc = SQLITE_IOERR_FSTAT; goto shm_open_err; } @@ -3535,25 +3749,28 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ goto shm_open_err; } - pShmNode->h = open(zShmFilename, O_RDWR|O_CREAT, (sStat.st_mode & 0777)); - if( pShmNode->h<0 ){ - rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); - goto shm_open_err; - } - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - rc = SQLITE_OK; - if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ - if( robust_ftruncate(pShmNode->h, 0) ){ - rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + if( pInode->bProcessLock==0 ){ + pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT, + (sStat.st_mode & 0777)); + if( pShmNode->h<0 ){ + rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); + goto shm_open_err; } + + /* Check to see if another process is holding the dead-man switch. + ** If not, truncate the file to zero length. + */ + rc = SQLITE_OK; + if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ + if( robust_ftruncate(pShmNode->h, 0) ){ + rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); + } + } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); + } + if( rc ) goto shm_open_err; } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1); - } - if( rc ) goto shm_open_err; } /* Make the new connection a child of the unixShmNode */ @@ -3627,6 +3844,9 @@ static int unixShmMap( pShmNode = p->pShmNode; sqlite3_mutex_enter(pShmNode->mutex); assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + assert( pShmNode->pInode==pDbFd->pInode ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); if( pShmNode->nRegion<=iRegion ){ char **apNew; /* New apRegion[] array */ @@ -3635,27 +3855,30 @@ static int unixShmMap( pShmNode->szRegion = szRegion; - /* The requested region is not mapped into this processes address space. - ** Check to see if it has been allocated (i.e. if the wal-index file is - ** large enough to contain the requested region). - */ - if( fstat(pShmNode->h, &sStat) ){ - rc = SQLITE_IOERR_SHMSIZE; - goto shmpage_out; - } - - if( sStat.st_sizeh>=0 ){ + /* The requested region is not mapped into this processes address space. + ** Check to see if it has been allocated (i.e. if the wal-index file is + ** large enough to contain the requested region). */ - if( !bExtend ) goto shmpage_out; - if( robust_ftruncate(pShmNode->h, nByte) ){ - rc = unixLogError(SQLITE_IOERR_SHMSIZE,"ftruncate",pShmNode->zFilename); + if( osFstat(pShmNode->h, &sStat) ){ + rc = SQLITE_IOERR_SHMSIZE; goto shmpage_out; } + + if( sStat.st_sizeh, nByte) ){ + rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate", + pShmNode->zFilename); + goto shmpage_out; + } + } } /* Map the requested memory region into this processes address space. */ @@ -3668,12 +3891,22 @@ static int unixShmMap( } pShmNode->apRegion = apNew; while(pShmNode->nRegion<=iRegion){ - void *pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, - MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion - ); - if( pMem==MAP_FAILED ){ - rc = SQLITE_IOERR; - goto shmpage_out; + void *pMem; + if( pShmNode->h>=0 ){ + pMem = mmap(0, szRegion, PROT_READ|PROT_WRITE, + MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion + ); + if( pMem==MAP_FAILED ){ + rc = SQLITE_IOERR; + goto shmpage_out; + } + }else{ + pMem = sqlite3_malloc(szRegion); + if( pMem==0 ){ + rc = SQLITE_NOMEM; + goto shmpage_out; + } + memset(pMem, 0, szRegion); } pShmNode->apRegion[pShmNode->nRegion] = pMem; pShmNode->nRegion++; @@ -3720,6 +3953,8 @@ static int unixShmLock( || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); + assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); mask = (1<<(ofst+n)) - (1<1 || mask==(1<nRef>0 ); pShmNode->nRef--; if( pShmNode->nRef==0 ){ - if( deleteFlag ) unlink(pShmNode->zFilename); + if( deleteFlag && pShmNode->h>=0 ) unlink(pShmNode->zFilename); unixShmPurge(pDbFd); } unixLeaveMutex(); @@ -4098,7 +4333,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ return &nfsIoMethods; } else { @@ -4140,7 +4375,7 @@ static const sqlite3_io_methods *autolockIoFinderImpl( lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; @@ -4174,7 +4409,8 @@ static int fillInUnixFile( sqlite3_file *pId, /* Write to the unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int noLock, /* Omit locking if true */ - int isDelete /* Delete on close if true */ + int isDelete, /* Delete on close if true */ + int isReadOnly /* True if the file is opened read-only */ ){ const sqlite3_io_methods *pLockingStyle; unixFile *pNew = (unixFile *)pId; @@ -4201,8 +4437,15 @@ static int fillInUnixFile( OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->dirfd = dirfd; - pNew->fileFlags = 0; pNew->zPath = zFilename; + if( memcmp(pVfs->zName,"unix-excl",10)==0 ){ + pNew->ctrlFlags = UNIXFILE_EXCL; + }else{ + pNew->ctrlFlags = 0; + } + if( isReadOnly ){ + pNew->ctrlFlags |= UNIXFILE_RDONLY; + } #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); @@ -4250,7 +4493,7 @@ static int fillInUnixFile( ** implicit assumption here is that if fstat() fails, things are in ** such bad shape that dropping a lock or two doesn't matter much. */ - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -4276,7 +4519,7 @@ static int fillInUnixFile( rc = findInodeInfo(pNew, &pNew->pInode); if( rc!=SQLITE_OK ){ sqlite3_free(pNew->lockingContext); - close(h); + robust_close(pNew, h, __LINE__); h = -1; } unixLeaveMutex(); @@ -4327,7 +4570,7 @@ static int fillInUnixFile( pNew->lastErrno = 0; #if OS_VXWORKS if( rc!=SQLITE_OK ){ - if( h>=0 ) close(h); + if( h>=0 ) robust_close(pNew, h, __LINE__); h = -1; unlink(zFilename); isDelete = 0; @@ -4335,8 +4578,8 @@ static int fillInUnixFile( pNew->isDelete = isDelete; #endif if( rc!=SQLITE_OK ){ - if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - if( h>=0 ) close(h); + if( dirfd>=0 ) robust_close(pNew, dirfd, __LINE__); + if( h>=0 ) robust_close(pNew, h, __LINE__); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); @@ -4363,10 +4606,10 @@ static int openDirectory(const char *zFilename, int *pFd){ for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--); if( ii>0 ){ zDirname[ii] = '\0'; - fd = open(zDirname, O_RDONLY|O_BINARY, 0); + fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); if( fd>=0 ){ #ifdef FD_CLOEXEC - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); #endif OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); } @@ -4396,9 +4639,9 @@ static const char *unixTempFileDir(void){ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); for(i=0; ilastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ - close(fd); /* silently leak if fail, in error */ + if( dirfd>=0 ) robust_close(p, dirfd, __LINE__); + robust_close(p, fd, __LINE__); return SQLITE_IOERR_ACCESS; } if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { @@ -4797,16 +5041,17 @@ static int unixOpen( ** the same file are working. */ p->lastErrno = errno; if( dirfd>=0 ){ - close(dirfd); /* silently leak if fail, in error */ + robust_close(p, dirfd, __LINE__); } - close(fd); /* silently leak if fail, in error */ + robust_close(p, fd, __LINE__); rc = SQLITE_IOERR_ACCESS; goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } if( useProxy ){ - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); if( rc!=SQLITE_OK ){ @@ -4823,7 +5068,8 @@ static int unixOpen( } #endif - rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, + isDelete, isReadonly); open_finished: if( rc!=SQLITE_OK ){ sqlite3_free(p->pUnused); @@ -4860,9 +5106,7 @@ static int unixDelete( { rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); } - if( close(fd)&&!rc ){ - rc = unixLogError(SQLITE_IOERR_DIR_CLOSE, "close", zPath); - } + robust_close(0, fd, __LINE__); } } #endif @@ -4902,7 +5146,7 @@ static int unixAccess( default: assert(!"Invalid flags argument"); } - *pResOut = (access(zPath, amode)==0); + *pResOut = (osAccess(zPath, amode)==0); if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){ struct stat buf; if( 0==stat(zPath, &buf) && buf.st_size==0 ){ @@ -4944,7 +5188,7 @@ static int unixFullPathname( sqlite3_snprintf(nOut, zOut, "%s", zPath); }else{ int nCwd; - if( getcwd(zOut, nOut-1)==0 ){ + if( osGetcwd(zOut, nOut-1)==0 ){ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); } nCwd = (int)strlen(zOut); @@ -5039,7 +5283,7 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ #if !defined(SQLITE_TEST) { int pid, fd; - fd = open("/dev/urandom", O_RDONLY); + fd = robust_open("/dev/urandom", O_RDONLY, 0); if( fd<0 ){ time_t t; time(&t); @@ -5049,8 +5293,8 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf ); nBuf = sizeof(t) + sizeof(pid); }else{ - do{ nBuf = read(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); - close(fd); + do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR ); + robust_close(0, fd, __LINE__); } } #endif @@ -5448,17 +5692,17 @@ static int proxyCreateUnixFile( } } if( fd<0 ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; if( fd<0 && errno==ENOENT && islockfile ){ if( proxyCreateLockPath(path) == SQLITE_OK ){ - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); } } } if( fd<0 ){ openFlags = O_RDONLY; - fd = open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); terrno = errno; } if( fd<0 ){ @@ -5487,13 +5731,13 @@ static int proxyCreateUnixFile( pUnused->flags = openFlags; pNew->pUnused = pUnused; - rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); + rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0, 0); if( rc==SQLITE_OK ){ *ppFile = pNew; return SQLITE_OK; } end_create_proxy: - close(fd); /* silently leak fd if error, we're already in error */ + robust_close(pNew, fd, __LINE__); sqlite3_free(pNew); sqlite3_free(pUnused); return rc; @@ -5572,18 +5816,19 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ goto end_breaklock; } /* read the conch content */ - readLen = pread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); + readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); if( readLenh); + robust_close(pFile, conchFile->h, __LINE__); conchFile->h = fd; conchFile->openFlags = O_RDWR | O_CREAT; @@ -5601,7 +5846,7 @@ end_breaklock: if( rc ){ if( fd>=0 ){ unlink(tPath); - close(fd); + robust_close(pFile, fd, __LINE__); } fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); } @@ -5629,7 +5874,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ * 3rd try: break the lock unless the mod time has changed. */ struct stat buf; - if( fstat(conchFile->h, &buf) ){ + if( osFstat(conchFile->h, &buf) ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; } @@ -5648,7 +5893,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ if( nTries==2 ){ char tBuf[PROXY_MAXCONCHLEN]; - int len = pread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); + int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); if( len<0 ){ pFile->lastErrno = errno; return SQLITE_IOERR_LOCK; @@ -5818,17 +6063,16 @@ static int proxyTakeConch(unixFile *pFile){ */ if( rc==SQLITE_OK && createConch ){ struct stat buf; - int rc; - int err = fstat(pFile->h, &buf); + int err = osFstat(pFile->h, &buf); if( err==0 ){ mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); /* try to match the database file R/W permissions, ignore failure */ #ifndef SQLITE_PROXY_DEBUG - fchmod(conchFile->h, cmode); + osFchmod(conchFile->h, cmode); #else do{ - rc = fchmod(conchFile->h, cmode); + rc = osFchmod(conchFile->h, cmode); }while( rc==(-1) && errno==EINTR ); if( rc!=0 ){ int code = errno; @@ -5851,17 +6095,10 @@ static int proxyTakeConch(unixFile *pFile){ OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); if( rc==SQLITE_OK && pFile->openFlags ){ if( pFile->h>=0 ){ -#ifdef STRICT_CLOSE_ERROR - if( close(pFile->h) ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } -#else - close(pFile->h); /* silently leak fd if fail */ -#endif + robust_close(pFile, pFile->h, __LINE__); } pFile->h = -1; - int fd = open(pCtx->dbPath, pFile->openFlags, + int fd = robust_open(pCtx->dbPath, pFile->openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS); OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); if( fd>=0 ){ @@ -6087,7 +6324,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) { struct stat conchInfo; int goLockless = 0; - if( stat(pCtx->conchFilePath, &conchInfo) == -1 ) { + if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { int err = errno; if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; @@ -6372,7 +6609,7 @@ int sqlite3_os_init(void){ ** that filesystem time. */ #define UNIXVFS(VFSNAME, FINDER) { \ - 2, /* iVersion */ \ + 3, /* iVersion */ \ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ @@ -6391,6 +6628,9 @@ int sqlite3_os_init(void){ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ + unixSetSystemCall, /* xSetSystemCall */ \ + unixGetSystemCall, /* xGetSystemCall */ \ + unixNextSystemCall, /* xNextSystemCall */ \ } /* @@ -6408,6 +6648,7 @@ int sqlite3_os_init(void){ #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-excl", posixIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif diff --git a/src/os_win.c b/src/os_win.c index 70425178e8..c8768339cd 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -2746,7 +2746,7 @@ static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { - 2, /* iVersion */ + 3, /* iVersion */ sizeof(winFile), /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ @@ -2765,6 +2765,9 @@ int sqlite3_os_init(void){ winCurrentTime, /* xCurrentTime */ winGetLastError, /* xGetLastError */ winCurrentTimeInt64, /* xCurrentTimeInt64 */ + 0, /* xSetSystemCall */ + 0, /* xGetSystemCall */ + 0, /* xNextSystemCall */ }; #ifndef SQLITE_OMIT_WAL diff --git a/src/pragma.c b/src/pragma.c index cbfe01f014..db55e4bb41 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -384,8 +384,7 @@ void sqlite3Pragma( sqlite3VdbeChangeP1(v, addr+1, iDb); sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); @@ -694,8 +693,7 @@ void sqlite3Pragma( if( !zRight ){ returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ - int size = sqlite3Atoi(zRight); - if( size<0 ) size = -size; + int size = sqlite3AbsInt32(sqlite3Atoi(zRight)); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } diff --git a/src/prepare.c b/src/prepare.c index 62a33b6717..45654ecfca 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -141,7 +141,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int meta[5]; InitData initData; char const *zMasterSchema; - char const *zMasterName = SCHEMA_TABLE(iDb); + char const *zMasterName; int openedTransaction = 0; /* @@ -278,9 +278,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ pDb->pSchema->enc = ENC(db); if( pDb->pSchema->cache_size==0 ){ - size = meta[BTREE_DEFAULT_CACHE_SIZE-1]; + size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]); if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; } - if( size<0 ) size = -size; pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } diff --git a/src/printf.c b/src/printf.c index c88bb30006..2a3dd81d7d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -400,7 +400,11 @@ void sqlite3VXPrintf( v = va_arg(ap,int); } if( v<0 ){ - longvalue = -v; + if( v==SMALLEST_INT64 ){ + longvalue = ((u64)1)<<63; + }else{ + longvalue = -v; + } prefix = '-'; }else{ longvalue = v; diff --git a/src/shell.c b/src/shell.c index 82bb20f5e0..7eed290918 100644 --- a/src/shell.c +++ b/src/shell.c @@ -419,6 +419,7 @@ struct callback_data { ** .explain ON */ char outfile[FILENAME_MAX]; /* Filename for *out */ const char *zDbFilename; /* name of the database file */ + const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ }; @@ -2172,29 +2173,45 @@ static int do_meta_command(char *zLine, struct callback_data *p){ }else if( c=='t' && n>=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ + static const struct { + const char *zCtrlName; /* Name of a test-control option */ + int ctrlCode; /* Integer code for that option */ + } aCtrl[] = { + { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, + { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, + { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, + { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, + { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, + { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, + { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, + { "assert", SQLITE_TESTCTRL_ASSERT }, + { "always", SQLITE_TESTCTRL_ALWAYS }, + { "reserve", SQLITE_TESTCTRL_RESERVE }, + { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, + { "iskeyword", SQLITE_TESTCTRL_ISKEYWORD }, + { "pghdrsz", SQLITE_TESTCTRL_PGHDRSZ }, + { "scratchmalloc", SQLITE_TESTCTRL_SCRATCHMALLOC }, + }; int testctrl = -1; int rc = 0; + int i, n; open_db(p); - /* convert testctrl text option to value. allow only the first - ** three characters of the option to be used or the numerical - ** value. */ - if( strncmp( azArg[1], "prng_save", 6 )==0 ) testctrl = SQLITE_TESTCTRL_PRNG_SAVE; - else if( strncmp( azArg[1], "prng_restore", 10 )==0 ) testctrl = SQLITE_TESTCTRL_PRNG_RESTORE; - else if( strncmp( azArg[1], "prng_reset", 10 )==0 ) testctrl = SQLITE_TESTCTRL_PRNG_RESET; - else if( strncmp( azArg[1], "bitvec_test", 6 )==3 ) testctrl = SQLITE_TESTCTRL_BITVEC_TEST; - else if( strncmp( azArg[1], "fault_install", 6 )==3 ) testctrl = SQLITE_TESTCTRL_FAULT_INSTALL; - else if( strncmp( azArg[1], "benign_malloc_hooks", 3 )==0 ) testctrl = SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS; - else if( strncmp( azArg[1], "pending_byte", 3 )==0 ) testctrl = SQLITE_TESTCTRL_PENDING_BYTE; - else if( strncmp( azArg[1], "assert", 3 )==0 ) testctrl = SQLITE_TESTCTRL_ASSERT; - else if( strncmp( azArg[1], "always", 3 )==0 ) testctrl = SQLITE_TESTCTRL_ALWAYS; - else if( strncmp( azArg[1], "reserve", 3 )==0 ) testctrl = SQLITE_TESTCTRL_RESERVE; - else if( strncmp( azArg[1], "optimizations", 3 )==0 ) testctrl = SQLITE_TESTCTRL_OPTIMIZATIONS; - else if( strncmp( azArg[1], "iskeyword", 3 )==0 ) testctrl = SQLITE_TESTCTRL_ISKEYWORD; - else if( strncmp( azArg[1], "pghdrsz", 3 )==0 ) testctrl = SQLITE_TESTCTRL_PGHDRSZ; - else if( strncmp( azArg[1], "scratchmalloc", 3 )==0 ) testctrl = SQLITE_TESTCTRL_SCRATCHMALLOC; - else testctrl = atoi(azArg[1]); - + /* convert testctrl text option to value. allow any unique prefix + ** of the option name, or a numerical value. */ + n = strlen(azArg[1]); + for(i=0; iSQLITE_TESTCTRL_LAST) ){ fprintf(stderr,"Error: invalid testctrl option: %s\n", azArg[1]); }else{ @@ -2208,7 +2225,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ rc = sqlite3_test_control(testctrl, p->db, opt); printf("%d (0x%08x)\n", rc, rc); } else { - fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); + fprintf(stderr,"Error: testctrl %s takes a single int option\n", + azArg[1]); } break; @@ -2232,7 +2250,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { - fprintf(stderr,"Error: testctrl %s takes a single unsigned int option\n", azArg[1]); + fprintf(stderr,"Error: testctrl %s takes a single unsigned" + " int option\n", azArg[1]); } break; @@ -2244,7 +2263,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { - fprintf(stderr,"Error: testctrl %s takes a single int option\n", azArg[1]); + fprintf(stderr,"Error: testctrl %s takes a single int option\n", + azArg[1]); } break; @@ -2256,7 +2276,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ rc = sqlite3_test_control(testctrl, opt); printf("%d (0x%08x)\n", rc, rc); } else { - fprintf(stderr,"Error: testctrl %s takes a single char * option\n", azArg[1]); + fprintf(stderr,"Error: testctrl %s takes a single char * option\n", + azArg[1]); } break; #endif @@ -2266,7 +2287,8 @@ static int do_meta_command(char *zLine, struct callback_data *p){ case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: case SQLITE_TESTCTRL_SCRATCHMALLOC: default: - fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", azArg[1]); + fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", + azArg[1]); break; } } @@ -2277,7 +2299,9 @@ static int do_meta_command(char *zLine, struct callback_data *p){ sqlite3_busy_timeout(p->db, atoi(azArg[1])); }else - if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 && nArg==2 ){ + if( HAS_TIMER && c=='t' && n>=5 && strncmp(azArg[0], "timer", n)==0 + && nArg==2 + ){ enableTimer = booleanValue(azArg[1]); }else @@ -2464,7 +2488,9 @@ static int process_input(struct callback_data *p, FILE *in){ } } if( zSql ){ - if( !_all_whitespace(zSql) ) fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); + if( !_all_whitespace(zSql) ){ + fprintf(stderr, "Error: incomplete SQL: %s\n", zSql); + } free(zSql); } free(zLine); @@ -2600,6 +2626,10 @@ static const char zOptions[] = " -stats print memory stats before each finalize\n" " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" + " -vfs NAME use NAME as the default VFS\n" +#ifdef SQLITE_ENABLE_VFSTRACE + " -vfstrace enable tracing of all VFS calls\n" +#endif ; static void usage(int showDetail){ fprintf(stderr, @@ -2684,6 +2714,25 @@ int main(int argc, char **argv){ #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64); #endif +#ifdef SQLITE_ENABLE_VFSTRACE + }else if( strcmp(argv[i],"-vfstrace")==0 ){ + extern int vfstrace_register( + const char *zTraceName, + const char *zOldVfsName, + int (*xOut)(const char*,void*), + void *pOutArg, + int makeDefault + ); + vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); +#endif + }else if( strcmp(argv[i],"-vfs")==0 ){ + sqlite3_vfs *pVfs = sqlite3_vfs_find(argv[++i]); + if( pVfs ){ + sqlite3_vfs_register(pVfs, 1); + }else{ + fprintf(stderr, "no such VFS: \"%s\"\n", argv[i]); + exit(1); + } } } if( i +** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2^12. Reasonable values +** for the minimum allocation size are 2^5 through 2^8. ** **
SQLITE_CONFIG_MUTEX
**
^(This option takes a single argument which is a pointer to an diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2582d7c990..9fa4161d2e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1629,7 +1629,7 @@ struct Expr { u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ - int iValue; /* Integer value if EP_IntValue */ + int iValue; /* Non-negative integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no @@ -2911,6 +2911,10 @@ Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*); int sqlite3CheckCollSeq(Parse *, CollSeq *); int sqlite3CheckObjectName(Parse *, const char *); void sqlite3VdbeSetChanges(sqlite3 *, int); +int sqlite3AddInt64(i64*,i64); +int sqlite3SubInt64(i64*,i64); +int sqlite3MulInt64(i64*,i64); +int sqlite3AbsInt32(int); const void *sqlite3ValueText(sqlite3_value*, u8); int sqlite3ValueBytes(sqlite3_value*, u8); diff --git a/src/test_config.c b/src/test_config.c index def12a9afc..6eee524c09 100644 --- a/src/test_config.c +++ b/src/test_config.c @@ -475,6 +475,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double", Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif +#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "0", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "1", TCL_GLOBAL_ONLY); +#endif + #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else diff --git a/src/test_vfstrace.c b/src/test_vfstrace.c new file mode 100644 index 0000000000..24bd0afaee --- /dev/null +++ b/src/test_vfstrace.c @@ -0,0 +1,770 @@ +/* +** 2011 March 16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code implements a VFS shim that writes diagnostic +** output for each VFS call, similar to "strace". +*/ +#include +#include +#include "sqlite3.h" + +/* +** An instance of this structure is attached to the each trace VFS to +** provide auxiliary information. +*/ +typedef struct vfstrace_info vfstrace_info; +struct vfstrace_info { + sqlite3_vfs *pRootVfs; /* The underlying real VFS */ + int (*xOut)(const char*, void*); /* Send output here */ + void *pOutArg; /* First argument to xOut */ + const char *zVfsName; /* Name of this trace-VFS */ + sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */ +}; + +/* +** The sqlite3_file object for the trace VFS +*/ +typedef struct vfstrace_file vfstrace_file; +struct vfstrace_file { + sqlite3_file base; /* Base class. Must be first */ + vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */ + const char *zFName; /* Base name of the file */ + sqlite3_file *pReal; /* The real underlying file */ +}; + +/* +** Method declarations for vfstrace_file. +*/ +static int vfstraceClose(sqlite3_file*); +static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64); +static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size); +static int vfstraceSync(sqlite3_file*, int flags); +static int vfstraceFileSize(sqlite3_file*, sqlite3_int64 *pSize); +static int vfstraceLock(sqlite3_file*, int); +static int vfstraceUnlock(sqlite3_file*, int); +static int vfstraceCheckReservedLock(sqlite3_file*, int *); +static int vfstraceFileControl(sqlite3_file*, int op, void *pArg); +static int vfstraceSectorSize(sqlite3_file*); +static int vfstraceDeviceCharacteristics(sqlite3_file*); +static int vfstraceShmLock(sqlite3_file*,int,int,int); +static int vfstraceShmMap(sqlite3_file*,int,int,int, void volatile **); +static void vfstraceShmBarrier(sqlite3_file*); +static int vfstraceShmUnmap(sqlite3_file*,int); + +/* +** Method declarations for vfstrace_vfs. +*/ +static int vfstraceOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int vfstraceDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int vfstraceAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int vfstraceFullPathname(sqlite3_vfs*, const char *zName, int, char *); +static void *vfstraceDlOpen(sqlite3_vfs*, const char *zFilename); +static void vfstraceDlError(sqlite3_vfs*, int nByte, char *zErrMsg); +static void (*vfstraceDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void); +static void vfstraceDlClose(sqlite3_vfs*, void*); +static int vfstraceRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int vfstraceSleep(sqlite3_vfs*, int microseconds); +static int vfstraceCurrentTime(sqlite3_vfs*, double*); +static int vfstraceGetLastError(sqlite3_vfs*, int, char*); +static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); +static int vfstraceSetSystemCall(sqlite3_vfs*, const char *zName, void *pFunc); +static void *vfstraceGetSystemCall(sqlite3_vfs*, const char *zName); +static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName); + +/* +** Return a pointer to the tail of the pathname. Examples: +** +** /home/drh/xyzzy.txt -> xyzzy.txt +** xyzzy.txt -> xyzzy.txt +*/ +static const char *fileTail(const char *z){ + int i; + if( z==0 ) return 0; + i = strlen(z)-1; + while( i>0 && z[i-1]!='/' ){ i--; } + return &z[i]; +} + +/* +** Send trace output defined by zFormat and subsequent arguments. +*/ +static void vfstrace_printf( + vfstrace_info *pInfo, + const char *zFormat, + ... +){ + va_list ap; + char *zMsg; + va_start(ap, zFormat); + zMsg = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + pInfo->xOut(zMsg, pInfo->pOutArg); + sqlite3_free(zMsg); +} + +/* +** Convert value rc into a string and print it using zFormat. zFormat +** should have exactly one %s +*/ +static void vfstrace_print_errcode( + vfstrace_info *pInfo, + const char *zFormat, + int rc +){ + char zBuf[50]; + char *zVal; + switch( rc ){ + case SQLITE_OK: zVal = "SQLITE_OK"; break; + case SQLITE_ERROR: zVal = "SQLITE_ERROR"; break; + case SQLITE_PERM: zVal = "SQLITE_PERM"; break; + case SQLITE_ABORT: zVal = "SQLITE_ABORT"; break; + case SQLITE_BUSY: zVal = "SQLITE_BUSY"; break; + case SQLITE_NOMEM: zVal = "SQLITE_NOMEM"; break; + case SQLITE_READONLY: zVal = "SQLITE_READONLY"; break; + case SQLITE_INTERRUPT: zVal = "SQLITE_INTERRUPT"; break; + case SQLITE_IOERR: zVal = "SQLITE_IOERR"; break; + case SQLITE_CORRUPT: zVal = "SQLITE_CORRUPT"; break; + case SQLITE_FULL: zVal = "SQLITE_FULL"; break; + case SQLITE_CANTOPEN: zVal = "SQLITE_CANTOPEN"; break; + case SQLITE_PROTOCOL: zVal = "SQLITE_PROTOCOL"; break; + case SQLITE_EMPTY: zVal = "SQLITE_EMPTY"; break; + case SQLITE_SCHEMA: zVal = "SQLITE_SCHEMA"; break; + case SQLITE_CONSTRAINT: zVal = "SQLITE_CONSTRAINT"; break; + case SQLITE_MISMATCH: zVal = "SQLITE_MISMATCH"; break; + case SQLITE_MISUSE: zVal = "SQLITE_MISUSE"; break; + case SQLITE_NOLFS: zVal = "SQLITE_NOLFS"; break; + case SQLITE_IOERR_READ: zVal = "SQLITE_IOERR_READ"; break; + case SQLITE_IOERR_SHORT_READ: zVal = "SQLITE_IOERR_SHORT_READ"; break; + case SQLITE_IOERR_WRITE: zVal = "SQLITE_IOERR_WRITE"; break; + case SQLITE_IOERR_FSYNC: zVal = "SQLITE_IOERR_FSYNC"; break; + case SQLITE_IOERR_DIR_FSYNC: zVal = "SQLITE_IOERR_DIR_FSYNC"; break; + case SQLITE_IOERR_TRUNCATE: zVal = "SQLITE_IOERR_TRUNCATE"; break; + case SQLITE_IOERR_FSTAT: zVal = "SQLITE_IOERR_FSTAT"; break; + case SQLITE_IOERR_UNLOCK: zVal = "SQLITE_IOERR_UNLOCK"; break; + case SQLITE_IOERR_RDLOCK: zVal = "SQLITE_IOERR_RDLOCK"; break; + case SQLITE_IOERR_DELETE: zVal = "SQLITE_IOERR_DELETE"; break; + case SQLITE_IOERR_BLOCKED: zVal = "SQLITE_IOERR_BLOCKED"; break; + case SQLITE_IOERR_NOMEM: zVal = "SQLITE_IOERR_NOMEM"; break; + case SQLITE_IOERR_ACCESS: zVal = "SQLITE_IOERR_ACCESS"; break; + case SQLITE_IOERR_CHECKRESERVEDLOCK: + zVal = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break; + case SQLITE_IOERR_LOCK: zVal = "SQLITE_IOERR_LOCK"; break; + case SQLITE_IOERR_CLOSE: zVal = "SQLITE_IOERR_CLOSE"; break; + case SQLITE_IOERR_DIR_CLOSE: zVal = "SQLITE_IOERR_DIR_CLOSE"; break; + case SQLITE_IOERR_SHMOPEN: zVal = "SQLITE_IOERR_SHMOPEN"; break; + case SQLITE_IOERR_SHMSIZE: zVal = "SQLITE_IOERR_SHMSIZE"; break; + case SQLITE_IOERR_SHMLOCK: zVal = "SQLITE_IOERR_SHMLOCK"; break; + case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break; + case SQLITE_BUSY_RECOVERY: zVal = "SQLITE_BUSY_RECOVERY"; break; + case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break; + default: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc); + zVal = zBuf; + break; + } + } + vfstrace_printf(pInfo, zFormat, zVal); +} + +/* +** Append to a buffer. +*/ +static void strappend(char *z, int *pI, const char *zAppend){ + int i = *pI; + while( zAppend[0] ){ z[i++] = *(zAppend++); } + z[i] = 0; + *pI = i; +} + +/* +** Close an vfstrace-file. +*/ +static int vfstraceClose(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xClose(p->pReal); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + if( rc==SQLITE_OK ){ + sqlite3_free((void*)p->base.pMethods); + p->base.pMethods = 0; + } + return rc; +} + +/* +** Read data from an vfstrace-file. +*/ +static int vfstraceRead( + sqlite3_file *pFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)", + pInfo->zVfsName, p->zFName, iAmt, iOfst); + rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Write data to an vfstrace-file. +*/ +static int vfstraceWrite( + sqlite3_file *pFile, + const void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)", + pInfo->zVfsName, p->zFName, iAmt, iOfst); + rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Truncate an vfstrace-file. +*/ +static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName, + size); + rc = p->pReal->pMethods->xTruncate(p->pReal, size); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Sync an vfstrace-file. +*/ +static int vfstraceSync(sqlite3_file *pFile, int flags){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + int i; + char zBuf[100]; + memcpy(zBuf, "|0", 3); + i = 0; + if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL"); + else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL"); + if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY"); + if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){ + sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags); + } + vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName, + &zBuf[1]); + rc = p->pReal->pMethods->xSync(p->pReal, flags); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Return the current file-size of an vfstrace-file. +*/ +static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); + vfstrace_print_errcode(pInfo, " -> %s,", rc); + vfstrace_printf(pInfo, " size=%lld\n", *pSize); + return rc; +} + +/* +** Return the name of a lock. +*/ +static const char *lockName(int eLock){ + const char *azLockNames[] = { + "NONE", "SHARED", "RESERVED", "PENDING", "EXCLUSIVE" + }; + if( eLock<0 || eLock>=sizeof(azLockNames)/sizeof(azLockNames[0]) ){ + return "???"; + }else{ + return azLockNames[eLock]; + } +} + +/* +** Lock an vfstrace-file. +*/ +static int vfstraceLock(sqlite3_file *pFile, int eLock){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName, + lockName(eLock)); + rc = p->pReal->pMethods->xLock(p->pReal, eLock); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Unlock an vfstrace-file. +*/ +static int vfstraceUnlock(sqlite3_file *pFile, int eLock){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName, + lockName(eLock)); + rc = p->pReal->pMethods->xUnlock(p->pReal, eLock); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Check if another file-handle holds a RESERVED lock on an vfstrace-file. +*/ +static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)", + pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); + return rc; +} + +/* +** File control method. For custom operations on an vfstrace-file. +*/ +static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + char zBuf[100]; + char *zOp; + switch( op ){ + case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break; + case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break; + case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break; + case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break; + case SQLITE_FCNTL_SIZE_HINT: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld", + *(sqlite3_int64*)pArg); + zOp = zBuf; + break; + } + case SQLITE_FCNTL_CHUNK_SIZE: { + sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg); + zOp = zBuf; + break; + } + case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER"; break; + case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED"; break; + case 0xca093fa0: zOp = "DB_UNCHANGED"; break; + default: { + sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op); + zOp = zBuf; + break; + } + } + vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)", + pInfo->zVfsName, p->zFName, zOp); + rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Return the sector-size in bytes for an vfstrace-file. +*/ +static int vfstraceSectorSize(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xSectorSize(p->pReal); + vfstrace_printf(pInfo, " -> %d\n", rc); + return rc; +} + +/* +** Return the device characteristic flags supported by an vfstrace-file. +*/ +static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)", + pInfo->zVfsName, p->zFName); + rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal); + vfstrace_printf(pInfo, " -> 0x%08x\n", rc); + return rc; +} + +/* +** Shared-memory operations. +*/ +static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + char zLck[100]; + int i = 0; + memcpy(zLck, "|0", 3); + if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK"); + if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK"); + if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED"); + if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE"); + if( flags & ~(0xf) ){ + sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags); + } + vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d,n=%d,%s)", + pInfo->zVfsName, p->zFName, ofst, n, &zLck[1]); + rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} +static int vfstraceShmMap( + sqlite3_file *pFile, + int iRegion, + int szRegion, + int isWrite, + void volatile **pp +){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)", + pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite); + rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} +static void vfstraceShmBarrier(sqlite3_file *pFile){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName); + p->pReal->pMethods->xShmBarrier(p->pReal); +} +static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){ + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = p->pInfo; + int rc; + vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)", + pInfo->zVfsName, p->zFName, delFlag); + rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + + + +/* +** Open an vfstrace file handle. +*/ +static int vfstraceOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + int rc; + vfstrace_file *p = (vfstrace_file *)pFile; + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + p->pInfo = pInfo; + p->zFName = zName ? fileTail(zName) : ""; + p->pReal = (sqlite3_file *)&p[1]; + rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags); + vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)", + pInfo->zVfsName, p->zFName, flags); + if( p->pReal->pMethods ){ + sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) ); + const sqlite3_io_methods *pSub = p->pReal->pMethods; + memset(pNew, 0, sizeof(*pNew)); + pNew->iVersion = pSub->iVersion; + pNew->xClose = vfstraceClose; + pNew->xRead = vfstraceRead; + pNew->xWrite = vfstraceWrite; + pNew->xTruncate = vfstraceTruncate; + pNew->xSync = vfstraceSync; + pNew->xFileSize = vfstraceFileSize; + pNew->xLock = vfstraceLock; + pNew->xUnlock = vfstraceUnlock; + pNew->xCheckReservedLock = vfstraceCheckReservedLock; + pNew->xFileControl = vfstraceFileControl; + pNew->xSectorSize = vfstraceSectorSize; + pNew->xDeviceCharacteristics = vfstraceDeviceCharacteristics; + if( pNew->iVersion>=2 ){ + pNew->xShmMap = pSub->xShmMap ? vfstraceShmMap : 0; + pNew->xShmLock = pSub->xShmLock ? vfstraceShmLock : 0; + pNew->xShmBarrier = pSub->xShmBarrier ? vfstraceShmBarrier : 0; + pNew->xShmUnmap = pSub->xShmUnmap ? vfstraceShmUnmap : 0; + } + pFile->pMethods = pNew; + } + vfstrace_print_errcode(pInfo, " -> %s", rc); + if( pOutFlags ){ + vfstrace_printf(pInfo, ", outFlags=0x%x\n", *pOutFlags); + }else{ + vfstrace_printf(pInfo, "\n"); + } + return rc; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", + pInfo->zVfsName, zPath, dirSync); + rc = pRoot->xDelete(pRoot, zPath, dirSync); + vfstrace_print_errcode(pInfo, " -> %s\n", rc); + return rc; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int vfstraceAccess( + sqlite3_vfs *pVfs, + const char *zPath, + int flags, + int *pResOut +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)", + pInfo->zVfsName, zPath, flags); + rc = pRoot->xAccess(pRoot, zPath, flags, pResOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=%d\n", *pResOut); + return rc; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (DEVSYM_MAX_PATHNAME+1) bytes. +*/ +static int vfstraceFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + int rc; + vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")", + pInfo->zVfsName, zPath); + rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut); + vfstrace_print_errcode(pInfo, " -> %s", rc); + vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut); + return rc; +} + +/* +** Open the dynamic library located at zPath and return a handle. +*/ +static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath); + return pRoot->xDlOpen(pRoot, zPath); +} + +/* +** Populate the buffer zErrMsg (size nByte bytes) with a human readable +** utf-8 string describing the most recent error encountered associated +** with dynamic libraries. +*/ +static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte); + pRoot->xDlError(pRoot, nByte, zErrMsg); + vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg); +} + +/* +** Return a pointer to the symbol zSymbol in the dynamic library pHandle. +*/ +static void (*vfstraceDlSym(sqlite3_vfs *pVfs,void *p,const char *zSym))(void){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlSym(\"%s\")\n", pInfo->zVfsName, zSym); + return pRoot->xDlSym(pRoot, p, zSym); +} + +/* +** Close the dynamic library handle pHandle. +*/ +static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName); + pRoot->xDlClose(pRoot, pHandle); +} + +/* +** Populate the buffer pointed to by zBufOut with nByte bytes of +** random data. +*/ +static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte); + return pRoot->xRandomness(pRoot, nByte, zBufOut); +} + +/* +** Sleep for nMicro microseconds. Return the number of microseconds +** actually slept. +*/ +static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xSleep(pRoot, nMicro); +} + +/* +** Return the current time as a Julian Day number in *pTimeOut. +*/ +static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xCurrentTime(pRoot, pTimeOut); +} +static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xCurrentTimeInt64(pRoot, pTimeOut); +} + +/* +** Return th3 emost recent error code and message +*/ +static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xGetLastError(pRoot, iErr, zErr); +} + +/* +** Override system calls. +*/ +static int vfstraceSetSystemCall( + sqlite3_vfs *pVfs, + const char *zName, + void *pFunc +){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xSetSystemCall(pRoot, zName, pFunc); +} +static void *vfstraceGetSystemCall(sqlite3_vfs *pVfs, const char *zName){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xGetSystemCall(pRoot, zName); +} +static const char *vfstraceNextSystemCall(sqlite3_vfs *pVfs, const char *zName){ + vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData; + sqlite3_vfs *pRoot = pInfo->pRootVfs; + return pRoot->xNextSystemCall(pRoot, zName); +} + + +/* +** Clients invoke this routine to construct a new trace-vfs shim. +** +** Return SQLITE_OK on success. +** +** SQLITE_NOMEM is returned in the case of a memory allocation error. +** SQLITE_NOTFOUND is returned if zOldVfsName does not exist. +*/ +int vfstrace_register( + const char *zTraceName, /* Name of the newly constructed VFS */ + const char *zOldVfsName, /* Name of the underlying VFS */ + int (*xOut)(const char*,void*), /* Output routine. ex: fputs */ + void *pOutArg, /* 2nd argument to xOut. ex: stderr */ + int makeDefault /* True to make the new VFS the default */ +){ + sqlite3_vfs *pNew; + sqlite3_vfs *pRoot; + vfstrace_info *pInfo; + int nName; + int nByte; + + pRoot = sqlite3_vfs_find(zOldVfsName); + if( pRoot==0 ) return SQLITE_NOTFOUND; + nName = strlen(zTraceName); + nByte = sizeof(*pNew) + sizeof(*pInfo) + nName + 1; + pNew = sqlite3_malloc( nByte ); + if( pNew==0 ) return SQLITE_NOMEM; + memset(pNew, 0, nByte); + pInfo = (vfstrace_info*)&pNew[1]; + pNew->iVersion = pRoot->iVersion; + pNew->szOsFile = pRoot->szOsFile + sizeof(vfstrace_file); + pNew->mxPathname = pRoot->mxPathname; + pNew->zName = (char*)&pInfo[1]; + memcpy((char*)&pInfo[1], zTraceName, nName+1); + pNew->pAppData = pInfo; + pNew->xOpen = vfstraceOpen; + pNew->xDelete = vfstraceDelete; + pNew->xAccess = vfstraceAccess; + pNew->xFullPathname = vfstraceFullPathname; + pNew->xDlOpen = pRoot->xDlOpen==0 ? 0 : vfstraceDlOpen; + pNew->xDlError = pRoot->xDlError==0 ? 0 : vfstraceDlError; + pNew->xDlSym = pRoot->xDlSym==0 ? 0 : vfstraceDlSym; + pNew->xDlClose = pRoot->xDlClose==0 ? 0 : vfstraceDlClose; + pNew->xRandomness = vfstraceRandomness; + pNew->xSleep = vfstraceSleep; + pNew->xCurrentTime = vfstraceCurrentTime; + pNew->xGetLastError = pRoot->xGetLastError==0 ? 0 : vfstraceGetLastError; + if( pNew->iVersion>=2 ){ + pNew->xCurrentTimeInt64 = pRoot->xCurrentTimeInt64==0 ? 0 : + vfstraceCurrentTimeInt64; + if( pNew->iVersion>=3 ){ + pNew->xSetSystemCall = pRoot->xSetSystemCall==0 ? 0 : + vfstraceSetSystemCall; + pNew->xGetSystemCall = pRoot->xGetSystemCall==0 ? 0 : + vfstraceGetSystemCall; + pNew->xNextSystemCall = pRoot->xNextSystemCall==0 ? 0 : + vfstraceNextSystemCall; + } + } + pInfo->pRootVfs = pRoot; + pInfo->xOut = xOut; + pInfo->pOutArg = pOutArg; + pInfo->zVfsName = pNew->zName; + pInfo->pTraceVfs = pNew; + vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n", + pInfo->zVfsName, pRoot->zName); + return sqlite3_vfs_register(pNew, makeDefault); +} diff --git a/src/trigger.c b/src/trigger.c index b1d43d06d2..ba8d47bccf 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -262,7 +262,6 @@ void sqlite3FinishTrigger( int iDb; /* Database containing the trigger */ Token nameToken; /* Trigger name for error reporting */ - pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; zName = pTrig->zName; diff --git a/src/update.c b/src/update.c index 4bf4a880b1..2c9f246d77 100644 --- a/src/update.c +++ b/src/update.c @@ -128,7 +128,6 @@ void sqlite3Update( int regNew; int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ - int regRec; /* Register used for new table record to insert */ memset(&sContext, 0, sizeof(sContext)); db = pParse->db; @@ -286,7 +285,6 @@ void sqlite3Update( } regNew = pParse->nMem + 1; pParse->nMem += pTab->nCol; - regRec = ++pParse->nMem; /* Start the view context. */ if( isView ){ @@ -396,7 +394,7 @@ void sqlite3Update( pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError ); for(i=0; inCol; i++){ - if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<=zEnd ) goto do_atoi_calc; - if( *zNum=='-' ){ - neg = 1; - zNum+=incr; - }else if( *zNum=='+' ){ - zNum+=incr; + if( zNum='0' && c<='9'; i+=incr){ - v = v*10 + c - '0'; + u = u*10 + c - '0'; + } + if( u>LARGEST_INT64 ){ + *pNum = SMALLEST_INT64; + }else if( neg ){ + *pNum = -(i64)u; + }else{ + *pNum = (i64)u; } - *pNum = neg ? -v : v; testcase( i==18 ); testcase( i==19 ); testcase( i==20 ); @@ -487,14 +496,25 @@ do_atoi_calc: return 1; }else if( i<19*incr ){ /* Less than 19 digits, so we know that it fits in 64 bits */ + assert( u<=LARGEST_INT64 ); return 0; }else{ - /* 19-digit numbers must be no larger than 9223372036854775807 if positive - ** or 9223372036854775808 if negative. Note that 9223372036854665808 - ** is 2^63. Return 1 if to large */ - c=compare2pow63(zNum, incr); - if( c==0 && neg==0 ) return 2; /* too big, exactly 9223372036854665808 */ - return c0 ){ + /* zNum is greater than 9223372036854775808 so it overflows */ + return 1; + }else{ + /* zNum is exactly 9223372036854775808. Fits if negative. The + ** special case 2 overflow if positive */ + assert( u-1==LARGEST_INT64 ); + assert( (*pNum)==SMALLEST_INT64 ); + return neg ? 0 : 2; + } } } @@ -1060,3 +1080,71 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ return 1; } } + +/* +** Attempt to add, substract, or multiply the 64-bit signed value iB against +** the other 64-bit signed integer at *pA and store the result in *pA. +** Return 0 on success. Or if the operation would have resulted in an +** overflow, leave *pA unchanged and return 1. +*/ +int sqlite3AddInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + testcase( iA==0 ); testcase( iA==1 ); + testcase( iB==-1 ); testcase( iB==0 ); + if( iB>=0 ){ + testcase( iA>0 && LARGEST_INT64 - iA == iB ); + testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); + if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; + *pA += iB; + }else{ + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); + testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); + if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; + *pA += iB; + } + return 0; +} +int sqlite3SubInt64(i64 *pA, i64 iB){ + testcase( iB==SMALLEST_INT64+1 ); + if( iB==SMALLEST_INT64 ){ + testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); + if( (*pA)>=0 ) return 1; + *pA -= iB; + return 0; + }else{ + return sqlite3AddInt64(pA, -iB); + } +} +#define TWOPOWER32 (((i64)1)<<32) +#define TWOPOWER31 (((i64)1)<<31) +int sqlite3MulInt64(i64 *pA, i64 iB){ + i64 iA = *pA; + i64 iA1, iA0, iB1, iB0, r; + + iA1 = iA/TWOPOWER32; + iA0 = iA % TWOPOWER32; + iB1 = iB/TWOPOWER32; + iB0 = iB % TWOPOWER32; + if( iA1*iB1 != 0 ) return 1; + assert( iA1*iB0==0 || iA0*iB1==0 ); + r = iA1*iB0 + iA0*iB1; + testcase( r==(-TWOPOWER31)-1 ); + testcase( r==(-TWOPOWER31) ); + testcase( r==TWOPOWER31 ); + testcase( r==TWOPOWER31-1 ); + if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; + r *= TWOPOWER32; + if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; + *pA = r; + return 0; +} + +/* +** Compute the absolute value of a 32-bit signed integer, of possible. Or +** if the integer has a value of -2147483648, return +2147483647 +*/ +int sqlite3AbsInt32(int x){ + if( x>=0 ) return x; + if( x==(int)0x80000000 ) return 0x7fffffff; + return -x; +} diff --git a/src/vdbe.c b/src/vdbe.c index 1ef59d0a39..90c78a3d0a 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -1246,19 +1246,12 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ iA = pIn1->u.i; iB = pIn2->u.i; switch( pOp->opcode ){ - case OP_Add: iB += iA; break; - case OP_Subtract: iB -= iA; break; - case OP_Multiply: iB *= iA; break; + case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break; + case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break; + case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break; case OP_Divide: { if( iA==0 ) goto arithmetic_result_is_null; - /* Dividing the largest possible negative 64-bit integer (1<<63) by - ** -1 returns an integer too large to store in a 64-bit data-type. On - ** some architectures, the value overflows to (1<<63). On others, - ** a SIGFPE is issued. The following statement normalizes this - ** behavior so that all architectures behave as if integer - ** overflow occurred. - */ - if( iA==-1 && iB==SMALLEST_INT64 ) iA = 1; + if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math; iB /= iA; break; } @@ -1272,6 +1265,7 @@ case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */ pOut->u.i = iB; MemSetTypeFlag(pOut, MEM_Int); }else{ +fp_math: rA = sqlite3VdbeRealValue(pIn1); rB = sqlite3VdbeRealValue(pIn2); switch( pOp->opcode ){ @@ -1466,8 +1460,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */ case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */ case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ - i64 a; - i64 b; + i64 iA; + u64 uA; + i64 iB; + u8 op; pIn1 = &aMem[pOp->p1]; pIn2 = &aMem[pOp->p2]; @@ -1476,16 +1472,38 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */ sqlite3VdbeMemSetNull(pOut); break; } - a = sqlite3VdbeIntValue(pIn2); - b = sqlite3VdbeIntValue(pIn1); - switch( pOp->opcode ){ - case OP_BitAnd: a &= b; break; - case OP_BitOr: a |= b; break; - case OP_ShiftLeft: a <<= b; break; - default: assert( pOp->opcode==OP_ShiftRight ); - a >>= b; break; + iA = sqlite3VdbeIntValue(pIn2); + iB = sqlite3VdbeIntValue(pIn1); + op = pOp->opcode; + if( op==OP_BitAnd ){ + iA &= iB; + }else if( op==OP_BitOr ){ + iA |= iB; + }else if( iB!=0 ){ + assert( op==OP_ShiftRight || op==OP_ShiftLeft ); + + /* If shifting by a negative amount, shift in the other direction */ + if( iB<0 ){ + assert( OP_ShiftRight==OP_ShiftLeft+1 ); + op = 2*OP_ShiftLeft + 1 - op; + iB = iB>(-64) ? -iB : 64; + } + + if( iB>=64 ){ + iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1; + }else{ + memcpy(&uA, &iA, sizeof(uA)); + if( op==OP_ShiftLeft ){ + uA <<= iB; + }else{ + uA >>= iB; + /* Sign-extend on a right shift of a negative number */ + if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB); + } + memcpy(&iA, &uA, sizeof(iA)); + } } - pOut->u.i = a; + pOut->u.i = iA; MemSetTypeFlag(pOut, MEM_Int); break; } @@ -2411,7 +2429,6 @@ case OP_MakeRecord: { */ nData = 0; /* Number of bytes of data space */ nHdr = 0; /* Number of bytes of header space */ - nByte = 0; /* Data space required for this record */ nZero = 0; /* Number of zero bytes at the end of the record */ nField = pOp->p1; zAffinity = pOp->p4.z; @@ -3685,7 +3702,6 @@ case OP_NewRowid: { /* out2-prerelease */ ** and try again, up to 100 times. */ assert( pC->isTable ); - cnt = 0; #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 66f8c0857f..64cd2191c8 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -702,13 +702,11 @@ static const Mem *columnNullValue(void){ */ static Mem *columnMem(sqlite3_stmt *pStmt, int i){ Vdbe *pVm; - int vals; Mem *pOut; pVm = (Vdbe *)pStmt; if( pVm && pVm->pResultSet!=0 && inResColumn && i>=0 ){ sqlite3_mutex_enter(pVm->db->mutex); - vals = sqlite3_data_count(pStmt); pOut = &pVm->pResultSet[i]; }else{ /* If the value passed as the second argument is out of range, return diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 9749251585..97026b89fd 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -75,6 +75,7 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt){ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ Vdbe tmp, *pTmp; char *zTmp; + assert( pA->db==pB->db ); tmp = *pA; *pA = *pB; *pB = tmp; @@ -1517,7 +1518,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ */ static void closeAllCursors(Vdbe *p){ if( p->pFrame ){ - VdbeFrame *pFrame = p->pFrame; + VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); sqlite3VdbeFrameRestore(pFrame); } @@ -2497,7 +2498,13 @@ u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){ if( file_format>=4 && (i&1)==i ){ return 8+(u32)i; } - u = i<0 ? -i : i; + if( i<0 ){ + if( i<(-MAX_6BYTE) ) return 6; + /* Previous test prevents: u = -(-9223372036854775808) */ + u = -i; + }else{ + u = i; + } if( u<=127 ) return 1; if( u<=32767 ) return 2; if( u<=8388607 ) return 3; diff --git a/src/vdbemem.c b/src/vdbemem.c index 104b93fd66..882c686334 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -367,7 +367,7 @@ i64 sqlite3VdbeIntValue(Mem *pMem){ }else if( flags & MEM_Real ){ return doubleToInt64(pMem->r); }else if( flags & (MEM_Str|MEM_Blob) ){ - i64 value; + i64 value = 0; assert( pMem->z || pMem->n==0 ); testcase( pMem->z==0 ); sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc); @@ -1077,9 +1077,14 @@ int sqlite3ValueFromExpr( /* This branch happens for multiple negative signs. Ex: -(-5) */ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){ sqlite3VdbeMemNumerify(pVal); - pVal->u.i = -1 * pVal->u.i; - /* (double)-1 In case of SQLITE_OMIT_FLOATING_POINT... */ - pVal->r = (double)-1 * pVal->r; + if( pVal->u.i==SMALLEST_INT64 ){ + pVal->flags &= MEM_Int; + pVal->flags |= MEM_Real; + pVal->r = (double)LARGEST_INT64; + }else{ + pVal->u.i = -pVal->u.i; + } + pVal->r = -pVal->r; sqlite3ValueApplyAffinity(pVal, affinity, enc); } }else if( op==TK_NULL ){ diff --git a/src/wal.c b/src/wal.c index 9f7545b61f..51ea18fb21 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1649,7 +1649,6 @@ static int walCheckpoint( } assert( pIter ); - mxPage = pWal->hdr.nPage; if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall; /* Compute in mxSafeFrame the index of the last frame of the WAL that is diff --git a/src/where.c b/src/where.c index 9ad2ad3202..cdcaa98c01 100644 --- a/src/where.c +++ b/src/where.c @@ -2520,7 +2520,8 @@ range_est_fallback: ** an equality constraint x=VALUE and where that VALUE occurs in ** the histogram data. This only works when x is the left-most ** column of an index and sqlite_stat2 histogram data is available -** for that index. +** for that index. When pExpr==NULL that means the constraint is +** "x IS NULL" instead of "x=VALUE". ** ** Write the estimated row count into *pnRow and return SQLITE_OK. ** If unable to make an estimate, leave *pnRow unchanged and return @@ -2545,8 +2546,12 @@ int whereEqualScanEst( assert( p->aSample!=0 ); aff = p->pTable->aCol[p->aiColumn[0]].affinity; - rc = valueFromExpr(pParse, pExpr, aff, &pRhs); - if( rc ) goto whereEqualScanEst_cancel; + if( pExpr ){ + rc = valueFromExpr(pParse, pExpr, aff, &pRhs); + if( rc ) goto whereEqualScanEst_cancel; + }else{ + pRhs = sqlite3ValueNew(pParse->db); + } if( pRhs==0 ) return SQLITE_NOTFOUND; rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower); if( rc ) goto whereEqualScanEst_cancel; @@ -2935,7 +2940,9 @@ static void bestBtreeIndex( ** VALUE and how common that value is according to the histogram. */ if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 ){ - if( pFirstTerm->eOperator==WO_EQ ){ + if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){ + testcase( pFirstTerm->eOperator==WO_EQ ); + testcase( pFirstTerm->pOperator==WO_ISNULL ); whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow); }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){ whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow); @@ -4001,7 +4008,13 @@ static Bitmask codeOneLoopStart( /* Record the instruction used to terminate the loop. Disable ** WHERE clause terms made redundant by the index range scan. */ - pLevel->op = bRev ? OP_Prev : OP_Next; + if( pLevel->plan.wsFlags & WHERE_UNIQUE ){ + pLevel->op = OP_Noop; + }else if( bRev ){ + pLevel->op = OP_Prev; + }else{ + pLevel->op = OP_Next; + } pLevel->p1 = iIdxCur; }else @@ -4047,7 +4060,6 @@ static Bitmask codeOneLoopStart( ** */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ - WhereTerm *pFinal; /* Final subterm within the OR-clause. */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ @@ -4063,7 +4075,6 @@ static Bitmask codeOneLoopStart( assert( pTerm->eOperator==WO_OR ); assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; - pFinal = &pOrWc->a[pOrWc->nTerm-1]; pLevel->op = OP_Return; pLevel->p1 = regReturn; @@ -4172,7 +4183,6 @@ static Bitmask codeOneLoopStart( ** the use of indices become tests that are evaluated against each row of ** the relevant input tables. */ - k = 0; for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){ Expr *pE; testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */ @@ -4190,7 +4200,6 @@ static Bitmask codeOneLoopStart( continue; } sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); - k = 1; pTerm->wtFlags |= TERM_CODED; } @@ -4498,8 +4507,6 @@ WhereInfo *sqlite3WhereBegin( ** clause. */ notReady = ~(Bitmask)0; - pTabItem = pTabList->a; - pLevel = pWInfo->a; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); for(i=iFrom=0, pLevel=pWInfo->a; ipIndex==0 /* (3) */ || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) diff --git a/test/analyze5.test b/test/analyze5.test index e08893a180..224433b7df 100644 --- a/test/analyze5.test +++ b/test/analyze5.test @@ -164,6 +164,8 @@ foreach {testid where index rows} { 301 {y=1} t1y 50 302 {y=0.1} t1y 50 + 400 {x IS NULL} t1x 400 + } { # Verify that the expected index is used with the expected row count do_test analyze5-1.${testid}a { @@ -190,5 +192,48 @@ foreach {testid where index rows} { } {ok} } +# Increase the number of NULLs in column x +# +db eval { + UPDATE t1 SET x=NULL; + UPDATE t1 SET x=rowid + WHERE rowid IN (SELECT rowid FROM t1 ORDER BY random() LIMIT 5); + ANALYZE; +} + +# Verify that range queries generate the correct row count estimates +# +foreach {testid where index rows} { + 500 {x IS NULL AND u='charlie'} t1u 20 + 501 {x=1 AND u='charlie'} t1x 5 + 502 {x IS NULL} {} 100 + 503 {x=1} t1x 50 + 504 {x IS NOT NULL} t1x 25 + +} { + # Verify that the expected index is used with the expected row count + do_test analyze5-1.${testid}a { + set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] + set idx {} + regexp {INDEX (t1.) } $x all idx + regexp {~([0-9]+) rows} $x all nrow + list $idx $nrow + } [list $index $rows] + + # Verify that the same result is achieved regardless of whether or not + # the index is used + do_test analyze5-1.${testid}b { + set w2 [string map {y +y z +z} $where] + set a1 [db eval "SELECT rowid FROM t1 NOT INDEXED WHERE $w2\ + ORDER BY +rowid"] + set a2 [db eval "SELECT rowid FROM t1 WHERE $where ORDER BY +rowid"] + if {$a1==$a2} { + set res ok + } else { + set res "a1=\[$a1\] a2=\[$a2\]" + } + set res + } {ok} +} finish_test diff --git a/test/analyze6.test b/test/analyze6.test new file mode 100644 index 0000000000..515747cbfc --- /dev/null +++ b/test/analyze6.test @@ -0,0 +1,74 @@ +# 2011 March 3 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file implements tests for SQLite library. The focus of the tests +# in this file a corner-case query planner optimization involving the +# join order of two tables of different sizes. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !stat2 { + finish_test + return +} + +set testprefix analyze6 + +proc eqp {sql {db db}} { + uplevel execsql [list "EXPLAIN QUERY PLAN $sql"] $db +} + +do_test analyze6-1.0 { + db eval { + CREATE TABLE cat(x INT); + CREATE UNIQUE INDEX catx ON cat(x); + /* Give cat 16 unique integers */ + INSERT INTO cat VALUES(1); + INSERT INTO cat VALUES(2); + INSERT INTO cat SELECT x+2 FROM cat; + INSERT INTO cat SELECT x+4 FROM cat; + INSERT INTO cat SELECT x+8 FROM cat; + + CREATE TABLE ev(y INT); + CREATE INDEX evy ON ev(y); + /* ev will hold 32 copies of 16 integers found in cat */ + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT x FROM cat; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + INSERT INTO ev SELECT y FROM ev; + ANALYZE; + SELECT count(*) FROM cat; + SELECT count(*) FROM ev; + } +} {16 512} + +# The lowest cost plan is to scan CAT and for each integer there, do a single +# lookup of the first corresponding entry in EV then read off the equal values +# in EV. (Prior to the 2011-03-04 enhancement to where.c, this query would +# have used EV for the outer loop instead of CAT - which was about 3x slower.) +# +do_test analyze6-1.1 { + eqp {SELECT count(*) FROM ev, cat WHERE x=y} +} {0 0 1 {SCAN TABLE cat (~16 rows)} 0 1 0 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + +# The same plan is chosen regardless of the order of the tables in the +# FROM clause. +# +do_test analyze6-1.2 { + eqp {SELECT count(*) FROM cat, ev WHERE x=y} +} {0 0 0 {SCAN TABLE cat (~16 rows)} 0 1 1 {SEARCH TABLE ev USING COVERING INDEX evy (y=?) (~32 rows)}} + + +finish_test diff --git a/test/expr.test b/test/expr.test index 140ab8d8b3..5a3d167517 100644 --- a/test/expr.test +++ b/test/expr.test @@ -82,8 +82,18 @@ test_expr expr-1.43 {i1=1, i2=2} {i1&i2} {0} test_expr expr-1.43b {i1=1, i2=2} {4&5} {4} test_expr expr-1.44 {i1=1} {~i1} {-2} test_expr expr-1.44b {i1=NULL} {~i1} {{}} -test_expr expr-1.45 {i1=1, i2=3} {i1<>i2} {4} +test_expr expr-1.45a {i1=1, i2=3} {i1<>i2} {8} +test_expr expr-1.45c {i1=1, i2=0} {i1<>i2} {0} +test_expr expr-1.46a {i1=32, i2=3} {i1>>i2} {4} +test_expr expr-1.46b {i1=32, i2=6} {i1>>i2} {0} +test_expr expr-1.46c {i1=-32, i2=3} {i1>>i2} {-4} +test_expr expr-1.46d {i1=-32, i2=100} {i1>>i2} {-1} +test_expr expr-1.46e {i1=32, i2=-3} {i1>>i2} {256} test_expr expr-1.47 {i1=9999999999, i2=8888888888} {i1i2} 1 @@ -154,10 +164,10 @@ ifcapable floatingpoint { } if {[working_64bit_int]} { - test_expr expr-1.106 {i1=0} {(1<<63)/-1} -9223372036854775808 + test_expr expr-1.106 {i1=0} {-9223372036854775808/-1} 9.22337203685478e+18 } -test_expr expr-1.107 {i1=0} {(1<<63)%-1} 0 +test_expr expr-1.107 {i1=0} {-9223372036854775808%-1} 0 test_expr expr-1.108 {i1=0} {1%0} {{}} test_expr expr-1.109 {i1=0} {1/0} {{}} @@ -190,6 +200,107 @@ test_expr expr-1.125 {i1=6, i2=NULL} \ test_expr expr-1.126 {i1=8, i2=8} \ {CASE WHEN i1 IS NOT i2 THEN 'yes' ELSE 'no' END} no +ifcapable floatingpoint {if {[working_64bit_int]} { + test_expr expr-1.200\ + {i1=9223372036854775806, i2=1} {i1+i2} 9223372036854775807 + test_expr expr-1.201\ + {i1=9223372036854775806, i2=2} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.202\ + {i1=9223372036854775806, i2=100000} {i1+i2} 9.22337203685488e+18 + test_expr expr-1.203\ + {i1=9223372036854775807, i2=0} {i1+i2} 9223372036854775807 + test_expr expr-1.204\ + {i1=9223372036854775807, i2=1} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.205\ + {i2=9223372036854775806, i1=1} {i1+i2} 9223372036854775807 + test_expr expr-1.206\ + {i2=9223372036854775806, i1=2} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.207\ + {i2=9223372036854775806, i1=100000} {i1+i2} 9.22337203685488e+18 + test_expr expr-1.208\ + {i2=9223372036854775807, i1=0} {i1+i2} 9223372036854775807 + test_expr expr-1.209\ + {i2=9223372036854775807, i1=1} {i1+i2} 9.22337203685478e+18 + test_expr expr-1.210\ + {i1=-9223372036854775807, i2=-1} {i1+i2} -9223372036854775808 + test_expr expr-1.211\ + {i1=-9223372036854775807, i2=-2} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.212\ + {i1=-9223372036854775807, i2=-100000} {i1+i2} -9.22337203685488e+18 + test_expr expr-1.213\ + {i1=-9223372036854775808, i2=0} {i1+i2} -9223372036854775808 + test_expr expr-1.214\ + {i1=-9223372036854775808, i2=-1} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.215\ + {i2=-9223372036854775807, i1=-1} {i1+i2} -9223372036854775808 + test_expr expr-1.216\ + {i2=-9223372036854775807, i1=-2} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.217\ + {i2=-9223372036854775807, i1=-100000} {i1+i2} -9.22337203685488e+18 + test_expr expr-1.218\ + {i2=-9223372036854775808, i1=0} {i1+i2} -9223372036854775808 + test_expr expr-1.219\ + {i2=-9223372036854775808, i1=-1} {i1+i2} -9.22337203685478e+18 + test_expr expr-1.220\ + {i1=9223372036854775806, i2=-1} {i1-i2} 9223372036854775807 + test_expr expr-1.221\ + {i1=9223372036854775806, i2=-2} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.222\ + {i1=9223372036854775806, i2=-100000} {i1-i2} 9.22337203685488e+18 + test_expr expr-1.223\ + {i1=9223372036854775807, i2=0} {i1-i2} 9223372036854775807 + test_expr expr-1.224\ + {i1=9223372036854775807, i2=-1} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.225\ + {i2=-9223372036854775806, i1=1} {i1-i2} 9223372036854775807 + test_expr expr-1.226\ + {i2=-9223372036854775806, i1=2} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.227\ + {i2=-9223372036854775806, i1=100000} {i1-i2} 9.22337203685488e+18 + test_expr expr-1.228\ + {i2=-9223372036854775807, i1=0} {i1-i2} 9223372036854775807 + test_expr expr-1.229\ + {i2=-9223372036854775807, i1=1} {i1-i2} 9.22337203685478e+18 + test_expr expr-1.230\ + {i1=-9223372036854775807, i2=1} {i1-i2} -9223372036854775808 + test_expr expr-1.231\ + {i1=-9223372036854775807, i2=2} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.232\ + {i1=-9223372036854775807, i2=100000} {i1-i2} -9.22337203685488e+18 + test_expr expr-1.233\ + {i1=-9223372036854775808, i2=0} {i1-i2} -9223372036854775808 + test_expr expr-1.234\ + {i1=-9223372036854775808, i2=1} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.235\ + {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 + test_expr expr-1.236\ + {i2=9223372036854775807, i1=-2} {i1-i2} -9.22337203685478e+18 + test_expr expr-1.237\ + {i2=9223372036854775807, i1=-100000} {i1-i2} -9.22337203685488e+18 + test_expr expr-1.238\ + {i2=9223372036854775807, i1=0} {i1-i2} -9223372036854775807 + test_expr expr-1.239\ + {i2=9223372036854775807, i1=-1} {i1-i2} -9223372036854775808 + + test_expr expr-1.250\ + {i1=4294967296, i2=2147483648} {i1*i2} 9.22337203685478e+18 + test_expr expr-1.251\ + {i1=4294967296, i2=2147483647} {i1*i2} 9223372032559808512 + test_expr expr-1.252\ + {i1=-4294967296, i2=2147483648} {i1*i2} -9223372036854775808 + test_expr expr-1.253\ + {i1=-4294967296, i2=2147483647} {i1*i2} -9223372032559808512 + test_expr expr-1.254\ + {i1=4294967296, i2=-2147483648} {i1*i2} -9223372036854775808 + test_expr expr-1.255\ + {i1=4294967296, i2=-2147483647} {i1*i2} -9223372032559808512 + test_expr expr-1.256\ + {i1=-4294967296, i2=-2147483648} {i1*i2} 9.22337203685478e+18 + test_expr expr-1.257\ + {i1=-4294967296, i2=-2147483647} {i1*i2} 9223372032559808512 + +}} + ifcapable floatingpoint { test_expr expr-2.1 {r1=1.23, r2=2.34} {r1+r2} 3.57 test_expr expr-2.2 {r1=1.23, r2=2.34} {r1-r2} -1.11 diff --git a/test/index.test b/test/index.test index 0a41a6de3e..790bed908e 100644 --- a/test/index.test +++ b/test/index.test @@ -354,7 +354,7 @@ do_test index-11.1 { } set sqlite_search_count 0 concat [execsql {SELECT c FROM t3 WHERE b==10}] $sqlite_search_count -} {0.1 3} +} {0.1 2} integrity_check index-11.2 diff --git a/test/mem5.test b/test/mem5.test new file mode 100644 index 0000000000..6365f1c64b --- /dev/null +++ b/test/mem5.test @@ -0,0 +1,66 @@ +# 2011 March 9 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# +# This file contains tests of the mem5 allocation subsystem. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !mem5 { + finish_test + return +} + +# The tests in this file configure the lookaside allocator after a +# connection is opened. This will not work if there is any "presql" +# configured (SQL run within the [sqlite3] wrapper in tester.tcl). +if {[info exists ::G(perm:presql)]} { + finish_test + return +} + +do_test mem5-1.1 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 25000000 0 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_OK} + +# try with min request size = 2^30 +do_test mem5-1.2 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 1 1073741824 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_NOMEM} + +# try with min request size = 2^30+1 +# previously this was causing the memsys5Log() func to infinitely loop. +do_test mem5-1.3 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 1 1073741825 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_NOMEM} + +do_test mem5-1.4 { + catch {db close} + sqlite3_shutdown + sqlite3_config_heap 0 0 + sqlite3_config_lookaside 0 0 + sqlite3_initialize +} {SQLITE_OK} + +finish_test diff --git a/test/omitunique.test b/test/omitunique.test new file mode 100644 index 0000000000..83a2a95bdc --- /dev/null +++ b/test/omitunique.test @@ -0,0 +1,170 @@ +# 2011 March 10 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the SQLITE_OMIT_UNIQUE_ENFORCEMENT +# compiler option. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set uniq_enforced 1 +ifcapable !unique_enforcement { + set uniq_enforced 0 +} + +# table with UNIQUE keyword on column +do_test omitunique-1.1 { + catchsql { CREATE TABLE t1(a TEXT UNIQUE); } +} {0 {}} + +# table with UNIQUE clause on column +do_test omitunique-1.2 { + catchsql { CREATE TABLE t2(a TEXT, UNIQUE(a)); } +} {0 {}} + +# table with UNIQUE index on column +do_test omitunique-1.3 { + catchsql { + CREATE TABLE t3(a TEXT); + CREATE UNIQUE INDEX t3a ON t3(a); + } +} {0 {}} + +# table with regular index on column +do_test omitunique-1.4 { + catchsql { + CREATE TABLE t4(a TEXT); + CREATE INDEX t4a ON t4(a); + } +} {0 {}} + +# table with no index on column +do_test omitunique-1.5 { + catchsql { CREATE TABLE t5(a TEXT); } +} {0 {}} + +# run our tests using several table/index forms +foreach {j tbl uniq cnt qp_est stat_enforce stat_omit } { +1 {t1} 1 1 1 {2 1} {9 9} +2 {t2} 1 1 1 {2 1} {9 9} +3 {t3} 1 1 1 {2 1} {9 9} +4 {t4} 0 9 10 {9 9} {9 9} +5 {t5} 0 9 100000 9 9 +} { + + do_test omitunique-2.0.$j.1 { + catchsql [ subst {INSERT INTO $tbl (a) VALUES('abc'); }] + } {0 {}} + do_test omitunique-2.0.$j.2 { + catchsql [ subst {INSERT INTO $tbl (a) VALUES('123'); }] + } {0 {}} + + # check various INSERT commands + foreach {i cmd err} { + 1 {INSERT} 1 + 2 {INSERT OR IGNORE} 0 + 3 {INSERT OR REPLACE} 0 + 4 {REPLACE} 0 + 5 {INSERT OR FAIL} 1 + 6 {INSERT OR ABORT} 1 + 7 {INSERT OR ROLLBACK} 1 + } { + + ifcapable explain { + set x [execsql [ subst { EXPLAIN $cmd INTO $tbl (a) VALUES('abc'); }]] + ifcapable unique_enforcement { + do_test omitunique-2.1.$j.$i.1 { + regexp { IsUnique } $x + } $uniq + } + ifcapable !unique_enforcement { + do_test omitunique-2.1.$j.$i.1 { + regexp { IsUnique } $x + } {0} + } + } + + if { $uniq_enforced==0 || $uniq==0 || $err==0 } { + set msg {0 {}} + } { + set msg {1 {column a is not unique}} + } + do_test omitunique-2.1.$j.$i.3 { + catchsql [ subst {$cmd INTO $tbl (a) VALUES('abc'); }] + } $msg + + } + # end foreach cmd + + # check UPDATE command + ifcapable explain { + set x [execsql [ subst { EXPLAIN UPDATE $tbl SET a='abc'; }]] + ifcapable unique_enforcement { + do_test omitunique-2.2.$j.1 { + regexp { IsUnique } $x + } $uniq + } + ifcapable !unique_enforcement { + do_test omitunique-2.2.$j.1 { + regexp { IsUnique } $x + } {0} + } + } + if { $uniq_enforced==0 || $uniq==0 } { + set msg {0 {}} + } { + set msg {1 {column a is not unique}} + } + do_test omitunique-2.2.$j.3 { + catchsql [ subst { UPDATE $tbl SET a='abc'; }] + } $msg + + # check record counts + do_test omitunique-2.3.$j { + execsql [ subst { SELECT count(*) FROM $tbl WHERE a='abc'; }] + } $cnt + + # make sure the query planner row estimate not affected because of omit enforcement + ifcapable explain { + do_test omitunique-2.4.$j { + set x [ execsql [ subst { EXPLAIN QUERY PLAN SELECT count(*) FROM $tbl WHERE a='abc'; }]] + set y [ subst {~$qp_est row} ] + regexp $y $x + } {1} + } + + # make sure we omit extra OP_Next opcodes when the UNIQUE constraints + # mean there will only be a single pass through the code + ifcapable explain { + set x [execsql [ subst { EXPLAIN SELECT * FROM $tbl WHERE a='abc'; }]] + do_test omitunique-2.5.$j { + if { [ regexp { Next } $x ] } { expr { 0 } } { expr { 1 } } + } $uniq + } + + # make sure analyze index stats correct + ifcapable analyze { + if { $uniq_enforced==0 } { + set msg [ list $stat_omit ] + } { + set msg [ list $stat_enforce ] + } + do_test omitunique-2.6.$j { + execsql [ subst { ANALYZE $tbl; } ] + execsql [ subst { SELECT stat FROM sqlite_stat1 WHERE tbl='$tbl'; } ] + } $msg + } + +} +# end foreach tbl + +finish_test diff --git a/test/oserror.test b/test/oserror.test index f2aa61008f..ecb02116ba 100644 --- a/test/oserror.test +++ b/test/oserror.test @@ -56,7 +56,7 @@ do_test 1.1.2 { catch { for {set i 0} {$i < 2000} {incr i} { dbh_$i close } } } {1} -do_re_test 1.1.3 { lindex $::log 0 } {^os_unix.c: open.*test.db$} +do_re_test 1.1.3 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*test.db\) - } # Test a failure in open() due to the path being a directory. @@ -67,7 +67,7 @@ do_test 1.2.1 { list [catch { sqlite3 dbh dir.db } msg] $msg } {1 {unable to open database file}} -do_re_test 1.2.2 { lindex $::log 0 } {^os_unix.c: open.*dir.db$} +do_re_test 1.2.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*dir.db\) - } # Test a failure in open() due to the path not existing. # @@ -76,7 +76,7 @@ do_test 1.3.1 { list [catch { sqlite3 dbh /x/y/z/test.db } msg] $msg } {1 {unable to open database file}} -do_re_test 1.3.2 { lindex $::log 0 } {^os_unix.c: open.*test.db$} +do_re_test 1.3.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) open\(.*test.db\) - } # Test a failure in open() due to the path not existing. # @@ -85,7 +85,7 @@ do_test 1.4.1 { list [catch { sqlite3 dbh /root/test.db } msg] $msg } {1 {unable to open database file}} -do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c: open.*test.db$} +do_re_test 1.4.2 { lindex $::log 0 } {^os_unix.c:\d*: \(\d+\) open\(.*test.db\) - } #-------------------------------------------------------------------------- # Tests oserror-1.* test failures in the unlink() system call. @@ -98,7 +98,7 @@ do_test 2.1.1 { catchsql { SELECT * FROM sqlite_master } dbh } {1 {disk I/O error}} -do_re_test 2.1.2 { lindex $::log 0 } {^os_unix.c: unlink.*test.db-wal$} +do_re_test 2.1.2 { lindex $::log 0 } {^os_unix.c:\d+: \(\d+\) unlink\(.*test.db-wal\) - } do_test 2.1.3 { dbh close forcedelete test.db-wal @@ -109,4 +109,3 @@ sqlite3_shutdown test_sqlite3_log sqlite3_initialize finish_test - diff --git a/test/progress.test b/test/progress.test old mode 100755 new mode 100644 diff --git a/tool/mkopts.tcl b/tool/mkopts.tcl old mode 100755 new mode 100644 diff --git a/tool/omittest.tcl b/tool/omittest.tcl index e03c48690c..e23dbaf4cb 100644 --- a/tool/omittest.tcl +++ b/tool/omittest.tcl @@ -202,6 +202,7 @@ proc main {argv} { SQLITE_OMIT_TRACE \ SQLITE_OMIT_TRIGGER \ SQLITE_OMIT_TRUNCATE_OPTIMIZATION \ + SQLITE_OMIT_UNIQUE_ENFORCEMENT \ SQLITE_OMIT_UTF16 \ SQLITE_OMIT_VACUUM \ SQLITE_OMIT_VIEW \