sqliteds.pas 12 KB

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