At 20:21 12.05.2009, Yu Luo wrote: >I would like to include a SQLite database as a resource file and compile that >into an executable. Would a DISQLite3Database then be able to load it? If so, >should I use the In Memory of DISQLite3Database? DISQLite3 can not by default access a databases directly from memory. However, it can do so with the help of a memory virtual file system (VFS). The VFS layer controls all file operations. I have attached a TStream VFS implementation (requires DISQLite3 Pro) which I use for some library testing. I believe that it should serve you well to access your resource file database directly via TResourceStream. First you must register the new VFS using: sqlite3_vfs_register_stream; Then you can open your database stream by simply calling: sqlite3_open_stream(@DB, Stream); Now you can use the usual sqlite3_... API calls on your TStream database. The stream VFS layer implementation gives read and write access, but does not create journal files. Therefore, ROLLBACK is unavailable. For TResourceStream, databases are of course read-only. Ralf
// {$DEFINE VFS_Debug} unit DISQLite3VfsStream; {$I DI.inc} {$I DISQLite3.inc} interface uses DISystemCompat, Classes, DISQLite3Api; function sqlite3_open_stream( const ADb: sqlite3_ptr; const AStream: TStream): Integer; function sqlite3_vfs_register_stream: Integer; implementation uses Windows, SysUtils; function isNt: Boolean; var sInfo: OSVERSIONINFO; begin sInfo.dwOSVersionInfoSize := SizeOf(sInfo); GetVersionEx(sInfo); Result := sInfo.dwPlatformId = VER_PLATFORM_WIN32_NT; end; //------------------------------------------------------------------------------ // IO Methods //------------------------------------------------------------------------------ type stream_file = packed record pMethods: sqlite3_io_methods_ptr; Stream: TStream; Name: PUtf8Char; end; stream_file_ptr = ^stream_file; //------------------------------------------------------------------------------ { Close a file. } function xClose( ID: sqlite3_file_ptr): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xClose ' + f^.Name); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { Read data from a file into a buffer. Return SQLITE_OK if all bytes were read successfully and SQLITE_IOERR if anything goes wrong. } function xRead( ID: sqlite3_file_ptr; // File to read from. pBuf: Pointer; // Write content into this buffer. iAmount: Integer; // Number of bytes to read. iOffset: Int64): Integer; // Begin reading at this offset. var {$IFDEF VFS_Debug} f: stream_file_ptr; {$ENDIF VFS_Debug} iRead: Integer; Stream: TStream; begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xRead ' + f^.Name); {$ENDIF VFS_Debug} Stream := stream_file_ptr(ID)^.Stream; if Assigned(Stream) then try Stream.Seek(iOffset, soBeginning); iRead := Stream.Read(pBuf^, iAmount); if iRead = iAmount then Result := SQLITE_OK else begin { Unread parts of the buffer must be zero-filled. } FillChar(PAnsiChar(pBuf)[iRead], iAmount - iRead, 0); Result := SQLITE_IOERR_SHORT_READ; end; except Result := SQLITE_IOERR_READ; end else begin Result := SQLITE_IOERR; end; end; //------------------------------------------------------------------------------ function xWrite( ID: sqlite3_file_ptr; const pBuf: Pointer; iAmount: Integer; iOffset: Int64): Integer; var f: stream_file_ptr; iWritten: Integer; begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xWrite ' + f^.Name); {$ENDIF VFS_Debug} f := stream_file_ptr(ID); if Assigned(f^.Stream) then try f^.Stream.Seek(iOffset, soBeginning); iWritten := f^.Stream.Write(pBuf^, iAmount); if iWritten = iAmount then Result := SQLITE_OK else Result := SQLITE_ERROR; except Result := SQLITE_ERROR; end else begin Result := SQLITE_OK; end; end; //------------------------------------------------------------------------------ function xTruncate( ID: sqlite3_file_ptr; Size: Int64): Integer; var Stream: TStream; begin {$IFDEF VFS_Debug}WriteLn('xTruncate'); {$ENDIF} Stream := stream_file_ptr(ID)^.Stream; try Stream.Size := Size; Result := SQLITE_OK except Result := SQLITE_ERROR; end; end; //------------------------------------------------------------------------------ { Make sure all writes to a particular file are committed to disk. } function xSync( ID: sqlite3_file_ptr; Flags: Integer): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xSync ' + f^.Name); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { Determine the current size of a file in bytes. Return SQLITE_OK on success. } function xFileSize( ID: sqlite3_file_ptr; pSize: PInt64): Integer; var {$IFDEF VFS_Debug} f: stream_file_ptr; {$ENDIF VFS_Debug} Stream: TStream; begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xFileSize ' + f^.Name); {$ENDIF VFS_Debug} Stream := stream_file_ptr(ID)^.Stream; if Assigned(Stream) then try pSize^ := Stream.Size; Result := SQLITE_OK; except Result := SQLITE_ERROR; end else begin pSize^ := 0; Result := SQLITE_OK; end; end; //------------------------------------------------------------------------------ function xLock( ID: sqlite3_file_ptr; lockType: Integer): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xLock ' + f^.Name); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { Lower the locking level on file descriptor id to locktype. locktype must be either NO_LOCK or SHARED_LOCK. If the locking level of the file descriptor is already at or below the requested locking level, this routine is a no-op. It is not possible for this routine to fail if the second argument is NO_LOCK. If the second argument is SHARED_LOCK then this routine might return SQLITE_IOERR. } function xUnlock( ID: sqlite3_file_ptr; lockType: Integer): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xUnlock ' + f^.Name); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { This routine checks if there is a RESERVED lock held on the specified file by this or any other process. If such a lock is held, return non-zero, otherwise zero. } function xCheckReservedLock( ID: sqlite3_file_ptr; pResOut: PInteger): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xCheckReservedLock ' + f^.Name); {$ENDIF VFS_Debug} pResOut^ := 0; Result := SQLITE_OK; end; //------------------------------------------------------------------------------ function xFileControl( ID: sqlite3_file_ptr; op: Integer; pArg: Pointer): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xFileControl ' + f^.Name); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { Not used. } function xLockState( ID: sqlite3_file_ptr): Integer; begin {$IFDEF VFS_Debug}WriteLn('xLockedState'); {$ENDIF} Result := SQLITE_LOCK_NONE; end; //------------------------------------------------------------------------------ function xSectorSize( ID: sqlite3_file_ptr): Integer; {$IFDEF VFS_Debug} var f: stream_file_ptr; {$ENDIF VFS_Debug} begin {$IFDEF VFS_Debug} f := stream_file_ptr(ID); WriteLn('xSectorSize ' + f^.Name); {$ENDIF VFS_Debug} Result := $200; end; //------------------------------------------------------------------------------ function xDeviceCharacteristics( ID: sqlite3_file_ptr): Integer; begin Result := SQLITE_OK; end; const stream_methods: sqlite3_io_methods = ( iVersion: 1; xClose: xClose; xRead: xRead; xWrite: xWrite; xTruncate: xTruncate; xSync: xSync; xFileSize: xFileSize; xLock: xLock; xUnlock: xUnlock; xCheckReservedLock: xCheckReservedLock; xFileControl: xFileControl; xLockState: nil; // xLockState; xSectorSize: xSectorSize; xDeviceCharacteristics: xDeviceCharacteristics); //------------------------------------------------------------------------------ // VFS Stream //------------------------------------------------------------------------------ { xOpen is used to open files on the underlying storage media. The result is an sqlite3_file object. There are additional methods, defined by the sqlite3_file object itself that are used to read and write and close the file. The additional methods are detailed below. The filename is in UTF-8. SQLite will guarantee that the zFilename string passed to xOpen() is a full pathname as generated by xFullPathname() and that the string will be valid and unchanged until xClose() is called. So the sqlite3_file can store a pointer to the filename if it needs to remember the filename for some reason. The flags argument to xOpen() is a copy of the flags argument to sqlite3_open_v2(). If sqlite3_open() or sqlite3_open16() is used, then flags is SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE. If xOpen() opens a file read-only then it sets *pOutFlags to include SQLITE_OPEN_READONLY. Other bits in *pOutFlags may be set. SQLite will also add one of the following flags to the xOpen() call, depending on the object being opened: * SQLITE_OPEN_MAIN_DB * SQLITE_OPEN_MAIN_JOURNAL * SQLITE_OPEN_TEMP_DB * SQLITE_OPEN_TEMP_JOURNAL * SQLITE_OPEN_SUBJOURNAL * SQLITE_OPEN_MASTER_JOURNAL The file I/O implementation can use the object type flags to changes the way it deals with files. For example, an application that does not care about crash recovery or rollback, might make the open of a journal file a no-op. Writes to this journal are also a no-op. Any attempt to read the journal return SQLITE_IOERR. Or the implementation might recognize that a database file will be doing page-aligned sector reads and writes in a random order and set up its I/O subsystem accordingly. SQLite might also add one of the following flags to the xOpen method: * SQLITE_OPEN_DELETEONCLOSE * SQLITE_OPEN_EXCLUSIVE The SQLITE_OPEN_DELETEONCLOSE flag means the file should be deleted when it is closed. This will always be set for TEMP databases and journals and for subjournals. The SQLITE_OPEN_EXCLUSIVE flag means the file should be opened for exclusive access. This flag is set for all files except for the main database file. The sqlite3_file structure passed as the third argument to xOpen is allocated by the caller. xOpen just fills it in. The caller allocates a minimum of szOsFile bytes for the sqlite3_file structure. } function xOpen( pVfs: sqlite3_vfs_ptr; const zName: PAnsiChar; pFile: sqlite3_file_ptr; Flags: Integer; pOutFlags: PInteger): Integer; var f: stream_file_ptr; begin {$IFDEF VFS_Debug} WriteLn('xOpen ', zName); {$ENDIF} f := stream_file_ptr(pFile); f^.pMethods := @stream_methods; f^.Name := zName; { Assigned stream for main DB only, not for journal files. } if Flags and SQLITE_OPEN_MAIN_DB <> 0 then begin pVfs.pAppData := zName; f^.Stream := TStream(StrToInt(zName)); end else begin f^.Stream := nil; end; Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { The xDelete method is used delete a file. The name of the file is given in the second parameter. The filename will be in UTF-8. The VFS must convert the filename into whatever character representation the underlying operating system expects. If the syncDir parameter is true, then the xDelete method should not return until the change to the directory contents for the directory containing the deleted file have been synced to disk in order to insure that the file does not "reappear" if a power failure occurs soon after. } function xDelete( pVfs: sqlite3_vfs_ptr; const zName: PAnsiChar; syncDir: Integer): Integer; begin {$IFDEF VFS_Debug} WriteLn('xDelete ' + zName); {$ENDIF VFS_Debug} Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { The xAccess method is used to check for access permissions on a file. The filename will be UTF-8 encoded. The flags argument will be SQLITE_ACCESS_EXISTS to check for the existence of the file, SQLITE_ACCESS_READWRITE to check to see if the file is both readable and writable, or SQLITE_ACCESS_READ to check to see if the file is at least readable. The "file" named by the second parameter might be a directory or folder name. Store True (1) if access is allowed, False (0) if not to pResOut^. Return SQLITE_OK or an error code. Not that SQLITE_IOERR_ACCESS has been specially added for this purpose. } function xAccess( pVfs: sqlite3_vfs_ptr; const zName: PUtf8Char; Flags: Integer; pResOut: PInteger): Integer; begin {$IFDEF VFS_Debug} WriteLn('xAccess ', zName, ' - Flags: ', Flags); {$ENDIF VFS_Debug} { Give access to the main DB stream only, not to journal files. } if StrComp(pVfs.pAppData, zName) = 0 then begin pResOut^ := 1; Result := SQLITE_OK; end else begin pResOut^ := 0; Result := SQLITE_OK; end; end; //------------------------------------------------------------------------------ { Turn a relative pathname into a full pathname. Write the full pathname into zOut. zOut will be at least pVfs->mxPathname bytes in size. } function xFullPathname( pVfs: sqlite3_vfs_ptr; // Pointer to vfs object. const zName: PUtf8Char; // Possibly relative input path. nOut: Integer; // Size of output buffer in bytes. zOut: PUtf8Char): Integer; // Output bufferd. begin {$IFDEF VFS_Debug} WriteLn('xFullPathname ', zName); {$ENDIF VFS_Debug} StrCopy(zOut, zName); Result := SQLITE_OK; end; //------------------------------------------------------------------------------ {$IFNDEF SQLITE_OMIT_LOAD_EXTENSION} { The xDlOpen, xDlError, xDlSym, and xDlClose methods are all used for accessing shared libraries at run-time. These methods may be omitted (and their pointers set to zero) if the library is compiled with SQLITE_OMIT_LOAD_EXTENSION or if the sqlite3_enable_load_extension() interface is never used to enable dynamic extension loading. The xDlOpen method opens a shared library or DLL and returns a pointer to a handle. NULL is returned if the open fails. If the open fails, the xDlError method can be used to obtain a text error message. The message is written into the zErrMsg buffer of the third parameter which is at least nByte bytes in length. The xDlSym returns a pointer to a symbol in the shared library. The name of the symbol is given by the second parameter. UTF-8 encoding is assumed. If the symbol is not found a NULL pointer is returned. The xDlClose routine closes the shared library. } function xDlOpen( pVfs: sqlite3_vfs_ptr; const zFilename: PUtf8Char): Pointer; var h: THandle; s: UnicodeString; begin {$IFDEF VFS_Debug}WriteLn('xDlOpen'); {$ENDIF} s := sqlite3_decodeutf8(zFilename); if isNt then h := LoadLibraryA(s); else h := LoadLibraryW(s); Result := Pointer(h); end; procedure xDlError( pVfs: sqlite3_vfs_ptr; nBuf: Integer; zBufOut: PUtf8Char); begin {$IFDEF VFS_Debug}WriteLn('xDlError'); {$ENDIF} getLastErrorMsg(nBuf, zBufOut); end; function xDlSym( pVfs: sqlite3_vfs_ptr; PHandle: Pointer; const zSymbol: PAnsiChar): Pointer; begin {$IFDEF VFS_Debug}WriteLn('xDlSym'); {$ENDIF} Result := GetProcAddress(THandle(PHandle), zSymbol); end; procedure xDlClose( pVfs: sqlite3_vfs_ptr; PHandle: Pointer); begin {$IFDEF VFS_Debug}WriteLn('xDlClose'); {$ENDIF} FreeLibrary(THandle(PHandle)); end; {$ENDIF !SQLITE_OMIT_LOAD_EXTENSION} //------------------------------------------------------------------------------ { The xRandomness method is used once by the first database connection that is opened. xRandomness should return high-quality randomness that SQLite will used to seeds its internal pseudo-random number generator (PRNG). The routine requests that nByte bytes of randomness be written into zOut. The routine returns the actual number of bytes of randomness obtained. The quality of the randomness so obtained will determine the quality of the randomness generated by built-in SQLite functions such as random() and randomblob(). } function xRandomness( pVfs: sqlite3_vfs_ptr; nByte: Integer; zOut: PAnsiChar): Integer; begin {$IFDEF VFS_Debug}WriteLn('xRandomness'); {$ENDIF} if SizeOf(TSystemTime) <= nByte then begin GetSystemTime(psystemtime(zOut)^); Result := SizeOf(TSystemTime); end else begin Result := 0; end; end; //------------------------------------------------------------------------------ { The xSleep method is used to suspend the calling thread for at least the number of microseconds given. This method is used to implement the sqlite3_sleep() and sqlite3_busy_timeout() APIs. In the case of sqlite3_sleep() the xSleep method of the default VFS is always used. If the underlying system does not have a microsecond resolution sleep capability, then the sleep time should be rounded up. xSleep returns this rounded-up value. } function xSleep( pVfs: sqlite3_vfs_ptr; microseconds: Integer): Integer; var m: Integer; begin {$IFDEF VFS_Debug}WriteLn('xSleep'); {$ENDIF} m := (microseconds + 999) div 1000; Sleep(m); Result := m * 1000; end; //------------------------------------------------------------------------------ { The xCurrentTime method finds the current time and date and writes the result as double-precision floating point value into pointer provided by the second parameter. The time and date is in coordinated universal time (UTC) and is a fractional julian day number. } function xCurrentTime( pVfs: sqlite3_vfs_ptr; pTimeOut: PDouble): Integer; var ft: TFileTime; begin {$IFDEF VFS_Debug}WriteLn('xCurrentTime'); {$ENDIF} GetSystemTimeAsFileTime(ft); pTimeOut^ := FileTimeToJulianDate(ft); Result := SQLITE_OK; end; //------------------------------------------------------------------------------ { The idea is that this function works like a combination of GetLastError() and FormatMessage() on windows (or errno and strerror_r() on unix). After an error is returned by an OS function, SQLite calls this function with zBuf pointing to a buffer of nBuf bytes. The OS layer should populate the buffer with a nul-terminated UTF-8 encoded error message describing the last IO error to have occurred within the calling thread. <p>If the error message is too large for the supplied buffer, it should be truncated. The return value of xGetLastError is zero if the error message fits in the buffer, or non-zero otherwise (if the message was truncated). If non-zero is returned, then it is not necessary to include the nul-terminator character in the output buffer. <p>Not supplying an error message will have no adverse effect on SQLite. It is fine to have an implementation that never returns an error message. <p>However if an error message is supplied, it will be incorporated by sqlite into the error message available to the user using sqlite3_errmsg(), possibly making IO errors easier to debug. } function xGetLastError( pVfs: sqlite3_vfs_ptr; nBuf: Integer; zBuf: PUtf8Char): Integer; begin {$IFDEF VFS_Debug}WriteLn('xGetLastError'); {$ENDIF} zBuf[0] := #0; Result := SQLITE_OK; end; var stream_vfs: sqlite3_vfs = ( iVersion: 1; szOsFile: SizeOf(stream_file); mxPathname: $20; pNext: nil; zName: 'stream'; pAppData: nil; xOpen: xOpen; xDelete: xDelete; xAccess: xAccess; xFullPathname: xFullPathname; xDlOpen: {$IFDEF SQLITE_OMIT_LOAD_EXTENSION}nil{$ELSE}xDlOpen{$ENDIF}; xDlError: {$IFDEF SQLITE_OMIT_LOAD_EXTENSION}nil{$ELSE}xDlError{$ENDIF}; xDlSym: {$IFDEF SQLITE_OMIT_LOAD_EXTENSION}nil{$ELSE}xDlSym{$ENDIF}; xDlClose: {$IFDEF SQLITE_OMIT_LOAD_EXTENSION}nil{$ELSE}xDlClose{$ENDIF}; xRandomness: xRandomness; xSleep: xSleep; xCurrentTime: xCurrentTime // xGetLastError: xGetLastError ); //------------------------------------------------------------------------------ // Interfaced Functions //------------------------------------------------------------------------------ { Register the VFS stream interface. } function sqlite3_vfs_register_stream: Integer; begin Result := sqlite3_vfs_register(@stream_vfs, 0); end; //------------------------------------------------------------------------------ function sqlite3_open_stream( const ADb: sqlite3_ptr; const AStream: TStream): Integer; begin Result := sqlite3_open_v2( PAnsiChar(IntToStr(Integer(AStream))), ADb, 0, 'stream'); end; end.