sqliteds.pas 11 KB

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