sqlite3ds.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. unit Sqlite3DS;
  2. {
  3. This is TSqlite3Dataset, a TDataset descendant class for use with fpc compiler
  4. Copyright (C) 2004 Luiz Américo Pereira Câmara
  5. Email: [email protected]
  6. This library is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU Library General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or (at your
  9. option) any later version with the following modification:
  10. As a special exception, the copyright holders of this library give you
  11. permission to link this library with independent modules to produce an
  12. executable, regardless of the license terms of these independent modules,and
  13. to copy and distribute the resulting executable under terms of your choice,
  14. provided that you also meet, for each linked independent module, the terms
  15. and conditions of the license of that module. An independent module is a
  16. module which is not derived from or based on this library. If you modify
  17. this library, you may extend this exception to your version of the library,
  18. but you are not obligated to do so. If you do not wish to do so, delete this
  19. exception statement from your version.
  20. This program is distributed in the hope that it will be useful, but WITHOUT
  21. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  22. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  23. for more details.
  24. You should have received a copy of the GNU Library General Public License
  25. along with this library; if not, write to the Free Software Foundation,
  26. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  27. }
  28. {$mode objfpc}
  29. {$H+}
  30. {.$Define DEBUG_SQLITEDS}
  31. interface
  32. uses
  33. Classes, SysUtils, CustomSqliteDS;
  34. type
  35. { TSqlite3Dataset }
  36. TSqlite3Dataset = class(TCustomSqliteDataset)
  37. protected
  38. procedure BuildLinkedList; override;
  39. function GetRowsAffected:Integer; override;
  40. procedure InternalCloseHandle; override;
  41. function InternalGetHandle: Pointer; override;
  42. procedure RetrieveFieldDefs; override;
  43. function SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer; override;
  44. public
  45. procedure ExecuteDirect(const ASQL: String); override;
  46. function QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects: Boolean): String; override;
  47. function ReturnString: String; override;
  48. class function SqliteVersion: String; override;
  49. end;
  50. implementation
  51. uses
  52. sqlite3, db, strutils;
  53. function SqliteCode2Str(Code: Integer): String;
  54. begin
  55. case Code of
  56. SQLITE_OK : Result := 'SQLITE_OK';
  57. SQLITE_ERROR : Result := 'SQLITE_ERROR';
  58. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL';
  59. SQLITE_PERM : Result := 'SQLITE_PERM';
  60. SQLITE_ABORT : Result := 'SQLITE_ABORT';
  61. SQLITE_BUSY : Result := 'SQLITE_BUSY';
  62. SQLITE_LOCKED : Result := 'SQLITE_LOCKED';
  63. SQLITE_NOMEM : Result := 'SQLITE_NOMEM';
  64. SQLITE_READONLY : Result := 'SQLITE_READONLY';
  65. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT';
  66. SQLITE_IOERR : Result := 'SQLITE_IOERR';
  67. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT';
  68. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND';
  69. SQLITE_FULL : Result := 'SQLITE_FULL';
  70. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN';
  71. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL';
  72. SQLITE_EMPTY : Result := 'SQLITE_EMPTY';
  73. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA';
  74. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG';
  75. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT';
  76. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH';
  77. SQLITE_MISUSE : Result := 'SQLITE_MISUSE';
  78. SQLITE_NOLFS : Result := 'SQLITE_NOLFS';
  79. SQLITE_AUTH : Result := 'SQLITE_AUTH';
  80. SQLITE_FORMAT : Result := 'SQLITE_FORMAT';
  81. SQLITE_RANGE : Result := 'SQLITE_RANGE';
  82. SQLITE_ROW : Result := 'SQLITE_ROW';
  83. SQLITE_NOTADB : Result := 'SQLITE_NOTADB';
  84. SQLITE_DONE : Result := 'SQLITE_DONE';
  85. else
  86. Result := 'Unknown Return Value';
  87. end;
  88. end;
  89. function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): Integer; cdecl;
  90. var
  91. CodeError, TempInt: Integer;
  92. begin
  93. TempInt := 0;
  94. if ColumnValues[0] <> nil then
  95. begin
  96. Val(String(ColumnValues[0]), TempInt, CodeError);
  97. if CodeError <> 0 then
  98. DatabaseError('TSqlite3Dataset: Error trying to get last autoinc value');
  99. end;
  100. Integer(NextValue^) := Succ(TempInt);
  101. Result := 1;
  102. end;
  103. { TSqlite3Dataset }
  104. function TSqlite3Dataset.SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer;
  105. begin
  106. Result := sqlite3_exec(FSqliteHandle, ASQL, ACallback, Data, nil);
  107. end;
  108. procedure TSqlite3Dataset.InternalCloseHandle;
  109. begin
  110. sqlite3_close(FSqliteHandle);
  111. FSqliteHandle := nil;
  112. //todo:handle return data
  113. end;
  114. function TSqlite3Dataset.InternalGetHandle: Pointer;
  115. const
  116. CheckFileSql = 'Select Name from sqlite_master LIMIT 1';
  117. var
  118. vm: Pointer;
  119. ErrorStr: String;
  120. begin
  121. sqlite3_open(PChar(FFileName), @Result);
  122. //sqlite3_open returns SQLITE_OK even for invalid files
  123. //do additional check here
  124. FReturnCode := sqlite3_prepare(Result, CheckFileSql, -1, @vm, nil);
  125. if FReturnCode <> SQLITE_OK then
  126. begin
  127. ErrorStr := SqliteCode2Str(FReturnCode) + ' - ' + sqlite3_errmsg(Result);
  128. sqlite3_close(Result);
  129. DatabaseError(ErrorStr, Self);
  130. end;
  131. sqlite3_finalize(vm);
  132. end;
  133. procedure TSqlite3Dataset.RetrieveFieldDefs;
  134. var
  135. vm: Pointer;
  136. ColumnStr: String;
  137. i, ColumnCount, DataSize: Integer;
  138. AType: TFieldType;
  139. begin
  140. {$ifdef DEBUG_SQLITEDS}
  141. WriteLn('##TSqlite3Dataset.InternalInitFieldDefs##');
  142. {$endif}
  143. FAutoIncFieldNo := -1;
  144. FieldDefs.Clear;
  145. FReturnCode := sqlite3_prepare(FSqliteHandle, PChar(FSQL), -1, @vm, nil);
  146. if FReturnCode <> SQLITE_OK then
  147. DatabaseError(ReturnString, Self);
  148. sqlite3_step(vm);
  149. ColumnCount := sqlite3_column_count(vm);
  150. //Prepare the array of pchar2sql functions
  151. SetLength(FGetSqlStr, ColumnCount);
  152. for i := 0 to ColumnCount - 1 do
  153. begin
  154. DataSize := 0;
  155. ColumnStr := UpperCase(String(sqlite3_column_decltype(vm, i)));
  156. if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
  157. begin
  158. if AutoIncrementKey and (UpperCase(String(sqlite3_column_name(vm, i))) = UpperCase(PrimaryKey)) then
  159. begin
  160. AType := ftAutoInc;
  161. FAutoIncFieldNo := i;
  162. end
  163. else
  164. AType := ftInteger;
  165. end else if Pos('VARCHAR', ColumnStr) = 1 then
  166. begin
  167. AType := ftString;
  168. DataSize := StrToIntDef(Trim(ExtractDelimited(2, ColumnStr, ['(', ')'])), DefaultStringSize);
  169. end else if Pos('BOOL', ColumnStr) = 1 then
  170. begin
  171. AType := ftBoolean;
  172. end else if Pos('AUTOINC', ColumnStr) = 1 then
  173. begin
  174. AType := ftAutoInc;
  175. if FAutoIncFieldNo = -1 then
  176. FAutoIncFieldNo := i;
  177. end else if (Pos('FLOAT', ColumnStr) = 1) or (Pos('NUMERIC', ColumnStr) = 1) then
  178. begin
  179. AType := ftFloat;
  180. end else if (ColumnStr = 'DATETIME') then
  181. begin
  182. AType := ftDateTime;
  183. end else if (ColumnStr = 'DATE') then
  184. begin
  185. AType := ftDate;
  186. end else if (ColumnStr = 'LARGEINT') then
  187. begin
  188. AType := ftLargeInt;
  189. end else if (ColumnStr = 'TIME') then
  190. begin
  191. AType := ftTime;
  192. end else if (ColumnStr = 'TEXT') then
  193. begin
  194. AType := ftMemo;
  195. end else if (ColumnStr = 'CURRENCY') then
  196. begin
  197. AType := ftCurrency;
  198. end else if (ColumnStr = 'WORD') then
  199. begin
  200. AType := ftWord;
  201. end else if (ColumnStr = '') then
  202. begin
  203. case sqlite3_column_type(vm, i) of
  204. SQLITE_INTEGER:
  205. AType := ftInteger;
  206. SQLITE_FLOAT:
  207. AType := ftFloat;
  208. else
  209. AType := ftString;
  210. end;
  211. end else
  212. begin
  213. AType := ftString;
  214. end;
  215. FieldDefs.Add(String(sqlite3_column_name(vm, i)), AType, DataSize);
  216. //Set the pchar2sql function
  217. if AType in [ftString, ftMemo] then
  218. FGetSqlStr[i] := @Char2SQLStr
  219. else
  220. FGetSqlStr[i] := @Num2SQLStr;
  221. {$ifdef DEBUG_SQLITEDS}
  222. WriteLn(' Field[', i, '] Name: ', sqlite3_column_name(vm, i));
  223. WriteLn(' Field[', i, '] Type: ', sqlite3_column_decltype(vm, i));
  224. {$endif}
  225. end;
  226. sqlite3_finalize(vm);
  227. {$ifdef DEBUG_SQLITEDS}
  228. WriteLn(' FieldDefs.Count: ', FieldDefs.Count);
  229. {$endif}
  230. end;
  231. function TSqlite3Dataset.GetRowsAffected: Integer;
  232. begin
  233. Result := sqlite3_changes(FSqliteHandle);
  234. end;
  235. procedure TSqlite3Dataset.ExecuteDirect(const ASQL: String);
  236. var
  237. vm: Pointer;
  238. begin
  239. FReturnCode := sqlite3_prepare(FSqliteHandle, Pchar(ASQL), -1, @vm, nil);
  240. if FReturnCode <> SQLITE_OK then
  241. DatabaseError(ReturnString, Self);
  242. FReturnCode := sqlite3_step(vm);
  243. sqlite3_finalize(vm);
  244. end;
  245. procedure TSqlite3Dataset.BuildLinkedList;
  246. var
  247. TempItem: PDataRecord;
  248. vm: Pointer;
  249. Counter, ColumnCount: Integer;
  250. begin
  251. //Get AutoInc Field initial value
  252. if FAutoIncFieldNo <> -1 then
  253. sqlite3_exec(FSqliteHandle, PChar('Select Max(' + Fields[FAutoIncFieldNo].FieldName +
  254. ') from ' + FTableName), @GetAutoIncValue, @FNextAutoInc, nil);
  255. FReturnCode := sqlite3_prepare(FSqliteHandle, PChar(FSQL), -1, @vm, nil);
  256. if FReturnCode <> SQLITE_OK then
  257. DatabaseError(ReturnString, Self);
  258. FDataAllocated := True;
  259. TempItem := FBeginItem;
  260. FRecordCount := 0;
  261. ColumnCount := sqlite3_column_count(vm);
  262. FRowCount := ColumnCount;
  263. //add extra rows for calculated fields
  264. if FCalcFieldList <> nil then
  265. Inc(FRowCount, FCalcFieldList.Count);
  266. FRowBufferSize := (SizeOf(PPChar) * FRowCount);
  267. FReturnCode := sqlite3_step(vm);
  268. while FReturnCode = SQLITE_ROW do
  269. begin
  270. Inc(FRecordCount);
  271. New(TempItem^.Next);
  272. TempItem^.Next^.Previous := TempItem;
  273. TempItem := TempItem^.Next;
  274. GetMem(TempItem^.Row, FRowBufferSize);
  275. for Counter := 0 to ColumnCount - 1 do
  276. TempItem^.Row[Counter] := StrNew(sqlite3_column_text(vm, Counter));
  277. //initialize calculated fields with nil
  278. for Counter := ColumnCount to FRowCount - 1 do
  279. TempItem^.Row[Counter] := nil;
  280. FReturnCode := sqlite3_step(vm);
  281. end;
  282. sqlite3_finalize(vm);
  283. // Attach EndItem
  284. TempItem^.Next := FEndItem;
  285. FEndItem^.Previous := TempItem;
  286. // Alloc temporary item used in append/insert
  287. GetMem(FCacheItem^.Row, FRowBufferSize);
  288. for Counter := 0 to FRowCount - 1 do
  289. FCacheItem^.Row[Counter] := nil;
  290. // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
  291. GetMem(FBeginItem^.Row, FRowBufferSize);
  292. //Todo: see if is better to nullif using FillDWord
  293. for Counter := 0 to FRowCount - 1 do
  294. FBeginItem^.Row[Counter] := nil;
  295. end;
  296. function TSqlite3Dataset.ReturnString: String;
  297. begin
  298. Result := SqliteCode2Str(FReturnCode) + ' - ' + sqlite3_errmsg(FSqliteHandle);
  299. end;
  300. class function TSqlite3Dataset.SqliteVersion: String;
  301. begin
  302. Result := String(sqlite3_version());
  303. end;
  304. function TSqlite3Dataset.QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects:Boolean): String;
  305. var
  306. vm: Pointer;
  307. procedure FillStrings;
  308. begin
  309. while FReturnCode = SQLITE_ROW do
  310. begin
  311. AStrList.Add(String(sqlite3_column_text(vm,0)));
  312. FReturnCode := sqlite3_step(vm);
  313. end;
  314. end;
  315. procedure FillStringsAndObjects;
  316. begin
  317. while FReturnCode = SQLITE_ROW do
  318. begin
  319. AStrList.AddObject(String(sqlite3_column_text(vm, 0)),
  320. TObject(PtrInt(sqlite3_column_int(vm, 1))));
  321. FReturnCode := sqlite3_step(vm);
  322. end;
  323. end;
  324. begin
  325. if FSqliteHandle = nil then
  326. GetSqliteHandle;
  327. Result := '';
  328. FReturnCode := sqlite3_prepare(FSqliteHandle,Pchar(ASQL), -1, @vm, nil);
  329. if FReturnCode <> SQLITE_OK then
  330. DatabaseError(ReturnString, Self);
  331. FReturnCode := sqlite3_step(vm);
  332. if (FReturnCode = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
  333. begin
  334. Result := String(sqlite3_column_text(vm, 0));
  335. if AStrList <> nil then
  336. begin
  337. if FillObjects and (sqlite3_column_count(vm) > 1) then
  338. FillStringsAndObjects
  339. else
  340. FillStrings;
  341. end;
  342. end;
  343. sqlite3_finalize(vm);
  344. end;
  345. end.