customsqliteds.pas 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264
  1. unit customsqliteds;
  2. {
  3. This is TCustomSqliteDataset, 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. { $Define DEBUGACTIVEBUFFER}
  23. interface
  24. uses
  25. Classes, SysUtils, Db;
  26. type
  27. PDataRecord = ^DataRecord;
  28. PPDataRecord = ^PDataRecord;
  29. DataRecord = record
  30. Row: PPchar;
  31. BookmarkFlag: TBookmarkFlag;
  32. Next: PDataRecord;
  33. Previous: PDataRecord;
  34. end;
  35. TDSStream = class(TStream)
  36. private
  37. FActiveItem:PDataRecord;
  38. FFieldRow:PChar;
  39. FFieldIndex:Integer;
  40. FRowSize: Integer;
  41. FPosition: Longint;
  42. public
  43. constructor Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
  44. function Write(const Buffer; Count: Longint): Longint; override;
  45. function Read(var Buffer; Count: Longint): Longint; override;
  46. function Seek(Offset: Longint; Origin: Word): Longint; override;
  47. end;
  48. TSqliteCallback = function (UserData:Pointer; Columns:longint; Values:PPchar; ColumnNames:PPchar):longint;cdecl;
  49. { TCustomSqliteDataset }
  50. TCustomSqliteDataset = class(TDataSet)
  51. private
  52. FPrimaryKey: String;
  53. FPrimaryKeyNo: Integer;
  54. {$ifdef DEBUGACTIVEBUFFER}
  55. FFCurrentItem: PDataRecord;
  56. {$else}
  57. FCurrentItem: PDataRecord;
  58. {$endif}
  59. FBufferSize: Integer;
  60. FExpectedAppends: Integer;
  61. FExpectedDeletes: Integer;
  62. FExpectedUpdates: Integer;
  63. FSaveOnClose: Boolean;
  64. FSaveOnRefetch: Boolean;
  65. FSqlMode: Boolean;
  66. FUpdatedItems: TFPList;
  67. FAddedItems: TFPList;
  68. FDeletedItems: TFPList;
  69. FOrphanItems: TFPList;
  70. FMasterLink: TMasterDataLink;
  71. FIndexFieldNames: String;
  72. FIndexFieldList: TList;
  73. function GetIndexFields(Value: Integer): TField;
  74. procedure UpdateIndexFields;
  75. function FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
  76. protected
  77. FFileName: String;
  78. FSql: String;
  79. FTableName: String;
  80. FAutoIncFieldNo: Integer;
  81. FNextAutoInc:Integer;
  82. FSqliteReturnId: Integer;
  83. FSqliteHandle: Pointer;
  84. FDataAllocated: Boolean;
  85. FRowBufferSize: Integer;
  86. FRowCount: Integer;
  87. FRecordCount: Integer;
  88. FBeginItem: PDataRecord;
  89. FEndItem: PDataRecord;
  90. FCacheItem: PDataRecord;
  91. function SqliteExec(AHandle: Pointer; Sql:PChar):Integer;virtual; abstract;
  92. procedure SqliteClose(AHandle: Pointer);virtual;abstract;
  93. function GetSqliteHandle: Pointer; virtual; abstract;
  94. function GetSqliteVersion: String; virtual; abstract;
  95. procedure BuildLinkedList; virtual; abstract;
  96. procedure DisposeLinkedList;
  97. procedure SetDetailFilter;
  98. procedure MasterChanged(Sender: TObject);
  99. procedure MasterDisabled(Sender: TObject);
  100. procedure SetMasterFields(Value:String);
  101. function GetMasterFields:String;
  102. procedure SetMasterSource(Value: TDataSource);
  103. function GetMasterSource:TDataSource;
  104. //TDataSet overrides
  105. function AllocRecordBuffer: PChar; override;
  106. function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
  107. procedure FreeRecordBuffer(var Buffer: PChar); override;
  108. procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
  109. function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
  110. function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
  111. function GetRecordCount: Integer; override;
  112. function GetRecNo: Integer; override;
  113. function GetRecordSize: Word; override;
  114. procedure InternalAddRecord(Buffer: Pointer; DoAppend: Boolean); override;
  115. procedure InternalClose; override;
  116. procedure InternalDelete; override;
  117. procedure InternalFirst; override;
  118. procedure InternalGotoBookmark(ABookmark: Pointer); override;
  119. procedure InternalInitRecord(Buffer: PChar); override;
  120. procedure InternalLast; override;
  121. procedure InternalOpen; override;
  122. procedure InternalPost; override;
  123. procedure InternalSetToRecord(Buffer: PChar); override;
  124. function IsCursorOpen: Boolean; override;
  125. procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
  126. procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
  127. procedure SetExpectedAppends(AValue:Integer);
  128. procedure SetExpectedUpdates(AValue:Integer);
  129. procedure SetExpectedDeletes(AValue:Integer);
  130. procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  131. procedure SetRecNo(Value: Integer); override;
  132. public
  133. constructor Create(AOwner: TComponent); override;
  134. destructor Destroy; override;
  135. function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
  136. function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
  137. function LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
  138. function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant;{$ifndef ver2_0_0}override;{$endif}
  139. // Additional procedures
  140. function ApplyUpdates: Boolean; virtual;
  141. function CreateTable: Boolean; virtual;
  142. function ExecSQL:Integer;
  143. function ExecSQL(const ASql:String):Integer;
  144. function QuickQuery(const ASql:String):String;overload;
  145. function QuickQuery(const ASql:String;const AStrList: TStrings):String;overload;
  146. function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;virtual;abstract;overload;
  147. procedure RefetchData;
  148. function SqliteReturnString: String; virtual;abstract;
  149. function TableExists: Boolean;virtual;abstract;
  150. function UpdatesPending: Boolean;
  151. {$ifdef DEBUGACTIVEBUFFER}
  152. procedure SetCurrentItem(Value:PDataRecord);
  153. property FCurrentItem: PDataRecord read FFCurrentItem write SetCurrentItem;
  154. {$endif}
  155. {$ifdef USE_SQLITEDS_INTERNALS}
  156. property BeginItem: PDataRecord read FBeginItem;
  157. property EndItem: PDataRecord read FEndItem;
  158. property UpdatedItems: TFPList read FUpdatedItems;
  159. property AddedItems: TFPList read FAddedItems;
  160. property DeletedItems: TFPList read FDeletedItems;
  161. {$endif}
  162. property ExpectedAppends: Integer read FExpectedAppends write SetExpectedAppends;
  163. property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
  164. property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
  165. property IndexFields[Value: Integer]: TField read GetIndexFields;
  166. property SqliteReturnId: Integer read FSqliteReturnId;
  167. property SqliteHandle: Pointer read FSqliteHandle;
  168. property SqliteVersion: String read GetSqliteVersion;
  169. published
  170. property IndexFieldNames: string read FIndexFieldNames write FIndexFieldNames;
  171. property FileName: String read FFileName write FFileName;
  172. property PrimaryKey: String read FPrimaryKey write FPrimaryKey;
  173. property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
  174. property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch;
  175. property SQL: String read FSql write FSql;
  176. property SqlMode: Boolean read FSqlMode write FSqlMode;
  177. property TableName: String read FTableName write FTableName;
  178. property MasterSource: TDataSource read GetMasterSource write SetMasterSource;
  179. property MasterFields: string read GetMasterFields write SetMasterFields;
  180. property Active;
  181. //Events
  182. property BeforeOpen;
  183. property AfterOpen;
  184. property BeforeClose;
  185. property AfterClose;
  186. property BeforeInsert;
  187. property AfterInsert;
  188. property BeforeEdit;
  189. property AfterEdit;
  190. property BeforePost;
  191. property AfterPost;
  192. property BeforeCancel;
  193. property AfterCancel;
  194. property BeforeDelete;
  195. property AfterDelete;
  196. property BeforeScroll;
  197. property AfterScroll;
  198. property OnDeleteError;
  199. property OnEditError;
  200. end;
  201. implementation
  202. uses
  203. strutils, variants;
  204. const
  205. SQLITE_OK = 0;//sqlite2.x.x and sqlite3.x.x defines this equal
  206. // TDSStream
  207. constructor TDSStream.Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
  208. begin
  209. inherited Create;
  210. FPosition:=0;
  211. FActiveItem:=ActiveItem;
  212. FFieldIndex:=FieldIndex;
  213. FFieldRow:=ActiveItem^.Row[FieldIndex];
  214. if FFieldRow <> nil then
  215. FRowSize:=StrLen(FFieldRow)
  216. else
  217. FRowSize:=0;
  218. end;
  219. function TDSStream.Seek(Offset: Longint; Origin: Word): Longint;
  220. begin
  221. Case Origin of
  222. soFromBeginning : FPosition:=Offset;
  223. soFromEnd : FPosition:=FRowSize+Offset;
  224. soFromCurrent : FPosition:=FPosition+Offset;
  225. end;
  226. Result:=FPosition;
  227. end;
  228. function TDSStream.Write(const Buffer; Count: Longint): Longint;
  229. var
  230. NewRow:PChar;
  231. begin
  232. Result:=Count;
  233. if Count = 0 then
  234. Exit;
  235. //Todo: see how TDbMemo read/write to field and choose best if order
  236. if FPosition = 0 then
  237. begin
  238. NewRow:=StrAlloc(Count+1);
  239. (NewRow+Count)^:=#0;
  240. Move(Buffer,NewRow^,Count);
  241. end
  242. else
  243. begin
  244. NewRow:=StrAlloc(FRowSize+Count+1);
  245. (NewRow+Count+FRowSize)^:=#0;
  246. Move(FFieldRow^,NewRow^,FRowSize);
  247. Move(Buffer,(NewRow+FRowSize)^,Count);
  248. end;
  249. FActiveItem^.Row[FFieldIndex]:=NewRow;
  250. StrDispose(FFieldRow);
  251. FFieldRow:=NewRow;
  252. FRowSize:=StrLen(NewRow);
  253. Inc(FPosition,Count);
  254. {$ifdef DEBUG}
  255. WriteLn('Writing a BlobStream');
  256. WriteLn('Stream.Size: ',StrLen(NewRow));
  257. WriteLn('Stream Value: ',NewRow);
  258. WriteLn('FPosition:',FPosition);
  259. {$endif}
  260. end;
  261. function TDSStream.Read(var Buffer; Count: Longint): Longint;
  262. var
  263. BytesToMove:Integer;
  264. begin
  265. if (FRowSize - FPosition) >= Count then
  266. BytesToMove:=Count
  267. else
  268. BytesToMove:=FRowSize - FPosition;
  269. Move((FFieldRow+FPosition)^,Buffer,BytesToMove);
  270. Inc(FPosition,BytesToMove);
  271. Result:=BytesToMove;
  272. {$ifdef DEBUG}
  273. WriteLn('Reading a BlobStream');
  274. WriteLn('Bytes requested: ',Count);
  275. WriteLn('Bytes Moved: ',BytesToMove);
  276. WriteLn('Stream.Size: ',FRowSize);
  277. WriteLn('Stream Value: ',FFieldRow);
  278. {$endif}
  279. end;
  280. // TCustomSqliteDataset override methods
  281. function TCustomSqliteDataset.AllocRecordBuffer: PChar;
  282. var
  283. APointer:Pointer;
  284. begin
  285. APointer := AllocMem(FBufferSize);
  286. PDataRecord(APointer^):=FBeginItem;
  287. Result:=APointer;
  288. end;
  289. constructor TCustomSqliteDataset.Create(AOwner: TComponent);
  290. begin
  291. // setup special items
  292. New(FBeginItem);
  293. New(FCacheItem);
  294. New(FEndItem);
  295. FBeginItem^.Previous:=nil;
  296. FEndItem^.Next:=nil;
  297. FBeginItem^.BookMarkFlag:=bfBOF;
  298. FCacheItem^.BookMarkFlag:=bfEOF;
  299. FEndItem^.BookMarkFlag:=bfEOF;
  300. FMasterLink:=TMasterDataLink.Create(Self);
  301. FMasterLink.OnMasterChange:=@MasterChanged;
  302. FMasterLink.OnMasterDisable:=@MasterDisabled;
  303. FIndexFieldList:=TList.Create;
  304. BookmarkSize := SizeOf(Pointer);
  305. FBufferSize := SizeOf(PPDataRecord);
  306. FUpdatedItems:= TFPList.Create;
  307. FUpdatedItems.Capacity:=20;
  308. FAddedItems:= TFPList.Create;
  309. FAddedItems.Capacity:=20;
  310. FOrphanItems:= TFPList.Create;
  311. FOrphanItems.Capacity:=20;
  312. FDeletedItems:= TFPList.Create;
  313. FDeletedItems.Capacity:=20;
  314. inherited Create(AOwner);
  315. end;
  316. function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
  317. begin
  318. Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
  319. end;
  320. destructor TCustomSqliteDataset.Destroy;
  321. begin
  322. inherited Destroy;
  323. FUpdatedItems.Destroy;
  324. FAddedItems.Destroy;
  325. FDeletedItems.Destroy;
  326. FOrphanItems.Destroy;
  327. FMasterLink.Destroy;
  328. FIndexFieldList.Destroy;
  329. // dispose special items
  330. Dispose(FBeginItem);
  331. Dispose(FCacheItem);
  332. Dispose(FEndItem);
  333. end;
  334. function TCustomSqliteDataset.GetIndexFields(Value: Integer): TField;
  335. begin
  336. if (Value < 0) or (Value > FIndexFieldList.Count - 1) then
  337. DatabaseError('Error acessing IndexFields: Index out of bonds',Self);
  338. Result:= TField(FIndexFieldList[Value]);
  339. end;
  340. procedure TCustomSqliteDataset.DisposeLinkedList;
  341. var
  342. TempItem:PDataRecord;
  343. Counter,I:Integer;
  344. begin
  345. //Todo: insert debug info
  346. FDataAllocated:=False;
  347. TempItem:=FBeginItem^.Next;
  348. if TempItem <> nil then
  349. while TempItem^.Next <> nil do
  350. begin
  351. for Counter:= 0 to FRowCount - 1 do
  352. StrDispose(TempItem^.Row[Counter]);
  353. FreeMem(TempItem^.Row,FRowBufferSize);
  354. TempItem:=TempItem^.Next;
  355. Dispose(TempItem^.Previous);
  356. end;
  357. //Dispose FBeginItem.Row
  358. FreeMem(FBeginItem^.Row,FRowBufferSize);
  359. //Dispose cache item
  360. for Counter:= 0 to FRowCount - 1 do
  361. StrDispose(FCacheItem^.Row[Counter]);
  362. FreeMem(FCacheItem^.Row,FRowBufferSize);
  363. //Dispose OrphanItems
  364. for Counter:= 0 to FOrphanItems.Count - 1 do
  365. begin
  366. TempItem:=PDataRecord(FOrphanItems[Counter]);
  367. for I:= 0 to FRowCount - 1 do
  368. StrDispose(TempItem^.Row[I]);
  369. FreeMem(TempItem^.Row,FRowBufferSize);
  370. Dispose(TempItem);
  371. end;
  372. end;
  373. procedure TCustomSqliteDataset.FreeRecordBuffer(var Buffer: PChar);
  374. begin
  375. FreeMem(Buffer);
  376. end;
  377. procedure TCustomSqliteDataset.GetBookmarkData(Buffer: PChar; Data: Pointer);
  378. begin
  379. Pointer(Data^) := PPDataRecord(Buffer)^;
  380. end;
  381. function TCustomSqliteDataset.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
  382. begin
  383. Result := PPDataRecord(Buffer)^^.BookmarkFlag;
  384. end;
  385. function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
  386. var
  387. ValError:Word;
  388. FieldRow:PChar;
  389. begin
  390. FieldRow:=PPDataRecord(ActiveBuffer)^^.Row[Field.FieldNo - 1];
  391. Result := FieldRow <> nil;
  392. if Result and (Buffer <> nil) then //supports GetIsNull
  393. begin
  394. case Field.Datatype of
  395. ftString:
  396. begin
  397. Move(FieldRow^,PChar(Buffer)^,StrLen(FieldRow)+1);
  398. end;
  399. ftInteger,ftAutoInc:
  400. begin
  401. Val(StrPas(FieldRow),LongInt(Buffer^),ValError);
  402. Result:= ValError = 0;
  403. end;
  404. ftBoolean,ftWord:
  405. begin
  406. Val(StrPas(FieldRow),Word(Buffer^),ValError);
  407. Result:= ValError = 0;
  408. end;
  409. ftFloat,ftDateTime,ftTime,ftDate,ftCurrency:
  410. begin
  411. Val(StrPas(FieldRow),Double(Buffer^),ValError);
  412. Result:= ValError = 0;
  413. end;
  414. ftLargeInt:
  415. begin
  416. Val(StrPas(FieldRow),Int64(Buffer^),ValError);
  417. Result:= ValError = 0;
  418. end;
  419. end;
  420. end;
  421. end;
  422. function TCustomSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
  423. begin
  424. Result := grOk;
  425. case GetMode of
  426. gmPrior:
  427. if (FCurrentItem^.Previous = FBeginItem) or (FCurrentItem = FBeginItem) then
  428. begin
  429. Result := grBOF;
  430. FCurrentItem := FBeginItem;
  431. end
  432. else
  433. FCurrentItem:=FCurrentItem^.Previous;
  434. gmCurrent:
  435. if (FCurrentItem = FBeginItem) or (FCurrentItem = FEndItem) then
  436. Result := grError;
  437. gmNext:
  438. if (FCurrentItem = FEndItem) or (FCurrentItem^.Next = FEndItem) then
  439. Result := grEOF
  440. else
  441. FCurrentItem:=FCurrentItem^.Next;
  442. end; //case
  443. if Result = grOk then
  444. begin
  445. PDataRecord(Pointer(Buffer)^):=FCurrentItem;
  446. FCurrentItem^.BookmarkFlag := bfCurrent;
  447. end
  448. else if (Result = grError) and DoCheck then
  449. DatabaseError('No records found',Self);
  450. end;
  451. function TCustomSqliteDataset.GetRecordCount: Integer;
  452. begin
  453. Result := FRecordCount;
  454. end;
  455. function TCustomSqliteDataset.GetRecNo: Integer;
  456. var
  457. TempItem,TempActive:PDataRecord;
  458. begin
  459. Result:= -1;
  460. if FRecordCount = 0 then
  461. Exit;
  462. TempItem:=FBeginItem;
  463. TempActive:=PPDataRecord(ActiveBuffer)^;
  464. if TempActive = FCacheItem then // Record not posted yet
  465. Result:=FRecordCount
  466. else
  467. while TempActive <> TempItem do
  468. begin
  469. if TempItem^.Next <> nil then
  470. begin
  471. inc(Result);
  472. TempItem:=TempItem^.Next;
  473. end
  474. else
  475. begin
  476. Result:=-1;
  477. DatabaseError('Sqliteds.GetRecNo - ActiveItem Not Found',Self);
  478. break;
  479. end;
  480. end;
  481. end;
  482. function TCustomSqliteDataset.GetRecordSize: Word;
  483. begin
  484. Result := FBufferSize; //??
  485. end;
  486. procedure TCustomSqliteDataset.InternalAddRecord(Buffer: Pointer; DoAppend: Boolean);
  487. var
  488. NewItem: PDataRecord;
  489. Counter:Integer;
  490. begin
  491. {$ifdef DEBUG}
  492. if PPDataRecord(Buffer)^ <> FCacheItem then
  493. DatabaseError('PPDataRecord(Buffer) <> FCacheItem - Problem',Self);
  494. {$endif}
  495. New(NewItem);
  496. GetMem(NewItem^.Row,FRowBufferSize);
  497. for Counter := 0 to FRowCount - 1 do
  498. NewItem^.Row[Counter]:=StrNew(FCacheItem^.Row[Counter]);
  499. FEndItem^.Previous^.Next:=NewItem;
  500. NewItem^.Previous:=FEndItem^.Previous;
  501. NewItem^.Next:=FEndItem;
  502. FEndItem^.Previous:=NewItem;
  503. Inc(FRecordCount);
  504. if FAutoIncFieldNo <> - 1 then
  505. Inc(FNextAutoInc);
  506. FAddedItems.Add(NewItem);
  507. end;
  508. procedure TCustomSqliteDataset.InternalClose;
  509. begin
  510. if FSaveOnClose then
  511. ApplyUpdates;
  512. //BindFields(False);
  513. if DefaultFields then
  514. DestroyFields;
  515. if FDataAllocated then
  516. DisposeLinkedList;
  517. if FSqliteHandle <> nil then
  518. begin
  519. SqliteClose(FSqliteHandle);
  520. FSqliteHandle := nil;
  521. end;
  522. FAddedItems.Clear;
  523. FUpdatedItems.Clear;
  524. FDeletedItems.Clear;
  525. FOrphanItems.Clear;
  526. FRecordCount:=0;
  527. end;
  528. procedure TCustomSqliteDataset.InternalDelete;
  529. var
  530. TempItem:PDataRecord;
  531. ValError,TempInteger:Integer;
  532. begin
  533. If FRecordCount = 0 then
  534. Exit;
  535. Dec(FRecordCount);
  536. TempItem:=PPDataRecord(ActiveBuffer)^;
  537. // Remove from changed list
  538. FUpdatedItems.Remove(TempItem);
  539. if FAddedItems.Remove(TempItem) = -1 then
  540. FDeletedItems.Add(TempItem);
  541. FOrphanItems.Add(TempItem);
  542. TempItem^.Next^.Previous:=TempItem^.Previous;
  543. TempItem^.Previous^.Next:=TempItem^.Next;
  544. if FCurrentItem = TempItem then
  545. begin
  546. if FCurrentItem^.Previous <> FBeginItem then
  547. FCurrentItem:= FCurrentItem^.Previous
  548. else
  549. FCurrentItem:= FCurrentItem^.Next;
  550. end;
  551. // Dec FNextAutoInc (only if deleted item is the last record)
  552. if FAutoIncFieldNo <> -1 then
  553. begin
  554. Val(StrPas(TempItem^.Row[FAutoIncFieldNo]),TempInteger,ValError);
  555. if (ValError = 0) and (TempInteger = (FNextAutoInc - 1)) then
  556. Dec(FNextAutoInc);
  557. end;
  558. end;
  559. procedure TCustomSqliteDataset.InternalFirst;
  560. begin
  561. FCurrentItem := FBeginItem;
  562. end;
  563. procedure TCustomSqliteDataset.InternalGotoBookmark(ABookmark: Pointer);
  564. begin
  565. FCurrentItem := PDataRecord(ABookmark^);
  566. end;
  567. procedure TCustomSqliteDataset.InternalInitRecord(Buffer: PChar);
  568. var
  569. Counter:Integer;
  570. TempStr:String;
  571. begin
  572. for Counter:= 0 to FRowCount - 1 do
  573. begin
  574. StrDispose(FCacheItem^.Row[Counter]);
  575. FCacheItem^.Row[Counter]:=nil;
  576. end;
  577. if FAutoIncFieldNo <> - 1 then
  578. begin
  579. Str(FNextAutoInc,TempStr);
  580. FCacheItem^.Row[FAutoIncFieldNo]:=StrAlloc(Length(TempStr)+1);
  581. StrPCopy(FCacheItem^.Row[FAutoIncFieldNo],TempStr);
  582. end;
  583. PPDataRecord(Buffer)^:=FCacheItem;
  584. end;
  585. procedure TCustomSqliteDataset.InternalLast;
  586. begin
  587. FCurrentItem := FEndItem;
  588. end;
  589. procedure TCustomSqliteDataset.InternalOpen;
  590. begin
  591. FAutoIncFieldNo:=-1;
  592. if not FileExists(FFileName) then
  593. DatabaseError('File "'+ExpandFileName(FFileName)+'" not found',Self);
  594. if (FTablename = '') and not (FSqlMode) then
  595. DatabaseError('Tablename not set',Self);
  596. if MasterSource <> nil then
  597. begin
  598. FSql := 'Select * from '+FTableName+';'; // forced to obtain all fields
  599. FMasterLink.FieldNames:=FMasterLink.FieldNames; //workaround to fill MasterLinks.Fields
  600. //if FMasterLink.Fields.Count = 0 MasterChanged will not be called anyway so ignore it
  601. end;
  602. FSqliteHandle:=GetSqliteHandle;
  603. if FSql = '' then
  604. FSql := 'Select * from '+FTableName+';';
  605. InternalInitFieldDefs;
  606. if DefaultFields then
  607. CreateFields;
  608. BindFields(True);
  609. UpdateIndexFields;
  610. if FMasterLink.Active then
  611. begin
  612. if FIndexFieldList.Count <> FMasterLink.Fields.Count then
  613. DatabaseError('MasterFields count doesn''t match IndexFields count',Self);
  614. //Set FSql considering MasterSource active record
  615. SetDetailFilter;
  616. end;
  617. // Get PrimaryKeyNo if available
  618. if Fields.FindField(FPrimaryKey) <> nil then
  619. FPrimaryKeyNo:=Fields.FindField(FPrimaryKey).FieldNo - 1
  620. else
  621. FPrimaryKeyNo:=FAutoIncFieldNo; // -1 if there's no AutoIncField
  622. BuildLinkedList;
  623. FCurrentItem:=FBeginItem;
  624. end;
  625. procedure TCustomSqliteDataset.InternalPost;
  626. begin
  627. if (State<>dsEdit) then
  628. InternalAddRecord(ActiveBuffer,True);
  629. end;
  630. procedure TCustomSqliteDataset.InternalSetToRecord(Buffer: PChar);
  631. begin
  632. FCurrentItem:=PPDataRecord(Buffer)^;
  633. end;
  634. function TCustomSqliteDataset.IsCursorOpen: Boolean;
  635. begin
  636. Result := FDataAllocated;
  637. end;
  638. function TCustomSqliteDataset.FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
  639. var
  640. AValue:String;
  641. AField:TField;
  642. AFieldIndex:Integer;
  643. TempItem:PDataRecord;
  644. begin
  645. Result:=nil;
  646. // Now, it allows to search only one field and ignores options
  647. AField:=Fields.FieldByName(KeyFields); //FieldByName raises an exeception if field not found
  648. AFieldIndex:=AField.FieldNo - 1;
  649. //get float types in appropriate format
  650. if not (AField.DataType in [ftFloat,ftDateTime,ftTime,ftDate]) then
  651. AValue:=keyvalues
  652. else
  653. begin
  654. Str(VarToDateTime(keyvalues),AValue);
  655. AValue:=Trim(AValue);
  656. end;
  657. {$ifdef DEBUG}
  658. writeln('=FindRecord=');
  659. writeln('keyfields: ',keyfields);
  660. writeln('keyvalues: ',keyvalues);
  661. writeln('AValue: ',AValue);
  662. {$endif}
  663. //Search the list
  664. TempItem:=StartItem;
  665. while TempItem <> FEndItem do
  666. begin
  667. if TempItem^.Row[AFieldIndex] <> nil then
  668. begin
  669. if StrComp(TempItem^.Row[AFieldIndex],PChar(AValue)) = 0 then
  670. begin
  671. Result:=TempItem;
  672. if DoResync then
  673. begin
  674. FCurrentItem:=TempItem;
  675. Resync([]);
  676. end;
  677. Break;
  678. end;
  679. end;
  680. TempItem:=TempItem^.Next;
  681. end;
  682. end;
  683. function TCustomSqliteDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
  684. begin
  685. Result:=FindRecordItem(FBeginItem^.Next,KeyFields,KeyValues,Options,True) <> nil;
  686. end;
  687. function TCustomSqliteDataset.LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
  688. begin
  689. Result:=FindRecordItem(PPDataRecord(ActiveBuffer)^^.Next,KeyFields,KeyValues,Options,True) <> nil;
  690. end;
  691. function TCustomSqliteDataset.Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant;
  692. var
  693. TempItem:PDataRecord;
  694. begin
  695. TempItem:=FindRecordItem(FBeginItem^.Next,KeyFields,KeyValues,[],False);
  696. if TempItem <> nil then
  697. Result:=TempItem^.Row[FieldByName(ResultFields).FieldNo - 1]
  698. else
  699. Result:=False;
  700. end;
  701. procedure TCustomSqliteDataset.SetBookmarkData(Buffer: PChar; Data: Pointer);
  702. begin
  703. //The BookMarkData is the Buffer itself;
  704. end;
  705. procedure TCustomSqliteDataset.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
  706. begin
  707. PPDataRecord(Buffer)^^.BookmarkFlag := Value;
  708. end;
  709. procedure TCustomSqliteDataset.SetExpectedAppends(AValue:Integer);
  710. begin
  711. FAddedItems.Capacity:=AValue;
  712. end;
  713. procedure TCustomSqliteDataset.SetExpectedUpdates(AValue:Integer);
  714. begin
  715. FUpdatedItems.Capacity:=AValue;
  716. end;
  717. procedure TCustomSqliteDataset.SetExpectedDeletes(AValue:Integer);
  718. begin
  719. FDeletedItems.Capacity:=AValue;
  720. FOrphanItems.Capacity:=AValue;
  721. end;
  722. procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
  723. var
  724. TempStr:String;
  725. ActiveItem:PDataRecord;
  726. begin
  727. ActiveItem:=PPDataRecord(ActiveBuffer)^;
  728. if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
  729. FUpdatedItems.Add(ActiveItem);
  730. StrDispose(ActiveItem^.Row[Pred(Field.FieldNo)]);
  731. if Buffer <> nil then
  732. begin
  733. case Field.Datatype of
  734. ftString:
  735. begin
  736. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrNew(PChar(Buffer));
  737. end;
  738. ftInteger:
  739. begin
  740. Str(LongInt(Buffer^),TempStr);
  741. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  742. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  743. end;
  744. ftBoolean,ftWord:
  745. begin
  746. Str(Word(Buffer^),TempStr);
  747. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  748. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  749. end;
  750. ftFloat,ftDateTime,ftDate,ftTime,ftCurrency:
  751. begin
  752. Str(Double(Buffer^),TempStr);
  753. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  754. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  755. end;
  756. ftLargeInt:
  757. begin
  758. Str(Int64(Buffer^),TempStr);
  759. ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
  760. StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
  761. end;
  762. end;// case
  763. end//if
  764. else
  765. ActiveItem^.Row[Pred(Field.FieldNo)]:=nil;
  766. if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
  767. DataEvent(deFieldChange, Ptrint(Field));
  768. end;
  769. procedure TCustomSqliteDataset.SetRecNo(Value: Integer);
  770. var
  771. Counter:Integer;
  772. TempItem:PDataRecord;
  773. begin
  774. if (Value >= FRecordCount) or (Value < 0) then
  775. DatabaseError('Record Number Out Of Range',Self);
  776. TempItem:=FBeginItem;
  777. for Counter := 0 to Value do
  778. TempItem:=TempItem^.Next;
  779. FCurrentItem:=TempItem;
  780. Resync([]);
  781. end;
  782. // Specific functions
  783. procedure TCustomSqliteDataset.SetDetailFilter;
  784. function GetSqlStr(AField:TField):String;
  785. begin
  786. case AField.DataType of
  787. ftString,ftMemo: Result:='"'+AField.AsString+'"';//todo: handle " caracter properly
  788. ftDateTime,ftDate,ftTime:Str(AField.AsDateTime,Result);
  789. else
  790. Result:=AField.AsString;
  791. end;//case
  792. end;//function
  793. var
  794. AFilter:String;
  795. i:Integer;
  796. begin
  797. if FMasterLink.Dataset.RecordCount = 0 then //Retrieve all data
  798. FSql:='Select * from '+FTableName
  799. else
  800. begin
  801. AFilter:=' where ';
  802. for i:= 0 to FMasterLink.Fields.Count - 1 do
  803. begin
  804. AFilter:=AFilter + IndexFields[i].FieldName +' = '+ GetSqlStr(TField(FMasterLink.Fields[i]));
  805. if i <> FMasterLink.Fields.Count - 1 then
  806. AFilter:= AFilter + ' and ';
  807. end;
  808. FSql:='Select * from '+FTableName+AFilter;
  809. end;
  810. end;
  811. procedure TCustomSqliteDataset.MasterChanged(Sender: TObject);
  812. begin
  813. SetDetailFilter;
  814. {$ifdef DEBUG}
  815. writeln('Sql used to filter detail dataset:');
  816. writeln(FSql);
  817. {$endif}
  818. RefetchData;
  819. end;
  820. procedure TCustomSqliteDataset.MasterDisabled(Sender: TObject);
  821. begin
  822. FSql:='Select * from '+FTableName+';';
  823. RefetchData;
  824. end;
  825. procedure TCustomSqliteDataset.SetMasterFields(Value: String);
  826. begin
  827. FMasterLink.FieldNames:=Value;
  828. if Active and FMasterLink.Active then
  829. begin
  830. UpdateIndexFields;
  831. if (FIndexFieldList.Count <> FMasterLink.Fields.Count) then
  832. DatabaseError('MasterFields count doesn''t match IndexFields count',Self);
  833. end;
  834. end;
  835. function TCustomSqliteDataset.GetMasterFields: String;
  836. begin
  837. Result:=FMasterLink.FieldNames;
  838. end;
  839. procedure TCustomSqliteDataset.UpdateIndexFields;
  840. begin
  841. FIndexFieldList.Clear;
  842. if FIndexFieldNames <> '' then
  843. begin
  844. try
  845. GetFieldList(FIndexFieldList, FIndexFieldNames);
  846. except
  847. FIndexFieldList.Clear;
  848. raise;
  849. end;
  850. end;
  851. end;
  852. function TCustomSqliteDataset.GetMasterSource: TDataSource;
  853. begin
  854. Result := FMasterLink.DataSource;
  855. end;
  856. procedure TCustomSqliteDataset.SetMasterSource(Value: TDataSource);
  857. begin
  858. FMasterLink.DataSource := Value;
  859. end;
  860. function TCustomSqliteDataset.ExecSQL(const ASql:String):Integer;
  861. var
  862. AHandle: Pointer;
  863. begin
  864. Result:=0;
  865. //Todo check if Filename exists
  866. if FSqliteHandle <> nil then
  867. AHandle:=FSqliteHandle
  868. else
  869. if FFileName <> '' then
  870. AHandle := GetSqliteHandle
  871. else
  872. DatabaseError ('ExecSql - FileName not set',Self);
  873. FSqliteReturnId:= SqliteExec(AHandle,PChar(ASql));
  874. //todo: add a way to get the num of changes
  875. //Result:=sqlite_changes(AHandle);
  876. if AHandle <> FSqliteHandle then
  877. SqliteClose(AHandle);
  878. end;
  879. function TCustomSqliteDataset.ExecSQL:Integer;
  880. begin
  881. Result:=ExecSQL(FSql);
  882. end;
  883. function TCustomSqliteDataset.ApplyUpdates:Boolean;
  884. var
  885. CounterFields,CounterItems,StatementsCounter:Integer;
  886. SqlTemp,KeyName,ASqlLine,TemplateStr:String;
  887. begin
  888. if not UpdatesPending then
  889. begin
  890. Result:=True;
  891. Exit;
  892. end;
  893. Result:=False;
  894. if (FPrimaryKeyNo <> -1) and not FSqlMode then
  895. begin
  896. StatementsCounter:=0;
  897. KeyName:=Fields[FPrimaryKeyNo].FieldName;
  898. {$ifdef DEBUG}
  899. WriteLn('ApplyUpdates called');
  900. if FPrimaryKeyNo = FAutoIncFieldNo then
  901. WriteLn('Using an AutoInc field as primary key');
  902. WriteLn('PrimaryKey: ',KeyName);
  903. WriteLn('PrimaryKeyNo: ',FPrimaryKeyNo);
  904. {$endif}
  905. SqlTemp:='BEGIN TRANSACTION;';
  906. // Update changed records
  907. if FUpdatedItems.Count > 0 then
  908. TemplateStr:='UPDATE '+FTableName+' SET ';
  909. for CounterItems:= 0 to FUpdatedItems.Count - 1 do
  910. begin
  911. ASqlLine:=TemplateStr;
  912. for CounterFields:= 0 to Fields.Count - 1 do
  913. begin
  914. if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
  915. begin
  916. ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = ';
  917. if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
  918. ASqlLine:=ASqlLine+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+ ','
  919. else
  920. ASqlLine:=ASqlLine+''''+
  921. AnsiReplaceStr(StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields]),'''','''''')+''',';
  922. end
  923. else
  924. ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = NULL,';
  925. end;
  926. //Todo: see if system.delete trunks AnsiString
  927. system.delete(ASqlLine,Length(ASqlLine),1);
  928. SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
  929. inc(StatementsCounter);
  930. //ApplyUpdates each 400 statements
  931. if StatementsCounter = 400 then
  932. begin
  933. SqlTemp:=SqlTemp+'END TRANSACTION;';
  934. FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
  935. StatementsCounter:=0;
  936. SqlTemp:='BEGIN TRANSACTION;';
  937. end;
  938. end;
  939. // Add new records
  940. // Build TemplateStr
  941. if FAddedItems.Count > 0 then
  942. begin
  943. TemplateStr:='INSERT INTO '+FTableName+ ' (';
  944. for CounterFields:= 0 to Fields.Count - 1 do
  945. begin
  946. TemplateStr:=TemplateStr + Fields[CounterFields].FieldName;
  947. if CounterFields <> Fields.Count - 1 then
  948. TemplateStr:=TemplateStr+',';
  949. end;
  950. TemplateStr:=TemplateStr+') VALUES (';
  951. end;
  952. for CounterItems:= 0 to FAddedItems.Count - 1 do
  953. begin
  954. ASqlLine:=TemplateStr;
  955. for CounterFields:= 0 to Fields.Count - 1 do
  956. begin
  957. if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
  958. begin
  959. if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
  960. ASqlLine:=ASqlLine+StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])
  961. else
  962. ASqlLine:=ASqlLine+''''+
  963. AnsiReplaceStr(StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields]),'''','''''')+'''';
  964. end
  965. else
  966. ASqlLine:=ASqlLine + 'NULL';
  967. //Todo: see if delete ASqline is faster
  968. if CounterFields <> Fields.Count - 1 then
  969. ASqlLine:=ASqlLine+',';
  970. end;
  971. SqlTemp:=SqlTemp+ASqlLine+');';
  972. inc(StatementsCounter);
  973. //ApplyUpdates each 400 statements
  974. if StatementsCounter = 400 then
  975. begin
  976. SqlTemp:=SqlTemp+'END TRANSACTION;';
  977. FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
  978. StatementsCounter:=0;
  979. SqlTemp:='BEGIN TRANSACTION;';
  980. end;
  981. end;
  982. // Delete Items
  983. if FDeletedItems.Count > 0 then
  984. TemplateStr:='DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = ';
  985. for CounterItems:= 0 to FDeletedItems.Count - 1 do
  986. begin
  987. SqlTemp:=SqlTemp+TemplateStr+
  988. StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
  989. inc(StatementsCounter);
  990. //ApplyUpdates each 400 statements
  991. if StatementsCounter = 400 then
  992. begin
  993. SqlTemp:=SqlTemp+'END TRANSACTION;';
  994. FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
  995. StatementsCounter:=0;
  996. SqlTemp:='BEGIN TRANSACTION;';
  997. end;
  998. end;
  999. SqlTemp:=SqlTemp+'END TRANSACTION;';
  1000. {$ifdef DEBUG}
  1001. writeln('ApplyUpdates Sql: ',SqlTemp);
  1002. {$endif}
  1003. FAddedItems.Clear;
  1004. FUpdatedItems.Clear;
  1005. FDeletedItems.Clear;
  1006. FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
  1007. Result:= FSqliteReturnId = SQLITE_OK;
  1008. end;
  1009. {$ifdef DEBUG}
  1010. writeln('ApplyUpdates Result: ',Result);
  1011. {$endif}
  1012. end;
  1013. function TCustomSqliteDataset.CreateTable: Boolean;
  1014. var
  1015. SqlTemp:String;
  1016. Counter:Integer;
  1017. begin
  1018. {$ifdef DEBUG}
  1019. if FTableName = '' then
  1020. WriteLn('CreateTable : TableName Not Set');
  1021. if FieldDefs.Count = 0 then
  1022. WriteLn('CreateTable : FieldDefs Not Initialized');
  1023. {$endif}
  1024. if (FTableName <> '') and (FieldDefs.Count > 0) then
  1025. begin
  1026. FSqliteHandle:= GetSqliteHandle;
  1027. SqlTemp:='CREATE TABLE '+FTableName+' (';
  1028. for Counter := 0 to FieldDefs.Count-1 do
  1029. begin
  1030. SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
  1031. case FieldDefs[Counter].DataType of
  1032. ftInteger:
  1033. SqlTemp:=SqlTemp + ' INTEGER';
  1034. ftString:
  1035. SqlTemp:=SqlTemp + ' VARCHAR';
  1036. ftBoolean:
  1037. SqlTemp:=SqlTemp + ' BOOLEAN';
  1038. ftFloat:
  1039. SqlTemp:=SqlTemp + ' FLOAT';
  1040. ftWord:
  1041. SqlTemp:=SqlTemp + ' WORD';
  1042. ftDateTime:
  1043. SqlTemp:=SqlTemp + ' DATETIME';
  1044. ftDate:
  1045. SqlTemp:=SqlTemp + ' DATE';
  1046. ftTime:
  1047. SqlTemp:=SqlTemp + ' TIME';
  1048. ftLargeInt:
  1049. SqlTemp:=SqlTemp + ' LARGEINT';
  1050. ftCurrency:
  1051. SqlTemp:=SqlTemp + ' CURRENCY';
  1052. ftAutoInc:
  1053. SqlTemp:=SqlTemp + ' AUTOINC';
  1054. ftMemo:
  1055. SqlTemp:=SqlTemp + ' MEMO';
  1056. else
  1057. DatabaseError('Field type "'+FieldTypeNames[FieldDefs[Counter].DataType]+'" not supported',Self);
  1058. end;
  1059. if Counter <> FieldDefs.Count - 1 then
  1060. SqlTemp:=SqlTemp+ ' , ';
  1061. end;
  1062. SqlTemp:=SqlTemp+');';
  1063. {$ifdef DEBUG}
  1064. writeln('CreateTable Sql: ',SqlTemp);
  1065. {$endif}
  1066. FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
  1067. Result:= FSqliteReturnId = SQLITE_OK;
  1068. SqliteClose(FSqliteHandle);
  1069. FSqliteHandle:=nil;
  1070. end
  1071. else
  1072. Result:=False;
  1073. end;
  1074. procedure TCustomSqliteDataset.RefetchData;
  1075. var
  1076. i:Integer;
  1077. begin
  1078. //Close
  1079. if FSaveOnRefetch then
  1080. ApplyUpdates;
  1081. if FDataAllocated then
  1082. DisposeLinkedList;
  1083. FAddedItems.Clear;
  1084. FUpdatedItems.Clear;
  1085. FDeletedItems.Clear;
  1086. FOrphanItems.Clear;
  1087. //Reopen
  1088. BuildLinkedList;
  1089. FCurrentItem:=FBeginItem;
  1090. for i := 0 to BufferCount - 1 do
  1091. PPDataRecord(Buffers[i])^:=FBeginItem;
  1092. Resync([]);
  1093. end;
  1094. function TCustomSqliteDataset.UpdatesPending: Boolean;
  1095. begin
  1096. //Sometimes FBeginItem is inserted in FUpdatedItems
  1097. FUpdatedItems.Remove(FBeginItem);
  1098. Result:= (FUpdatedItems.Count > 0) or
  1099. (FAddedItems.Count > 0) or (FDeletedItems.Count > 0);
  1100. end;
  1101. function TCustomSqliteDataset.QuickQuery(const ASql:String):String;
  1102. begin
  1103. Result:=QuickQuery(ASql,nil,False);
  1104. end;
  1105. function TCustomSqliteDataset.QuickQuery(const ASql:String;const AStrList: TStrings):String;
  1106. begin
  1107. Result:=QuickQuery(ASql,AStrList,False)
  1108. end;
  1109. {$ifdef DEBUGACTIVEBUFFER}
  1110. procedure TCustomSqliteDataset.SetCurrentItem(Value:PDataRecord);
  1111. var
  1112. ANo:Integer;
  1113. function GetItemPos:Integer;
  1114. var
  1115. TempItem:PDataRecord;
  1116. begin
  1117. Result:= -1;
  1118. TempItem:=FBeginItem;
  1119. if Value = FCacheItem then
  1120. Result:=-2
  1121. else
  1122. while Value <> TempItem do
  1123. begin
  1124. if TempItem^.Next <> nil then
  1125. begin
  1126. inc(Result);
  1127. TempItem:=TempItem^.Next;
  1128. end
  1129. else
  1130. begin
  1131. Result:=-1;
  1132. break;
  1133. end;
  1134. end;
  1135. end;
  1136. begin
  1137. if Value = FBeginItem then
  1138. begin
  1139. writeln('FCurrentItem set to FBeginItem: ',IntToHex(Integer(Value),0));
  1140. FFCurrentItem:=Value;
  1141. end
  1142. else
  1143. if Value = FEndItem then
  1144. begin
  1145. writeln('FCurrentItem set to FEndItem: ',IntToHex(Integer(Value),0));
  1146. FFCurrentItem:=Value;
  1147. end
  1148. else
  1149. if Value = FCacheItem then
  1150. begin
  1151. writeln('FCurrentItem set to FCacheItem: ',IntToHex(Integer(Value),0));
  1152. FFCurrentItem:=Value;
  1153. end
  1154. else
  1155. begin
  1156. writeln('FCurrentItem set from ',IntToHex(Integer(FFCurrentItem),0),' to ',IntToHex(Integer(Value),0));
  1157. Ano:=GetItemPos;
  1158. writeln('Item position is ',ANo);
  1159. FFCurrentItem:=Value;
  1160. end;
  1161. end;
  1162. {$endif}
  1163. end.