sqliteds.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. unit SqliteDS;
  2. {
  3. This is SqliteDS/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 Classes, SysUtils, Db;
  23. type
  24. PDataRecord = ^DataRecord;
  25. PPDataRecord = ^PDataRecord;
  26. DataRecord = record
  27. Row: PPchar;
  28. BookmarkData: Pointer;
  29. BookmarkFlag: TBookmarkFlag;
  30. Next: PDataRecord;
  31. Previous: PDataRecord;
  32. end;
  33. TSqliteDataset = class(TDataSet)
  34. private
  35. FFileName: String;
  36. FSql: String;
  37. FTableName: String;
  38. FCurrentItem: PDataRecord;
  39. FBeginItem: PDataRecord;
  40. FEndItem: PDataRecord;
  41. FCacheItem: PDataRecord;
  42. FBufferSize: Integer;
  43. FRowBufferSize: Integer;
  44. FRowCount: Integer;
  45. FRecordCount: Integer;
  46. FSqliteReturnId: Integer;
  47. FDataAllocated: Boolean;
  48. FSaveOnClose: Boolean;
  49. FSqliteHandle: Pointer;
  50. FDBError: PPChar;
  51. FUpdatedItems: TList;
  52. FAddedItems: TList;
  53. FDeletedItems: TList;
  54. FOrphanItems: TList;
  55. procedure BuildLinkedList;
  56. procedure DisposeLinkedList;
  57. procedure SetSql (AValue:String);
  58. protected
  59. function AllocRecordBuffer: PChar; override;
  60. procedure FreeRecordBuffer(var Buffer: PChar); override;
  61. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  62. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  63. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  64. function GetRecordCount: Integer; override;
  65. function GetRecNo: Integer; override;
  66. function GetRecordSize: Word; override;
  67. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  68. procedure InternalClose; override;
  69. procedure InternalDelete; override;
  70. procedure InternalFirst; override;
  71. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  72. procedure InternalHandleException; override;
  73. procedure InternalInitFieldDefs; override;
  74. procedure InternalInitRecord(Buffer: PChar); override;
  75. procedure InternalLast; override;
  76. procedure InternalOpen; override;
  77. procedure InternalPost; override;
  78. procedure InternalSetToRecord(Buffer: PChar); override;
  79. function IsCursorOpen: Boolean; override;
  80. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  81. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  82. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  83. procedure SetRecNo(Value: Integer); override;
  84. public
  85. constructor Create(AOwner: TComponent); override;
  86. destructor Destroy; override;
  87. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  88. // Additional procedures
  89. function ApplyUpdates: Boolean;
  90. procedure CloseHandle;
  91. function CreateDataSet(MustCloseHandle:Boolean): Boolean;
  92. function ExecSQL:Integer;
  93. function ExecSQL(ASql:String):Integer;
  94. function SqliteReturnString: String;
  95. {$Ifdef DEBUG}
  96. property BeginItem: PDataRecord read FBeginItem;
  97. property EndItem: PDataRecord read FEndItem;
  98. {$Endif}
  99. property SqliteReturnId: Integer read FSqliteReturnId;
  100. property SQL: String read FSql write SetSql;
  101. property TableName: String read FTableName write FTableName;
  102. property FileName: String read FFileName write FFileName;
  103. property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
  104. published
  105. property Active;
  106. property BeforeOpen;
  107. property AfterOpen;
  108. property BeforeClose;
  109. property AfterClose;
  110. property BeforeInsert;
  111. property AfterInsert;
  112. property BeforeEdit;
  113. property AfterEdit;
  114. property BeforePost;
  115. property AfterPost;
  116. property BeforeCancel;
  117. property AfterCancel;
  118. property BeforeDelete;
  119. property AfterDelete;
  120. property BeforeScroll;
  121. property AfterScroll;
  122. property OnDeleteError;
  123. property OnEditError;
  124. end;
  125. implementation
  126. uses SQLite;
  127. function GetFieldDefs(TheDataset: TDataset; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  128. var
  129. FieldSize:Word;
  130. Counter:Integer;
  131. AType:TFieldType;
  132. ColumnStr:String;
  133. TempAttributes:TFieldAttributes;
  134. begin
  135. // Sqlite is typeless (allows any type in any field)
  136. // regardless of what is in Create Table, but returns
  137. // exactly what is in Create Table statement
  138. // here is a trick to get the datatype.
  139. // If the field contains another type, there will be problems
  140. For Counter:= 0 to Columns - 1 do
  141. begin
  142. ColumnStr:= UpCase(StrPas(ColumnNames[Counter + Columns]));
  143. If (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
  144. begin
  145. AType:= ftInteger;
  146. FieldSize:=SizeOf(Integer);
  147. end
  148. else
  149. begin
  150. AType:= ftString;
  151. FieldSize:=0;
  152. end;
  153. with TheDataset.FieldDefs do
  154. begin
  155. Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
  156. If Items[Counter].Name = '_ROWID_' then
  157. begin
  158. TempAttributes:=Items[Counter].Attributes;
  159. System.Include(TempAttributes,faReadonly);
  160. Items[Counter].Attributes:=TempAttributes;
  161. end;
  162. end;
  163. end;
  164. result:=-1;
  165. end;
  166. // TSqliteDataset override methods
  167. function TSqliteDataset.AllocRecordBuffer: PChar;
  168. begin
  169. Result := AllocMem(FBufferSize);
  170. end;
  171. procedure TSqliteDataset.BuildLinkedList;
  172. var
  173. TempItem:PDataRecord;
  174. vm:Pointer;
  175. ColumnNames,ColumnValues:PPChar;
  176. Counter:Integer;
  177. begin
  178. FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(FSql),nil,@vm,nil);
  179. if FSqliteReturnId <> SQLITE_OK then
  180. case FSqliteReturnId of
  181. SQLITE_ERROR:
  182. DatabaseError('Invalid Sql',Self);
  183. else
  184. DatabaseError('Unknow Error',Self);
  185. end;
  186. FDataAllocated:=True;
  187. New(FBeginItem);
  188. FBeginItem^.Next:=nil;
  189. FBeginItem^.Previous:=nil;
  190. TempItem:=FBeginItem;
  191. FRecordCount:=0;
  192. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  193. while FSqliteReturnId = SQLITE_ROW do
  194. begin
  195. Inc(FRecordCount);
  196. New(TempItem^.Next);
  197. TempItem^.Next^.Previous:=TempItem;
  198. TempItem:=TempItem^.Next;
  199. GetMem(TempItem^.Row,FRowBufferSize);
  200. For Counter := 0 to FRowCount - 1 do
  201. TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
  202. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  203. end;
  204. sqlite_finalize(vm, nil);
  205. // Init EndItem
  206. if FRecordCount <> 0 then
  207. begin
  208. New(TempItem^.Next);
  209. TempItem^.Next^.Previous:=TempItem;
  210. FEndItem:=TempItem^.Next;
  211. end
  212. else
  213. begin
  214. New(FEndItem);
  215. FEndItem^.Previous:=FBeginItem;
  216. FBeginItem^.Next:=FEndItem;
  217. end;
  218. FEndItem^.Next:=nil;
  219. // Alloc item used in append/insert
  220. New(FCacheItem);
  221. GetMem(FCacheItem^.Row,FRowBufferSize);
  222. For Counter := 0 to FRowCount - 1 do
  223. FCacheItem^.Row[Counter]:=nil;
  224. end;
  225. constructor TSqliteDataset.Create(AOwner: TComponent);
  226. begin
  227. BookmarkSize := SizeOf(Pointer);
  228. FBufferSize := SizeOf(PPDataRecord);
  229. FUpdatedItems:= TList.Create;
  230. FUpdatedItems.Capacity:=20;
  231. FAddedItems:= TList.Create;
  232. FAddedItems.Capacity:=20;
  233. FOrphanItems:= TList.Create;
  234. FOrphanItems.Capacity:=20;
  235. FDeletedItems:= TList.Create;
  236. FDeletedItems.Capacity:=20;
  237. FSaveOnClose:=False;
  238. inherited Create(AOwner);
  239. end;
  240. destructor TSqliteDataset.Destroy;
  241. begin
  242. FUpdatedItems.Destroy;
  243. FAddedItems.Destroy;
  244. FDeletedItems.Destroy;
  245. FOrphanItems.Destroy;
  246. inherited Destroy;
  247. end;
  248. procedure TSqliteDataset.DisposeLinkedList;
  249. var
  250. TempItem:PDataRecord;
  251. Counter:Integer;
  252. begin
  253. //Todo insert debug info
  254. FDataAllocated:=False;
  255. //Dispose cache item
  256. for Counter:= 0 to FRowCount - 1 do
  257. StrDispose(FCacheItem^.Row[Counter]);
  258. FreeMem(FCacheItem^.Row,FRowBufferSize);
  259. Dispose(FCacheItem);
  260. If FBeginItem^.Next = nil then //remove it??
  261. exit;
  262. TempItem:=FBeginItem^.Next;
  263. Dispose(FBeginItem);
  264. while TempItem^.Next <> nil do
  265. begin
  266. for Counter:= 0 to FRowCount - 1 do
  267. StrDispose(TempItem^.Row[Counter]);
  268. FreeMem(TempItem^.Row,FRowBufferSize);
  269. TempItem:=TempItem^.Next;
  270. Dispose(TempItem^.Previous);
  271. end;
  272. // Free last item
  273. Dispose(TempItem);
  274. for Counter:= 0 to FOrphanItems.Count - 1 do
  275. begin
  276. TempItem:=PDataRecord(FOrphanItems[Counter]);
  277. for Counter:= 0 to FRowCount - 1 do
  278. StrDispose(TempItem^.Row[Counter]);
  279. FreeMem(TempItem^.Row,FRowBufferSize);
  280. Dispose(TempItem);
  281. end;
  282. end;
  283. procedure TSqliteDataset.FreeRecordBuffer(var Buffer: PChar);
  284. begin
  285. FreeMem(Buffer);
  286. end;
  287. procedure TSqliteDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
  288. begin
  289. Pointer(Data^) := PPDataRecord(Buffer)^^.BookmarkData;
  290. end;
  291. function TSqliteDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  292. begin
  293. Result := PPDataRecord(Buffer)^^.BookmarkFlag;
  294. end;
  295. function TSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  296. var
  297. ValError:Word;
  298. FieldRow:PChar;
  299. begin
  300. if FRecordCount = 0 then // avoid exception in empty datasets
  301. begin
  302. Result:=False;
  303. Exit;
  304. end;
  305. FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.Index];
  306. Result := FieldRow <> nil;
  307. if Result and (Buffer <> nil) then //supports GetIsNull
  308. begin
  309. case Field.Datatype of
  310. ftString:
  311. begin
  312. Move(FieldRow^,PChar(Buffer)^,StrLen(FieldRow)+1);
  313. end;
  314. ftInteger:
  315. begin
  316. Val(StrPas(FieldRow),LongInt(Buffer^),ValError);
  317. Result:= ValError = 0;
  318. end;
  319. end;
  320. end;
  321. end;
  322. function TSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  323. begin
  324. Result := grOk;
  325. case GetMode of
  326. gmPrior:
  327. if (FCurrentItem^.Previous = FBeginItem) or (FCurrentItem = FBeginItem) then
  328. begin
  329. Result := grBOF;
  330. FCurrentItem := FBeginItem;
  331. end
  332. else
  333. FCurrentItem:=FCurrentItem^.Previous;
  334. gmCurrent:
  335. if (FCurrentItem = FBeginItem) or (FCurrentItem = FEndItem) then
  336. Result := grError;
  337. gmNext:
  338. if (FCurrentItem = FEndItem) or (FCurrentItem^.Next = FEndItem) then
  339. Result := grEOF
  340. else
  341. FCurrentItem:=FCurrentItem^.Next;
  342. end; //case
  343. if Result = grOk then
  344. begin
  345. PDataRecord(Pointer(Buffer)^):=FCurrentItem;
  346. with FCurrentItem^ do
  347. begin
  348. BookmarkData := FCurrentItem;
  349. BookmarkFlag := bfCurrent;
  350. end;
  351. end
  352. else if (Result = grError) and DoCheck then
  353. DatabaseError('SqliteDs - No records',Self);
  354. end;
  355. function TSqliteDataset.GetRecordCount: Integer;
  356. begin
  357. Result := FRecordCount;
  358. end;
  359. function TSqliteDataset.GetRecNo: Integer;
  360. var
  361. TempItem,TempActive:PDataRecord;
  362. begin
  363. Result:= -1;
  364. TempItem:=FBeginItem;
  365. TempActive:=PPDataRecord(ActiveBuffer)^;
  366. while TempActive <> TempItem do
  367. begin
  368. if TempItem^.Next <> nil then
  369. begin
  370. inc(Result);
  371. TempItem:=TempItem^.Next;
  372. end
  373. else
  374. begin
  375. Result:=-1;
  376. DatabaseError('GetRecNo - ActiveItem Not Found',Self);
  377. break;
  378. end;
  379. end;
  380. end;
  381. function TSqliteDataset.GetRecordSize: Word;
  382. begin
  383. Result := FBufferSize; //??
  384. end;
  385. procedure TSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  386. var
  387. NewItem: PDataRecord;
  388. Counter:Integer;
  389. begin
  390. //Todo: implement insert ??
  391. if PPDataRecord(Buffer)^ <> FCacheItem then
  392. DatabaseError('PPDataRecord(Buffer) <> FCacheItem - Problem',Self);
  393. New(NewItem);
  394. GetMem(NewItem^.Row,FRowBufferSize);
  395. for Counter := 0 to FRowCount - 1 do
  396. NewItem^.Row[Counter]:=StrNew(FCacheItem^.Row[Counter]);
  397. FEndItem^.Previous^.Next:=NewItem;
  398. NewItem^.Previous:=FEndItem^.Previous;
  399. NewItem^.Next:=FEndItem;
  400. FEndItem^.Previous:=NewItem;
  401. Inc(FRecordCount);
  402. FAddedItems.Add(NewItem);
  403. end;
  404. procedure TSqliteDataset.InternalClose;
  405. begin
  406. if FSaveOnClose then
  407. ApplyUpdates;
  408. if DefaultFields then
  409. DestroyFields;
  410. if FDataAllocated then
  411. DisposeLinkedList;
  412. if FSqliteHandle <> nil then
  413. begin
  414. CloseHandle;
  415. FSqliteHandle := nil;
  416. end;
  417. FAddedItems.Clear;
  418. FUpdatedItems.Clear;
  419. FDeletedItems.Clear;
  420. FOrphanItems.Clear;
  421. FRecordCount:=0;
  422. end;
  423. procedure TSqliteDataset.InternalDelete;
  424. var
  425. TempItem:PDataRecord;
  426. begin
  427. Dec(FRecordCount);
  428. TempItem:=PPDataRecord(ActiveBuffer)^;
  429. // Remove from changed list
  430. FUpdatedItems.Remove(TempItem);
  431. if FAddedItems.Remove(TempItem) = -1 then
  432. FDeletedItems.Add(TempItem);
  433. FOrphanItems.Add(TempItem);
  434. TempItem^.Next^.Previous:=TempItem^.Previous;
  435. TempItem^.Previous^.Next:=TempItem^.Next;
  436. if FCurrentItem = TempItem then
  437. begin
  438. if FCurrentItem^.Previous <> FBeginItem then
  439. FCurrentItem:= FCurrentItem^.Previous
  440. else
  441. FCurrentItem:= FCurrentItem^.Next;
  442. end;
  443. end;
  444. procedure TSqliteDataset.InternalFirst;
  445. begin
  446. FCurrentItem := FBeginItem;
  447. end;
  448. procedure TSqliteDataset.InternalGotoBookmark(ABookmark: Pointer);
  449. begin
  450. FCurrentItem := PDataRecord(ABookmark^);
  451. end;
  452. procedure TSqliteDataset.InternalHandleException;
  453. begin
  454. //??
  455. end;
  456. procedure TSqliteDataset.InternalInitFieldDefs;
  457. begin
  458. FieldDefs.Clear;
  459. sqlite_exec(FSqliteHandle,PChar('PRAGMA empty_result_callbacks = ON;PRAGMA show_datatypes = ON;'),nil,nil,nil);
  460. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(FSql),@GetFieldDefs,Self,nil);
  461. {
  462. if FSqliteReturnId <> SQLITE_ABORT then
  463. DatabaseError(SqliteReturnString,Self);
  464. }
  465. FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
  466. end;
  467. procedure TSqliteDataset.InternalInitRecord(Buffer: PChar);
  468. var
  469. Counter:Integer;
  470. begin
  471. for Counter:= 0 to FRowCount - 1 do
  472. begin
  473. StrDispose(FCacheItem^.Row[Counter]);
  474. FCacheItem^.Row[Counter]:=nil;
  475. end;
  476. PPDataRecord(Buffer)^:=FCacheItem;
  477. end;
  478. procedure TSqliteDataset.InternalLast;
  479. begin
  480. FCurrentItem := FEndItem;
  481. end;
  482. procedure TSqliteDataset.InternalOpen;
  483. begin
  484. if not FileExists(FFileName) then
  485. DatabaseError('File '+FFileName+' not found',Self);
  486. FSqliteHandle:=sqlite_open(PChar(FFileName),0,FDBError);
  487. InternalInitFieldDefs;
  488. BuildLinkedList;
  489. FCurrentItem:=FBeginItem;
  490. if DefaultFields then
  491. CreateFields;
  492. BindFields(True);
  493. end;
  494. procedure TSqliteDataset.InternalPost;
  495. begin
  496. if (State<>dsEdit) then
  497. InternalAddRecord(ActiveBuffer,True);
  498. end;
  499. procedure TSqliteDataset.InternalSetToRecord(Buffer: PChar);
  500. begin
  501. FCurrentItem:=PPDataRecord(Buffer)^;
  502. end;
  503. function TSqliteDataset.IsCursorOpen: Boolean;
  504. begin
  505. Result := FDataAllocated;
  506. end;
  507. procedure TSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
  508. begin
  509. PPDataRecord(Buffer)^^.BookmarkData := Pointer(Data^);
  510. end;
  511. procedure TSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  512. begin
  513. PPDataRecord(Buffer)^^.BookmarkFlag := Value;
  514. end;
  515. procedure TSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
  516. var
  517. TempStr:String;
  518. ActiveItem:PDataRecord;
  519. begin
  520. ActiveItem:=PPDataRecord(ActiveBuffer)^;
  521. if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
  522. FUpdatedItems.Add(ActiveItem);
  523. if Buffer = nil then
  524. ActiveItem^.Row[Field.Index]:=nil
  525. else
  526. case Field.Datatype of
  527. ftString:
  528. begin
  529. StrDispose(ActiveItem^.Row[Field.Index]);
  530. ActiveItem^.Row[Field.Index]:=StrNew(PChar(Buffer));
  531. end;
  532. ftInteger:
  533. begin
  534. StrDispose(ActiveItem^.Row[Field.Index]);
  535. Str(LongInt(Buffer^),TempStr);
  536. ActiveItem^.Row[Field.Index]:=StrAlloc(Length(TempStr)+1);
  537. StrPCopy(ActiveItem^.Row[Field.Index],TempStr);
  538. end;
  539. end;
  540. end;
  541. procedure TSqliteDataset.SetRecNo(Value: Integer);
  542. begin
  543. //
  544. end;
  545. // Specific functions
  546. procedure TSqliteDataset.SetSql(AValue:String);
  547. begin
  548. FSql:=AValue;
  549. // Todo: Retrieve Tablename from SQL ??
  550. end;
  551. function TSqliteDataset.ExecSQL(ASql:String):Integer;
  552. begin
  553. Result:=0;
  554. if FSqliteHandle <> nil then
  555. begin
  556. FSqliteReturnId:= sqlite_exec(FSqliteHandle,PChar(ASql),nil,nil,nil);
  557. Result:=sqlite_changes(FSqliteHandle);
  558. end;
  559. end;
  560. function TSqliteDataset.ExecSQL:Integer;
  561. begin
  562. Result:=ExecSQL(FSql);
  563. end;
  564. function TSqliteDataset.ApplyUpdates:Boolean;
  565. var
  566. CounterFields,CounterItems:Integer;
  567. SqlTemp:String;
  568. Quote:Char;
  569. begin
  570. Result:=False;
  571. if (FTableName <> '') and (Fields[0].FieldName = '_ROWID_') then
  572. begin
  573. SqlTemp:='BEGIN TRANSACTION; ';
  574. // Update changed records
  575. For CounterItems:= 0 to FUpdatedItems.Count - 1 do
  576. begin
  577. SqlTemp:=SqlTemp+'UPDATE '+FTableName+' SET ';
  578. for CounterFields:= 1 to Fields.Count - 1 do
  579. begin
  580. if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
  581. begin
  582. if Fields[CounterFields].DataType = ftString then
  583. Quote:='"'
  584. else
  585. Quote:=' ';
  586. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = '+Quote+
  587. StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+Quote+' , ';
  588. end
  589. else
  590. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = NULL , ';
  591. end;
  592. system.delete(SqlTemp,Length(SqlTemp)-2,2);
  593. SqlTemp:=SqlTemp+'WHERE _ROWID_ = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[0])+';';
  594. end;
  595. // Add new records
  596. For CounterItems:= 0 to FAddedItems.Count - 1 do
  597. begin
  598. SqlTemp:=SqlTemp+'INSERT INTO '+FTableName+ ' ( ';
  599. for CounterFields:= 1 to Fields.Count - 1 do
  600. begin
  601. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName;
  602. if CounterFields <> Fields.Count - 1 then
  603. SqlTemp:=SqlTemp+' , ';
  604. end;
  605. SqlTemp:=SqlTemp+') VALUES ( ';
  606. for CounterFields:= 1 to Fields.Count - 1 do
  607. begin
  608. if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
  609. begin
  610. if Fields[CounterFields].DataType = ftString then
  611. Quote:='"'
  612. else
  613. Quote:=' ';
  614. SqlTemp:=SqlTemp + Quote+ StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])+Quote;
  615. end
  616. else
  617. SqlTemp:=SqlTemp + 'NULL';
  618. if CounterFields <> Fields.Count - 1 then
  619. SqlTemp:=SqlTemp+' , ';
  620. end;
  621. SqlTemp:=SqlTemp+') ;';
  622. end;
  623. // Delete Items
  624. For CounterItems:= 0 to FDeletedItems.Count - 1 do
  625. begin
  626. SqlTemp:=SqlTemp+'DELETE FROM '+FTableName+ ' WHERE _ROWID_ = '+
  627. StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[0])+';';
  628. end;
  629. SqlTemp:=SqlTemp+'END TRANSACTION; ';
  630. {$ifdef DEBUG}
  631. writeln('ApplyUpdates Sql: ',SqlTemp);
  632. {$endif}
  633. FAddedItems.Clear;
  634. FUpdatedItems.Clear;
  635. FDeletedItems.Clear;
  636. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
  637. Result:= FSqliteReturnId = SQLITE_OK;
  638. end;
  639. {$ifdef DEBUG}
  640. writeln('ApplyUpdates Result: ',Result);
  641. {$endif}
  642. end;
  643. procedure TSqliteDataset.CloseHandle;
  644. begin
  645. sqlite_close(FSqliteHandle);
  646. end;
  647. function TSqliteDataset.CreateDataSet(MustCloseHandle:Boolean): Boolean;
  648. var
  649. SqlTemp:String;
  650. Counter:Integer;
  651. begin
  652. if (FTableName <> '') and (FieldDefs.Count > 0) then
  653. begin
  654. FSqliteHandle:= sqlite_open(PChar(FFileName),0,FDBError);
  655. SqlTemp:='CREATE TABLE '+FTableName+' (';
  656. for Counter := 0 to FieldDefs.Count-1 do
  657. begin
  658. SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
  659. if FieldDefs[Counter].DataType = ftInteger then
  660. SqlTemp:=SqlTemp + ' INTEGER'
  661. else
  662. SqlTemp:=SqlTemp + ' VARCHAR';
  663. if Counter <> FieldDefs.Count - 1 then
  664. SqlTemp:=SqlTemp+ ' , ';
  665. end;
  666. SqlTemp:=SqlTemp+');';
  667. {$ifdef DEBUG}
  668. writeln('CreateDataSet Sql: ',SqlTemp);
  669. {$endif}
  670. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
  671. Result:= FSqliteReturnId = SQLITE_OK;
  672. if MustCloseHandle then
  673. CloseHandle;
  674. end
  675. else
  676. Result:=False;
  677. end;
  678. function TSqliteDataset.SqliteReturnString: String;
  679. begin
  680. case FSqliteReturnId of
  681. SQLITE_OK : Result := 'SQLITE_OK ';
  682. SQLITE_ERROR : Result := 'SQLITE_ERROR ';
  683. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
  684. SQLITE_PERM : Result := 'SQLITE_PERM ';
  685. SQLITE_ABORT : Result := 'SQLITE_ABORT ';
  686. SQLITE_BUSY : Result := 'SQLITE_BUSY ';
  687. SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
  688. SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
  689. SQLITE_READONLY : Result := 'SQLITE_READONLY ';
  690. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
  691. SQLITE_IOERR : Result := 'SQLITE_IOERR ';
  692. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
  693. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
  694. SQLITE_FULL : Result := 'SQLITE_FULL ';
  695. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
  696. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
  697. SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
  698. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
  699. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
  700. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
  701. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
  702. SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
  703. SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
  704. SQLITE_AUTH : Result := 'SQLITE_AUTH ';
  705. SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
  706. // SQLITE_RANGE : Result := 'SQLITE_RANGE ';
  707. SQLITE_ROW : Result := 'SQLITE_ROW ';
  708. SQLITE_DONE : Result := 'SQLITE_DONE ';
  709. else
  710. Result:='Unknow Return Value';
  711. end;
  712. end;
  713. end.