sqliteds.pas 28 KB

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