diff -r 3da531bb4329 -r 28839de615b4 persistentstorage/sql/OsLayer/os_symbian.cpp --- a/persistentstorage/sql/OsLayer/os_symbian.cpp Mon Jun 21 17:37:53 2010 +0300 +++ b/persistentstorage/sql/OsLayer/os_symbian.cpp Thu Aug 19 11:36:21 2010 +0300 @@ -84,6 +84,9 @@ EPanicFastCounterFreq =21 }; +//The SQLite temp files willl be created in this subdir +_LIT(KTempFileDir, "temp"); + //Bit-mask constant. If xOpen()'s "aFlag" parameter contains one of these bits set, then the the file top be //opened or created is a journal file. const TUint KJournalFileTypeBitMask = SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_TEMP_JOURNAL | SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_MASTER_JOURNAL; @@ -421,7 +424,7 @@ fname.Copy(fn8); } // 0 1 2 3 4 5 6 7 8 9 10 - RDebug::Print(_L("[SQL-OS]¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬\"%X\"¬%c¬%S¬%d¬%d¬%ld¬%d¬%ld¬%ld¬%ld¬%S\n"), + RDebug::Print(_L("[SQL-OS]¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬¬\"%X\"¬%c¬%S¬%d¬%d¬%ld¬%d¬%ld¬%ld¬%ld¬%S\r\n"), //[SQL-OS] //Handle //Time from start, microseconds @@ -720,7 +723,6 @@ public: RFs iFs; //File session instance. TFileName iSysPrivDir;//":\" + process's private data path. Initialized in sqlite3SymbianFsOpen(). - //Used for storing sqlite temporary files. TInt64 iSeed; private: @@ -767,12 +769,13 @@ { inline TDbFile(); RFileBuf64 iFileBuf; - HBufC* iFullName; //Used for the "delete file" operation (RFile64::FullName() makes an IPC call!) TInt iLockType; //File lock type TBool iReadOnly; //True if the file is read-only TInt iSectorSize; //Media sector-size TInt iDeviceCharacteristics; TSqlFreePageCallback iFreePageCallback; + TBool iIsFileCreated; //If the file to be created is a temp file, + //it will not be created until the data is to be written to. #ifdef _SQLPROFILER TBool iIsJournal; #endif @@ -814,6 +817,8 @@ static int FileControl(sqlite3_file* aDbFile, int aOp, void* aArg); static int SectorSize(sqlite3_file* aDbFile); static int DeviceCharacteristics(sqlite3_file* aDbFile); +private: + static TInt DoCreateTempFile(TDbFile& aDbFile); }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -842,12 +847,13 @@ static int Sleep(sqlite3_vfs* aVfs, int aMicrosec); static int CurrentTime(sqlite3_vfs* aVfs, double* aNow); static int GetLastError(sqlite3_vfs *sVfs, int aBufLen, char* aBuf); + static TInt DoGetDeviceCharacteristicsAndSectorSize(TDbFile& aDbFile, TInt& aRecReadBufSize); + private: static TInt DoOpenFromHandle(TDbFile& aDbFile, const RMessage2& aMsg, TBool aReadOnly); static inline TInt DoGetVolumeIoParamInfo(RFs& aFs, TInt aDriveNo, TVolumeIOParamInfo& aVolumeInfo); static TInt DoGetDeviceCharacteristics(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo); static TInt DoGetSectorSize(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo); - static TInt DoGetDeviceCharacteristicsAndSectorSize(TDbFile& aDbFile, TInt& aRecReadBufSize); static TInt DoFileSizeCorruptionCheck(TDbFile& aDbFile, const TDesC& aFname, TInt aFmode); }; @@ -1136,6 +1142,13 @@ TParse parse; (void)parse.Set(driveName, &privateDir, 0);//this call can't fail iSysPrivDir.Copy(parse.DriveAndPath()); + //Create the temp files directory + (void)parse.AddDir(KTempFileDir);//this call can't fail + err = iFs.MkDir(parse.DriveAndPath()); + if(err != KErrNone && err != KErrAlreadyExists) + { + return err; + } return KErrNone; } @@ -1446,11 +1459,11 @@ */ inline TDbFile::TDbFile() : iFileBuf(KFileBufSize), - iFullName(0), iLockType(SQLITE_LOCK_NONE), iReadOnly(EFalse), iSectorSize(0), - iDeviceCharacteristics(-1) + iDeviceCharacteristics(-1), + iIsFileCreated(ETrue) { #ifdef _SQLPROFILER iIsJournal = EFalse; @@ -1488,7 +1501,6 @@ SQLite OS porting layer API. Closes the file referred by aDbFile parameter. -If aDbFile.iFullName data member is not NULL, then the file will be deleted. @param aDbFile A pointer to a TDbFile instance, than contains the file handle to be closed. @@ -1503,14 +1515,7 @@ __OS_CALL(EOsFileClose, 0, 0); __OSTIME_COUNTER(TheOsCallTicks[EOsFileClose], ::OsCallProfile(dbFile.iIsJournal, EOsFileClose), 0, 0, aDbFile, 0); __FS_CALL(EFsOpFileClose, 0); - dbFile.iFileBuf.Close(); - if(dbFile.iFullName) - {//"iFullName" will not be NULL only when TVfs::Open() is called with SQLITE_OPEN_DELETEONCLOSE flag. - //That means - SQlite expects the file to be deleted after the file close operation. - __FS_CALL(EFsOpFileDelete, 0); - (void)COsLayerData::Instance().iFs.Delete(*dbFile.iFullName); - delete dbFile.iFullName; - } + dbFile.iFileBuf.Close(); return SQLITE_OK; } @@ -1573,6 +1578,51 @@ return sqliteErr; } +//Creates a temporary file in "\temp" subdirectory of osLayerData.iSysPrivDir directory. +//If the function fails, the temp file will be closed and deleted, +//the related Symbian OS error will be returned to the caller. +/* static */TInt TFileIo::DoCreateTempFile(TDbFile& aDbFile) + { + COsLayerData& osLayerData = COsLayerData::Instance(); + //TParse2 is used in order to avoid the need of another TFileName stack based variable + class TParse2 : public TParse + { + public: + inline TFileName& FileName() + { + return static_cast (NameBuf()); + } + }; + TParse2 parse; + (void)parse.Set(osLayerData.iSysPrivDir, 0, 0);//this call can't fail + (void)parse.AddDir(KTempFileDir);//this call can't fail + __FS_CALL(EFsOpFileCreateTemp, 0); + TInt err = aDbFile.iFileBuf.Temp(osLayerData.iFs, parse.FullName(), parse.FileName(), EFileRead|EFileWrite|EDeleteOnClose); + if(err == KErrPathNotFound) + { + err = osLayerData.iFs.MkDirAll(parse.DriveAndPath()); + if(err == KErrNone) + { + err = aDbFile.iFileBuf.Temp(osLayerData.iFs, parse.FullName(), parse.FileName(), EFileRead|EFileWrite|EDeleteOnClose); + } + } + if(err == KErrNone) + { + TInt recReadBufSize = -1; + err = TVfs::DoGetDeviceCharacteristicsAndSectorSize(aDbFile, recReadBufSize); + if(err != KErrNone) + { + aDbFile.iFileBuf.Close();//With EDeleteOnClose flag set, the file will be deleted + } + else + { + (void)aDbFile.iFileBuf.SetReadAheadSize(aDbFile.iSectorSize, recReadBufSize); + aDbFile.iIsFileCreated = ETrue; + } + } + return err; + } + /** SQLite OS porting layer API. @@ -1583,6 +1633,8 @@ (TDbFile::iFreePageCallback) and the free pages count is above the defined value, then the callback will be called. +If the file to be written to is a temp file, which is not created yet, then the file will be created. + @param aDbFile A pointer to a TDbFile instance, that contains the file handle to be written to. @param aData The data to be written to the file. The buffer size must be at least aAmt bytes. @param aAmt The amount of data to be written to the file. @@ -1604,32 +1656,42 @@ SQLUTRACE_PROFILER(aDbFile); SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileWrite, aAmt, aOffset)); TDbFile& dbFile = ::DbFile(aDbFile); - __OS_CALL(EOsFileWrite, 0, 0); - __COUNTER_INCR(TheSqlSrvProfilerFileWrite); - __OSTIME_COUNTER(TheOsCallTicks[EOsFileWrite], ::OsCallProfile(dbFile.iIsJournal, EOsFileWrite), aOffset, aAmt, aDbFile, 0); - TInt err = KErrAccessDenied; - if(!dbFile.iReadOnly) - { - TPtrC8 ptr((const TUint8*)aData, aAmt); - err = dbFile.iFileBuf.Write(aOffset, ptr); - } - COsLayerData::Instance().SetOsErrorCode(err); + TInt err = KErrNone; + if(!dbFile.iIsFileCreated) + {//Create a temp file if it has not been created. + err = TFileIo::DoCreateTempFile(dbFile); + } + if(err != KErrNone) + { + COsLayerData::Instance().SetOsErrorCode(err); + return err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_FULL; + } - const TInt KFreePageCountOffset = 36;//hard-coded constant. SQLite does not offer anything - a constant or #define. - //The checks in the "if" bellow do: - // - "err == KErrNone" - check the free page count only after a successful "write"; - // - "aOffset == 0" - check the free page count only if the write operation affects the system page (at aOffset = 0); - // - "aAmt >= (KFreePageCountOffset + sizeof(int))" - check the free page count only if the amount of bytes to be written - // is more than the offset of the free page counter (othewrise the free page counter is not affected - // by this write operation); - // - "dbFile.iFreePageCallback.IsValid()" - check the free page count only if there is a valid callback; - if(err == KErrNone && aOffset == 0 && aAmt >= (KFreePageCountOffset + sizeof(int)) && dbFile.iFreePageCallback.IsValid()) - { - const TUint8* ptr = static_cast (aData) + KFreePageCountOffset; - TInt freePageCount = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; - dbFile.iFreePageCallback.CheckAndCallback(freePageCount); - } - + __OS_CALL(EOsFileWrite, 0, 0); + __COUNTER_INCR(TheSqlSrvProfilerFileWrite); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileWrite], ::OsCallProfile(dbFile.iIsJournal, EOsFileWrite), aOffset, aAmt, aDbFile, 0); + err = KErrAccessDenied; + if(!dbFile.iReadOnly) + { + TPtrC8 ptr((const TUint8*)aData, aAmt); + err = dbFile.iFileBuf.Write(aOffset, ptr); + } + COsLayerData::Instance().SetOsErrorCode(err); + + const TInt KFreePageCountOffset = 36;//hard-coded constant. SQLite does not offer anything - a constant or #define. + //The checks in the "if" bellow do: + // - "err == KErrNone" - check the free page count only after a successful "write"; + // - "aOffset == 0" - check the free page count only if the write operation affects the system page (at aOffset = 0); + // - "aAmt >= (KFreePageCountOffset + sizeof(int))" - check the free page count only if the amount of bytes to be written + // is more than the offset of the free page counter (othewrise the free page counter is not affected + // by this write operation); + // - "dbFile.iFreePageCallback.IsValid()" - check the free page count only if there is a valid callback; + if(err == KErrNone && aOffset == 0 && aAmt >= (KFreePageCountOffset + sizeof(int)) && dbFile.iFreePageCallback.IsValid()) + { + const TUint8* ptr = static_cast (aData) + KFreePageCountOffset; + TInt freePageCount = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; + dbFile.iFreePageCallback.CheckAndCallback(freePageCount); + } return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_FULL); } @@ -2233,116 +2295,106 @@ @see TDbFile */ /* static */ int TVfs::Open(sqlite3_vfs* aVfs, const char* aFileName, sqlite3_file* aDbFile, int aFlags, int* aOutFlags) - { - SQLUTRACE_PROFILER(aVfs); - __OS_CALL(EOsVfsOpen, 0, 0); - __OSTIME_COUNTER(TheOsCallTicks[EOsVfsOpen], ::OsCallProfile(EFalse, EOsVfsOpen), 0, 0, aDbFile, aFileName); - COsLayerData& osLayerData = COsLayerData::Instance(); - TFileName fname; - if(aFileName && !::ConvertToUnicode(aFileName, fname)) - { - osLayerData.SetOsErrorCode(KErrBadName); - return SQLITE_CANTOPEN; - } - SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileOpen, aDbFile, &fname)); - new (aDbFile) TDbFile; - TDbFile& dbFile = ::DbFile(aDbFile); - TFhStrType fhStrType = aFileName ? ::FhStringProps(aFileName) : ENotFhStr; - if(aFileName && (aFlags & SQLITE_OPEN_DELETEONCLOSE)) - { - dbFile.iFullName = fname.Alloc(); - if(!dbFile.iFullName) - { - osLayerData.SetOsErrorCode(KErrNoMemory); - return SQLITE_IOERR_NOMEM; - } - } - TInt recReadBufSize = -1; - TInt err = KErrNone; - if(fhStrType == EFhMainDbStr) - {//Main db file, open from handle - const RMessage2* msg; - TBool readOnly; - osLayerData.RetrieveAndResetFhData(msg, readOnly); - err = msg != NULL ? TVfs::DoOpenFromHandle(dbFile, *msg, readOnly) : KErrGeneral; - } - else - { - if(fhStrType == EFhStr) - {//Not the main db file. Remove invalid characters in the file name - ::FhConvertToFileName(fname, osLayerData.iSysPrivDir);//If fname does not have a path, iSysPrivDir will be used - } - TInt fmode = EFileRead; - if(aFlags & SQLITE_OPEN_READWRITE) - { - fmode |= EFileWrite; - } - if(aFlags & SQLITE_OPEN_EXCLUSIVE) - { - fmode |= EFileShareExclusive; - } - if(!aFileName) - { - __FS_CALL(EFsOpFileCreateTemp, 0); - err = dbFile.iFileBuf.Temp(osLayerData.iFs, osLayerData.iSysPrivDir, fname, fmode); - if(err == KErrNone) - { - dbFile.iFullName = fname.Alloc(); - if(!dbFile.iFullName) - { - err = KErrNoMemory; - } - } - } - else - { - err = KErrAccessDenied; - TInt prevErr = KErrNone; - if(aFlags & SQLITE_OPEN_CREATE) - { - __FS_CALL(EFsOpFileCreate, 0); - prevErr = err = dbFile.iFileBuf.Create(osLayerData.iFs, fname, fmode); - } - if(err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) - { - __FS_CALL(EFsOpFileOpen, 0); - err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); - - if(err == KErrNone && (aFlags & KJournalFileTypeBitMask)) - { + { + SQLUTRACE_PROFILER(aVfs); + __OS_CALL(EOsVfsOpen, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsOpen], ::OsCallProfile(EFalse, EOsVfsOpen), 0, 0, aDbFile, aFileName); + + COsLayerData& osLayerData = COsLayerData::Instance(); + TFhStrType fhStrType = ENotFhStr; + new (aDbFile) TDbFile; + TDbFile& dbFile = ::DbFile(aDbFile); + + if(!aFileName) + { + //It is to create and open a temp file if aFileName is NULL. In this case, + //we will defer the file creation util it is needed. + + dbFile.pMethods = &TheFileIoApi; + dbFile.iIsFileCreated = EFalse; + if(aOutFlags) + { + *aOutFlags = SQLITE_OPEN_READWRITE; + } + return SQLITE_OK; + } + + TFileName fname; + if(!::ConvertToUnicode(aFileName, fname)) + { + osLayerData.SetOsErrorCode(KErrBadName); + return SQLITE_CANTOPEN; + } + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileOpen, aDbFile, &fname)); + fhStrType = ::FhStringProps(aFileName); + TInt err = KErrNone; + TInt recReadBufSize = -1; + if(fhStrType == EFhMainDbStr) + {//Main db file, open from handle + const RMessage2* msg; + TBool readOnly; + osLayerData.RetrieveAndResetFhData(msg, readOnly); + err = msg != NULL ? TVfs::DoOpenFromHandle(dbFile, *msg, readOnly) : KErrGeneral; + } + else + { + if(fhStrType == EFhStr) + {//Not the main db file. Remove invalid characters in the file name + ::FhConvertToFileName(fname, osLayerData.iSysPrivDir);//If fname does not have a path, iSysPrivDir will be used + } + TInt fmode = EFileRead; + if(aFlags & SQLITE_OPEN_READWRITE) + { + fmode |= EFileWrite; + } + if(aFlags & SQLITE_OPEN_EXCLUSIVE) + { + fmode |= EFileShareExclusive; + } + err = KErrAccessDenied; + TInt prevErr = KErrNone; + if(aFlags & SQLITE_OPEN_DELETEONCLOSE) + { + fmode |= EDeleteOnClose; + } + if(aFlags & SQLITE_OPEN_CREATE) + { + __FS_CALL(EFsOpFileCreate, 0); + prevErr = err = dbFile.iFileBuf.Create(osLayerData.iFs, fname, fmode); + } + if(err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) + { + __FS_CALL(EFsOpFileOpen, 0); + err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); + + if(err == KErrNone && (aFlags & KJournalFileTypeBitMask)) + { err = TVfs::DoFileSizeCorruptionCheck(dbFile, fname, fmode); - } - } - if((err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) && (aFlags & SQLITE_OPEN_READWRITE)) - { - aFlags &= ~SQLITE_OPEN_READWRITE; - aFlags |= SQLITE_OPEN_READONLY; - fmode &= ~EFileWrite; - __FS_CALL(EFsOpFileOpen, 0); - err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); - } - if(err != KErrNone && prevErr == KErrAccessDenied) - { - err = KErrAccessDenied; - } - } - } - if(err == KErrNone) - { - err = TVfs::DoGetDeviceCharacteristicsAndSectorSize(dbFile, recReadBufSize); - } + } + } + if((err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) && (aFlags & SQLITE_OPEN_READWRITE)) + { + aFlags &= ~SQLITE_OPEN_READWRITE; + aFlags |= SQLITE_OPEN_READONLY; + fmode &= ~EFileWrite; + __FS_CALL(EFsOpFileOpen, 0); + err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); + } + if(err != KErrNone && prevErr == KErrAccessDenied) + { + err = KErrAccessDenied; + } + } + if(err == KErrNone) + { + err = TVfs::DoGetDeviceCharacteristicsAndSectorSize(dbFile, recReadBufSize); + } + osLayerData.SetOsErrorCode(err); if(err != KErrNone) { __FS_CALL(EFsOpFileClose, 0); dbFile.iFileBuf.Close(); - delete dbFile.iFullName; - dbFile.iFullName = NULL; - if(!aFileName && fname.Length() > 0) - {//temporary file, the error is not KErrNone. Then delete the file (after a successfull - //temporary file creation there could be a failed memory allocation) - (void)osLayerData.iFs.Delete(fname); - } } else {