From 30343a531999b5e50673ee1731f1c54cbc008dfd Mon Sep 17 00:00:00 2001 From: jaybinks Date: Wed, 5 Sep 2007 12:02:06 +0000 Subject: added 3rd party dependencies ( except Jedi-SDL ) modified DPR to statically include all files needed (using relative paths) this means 3rd party components should not need installation in the IDE, or adding to search paths. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@368 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/lib/SQLite/SQLite3.dcu | Bin 0 -> 8243 bytes Game/Code/lib/SQLite/SQLite3.pas | 189 +++++++ Game/Code/lib/SQLite/SQLiteSpy.db3 | Bin 0 -> 154624 bytes Game/Code/lib/SQLite/SQLiteSpy.exe | Bin 0 -> 1657856 bytes Game/Code/lib/SQLite/SQLiteTable3.dcu | Bin 0 -> 19499 bytes Game/Code/lib/SQLite/SQLiteTable3.pas | 761 +++++++++++++++++++++++++++ Game/Code/lib/SQLite/SQLiteTable3.~pas | 761 +++++++++++++++++++++++++++ Game/Code/lib/SQLite/example/Sunset.jpg | Bin 0 -> 71189 bytes Game/Code/lib/SQLite/example/TestSqlite.dpr | 15 + Game/Code/lib/SQLite/example/TestSqlite.exe | Bin 0 -> 504832 bytes Game/Code/lib/SQLite/example/TestSqlite.res | Bin 0 -> 876 bytes Game/Code/lib/SQLite/example/uTestSqlite.dfm | 110 ++++ Game/Code/lib/SQLite/example/uTestSqlite.pas | 233 ++++++++ Game/Code/lib/SQLite/readme.txt | 82 +++ Game/Code/lib/SQLite/sqlite3.dll | Bin 0 -> 388126 bytes Game/Code/lib/SQLite/test.db | Bin 0 -> 73728 bytes 16 files changed, 2151 insertions(+) create mode 100644 Game/Code/lib/SQLite/SQLite3.dcu create mode 100644 Game/Code/lib/SQLite/SQLite3.pas create mode 100644 Game/Code/lib/SQLite/SQLiteSpy.db3 create mode 100644 Game/Code/lib/SQLite/SQLiteSpy.exe create mode 100644 Game/Code/lib/SQLite/SQLiteTable3.dcu create mode 100644 Game/Code/lib/SQLite/SQLiteTable3.pas create mode 100644 Game/Code/lib/SQLite/SQLiteTable3.~pas create mode 100644 Game/Code/lib/SQLite/example/Sunset.jpg create mode 100644 Game/Code/lib/SQLite/example/TestSqlite.dpr create mode 100644 Game/Code/lib/SQLite/example/TestSqlite.exe create mode 100644 Game/Code/lib/SQLite/example/TestSqlite.res create mode 100644 Game/Code/lib/SQLite/example/uTestSqlite.dfm create mode 100644 Game/Code/lib/SQLite/example/uTestSqlite.pas create mode 100644 Game/Code/lib/SQLite/readme.txt create mode 100644 Game/Code/lib/SQLite/sqlite3.dll create mode 100644 Game/Code/lib/SQLite/test.db (limited to 'Game/Code/lib/SQLite') diff --git a/Game/Code/lib/SQLite/SQLite3.dcu b/Game/Code/lib/SQLite/SQLite3.dcu new file mode 100644 index 00000000..4ff1c1c7 Binary files /dev/null and b/Game/Code/lib/SQLite/SQLite3.dcu differ diff --git a/Game/Code/lib/SQLite/SQLite3.pas b/Game/Code/lib/SQLite/SQLite3.pas new file mode 100644 index 00000000..b7f9d375 --- /dev/null +++ b/Game/Code/lib/SQLite/SQLite3.pas @@ -0,0 +1,189 @@ +unit SQLite3; + +{ + Simplified interface for SQLite. + Updated for Sqlite 3 by Tim Anderson (tim@itwriting.com) + Note: NOT COMPLETE for version 3, just minimal functionality + Adapted from file created by Pablo Pissanetzky (pablo@myhtpc.net) + which was based on SQLite.pas by Ben Hochstrasser (bhoc@surfeu.ch) +} + +interface + +const + + SQLiteDLL = 'sqlite3.dll'; + +// Return values for sqlite3_exec() and sqlite3_step() + + SQLITE_OK = 0; // Successful result + SQLITE_ERROR = 1; // SQL error or missing database + SQLITE_INTERNAL = 2; // An internal logic error in SQLite + SQLITE_PERM = 3; // Access permission denied + SQLITE_ABORT = 4; // Callback routine requested an abort + SQLITE_BUSY = 5; // The database file is locked + SQLITE_LOCKED = 6; // A table in the database is locked + SQLITE_NOMEM = 7; // A malloc() failed + SQLITE_READONLY = 8; // Attempt to write a readonly database + SQLITE_INTERRUPT = 9; // Operation terminated by sqlite3_interrupt() + SQLITE_IOERR = 10; // Some kind of disk I/O error occurred + SQLITE_CORRUPT = 11; // The database disk image is malformed + SQLITE_NOTFOUND = 12; // (Internal Only) Table or record not found + SQLITE_FULL = 13; // Insertion failed because database is full + SQLITE_CANTOPEN = 14; // Unable to open the database file + SQLITE_PROTOCOL = 15; // Database lock protocol error + SQLITE_EMPTY = 16; // Database is empty + SQLITE_SCHEMA = 17; // The database schema changed + SQLITE_TOOBIG = 18; // Too much data for one row of a table + SQLITE_CONSTRAINT = 19; // Abort due to contraint violation + SQLITE_MISMATCH = 20; // Data type mismatch + SQLITE_MISUSE = 21; // Library used incorrectly + SQLITE_NOLFS = 22; // Uses OS features not supported on host + SQLITE_AUTH = 23; // Authorization denied + SQLITE_FORMAT = 24; // Auxiliary database format error + SQLITE_RANGE = 25; // 2nd parameter to sqlite3_bind out of range + SQLITE_NOTADB = 26; // File opened that is not a database file + SQLITE_ROW = 100; // sqlite3_step() has another row ready + SQLITE_DONE = 101; // sqlite3_step() has finished executing + + SQLITE_INTEGER = 1; + SQLITE_FLOAT = 2; + SQLITE_TEXT = 3; + SQLITE_BLOB = 4; + SQLITE_NULL = 5; + +type + TSQLiteDB = Pointer; + TSQLiteResult = ^PChar; + TSQLiteStmt = Pointer; + +function SQLite3_Open(dbname: PChar; var db: TSqliteDB): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_open'; +function SQLite3_Close(db: TSQLiteDB): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_close'; +function SQLite3_Exec(db: TSQLiteDB; SQLStatement: PChar; CallbackPtr: Pointer; Sender: TObject; var ErrMsg: PChar): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_exec'; +function SQLite3_Version(): PChar; cdecl; external 'sqlite3.dll' name 'sqlite3_libversion'; +function SQLite3_ErrMsg(db: TSQLiteDB): PChar; cdecl; external 'sqlite3.dll' name 'sqlite3_errmsg'; +function SQLite3_ErrCode(db: TSQLiteDB): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_errcode'; +procedure SQlite3_Free(P: PChar); cdecl; external 'sqlite3.dll' name 'sqlite3_free'; +function SQLite3_GetTable(db: TSQLiteDB; SQLStatement: PChar; var ResultPtr: TSQLiteResult; var RowCount: Cardinal; var ColCount: Cardinal; var ErrMsg: PChar): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_get_table'; +procedure SQLite3_FreeTable(Table: TSQLiteResult); cdecl; external 'sqlite3.dll' name 'sqlite3_free_table'; +function SQLite3_Complete(P: PChar): boolean; cdecl; external 'sqlite3.dll' name 'sqlite3_complete'; +function SQLite3_LastInsertRowID(db: TSQLiteDB): int64; cdecl; external 'sqlite3.dll' name 'sqlite3_last_insert_rowid'; +procedure SQLite3_Interrupt(db: TSQLiteDB); cdecl; external 'sqlite3.dll' name 'sqlite3_interrupt'; +procedure SQLite3_BusyHandler(db: TSQLiteDB; CallbackPtr: Pointer; Sender: TObject); cdecl; external 'sqlite3.dll' name 'sqlite3_busy_handler'; +procedure SQLite3_BusyTimeout(db: TSQLiteDB; TimeOut: integer); cdecl; external 'sqlite3.dll' name 'sqlite3_busy_timeout'; +function SQLite3_Changes(db: TSQLiteDB): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_changes'; +function SQLite3_TotalChanges(db: TSQLiteDB): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_total_changes'; +function SQLite3_Prepare(db: TSQLiteDB; SQLStatement: PChar; nBytes: integer; var hStmt: TSqliteStmt; var pzTail: PChar): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_prepare'; +function SQLite3_ColumnCount(hStmt: TSqliteStmt): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_column_count'; +function Sqlite3_ColumnName(hStmt: TSqliteStmt; ColNum: integer): pchar; cdecl; external 'sqlite3.dll' name 'sqlite3_column_name'; +function Sqlite3_ColumnDeclType(hStmt: TSqliteStmt; ColNum: integer): pchar; cdecl; external 'sqlite3.dll' name 'sqlite3_column_decltype'; +function Sqlite3_Step(hStmt: TSqliteStmt): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_step'; +function SQLite3_DataCount(hStmt: TSqliteStmt): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_data_count'; + +function Sqlite3_ColumnBlob(hStmt: TSqliteStmt; ColNum: integer): pointer; cdecl; external 'sqlite3.dll' name 'sqlite3_column_blob'; +function Sqlite3_ColumnBytes(hStmt: TSqliteStmt; ColNum: integer): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_column_bytes'; +function Sqlite3_ColumnDouble(hStmt: TSqliteStmt; ColNum: integer): double; cdecl; external 'sqlite3.dll' name 'sqlite3_column_double'; +function Sqlite3_ColumnInt(hStmt: TSqliteStmt; ColNum: integer): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_column_int'; +function Sqlite3_ColumnText(hStmt: TSqliteStmt; ColNum: integer): pchar; cdecl; external 'sqlite3.dll' name 'sqlite3_column_text'; +function Sqlite3_ColumnType(hStmt: TSqliteStmt; ColNum: integer): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_column_type'; +function Sqlite3_ColumnInt64(hStmt: TSqliteStmt; ColNum: integer): Int64; cdecl; external 'sqlite3.dll' name 'sqlite3_column_int64'; +function SQLite3_Finalize(hStmt: TSqliteStmt): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_finalize'; +function SQLite3_Reset(hStmt: TSqliteStmt): integer; cdecl; external 'sqlite3.dll' name 'sqlite3_reset'; + +// +// In the SQL strings input to sqlite3_prepare() and sqlite3_prepare16(), +// one or more literals can be replace by a wildcard "?" or ":N:" where +// N is an integer. These value of these wildcard literals can be set +// using the routines listed below. +// +// In every case, the first parameter is a pointer to the sqlite3_stmt +// structure returned from sqlite3_prepare(). The second parameter is the +// index of the wildcard. The first "?" has an index of 1. ":N:" wildcards +// use the index N. +// + // The fifth parameter to sqlite3_bind_blob(), sqlite3_bind_text(), and + //sqlite3_bind_text16() is a destructor used to dispose of the BLOB or +//text after SQLite has finished with it. If the fifth argument is the +// special value SQLITE_STATIC, then the library assumes that the information +// is in static, unmanaged space and does not need to be freed. If the +// fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its +// own private copy of the data. +// +// The sqlite3_bind_* routine must be called before sqlite3_step() after +// an sqlite3_prepare() or sqlite3_reset(). Unbound wildcards are interpreted +// as NULL. +// + +function SQLite3_BindBlob(hStmt: TSqliteStmt; ParamNum: integer; + ptrData: pointer; numBytes: integer; ptrDestructor: pointer): integer; +cdecl; external 'sqlite3.dll' name 'sqlite3_bind_blob'; + +function SQLiteFieldType(SQLiteFieldTypeCode: Integer): AnsiString; +function SQLiteErrorStr(SQLiteErrorCode: Integer): AnsiString; + +implementation + +uses + SysUtils; + +function SQLiteFieldType(SQLiteFieldTypeCode: Integer): AnsiString; +begin + case SQLiteFieldTypeCode of + SQLITE_INTEGER: Result := 'Integer'; + SQLITE_FLOAT: Result := 'Float'; + SQLITE_TEXT: Result := 'Text'; + SQLITE_BLOB: Result := 'Blob'; + SQLITE_NULL: Result := 'Null'; + else + Result := 'Unknown SQLite Field Type Code "' + IntToStr(SQLiteFieldTypeCode) + '"'; + end; +end; + +function SQLiteErrorStr(SQLiteErrorCode: Integer): AnsiString; +begin + case SQLiteErrorCode of + SQLITE_OK: Result := 'Successful result'; + SQLITE_ERROR: Result := 'SQL error or missing database'; + SQLITE_INTERNAL: Result := 'An internal logic error in SQLite'; + SQLITE_PERM: Result := 'Access permission denied'; + SQLITE_ABORT: Result := 'Callback routine requested an abort'; + SQLITE_BUSY: Result := 'The database file is locked'; + SQLITE_LOCKED: Result := 'A table in the database is locked'; + SQLITE_NOMEM: Result := 'A malloc() failed'; + SQLITE_READONLY: Result := 'Attempt to write a readonly database'; + SQLITE_INTERRUPT: Result := 'Operation terminated by sqlite3_interrupt()'; + SQLITE_IOERR: Result := 'Some kind of disk I/O error occurred'; + SQLITE_CORRUPT: Result := 'The database disk image is malformed'; + SQLITE_NOTFOUND: Result := '(Internal Only) Table or record not found'; + SQLITE_FULL: Result := 'Insertion failed because database is full'; + SQLITE_CANTOPEN: Result := 'Unable to open the database file'; + SQLITE_PROTOCOL: Result := 'Database lock protocol error'; + SQLITE_EMPTY: Result := 'Database is empty'; + SQLITE_SCHEMA: Result := 'The database schema changed'; + SQLITE_TOOBIG: Result := 'Too much data for one row of a table'; + SQLITE_CONSTRAINT: Result := 'Abort due to contraint violation'; + SQLITE_MISMATCH: Result := 'Data type mismatch'; + SQLITE_MISUSE: Result := 'Library used incorrectly'; + SQLITE_NOLFS: Result := 'Uses OS features not supported on host'; + SQLITE_AUTH: Result := 'Authorization denied'; + SQLITE_FORMAT: Result := 'Auxiliary database format error'; + SQLITE_RANGE: Result := '2nd parameter to sqlite3_bind out of range'; + SQLITE_NOTADB: Result := 'File opened that is not a database file'; + SQLITE_ROW: Result := 'sqlite3_step() has another row ready'; + SQLITE_DONE: Result := 'sqlite3_step() has finished executing'; + else + Result := 'Unknown SQLite Error Code "' + IntToStr(SQLiteErrorCode) + '"'; + end; +end; + +function ColValueToStr(Value: PChar): AnsiString; +begin + if (Value = nil) then + Result := 'NULL' + else + Result := Value; +end; + + +end. + diff --git a/Game/Code/lib/SQLite/SQLiteSpy.db3 b/Game/Code/lib/SQLite/SQLiteSpy.db3 new file mode 100644 index 00000000..39ec37b5 Binary files /dev/null and b/Game/Code/lib/SQLite/SQLiteSpy.db3 differ diff --git a/Game/Code/lib/SQLite/SQLiteSpy.exe b/Game/Code/lib/SQLite/SQLiteSpy.exe new file mode 100644 index 00000000..04cf3c80 Binary files /dev/null and b/Game/Code/lib/SQLite/SQLiteSpy.exe differ diff --git a/Game/Code/lib/SQLite/SQLiteTable3.dcu b/Game/Code/lib/SQLite/SQLiteTable3.dcu new file mode 100644 index 00000000..59ec7037 Binary files /dev/null and b/Game/Code/lib/SQLite/SQLiteTable3.dcu differ diff --git a/Game/Code/lib/SQLite/SQLiteTable3.pas b/Game/Code/lib/SQLite/SQLiteTable3.pas new file mode 100644 index 00000000..19346915 --- /dev/null +++ b/Game/Code/lib/SQLite/SQLiteTable3.pas @@ -0,0 +1,761 @@ +unit SQLiteTable3; + +{ + Simple classes for using SQLite's exec and get_table. + + TSQLiteDatabase wraps the calls to open and close an SQLite database. + It also wraps SQLite_exec for queries that do not return a result set + + TSQLiteTable wraps sqlite_get_table. + It allows accessing fields by name as well as index and can step through a + result set with the Next procedure. + + Adapted by Tim Anderson (tim@itwriting.com) + Originally created by Pablo Pissanetzky (pablo@myhtpc.net) + Modified and enhanced by Lukas Gebauer +} + +interface + +uses + Windows, SQLite3, Classes, SysUtils; + +const + + dtInt = 1; + dtNumeric = 2; + dtStr = 3; + dtBlob = 4; + dtNull = 5; + +type + + ESQLiteException = class(Exception) + end; + + TSQLiteTable = class; + + TSQLiteDatabase = class + private + fDB: TSQLiteDB; + fInTrans: boolean; + procedure RaiseError(s: string; SQL: string); + public + constructor Create(const FileName: string); + destructor Destroy; override; + function GetTable(const SQL: string): TSQLiteTable; + procedure ExecSQL(const SQL: string); + function GetTableValue(const SQL: string): int64; + function GetTableString(const SQL: string): string; + procedure UpdateBlob(const SQL: string; BlobData: TStream); + procedure BeginTransaction; + procedure Commit; + procedure Rollback; + function TableExists(TableName: string): boolean; + function GetLastInsertRowID: int64; + procedure SetTimeout(Value: integer); + function version: string; + published + property isTransactionOpen: boolean read fInTrans; + end; + + TSQLiteTable = class + private + fResults: TList; + fRowCount: cardinal; + fColCount: cardinal; + fCols: TStringList; + fColTypes: TList; + fRow: cardinal; + function GetFields(I: cardinal): string; + function GetEOF: boolean; + function GetBOF: boolean; + function GetColumns(I: integer): string; + function GetFieldByName(FieldName: string): string; + function GetFieldIndex(FieldName: string): integer; + function GetCount: integer; + function GetCountResult: integer; + public + constructor Create(DB: TSQLiteDatabase; const SQL: string); + destructor Destroy; override; + function FieldAsInteger(I: cardinal): int64; + function FieldAsBlob(I: cardinal): TMemoryStream; + function FieldAsBlobText(I: cardinal): string; + function FieldIsNull(I: cardinal): boolean; + function FieldAsString(I: cardinal): string; + function FieldAsDouble(I: cardinal): double; + function Next: boolean; + function Previous: boolean; + property EOF: boolean read GetEOF; + property BOF: boolean read GetBOF; + property Fields[I: cardinal]: string read GetFields; + property FieldByName[FieldName: string]: string read GetFieldByName; + property FieldIndex[FieldName: string]: integer read GetFieldIndex; + property Columns[I: integer]: string read GetColumns; + property ColCount: cardinal read fColCount; + property RowCount: cardinal read fRowCount; + property Row: cardinal read fRow; + function MoveFirst: boolean; + function MoveLast: boolean; + property Count: integer read GetCount; + // The property CountResult is used when you execute count(*) queries. + // It returns 0 if the result set is empty or the value of the + // first field as an integer. + property CountResult: integer read GetCountResult; + end; + +procedure DisposePointer(ptr: pointer); cdecl; + + +implementation + +procedure DisposePointer(ptr: pointer); cdecl; +begin + if assigned(ptr) then + freemem(ptr); +end; + +//------------------------------------------------------------------------------ +// TSQLiteDatabase +//------------------------------------------------------------------------------ + +constructor TSQLiteDatabase.Create(const FileName: string); +var + Msg: pchar; + iResult: integer; +begin + inherited Create; + + self.fInTrans := False; + + Msg := nil; + try + iResult := SQLite3_Open(PChar(FileName), Fdb); + + if iResult <> SQLITE_OK then + if Assigned(Fdb) then + begin + Msg := Sqlite3_ErrMsg(Fdb); + raise ESqliteException.CreateFmt('Failed to open database "%s" : %s', + [FileName, Msg]); + end + else + raise ESqliteException.CreateFmt('Failed to open database "%s" : unknown error', + [FileName]); + + //set a few configs + self.ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;'); +// self.ExecSQL('PRAGMA full_column_names = 1;'); + self.ExecSQL('PRAGMA temp_store = MEMORY;'); + + finally + if Assigned(Msg) then + SQLite3_Free(Msg); + end; + +end; + + +//.............................................................................. + +destructor TSQLiteDatabase.Destroy; +begin + + if self.fInTrans then + self.ExecSQL('ROLLBACK;'); //assume rollback + + if Assigned(fDB) then + SQLite3_Close(fDB); + + inherited; +end; + +function TSQLiteDatabase.GetLastInsertRowID: int64; +begin + Result := Sqlite3_LastInsertRowID(self.fDB); +end; + +//.............................................................................. + +procedure TSQLiteDatabase.RaiseError(s: string; SQL: string); +//look up last error and raise an exception with an appropriate message +var + Msg: PChar; +begin + + Msg := nil; + + if sqlite3_errcode(self.fDB) <> SQLITE_OK then + Msg := sqlite3_errmsg(self.fDB); + + if Msg <> nil then + raise ESqliteException.CreateFmt(s + ' "%s" : %s', [SQL, Msg]) + else + raise ESqliteException.CreateFmt(s, [SQL, 'No message']); + +end; + +procedure TSQLiteDatabase.ExecSQL(const SQL: string); +var + Stmt: TSQLiteStmt; + NextSQLStatement: Pchar; + iStepResult: integer; +begin + try + + if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> + SQLITE_OK then + RaiseError('Error executing SQL', SQL); + + if (Stmt = nil) then + RaiseError('Could not prepare SQL statement', SQL); + + iStepResult := Sqlite3_step(Stmt); + + if (iStepResult <> SQLITE_DONE) then + RaiseError('Error executing SQL statement', SQL); + + finally + + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + + end; +end; + +procedure TSQLiteDatabase.UpdateBlob(const SQL: string; BlobData: TStream); +var + iSize: integer; + ptr: pointer; + Stmt: TSQLiteStmt; + Msg: Pchar; + NextSQLStatement: Pchar; + iStepResult: integer; + iBindResult: integer; +begin + //expects SQL of the form 'UPDATE MYTABLE SET MYFIELD = ? WHERE MYKEY = 1' + + if pos('?', SQL) = 0 then + RaiseError('SQL must include a ? parameter', SQL); + + Msg := nil; + try + + if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> + SQLITE_OK then + RaiseError('Could not prepare SQL statement', SQL); + + if (Stmt = nil) then + RaiseError('Could not prepare SQL statement', SQL); + + //now bind the blob data + iSize := BlobData.size; + + GetMem(ptr, iSize); + + if (ptr = nil) then + raise ESqliteException.CreateFmt('Error getting memory to save blob', + [SQL, 'Error']); + + BlobData.position := 0; + BlobData.Read(ptr^, iSize); + + iBindResult := SQLite3_BindBlob(stmt, 1, ptr, iSize, @DisposePointer); + + if iBindResult <> SQLITE_OK then + RaiseError('Error binding blob to database', SQL); + + iStepResult := Sqlite3_step(Stmt); + + if (iStepResult <> SQLITE_DONE) then + RaiseError('Error executing SQL statement', SQL); + + finally + + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + + if Assigned(Msg) then + SQLite3_Free(Msg); + end; + +end; + +//.............................................................................. + +function TSQLiteDatabase.GetTable(const SQL: string): TSQLiteTable; +begin + Result := TSQLiteTable.Create(Self, SQL); +end; + +function TSQLiteDatabase.GetTableValue(const SQL: string): int64; +var + Table: TSQLiteTable; +begin + Table := self.GetTable(SQL); + try + Result := Table.FieldAsInteger(0); + finally + Table.Free; + end; +end; + +function TSQLiteDatabase.GetTableString(const SQL: string): string; +var + Table: TSQLiteTable; +begin + Table := self.GetTable(SQL); + try + Result := Table.FieldAsString(0); + finally + Table.Free; + end; +end; + + +procedure TSQLiteDatabase.BeginTransaction; +begin + if not self.fInTrans then + begin + self.ExecSQL('BEGIN TRANSACTION;'); + self.fInTrans := True; + end + else + raise ESqliteException.Create('Transaction already open'); +end; + +procedure TSQLiteDatabase.Commit; +begin + self.ExecSQL('COMMIT;'); + self.fInTrans := False; +end; + +procedure TSQLiteDatabase.Rollback; +begin + self.ExecSQL('ROLLBACK;'); + self.fInTrans := False; +end; + +function TSQLiteDatabase.TableExists(TableName: string): boolean; +var + sql: string; + ds: TSqliteTable; +begin + //returns true if table exists in the database + sql := 'select [sql] from sqlite_master where [type] = ''table'' and lower(name) = ''' + + lowercase(TableName) + ''' '; + ds := self.GetTable(sql); + try + Result := (ds.Count > 0); + finally + ds.Free; + end; +end; + +procedure TSQLiteDatabase.SetTimeout(Value: integer); +begin + SQLite3_BusyTimeout(self.fDB, Value); +end; + +function TSQLiteDatabase.version: string; +begin + Result := SQLite3_Version; +end; + + +//------------------------------------------------------------------------------ +// TSQLiteTable +//------------------------------------------------------------------------------ + +constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: string); +var + Stmt: TSQLiteStmt; + NextSQLStatement: Pchar; + iStepResult: integer; + ptr: pointer; + iNumBytes: integer; + thisBlobValue: TMemoryStream; + thisStringValue: pstring; + thisDoubleValue: pDouble; + thisIntValue: pInt64; + thisColType: pInteger; + i: integer; + DeclaredColType: Pchar; + ActualColType: integer; + ptrValue: Pchar; +begin + try + self.fRowCount := 0; + self.fColCount := 0; + //if there are several SQL statements in SQL, NextSQLStatment points to the + //beginning of the next one. Prepare only prepares the first SQL statement. + if Sqlite3_Prepare(DB.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then + DB.RaiseError('Error executing SQL', SQL); + if (Stmt = nil) then + DB.RaiseError('Could not prepare SQL statement', SQL); + iStepResult := Sqlite3_step(Stmt); + while (iStepResult <> SQLITE_DONE) do + begin + case iStepResult of + SQLITE_ROW: + begin + Inc(fRowCount); + if (fRowCount = 1) then + begin + //get data types + fCols := TStringList.Create; + fColTypes := TList.Create; + fColCount := SQLite3_ColumnCount(stmt); + for i := 0 to Pred(fColCount) do + fCols.Add(AnsiUpperCase(Sqlite3_ColumnName(stmt, i))); + for i := 0 to Pred(fColCount) do + begin + new(thisColType); + DeclaredColType := Sqlite3_ColumnDeclType(stmt, i); + if DeclaredColType = nil then + thisColType^ := Sqlite3_ColumnType(stmt, i) //use the actual column type instead + //seems to be needed for last_insert_rowid + else + if (DeclaredColType = 'INTEGER') or (DeclaredColType = 'BOOLEAN') then + thisColType^ := dtInt + else + if (DeclaredColType = 'NUMERIC') or + (DeclaredColType = 'FLOAT') or + (DeclaredColType = 'DOUBLE') or + (DeclaredColType = 'REAL') then + thisColType^ := dtNumeric + else + if DeclaredColType = 'BLOB' then + thisColType^ := dtBlob + else + thisColType^ := dtStr; + fColTypes.Add(thiscoltype); + end; + fResults := TList.Create; + end; + + //get column values + for i := 0 to Pred(ColCount) do + begin + ActualColType := Sqlite3_ColumnType(stmt, i); + if (ActualColType = SQLITE_NULL) then + fResults.Add(nil) + else + if pInteger(fColTypes[i])^ = dtInt then + begin + new(thisintvalue); + thisintvalue^ := Sqlite3_ColumnInt64(stmt, i); + fResults.Add(thisintvalue); + end + else + if pInteger(fColTypes[i])^ = dtNumeric then + begin + new(thisdoublevalue); + thisdoublevalue^ := Sqlite3_ColumnDouble(stmt, i); + fResults.Add(thisdoublevalue); + end + else + if pInteger(fColTypes[i])^ = dtBlob then + begin + iNumBytes := Sqlite3_ColumnBytes(stmt, i); + if iNumBytes = 0 then + thisblobvalue := nil + else + begin + thisblobvalue := TMemoryStream.Create; + thisblobvalue.position := 0; + ptr := Sqlite3_ColumnBlob(stmt, i); + thisblobvalue.writebuffer(ptr^, iNumBytes); + end; + fResults.Add(thisblobvalue); + end + else + begin + new(thisstringvalue); + ptrValue := Sqlite3_ColumnText(stmt, i); + setstring(thisstringvalue^, ptrvalue, strlen(ptrvalue)); + fResults.Add(thisstringvalue); + end; + end; + end; + SQLITE_BUSY: + raise ESqliteException.CreateFmt('Could not prepare SQL statement', + [SQL, 'SQLite is Busy']); + else + DB.RaiseError('Could not retrieve data', SQL); + end; + iStepResult := Sqlite3_step(Stmt); + end; + fRow := 0; + finally + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + end; +end; + +//.............................................................................. + +destructor TSQLiteTable.Destroy; +var + i: cardinal; + iColNo: integer; +begin + if Assigned(fResults) then + begin + for i := 0 to fResults.Count - 1 do + begin + //check for blob type + iColNo := (i mod fColCount); + case pInteger(self.fColTypes[iColNo])^ of + dtBlob: + TMemoryStream(fResults[i]).Free; + dtStr: + if fResults[i] <> nil then + begin + setstring(string(fResults[i]^), nil, 0); + dispose(fResults[i]); + end; + else + dispose(fResults[i]); + end; + end; + fResults.Free; + end; + if Assigned(fCols) then + fCols.Free; + if Assigned(fColTypes) then + for i := 0 to fColTypes.Count - 1 do + dispose(fColTypes[i]); + fColTypes.Free; + inherited; +end; + +//.............................................................................. + +function TSQLiteTable.GetColumns(I: integer): string; +begin + Result := fCols[I]; +end; + +//.............................................................................. + +function TSQLiteTable.GetCountResult: integer; +begin + if not EOF then + Result := StrToInt(Fields[0]) + else + Result := 0; +end; + +function TSQLiteTable.GetCount: integer; +begin + Result := FRowCount; +end; + +//.............................................................................. + +function TSQLiteTable.GetEOF: boolean; +begin + Result := fRow >= fRowCount; +end; + +function TSQLiteTable.GetBOF: boolean; +begin + Result := fRow <= 0; +end; + +//.............................................................................. + +function TSQLiteTable.GetFieldByName(FieldName: string): string; +begin + Result := GetFields(self.GetFieldIndex(FieldName)); +end; + +function TSQLiteTable.GetFieldIndex(FieldName: string): integer; +begin + + if (fCols = nil) then + begin + raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset'); + exit; + end; + + if (fCols.count = 0) then + begin + raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset'); + exit; + end; + + Result := fCols.IndexOf(AnsiUpperCase(FieldName)); + + if (result < 0) then + begin raise ESqliteException.Create('Field not found in dataset: ' + fieldname) end; + +end; + +//.............................................................................. + +function TSQLiteTable.GetFields(I: cardinal): string; +var + thisvalue: pstring; + thistype: integer; +begin + Result := ''; + if EOF then + raise ESqliteException.Create('Table is at End of File'); + //integer types are not stored in the resultset + //as strings, so they should be retrieved using the type-specific + //methods + thistype := pInteger(self.fColTypes[I])^; + + case thistype of + dtStr: + begin + thisvalue := self.fResults[(self.frow * self.fColCount) + I]; + if (thisvalue <> nil) then + Result := thisvalue^ + else + Result := ''; + end; + dtInt: + Result := IntToStr(self.FieldAsInteger(I)); + dtNumeric: + Result := FloatToStr(self.FieldAsDouble(I)); + dtBlob: + Result := self.FieldAsBlobText(I); + else + Result := ''; + end; +end; + +function TSqliteTable.FieldAsBlob(I: cardinal): TMemoryStream; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := nil + else + if pInteger(self.fColTypes[I])^ = dtBlob then + Result := TMemoryStream(self.fResults[(self.frow * self.fColCount) + I]) + else + raise ESqliteException.Create('Not a Blob field'); +end; + +function TSqliteTable.FieldAsBlobText(I: cardinal): string; +var + MemStream: TMemoryStream; + Buffer: PChar; +begin + Result := ''; + MemStream := self.FieldAsBlob(I); + if MemStream <> nil then + if MemStream.Size > 0 then + begin + MemStream.position := 0; + Buffer := stralloc(MemStream.Size + 1); + MemStream.readbuffer(Buffer[0], MemStream.Size); + (Buffer + MemStream.Size)^ := chr(0); + SetString(Result, Buffer, MemStream.size); + strdispose(Buffer); + end; +end; + + +function TSqliteTable.FieldAsInteger(I: cardinal): int64; +begin + if EOF then + //raise ESqliteException.Create('Table is at End of File'); + Result := 0 + else if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := 0 + else + if pInteger(self.fColTypes[I])^ = dtInt then + Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^ + else + if pInteger(self.fColTypes[I])^ = dtNumeric then + Result := trunc(strtofloat(pString(self.fResults[(self.frow * self.fColCount) + I])^)) + else + raise ESqliteException.Create('Not an integer or numeric field'); +end; + +function TSqliteTable.FieldAsDouble(I: cardinal): double; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := 0 + else + if pInteger(self.fColTypes[I])^ = dtInt then + Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^ + else + if pInteger(self.fColTypes[I])^ = dtNumeric then + Result := pDouble(self.fResults[(self.frow * self.fColCount) + I])^ + else + raise ESqliteException.Create('Not an integer or numeric field'); +end; + +function TSqliteTable.FieldAsString(I: cardinal): string; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := '' + else + Result := self.GetFields(I); +end; + +function TSqliteTable.FieldIsNull(I: cardinal): boolean; +var + thisvalue: pointer; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + thisvalue := self.fResults[(self.frow * self.fColCount) + I]; + Result := (thisvalue = nil); +end; + +//.............................................................................. + +function TSQLiteTable.Next: boolean; +begin + Result := False; + if not EOF then + begin + Inc(fRow); + Result := True; + end; +end; + +function TSQLiteTable.Previous: boolean; +begin + Result := False; + if not BOF then + begin + Dec(fRow); + Result := True; + end; +end; + +function TSQLiteTable.MoveFirst: boolean; +begin + Result := False; + if self.fRowCount > 0 then + begin + fRow := 0; + Result := True; + end; +end; + +function TSQLiteTable.MoveLast: boolean; +begin + Result := False; + if self.fRowCount > 0 then + begin + fRow := fRowCount - 1; + Result := True; + end; +end; + + +end. + diff --git a/Game/Code/lib/SQLite/SQLiteTable3.~pas b/Game/Code/lib/SQLite/SQLiteTable3.~pas new file mode 100644 index 00000000..01be1e5b --- /dev/null +++ b/Game/Code/lib/SQLite/SQLiteTable3.~pas @@ -0,0 +1,761 @@ +unit SQLiteTable3; + +{ + Simple classes for using SQLite's exec and get_table. + + TSQLiteDatabase wraps the calls to open and close an SQLite database. + It also wraps SQLite_exec for queries that do not return a result set + + TSQLiteTable wraps sqlite_get_table. + It allows accessing fields by name as well as index and can step through a + result set with the Next procedure. + + Adapted by Tim Anderson (tim@itwriting.com) + Originally created by Pablo Pissanetzky (pablo@myhtpc.net) + Modified and enhanced by Lukas Gebauer +} + +interface + +uses + Windows, SQLite3, Classes, SysUtils; + +const + + dtInt = 1; + dtNumeric = 2; + dtStr = 3; + dtBlob = 4; + dtNull = 5; + +type + + ESQLiteException = class(Exception) + end; + + TSQLiteTable = class; + + TSQLiteDatabase = class + private + fDB: TSQLiteDB; + fInTrans: boolean; + procedure RaiseError(s: string; SQL: string); + public + constructor Create(const FileName: string); + destructor Destroy; override; + function GetTable(const SQL: string): TSQLiteTable; + procedure ExecSQL(const SQL: string); + function GetTableValue(const SQL: string): int64; + function GetTableString(const SQL: string): string; + procedure UpdateBlob(const SQL: string; BlobData: TStream); + procedure BeginTransaction; + procedure Commit; + procedure Rollback; + function TableExists(TableName: string): boolean; + function GetLastInsertRowID: int64; + procedure SetTimeout(Value: integer); + function version: string; + published + property isTransactionOpen: boolean read fInTrans; + end; + + TSQLiteTable = class + private + fResults: TList; + fRowCount: cardinal; + fColCount: cardinal; + fCols: TStringList; + fColTypes: TList; + fRow: cardinal; + function GetFields(I: cardinal): string; + function GetEOF: boolean; + function GetBOF: boolean; + function GetColumns(I: integer): string; + function GetFieldByName(FieldName: string): string; + function GetFieldIndex(FieldName: string): integer; + function GetCount: integer; + function GetCountResult: integer; + public + constructor Create(DB: TSQLiteDatabase; const SQL: string); + destructor Destroy; override; + function FieldAsInteger(I: cardinal): int64; + function FieldAsBlob(I: cardinal): TMemoryStream; + function FieldAsBlobText(I: cardinal): string; + function FieldIsNull(I: cardinal): boolean; + function FieldAsString(I: cardinal): string; + function FieldAsDouble(I: cardinal): double; + function Next: boolean; + function Previous: boolean; + property EOF: boolean read GetEOF; + property BOF: boolean read GetBOF; + property Fields[I: cardinal]: string read GetFields; + property FieldByName[FieldName: string]: string read GetFieldByName; + property FieldIndex[FieldName: string]: integer read GetFieldIndex; + property Columns[I: integer]: string read GetColumns; + property ColCount: cardinal read fColCount; + property RowCount: cardinal read fRowCount; + property Row: cardinal read fRow; + function MoveFirst: boolean; + function MoveLast: boolean; + property Count: integer read GetCount; + // The property CountResult is used when you execute count(*) queries. + // It returns 0 if the result set is empty or the value of the + // first field as an integer. + property CountResult: integer read GetCountResult; + end; + +procedure DisposePointer(ptr: pointer); cdecl; + + +implementation + +procedure DisposePointer(ptr: pointer); cdecl; +begin + if assigned(ptr) then + freemem(ptr); +end; + +//------------------------------------------------------------------------------ +// TSQLiteDatabase +//------------------------------------------------------------------------------ + +constructor TSQLiteDatabase.Create(const FileName: string); +var + Msg: pchar; + iResult: integer; +begin + inherited Create; + + self.fInTrans := False; + + Msg := nil; + try + iResult := SQLite3_Open(PChar(FileName), Fdb); + + if iResult <> SQLITE_OK then + if Assigned(Fdb) then + begin + Msg := Sqlite3_ErrMsg(Fdb); + raise ESqliteException.CreateFmt('Failed to open database "%s" : %s', + [FileName, Msg]); + end + else + raise ESqliteException.CreateFmt('Failed to open database "%s" : unknown error', + [FileName]); + + //set a few configs + self.ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;'); +// self.ExecSQL('PRAGMA full_column_names = 1;'); + self.ExecSQL('PRAGMA temp_store = MEMORY;'); + + finally + if Assigned(Msg) then + SQLite3_Free(Msg); + end; + +end; + + +//.............................................................................. + +destructor TSQLiteDatabase.Destroy; +begin + + if self.fInTrans then + self.ExecSQL('ROLLBACK;'); //assume rollback + + if Assigned(fDB) then + SQLite3_Close(fDB); + + inherited; +end; + +function TSQLiteDatabase.GetLastInsertRowID: int64; +begin + Result := Sqlite3_LastInsertRowID(self.fDB); +end; + +//.............................................................................. + +procedure TSQLiteDatabase.RaiseError(s: string; SQL: string); +//look up last error and raise an exception with an appropriate message +var + Msg: PChar; +begin + + Msg := nil; + + if sqlite3_errcode(self.fDB) <> SQLITE_OK then + Msg := sqlite3_errmsg(self.fDB); + + if Msg <> nil then + raise ESqliteException.CreateFmt(s + ' "%s" : %s', [SQL, Msg]) + else + raise ESqliteException.CreateFmt(s, [SQL, 'No message']); + +end; + +procedure TSQLiteDatabase.ExecSQL(const SQL: string); +var + Stmt: TSQLiteStmt; + NextSQLStatement: Pchar; + iStepResult: integer; +begin + try + + if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> + SQLITE_OK then + RaiseError('Error executing SQL', SQL); + + if (Stmt = nil) then + RaiseError('Could not prepare SQL statement', SQL); + + iStepResult := Sqlite3_step(Stmt); + + if (iStepResult <> SQLITE_DONE) then + RaiseError('Error executing SQL statement', SQL); + + finally + + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + + end; +end; + +procedure TSQLiteDatabase.UpdateBlob(const SQL: string; BlobData: TStream); +var + iSize: integer; + ptr: pointer; + Stmt: TSQLiteStmt; + Msg: Pchar; + NextSQLStatement: Pchar; + iStepResult: integer; + iBindResult: integer; +begin + //expects SQL of the form 'UPDATE MYTABLE SET MYFIELD = ? WHERE MYKEY = 1' + + if pos('?', SQL) = 0 then + RaiseError('SQL must include a ? parameter', SQL); + + Msg := nil; + try + + if Sqlite3_Prepare(self.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> + SQLITE_OK then + RaiseError('Could not prepare SQL statement', SQL); + + if (Stmt = nil) then + RaiseError('Could not prepare SQL statement', SQL); + + //now bind the blob data + iSize := BlobData.size; + + GetMem(ptr, iSize); + + if (ptr = nil) then + raise ESqliteException.CreateFmt('Error getting memory to save blob', + [SQL, 'Error']); + + BlobData.position := 0; + BlobData.Read(ptr^, iSize); + + iBindResult := SQLite3_BindBlob(stmt, 1, ptr, iSize, @DisposePointer); + + if iBindResult <> SQLITE_OK then + RaiseError('Error binding blob to database', SQL); + + iStepResult := Sqlite3_step(Stmt); + + if (iStepResult <> SQLITE_DONE) then + RaiseError('Error executing SQL statement', SQL); + + finally + + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + + if Assigned(Msg) then + SQLite3_Free(Msg); + end; + +end; + +//.............................................................................. + +function TSQLiteDatabase.GetTable(const SQL: string): TSQLiteTable; +begin + Result := TSQLiteTable.Create(Self, SQL); +end; + +function TSQLiteDatabase.GetTableValue(const SQL: string): int64; +var + Table: TSQLiteTable; +begin + Table := self.GetTable(SQL); + try + Result := Table.FieldAsInteger(0); + finally + Table.Free; + end; +end; + +function TSQLiteDatabase.GetTableString(const SQL: string): string; +var + Table: TSQLiteTable; +begin + Table := self.GetTable(SQL); + try + Result := Table.FieldAsString(0); + finally + Table.Free; + end; +end; + + +procedure TSQLiteDatabase.BeginTransaction; +begin + if not self.fInTrans then + begin + self.ExecSQL('BEGIN TRANSACTION;'); + self.fInTrans := True; + end + else + raise ESqliteException.Create('Transaction already open'); +end; + +procedure TSQLiteDatabase.Commit; +begin + self.ExecSQL('COMMIT;'); + self.fInTrans := False; +end; + +procedure TSQLiteDatabase.Rollback; +begin + self.ExecSQL('ROLLBACK;'); + self.fInTrans := False; +end; + +function TSQLiteDatabase.TableExists(TableName: string): boolean; +var + sql: string; + ds: TSqliteTable; +begin + //returns true if table exists in the database + sql := 'select [sql] from sqlite_master where [type] = ''table'' and lower(name) = ''' + + lowercase(TableName) + ''' '; + ds := self.GetTable(sql); + try + Result := (ds.Count > 0); + finally + ds.Free; + end; +end; + +procedure TSQLiteDatabase.SetTimeout(Value: integer); +begin + SQLite3_BusyTimeout(self.fDB, Value); +end; + +function TSQLiteDatabase.version: string; +begin + Result := SQLite3_Version; +end; + + +//------------------------------------------------------------------------------ +// TSQLiteTable +//------------------------------------------------------------------------------ + +constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: string); +var + Stmt: TSQLiteStmt; + NextSQLStatement: Pchar; + iStepResult: integer; + ptr: pointer; + iNumBytes: integer; + thisBlobValue: TMemoryStream; + thisStringValue: pstring; + thisDoubleValue: pDouble; + thisIntValue: pInt64; + thisColType: pInteger; + i: integer; + DeclaredColType: Pchar; + ActualColType: integer; + ptrValue: Pchar; +begin + try + self.fRowCount := 0; + self.fColCount := 0; + //if there are several SQL statements in SQL, NextSQLStatment points to the + //beginning of the next one. Prepare only prepares the first SQL statement. + if Sqlite3_Prepare(DB.fDB, PChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then + DB.RaiseError('Error executing SQL', SQL); + if (Stmt = nil) then + DB.RaiseError('Could not prepare SQL statement', SQL); + iStepResult := Sqlite3_step(Stmt); + while (iStepResult <> SQLITE_DONE) do + begin + case iStepResult of + SQLITE_ROW: + begin + Inc(fRowCount); + if (fRowCount = 1) then + begin + //get data types + fCols := TStringList.Create; + fColTypes := TList.Create; + fColCount := SQLite3_ColumnCount(stmt); + for i := 0 to Pred(fColCount) do + fCols.Add(AnsiUpperCase(Sqlite3_ColumnName(stmt, i))); + for i := 0 to Pred(fColCount) do + begin + new(thisColType); + DeclaredColType := Sqlite3_ColumnDeclType(stmt, i); + if DeclaredColType = nil then + thisColType^ := Sqlite3_ColumnType(stmt, i) //use the actual column type instead + //seems to be needed for last_insert_rowid + else + if (DeclaredColType = 'INTEGER') or (DeclaredColType = 'BOOLEAN') then + thisColType^ := dtInt + else + if (DeclaredColType = 'NUMERIC') or + (DeclaredColType = 'FLOAT') or + (DeclaredColType = 'DOUBLE') or + (DeclaredColType = 'REAL') then + thisColType^ := dtNumeric + else + if DeclaredColType = 'BLOB' then + thisColType^ := dtBlob + else + thisColType^ := dtStr; + fColTypes.Add(thiscoltype); + end; + fResults := TList.Create; + end; + + //get column values + for i := 0 to Pred(ColCount) do + begin + ActualColType := Sqlite3_ColumnType(stmt, i); + if (ActualColType = SQLITE_NULL) then + fResults.Add(nil) + else + if pInteger(fColTypes[i])^ = dtInt then + begin + new(thisintvalue); + thisintvalue^ := Sqlite3_ColumnInt64(stmt, i); + fResults.Add(thisintvalue); + end + else + if pInteger(fColTypes[i])^ = dtNumeric then + begin + new(thisdoublevalue); + thisdoublevalue^ := Sqlite3_ColumnDouble(stmt, i); + fResults.Add(thisdoublevalue); + end + else + if pInteger(fColTypes[i])^ = dtBlob then + begin + iNumBytes := Sqlite3_ColumnBytes(stmt, i); + if iNumBytes = 0 then + thisblobvalue := nil + else + begin + thisblobvalue := TMemoryStream.Create; + thisblobvalue.position := 0; + ptr := Sqlite3_ColumnBlob(stmt, i); + thisblobvalue.writebuffer(ptr^, iNumBytes); + end; + fResults.Add(thisblobvalue); + end + else + begin + new(thisstringvalue); + ptrValue := Sqlite3_ColumnText(stmt, i); + setstring(thisstringvalue^, ptrvalue, strlen(ptrvalue)); + fResults.Add(thisstringvalue); + end; + end; + end; + SQLITE_BUSY: + raise ESqliteException.CreateFmt('Could not prepare SQL statement', + [SQL, 'SQLite is Busy']); + else + DB.RaiseError('Could not retrieve data', SQL); + end; + iStepResult := Sqlite3_step(Stmt); + end; + fRow := 0; + finally + if Assigned(Stmt) then + Sqlite3_Finalize(stmt); + end; +end; + +//.............................................................................. + +destructor TSQLiteTable.Destroy; +var + i: cardinal; + iColNo: integer; +begin + if Assigned(fResults) then + begin + for i := 0 to fResults.Count - 1 do + begin + //check for blob type + iColNo := (i mod fColCount); + case pInteger(self.fColTypes[iColNo])^ of + dtBlob: + TMemoryStream(fResults[i]).Free; + dtStr: + if fResults[i] <> nil then + begin + setstring(string(fResults[i]^), nil, 0); + dispose(fResults[i]); + end; + else + dispose(fResults[i]); + end; + end; + fResults.Free; + end; + if Assigned(fCols) then + fCols.Free; + if Assigned(fColTypes) then + for i := 0 to fColTypes.Count - 1 do + dispose(fColTypes[i]); + fColTypes.Free; + inherited; +end; + +//.............................................................................. + +function TSQLiteTable.GetColumns(I: integer): string; +begin + Result := fCols[I]; +end; + +//.............................................................................. + +function TSQLiteTable.GetCountResult: integer; +begin + if not EOF then + Result := StrToInt(Fields[0]) + else + Result := 0; +end; + +function TSQLiteTable.GetCount: integer; +begin + Result := FRowCount; +end; + +//.............................................................................. + +function TSQLiteTable.GetEOF: boolean; +begin + Result := fRow >= fRowCount; +end; + +function TSQLiteTable.GetBOF: boolean; +begin + Result := fRow <= 0; +end; + +//.............................................................................. + +function TSQLiteTable.GetFieldByName(FieldName: string): string; +begin + Result := GetFields(self.GetFieldIndex(FieldName)); +end; + +function TSQLiteTable.GetFieldIndex(FieldName: string): integer; +begin + + if (fCols = nil) then + begin + raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset'); + exit; + end; + + if (fCols.count = 0) then + begin + raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset'); + exit; + end; + + Result := fCols.IndexOf(AnsiUpperCase(FieldName)); + + if (result < 0) then + begin raise ESqliteException.Create('Field not found in dataset: ' + fieldname) end; + +end; + +//.............................................................................. + +function TSQLiteTable.GetFields(I: cardinal): string; +var + thisvalue: pstring; + thistype: integer; +begin + Result := ''; + if EOF then + raise ESqliteException.Create('Table is at End of File'); + //integer types are not stored in the resultset + //as strings, so they should be retrieved using the type-specific + //methods + thistype := pInteger(self.fColTypes[I])^; + + case thistype of + dtStr: + begin + thisvalue := self.fResults[(self.frow * self.fColCount) + I]; + if (thisvalue <> nil) then + Result := thisvalue^ + else + Result := ''; + end; + dtInt: + Result := IntToStr(self.FieldAsInteger(I)); + dtNumeric: + Result := FloatToStr(self.FieldAsDouble(I)); + dtBlob: + Result := self.FieldAsBlobText(I); + else + Result := ''; + end; +end; + +function TSqliteTable.FieldAsBlob(I: cardinal): TMemoryStream; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := nil + else + if pInteger(self.fColTypes[I])^ = dtBlob then + Result := TMemoryStream(self.fResults[(self.frow * self.fColCount) + I]) + else + raise ESqliteException.Create('Not a Blob field'); +end; + +function TSqliteTable.FieldAsBlobText(I: cardinal): string; +var + MemStream: TMemoryStream; + Buffer: PChar; +begin + Result := ''; + MemStream := self.FieldAsBlob(I); + if MemStream <> nil then + if MemStream.Size > 0 then + begin + MemStream.position := 0; + Buffer := stralloc(MemStream.Size + 1); + MemStream.readbuffer(Buffer[0], MemStream.Size); + (Buffer + MemStream.Size)^ := chr(0); + SetString(Result, Buffer, MemStream.size); + strdispose(Buffer); + end; +end; + + +function TSqliteTable.FieldAsInteger(I: cardinal): int64; +begin + if EOF then + //raise ESqliteException.Create('Table is at End of File'); + Result := 0; + else if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := 0 + else + if pInteger(self.fColTypes[I])^ = dtInt then + Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^ + else + if pInteger(self.fColTypes[I])^ = dtNumeric then + Result := trunc(strtofloat(pString(self.fResults[(self.frow * self.fColCount) + I])^)) + else + raise ESqliteException.Create('Not an integer or numeric field'); +end; + +function TSqliteTable.FieldAsDouble(I: cardinal): double; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := 0 + else + if pInteger(self.fColTypes[I])^ = dtInt then + Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^ + else + if pInteger(self.fColTypes[I])^ = dtNumeric then + Result := pDouble(self.fResults[(self.frow * self.fColCount) + I])^ + else + raise ESqliteException.Create('Not an integer or numeric field'); +end; + +function TSqliteTable.FieldAsString(I: cardinal): string; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + if (self.fResults[(self.frow * self.fColCount) + I] = nil) then + Result := '' + else + Result := self.GetFields(I); +end; + +function TSqliteTable.FieldIsNull(I: cardinal): boolean; +var + thisvalue: pointer; +begin + if EOF then + raise ESqliteException.Create('Table is at End of File'); + thisvalue := self.fResults[(self.frow * self.fColCount) + I]; + Result := (thisvalue = nil); +end; + +//.............................................................................. + +function TSQLiteTable.Next: boolean; +begin + Result := False; + if not EOF then + begin + Inc(fRow); + Result := True; + end; +end; + +function TSQLiteTable.Previous: boolean; +begin + Result := False; + if not BOF then + begin + Dec(fRow); + Result := True; + end; +end; + +function TSQLiteTable.MoveFirst: boolean; +begin + Result := False; + if self.fRowCount > 0 then + begin + fRow := 0; + Result := True; + end; +end; + +function TSQLiteTable.MoveLast: boolean; +begin + Result := False; + if self.fRowCount > 0 then + begin + fRow := fRowCount - 1; + Result := True; + end; +end; + + +end. + diff --git a/Game/Code/lib/SQLite/example/Sunset.jpg b/Game/Code/lib/SQLite/example/Sunset.jpg new file mode 100644 index 00000000..860f6eec Binary files /dev/null and b/Game/Code/lib/SQLite/example/Sunset.jpg differ diff --git a/Game/Code/lib/SQLite/example/TestSqlite.dpr b/Game/Code/lib/SQLite/example/TestSqlite.dpr new file mode 100644 index 00000000..82f2a468 --- /dev/null +++ b/Game/Code/lib/SQLite/example/TestSqlite.dpr @@ -0,0 +1,15 @@ +program TestSqlite; + +uses + Forms, + uTestSqlite in 'uTestSqlite.pas' {Form1}, + SQLiteTable3 in 'SQLiteTable3.pas', + SQLite3 in 'SQLite3.pas'; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/Game/Code/lib/SQLite/example/TestSqlite.exe b/Game/Code/lib/SQLite/example/TestSqlite.exe new file mode 100644 index 00000000..12f77a3d Binary files /dev/null and b/Game/Code/lib/SQLite/example/TestSqlite.exe differ diff --git a/Game/Code/lib/SQLite/example/TestSqlite.res b/Game/Code/lib/SQLite/example/TestSqlite.res new file mode 100644 index 00000000..4bdd5e2e Binary files /dev/null and b/Game/Code/lib/SQLite/example/TestSqlite.res differ diff --git a/Game/Code/lib/SQLite/example/uTestSqlite.dfm b/Game/Code/lib/SQLite/example/uTestSqlite.dfm new file mode 100644 index 00000000..b77ec2c7 --- /dev/null +++ b/Game/Code/lib/SQLite/example/uTestSqlite.dfm @@ -0,0 +1,110 @@ +object Form1: TForm1 + Left = 242 + Top = 242 + Width = 541 + Height = 308 + Caption = 'Test SQLite 3' + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = False + PixelsPerInch = 96 + TextHeight = 13 + object Label1: TLabel + Left = 24 + Top = 104 + Width = 28 + Height = 13 + Caption = 'Notes' + end + object Label2: TLabel + Left = 24 + Top = 44 + Width = 28 + Height = 13 + Caption = 'Name' + end + object Label3: TLabel + Left = 24 + Top = 72 + Width = 40 + Height = 13 + Caption = 'Number:' + end + object Label4: TLabel + Left = 24 + Top = 12 + Width = 11 + Height = 13 + Caption = 'ID' + end + object Image1: TImage + Left = 272 + Top = 12 + Width = 241 + Height = 165 + Proportional = True + Stretch = True + end + object btnTest: TButton + Left = 24 + Top = 224 + Width = 161 + Height = 37 + Caption = 'Test SQLite 3' + TabOrder = 0 + OnClick = btnTestClick + end + object memNotes: TMemo + Left = 24 + Top = 124 + Width = 185 + Height = 89 + Lines.Strings = ( + '') + ScrollBars = ssVertical + TabOrder = 1 + end + object ebName: TEdit + Left = 72 + Top = 40 + Width = 173 + Height = 21 + TabOrder = 2 + end + object ebNumber: TEdit + Left = 72 + Top = 68 + Width = 173 + Height = 21 + TabOrder = 3 + end + object ebID: TEdit + Left = 72 + Top = 12 + Width = 173 + Height = 21 + TabOrder = 4 + end + object btnLoadImage: TButton + Left = 192 + Top = 224 + Width = 157 + Height = 37 + Caption = 'Load image' + TabOrder = 5 + OnClick = btnLoadImageClick + end + object btnDisplayImage: TButton + Left = 360 + Top = 224 + Width = 157 + Height = 37 + Caption = 'Display image' + TabOrder = 6 + OnClick = btnDisplayImageClick + end +end diff --git a/Game/Code/lib/SQLite/example/uTestSqlite.pas b/Game/Code/lib/SQLite/example/uTestSqlite.pas new file mode 100644 index 00000000..6691fece --- /dev/null +++ b/Game/Code/lib/SQLite/example/uTestSqlite.pas @@ -0,0 +1,233 @@ +unit uTestSqlite; + +interface + +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, + Dialogs, StdCtrls,SQLiteTable3, ExtCtrls, jpeg; + +type + TForm1 = class(TForm) + btnTest: TButton; + memNotes: TMemo; + Label1: TLabel; + Label2: TLabel; + ebName: TEdit; + Label3: TLabel; + ebNumber: TEdit; + Label4: TLabel; + ebID: TEdit; + Image1: TImage; + btnLoadImage: TButton; + btnDisplayImage: TButton; + procedure btnTestClick(Sender: TObject); + procedure btnLoadImageClick(Sender: TObject); + procedure btnDisplayImageClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + Form1: TForm1; + +implementation + +{$R *.dfm} + +procedure TForm1.btnTestClick(Sender: TObject); +var +slDBpath: string; +sldb: TSQLiteDatabase; +sltb: TSQLIteTable; +sSQL: String; +Notes: String; + +begin + +slDBPath := ExtractFilepath(application.exename) ++ 'test.db'; + +sldb := TSQLiteDatabase.Create(slDBPath); +try + +if sldb.TableExists('testTable') then begin +sSQL := 'DROP TABLE testtable'; +sldb.execsql(sSQL); +end; + +sSQL := 'CREATE TABLE testtable ([ID] INTEGER PRIMARY KEY,[OtherID] INTEGER NULL,'; +sSQL := sSQL + '[Name] VARCHAR (255),[Number] FLOAT, [notes] BLOB, [picture] BLOB COLLATE NOCASE);'; + +sldb.execsql(sSQL); + +sldb.execsql('CREATE INDEX TestTableName ON [testtable]([Name]);'); + +//begin a transaction +sldb.BeginTransaction; + +sSQL := 'INSERT INTO testtable(Name,OtherID,Number,Notes) VALUES ("Some Name",4,587.6594,"Here are some notes");'; +//do the insert +sldb.ExecSQL(sSQL); + +sSQL := 'INSERT INTO testtable(Name,OtherID,Number,Notes) VALUES ("Another Name",12,4758.3265,"More notes");'; +//do the insert +sldb.ExecSQL(sSQL); + +//end the transaction +sldb.Commit; + +//query the data +sltb := slDb.GetTable('SELECT * FROM testtable'); +try + +if sltb.Count > 0 then +begin +//display first row + +ebName.Text := sltb.FieldAsString(sltb.FieldIndex['Name']); +ebID.Text := inttostr(sltb.FieldAsInteger(sltb.FieldIndex['ID'])); +ebNumber.Text := floattostr( sltb.FieldAsDouble(sltb.FieldIndex['Number'])); +Notes := sltb.FieldAsBlobText(sltb.FieldIndex['Notes']); +memNotes.Text := notes; + +end; + +finally +sltb.Free; +end; + +finally +sldb.Free; + +end; + +end; + +procedure TForm1.btnLoadImageClick(Sender: TObject); +var +slDBpath: string; +sldb: TSQLiteDatabase; +sltb: TSQLIteTable; +iID: integer; +fs: TFileStream; + +begin + +slDBPath := ExtractFilepath(application.exename) ++ 'test.db'; + +if not FileExists(slDBPath) then begin +MessageDLg('Test.db does not exist. Click Test Sqlite 3 to create it.',mtInformation,[mbOK],0); +exit; +end; + +sldb := TSQLiteDatabase.Create(slDBPath); +try + +//get an ID +//query the data +sltb := slDb.GetTable('SELECT ID FROM testtable'); +try + +if sltb.Count = 0 then begin +MessageDLg('There are no rows in the database. Click Test Sqlite 3 to insert a row.',mtInformation,[mbOK],0); +exit; +end; + +iID := sltb.FieldAsInteger(sltb.FieldIndex['ID']); + +finally +sltb.Free; +end; + +//load an image +fs := TFileStream.Create(ExtractFileDir(application.ExeName) + '\sunset.jpg',fmOpenRead); +try + +//insert the image into the db +sldb.UpdateBlob('UPDATE testtable set picture = ? WHERE ID = ' + inttostr(iID),fs); + +finally +fs.Free; +end; + +finally +sldb.Free; + +end; + +end; + +procedure TForm1.btnDisplayImageClick(Sender: TObject); +var +slDBpath: string; +sldb: TSQLiteDatabase; +sltb: TSQLIteTable; +iID: integer; +ms: TMemoryStream; +pic: TJPegImage; + +begin + +slDBPath := ExtractFilepath(application.exename) ++ 'test.db'; + +if not FileExists(slDBPath) then begin +MessageDLg('Test.db does not exist. Click Test Sqlite 3 to create it, then Load image to load an image.',mtInformation,[mbOK],0); +exit; +end; + +sldb := TSQLiteDatabase.Create(slDBPath); +try + +//get an ID +//query the data +sltb := slDb.GetTable('SELECT ID FROM testtable'); +try + +if not sltb.Count = 0 then begin +MessageDLg('No rows in the test database. Click Test Sqlite 3 to insert a row, then Load image to load an image.',mtInformation,[mbOK],0); +exit; +end; + +iID := sltb.FieldAsInteger(sltb.FieldIndex['ID']); + +finally +sltb.Free; +end; + +sltb := sldb.GetTable('SELECT picture FROM testtable where ID = ' + inttostr(iID)); +try + +ms := sltb.FieldAsBlob(sltb.FieldIndex['picture']); +//note that the memory stream is freed when the TSqliteTable is destroyed. + +if (ms = nil) then begin +MessageDLg('No image in the test database. Click Load image to load an image.',mtInformation,[mbOK],0); +exit; +end; + +ms.Position := 0; + +pic := TJPEGImage.Create; +pic.LoadFromStream(ms); + +self.Image1.Picture.Graphic := pic; + +pic.Free; + +finally +sltb.Free; +end; + +finally +sldb.Free; + +end; + + +end; + +end. diff --git a/Game/Code/lib/SQLite/readme.txt b/Game/Code/lib/SQLite/readme.txt new file mode 100644 index 00000000..80e5b3a1 --- /dev/null +++ b/Game/Code/lib/SQLite/readme.txt @@ -0,0 +1,82 @@ +14 Aug 2005 + +The following changes were made by Lukas Gebauer (geby@volny.cz). In addition, some changes from a previous D5-compatible version were merged, and the supplied sqlite3.dll is updated to version 3.2.2 + +Notes from Lukas: + +- added support for delphi 4+ + +- datatype constants matches SQlite datatypes contants. (otherwise in some situations you got bad column datatype!) + +- removed dependency on strutils + +- code is reformated to better look (official borland formationg +rules) + +- added some pragma's after database is open (temp is in memory) + +- TSQLiteDatabase.GetTableValue(const SQL: string): int64 for easy call of SQL commands what returning one number only. (like select +count(*)...) + +- TSQLiteDatabase.GetTableString(const SQL: string): String for easy call of SQL commands what returning one string only. (like PRAGMA +integrity_check) + +- TSQLiteDatabase.SetTimeout(Value: integer); you can set timeout for accessing to some table. Good for database sharing! + +- TSQLiteDatabase.version: string; returns SQLITE version string + +- removed bool fieldtype (it is not natural SQLite3 type) + +- fild type detection by Sqite3_columnType knows REAL too. + +- integer filedtype is based on Int64 + +- GetFields can get data from any supported fieldtype + +- changed some integers to cardinal for avoid signed and unsigned mismatch + +- TSqliteTable.FieldAsInteger(I: cardinal): int64; returns int64 + + +3 May 2005 Fixed bug where strupper called on column type before checking for nil + +2 May 2005 Add extra check for nil in TSqliteTable.Destroy, thanks to Tim Maddrell + +22 Apr 2005 Revise TSqliteTable.Destroy to fix memory leak with dtStr type (thanks to +Jose Brito) + +21 Apr 2005 Quick revision to fix case sensitivity in detecting column type, +and remove PRAGMA full_column_names = 1 which is deprecated. Warning: may break code. Fix your SQL code so that all column names in a result set are unique. + +21 Feb 2005 Sqlite DLL now 3.1.3 + +19 Feb 2005 Revised for Sqlite 3.1.2 + +21 Dec 2004 First public release + +The following notice appears in the Sqlite source code: + +* +** 2001 September 15 +** +** +** 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. + + +For more information about SQLite, see http://www.sqlite.org + +For more information about this simple wrapper, see http://www.itwriting.com/sqlitesimple.php + + + + + diff --git a/Game/Code/lib/SQLite/sqlite3.dll b/Game/Code/lib/SQLite/sqlite3.dll new file mode 100644 index 00000000..7111873f Binary files /dev/null and b/Game/Code/lib/SQLite/sqlite3.dll differ diff --git a/Game/Code/lib/SQLite/test.db b/Game/Code/lib/SQLite/test.db new file mode 100644 index 00000000..c7fc4356 Binary files /dev/null and b/Game/Code/lib/SQLite/test.db differ -- cgit v1.2.3