customsqliteds.pas 37 KB

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