sqlite3ds.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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 program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU Lesser General Public License as published by
  8. the Free Software Foundation; either version 2.1 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU Lesser General Public License for more details.
  14. You should have received a copy of the GNU Lesser General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. }
  18. {$mode objfpc}
  19. {$H+}
  20. { $Define DEBUG}
  21. interface
  22. uses
  23. Classes, SysUtils, customsqliteds;
  24. type
  25. { TSqlite3Dataset }
  26. TSqlite3Dataset = class (TCustomSqliteDataset)
  27. private
  28. function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
  29. function GetSqliteHandle: Pointer; override;
  30. function GetSqliteVersion: String; override;
  31. procedure SqliteClose(AHandle: Pointer);override;
  32. procedure BuildLinkedList; override;
  33. protected
  34. procedure InternalInitFieldDefs; override;
  35. public
  36. function SqliteReturnString: String; override;
  37. function TableExists: Boolean;override;
  38. function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
  39. end;
  40. implementation
  41. uses
  42. sqlite3,db;
  43. function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  44. var
  45. CodeError, TempInt: Integer;
  46. begin
  47. TempInt:=-1;
  48. if ColumnValues[0] <> nil then
  49. begin
  50. Val(StrPas(ColumnValues[0]),TempInt,CodeError);
  51. if CodeError <> 0 then
  52. DatabaseError('SqliteDs - Error trying to get last autoinc value');
  53. end;
  54. Integer(NextValue^):=Succ(TempInt);
  55. Result:=1;
  56. end;
  57. { TSqlite3Dataset }
  58. function TSqlite3Dataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
  59. begin
  60. Result:=sqlite3_exec(AHandle, ASql, nil, nil, nil);
  61. end;
  62. procedure TSqlite3Dataset.SqliteClose(AHandle: Pointer);
  63. begin
  64. sqlite3_close(AHandle);
  65. //todo:handle return data
  66. end;
  67. function TSqlite3Dataset.GetSqliteHandle: Pointer;
  68. begin
  69. FSqliteReturnId:=sqlite3_open(PChar(FFileName),@Result);
  70. end;
  71. procedure TSqlite3Dataset.InternalInitFieldDefs;
  72. var
  73. vm:Pointer;
  74. ColumnStr:String;
  75. Counter,FieldSize:Integer;
  76. AType:TFieldType;
  77. begin
  78. FieldDefs.Clear;
  79. sqlite3_prepare(FSqliteHandle,PChar(FSql),-1,@vm,nil);
  80. sqlite3_step(vm);
  81. for Counter:= 0 to sqlite3_column_count(vm) - 1 do
  82. begin
  83. ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,Counter)));
  84. if (ColumnStr = 'INTEGER') then
  85. begin
  86. AType:= ftInteger;
  87. FieldSize:=SizeOf(Integer);
  88. end else if (ColumnStr = 'BOOLEAN') then
  89. begin
  90. AType:= ftBoolean;
  91. FieldSize:=SizeOf(Boolean);
  92. end else if (ColumnStr = 'FLOAT') then
  93. begin
  94. AType:= ftFloat;
  95. FieldSize:=SizeOf(Double);
  96. end else if (ColumnStr = 'WORD') then
  97. begin
  98. AType:= ftWord;
  99. FieldSize:=SizeOf(Word);
  100. end else if (ColumnStr = 'DATETIME') then
  101. begin
  102. AType:= ftDateTime;
  103. FieldSize:=SizeOf(TDateTime);
  104. end else if (ColumnStr = 'DATE') then
  105. begin
  106. AType:= ftDate;
  107. FieldSize:=SizeOf(TDateTime);
  108. end else if (ColumnStr = 'TIME') then
  109. begin
  110. AType:= ftTime;
  111. FieldSize:=SizeOf(TDateTime);
  112. end else if (ColumnStr = 'MEMO') then
  113. begin
  114. AType:= ftMemo;
  115. FieldSize:=10;//??
  116. end else if (ColumnStr = 'AUTOINC') then
  117. begin
  118. AType:= ftAutoInc;
  119. FieldSize:=SizeOf(Integer);
  120. if FAutoIncFieldNo = -1 then
  121. FAutoIncFieldNo:= Counter;
  122. end else
  123. begin
  124. AType:= ftString;
  125. FieldSize:=10; //??
  126. end;
  127. FieldDefs.Add(StrPas(sqlite3_column_name(vm,Counter)), AType, FieldSize, False);
  128. {$ifdef DEBUG}
  129. writeln('Field Name: ',sqlite3_column_name(vm,Counter));
  130. writeln('Field Type: ',sqlite3_column_decltype(vm,Counter));
  131. {$endif}
  132. end;
  133. sqlite3_finalize(vm);
  134. FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
  135. {$ifdef DEBUG}
  136. writeln('FieldDefs.Count: ',FieldDefs.Count);
  137. {$endif}
  138. end;
  139. procedure TSqlite3Dataset.BuildLinkedList;
  140. var
  141. TempItem:PDataRecord;
  142. vm:Pointer;
  143. Counter:Integer;
  144. begin
  145. //Get AutoInc Field initial value
  146. if FAutoIncFieldNo <> -1 then
  147. sqlite3_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
  148. @GetAutoIncValue,@FNextAutoInc,nil);
  149. FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(FSql),-1,@vm,nil);
  150. if FSqliteReturnId <> SQLITE_OK then
  151. case FSqliteReturnId of
  152. SQLITE_ERROR:
  153. DatabaseError('Invalid SQL',Self);
  154. else
  155. DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
  156. end;
  157. FDataAllocated:=True;
  158. TempItem:=FBeginItem;
  159. FRecordCount:=0;
  160. FRowCount:=sqlite3_column_count(vm);
  161. FSqliteReturnId:=sqlite3_step(vm);
  162. while FSqliteReturnId = SQLITE_ROW do
  163. begin
  164. Inc(FRecordCount);
  165. New(TempItem^.Next);
  166. TempItem^.Next^.Previous:=TempItem;
  167. TempItem:=TempItem^.Next;
  168. GetMem(TempItem^.Row,FRowBufferSize);
  169. For Counter := 0 to FRowCount - 1 do
  170. TempItem^.Row[Counter]:=StrNew(sqlite3_column_text(vm,Counter));
  171. FSqliteReturnId:=sqlite3_step(vm);
  172. end;
  173. sqlite3_finalize(vm);
  174. // Attach EndItem
  175. TempItem^.Next:=FEndItem;
  176. FEndItem^.Previous:=TempItem;
  177. // Alloc item used in append/insert
  178. GetMem(FCacheItem^.Row,FRowBufferSize);
  179. for Counter := 0 to FRowCount - 1 do
  180. FCacheItem^.Row[Counter]:=nil;
  181. // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
  182. GetMem(FBeginItem^.Row,FRowBufferSize);
  183. for Counter := 0 to FRowCount - 1 do
  184. FBeginItem^.Row[Counter]:=nil;
  185. end;
  186. function TSqlite3Dataset.TableExists: Boolean;
  187. var
  188. AHandle,vm:Pointer;
  189. begin
  190. Result:=False;
  191. if not (FTableName = '') and FileExists(FFileName) then
  192. begin
  193. if FSqliteHandle = nil then
  194. begin
  195. {$ifdef DEBUG}
  196. writeln('TableExists - FSqliteHandle=nil : Opening a file');
  197. {$endif}
  198. AHandle:=GetSqliteHandle;
  199. end
  200. else
  201. begin
  202. {$ifdef DEBUG}
  203. writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
  204. {$endif}
  205. AHandle:=FSqliteHandle;
  206. end;
  207. FSqliteReturnId:=sqlite3_prepare(AHandle,
  208. Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
  209. -1,@vm,nil);
  210. {$ifdef DEBUG}
  211. WriteLn('TableExists.sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
  212. {$endif}
  213. FSqliteReturnId:=sqlite3_step(vm);
  214. {$ifdef DEBUG}
  215. WriteLn('TableExists.sqlite3_step - SqliteReturnString:',SqliteReturnString);
  216. {$endif}
  217. Result:=FSqliteReturnId = SQLITE_ROW;
  218. sqlite3_finalize(vm);
  219. if (FSqliteHandle = nil) then
  220. sqlite3_close(AHandle);
  221. end;
  222. {$ifdef DEBUG}
  223. WriteLn('TableExists ('+FTableName+') Result:',Result);
  224. {$endif}
  225. end;
  226. function TSqlite3Dataset.SqliteReturnString: String;
  227. begin
  228. case FSqliteReturnId of
  229. SQLITE_OK : Result := 'SQLITE_OK ';
  230. SQLITE_ERROR : Result := 'SQLITE_ERROR ';
  231. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
  232. SQLITE_PERM : Result := 'SQLITE_PERM ';
  233. SQLITE_ABORT : Result := 'SQLITE_ABORT ';
  234. SQLITE_BUSY : Result := 'SQLITE_BUSY ';
  235. SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
  236. SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
  237. SQLITE_READONLY : Result := 'SQLITE_READONLY ';
  238. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
  239. SQLITE_IOERR : Result := 'SQLITE_IOERR ';
  240. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
  241. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
  242. SQLITE_FULL : Result := 'SQLITE_FULL ';
  243. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
  244. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
  245. SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
  246. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
  247. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
  248. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
  249. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
  250. SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
  251. SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
  252. SQLITE_AUTH : Result := 'SQLITE_AUTH ';
  253. SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
  254. SQLITE_RANGE : Result := 'SQLITE_RANGE ';
  255. SQLITE_ROW : Result := 'SQLITE_ROW ';
  256. SQLITE_NOTADB : Result := 'SQLITE_NOTADB ';
  257. SQLITE_DONE : Result := 'SQLITE_DONE ';
  258. else
  259. Result:='Unknow Return Value';
  260. end;
  261. end;
  262. function TSqlite3Dataset.GetSqliteVersion: String;
  263. begin
  264. Result:=StrPas(sqlite3_version);
  265. end;
  266. function TSqlite3Dataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
  267. var
  268. vm,AHandle:Pointer;
  269. procedure FillStrings;
  270. begin
  271. while FSqliteReturnId = SQLITE_ROW do
  272. begin
  273. AStrList.Add(StrPas(sqlite3_column_text(vm,0)));
  274. FSqliteReturnId:=sqlite3_step(vm);
  275. end;
  276. end;
  277. procedure FillStringsAndObjects;
  278. begin
  279. while FSqliteReturnId = SQLITE_ROW do
  280. begin
  281. AStrList.AddObject(StrPas(sqlite3_column_text(vm,0)),TObject(PtrInt(sqlite3_column_int(vm,1))));
  282. FSqliteReturnId:=sqlite3_step(vm);
  283. end;
  284. end;
  285. begin
  286. if FSqliteHandle <> nil then
  287. AHandle:=FSqliteHandle
  288. else
  289. if FileExists(FFileName) then
  290. AHandle:=GetSqliteHandle
  291. else
  292. DatabaseError('File "'+FFileName+'" not Exists',Self);
  293. Result:='';
  294. // It's up to the caller clear or not the list
  295. //if AStrList <> nil then
  296. // AStrList.Clear;
  297. FSqliteReturnId:=sqlite3_prepare(AHandle,Pchar(ASql),-1,@vm,nil);
  298. if FSqliteReturnId <> SQLITE_OK then
  299. DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
  300. FSqliteReturnId:=sqlite3_step(vm);
  301. if (FSqliteReturnId = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
  302. begin
  303. Result:=StrPas(sqlite3_column_text(vm,0));
  304. if AStrList <> nil then
  305. begin
  306. if FillObjects and (sqlite3_column_count(vm) > 1) then
  307. FillStringsAndObjects
  308. else
  309. FillStrings;
  310. end;
  311. end;
  312. sqlite3_finalize(vm);
  313. if FSqliteHandle = nil then
  314. sqlite3_close(AHandle);
  315. end;
  316. end.