mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 20:09:18 +02:00
* Patch fom Luiz Americo
- Add support to TMemoField - Add RefetchData, TableExists, UpdatesPending methods - Optimize ApplyUpdates when using/creating big datasets - Other fixes/improvements
This commit is contained in:
parent
df057f7550
commit
7b3db009fc
@ -28,7 +28,11 @@ unit sqliteds;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses Classes, SysUtils, Db;
|
uses Classes, SysUtils, Db
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
,Crt
|
||||||
|
{$endif}
|
||||||
|
;
|
||||||
|
|
||||||
type
|
type
|
||||||
PDataRecord = ^DataRecord;
|
PDataRecord = ^DataRecord;
|
||||||
@ -40,7 +44,24 @@ type
|
|||||||
Next: PDataRecord;
|
Next: PDataRecord;
|
||||||
Previous: PDataRecord;
|
Previous: PDataRecord;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TDSStream = class(TStream)
|
||||||
|
private
|
||||||
|
FActiveItem:PDataRecord;
|
||||||
|
FFieldRow:PChar;
|
||||||
|
FFieldIndex:Integer;
|
||||||
|
FRowSize: Integer;
|
||||||
|
FPosition: Longint;
|
||||||
|
public
|
||||||
|
constructor Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
|
||||||
|
function Write(const Buffer; Count: Longint): Longint; override;
|
||||||
|
function Read(var Buffer; Count: Longint): Longint; override;
|
||||||
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
||||||
|
// function Seek(Offset: Int64; Origin: TSeekOrigin): Int64; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSqliteDataset }
|
||||||
|
|
||||||
TSqliteDataset = class(TDataSet)
|
TSqliteDataset = class(TDataSet)
|
||||||
private
|
private
|
||||||
FFileName: String;
|
FFileName: String;
|
||||||
@ -64,8 +85,9 @@ type
|
|||||||
FSqliteReturnId: Integer;
|
FSqliteReturnId: Integer;
|
||||||
FDataAllocated: Boolean;
|
FDataAllocated: Boolean;
|
||||||
FSaveOnClose: Boolean;
|
FSaveOnClose: Boolean;
|
||||||
|
FSaveOnRefetch: Boolean;
|
||||||
|
FComplexSql: Boolean;
|
||||||
FSqliteHandle: Pointer;
|
FSqliteHandle: Pointer;
|
||||||
FDBError: PPChar;
|
|
||||||
FUpdatedItems: TList;
|
FUpdatedItems: TList;
|
||||||
FAddedItems: TList;
|
FAddedItems: TList;
|
||||||
FDeletedItems: TList;
|
FDeletedItems: TList;
|
||||||
@ -74,6 +96,7 @@ type
|
|||||||
procedure DisposeLinkedList;
|
procedure DisposeLinkedList;
|
||||||
protected
|
protected
|
||||||
function AllocRecordBuffer: PChar; override;
|
function AllocRecordBuffer: PChar; override;
|
||||||
|
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
|
||||||
procedure FreeRecordBuffer(var Buffer: PChar); override;
|
procedure FreeRecordBuffer(var Buffer: PChar); override;
|
||||||
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
|
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
|
||||||
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
|
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
|
||||||
@ -110,7 +133,10 @@ type
|
|||||||
function CreateTable: Boolean;
|
function CreateTable: Boolean;
|
||||||
function ExecSQL:Integer;
|
function ExecSQL:Integer;
|
||||||
function ExecSQL(ASql:String):Integer;
|
function ExecSQL(ASql:String):Integer;
|
||||||
|
function TableExists: Boolean;
|
||||||
|
procedure RefetchData;
|
||||||
function SqliteReturnString: String;
|
function SqliteReturnString: String;
|
||||||
|
function UpdatesPending: Boolean;
|
||||||
{$ifdef USE_SQLITEDS_INTERNALS}
|
{$ifdef USE_SQLITEDS_INTERNALS}
|
||||||
property BeginItem: PDataRecord read FBeginItem;
|
property BeginItem: PDataRecord read FBeginItem;
|
||||||
property EndItem: PDataRecord read FEndItem;
|
property EndItem: PDataRecord read FEndItem;
|
||||||
@ -118,6 +144,7 @@ type
|
|||||||
property AddedItems: TList read FAddedItems;
|
property AddedItems: TList read FAddedItems;
|
||||||
property DeletedItems: TList read FDeletedItems;
|
property DeletedItems: TList read FDeletedItems;
|
||||||
{$endif}
|
{$endif}
|
||||||
|
property ComplexSql: Boolean read FComplexSql write FComplexSql;
|
||||||
property ExpectedAppends: Integer read FExpectedAppends write SetExpectedAppends;
|
property ExpectedAppends: Integer read FExpectedAppends write SetExpectedAppends;
|
||||||
property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
|
property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
|
||||||
property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
|
property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
|
||||||
@ -126,6 +153,7 @@ type
|
|||||||
property FileName: String read FFileName write FFileName;
|
property FileName: String read FFileName write FFileName;
|
||||||
property IndexFieldName: String read FIndexFieldName write FIndexFieldName;
|
property IndexFieldName: String read FIndexFieldName write FIndexFieldName;
|
||||||
property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
|
property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
|
||||||
|
property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch;
|
||||||
property SQL: String read FSql write FSql;
|
property SQL: String read FSql write FSql;
|
||||||
property TableName: String read FTableName write FTableName;
|
property TableName: String read FTableName write FTableName;
|
||||||
//property Active;
|
//property Active;
|
||||||
@ -156,7 +184,7 @@ type
|
|||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses SQLite;
|
uses SQLite,strutils;
|
||||||
|
|
||||||
function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
|
function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
|
||||||
var
|
var
|
||||||
@ -216,11 +244,12 @@ begin
|
|||||||
begin
|
begin
|
||||||
AType:= ftTime;
|
AType:= ftTime;
|
||||||
FieldSize:=SizeOf(TDateTime);
|
FieldSize:=SizeOf(TDateTime);
|
||||||
|
end else if (ColumnStr = 'MEMO') then
|
||||||
|
begin
|
||||||
|
AType:= ftMemo;
|
||||||
|
FieldSize:=10;//??
|
||||||
end else if (ColumnStr = 'AUTOINC') then
|
end else if (ColumnStr = 'AUTOINC') then
|
||||||
begin
|
begin
|
||||||
//Todo: remove this check. do it in open
|
|
||||||
if TSqliteDataset(TheDataset).Tablename = '' then
|
|
||||||
DatabaseError('Sqliteds - AutoInc fields requires Tablename to be set');
|
|
||||||
AType:= ftAutoInc;
|
AType:= ftAutoInc;
|
||||||
FieldSize:=SizeOf(Integer);
|
FieldSize:=SizeOf(Integer);
|
||||||
if TSqliteDataset(TheDataset).FAutoIncFieldNo = -1 then
|
if TSqliteDataset(TheDataset).FAutoIncFieldNo = -1 then
|
||||||
@ -234,6 +263,95 @@ begin
|
|||||||
end;
|
end;
|
||||||
result:=-1;
|
result:=-1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// TDSStream
|
||||||
|
|
||||||
|
constructor TDSStream.Create(const ActiveItem: PDataRecord; FieldIndex:Integer);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FPosition:=0;
|
||||||
|
FActiveItem:=ActiveItem;
|
||||||
|
FFieldIndex:=FieldIndex;
|
||||||
|
FFieldRow:=ActiveItem^.Row[FieldIndex];
|
||||||
|
if FFieldRow <> nil then
|
||||||
|
FRowSize:=StrLen(FFieldRow)
|
||||||
|
else
|
||||||
|
FRowSize:=0;
|
||||||
|
end;
|
||||||
|
{
|
||||||
|
function TDSMemoryStream.Seek(Offset: Int64; Origin: TSeekOrigin): Int64;
|
||||||
|
begin
|
||||||
|
Case Origin of
|
||||||
|
soBeginning : FPosition:=Offset;
|
||||||
|
soEnd : FPosition:=FRowSize+Offset;
|
||||||
|
soCurrent : FPosition:=FPosition+Offset;
|
||||||
|
end;
|
||||||
|
Result:=FPosition;
|
||||||
|
end;
|
||||||
|
}
|
||||||
|
function TDSStream.Seek(Offset: Longint; Origin: Word): Longint;
|
||||||
|
begin
|
||||||
|
Case Origin of
|
||||||
|
soFromBeginning : FPosition:=Offset;
|
||||||
|
soFromEnd : FPosition:=FRowSize+Offset;
|
||||||
|
soFromCurrent : FPosition:=FPosition+Offset;
|
||||||
|
end;
|
||||||
|
Result:=FPosition;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDSStream.Write(const Buffer; Count: Longint): Longint;
|
||||||
|
var
|
||||||
|
NewRow:PChar;
|
||||||
|
begin
|
||||||
|
Result:=Count;
|
||||||
|
if Count = 0 then
|
||||||
|
Exit;
|
||||||
|
//Todo: see how TDbMemo read/write to field and choose best if order
|
||||||
|
if FPosition = 0 then
|
||||||
|
begin
|
||||||
|
NewRow:=StrAlloc(Count+1);
|
||||||
|
(NewRow+Count)^:=#0;
|
||||||
|
Move(Buffer,NewRow^,Count);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
NewRow:=StrAlloc(FRowSize+Count+1);
|
||||||
|
(NewRow+Count+FRowSize)^:=#0;
|
||||||
|
Move(FFieldRow^,NewRow^,FRowSize);
|
||||||
|
Move(Buffer,(NewRow+FRowSize)^,Count);
|
||||||
|
end;
|
||||||
|
FActiveItem^.Row[FFieldIndex]:=NewRow;
|
||||||
|
StrDispose(FFieldRow);
|
||||||
|
FFieldRow:=NewRow;
|
||||||
|
FRowSize:=StrLen(NewRow);
|
||||||
|
Inc(FPosition,Count);
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('Writing a BlobStream');
|
||||||
|
WriteLn('Stream.Size: ',StrLen(NewRow));
|
||||||
|
WriteLn('Stream Value: ',NewRow);
|
||||||
|
WriteLn('FPosition:',FPosition);
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TDSStream.Read(var Buffer; Count: Longint): Longint;
|
||||||
|
var
|
||||||
|
BytesToMove:Integer;
|
||||||
|
begin
|
||||||
|
if (FRowSize - FPosition) >= Count then
|
||||||
|
BytesToMove:=Count
|
||||||
|
else
|
||||||
|
BytesToMove:=FRowSize - FPosition;
|
||||||
|
Move((FFieldRow+FPosition)^,Buffer,BytesToMove);
|
||||||
|
Inc(FPosition,BytesToMove);
|
||||||
|
Result:=BytesToMove;
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('Reading a BlobStream');
|
||||||
|
WriteLn('Bytes requested: ',Count);
|
||||||
|
WriteLn('Bytes Moved: ',BytesToMove);
|
||||||
|
WriteLn('Stream.Size: ',FRowSize);
|
||||||
|
WriteLn('Stream Value: ',FFieldRow);
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
|
||||||
// TSqliteDataset override methods
|
// TSqliteDataset override methods
|
||||||
|
|
||||||
@ -310,6 +428,7 @@ end;
|
|||||||
|
|
||||||
constructor TSqliteDataset.Create(AOwner: TComponent);
|
constructor TSqliteDataset.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
|
//FComplexSql:=False;
|
||||||
BookmarkSize := SizeOf(Pointer);
|
BookmarkSize := SizeOf(Pointer);
|
||||||
FBufferSize := SizeOf(PPDataRecord);
|
FBufferSize := SizeOf(PPDataRecord);
|
||||||
FUpdatedItems:= TList.Create;
|
FUpdatedItems:= TList.Create;
|
||||||
@ -320,10 +439,14 @@ begin
|
|||||||
FOrphanItems.Capacity:=20;
|
FOrphanItems.Capacity:=20;
|
||||||
FDeletedItems:= TList.Create;
|
FDeletedItems:= TList.Create;
|
||||||
FDeletedItems.Capacity:=20;
|
FDeletedItems.Capacity:=20;
|
||||||
FSaveOnClose:=False;
|
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
|
||||||
|
begin
|
||||||
|
Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
|
||||||
|
end;
|
||||||
|
|
||||||
destructor TSqliteDataset.Destroy;
|
destructor TSqliteDataset.Destroy;
|
||||||
begin
|
begin
|
||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
@ -338,7 +461,7 @@ var
|
|||||||
TempItem:PDataRecord;
|
TempItem:PDataRecord;
|
||||||
Counter,I:Integer;
|
Counter,I:Integer;
|
||||||
begin
|
begin
|
||||||
//Todo insert debug info
|
//Todo: insert debug info
|
||||||
FDataAllocated:=False;
|
FDataAllocated:=False;
|
||||||
//Dispose cache item
|
//Dispose cache item
|
||||||
for Counter:= 0 to FRowCount - 1 do
|
for Counter:= 0 to FRowCount - 1 do
|
||||||
@ -541,6 +664,7 @@ end;
|
|||||||
procedure TSqliteDataset.InternalDelete;
|
procedure TSqliteDataset.InternalDelete;
|
||||||
var
|
var
|
||||||
TempItem:PDataRecord;
|
TempItem:PDataRecord;
|
||||||
|
ValError,TempInteger:Integer;
|
||||||
begin
|
begin
|
||||||
If FRecordCount = 0 then
|
If FRecordCount = 0 then
|
||||||
Exit;
|
Exit;
|
||||||
@ -560,10 +684,13 @@ begin
|
|||||||
else
|
else
|
||||||
FCurrentItem:= FCurrentItem^.Next;
|
FCurrentItem:= FCurrentItem^.Next;
|
||||||
end;
|
end;
|
||||||
// Dec FNextAutoInc
|
// Dec FNextAutoInc (only if deleted item is the last record)
|
||||||
if FAutoIncFieldNo <> -1 then
|
if FAutoIncFieldNo <> -1 then
|
||||||
if StrToInt(StrPas(TempItem^.Row[FAutoIncFieldNo])) = (FNextAutoInc - 1) then
|
begin
|
||||||
|
Val(StrPas(TempItem^.Row[FAutoIncFieldNo]),TempInteger,ValError);
|
||||||
|
if (ValError = 0) and (TempInteger = (FNextAutoInc - 1)) then
|
||||||
Dec(FNextAutoInc);
|
Dec(FNextAutoInc);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSqliteDataset.InternalFirst;
|
procedure TSqliteDataset.InternalFirst;
|
||||||
@ -622,8 +749,12 @@ procedure TSqliteDataset.InternalOpen;
|
|||||||
begin
|
begin
|
||||||
FAutoIncFieldNo:=-1;
|
FAutoIncFieldNo:=-1;
|
||||||
if not FileExists(FFileName) then
|
if not FileExists(FFileName) then
|
||||||
DatabaseError('File '+FFileName+' not found',Self);
|
DatabaseError('TSqliteDataset - File '+FFileName+' not found');
|
||||||
|
if (FTablename = '') and not (FComplexSql) then
|
||||||
|
DatabaseError('TSqliteDataset - Tablename not set');
|
||||||
FSqliteHandle:=sqlite_open(PChar(FFileName),0,nil);
|
FSqliteHandle:=sqlite_open(PChar(FFileName),0,nil);
|
||||||
|
if FSql = '' then
|
||||||
|
FSql := 'Select * from '+FTableName+';';
|
||||||
InternalInitFieldDefs;
|
InternalInitFieldDefs;
|
||||||
if DefaultFields then
|
if DefaultFields then
|
||||||
CreateFields;
|
CreateFields;
|
||||||
@ -748,77 +879,116 @@ end;
|
|||||||
|
|
||||||
function TSqliteDataset.ApplyUpdates:Boolean;
|
function TSqliteDataset.ApplyUpdates:Boolean;
|
||||||
var
|
var
|
||||||
CounterFields,CounterItems:Integer;
|
CounterFields,CounterItems,StatementsCounter:Integer;
|
||||||
SqlTemp,KeyName:String;
|
SqlTemp,KeyName,ASqlLine,TemplateStr:String;
|
||||||
Quote:Char;
|
|
||||||
begin
|
begin
|
||||||
Result:=False;
|
Result:=False;
|
||||||
if (FTableName <> '') and (FIndexFieldNo <> -1) then
|
if (FIndexFieldNo <> -1) and not FComplexSql then
|
||||||
begin
|
begin
|
||||||
|
StatementsCounter:=0;
|
||||||
KeyName:=Fields[FIndexFieldNo].FieldName;
|
KeyName:=Fields[FIndexFieldNo].FieldName;
|
||||||
{$ifdef DEBUG}
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('ApplyUpdates called');
|
||||||
if FIndexFieldNo = FAutoIncFieldNo then
|
if FIndexFieldNo = FAutoIncFieldNo then
|
||||||
WriteLn('Using an AutoInc field as primary key');
|
WriteLn('Using an AutoInc field as primary key');
|
||||||
WriteLn('IndexFieldName: ',KeyName);
|
WriteLn('IndexFieldName: ',KeyName);
|
||||||
WriteLn('IndexFieldNo: ',FIndexFieldNo);
|
WriteLn('IndexFieldNo: ',FIndexFieldNo);
|
||||||
{$endif}
|
{$endif}
|
||||||
SqlTemp:='BEGIN TRANSACTION; ';
|
SqlTemp:='BEGIN TRANSACTION;';
|
||||||
// Update changed records
|
// Update changed records
|
||||||
For CounterItems:= 0 to FUpdatedItems.Count - 1 do
|
if FUpdatedItems.Count > 0 then
|
||||||
|
TemplateStr:='UPDATE '+FTableName+' SET ';
|
||||||
|
for CounterItems:= 0 to FUpdatedItems.Count - 1 do
|
||||||
begin
|
begin
|
||||||
SqlTemp:=SqlTemp+'UPDATE '+FTableName+' SET ';
|
ASqlLine:=TemplateStr;
|
||||||
for CounterFields:= 0 to Fields.Count - 1 do
|
for CounterFields:= 0 to Fields.Count - 1 do
|
||||||
begin
|
begin
|
||||||
if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
|
if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
|
||||||
begin
|
begin
|
||||||
if Fields[CounterFields].DataType = ftString then
|
ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = ';
|
||||||
Quote:='"'
|
if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
|
||||||
|
ASqlLine:=ASqlLine+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+ ','
|
||||||
else
|
else
|
||||||
Quote:=' ';
|
ASqlLine:=ASqlLine+''''+
|
||||||
SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = '+Quote+
|
AnsiReplaceStr(StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields]),'''','''''')+''',';
|
||||||
StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+Quote+' , ';
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
SqlTemp:=SqlTemp + Fields[CounterFields].FieldName +' = NULL , ';
|
ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = NULL,';
|
||||||
end;
|
end;
|
||||||
system.delete(SqlTemp,Length(SqlTemp)-2,2);
|
//Todo: see if system.delete trunks AnsiString
|
||||||
SqlTemp:=SqlTemp+'WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FIndexFieldNo])+';';
|
system.delete(ASqlLine,Length(ASqlLine),1);
|
||||||
|
SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FIndexFieldNo])+';';
|
||||||
|
inc(StatementsCounter);
|
||||||
|
//ApplyUpdates each 400 statements
|
||||||
|
if StatementsCounter = 400 then
|
||||||
|
begin
|
||||||
|
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||||
|
FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
|
||||||
|
StatementsCounter:=0;
|
||||||
|
SqlTemp:='BEGIN TRANSACTION;';
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
// Add new records
|
// Add new records
|
||||||
For CounterItems:= 0 to FAddedItems.Count - 1 do
|
// Build TemplateStr
|
||||||
|
if FAddedItems.Count > 0 then
|
||||||
begin
|
begin
|
||||||
SqlTemp:=SqlTemp+'INSERT INTO '+FTableName+ ' ( ';
|
TemplateStr:='INSERT INTO '+FTableName+ ' (';
|
||||||
for CounterFields:= 0 to Fields.Count - 1 do
|
for CounterFields:= 0 to Fields.Count - 1 do
|
||||||
begin
|
begin
|
||||||
SqlTemp:=SqlTemp + Fields[CounterFields].FieldName;
|
TemplateStr:=TemplateStr + Fields[CounterFields].FieldName;
|
||||||
if CounterFields <> Fields.Count - 1 then
|
if CounterFields <> Fields.Count - 1 then
|
||||||
SqlTemp:=SqlTemp+' , ';
|
TemplateStr:=TemplateStr+',';
|
||||||
end;
|
end;
|
||||||
SqlTemp:=SqlTemp+') VALUES ( ';
|
TemplateStr:=TemplateStr+') VALUES (';
|
||||||
|
end;
|
||||||
|
for CounterItems:= 0 to FAddedItems.Count - 1 do
|
||||||
|
begin
|
||||||
|
ASqlLine:=TemplateStr;
|
||||||
for CounterFields:= 0 to Fields.Count - 1 do
|
for CounterFields:= 0 to Fields.Count - 1 do
|
||||||
begin
|
begin
|
||||||
if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
|
if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
|
||||||
begin
|
begin
|
||||||
if Fields[CounterFields].DataType = ftString then
|
if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
|
||||||
Quote:='"'
|
ASqlLine:=ASqlLine+StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])
|
||||||
else
|
else
|
||||||
Quote:=' ';
|
ASqlLine:=ASqlLine+''''+
|
||||||
SqlTemp:=SqlTemp + Quote+ StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])+Quote;
|
AnsiReplaceStr(StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields]),'''','''''')+'''';
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
SqlTemp:=SqlTemp + 'NULL';
|
ASqlLine:=ASqlLine + 'NULL';
|
||||||
|
//Todo: see if delete ASqline is faster
|
||||||
if CounterFields <> Fields.Count - 1 then
|
if CounterFields <> Fields.Count - 1 then
|
||||||
SqlTemp:=SqlTemp+' , ';
|
ASqlLine:=ASqlLine+',';
|
||||||
end;
|
end;
|
||||||
SqlTemp:=SqlTemp+') ;';
|
SqlTemp:=SqlTemp+ASqlLine+');';
|
||||||
|
inc(StatementsCounter);
|
||||||
|
//ApplyUpdates each 400 statements
|
||||||
|
if StatementsCounter = 400 then
|
||||||
|
begin
|
||||||
|
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||||
|
FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
|
||||||
|
StatementsCounter:=0;
|
||||||
|
SqlTemp:='BEGIN TRANSACTION;';
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
// Delete Items
|
// Delete Items
|
||||||
For CounterItems:= 0 to FDeletedItems.Count - 1 do
|
if FDeletedItems.Count > 0 then
|
||||||
|
TemplateStr:='DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = ';
|
||||||
|
for CounterItems:= 0 to FDeletedItems.Count - 1 do
|
||||||
begin
|
begin
|
||||||
SqlTemp:=SqlTemp+'DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = '+
|
SqlTemp:=SqlTemp+TemplateStr+
|
||||||
StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FIndexFieldNo])+';';
|
StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FIndexFieldNo])+';';
|
||||||
|
inc(StatementsCounter);
|
||||||
|
//ApplyUpdates each 400 statements
|
||||||
|
if StatementsCounter = 400 then
|
||||||
|
begin
|
||||||
|
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||||
|
FSqliteReturnId:=sqlite_exec(FSqliteHandle,PChar(SqlTemp),nil,nil,nil);
|
||||||
|
StatementsCounter:=0;
|
||||||
|
SqlTemp:='BEGIN TRANSACTION;';
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
SqlTemp:=SqlTemp+'END TRANSACTION; ';
|
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||||
{$ifdef DEBUG}
|
{$ifdef DEBUG}
|
||||||
writeln('ApplyUpdates Sql: ',SqlTemp);
|
writeln('ApplyUpdates Sql: ',SqlTemp);
|
||||||
{$endif}
|
{$endif}
|
||||||
@ -846,7 +1016,7 @@ begin
|
|||||||
{$endif}
|
{$endif}
|
||||||
if (FTableName <> '') and (FieldDefs.Count > 0) then
|
if (FTableName <> '') and (FieldDefs.Count > 0) then
|
||||||
begin
|
begin
|
||||||
FSqliteHandle:= sqlite_open(PChar(FFileName),0,FDBError);
|
FSqliteHandle:= sqlite_open(PChar(FFileName),0,nil);
|
||||||
SqlTemp:='CREATE TABLE '+FTableName+' (';
|
SqlTemp:='CREATE TABLE '+FTableName+' (';
|
||||||
for Counter := 0 to FieldDefs.Count-1 do
|
for Counter := 0 to FieldDefs.Count-1 do
|
||||||
begin
|
begin
|
||||||
@ -870,6 +1040,8 @@ begin
|
|||||||
SqlTemp:=SqlTemp + ' TIME';
|
SqlTemp:=SqlTemp + ' TIME';
|
||||||
ftAutoInc:
|
ftAutoInc:
|
||||||
SqlTemp:=SqlTemp + ' AUTOINC';
|
SqlTemp:=SqlTemp + ' AUTOINC';
|
||||||
|
ftMemo:
|
||||||
|
SqlTemp:=SqlTemp + ' MEMO';
|
||||||
else
|
else
|
||||||
SqlTemp:=SqlTemp + ' VARCHAR';
|
SqlTemp:=SqlTemp + ' VARCHAR';
|
||||||
end;
|
end;
|
||||||
@ -888,6 +1060,67 @@ begin
|
|||||||
Result:=False;
|
Result:=False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSqliteDataset.TableExists: Boolean;
|
||||||
|
var
|
||||||
|
AHandle,vm:Pointer;
|
||||||
|
ColumnNames,ColumnValues:PPChar;
|
||||||
|
AInt:Integer;
|
||||||
|
begin
|
||||||
|
Result:=False;
|
||||||
|
if not (FTableName = '') and FileExists(FFileName) then
|
||||||
|
begin
|
||||||
|
if FSqliteHandle = nil then
|
||||||
|
begin
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
writeln('TableExists - FSqliteHandle=nil : Opening a file');
|
||||||
|
{$endif}
|
||||||
|
AHandle:=sqlite_open(PChar(FFileName),0,nil);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
|
||||||
|
{$endif}
|
||||||
|
AHandle:=FSqliteHandle;
|
||||||
|
end;
|
||||||
|
FSqliteReturnId:=sqlite_compile(AHandle,
|
||||||
|
Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
|
||||||
|
nil,@vm,nil);
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('TableExists.sqlite_compile - SqliteReturnString:',SqliteReturnString);
|
||||||
|
{$endif}
|
||||||
|
FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('TableExists.sqlite_step - SqliteReturnString:',SqliteReturnString);
|
||||||
|
{$endif}
|
||||||
|
Result:=FSqliteReturnId = SQLITE_ROW;
|
||||||
|
sqlite_finalize(vm, nil);
|
||||||
|
if (FSqliteHandle = nil) then
|
||||||
|
sqlite_close(AHandle);
|
||||||
|
end;
|
||||||
|
{$ifdef DEBUG}
|
||||||
|
WriteLn('TableExists ('+FTableName+') Result:',Result);
|
||||||
|
{$endif}
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSqliteDataset.RefetchData;
|
||||||
|
begin
|
||||||
|
//Close
|
||||||
|
if FSaveOnRefetch then
|
||||||
|
ApplyUpdates;
|
||||||
|
if FDataAllocated then
|
||||||
|
DisposeLinkedList;
|
||||||
|
FAddedItems.Clear;
|
||||||
|
FUpdatedItems.Clear;
|
||||||
|
FDeletedItems.Clear;
|
||||||
|
FOrphanItems.Clear;
|
||||||
|
FRecordCount:=0;
|
||||||
|
//Reopen
|
||||||
|
BuildLinkedList;
|
||||||
|
FCurrentItem:=FBeginItem;
|
||||||
|
Resync([]);
|
||||||
|
end;
|
||||||
|
|
||||||
function TSqliteDataset.SqliteReturnString: String;
|
function TSqliteDataset.SqliteReturnString: String;
|
||||||
begin
|
begin
|
||||||
case FSqliteReturnId of
|
case FSqliteReturnId of
|
||||||
@ -922,11 +1155,17 @@ begin
|
|||||||
else
|
else
|
||||||
Result:='Unknow Return Value';
|
Result:='Unknow Return Value';
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSqliteDataset.UpdatesPending: Boolean;
|
||||||
|
begin
|
||||||
|
Result:= (FDeletedItems.Count > 0) or
|
||||||
|
(FAddedItems.Count > 0) or (FUpdatedItems.Count > 0);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure Register;
|
procedure Register;
|
||||||
begin
|
begin
|
||||||
RegisterComponents('Data Access', [TSqliteDataset]);
|
RegisterComponents('Data Access', [TSqliteDataset]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
Loading…
Reference in New Issue
Block a user