SQLiteTable3.~pas 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399
  1. unit SQLiteTable3;
  2. {
  3. Simple classes for using SQLite's exec and get_table.
  4. TSQLiteDatabase wraps the calls to open and close an SQLite database.
  5. It also wraps SQLite_exec for queries that do not return a result set
  6. TSQLiteTable wraps execution of SQL query.
  7. It run query and read all returned rows to internal buffer.
  8. It allows accessing fields by name as well as index and can move through a
  9. result set forward and backwards, or randomly to any row.
  10. TSQLiteUniTable wraps execution of SQL query.
  11. It run query as TSQLiteTable, but reading just first row only!
  12. You can step to next row (until not EOF) by 'Next' method.
  13. You cannot step backwards! (So, it is called as UniDirectional result set.)
  14. It not using any internal buffering, this class is very close to Sqlite API.
  15. It allows accessing fields by name as well as index on actual row only.
  16. Very good and fast for sequentional scanning of large result sets with minimal
  17. memory footprint.
  18. Warning! Do not close TSQLiteDatabase before any TSQLiteUniTable,
  19. because query is closed on TSQLiteUniTable destructor and database connection
  20. is used during TSQLiteUniTable live!
  21. SQL parameter usage:
  22. You can add named parameter values by call set of AddParam* methods.
  23. Parameters will be used for first next SQL statement only.
  24. Parameter name must be prefixed by ':', '$' or '@' and same prefix must be
  25. used in SQL statement!
  26. Sample:
  27. table.AddParamText(':str', 'some value');
  28. s := table.GetTableString('SELECT value FROM sometable WHERE id=:str');
  29. Notes from Andrew Retmanski on prepared queries
  30. The changes are as follows:
  31. SQLiteTable3.pas
  32. - Added new boolean property Synchronised (this controls the SYNCHRONOUS pragma as I found that turning this OFF increased the write performance in my application)
  33. - Added new type TSQLiteQuery (this is just a simple record wrapper around the SQL string and a TSQLiteStmt pointer)
  34. - Added PrepareSQL method to prepare SQL query - returns TSQLiteQuery
  35. - Added ReleaseSQL method to release previously prepared query
  36. - Added overloaded BindSQL methods for Integer and String types - these set new values for the prepared query parameters
  37. - Added overloaded ExecSQL method to execute a prepared TSQLiteQuery
  38. Usage of the new methods should be self explanatory but the process is in essence:
  39. 1. Call PrepareSQL to return TSQLiteQuery 2. Call BindSQL for each parameter in the prepared query 3. Call ExecSQL to run the prepared query 4. Repeat steps 2 & 3 as required 5. Call ReleaseSQL to free SQLite resources
  40. One other point - the Synchronised property throws an error if used inside a transaction.
  41. Acknowledments
  42. Adapted by Tim Anderson (tim@itwriting.com)
  43. Originally created by Pablo Pissanetzky (pablo@myhtpc.net)
  44. Modified and enhanced by Lukas Gebauer
  45. Modified and enhanced by Tobias Gunkel
  46. }
  47. interface
  48. {$IFDEF FPC}
  49. {$MODE Delphi}{$H+}
  50. {$ENDIF}
  51. uses
  52. {$IFDEF WIN32}
  53. Windows,
  54. {$ENDIF}
  55. SQLite3, Classes, SysUtils;
  56. const
  57. dtInt = 1;
  58. dtNumeric = 2;
  59. dtStr = 3;
  60. dtBlob = 4;
  61. dtNull = 5;
  62. type
  63. ESQLiteException = class(Exception)
  64. end;
  65. TSQliteParam = class
  66. public
  67. name: string;
  68. valuetype: integer;
  69. valueinteger: int64;
  70. valuefloat: double;
  71. valuedata: string;
  72. end;
  73. THookQuery = procedure(Sender: TObject; SQL: string) of object;
  74. TSQLiteQuery = record
  75. SQL: string;
  76. Statement: TSQLiteStmt;
  77. end;
  78. TSQLiteTable = class;
  79. TSQLiteUniTable = class;
  80. TSQLiteDatabase = class
  81. private
  82. fDB: TSQLiteDB;
  83. fInTrans: boolean;
  84. fSync: boolean;
  85. fParams: TList;
  86. FOnQuery: THookQuery;
  87. procedure RaiseError(s: string; SQL: string);
  88. procedure SetParams(Stmt: TSQLiteStmt);
  89. procedure BindData(Stmt: TSQLiteStmt; const Bindings: array of const);
  90. function GetRowsChanged: integer;
  91. protected
  92. procedure SetSynchronised(Value: boolean);
  93. procedure DoQuery(value: string);
  94. public
  95. constructor Create(const FileName: string);
  96. destructor Destroy; override;
  97. function GetTable(const SQL: Ansistring): TSQLiteTable; overload;
  98. function GetTable(const SQL: Ansistring; const Bindings: array of const): TSQLiteTable; overload;
  99. procedure ExecSQL(const SQL: Ansistring); overload;
  100. procedure ExecSQL(const SQL: Ansistring; const Bindings: array of const); overload;
  101. procedure ExecSQL(Query: TSQLiteQuery); overload;
  102. function PrepareSQL(const SQL: Ansistring): TSQLiteQuery;
  103. procedure BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: Integer); overload;
  104. procedure BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: string); overload;
  105. procedure ReleaseSQL(Query: TSQLiteQuery);
  106. function GetUniTable(const SQL: Ansistring): TSQLiteUniTable; overload;
  107. function GetUniTable(const SQL: Ansistring; const Bindings: array of const): TSQLiteUniTable; overload;
  108. function GetTableValue(const SQL: Ansistring): int64; overload;
  109. function GetTableValue(const SQL: Ansistring; const Bindings: array of const): int64; overload;
  110. function GetTableString(const SQL: Ansistring): string; overload;
  111. function GetTableString(const SQL: Ansistring; const Bindings: array of const): string; overload;
  112. procedure GetTableStrings(const SQL: Ansistring; const Value: TStrings);
  113. procedure UpdateBlob(const SQL: Ansistring; BlobData: TStream);
  114. procedure BeginTransaction;
  115. procedure Commit;
  116. procedure Rollback;
  117. function TableExists(TableName: string): boolean;
  118. function GetLastInsertRowID: int64;
  119. function GetLastChangedRows: int64;
  120. procedure SetTimeout(Value: integer);
  121. function Version: string;
  122. procedure AddCustomCollate(name: string; xCompare: TCollateXCompare);
  123. //adds collate named SYSTEM for correct data sorting by user's locale
  124. procedure AddSystemCollate;
  125. procedure ParamsClear;
  126. procedure AddParamInt(name: string; value: int64);
  127. procedure AddParamFloat(name: string; value: double);
  128. procedure AddParamText(name: string; value: string);
  129. procedure AddParamNull(name: string);
  130. property DB: TSQLiteDB read fDB;
  131. published
  132. property IsTransactionOpen: boolean read fInTrans;
  133. //database rows that were changed (or inserted or deleted) by the most recent SQL statement
  134. property RowsChanged: integer read getRowsChanged;
  135. property Synchronised: boolean read FSync write SetSynchronised;
  136. property OnQuery: THookQuery read FOnQuery write FOnQuery;
  137. end;
  138. TSQLiteTable = class
  139. private
  140. fResults: TList;
  141. fRowCount: cardinal;
  142. fColCount: cardinal;
  143. fCols: TStringList;
  144. fColTypes: TList;
  145. fRow: cardinal;
  146. function GetFields(I: cardinal): string;
  147. function GetEOF: boolean;
  148. function GetBOF: boolean;
  149. function GetColumns(I: integer): string;
  150. function GetFieldByName(FieldName: string): string;
  151. function GetFieldIndex(FieldName: string): integer;
  152. function GetCount: integer;
  153. function GetCountResult: integer;
  154. public
  155. constructor Create(DB: TSQLiteDatabase; const SQL: Ansistring); overload;
  156. constructor Create(DB: TSQLiteDatabase; const SQL: Ansistring; const Bindings: array of const); overload;
  157. destructor Destroy; override;
  158. function FieldAsInteger(I: cardinal): int64;
  159. function FieldAsBlob(I: cardinal): TMemoryStream;
  160. function FieldAsBlobText(I: cardinal): string;
  161. function FieldIsNull(I: cardinal): boolean;
  162. function FieldAsString(I: cardinal): string;
  163. function FieldAsDouble(I: cardinal): double;
  164. function Next: boolean;
  165. function Previous: boolean;
  166. property EOF: boolean read GetEOF;
  167. property BOF: boolean read GetBOF;
  168. property Fields[I: cardinal]: string read GetFields;
  169. property FieldByName[FieldName: string]: string read GetFieldByName;
  170. property FieldIndex[FieldName: string]: integer read GetFieldIndex;
  171. property Columns[I: integer]: string read GetColumns;
  172. property ColCount: cardinal read fColCount;
  173. property RowCount: cardinal read fRowCount;
  174. property Row: cardinal read fRow;
  175. function MoveFirst: boolean;
  176. function MoveLast: boolean;
  177. function MoveTo(position: cardinal): boolean;
  178. property Count: integer read GetCount;
  179. // The property CountResult is used when you execute count(*) queries.
  180. // It returns 0 if the result set is empty or the value of the
  181. // first field as an integer.
  182. property CountResult: integer read GetCountResult;
  183. end;
  184. TSQLiteUniTable = class
  185. private
  186. fColCount: cardinal;
  187. fCols: TStringList;
  188. fRow: cardinal;
  189. fEOF: boolean;
  190. fStmt: TSQLiteStmt;
  191. fDB: TSQLiteDatabase;
  192. fSQL: string;
  193. function GetFields(I: cardinal): string;
  194. function GetColumns(I: integer): string;
  195. function GetFieldByName(FieldName: string): string;
  196. function GetFieldIndex(FieldName: string): integer;
  197. public
  198. constructor Create(DB: TSQLiteDatabase; const SQL: Ansistring); overload;
  199. constructor Create(DB: TSQLiteDatabase; const SQL: Ansistring; const Bindings: array of const); overload;
  200. destructor Destroy; override;
  201. function FieldAsInteger(I: cardinal): int64;
  202. function FieldAsBlob(I: cardinal): TMemoryStream;
  203. function FieldAsBlobPtr(I: cardinal; out iNumBytes: integer): Pointer;
  204. function FieldAsBlobText(I: cardinal): string;
  205. function FieldIsNull(I: cardinal): boolean;
  206. function FieldAsString(I: cardinal): string;
  207. function FieldAsDouble(I: cardinal): double;
  208. function Next: boolean;
  209. property EOF: boolean read FEOF;
  210. property Fields[I: cardinal]: string read GetFields;
  211. property FieldByName[FieldName: string]: string read GetFieldByName;
  212. property FieldIndex[FieldName: string]: integer read GetFieldIndex;
  213. property Columns[I: integer]: string read GetColumns;
  214. property ColCount: cardinal read fColCount;
  215. property Row: cardinal read fRow;
  216. end;
  217. procedure DisposePointer(ptr: pointer); cdecl;
  218. {$IFDEF WIN32}
  219. function SystemCollate(Userdta: pointer; Buf1Len: integer; Buf1: pointer;
  220. Buf2Len: integer; Buf2: pointer): integer; cdecl;
  221. {$ENDIF}
  222. implementation
  223. procedure DisposePointer(ptr: pointer); cdecl;
  224. begin
  225. if assigned(ptr) then
  226. freemem(ptr);
  227. end;
  228. {$IFDEF WIN32}
  229. function SystemCollate(Userdta: pointer; Buf1Len: integer; Buf1: pointer;
  230. Buf2Len: integer; Buf2: pointer): integer; cdecl;
  231. begin
  232. Result := CompareStringW(LOCALE_USER_DEFAULT, 0, PWideChar(Buf1), Buf1Len,
  233. PWideChar(Buf2), Buf2Len) - 2;
  234. end;
  235. {$ENDIF}
  236. //------------------------------------------------------------------------------
  237. // TSQLiteDatabase
  238. //------------------------------------------------------------------------------
  239. constructor TSQLiteDatabase.Create(const FileName: string);
  240. var
  241. Msg: PAnsiChar;
  242. iResult: integer;
  243. utf8FileName: UTF8string;
  244. begin
  245. inherited Create;
  246. fParams := TList.Create;
  247. self.fInTrans := False;
  248. Msg := nil;
  249. try
  250. utf8FileName := UTF8String(FileName);
  251. iResult := SQLite3_Open(PAnsiChar(utf8FileName), Fdb);
  252. if iResult <> SQLITE_OK then
  253. if Assigned(Fdb) then
  254. begin
  255. Msg := Sqlite3_ErrMsg(Fdb);
  256. raise ESqliteException.CreateFmt('Failed to open database "%s" : %s',
  257. [FileName, Msg]);
  258. end
  259. else
  260. raise ESqliteException.CreateFmt('Failed to open database "%s" : unknown error',
  261. [FileName]);
  262. //set a few configs
  263. //L.G. Do not call it here. Because busy handler is not setted here,
  264. // any share violation causing exception!
  265. // self.ExecSQL('PRAGMA SYNCHRONOUS=NORMAL;');
  266. // self.ExecSQL('PRAGMA temp_store = MEMORY;');
  267. finally
  268. if Assigned(Msg) then
  269. SQLite3_Free(Msg);
  270. end;
  271. end;
  272. //..............................................................................
  273. destructor TSQLiteDatabase.Destroy;
  274. begin
  275. if self.fInTrans then
  276. self.Rollback; //assume rollback
  277. if Assigned(fDB) then
  278. SQLite3_Close(fDB);
  279. ParamsClear;
  280. fParams.Free;
  281. inherited;
  282. end;
  283. function TSQLiteDatabase.GetLastInsertRowID: int64;
  284. begin
  285. Result := Sqlite3_LastInsertRowID(self.fDB);
  286. end;
  287. function TSQLiteDatabase.GetLastChangedRows: int64;
  288. begin
  289. Result := SQLite3_TotalChanges(self.fDB);
  290. end;
  291. //..............................................................................
  292. procedure TSQLiteDatabase.RaiseError(s: string; SQL: string);
  293. //look up last error and raise an exception with an appropriate message
  294. var
  295. Msg: PAnsiChar;
  296. ret: integer;
  297. begin
  298. Msg := nil;
  299. ret := sqlite3_errcode(self.fDB);
  300. if ret <> SQLITE_OK then
  301. Msg := sqlite3_errmsg(self.fDB);
  302. if Msg <> nil then
  303. raise ESqliteException.CreateFmt(s + '.'#13'Error [%d]: %s.'#13'"%s": %s', [ret, SQLiteErrorStr(ret), SQL, Msg])
  304. else
  305. raise ESqliteException.CreateFmt(s, [SQL, 'No message']);
  306. end;
  307. procedure TSQLiteDatabase.SetSynchronised(Value: boolean);
  308. begin
  309. if Value <> fSync then
  310. begin
  311. if Value then
  312. ExecSQL('PRAGMA synchronous = ON;')
  313. else
  314. ExecSQL('PRAGMA synchronous = OFF;');
  315. fSync := Value;
  316. end;
  317. end;
  318. procedure TSQLiteDatabase.BindData(Stmt: TSQLiteStmt; const Bindings: array of const);
  319. var
  320. BlobMemStream: TCustomMemoryStream;
  321. BlobStdStream: TStream;
  322. DataPtr: Pointer;
  323. DataSize: integer;
  324. AnsiStr: AnsiString;
  325. AnsiStrPtr: PAnsiString;
  326. I: integer;
  327. begin
  328. for I := 0 to High(Bindings) do
  329. begin
  330. case Bindings[I].VType of
  331. vtString,
  332. vtAnsiString, vtPChar,
  333. vtWideString, vtPWideChar,
  334. vtChar, vtWideChar:
  335. begin
  336. case Bindings[I].VType of
  337. vtString: begin // ShortString
  338. AnsiStr := Bindings[I].VString^;
  339. DataPtr := PAnsiChar(AnsiStr);
  340. DataSize := Length(AnsiStr) + 1;
  341. end;
  342. vtPChar: begin
  343. DataPtr := Bindings[I].VPChar;
  344. DataSize := -1;
  345. end;
  346. vtAnsiString: begin
  347. AnsiStrPtr := PAnsiString(@Bindings[I].VAnsiString);
  348. DataPtr := PAnsiChar(AnsiStrPtr^);
  349. DataSize := Length(AnsiStrPtr^) + 1;
  350. end;
  351. vtPWideChar: begin
  352. DataPtr := PAnsiChar(UTF8Encode(WideString(Bindings[I].VPWideChar)));
  353. DataSize := -1;
  354. end;
  355. vtWideString: begin
  356. DataPtr := PAnsiChar(UTF8Encode(PWideString(@Bindings[I].VWideString)^));
  357. DataSize := -1;
  358. end;
  359. vtChar: begin
  360. DataPtr := PAnsiChar(string(Bindings[I].VChar));
  361. DataSize := 2;
  362. end;
  363. vtWideChar: begin
  364. DataPtr := PAnsiChar(UTF8Encode(WideString(Bindings[I].VWideChar)));
  365. DataSize := -1;
  366. end;
  367. else
  368. raise ESqliteException.Create('Unknown string-type');
  369. end;
  370. if (sqlite3_bind_text(Stmt, I + 1, DataPtr, DataSize, SQLITE_STATIC) <> SQLITE_OK) then
  371. RaiseError('Could not bind text', 'BindData');
  372. end;
  373. vtInteger:
  374. if (sqlite3_bind_int(Stmt, I + 1, Bindings[I].VInteger) <> SQLITE_OK) then
  375. RaiseError('Could not bind integer', 'BindData');
  376. vtInt64:
  377. if (sqlite3_bind_int64(Stmt, I + 1, Bindings[I].VInt64^) <> SQLITE_OK) then
  378. RaiseError('Could not bind int64', 'BindData');
  379. vtExtended:
  380. if (sqlite3_bind_double(Stmt, I + 1, Bindings[I].VExtended^) <> SQLITE_OK) then
  381. RaiseError('Could not bind extended', 'BindData');
  382. vtBoolean:
  383. if (sqlite3_bind_int(Stmt, I + 1, Integer(Bindings[I].VBoolean)) <> SQLITE_OK) then
  384. RaiseError('Could not bind boolean', 'BindData');
  385. vtPointer:
  386. begin
  387. if (Bindings[I].VPointer = nil) then
  388. begin
  389. if (sqlite3_bind_null(Stmt, I + 1) <> SQLITE_OK) then
  390. RaiseError('Could not bind null', 'BindData');
  391. end
  392. else
  393. raise ESqliteException.Create('Unhandled pointer (<> nil)');
  394. end;
  395. vtObject:
  396. begin
  397. if (Bindings[I].VObject is TCustomMemoryStream) then
  398. begin
  399. BlobMemStream := TCustomMemoryStream(Bindings[I].VObject);
  400. if (sqlite3_bind_blob(Stmt, I + 1, @PAnsiChar(BlobMemStream.Memory)[BlobMemStream.Position],
  401. BlobMemStream.Size - BlobMemStream.Position, SQLITE_STATIC) <> SQLITE_OK) then
  402. begin
  403. RaiseError('Could not bind BLOB', 'BindData');
  404. end;
  405. end
  406. else if (Bindings[I].VObject is TStream) then
  407. begin
  408. BlobStdStream := TStream(Bindings[I].VObject);
  409. DataSize := BlobStdStream.Size;
  410. GetMem(DataPtr, DataSize);
  411. if (DataPtr = nil) then
  412. raise ESqliteException.Create('Error getting memory to save blob');
  413. BlobStdStream.Position := 0;
  414. BlobStdStream.Read(DataPtr^, DataSize);
  415. if (sqlite3_bind_blob(stmt, I + 1, DataPtr, DataSize, @DisposePointer) <> SQLITE_OK) then
  416. RaiseError('Could not bind BLOB', 'BindData');
  417. end
  418. else
  419. raise ESqliteException.Create('Unhandled object-type in binding');
  420. end
  421. else
  422. begin
  423. raise ESqliteException.Create('Unhandled binding');
  424. end;
  425. end;
  426. end;
  427. end;
  428. procedure TSQLiteDatabase.ExecSQL(const SQL: Ansistring);
  429. begin
  430. ExecSQL(SQL, []);
  431. end;
  432. procedure TSQLiteDatabase.ExecSQL(const SQL: Ansistring; const Bindings: array of const);
  433. var
  434. Stmt: TSQLiteStmt;
  435. NextSQLStatement: PAnsiChar;
  436. iStepResult: integer;
  437. begin
  438. try
  439. if Sqlite3_Prepare_v2(self.fDB, PAnsiChar(SQL), -1, Stmt, NextSQLStatement) <>
  440. SQLITE_OK then
  441. RaiseError('Error executing SQL', SQL);
  442. if (Stmt = nil) then
  443. RaiseError('Could not prepare SQL statement', SQL);
  444. DoQuery(SQL);
  445. SetParams(Stmt);
  446. BindData(Stmt, Bindings);
  447. iStepResult := Sqlite3_step(Stmt);
  448. if (iStepResult <> SQLITE_DONE) then
  449. begin
  450. SQLite3_reset(stmt);
  451. RaiseError('Error executing SQL statement', SQL);
  452. end;
  453. finally
  454. if Assigned(Stmt) then
  455. Sqlite3_Finalize(stmt);
  456. end;
  457. end;
  458. {$WARNINGS OFF}
  459. procedure TSQLiteDatabase.ExecSQL(Query: TSQLiteQuery);
  460. var
  461. iStepResult: integer;
  462. begin
  463. if Assigned(Query.Statement) then
  464. begin
  465. iStepResult := Sqlite3_step(Query.Statement);
  466. if (iStepResult <> SQLITE_DONE) then
  467. begin
  468. SQLite3_reset(Query.Statement);
  469. RaiseError('Error executing prepared SQL statement', Query.SQL);
  470. end;
  471. Sqlite3_Reset(Query.Statement);
  472. end;
  473. end;
  474. {$WARNINGS ON}
  475. {$WARNINGS OFF}
  476. function TSQLiteDatabase.PrepareSQL(const SQL: Ansistring): TSQLiteQuery;
  477. var
  478. Stmt: TSQLiteStmt;
  479. NextSQLStatement: PAnsiChar;
  480. begin
  481. Result.SQL := SQL;
  482. Result.Statement := nil;
  483. if Sqlite3_Prepare(self.fDB, PAnsiChar(SQL), -1, Stmt, NextSQLStatement) <>
  484. SQLITE_OK then
  485. RaiseError('Error executing SQL', SQL)
  486. else
  487. Result.Statement := Stmt;
  488. if (Result.Statement = nil) then
  489. RaiseError('Could not prepare SQL statement', SQL);
  490. DoQuery(SQL);
  491. end;
  492. {$WARNINGS ON}
  493. {$WARNINGS OFF}
  494. procedure TSQLiteDatabase.BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: Integer);
  495. begin
  496. if Assigned(Query.Statement) then
  497. sqlite3_Bind_Int(Query.Statement, Index, Value)
  498. else
  499. RaiseError('Could not bind integer to prepared SQL statement', Query.SQL);
  500. end;
  501. {$WARNINGS ON}
  502. {$WARNINGS OFF}
  503. procedure TSQLiteDatabase.BindSQL(Query: TSQLiteQuery; const Index: Integer; const Value: string);
  504. begin
  505. if Assigned(Query.Statement) then
  506. Sqlite3_Bind_Text(Query.Statement, Index, PAnsiChar(Value), Length(Value), Pointer(SQLITE_STATIC))
  507. else
  508. RaiseError('Could not bind string to prepared SQL statement', Query.SQL);
  509. end;
  510. {$WARNINGS ON}
  511. {$WARNINGS OFF}
  512. procedure TSQLiteDatabase.ReleaseSQL(Query: TSQLiteQuery);
  513. begin
  514. if Assigned(Query.Statement) then
  515. begin
  516. Sqlite3_Finalize(Query.Statement);
  517. Query.Statement := nil;
  518. end
  519. else
  520. RaiseError('Could not release prepared SQL statement', Query.SQL);
  521. end;
  522. {$WARNINGS ON}
  523. procedure TSQLiteDatabase.UpdateBlob(const SQL: Ansistring; BlobData: TStream);
  524. var
  525. iSize: integer;
  526. ptr: pointer;
  527. Stmt: TSQLiteStmt;
  528. Msg: PAnsiChar;
  529. NextSQLStatement: PAnsiChar;
  530. iStepResult: integer;
  531. iBindResult: integer;
  532. begin
  533. //expects SQL of the form 'UPDATE MYTABLE SET MYFIELD = ? WHERE MYKEY = 1'
  534. if pos('?', SQL) = 0 then
  535. RaiseError('SQL must include a ? parameter', SQL);
  536. Msg := nil;
  537. try
  538. if Sqlite3_Prepare_v2(self.fDB, PAnsiChar(SQL), -1, Stmt, NextSQLStatement) <>
  539. SQLITE_OK then
  540. RaiseError('Could not prepare SQL statement', SQL);
  541. if (Stmt = nil) then
  542. RaiseError('Could not prepare SQL statement', SQL);
  543. DoQuery(SQL);
  544. //now bind the blob data
  545. iSize := BlobData.size;
  546. GetMem(ptr, iSize);
  547. if (ptr = nil) then
  548. raise ESqliteException.CreateFmt('Error getting memory to save blob',
  549. [SQL, 'Error']);
  550. BlobData.position := 0;
  551. BlobData.Read(ptr^, iSize);
  552. iBindResult := SQLite3_Bind_Blob(stmt, 1, ptr, iSize, @DisposePointer);
  553. if iBindResult <> SQLITE_OK then
  554. RaiseError('Error binding blob to database', SQL);
  555. iStepResult := Sqlite3_step(Stmt);
  556. if (iStepResult <> SQLITE_DONE) then
  557. begin
  558. SQLite3_reset(stmt);
  559. RaiseError('Error executing SQL statement', SQL);
  560. end;
  561. finally
  562. if Assigned(Stmt) then
  563. Sqlite3_Finalize(stmt);
  564. if Assigned(Msg) then
  565. SQLite3_Free(Msg);
  566. end;
  567. end;
  568. //..............................................................................
  569. function TSQLiteDatabase.GetTable(const SQL: Ansistring): TSQLiteTable;
  570. begin
  571. Result := TSQLiteTable.Create(Self, SQL);
  572. end;
  573. function TSQLiteDatabase.GetTable(const SQL: Ansistring; const Bindings: array of const): TSQLiteTable;
  574. begin
  575. Result := TSQLiteTable.Create(Self, SQL, Bindings);
  576. end;
  577. function TSQLiteDatabase.GetUniTable(const SQL: Ansistring): TSQLiteUniTable;
  578. begin
  579. Result := TSQLiteUniTable.Create(Self, SQL);
  580. end;
  581. function TSQLiteDatabase.GetUniTable(const SQL: Ansistring; const Bindings: array of const): TSQLiteUniTable;
  582. begin
  583. Result := TSQLiteUniTable.Create(Self, SQL, Bindings);
  584. end;
  585. function TSQLiteDatabase.GetTableValue(const SQL: Ansistring): int64;
  586. begin
  587. Result := GetTableValue(SQL, []);
  588. end;
  589. function TSQLiteDatabase.GetTableValue(const SQL: Ansistring; const Bindings: array of const): int64;
  590. var
  591. Table: TSQLiteUniTable;
  592. begin
  593. Result := 0;
  594. Table := self.GetUniTable(SQL, Bindings);
  595. try
  596. if not Table.EOF then
  597. Result := Table.FieldAsInteger(0);
  598. finally
  599. Table.Free;
  600. end;
  601. end;
  602. function TSQLiteDatabase.GetTableString(const SQL: Ansistring): string;
  603. begin
  604. Result := GetTableString(SQL, []);
  605. end;
  606. function TSQLiteDatabase.GetTableString(const SQL: Ansistring; const Bindings: array of const): string;
  607. var
  608. Table: TSQLiteUniTable;
  609. begin
  610. Result := '';
  611. Table := self.GetUniTable(SQL, Bindings);
  612. try
  613. if not Table.EOF then
  614. Result := Table.FieldAsString(0);
  615. finally
  616. Table.Free;
  617. end;
  618. end;
  619. procedure TSQLiteDatabase.GetTableStrings(const SQL: Ansistring;
  620. const Value: TStrings);
  621. var
  622. Table: TSQLiteUniTable;
  623. begin
  624. Value.Clear;
  625. Table := self.GetUniTable(SQL);
  626. try
  627. while not table.EOF do
  628. begin
  629. Value.Add(Table.FieldAsString(0));
  630. table.Next;
  631. end;
  632. finally
  633. Table.Free;
  634. end;
  635. end;
  636. procedure TSQLiteDatabase.BeginTransaction;
  637. begin
  638. if not self.fInTrans then
  639. begin
  640. self.ExecSQL('BEGIN TRANSACTION');
  641. self.fInTrans := True;
  642. end
  643. else
  644. raise ESqliteException.Create('Transaction already open');
  645. end;
  646. procedure TSQLiteDatabase.Commit;
  647. begin
  648. self.ExecSQL('COMMIT');
  649. self.fInTrans := False;
  650. end;
  651. procedure TSQLiteDatabase.Rollback;
  652. begin
  653. self.ExecSQL('ROLLBACK');
  654. self.fInTrans := False;
  655. end;
  656. function TSQLiteDatabase.TableExists(TableName: string): boolean;
  657. var
  658. sql: string;
  659. ds: TSqliteTable;
  660. begin
  661. //returns true if table exists in the database
  662. sql := 'select [sql] from sqlite_master where [type] = ''table'' and lower(name) = ''' +
  663. lowercase(TableName) + ''' ';
  664. ds := self.GetTable(sql);
  665. try
  666. Result := (ds.Count > 0);
  667. finally
  668. ds.Free;
  669. end;
  670. end;
  671. procedure TSQLiteDatabase.SetTimeout(Value: integer);
  672. begin
  673. SQLite3_BusyTimeout(self.fDB, Value);
  674. end;
  675. function TSQLiteDatabase.Version: string;
  676. begin
  677. Result := SQLite3_Version;
  678. end;
  679. procedure TSQLiteDatabase.AddCustomCollate(name: string;
  680. xCompare: TCollateXCompare);
  681. begin
  682. sqlite3_create_collation(fdb, PAnsiChar(name), SQLITE_UTF8, nil, xCompare);
  683. end;
  684. procedure TSQLiteDatabase.AddSystemCollate;
  685. begin
  686. {$IFDEF WIN32}
  687. sqlite3_create_collation(fdb, 'SYSTEM', SQLITE_UTF16LE, nil, @SystemCollate);
  688. {$ENDIF}
  689. end;
  690. procedure TSQLiteDatabase.ParamsClear;
  691. var
  692. n: integer;
  693. begin
  694. for n := fParams.Count - 1 downto 0 do
  695. TSQliteParam(fparams[n]).free;
  696. fParams.Clear;
  697. end;
  698. procedure TSQLiteDatabase.AddParamInt(name: string; value: int64);
  699. var
  700. par: TSQliteParam;
  701. begin
  702. par := TSQliteParam.Create;
  703. par.name := name;
  704. par.valuetype := SQLITE_INTEGER;
  705. par.valueinteger := value;
  706. fParams.Add(par);
  707. end;
  708. procedure TSQLiteDatabase.AddParamFloat(name: string; value: double);
  709. var
  710. par: TSQliteParam;
  711. begin
  712. par := TSQliteParam.Create;
  713. par.name := name;
  714. par.valuetype := SQLITE_FLOAT;
  715. par.valuefloat := value;
  716. fParams.Add(par);
  717. end;
  718. procedure TSQLiteDatabase.AddParamText(name: string; value: string);
  719. var
  720. par: TSQliteParam;
  721. begin
  722. par := TSQliteParam.Create;
  723. par.name := name;
  724. par.valuetype := SQLITE_TEXT;
  725. par.valuedata := value;
  726. fParams.Add(par);
  727. end;
  728. procedure TSQLiteDatabase.AddParamNull(name: string);
  729. var
  730. par: TSQliteParam;
  731. begin
  732. par := TSQliteParam.Create;
  733. par.name := name;
  734. par.valuetype := SQLITE_NULL;
  735. fParams.Add(par);
  736. end;
  737. procedure TSQLiteDatabase.SetParams(Stmt: TSQLiteStmt);
  738. var
  739. n: integer;
  740. i: integer;
  741. par: TSQliteParam;
  742. begin
  743. try
  744. for n := 0 to fParams.Count - 1 do
  745. begin
  746. par := TSQliteParam(fParams[n]);
  747. i := sqlite3_bind_parameter_index(Stmt, PAnsiChar(par.name));
  748. if i > 0 then
  749. begin
  750. case par.valuetype of
  751. SQLITE_INTEGER:
  752. sqlite3_bind_int64(Stmt, i, par.valueinteger);
  753. SQLITE_FLOAT:
  754. sqlite3_bind_double(Stmt, i, par.valuefloat);
  755. SQLITE_TEXT:
  756. sqlite3_bind_text(Stmt, i, PAnsiChar(par.valuedata),
  757. length(par.valuedata), SQLITE_TRANSIENT);
  758. SQLITE_NULL:
  759. sqlite3_bind_null(Stmt, i);
  760. end;
  761. end;
  762. end;
  763. finally
  764. ParamsClear;
  765. end;
  766. end;
  767. //database rows that were changed (or inserted or deleted) by the most recent SQL statement
  768. function TSQLiteDatabase.GetRowsChanged: integer;
  769. begin
  770. Result := SQLite3_Changes(self.fDB);
  771. end;
  772. procedure TSQLiteDatabase.DoQuery(value: string);
  773. begin
  774. if assigned(OnQuery) then
  775. OnQuery(Self, Value);
  776. end;
  777. //------------------------------------------------------------------------------
  778. // TSQLiteTable
  779. //------------------------------------------------------------------------------
  780. constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: Ansistring);
  781. begin
  782. Create(DB, SQL, []);
  783. end;
  784. constructor TSQLiteTable.Create(DB: TSQLiteDatabase; const SQL: Ansistring; const Bindings: array of const);
  785. var
  786. Stmt: TSQLiteStmt;
  787. NextSQLStatement: PAnsiChar;
  788. iStepResult: integer;
  789. ptr: pointer;
  790. iNumBytes: integer;
  791. thisBlobValue: TMemoryStream;
  792. thisStringValue: pstring;
  793. thisDoubleValue: pDouble;
  794. thisIntValue: pInt64;
  795. thisColType: pInteger;
  796. i: integer;
  797. DeclaredColType: PAnsiChar;
  798. ActualColType: integer;
  799. ptrValue: PAnsiChar;
  800. begin
  801. inherited create;
  802. try
  803. self.fRowCount := 0;
  804. self.fColCount := 0;
  805. //if there are several SQL statements in SQL, NextSQLStatment points to the
  806. //beginning of the next one. Prepare only prepares the first SQL statement.
  807. if Sqlite3_Prepare_v2(DB.fDB, PAnsiChar(SQL), -1, Stmt, NextSQLStatement) <> SQLITE_OK then
  808. DB.RaiseError('Error executing SQL', SQL);
  809. if (Stmt = nil) then
  810. DB.RaiseError('Could not prepare SQL statement', SQL);
  811. DB.DoQuery(SQL);
  812. DB.SetParams(Stmt);
  813. DB.BindData(Stmt, Bindings);
  814. iStepResult := Sqlite3_step(Stmt);
  815. while (iStepResult <> SQLITE_DONE) do
  816. begin
  817. case iStepResult of
  818. SQLITE_ROW:
  819. begin
  820. Inc(fRowCount);
  821. if (fRowCount = 1) then
  822. begin
  823. //get data types
  824. fCols := TStringList.Create;
  825. fColTypes := TList.Create;
  826. fColCount := SQLite3_ColumnCount(stmt);
  827. for i := 0 to Pred(fColCount) do
  828. fCols.Add(AnsiUpperCase(Sqlite3_ColumnName(stmt, i)));
  829. for i := 0 to Pred(fColCount) do
  830. begin
  831. new(thisColType);
  832. DeclaredColType := Sqlite3_ColumnDeclType(stmt, i);
  833. if DeclaredColType = nil then
  834. thisColType^ := Sqlite3_ColumnType(stmt, i) //use the actual column type instead
  835. //seems to be needed for last_insert_rowid
  836. else
  837. if (DeclaredColType = 'INTEGER') or (DeclaredColType = 'BOOLEAN') then
  838. thisColType^ := dtInt
  839. else
  840. if (DeclaredColType = 'NUMERIC') or
  841. (DeclaredColType = 'FLOAT') or
  842. (DeclaredColType = 'DOUBLE') or
  843. (DeclaredColType = 'REAL') then
  844. thisColType^ := dtNumeric
  845. else
  846. if DeclaredColType = 'BLOB' then
  847. thisColType^ := dtBlob
  848. else
  849. thisColType^ := dtStr;
  850. fColTypes.Add(thiscoltype);
  851. end;
  852. fResults := TList.Create;
  853. end;
  854. //get column values
  855. for i := 0 to Pred(ColCount) do
  856. begin
  857. ActualColType := Sqlite3_ColumnType(stmt, i);
  858. if (ActualColType = SQLITE_NULL) then
  859. fResults.Add(nil)
  860. else
  861. if pInteger(fColTypes[i])^ = dtInt then
  862. begin
  863. new(thisintvalue);
  864. thisintvalue^ := Sqlite3_ColumnInt64(stmt, i);
  865. fResults.Add(thisintvalue);
  866. end
  867. else
  868. if pInteger(fColTypes[i])^ = dtNumeric then
  869. begin
  870. new(thisdoublevalue);
  871. thisdoublevalue^ := Sqlite3_ColumnDouble(stmt, i);
  872. fResults.Add(thisdoublevalue);
  873. end
  874. else
  875. if pInteger(fColTypes[i])^ = dtBlob then
  876. begin
  877. iNumBytes := Sqlite3_ColumnBytes(stmt, i);
  878. if iNumBytes = 0 then
  879. thisblobvalue := nil
  880. else
  881. begin
  882. thisblobvalue := TMemoryStream.Create;
  883. thisblobvalue.position := 0;
  884. ptr := Sqlite3_ColumnBlob(stmt, i);
  885. thisblobvalue.writebuffer(ptr^, iNumBytes);
  886. end;
  887. fResults.Add(thisblobvalue);
  888. end
  889. else
  890. begin
  891. new(thisstringvalue);
  892. ptrValue := Sqlite3_ColumnText(stmt, i);
  893. setstring(thisstringvalue^, ptrvalue, strlen(ptrvalue));
  894. fResults.Add(thisstringvalue);
  895. end;
  896. end;
  897. end;
  898. SQLITE_BUSY:
  899. raise ESqliteException.CreateFmt('Could not prepare SQL statement',
  900. [SQL, 'SQLite is Busy']);
  901. else
  902. begin
  903. SQLite3_reset(stmt);
  904. DB.RaiseError('Could not retrieve data', SQL);
  905. end;
  906. end;
  907. iStepResult := Sqlite3_step(Stmt);
  908. end;
  909. fRow := 0;
  910. finally
  911. if Assigned(Stmt) then
  912. Sqlite3_Finalize(stmt);
  913. end;
  914. end;
  915. //..............................................................................
  916. destructor TSQLiteTable.Destroy;
  917. var
  918. i: cardinal;
  919. iColNo: integer;
  920. begin
  921. if Assigned(fResults) then
  922. begin
  923. for i := 0 to fResults.Count - 1 do
  924. begin
  925. //check for blob type
  926. iColNo := (i mod fColCount);
  927. case pInteger(self.fColTypes[iColNo])^ of
  928. dtBlob:
  929. TMemoryStream(fResults[i]).Free;
  930. dtStr:
  931. if fResults[i] <> nil then
  932. begin
  933. setstring(string(fResults[i]^), nil, 0);
  934. dispose(fResults[i]);
  935. end;
  936. else
  937. dispose(fResults[i]);
  938. end;
  939. end;
  940. fResults.Free;
  941. end;
  942. if Assigned(fCols) then
  943. fCols.Free;
  944. if Assigned(fColTypes) then
  945. for i := 0 to fColTypes.Count - 1 do
  946. dispose(fColTypes[i]);
  947. fColTypes.Free;
  948. inherited;
  949. end;
  950. //..............................................................................
  951. function TSQLiteTable.GetColumns(I: integer): string;
  952. begin
  953. Result := fCols[I];
  954. end;
  955. //..............................................................................
  956. function TSQLiteTable.GetCountResult: integer;
  957. begin
  958. if not EOF then
  959. Result := StrToInt(Fields[0])
  960. else
  961. Result := 0;
  962. end;
  963. function TSQLiteTable.GetCount: integer;
  964. begin
  965. Result := FRowCount;
  966. end;
  967. //..............................................................................
  968. function TSQLiteTable.GetEOF: boolean;
  969. begin
  970. Result := fRow >= fRowCount;
  971. end;
  972. function TSQLiteTable.GetBOF: boolean;
  973. begin
  974. Result := fRow <= 0;
  975. end;
  976. //..............................................................................
  977. function TSQLiteTable.GetFieldByName(FieldName: string): string;
  978. begin
  979. Result := GetFields(self.GetFieldIndex(FieldName));
  980. end;
  981. function TSQLiteTable.GetFieldIndex(FieldName: string): integer;
  982. begin
  983. if (fCols = nil) then
  984. begin
  985. raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
  986. exit;
  987. end;
  988. if (fCols.count = 0) then
  989. begin
  990. raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
  991. exit;
  992. end;
  993. Result := fCols.IndexOf(AnsiUpperCase(FieldName));
  994. if (result < 0) then
  995. begin
  996. raise ESqliteException.Create('Field not found in dataset: ' + fieldname)
  997. end;
  998. end;
  999. //..............................................................................
  1000. function TSQLiteTable.GetFields(I: cardinal): string;
  1001. var
  1002. thisvalue: pstring;
  1003. thistype: integer;
  1004. begin
  1005. Result := '';
  1006. if EOF then
  1007. raise ESqliteException.Create('Table is at End of File');
  1008. //integer types are not stored in the resultset
  1009. //as strings, so they should be retrieved using the type-specific
  1010. //methods
  1011. thistype := pInteger(self.fColTypes[I])^;
  1012. case thistype of
  1013. dtStr:
  1014. begin
  1015. thisvalue := self.fResults[(self.frow * self.fColCount) + I];
  1016. if (thisvalue <> nil) then
  1017. Result := thisvalue^
  1018. else
  1019. Result := '';
  1020. end;
  1021. dtInt:
  1022. Result := IntToStr(self.FieldAsInteger(I));
  1023. dtNumeric:
  1024. Result := FloatToStr(self.FieldAsDouble(I));
  1025. dtBlob:
  1026. Result := self.FieldAsBlobText(I);
  1027. else
  1028. Result := '';
  1029. end;
  1030. end;
  1031. function TSqliteTable.FieldAsBlob(I: cardinal): TMemoryStream;
  1032. begin
  1033. if EOF then
  1034. raise ESqliteException.Create('Table is at End of File');
  1035. if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
  1036. Result := nil
  1037. else
  1038. if pInteger(self.fColTypes[I])^ = dtBlob then
  1039. Result := TMemoryStream(self.fResults[(self.frow * self.fColCount) + I])
  1040. else
  1041. raise ESqliteException.Create('Not a Blob field');
  1042. end;
  1043. function TSqliteTable.FieldAsBlobText(I: cardinal): string;
  1044. var
  1045. MemStream: TMemoryStream;
  1046. Buffer: PAnsiChar;
  1047. begin
  1048. Result := '';
  1049. MemStream := self.FieldAsBlob(I);
  1050. if MemStream <> nil then
  1051. if MemStream.Size > 0 then
  1052. begin
  1053. MemStream.position := 0;
  1054. {$IFDEF UNICODE}
  1055. Buffer := AnsiStralloc(MemStream.Size + 1);
  1056. {$ELSE}
  1057. Buffer := Stralloc(MemStream.Size + 1);
  1058. {$ENDIF}
  1059. MemStream.readbuffer(Buffer[0], MemStream.Size);
  1060. (Buffer + MemStream.Size)^ := chr(0);
  1061. SetString(Result, Buffer, MemStream.size);
  1062. strdispose(Buffer);
  1063. end;
  1064. //do not free the TMemoryStream here; it is freed when
  1065. //TSqliteTable is destroyed
  1066. end;
  1067. function TSqliteTable.FieldAsInteger(I: cardinal): int64;
  1068. begin
  1069. if EOF then
  1070. raise ESqliteException.Create('Table is at End of File');
  1071. if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
  1072. Result := 0
  1073. else
  1074. if pInteger(self.fColTypes[I])^ = dtInt then
  1075. Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^
  1076. else
  1077. if pInteger(self.fColTypes[I])^ = dtNumeric then
  1078. Result := trunc(strtofloat(pString(self.fResults[(self.frow * self.fColCount) + I])^))
  1079. else
  1080. raise ESqliteException.Create('Not an integer or numeric field');
  1081. end;
  1082. function TSqliteTable.FieldAsDouble(I: cardinal): double;
  1083. begin
  1084. if EOF then
  1085. raise ESqliteException.Create('Table is at End of File');
  1086. if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
  1087. Result := 0
  1088. else
  1089. if pInteger(self.fColTypes[I])^ = dtInt then
  1090. Result := pInt64(self.fResults[(self.frow * self.fColCount) + I])^
  1091. else
  1092. if pInteger(self.fColTypes[I])^ = dtNumeric then
  1093. Result := pDouble(self.fResults[(self.frow * self.fColCount) + I])^
  1094. else
  1095. raise ESqliteException.Create('Not an integer or numeric field');
  1096. end;
  1097. function TSqliteTable.FieldAsString(I: cardinal): string;
  1098. begin
  1099. if EOF then
  1100. raise ESqliteException.Create('Table is at End of File');
  1101. if (self.fResults[(self.frow * self.fColCount) + I] = nil) then
  1102. Result := ''
  1103. else
  1104. Result := self.GetFields(I);
  1105. end;
  1106. function TSqliteTable.FieldIsNull(I: cardinal): boolean;
  1107. var
  1108. thisvalue: pointer;
  1109. begin
  1110. if EOF then
  1111. raise ESqliteException.Create('Table is at End of File');
  1112. thisvalue := self.fResults[(self.frow * self.fColCount) + I];
  1113. Result := (thisvalue = nil);
  1114. end;
  1115. //..............................................................................
  1116. function TSQLiteTable.Next: boolean;
  1117. begin
  1118. Result := False;
  1119. if not EOF then
  1120. begin
  1121. Inc(fRow);
  1122. Result := True;
  1123. end;
  1124. end;
  1125. function TSQLiteTable.Previous: boolean;
  1126. begin
  1127. Result := False;
  1128. if not BOF then
  1129. begin
  1130. Dec(fRow);
  1131. Result := True;
  1132. end;
  1133. end;
  1134. function TSQLiteTable.MoveFirst: boolean;
  1135. begin
  1136. Result := False;
  1137. if self.fRowCount > 0 then
  1138. begin
  1139. fRow := 0;
  1140. Result := True;
  1141. end;
  1142. end;
  1143. function TSQLiteTable.MoveLast: boolean;
  1144. begin
  1145. Result := False;
  1146. if self.fRowCount > 0 then
  1147. begin
  1148. fRow := fRowCount - 1;
  1149. Result := True;
  1150. end;
  1151. end;
  1152. {$WARNINGS OFF}
  1153. function TSQLiteTable.MoveTo(position: cardinal): boolean;
  1154. begin
  1155. Result := False;
  1156. if (self.fRowCount > 0) and (self.fRowCount > position) then
  1157. begin
  1158. fRow := position;
  1159. Result := True;
  1160. end;
  1161. end;
  1162. {$WARNINGS ON}
  1163. { TSQLiteUniTable }
  1164. constructor TSQLiteUniTable.Create(DB: TSQLiteDatabase; const SQL: Ansistring);
  1165. begin
  1166. Create(DB, SQL, []);
  1167. end;
  1168. constructor TSQLiteUniTable.Create(DB: TSQLiteDatabase; const SQL: Ansistring; const Bindings: array of const);
  1169. var
  1170. NextSQLStatement: PAnsiChar;
  1171. i: integer;
  1172. begin
  1173. inherited create;
  1174. self.fDB := db;
  1175. self.fEOF := false;
  1176. self.fRow := 0;
  1177. self.fColCount := 0;
  1178. self.fSQL := SQL;
  1179. if Sqlite3_Prepare_v2(DB.fDB, PAnsiChar(SQL), -1, fStmt, NextSQLStatement) <> SQLITE_OK then
  1180. DB.RaiseError('Error executing SQL', SQL);
  1181. if (fStmt = nil) then
  1182. DB.RaiseError('Could not prepare SQL statement', SQL);
  1183. DB.DoQuery(SQL);
  1184. DB.SetParams(fStmt);
  1185. DB.BindData(fStmt, Bindings);
  1186. //get data types
  1187. fCols := TStringList.Create;
  1188. fColCount := SQLite3_ColumnCount(fstmt);
  1189. for i := 0 to Pred(fColCount) do
  1190. fCols.Add(AnsiUpperCase(Sqlite3_ColumnName(fstmt, i)));
  1191. Next;
  1192. end;
  1193. destructor TSQLiteUniTable.Destroy;
  1194. begin
  1195. if Assigned(fStmt) then
  1196. Sqlite3_Finalize(fstmt);
  1197. if Assigned(fCols) then
  1198. fCols.Free;
  1199. inherited;
  1200. end;
  1201. function TSQLiteUniTable.FieldAsBlob(I: cardinal): TMemoryStream;
  1202. var
  1203. iNumBytes: integer;
  1204. ptr: pointer;
  1205. begin
  1206. Result := TMemoryStream.Create;
  1207. iNumBytes := Sqlite3_ColumnBytes(fstmt, i);
  1208. if iNumBytes > 0 then
  1209. begin
  1210. ptr := Sqlite3_ColumnBlob(fstmt, i);
  1211. Result.writebuffer(ptr^, iNumBytes);
  1212. Result.Position := 0;
  1213. end;
  1214. end;
  1215. function TSQLiteUniTable.FieldAsBlobPtr(I: cardinal; out iNumBytes: integer): Pointer;
  1216. begin
  1217. iNumBytes := Sqlite3_ColumnBytes(fstmt, i);
  1218. Result := Sqlite3_ColumnBlob(fstmt, i);
  1219. end;
  1220. function TSQLiteUniTable.FieldAsBlobText(I: cardinal): string;
  1221. var
  1222. MemStream: TMemoryStream;
  1223. Buffer: PAnsiChar;
  1224. begin
  1225. Result := '';
  1226. MemStream := self.FieldAsBlob(I);
  1227. if MemStream <> nil then
  1228. try
  1229. if MemStream.Size > 0 then
  1230. begin
  1231. MemStream.position := 0;
  1232. {$IFDEF UNICODE}
  1233. Buffer := AnsiStralloc(MemStream.Size + 1);
  1234. {$ELSE}
  1235. Buffer := Stralloc(MemStream.Size + 1);
  1236. {$ENDIF}
  1237. MemStream.readbuffer(Buffer[0], MemStream.Size);
  1238. (Buffer + MemStream.Size)^ := chr(0);
  1239. SetString(Result, Buffer, MemStream.size);
  1240. strdispose(Buffer);
  1241. end;
  1242. finally
  1243. MemStream.Free;
  1244. end
  1245. end;
  1246. function TSQLiteUniTable.FieldAsDouble(I: cardinal): double;
  1247. begin
  1248. Result := Sqlite3_ColumnDouble(fstmt, i);
  1249. end;
  1250. function TSQLiteUniTable.FieldAsInteger(I: cardinal): int64;
  1251. begin
  1252. Result := Sqlite3_ColumnInt64(fstmt, i);
  1253. end;
  1254. function TSQLiteUniTable.FieldAsString(I: cardinal): string;
  1255. begin
  1256. Result := self.GetFields(I);
  1257. end;
  1258. function TSQLiteUniTable.FieldIsNull(I: cardinal): boolean;
  1259. begin
  1260. Result := Sqlite3_ColumnText(fstmt, i) = nil;
  1261. end;
  1262. function TSQLiteUniTable.GetColumns(I: integer): string;
  1263. begin
  1264. Result := fCols[I];
  1265. end;
  1266. function TSQLiteUniTable.GetFieldByName(FieldName: string): string;
  1267. begin
  1268. Result := GetFields(self.GetFieldIndex(FieldName));
  1269. end;
  1270. function TSQLiteUniTable.GetFieldIndex(FieldName: string): integer;
  1271. begin
  1272. if (fCols = nil) then
  1273. begin
  1274. raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
  1275. exit;
  1276. end;
  1277. if (fCols.count = 0) then
  1278. begin
  1279. raise ESqliteException.Create('Field ' + fieldname + ' Not found. Empty dataset');
  1280. exit;
  1281. end;
  1282. Result := fCols.IndexOf(AnsiUpperCase(FieldName));
  1283. if (result < 0) then
  1284. begin
  1285. raise ESqliteException.Create('Field not found in dataset: ' + fieldname)
  1286. end;
  1287. end;
  1288. function TSQLiteUniTable.GetFields(I: cardinal): string;
  1289. begin
  1290. Result := Sqlite3_ColumnText(fstmt, i);
  1291. end;
  1292. function TSQLiteUniTable.Next: boolean;
  1293. var
  1294. iStepResult: integer;
  1295. begin
  1296. fEOF := true;
  1297. iStepResult := Sqlite3_step(fStmt);
  1298. case iStepResult of
  1299. SQLITE_ROW:
  1300. begin
  1301. fEOF := false;
  1302. inc(fRow);
  1303. end;
  1304. SQLITE_DONE:
  1305. // we are on the end of dataset
  1306. // return EOF=true only
  1307. ;
  1308. else
  1309. begin
  1310. SQLite3_reset(fStmt);
  1311. fDB.RaiseError('Could not retrieve data', fSQL);
  1312. end;
  1313. end;
  1314. Result := not fEOF;
  1315. end;
  1316. end.