sqlite3ds.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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(LongInt);
  88. end else if (ColumnStr = 'VARCHAR') then
  89. begin
  90. AType:= ftString;
  91. FieldSize:=10;//??
  92. end else if (ColumnStr = 'BOOLEAN') then
  93. begin
  94. AType:= ftBoolean;
  95. FieldSize:=SizeOf(Boolean);
  96. end else if (ColumnStr = 'FLOAT') then
  97. begin
  98. AType:= ftFloat;
  99. FieldSize:=SizeOf(Double);
  100. end else if (ColumnStr = 'WORD') then
  101. begin
  102. AType:= ftWord;
  103. FieldSize:=SizeOf(Word);
  104. end else if (ColumnStr = 'DATETIME') then
  105. begin
  106. AType:= ftDateTime;
  107. FieldSize:=SizeOf(TDateTime);
  108. end else if (ColumnStr = 'DATE') then
  109. begin
  110. AType:= ftDate;
  111. FieldSize:=SizeOf(TDateTime);
  112. end else if (ColumnStr = 'LARGEINT') then
  113. begin
  114. AType:= ftLargeInt;
  115. FieldSize:=SizeOf(Int64);
  116. end else if (ColumnStr = 'CURRENCY') then
  117. begin
  118. AType:= ftCurrency;
  119. FieldSize:=SizeOf(Double);
  120. end else if (ColumnStr = 'TIME') then
  121. begin
  122. AType:= ftTime;
  123. FieldSize:=SizeOf(TDateTime);
  124. end else if (ColumnStr = 'MEMO') then
  125. begin
  126. AType:= ftMemo;
  127. FieldSize:=10;//??
  128. end else if (ColumnStr = 'AUTOINC') then
  129. begin
  130. AType:= ftAutoInc;
  131. FieldSize:=SizeOf(Integer);
  132. if FAutoIncFieldNo = -1 then
  133. FAutoIncFieldNo:= Counter;
  134. end else
  135. begin
  136. DatabaseError('Field type "'+ColumnStr+'" not recognized',Self);
  137. end;
  138. FieldDefs.Add(StrPas(sqlite3_column_name(vm,Counter)), AType, FieldSize, False);
  139. {$ifdef DEBUG}
  140. writeln('Field Name: ',sqlite3_column_name(vm,Counter));
  141. writeln('Field Type: ',sqlite3_column_decltype(vm,Counter));
  142. {$endif}
  143. end;
  144. sqlite3_finalize(vm);
  145. FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
  146. {$ifdef DEBUG}
  147. writeln('FieldDefs.Count: ',FieldDefs.Count);
  148. {$endif}
  149. end;
  150. procedure TSqlite3Dataset.BuildLinkedList;
  151. var
  152. TempItem:PDataRecord;
  153. vm:Pointer;
  154. Counter:Integer;
  155. begin
  156. //Get AutoInc Field initial value
  157. if FAutoIncFieldNo <> -1 then
  158. sqlite3_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
  159. @GetAutoIncValue,@FNextAutoInc,nil);
  160. FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(FSql),-1,@vm,nil);
  161. if FSqliteReturnId <> SQLITE_OK then
  162. case FSqliteReturnId of
  163. SQLITE_ERROR:
  164. DatabaseError('Invalid SQL',Self);
  165. else
  166. DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
  167. end;
  168. FDataAllocated:=True;
  169. TempItem:=FBeginItem;
  170. FRecordCount:=0;
  171. FRowCount:=sqlite3_column_count(vm);
  172. FSqliteReturnId:=sqlite3_step(vm);
  173. while FSqliteReturnId = SQLITE_ROW do
  174. begin
  175. Inc(FRecordCount);
  176. New(TempItem^.Next);
  177. TempItem^.Next^.Previous:=TempItem;
  178. TempItem:=TempItem^.Next;
  179. GetMem(TempItem^.Row,FRowBufferSize);
  180. For Counter := 0 to FRowCount - 1 do
  181. TempItem^.Row[Counter]:=StrNew(sqlite3_column_text(vm,Counter));
  182. FSqliteReturnId:=sqlite3_step(vm);
  183. end;
  184. sqlite3_finalize(vm);
  185. // Attach EndItem
  186. TempItem^.Next:=FEndItem;
  187. FEndItem^.Previous:=TempItem;
  188. // Alloc item used in append/insert
  189. GetMem(FCacheItem^.Row,FRowBufferSize);
  190. for Counter := 0 to FRowCount - 1 do
  191. FCacheItem^.Row[Counter]:=nil;
  192. // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
  193. GetMem(FBeginItem^.Row,FRowBufferSize);
  194. for Counter := 0 to FRowCount - 1 do
  195. FBeginItem^.Row[Counter]:=nil;
  196. end;
  197. function TSqlite3Dataset.TableExists: Boolean;
  198. var
  199. AHandle,vm:Pointer;
  200. begin
  201. Result:=False;
  202. if not (FTableName = '') and FileExists(FFileName) then
  203. begin
  204. if FSqliteHandle = nil then
  205. begin
  206. {$ifdef DEBUG}
  207. writeln('TableExists - FSqliteHandle=nil : Opening a file');
  208. {$endif}
  209. AHandle:=GetSqliteHandle;
  210. end
  211. else
  212. begin
  213. {$ifdef DEBUG}
  214. writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
  215. {$endif}
  216. AHandle:=FSqliteHandle;
  217. end;
  218. FSqliteReturnId:=sqlite3_prepare(AHandle,
  219. Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
  220. -1,@vm,nil);
  221. {$ifdef DEBUG}
  222. WriteLn('TableExists.sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
  223. {$endif}
  224. FSqliteReturnId:=sqlite3_step(vm);
  225. {$ifdef DEBUG}
  226. WriteLn('TableExists.sqlite3_step - SqliteReturnString:',SqliteReturnString);
  227. {$endif}
  228. Result:=FSqliteReturnId = SQLITE_ROW;
  229. sqlite3_finalize(vm);
  230. if (FSqliteHandle = nil) then
  231. sqlite3_close(AHandle);
  232. end;
  233. {$ifdef DEBUG}
  234. WriteLn('TableExists ('+FTableName+') Result:',Result);
  235. {$endif}
  236. end;
  237. function TSqlite3Dataset.SqliteReturnString: String;
  238. begin
  239. case FSqliteReturnId of
  240. SQLITE_OK : Result := 'SQLITE_OK ';
  241. SQLITE_ERROR : Result := 'SQLITE_ERROR ';
  242. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
  243. SQLITE_PERM : Result := 'SQLITE_PERM ';
  244. SQLITE_ABORT : Result := 'SQLITE_ABORT ';
  245. SQLITE_BUSY : Result := 'SQLITE_BUSY ';
  246. SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
  247. SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
  248. SQLITE_READONLY : Result := 'SQLITE_READONLY ';
  249. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
  250. SQLITE_IOERR : Result := 'SQLITE_IOERR ';
  251. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
  252. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
  253. SQLITE_FULL : Result := 'SQLITE_FULL ';
  254. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
  255. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
  256. SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
  257. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
  258. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
  259. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
  260. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
  261. SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
  262. SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
  263. SQLITE_AUTH : Result := 'SQLITE_AUTH ';
  264. SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
  265. SQLITE_RANGE : Result := 'SQLITE_RANGE ';
  266. SQLITE_ROW : Result := 'SQLITE_ROW ';
  267. SQLITE_NOTADB : Result := 'SQLITE_NOTADB ';
  268. SQLITE_DONE : Result := 'SQLITE_DONE ';
  269. else
  270. Result:='Unknow Return Value';
  271. end;
  272. end;
  273. function TSqlite3Dataset.GetSqliteVersion: String;
  274. begin
  275. Result:=StrPas(sqlite3_version);
  276. end;
  277. function TSqlite3Dataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
  278. var
  279. vm,AHandle:Pointer;
  280. procedure FillStrings;
  281. begin
  282. while FSqliteReturnId = SQLITE_ROW do
  283. begin
  284. AStrList.Add(StrPas(sqlite3_column_text(vm,0)));
  285. FSqliteReturnId:=sqlite3_step(vm);
  286. end;
  287. end;
  288. procedure FillStringsAndObjects;
  289. begin
  290. while FSqliteReturnId = SQLITE_ROW do
  291. begin
  292. AStrList.AddObject(StrPas(sqlite3_column_text(vm,0)),TObject(PtrInt(sqlite3_column_int(vm,1))));
  293. FSqliteReturnId:=sqlite3_step(vm);
  294. end;
  295. end;
  296. begin
  297. if FSqliteHandle <> nil then
  298. AHandle:=FSqliteHandle
  299. else
  300. if FileExists(FFileName) then
  301. AHandle:=GetSqliteHandle
  302. else
  303. DatabaseError('File "'+FFileName+'" not Exists',Self);
  304. Result:='';
  305. // It's up to the caller clear or not the list
  306. //if AStrList <> nil then
  307. // AStrList.Clear;
  308. FSqliteReturnId:=sqlite3_prepare(AHandle,Pchar(ASql),-1,@vm,nil);
  309. if FSqliteReturnId <> SQLITE_OK then
  310. DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
  311. FSqliteReturnId:=sqlite3_step(vm);
  312. if (FSqliteReturnId = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
  313. begin
  314. Result:=StrPas(sqlite3_column_text(vm,0));
  315. if AStrList <> nil then
  316. begin
  317. if FillObjects and (sqlite3_column_count(vm) > 1) then
  318. FillStringsAndObjects
  319. else
  320. FillStrings;
  321. end;
  322. end;
  323. sqlite3_finalize(vm);
  324. if FSqliteHandle = nil then
  325. sqlite3_close(AHandle);
  326. end;
  327. end.