customsqliteds.pas 39 KB

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