sqliteds.pas 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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 GetSqliteHandle: Pointer; override;
  30. procedure SqliteClose(AHandle: Pointer);override;
  31. procedure BuildLinkedList; override;
  32. protected
  33. procedure InternalInitFieldDefs; override;
  34. public
  35. function SqliteReturnString: String; override;
  36. function TableExists: Boolean;override;
  37. end;
  38. implementation
  39. uses
  40. sqlite,db;
  41. var
  42. DummyAutoIncFieldNo:Integer;
  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. function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  58. var
  59. FieldSize:Word;
  60. Counter:Integer;
  61. AType:TFieldType;
  62. ColumnStr:String;
  63. begin
  64. // Sqlite is typeless (allows any type in any field)
  65. // regardless of what is in Create Table, but returns
  66. // exactly what is in Create Table statement
  67. // here is a trick to get the datatype.
  68. // If the field contains another type, there will be problems
  69. for Counter:= 0 to Columns - 1 do
  70. begin
  71. ColumnStr:= UpperCase(StrPas(ColumnNames[Counter + Columns]));
  72. if (ColumnStr = 'INTEGER') then
  73. begin
  74. AType:= ftInteger;
  75. FieldSize:=SizeOf(Integer);
  76. end else if (ColumnStr = 'BOOLEAN') then
  77. begin
  78. AType:= ftBoolean;
  79. FieldSize:=SizeOf(Boolean);
  80. end else if (ColumnStr = 'FLOAT') then
  81. begin
  82. AType:= ftFloat;
  83. FieldSize:=SizeOf(Double);
  84. end else if (ColumnStr = 'WORD') then
  85. begin
  86. AType:= ftWord;
  87. FieldSize:=SizeOf(Word);
  88. end else if (ColumnStr = 'DATETIME') then
  89. begin
  90. AType:= ftDateTime;
  91. FieldSize:=SizeOf(TDateTime);
  92. end else if (ColumnStr = 'DATE') then
  93. begin
  94. AType:= ftDate;
  95. FieldSize:=SizeOf(TDateTime);
  96. end else if (ColumnStr = 'TIME') then
  97. begin
  98. AType:= ftTime;
  99. FieldSize:=SizeOf(TDateTime);
  100. end else if (ColumnStr = 'MEMO') then
  101. begin
  102. AType:= ftMemo;
  103. FieldSize:=10;//??
  104. end else if (ColumnStr = 'AUTOINC') then
  105. begin
  106. AType:= ftAutoInc;
  107. FieldSize:=SizeOf(Integer);
  108. if DummyAutoIncFieldNo = -1 then
  109. DummyAutoIncFieldNo:= Counter;
  110. end else
  111. begin
  112. AType:= ftString;
  113. FieldSize:=10; //??
  114. end;
  115. TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
  116. end;
  117. result:=-1;
  118. end;
  119. { TSqliteDataset }
  120. function TSqliteDataset.SqliteExec(AHandle: Pointer; ASql: PChar): Integer;
  121. begin
  122. Result:=sqlite_exec(AHandle, ASql, nil, nil, nil);
  123. end;
  124. procedure TSqliteDataset.SqliteClose(AHandle: Pointer);
  125. begin
  126. sqlite_close(AHandle);
  127. end;
  128. function TSqliteDataset.GetSqliteHandle: Pointer;
  129. begin
  130. Result:=sqlite_open(PChar(FFileName),0,nil);
  131. end;
  132. procedure TSqliteDataset.InternalInitFieldDefs;
  133. begin
  134. FieldDefs.Clear;
  135. sqlite_exec(FSqliteHandle,PChar('PRAGMA empty_result_callbacks = ON;PRAGMA show_datatypes = ON;'),nil,nil,nil);
  136. DummyAutoIncFieldNo:=-1;
  137. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(FSql),@GetFieldDefs,Self,nil);
  138. FAutoIncFieldNo:=DummyAutoIncFieldNo;
  139. {
  140. if FSqliteReturnId <> SQLITE_ABORT then
  141. DatabaseError(SqliteReturnString,Self);
  142. }
  143. FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
  144. end;
  145. procedure TSqliteDataset.BuildLinkedList;
  146. var
  147. TempItem:PDataRecord;
  148. vm:Pointer;
  149. ColumnNames,ColumnValues:PPChar;
  150. Counter:Integer;
  151. begin
  152. //Get AutoInc Field initial value
  153. if FAutoIncFieldNo <> -1 then
  154. sqlite_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
  155. @GetAutoIncValue,@FNextAutoInc,nil);
  156. FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(FSql),nil,@vm,nil);
  157. if FSqliteReturnId <> SQLITE_OK then
  158. case FSqliteReturnId of
  159. SQLITE_ERROR:
  160. DatabaseError('Invalid SQL',Self);
  161. else
  162. DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
  163. end;
  164. FDataAllocated:=True;
  165. TempItem:=FBeginItem;
  166. FRecordCount:=0;
  167. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  168. while FSqliteReturnId = SQLITE_ROW do
  169. begin
  170. Inc(FRecordCount);
  171. New(TempItem^.Next);
  172. TempItem^.Next^.Previous:=TempItem;
  173. TempItem:=TempItem^.Next;
  174. GetMem(TempItem^.Row,FRowBufferSize);
  175. For Counter := 0 to FRowCount - 1 do
  176. TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
  177. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  178. end;
  179. sqlite_finalize(vm, nil);
  180. // Attach EndItem
  181. TempItem^.Next:=FEndItem;
  182. FEndItem^.Previous:=TempItem;
  183. // Alloc item used in append/insert
  184. GetMem(FCacheItem^.Row,FRowBufferSize);
  185. for Counter := 0 to FRowCount - 1 do
  186. FCacheItem^.Row[Counter]:=nil;
  187. // Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
  188. GetMem(FBeginItem^.Row,FRowBufferSize);
  189. for Counter := 0 to FRowCount - 1 do
  190. FBeginItem^.Row[Counter]:=nil;
  191. end;
  192. function TSqliteDataset.TableExists: Boolean;
  193. var
  194. AHandle,vm:Pointer;
  195. ColumnNames,ColumnValues:PPChar;
  196. AInt:Integer;
  197. begin
  198. Result:=False;
  199. if not (FTableName = '') and FileExists(FFileName) then
  200. begin
  201. if FSqliteHandle = nil then
  202. begin
  203. {$ifdef DEBUG}
  204. writeln('TableExists - FSqliteHandle=nil : Opening a file');
  205. {$endif}
  206. AHandle:=GetSqliteHandle;
  207. end
  208. else
  209. begin
  210. {$ifdef DEBUG}
  211. writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
  212. {$endif}
  213. AHandle:=FSqliteHandle;
  214. end;
  215. FSqliteReturnId:=sqlite_compile(AHandle,
  216. Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
  217. nil,@vm,nil);
  218. {$ifdef DEBUG}
  219. WriteLn('TableExists.sqlite_compile - SqliteReturnString:',SqliteReturnString);
  220. {$endif}
  221. FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
  222. {$ifdef DEBUG}
  223. WriteLn('TableExists.sqlite_step - SqliteReturnString:',SqliteReturnString);
  224. {$endif}
  225. Result:=FSqliteReturnId = SQLITE_ROW;
  226. sqlite_finalize(vm, nil);
  227. if (FSqliteHandle = nil) then
  228. SqliteClose(AHandle);
  229. end;
  230. {$ifdef DEBUG}
  231. WriteLn('TableExists ('+FTableName+') Result:',Result);
  232. {$endif}
  233. end;
  234. function TSqliteDataset.SqliteReturnString: String;
  235. begin
  236. case FSqliteReturnId 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 : Result := 'SQLITE_ROW ';
  264. SQLITE_DONE : Result := 'SQLITE_DONE ';
  265. else
  266. Result:='Unknow Return Value';
  267. end;
  268. end;
  269. end.