sqlite3ds.pas 12 KB

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