sqliteds.pas 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  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 USE_SQLITEDS_INTERNALS}
  21. { $Define DEBUG}
  22. interface
  23. uses Classes, SysUtils, Db;
  24. type
  25. PDataRecord = ^DataRecord;
  26. PPDataRecord = ^PDataRecord;
  27. DataRecord = record
  28. Row: PPchar;
  29. BookmarkData: Pointer;
  30. BookmarkFlag: TBookmarkFlag;
  31. Next: PDataRecord;
  32. Previous: PDataRecord;
  33. end;
  34. TSqliteDataset = class(TDataSet)
  35. private
  36. FFileName: String;
  37. FSql: String;
  38. FTableName: String;
  39. FIndexFieldName: String;
  40. FIndexFieldNo: Integer;
  41. FAutoIncFieldNo: Integer;
  42. FNextAutoInc:Integer;
  43. FCurrentItem: PDataRecord;
  44. FBeginItem: PDataRecord;
  45. FEndItem: PDataRecord;
  46. FCacheItem: PDataRecord;
  47. FBufferSize: Integer;
  48. FRowBufferSize: Integer;
  49. FRowCount: Integer;
  50. FRecordCount: Integer;
  51. FExpectedAppends: Integer;
  52. FExpectedDeletes: Integer;
  53. FExpectedUpdates: Integer;
  54. FSqliteReturnId: Integer;
  55. FDataAllocated: Boolean;
  56. FSaveOnClose: Boolean;
  57. FSqliteHandle: Pointer;
  58. FDBError: PPChar;
  59. FUpdatedItems: TList;
  60. FAddedItems: TList;
  61. FDeletedItems: TList;
  62. FOrphanItems: TList;
  63. procedure BuildLinkedList;
  64. procedure DisposeLinkedList;
  65. protected
  66. function AllocRecordBuffer: PChar; override;
  67. procedure FreeRecordBuffer(var Buffer: PChar); override;
  68. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  69. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  70. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  71. function GetRecordCount: Integer; override;
  72. function GetRecNo: Integer; override;
  73. function GetRecordSize: Word; override;
  74. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  75. procedure InternalClose; override;
  76. procedure InternalDelete; override;
  77. procedure InternalFirst; override;
  78. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  79. procedure InternalHandleException; override;
  80. procedure InternalInitFieldDefs; override;
  81. procedure InternalInitRecord(Buffer: PChar); override;
  82. procedure InternalLast; override;
  83. procedure InternalOpen; override;
  84. procedure InternalPost; override;
  85. procedure InternalSetToRecord(Buffer: PChar); override;
  86. function IsCursorOpen: Boolean; override;
  87. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  88. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  89. procedure SetExpectedAppends(AValue:Integer);
  90. procedure SetExpectedUpdates(AValue:Integer);
  91. procedure SetExpectedDeletes(AValue:Integer);
  92. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  93. procedure SetRecNo(Value: Integer); override;
  94. public
  95. constructor Create(AOwner: TComponent); override;
  96. destructor Destroy; override;
  97. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  98. // Additional procedures
  99. function ApplyUpdates: Boolean;
  100. function CreateTable: Boolean;
  101. function ExecSQL:Integer;
  102. function ExecSQL(ASql:String):Integer;
  103. function SqliteReturnString: String;
  104. {$ifdef USE_SQLITEDS_INTERNALS}
  105. property BeginItem: PDataRecord read FBeginItem;
  106. property EndItem: PDataRecord read FEndItem;
  107. property UpdatedItems: TList read FUpdatedItems;
  108. property AddedItems: TList read FAddedItems;
  109. property DeletedItems: TList read FDeletedItems;
  110. {$endif}
  111. property ExpectedAppends: Integer read FExpectedAppends write SetExpectedAppends;
  112. property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
  113. property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
  114. property SqliteReturnId: Integer read FSqliteReturnId;
  115. published
  116. property FileName: String read FFileName write FFileName;
  117. property IndexFieldName: String read FIndexFieldName write FIndexFieldName;
  118. property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
  119. property SQL: String read FSql write FSql;
  120. property TableName: String read FTableName write FTableName;
  121. //property Active;
  122. property FieldDefs;
  123. //Events
  124. property BeforeOpen;
  125. property AfterOpen;
  126. property BeforeClose;
  127. property AfterClose;
  128. property BeforeInsert;
  129. property AfterInsert;
  130. property BeforeEdit;
  131. property AfterEdit;
  132. property BeforePost;
  133. property AfterPost;
  134. property BeforeCancel;
  135. property AfterCancel;
  136. property BeforeDelete;
  137. property AfterDelete;
  138. property BeforeScroll;
  139. property AfterScroll;
  140. property OnDeleteError;
  141. property OnEditError;
  142. end;
  143. procedure Register;
  144. implementation
  145. uses SQLite;
  146. function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  147. var
  148. CodeError, TempInt: Integer;
  149. begin
  150. TempInt:=-1;
  151. if ColumnValues[0] <> nil then
  152. begin
  153. Val(StrPas(ColumnValues[0]),TempInt,CodeError);
  154. if CodeError <> 0 then
  155. DatabaseError('SqliteDs - Error trying to get last autoinc value');
  156. end;
  157. Integer(NextValue^):=Succ(TempInt);
  158. Result:=1;
  159. end;
  160. function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
  161. var
  162. FieldSize:Word;
  163. Counter:Integer;
  164. AType:TFieldType;
  165. ColumnStr:String;
  166. begin
  167. // Sqlite is typeless (allows any type in any field)
  168. // regardless of what is in Create Table, but returns
  169. // exactly what is in Create Table statement
  170. // here is a trick to get the datatype.
  171. // If the field contains another type, there will be problems
  172. For Counter:= 0 to Columns - 1 do
  173. begin
  174. ColumnStr:= UpCase(StrPas(ColumnNames[Counter + Columns]));
  175. If (ColumnStr = 'INTEGER') then
  176. begin
  177. AType:= ftInteger;
  178. FieldSize:=SizeOf(Integer);
  179. end else if (ColumnStr = 'BOOLEAN') then
  180. begin
  181. AType:= ftBoolean;
  182. FieldSize:=SizeOf(Boolean);
  183. end else if (ColumnStr = 'FLOAT') then
  184. begin
  185. AType:= ftFloat;
  186. FieldSize:=SizeOf(Double);
  187. end else if (ColumnStr = 'WORD') then
  188. begin
  189. AType:= ftWord;
  190. FieldSize:=SizeOf(Word);
  191. end else if (ColumnStr = 'DATETIME') then
  192. begin
  193. AType:= ftDateTime;
  194. FieldSize:=SizeOf(TDateTime);
  195. end else if (ColumnStr = 'DATE') then
  196. begin
  197. AType:= ftDate;
  198. FieldSize:=SizeOf(TDateTime);
  199. end else if (ColumnStr = 'TIME') then
  200. begin
  201. AType:= ftTime;
  202. FieldSize:=SizeOf(TDateTime);
  203. end else if (ColumnStr = 'AUTOINC') then
  204. begin
  205. //Todo: remove this check. do it in open
  206. if TSqliteDataset(TheDataset).Tablename = '' then
  207. DatabaseError('Sqliteds - AutoInc fields requires Tablename to be set');
  208. AType:= ftAutoInc;
  209. FieldSize:=SizeOf(Integer);
  210. if TSqliteDataset(TheDataset).FAutoIncFieldNo = -1 then
  211. TSqliteDataset(TheDataset).FAutoIncFieldNo:= Counter;
  212. end else
  213. begin
  214. AType:= ftString;
  215. FieldSize:=10; //??
  216. end;
  217. TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
  218. end;
  219. result:=-1;
  220. end;
  221. // TSqliteDataset override methods
  222. function TSqliteDataset.AllocRecordBuffer: PChar;
  223. var
  224. APointer:Pointer;
  225. begin
  226. APointer := AllocMem(FBufferSize);
  227. PDataRecord(APointer^):=FBeginItem;
  228. Result:=APointer;
  229. end;
  230. procedure TSqliteDataset.BuildLinkedList;
  231. var
  232. TempItem:PDataRecord;
  233. vm:Pointer;
  234. ColumnNames,ColumnValues:PPChar;
  235. Counter:Integer;
  236. begin
  237. //Get AutoInc Field initial value
  238. if FAutoIncFieldNo <> -1 then
  239. sqlite_exec(FSqliteHandle,PChar('Select Max('+Fields[FAutoIncFieldNo].FieldName+') from ' + FTableName),
  240. @GetAutoIncValue,@FNextAutoInc,nil);
  241. FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(FSql),nil,@vm,nil);
  242. if FSqliteReturnId <> SQLITE_OK then
  243. case FSqliteReturnId of
  244. SQLITE_ERROR:
  245. DatabaseError('Invalid Sql',Self);
  246. else
  247. DatabaseError('Unknow Error',Self);
  248. end;
  249. FDataAllocated:=True;
  250. New(FBeginItem);
  251. FBeginItem^.Next:=nil;
  252. FBeginItem^.Previous:=nil;
  253. FBeginItem^.BookMarkFlag:=bfBOF;
  254. TempItem:=FBeginItem;
  255. FRecordCount:=0;
  256. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  257. while FSqliteReturnId = SQLITE_ROW do
  258. begin
  259. Inc(FRecordCount);
  260. New(TempItem^.Next);
  261. TempItem^.Next^.Previous:=TempItem;
  262. TempItem:=TempItem^.Next;
  263. GetMem(TempItem^.Row,FRowBufferSize);
  264. For Counter := 0 to FRowCount - 1 do
  265. TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
  266. FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
  267. end;
  268. sqlite_finalize(vm, nil);
  269. // Init EndItem
  270. if FRecordCount <> 0 then
  271. begin
  272. New(TempItem^.Next);
  273. TempItem^.Next^.Previous:=TempItem;
  274. FEndItem:=TempItem^.Next;
  275. end
  276. else
  277. begin
  278. New(FEndItem);
  279. FEndItem^.Previous:=FBeginItem;
  280. FBeginItem^.Next:=FEndItem;
  281. end;
  282. FEndItem^.Next:=nil;
  283. // Alloc item used in append/insert
  284. New(FCacheItem);
  285. GetMem(FCacheItem^.Row,FRowBufferSize);
  286. For Counter := 0 to FRowCount - 1 do
  287. FCacheItem^.Row[Counter]:=nil;
  288. end;
  289. constructor TSqliteDataset.Create(AOwner: TComponent);
  290. begin
  291. BookmarkSize := SizeOf(Pointer);
  292. FBufferSize := SizeOf(PPDataRecord);
  293. FUpdatedItems:= TList.Create;
  294. FUpdatedItems.Capacity:=20;
  295. FAddedItems:= TList.Create;
  296. FAddedItems.Capacity:=20;
  297. FOrphanItems:= TList.Create;
  298. FOrphanItems.Capacity:=20;
  299. FDeletedItems:= TList.Create;
  300. FDeletedItems.Capacity:=20;
  301. FSaveOnClose:=False;
  302. inherited Create(AOwner);
  303. end;
  304. destructor TSqliteDataset.Destroy;
  305. begin
  306. inherited Destroy;
  307. FUpdatedItems.Destroy;
  308. FAddedItems.Destroy;
  309. FDeletedItems.Destroy;
  310. FOrphanItems.Destroy;
  311. end;
  312. procedure TSqliteDataset.DisposeLinkedList;
  313. var
  314. TempItem:PDataRecord;
  315. Counter,I:Integer;
  316. begin
  317. //Todo insert debug info
  318. FDataAllocated:=False;
  319. //Dispose cache item
  320. for Counter:= 0 to FRowCount - 1 do
  321. StrDispose(FCacheItem^.Row[Counter]);
  322. FreeMem(FCacheItem^.Row,FRowBufferSize);
  323. Dispose(FCacheItem);
  324. If FBeginItem^.Next = nil then //remove it??
  325. exit;
  326. TempItem:=FBeginItem^.Next;
  327. Dispose(FBeginItem);
  328. while TempItem^.Next <> nil do
  329. begin
  330. for Counter:= 0 to FRowCount - 1 do
  331. StrDispose(TempItem^.Row[Counter]);
  332. FreeMem(TempItem^.Row,FRowBufferSize);
  333. TempItem:=TempItem^.Next;
  334. Dispose(TempItem^.Previous);
  335. end;
  336. // Free last item
  337. Dispose(TempItem);
  338. for Counter:= 0 to FOrphanItems.Count - 1 do
  339. begin
  340. TempItem:=PDataRecord(FOrphanItems[Counter]);
  341. for I:= 0 to FRowCount - 1 do
  342. StrDispose(TempItem^.Row[I]);
  343. FreeMem(TempItem^.Row,FRowBufferSize);
  344. Dispose(TempItem);
  345. end;
  346. end;
  347. procedure TSqliteDataset.FreeRecordBuffer(var Buffer: PChar);
  348. begin
  349. FreeMem(Buffer);
  350. end;
  351. procedure TSqliteDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
  352. begin
  353. Pointer(Data^) := PPDataRecord(Buffer)^^.BookmarkData;
  354. end;
  355. function TSqliteDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  356. begin
  357. Result := PPDataRecord(Buffer)^^.BookmarkFlag;
  358. end;
  359. function TSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  360. var
  361. ValError:Word;
  362. FieldRow:PChar;
  363. //FieldIndex:Integer;
  364. begin
  365. if FRecordCount = 0 then // avoid exception in empty datasets -Todo: see if still applys
  366. begin
  367. Result:=False;
  368. Exit;
  369. end;
  370. //Small hack to allow reopening datasets with TDbEdit
  371. //while not fix it in LCL (It seems that TDataLink doesnt update Field property
  372. //after Closing and reopening datasets)
  373. //FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.Index];
  374. //FieldIndex:=Field.FieldNo - 1;
  375. FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.FieldNo - 1];
  376. Result := FieldRow <> nil;
  377. if Result and (Buffer <> nil) then //supports GetIsNull
  378. begin
  379. case Field.Datatype of
  380. ftString:
  381. begin
  382. Move(FieldRow^,PChar(Buffer)^,StrLen(FieldRow)+1);
  383. end;
  384. ftInteger,ftBoolean,ftWord,ftAutoInc:
  385. begin
  386. Val(StrPas(FieldRow),LongInt(Buffer^),ValError);
  387. Result:= ValError = 0;
  388. end;
  389. ftFloat,ftDateTime,ftTime,ftDate:
  390. begin
  391. Val(StrPas(FieldRow),Double(Buffer^),ValError);
  392. Result:= ValError = 0;
  393. end;
  394. end;
  395. end;
  396. end;
  397. function TSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  398. begin
  399. Result := grOk;
  400. case GetMode of
  401. gmPrior:
  402. if (FCurrentItem^.Previous = FBeginItem) or (FCurrentItem = FBeginItem) then
  403. begin
  404. Result := grBOF;
  405. FCurrentItem := FBeginItem;
  406. end
  407. else
  408. FCurrentItem:=FCurrentItem^.Previous;
  409. gmCurrent:
  410. if (FCurrentItem = FBeginItem) or (FCurrentItem = FEndItem) then
  411. Result := grError;
  412. gmNext:
  413. if (FCurrentItem = FEndItem) or (FCurrentItem^.Next = FEndItem) then
  414. Result := grEOF
  415. else
  416. FCurrentItem:=FCurrentItem^.Next;
  417. end; //case
  418. if Result = grOk then
  419. begin
  420. PDataRecord(Pointer(Buffer)^):=FCurrentItem;
  421. with FCurrentItem^ do
  422. begin
  423. BookmarkData := FCurrentItem;
  424. BookmarkFlag := bfCurrent;
  425. end;
  426. end
  427. else if (Result = grError) and DoCheck then
  428. DatabaseError('SqliteDs - No records',Self);
  429. end;
  430. function TSqliteDataset.GetRecordCount: Integer;
  431. begin
  432. Result := FRecordCount;
  433. end;
  434. function TSqliteDataset.GetRecNo: Integer;
  435. var
  436. TempItem,TempActive:PDataRecord;
  437. begin
  438. Result:= -1;
  439. if FRecordCount = 0 then
  440. Exit;
  441. TempItem:=FBeginItem;
  442. TempActive:=PPDataRecord(ActiveBuffer)^;
  443. if TempActive = FCacheItem then // Record not posted yet
  444. Result:=FRecordCount
  445. else
  446. while TempActive <> TempItem do
  447. begin
  448. if TempItem^.Next <> nil then
  449. begin
  450. inc(Result);
  451. TempItem:=TempItem^.Next;
  452. end
  453. else
  454. begin
  455. Result:=-1;
  456. DatabaseError('Sqliteds.GetRecNo - ActiveItem Not Found',Self);
  457. break;
  458. end;
  459. end;
  460. end;
  461. function TSqliteDataset.GetRecordSize: Word;
  462. begin
  463. Result := FBufferSize; //??
  464. end;
  465. procedure TSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  466. var
  467. NewItem: PDataRecord;
  468. Counter:Integer;
  469. begin
  470. //Todo: implement insert ??
  471. if PPDataRecord(Buffer)^ <> FCacheItem then
  472. DatabaseError('PPDataRecord(Buffer) <> FCacheItem - Problem',Self);
  473. New(NewItem);
  474. GetMem(NewItem^.Row,FRowBufferSize);
  475. for Counter := 0 to FRowCount - 1 do
  476. NewItem^.Row[Counter]:=StrNew(FCacheItem^.Row[Counter]);
  477. FEndItem^.Previous^.Next:=NewItem;
  478. NewItem^.Previous:=FEndItem^.Previous;
  479. NewItem^.Next:=FEndItem;
  480. FEndItem^.Previous:=NewItem;
  481. Inc(FRecordCount);
  482. if FAutoIncFieldNo <> - 1 then
  483. Inc(FNextAutoInc);
  484. FAddedItems.Add(NewItem);
  485. end;
  486. procedure TSqliteDataset.InternalClose;
  487. begin
  488. if FSaveOnClose then
  489. ApplyUpdates;
  490. //BindFields(False);
  491. if DefaultFields then
  492. DestroyFields;
  493. if FDataAllocated then
  494. DisposeLinkedList;
  495. if FSqliteHandle <> nil then
  496. begin
  497. sqlite_close(FSqliteHandle);
  498. FSqliteHandle := nil;
  499. end;
  500. FAddedItems.Clear;
  501. FUpdatedItems.Clear;
  502. FDeletedItems.Clear;
  503. FOrphanItems.Clear;
  504. FRecordCount:=0;
  505. end;
  506. procedure TSqliteDataset.InternalDelete;
  507. var
  508. TempItem:PDataRecord;
  509. begin
  510. If FRecordCount = 0 then
  511. Exit;
  512. Dec(FRecordCount);
  513. TempItem:=PPDataRecord(ActiveBuffer)^;
  514. // Remove from changed list
  515. FUpdatedItems.Remove(TempItem);
  516. if FAddedItems.Remove(TempItem) = -1 then
  517. FDeletedItems.Add(TempItem);
  518. FOrphanItems.Add(TempItem);
  519. TempItem^.Next^.Previous:=TempItem^.Previous;
  520. TempItem^.Previous^.Next:=TempItem^.Next;
  521. if FCurrentItem = TempItem then
  522. begin
  523. if FCurrentItem^.Previous <> FBeginItem then
  524. FCurrentItem:= FCurrentItem^.Previous
  525. else
  526. FCurrentItem:= FCurrentItem^.Next;
  527. end;
  528. // Dec FNextAutoInc
  529. if FAutoIncFieldNo <> -1 then
  530. if StrToInt(StrPas(TempItem^.Row[FAutoIncFieldNo])) = (FNextAutoInc - 1) then
  531. Dec(FNextAutoInc);
  532. end;
  533. procedure TSqliteDataset.InternalFirst;
  534. begin
  535. FCurrentItem := FBeginItem;
  536. end;
  537. procedure TSqliteDataset.InternalGotoBookmark(ABookmark: Pointer);
  538. begin
  539. FCurrentItem := PDataRecord(ABookmark^);
  540. end;
  541. procedure TSqliteDataset.InternalHandleException;
  542. begin
  543. //??
  544. end;
  545. procedure TSqliteDataset.InternalInitFieldDefs;
  546. begin
  547. FieldDefs.Clear;
  548. sqlite_exec(FSqliteHandle,PChar('PRAGMA empty_result_callbacks = ON;PRAGMA show_datatypes = ON;'),nil,nil,nil);
  549. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(FSql),@GetFieldDefs,Self,nil);
  550. {
  551. if FSqliteReturnId <> SQLITE_ABORT then
  552. DatabaseError(SqliteReturnString,Self);
  553. }
  554. FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
  555. end;
  556. procedure TSqliteDataset.InternalInitRecord(Buffer: PChar);
  557. var
  558. Counter:Integer;
  559. TempStr:String;
  560. begin
  561. for Counter:= 0 to FRowCount - 1 do
  562. begin
  563. StrDispose(FCacheItem^.Row[Counter]);
  564. FCacheItem^.Row[Counter]:=nil;
  565. end;
  566. if FAutoIncFieldNo <> - 1 then
  567. begin
  568. Str(FNextAutoInc,TempStr);
  569. FCacheItem^.Row[FAutoIncFieldNo]:=StrAlloc(Length(TempStr)+1);
  570. StrPCopy(FCacheItem^.Row[FAutoIncFieldNo],TempStr);
  571. end;
  572. PPDataRecord(Buffer)^:=FCacheItem;
  573. end;
  574. procedure TSqliteDataset.InternalLast;
  575. begin
  576. FCurrentItem := FEndItem;
  577. end;
  578. procedure TSqliteDataset.InternalOpen;
  579. begin
  580. FAutoIncFieldNo:=-1;
  581. if not FileExists(FFileName) then
  582. DatabaseError('File '+FFileName+' not found',Self);
  583. FSqliteHandle:=sqlite_open(PChar(FFileName),0,nil);
  584. InternalInitFieldDefs;
  585. if DefaultFields then
  586. CreateFields;
  587. BindFields(True);
  588. // Get indexfieldno if available
  589. if FIndexFieldName <> '' then
  590. FIndexFieldNo:=FieldByName(FIndexFieldName).FieldNo - 1
  591. else
  592. FIndexFieldNo:=FAutoIncFieldNo;
  593. BuildLinkedList;
  594. FCurrentItem:=FBeginItem;
  595. end;
  596. procedure TSqliteDataset.InternalPost;
  597. begin
  598. if (State<>dsEdit) then
  599. InternalAddRecord(ActiveBuffer,True);
  600. end;
  601. procedure TSqliteDataset.InternalSetToRecord(Buffer: PChar);
  602. begin
  603. FCurrentItem:=PPDataRecord(Buffer)^;
  604. end;
  605. function TSqliteDataset.IsCursorOpen: Boolean;
  606. begin
  607. Result := FDataAllocated;
  608. end;
  609. procedure TSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
  610. begin
  611. PPDataRecord(Buffer)^^.BookmarkData := Pointer(Data^);
  612. end;
  613. procedure TSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  614. begin
  615. PPDataRecord(Buffer)^^.BookmarkFlag := Value;
  616. end;
  617. procedure TSqliteDataset.SetExpectedAppends(AValue:Integer);
  618. begin
  619. if Assigned(FAddedItems) then
  620. FAddedItems.Capacity:=AValue;
  621. end;
  622. procedure TSqliteDataset.SetExpectedUpdates(AValue:Integer);
  623. begin
  624. if Assigned(FUpdatedItems) then
  625. FUpdatedItems.Capacity:=AValue;
  626. end;
  627. procedure TSqliteDataset.SetExpectedDeletes(AValue:Integer);
  628. begin
  629. if Assigned(FDeletedItems) then
  630. FDeletedItems.Capacity:=AValue;
  631. end;
  632. procedure TSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
  633. var
  634. TempStr:String;
  635. ActiveItem:PDataRecord;
  636. begin
  637. ActiveItem:=PPDataRecord(ActiveBuffer)^;
  638. if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
  639. FUpdatedItems.Add(ActiveItem);
  640. if Buffer = nil then
  641. ActiveItem^.Row[Pred(Field.FieldNo)]:=nil
  642. else
  643. begin
  644. StrDispose(ActiveItem^.Row[Pred(Field.FieldNo)]);
  645. case Field.Datatype of
  646. ftString:
  647. begin
  648. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrNew(PChar(Buffer));
  649. end;
  650. ftInteger,ftBoolean,ftWord:
  651. begin
  652. Str(LongInt(Buffer^),TempStr);
  653. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  654. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  655. end;
  656. ftFloat,ftDateTime,ftDate,ftTime:
  657. begin
  658. Str(Double(Buffer^),TempStr);
  659. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  660. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  661. end;
  662. end;// case
  663. end;//if
  664. end;
  665. procedure TSqliteDataset.SetRecNo(Value: Integer);
  666. var
  667. Counter:Integer;
  668. TempItem:PDataRecord;
  669. begin
  670. if (Value >= FRecordCount) or (Value < 0) then
  671. DatabaseError('SqliteDs - Record Number Out Of Range');
  672. TempItem:=FBeginItem;
  673. for Counter := 0 to Value do
  674. TempItem:=TempItem^.Next;
  675. PPDataRecord(ActiveBuffer)^:=TempItem;
  676. end;
  677. // Specific functions
  678. function TSqliteDataset.ExecSQL(ASql:String):Integer;
  679. begin
  680. Result:=0;
  681. if FSqliteHandle <> nil then
  682. begin
  683. FSqliteReturnId:= sqlite_exec(FSqliteHandle,PChar(ASql),nil,nil,nil);
  684. Result:=sqlite_changes(FSqliteHandle);
  685. end;
  686. end;
  687. function TSqliteDataset.ExecSQL:Integer;
  688. begin
  689. Result:=ExecSQL(FSql);
  690. end;
  691. function TSqliteDataset.ApplyUpdates:Boolean;
  692. var
  693. CounterFields,CounterItems:Integer;
  694. SqlTemp,KeyName:String;
  695. Quote:Char;
  696. begin
  697. Result:=False;
  698. if (FTableName <> '') and (FIndexFieldNo <> -1) then
  699. begin
  700. KeyName:=Fields[FIndexFieldNo].FieldName;
  701. {$ifdef DEBUG}
  702. if FIndexFieldNo = FAutoIncFieldNo then
  703. WriteLn('Using an AutoInc field as primary key');
  704. WriteLn('IndexFieldName: ',KeyName);
  705. WriteLn('IndexFieldNo: ',FIndexFieldNo);
  706. {$endif}
  707. SqlTemp:='BEGIN TRANSACTION; ';
  708. // Update changed records
  709. For CounterItems:= 0 to FUpdatedItems.Count - 1 do
  710. begin
  711. SqlTemp:=SqlTemp+'UPDATE '+FTableName+' SET ';
  712. for CounterFields:= 0 to Fields.Count - 1 do
  713. begin
  714. if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
  715. begin
  716. if Fields[CounterFields].DataType = ftString then
  717. Quote:='"'
  718. else
  719. Quote:=' ';
  720. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = '+Quote+
  721. StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+Quote+' , ';
  722. end
  723. else
  724. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = NULL , ';
  725. end;
  726. system.delete(SqlTemp,Length(SqlTemp)-2,2);
  727. SqlTemp:=SqlTemp+'WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FIndexFieldNo])+';';
  728. end;
  729. // Add new records
  730. For CounterItems:= 0 to FAddedItems.Count - 1 do
  731. begin
  732. SqlTemp:=SqlTemp+'INSERT INTO '+FTableName+ ' ( ';
  733. for CounterFields:= 0 to Fields.Count - 1 do
  734. begin
  735. SqlTemp:=SqlTemp + Fields[CounterFields].FieldName;
  736. if CounterFields <> Fields.Count - 1 then
  737. SqlTemp:=SqlTemp+' , ';
  738. end;
  739. SqlTemp:=SqlTemp+') VALUES ( ';
  740. for CounterFields:= 0 to Fields.Count - 1 do
  741. begin
  742. if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
  743. begin
  744. if Fields[CounterFields].DataType = ftString then
  745. Quote:='"'
  746. else
  747. Quote:=' ';
  748. SqlTemp:=SqlTemp + Quote+ StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])+Quote;
  749. end
  750. else
  751. SqlTemp:=SqlTemp + 'NULL';
  752. if CounterFields <> Fields.Count - 1 then
  753. SqlTemp:=SqlTemp+' , ';
  754. end;
  755. SqlTemp:=SqlTemp+') ;';
  756. end;
  757. // Delete Items
  758. For CounterItems:= 0 to FDeletedItems.Count - 1 do
  759. begin
  760. SqlTemp:=SqlTemp+'DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = '+
  761. StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FIndexFieldNo])+';';
  762. end;
  763. SqlTemp:=SqlTemp+'END TRANSACTION; ';
  764. {$ifdef DEBUG}
  765. writeln('ApplyUpdates Sql: ',SqlTemp);
  766. {$endif}
  767. FAddedItems.Clear;
  768. FUpdatedItems.Clear;
  769. FDeletedItems.Clear;
  770. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
  771. Result:= FSqliteReturnId = SQLITE_OK;
  772. end;
  773. {$ifdef DEBUG}
  774. writeln('ApplyUpdates Result: ',Result);
  775. {$endif}
  776. end;
  777. function TSqliteDataset.CreateTable: Boolean;
  778. var
  779. SqlTemp:String;
  780. Counter:Integer;
  781. begin
  782. {$ifdef DEBUG}
  783. if FTableName = '' then
  784. WriteLn('CreateTable : TableName Not Set');
  785. if FieldDefs.Count = 0 then
  786. WriteLn('CreateTable : FieldDefs Not Initialized');
  787. {$endif}
  788. if (FTableName <> '') and (FieldDefs.Count > 0) then
  789. begin
  790. FSqliteHandle:= sqlite_open(PChar(FFileName),0,FDBError);
  791. SqlTemp:='CREATE TABLE '+FTableName+' (';
  792. for Counter := 0 to FieldDefs.Count-1 do
  793. begin
  794. SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
  795. case FieldDefs[Counter].DataType of
  796. ftInteger:
  797. SqlTemp:=SqlTemp + ' INTEGER';
  798. ftString:
  799. SqlTemp:=SqlTemp + ' VARCHAR';
  800. ftBoolean:
  801. SqlTemp:=SqlTemp + ' BOOLEAN';
  802. ftFloat:
  803. SqlTemp:=SqlTemp + ' FLOAT';
  804. ftWord:
  805. SqlTemp:=SqlTemp + ' WORD';
  806. ftDateTime:
  807. SqlTemp:=SqlTemp + ' DATETIME';
  808. ftDate:
  809. SqlTemp:=SqlTemp + ' DATE';
  810. ftTime:
  811. SqlTemp:=SqlTemp + ' TIME';
  812. ftAutoInc:
  813. SqlTemp:=SqlTemp + ' AUTOINC';
  814. else
  815. SqlTemp:=SqlTemp + ' VARCHAR';
  816. end;
  817. if Counter <> FieldDefs.Count - 1 then
  818. SqlTemp:=SqlTemp+ ' , ';
  819. end;
  820. SqlTemp:=SqlTemp+');';
  821. {$ifdef DEBUG}
  822. writeln('CreateTable Sql: ',SqlTemp);
  823. {$endif}
  824. FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
  825. Result:= FSqliteReturnId = SQLITE_OK;
  826. sqlite_close(FSqliteHandle);
  827. end
  828. else
  829. Result:=False;
  830. end;
  831. function TSqliteDataset.SqliteReturnString: String;
  832. begin
  833. case FSqliteReturnId of
  834. SQLITE_OK : Result := 'SQLITE_OK ';
  835. SQLITE_ERROR : Result := 'SQLITE_ERROR ';
  836. SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
  837. SQLITE_PERM : Result := 'SQLITE_PERM ';
  838. SQLITE_ABORT : Result := 'SQLITE_ABORT ';
  839. SQLITE_BUSY : Result := 'SQLITE_BUSY ';
  840. SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
  841. SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
  842. SQLITE_READONLY : Result := 'SQLITE_READONLY ';
  843. SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
  844. SQLITE_IOERR : Result := 'SQLITE_IOERR ';
  845. SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
  846. SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
  847. SQLITE_FULL : Result := 'SQLITE_FULL ';
  848. SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
  849. SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
  850. SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
  851. SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
  852. SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
  853. SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
  854. SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
  855. SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
  856. SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
  857. SQLITE_AUTH : Result := 'SQLITE_AUTH ';
  858. SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
  859. // SQLITE_RANGE : Result := 'SQLITE_RANGE ';
  860. SQLITE_ROW : Result := 'SQLITE_ROW ';
  861. SQLITE_DONE : Result := 'SQLITE_DONE ';
  862. else
  863. Result:='Unknow Return Value';
  864. end;
  865. end;
  866. procedure Register;
  867. begin
  868. RegisterComponents('Data Access', [TSqliteDataset]);
  869. end;
  870. end.