sqlite3ds.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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}
  31. interface
  32. uses
  33. Classes, SysUtils, customsqliteds;
  34. type
  35. { TSqlite3Dataset }
  36. TSqlite3Dataset = class (TCustomSqliteDataset)
  37. private
  38. function SqliteExec(ASql:PChar; ACallback: TSqliteCdeclCallback; Data: Pointer):Integer;override;
  39. function InternalGetHandle: Pointer; override;
  40. function GetSqliteVersion: String; override;
  41. procedure InternalCloseHandle;override;
  42. procedure BuildLinkedList; override;
  43. protected
  44. procedure InternalInitFieldDefs; override;
  45. function GetRowsAffected:Integer; override;
  46. public
  47. procedure ExecuteDirect(const ASql: String);override;
  48. function ReturnString: String; override;
  49. function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
  50. end;
  51. implementation
  52. uses
  53. sqlite3,db;
  54. function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  55. var
  56. CodeError, TempInt: Integer;
  57. begin
  58. TempInt:=-1;
  59. if ColumnValues[0] <> nil then
  60. begin
  61. Val(StrPas(ColumnValues[0]),TempInt,CodeError);
  62. if CodeError <> 0 then
  63. DatabaseError('SqliteDs - Error trying to get last autoinc value');
  64. end;
  65. Integer(NextValue^):=Succ(TempInt);
  66. Result:=1;
  67. end;
  68. { TSqlite3Dataset }
  69. function TSqlite3Dataset.SqliteExec(ASql: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer;
  70. begin
  71. Result:=sqlite3_exec(FSqliteHandle, ASql, ACallback, Data, nil);
  72. end;
  73. procedure TSqlite3Dataset.InternalCloseHandle;
  74. begin
  75. sqlite3_close(FSqliteHandle);
  76. FSqliteHandle:=nil;
  77. //todo:handle return data
  78. end;
  79. function TSqlite3Dataset.InternalGetHandle: Pointer;
  80. begin
  81. FReturnCode:=sqlite3_open(PChar(FFileName),@Result);
  82. end;
  83. procedure TSqlite3Dataset.InternalInitFieldDefs;
  84. var
  85. vm:Pointer;
  86. ColumnStr:String;
  87. i,ColumnCount,FieldSize:Integer;
  88. AType:TFieldType;
  89. begin
  90. {$ifdef DEBUG}
  91. WriteLn('##TSqlite3Dataset.InternalInitFieldDefs##');
  92. {$endif}
  93. FAutoIncFieldNo:=-1;
  94. FieldDefs.Clear;
  95. sqlite3_prepare(FSqliteHandle,PChar(FSql),-1,@vm,nil);
  96. sqlite3_step(vm);
  97. ColumnCount:=sqlite3_column_count(vm);
  98. //Set BufferSize
  99. FRowBufferSize:=(SizeOf(PPChar)*ColumnCount);
  100. //Prepare the array of pchar2sql functions
  101. SetLength(FGetSqlStr,ColumnCount);
  102. for i:= 0 to ColumnCount - 1 do
  103. begin
  104. ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,i)));
  105. if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
  106. begin
  107. if AutoIncrementKey and (UpperCase(StrPas(sqlite3_column_name(vm,i))) = UpperCase(PrimaryKey)) then
  108. begin
  109. AType:= ftAutoInc;
  110. FAutoIncFieldNo:=i;
  111. end
  112. else
  113. AType:= ftInteger;
  114. FieldSize:=SizeOf(LongInt);
  115. end else if Pos('VARCHAR',ColumnStr) = 1 then
  116. begin
  117. AType:= ftString;
  118. FieldSize:=0;
  119. end else if Pos('BOOL',ColumnStr) = 1 then
  120. begin
  121. AType:= ftBoolean;
  122. FieldSize:=SizeOf(WordBool);
  123. end else if Pos('AUTOINC',ColumnStr) = 1 then
  124. begin
  125. AType:= ftAutoInc;
  126. FieldSize:=SizeOf(LongInt);
  127. if FAutoIncFieldNo = -1 then
  128. FAutoIncFieldNo:= i;
  129. end else if (Pos('FLOAT',ColumnStr)=1) or (Pos('NUMERIC',ColumnStr)=1) then
  130. begin
  131. AType:= ftFloat;
  132. FieldSize:=SizeOf(Double);
  133. end else if (ColumnStr = 'DATETIME') then
  134. begin
  135. AType:= ftDateTime;
  136. FieldSize:=SizeOf(TDateTime);
  137. end else if (ColumnStr = 'DATE') then
  138. begin
  139. AType:= ftDate;
  140. FieldSize:=SizeOf(TDateTime);
  141. end else if (ColumnStr = 'LARGEINT') then
  142. begin
  143. AType:= ftLargeInt;
  144. FieldSize:=SizeOf(Int64);
  145. end else if (ColumnStr = 'TIME') then
  146. begin
  147. AType:= ftTime;
  148. FieldSize:=SizeOf(TDateTime);
  149. end else if (ColumnStr = 'TEXT') then
  150. begin
  151. AType:= ftMemo;
  152. FieldSize:=0;
  153. end else if (ColumnStr = 'CURRENCY') then
  154. begin
  155. AType:= ftCurrency;
  156. FieldSize:=SizeOf(Double);
  157. end else if (ColumnStr = 'WORD') then
  158. begin
  159. AType:= ftWord;
  160. FieldSize:=SizeOf(Word);
  161. end else
  162. begin
  163. AType:= ftString;
  164. FieldSize:=0;
  165. end;
  166. FieldDefs.Add(StrPas(sqlite3_column_name(vm,i)), AType, FieldSize, False);
  167. //Set the pchar2sql function
  168. if AType in [ftString,ftMemo] then
  169. FGetSqlStr[i]:=@Char2SqlStr
  170. else
  171. FGetSqlStr[i]:=@Num2SqlStr;
  172. {$ifdef DEBUG}
  173. writeln(' Field[',i,'] Name: ',sqlite3_column_name(vm,i));
  174. writeln(' Field[',i,'] Type: ',sqlite3_column_decltype(vm,i));
  175. {$endif}
  176. end;
  177. sqlite3_finalize(vm);
  178. {$ifdef DEBUG}
  179. writeln(' FieldDefs.Count: ',FieldDefs.Count);
  180. {$endif}
  181. end;
  182. function TSqlite3Dataset.GetRowsAffected: Integer;
  183. begin
  184. Result:=sqlite3_changes(FSqliteHandle);
  185. end;
  186. procedure TSqlite3Dataset.ExecuteDirect(const ASql: String);
  187. var
  188. vm:Pointer;
  189. begin
  190. FReturnCode:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
  191. if FReturnCode <> SQLITE_OK then
  192. DatabaseError(ReturnString,Self);
  193. FReturnCode:=sqlite3_step(vm);
  194. sqlite3_finalize(vm);
  195. end;
  196. procedure TSqlite3Dataset.BuildLinkedList;
  197. var
  198. TempItem:PDataRecord;
  199. vm:Pointer;
  200. Counter:Integer;
  201. begin
  202. //Get AutoInc Field initial value
  203. if FAutoIncFieldNo <> -1 then
  204. sqlite3_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
  205. @GetAutoIncValue,@FNextAutoInc,nil);
  206. FReturnCode:=sqlite3_prepare(FSqliteHandle,Pchar(FSql),-1,@vm,nil);
  207. if FReturnCode <> SQLITE_OK then
  208. DatabaseError(ReturnString,Self);
  209. FDataAllocated:=True;
  210. TempItem:=FBeginItem;
  211. FRecordCount:=0;
  212. FRowCount:=sqlite3_column_count(vm);
  213. FReturnCode:=sqlite3_step(vm);
  214. while FReturnCode = SQLITE_ROW do
  215. begin
  216. Inc(FRecordCount);
  217. New(TempItem^.Next);
  218. TempItem^.Next^.Previous:=TempItem;
  219. TempItem:=TempItem^.Next;
  220. GetMem(TempItem^.Row,FRowBufferSize);
  221. for Counter := 0 to FRowCount - 1 do
  222. TempItem^.Row[Counter]:=StrNew(sqlite3_column_text(vm,Counter));
  223. FReturnCode:=sqlite3_step(vm);
  224. end;
  225. sqlite3_finalize(vm);
  226. // Attach EndItem
  227. TempItem^.Next:=FEndItem;
  228. FEndItem^.Previous:=TempItem;
  229. // Alloc temporary item used in append/insert
  230. GetMem(FCacheItem^.Row,FRowBufferSize);
  231. for Counter := 0 to FRowCount - 1 do
  232. FCacheItem^.Row[Counter]:=nil;
  233. // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
  234. GetMem(FBeginItem^.Row,FRowBufferSize);
  235. //Todo: see if is better to nullif using FillDWord
  236. for Counter := 0 to FRowCount - 1 do
  237. FBeginItem^.Row[Counter]:=nil;
  238. end;
  239. function TSqlite3Dataset.ReturnString: String;
  240. begin
  241. case FReturnCode of
  242. SQLITE_OK : Result := 'SQLITE_OK';
  243. SQLITE_ERROR : Result := 'SQLITE_ERROR';
  244. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL';
  245. SQLITE_PERM : Result := 'SQLITE_PERM';
  246. SQLITE_ABORT : Result := 'SQLITE_ABORT';
  247. SQLITE_BUSY : Result := 'SQLITE_BUSY';
  248. SQLITE_LOCKED : Result := 'SQLITE_LOCKED';
  249. SQLITE_NOMEM : Result := 'SQLITE_NOMEM';
  250. SQLITE_READONLY : Result := 'SQLITE_READONLY';
  251. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT';
  252. SQLITE_IOERR : Result := 'SQLITE_IOERR';
  253. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT';
  254. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND';
  255. SQLITE_FULL : Result := 'SQLITE_FULL';
  256. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN';
  257. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL';
  258. SQLITE_EMPTY : Result := 'SQLITE_EMPTY';
  259. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA';
  260. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG';
  261. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT';
  262. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH';
  263. SQLITE_MISUSE : Result := 'SQLITE_MISUSE';
  264. SQLITE_NOLFS : Result := 'SQLITE_NOLFS';
  265. SQLITE_AUTH : Result := 'SQLITE_AUTH';
  266. SQLITE_FORMAT : Result := 'SQLITE_FORMAT';
  267. SQLITE_RANGE : Result := 'SQLITE_RANGE';
  268. SQLITE_ROW : Result := 'SQLITE_ROW';
  269. SQLITE_NOTADB : Result := 'SQLITE_NOTADB';
  270. SQLITE_DONE : Result := 'SQLITE_DONE';
  271. else
  272. Result:='Unknow Return Value';
  273. end;
  274. Result:=Result+' - '+sqlite3_errmsg(FSqliteHandle);
  275. end;
  276. function TSqlite3Dataset.GetSqliteVersion: String;
  277. begin
  278. Result:=StrPas(sqlite3_version());
  279. end;
  280. function TSqlite3Dataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
  281. var
  282. vm:Pointer;
  283. procedure FillStrings;
  284. begin
  285. while FReturnCode = SQLITE_ROW do
  286. begin
  287. AStrList.Add(StrPas(sqlite3_column_text(vm,0)));
  288. FReturnCode:=sqlite3_step(vm);
  289. end;
  290. end;
  291. procedure FillStringsAndObjects;
  292. begin
  293. while FReturnCode = SQLITE_ROW do
  294. begin
  295. AStrList.AddObject(StrPas(sqlite3_column_text(vm,0)),TObject(PtrInt(sqlite3_column_int(vm,1))));
  296. FReturnCode:=sqlite3_step(vm);
  297. end;
  298. end;
  299. begin
  300. if FSqliteHandle = nil then
  301. GetSqliteHandle;
  302. Result:='';
  303. FReturnCode:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
  304. if FReturnCode <> SQLITE_OK then
  305. DatabaseError(ReturnString,Self);
  306. FReturnCode:=sqlite3_step(vm);
  307. if (FReturnCode = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
  308. begin
  309. Result:=StrPas(sqlite3_column_text(vm,0));
  310. if AStrList <> nil then
  311. begin
  312. if FillObjects and (sqlite3_column_count(vm) > 1) then
  313. FillStringsAndObjects
  314. else
  315. FillStrings;
  316. end;
  317. end;
  318. sqlite3_finalize(vm);
  319. end;
  320. end.