mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 16:09:31 +02:00
+ updated to latest version from Luiz Camara
git-svn-id: trunk@3061 -
This commit is contained in:
parent
dfd84c6fd3
commit
9b7d7cb21f
@ -62,9 +62,6 @@ type
|
||||
|
||||
TCustomSqliteDataset = class(TDataSet)
|
||||
private
|
||||
FPrimaryKey: String;
|
||||
FPrimaryKeyNo: Integer;
|
||||
|
||||
{$ifdef DEBUGACTIVEBUFFER}
|
||||
FFCurrentItem: PDataRecord;
|
||||
{$else}
|
||||
@ -74,25 +71,30 @@ type
|
||||
FExpectedAppends: Integer;
|
||||
FExpectedDeletes: Integer;
|
||||
FExpectedUpdates: Integer;
|
||||
//FPersistentHandle: Boolean;
|
||||
FSaveOnClose: Boolean;
|
||||
FSaveOnRefetch: Boolean;
|
||||
FSqlMode: Boolean;
|
||||
FUpdatedItems: TFPList;
|
||||
FAddedItems: TFPList;
|
||||
FDeletedItems: TFPList;
|
||||
FOrphanItems: TFPList;
|
||||
FAutoIncrementKey: Boolean;
|
||||
FMasterLink: TMasterDataLink;
|
||||
FIndexFieldNames: String;
|
||||
FIndexFieldList: TList;
|
||||
FSqlList:TStrings;
|
||||
function GetIndexFields(Value: Integer): TField;
|
||||
procedure UpdateIndexFields;
|
||||
function FindRecordItem(StartItem: PDataRecord; const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; DoResync:Boolean):PDataRecord;
|
||||
protected
|
||||
FPrimaryKey: String;
|
||||
FPrimaryKeyNo: Integer;
|
||||
FFileName: String;
|
||||
FSql: String;
|
||||
FTableName: String;
|
||||
FSelectSqlStr: String;
|
||||
FAutoIncFieldNo: Integer;
|
||||
FNextAutoInc:Integer;
|
||||
FUpdatedItems: TFPList;
|
||||
FAddedItems: TFPList;
|
||||
FDeletedItems: TFPList;
|
||||
FOrphanItems: TFPList;
|
||||
FSqliteReturnId: Integer;
|
||||
FSqliteHandle: Pointer;
|
||||
FDataAllocated: Boolean;
|
||||
@ -103,8 +105,9 @@ type
|
||||
FEndItem: PDataRecord;
|
||||
FCacheItem: PDataRecord;
|
||||
function SqliteExec(AHandle: Pointer; Sql:PChar):Integer;virtual; abstract;
|
||||
procedure SqliteClose(AHandle: Pointer);virtual;abstract;
|
||||
function GetSqliteHandle: Pointer; virtual; abstract;
|
||||
procedure InternalCloseHandle;virtual;abstract;
|
||||
function InternalGetHandle: Pointer; virtual; abstract;
|
||||
procedure GetSqliteHandle;
|
||||
function GetSqliteVersion: String; virtual; abstract;
|
||||
procedure BuildLinkedList; virtual; abstract;
|
||||
procedure DisposeLinkedList;
|
||||
@ -115,6 +118,8 @@ type
|
||||
function GetMasterFields:String;
|
||||
procedure SetMasterSource(Value: TDataSource);
|
||||
function GetMasterSource:TDataSource;
|
||||
procedure SetFileName(Value: String);
|
||||
function GetRowsAffected:Integer; virtual;abstract;
|
||||
//TDataSet overrides
|
||||
function AllocRecordBuffer: PChar; override;
|
||||
function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
|
||||
@ -141,26 +146,32 @@ type
|
||||
procedure SetExpectedAppends(AValue:Integer);
|
||||
procedure SetExpectedUpdates(AValue:Integer);
|
||||
procedure SetExpectedDeletes(AValue:Integer);
|
||||
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
|
||||
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
|
||||
procedure SetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean); override;
|
||||
procedure SetRecNo(Value: Integer); override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
|
||||
function GetFieldData(Field: TField; Buffer: Pointer; NativeFormat: Boolean): Boolean; override;
|
||||
function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean; override;
|
||||
function LocateNext(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
|
||||
function Lookup(const KeyFields: string; const KeyValues: Variant; const ResultFields: string): Variant;{$ifndef ver2_0_0}override;{$endif}
|
||||
// Additional procedures
|
||||
function ApplyUpdates: Boolean; virtual;
|
||||
function CreateTable: Boolean; virtual;
|
||||
function ExecSQL:Integer;
|
||||
function ExecSQL(const ASql:String):Integer;
|
||||
function ApplyUpdates: Boolean;
|
||||
function CreateTable: Boolean;
|
||||
function CreateTable(const ATableName: String): Boolean;
|
||||
procedure ExecSQL;
|
||||
procedure ExecSQL(const ASql:String);
|
||||
procedure ExecSQLList;
|
||||
procedure ExecuteDirect(const ASql: String);virtual;abstract;
|
||||
function QuickQuery(const ASql:String):String;overload;
|
||||
function QuickQuery(const ASql:String;const AStrList: TStrings):String;overload;
|
||||
function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;virtual;abstract;overload;
|
||||
procedure RefetchData;
|
||||
function SqliteReturnString: String; virtual;abstract;
|
||||
function TableExists: Boolean;virtual;abstract;
|
||||
function TableExists: Boolean;overload;
|
||||
function TableExists(const ATableName:String):Boolean;virtual;abstract;overload;
|
||||
function UpdatesPending: Boolean;
|
||||
{$ifdef DEBUGACTIVEBUFFER}
|
||||
procedure SetCurrentItem(Value:PDataRecord);
|
||||
@ -177,17 +188,20 @@ type
|
||||
property ExpectedUpdates: Integer read FExpectedUpdates write SetExpectedUpdates;
|
||||
property ExpectedDeletes: Integer read FExpectedDeletes write SetExpectedDeletes;
|
||||
property IndexFields[Value: Integer]: TField read GetIndexFields;
|
||||
property RowsAffected: Integer read GetRowsAffected;
|
||||
//property PersistentHandle: boolean read FPersistentHandle write FPersistentHandle;
|
||||
property SqliteReturnId: Integer read FSqliteReturnId;
|
||||
property SqliteHandle: Pointer read FSqliteHandle;
|
||||
property SqliteVersion: String read GetSqliteVersion;
|
||||
property SQLList:TStrings read FSqlList;
|
||||
published
|
||||
property AutoIncrementKey: Boolean read FAutoIncrementKey write FAutoIncrementKey;
|
||||
property IndexFieldNames: string read FIndexFieldNames write FIndexFieldNames;
|
||||
property FileName: String read FFileName write FFileName;
|
||||
property FileName: String read FFileName write SetFileName;
|
||||
property PrimaryKey: String read FPrimaryKey write FPrimaryKey;
|
||||
property SaveOnClose: Boolean read FSaveOnClose write FSaveOnClose;
|
||||
property SaveOnRefetch: Boolean read FSaveOnRefetch write FSaveOnRefetch;
|
||||
property SQL: String read FSql write FSql;
|
||||
property SqlMode: Boolean read FSqlMode write FSqlMode;
|
||||
property TableName: String read FTableName write FTableName;
|
||||
property MasterSource: TDataSource read GetMasterSource write SetMasterSource;
|
||||
property MasterFields: string read GetMasterFields write SetMasterFields;
|
||||
@ -255,31 +269,26 @@ 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;
|
||||
//FRowSize is always 0 when FPosition = 0,
|
||||
//so there's no need to check FPosition
|
||||
NewRow:=StrAlloc(FRowSize+Count+1);
|
||||
(NewRow+Count+FRowSize)^:=#0;
|
||||
if FRowSize > 0 then
|
||||
Move(FFieldRow^,NewRow^,FRowSize);
|
||||
Move(Buffer,(NewRow+FRowSize)^,Count);
|
||||
end;
|
||||
Move(Buffer,(NewRow+FRowSize)^,Count);
|
||||
FActiveItem^.Row[FFieldIndex]:=NewRow;
|
||||
StrDispose(FFieldRow);
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('##TDSStream.Write##');
|
||||
WriteLn(' FPosition(Before): ',FPosition);
|
||||
WriteLn(' FRowSize(Before): ',FRowSize);
|
||||
WriteLn(' FPosition(After): ',FPosition+Count);
|
||||
WriteLn(' FRowSize(After): ',StrLen(NewRow));
|
||||
//WriteLn(' Stream Value: ',NewRow);
|
||||
{$endif}
|
||||
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;
|
||||
@ -294,11 +303,11 @@ begin
|
||||
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);
|
||||
WriteLn('##TDSStream.Read##');
|
||||
WriteLn(' Bytes requested: ',Count);
|
||||
WriteLn(' Bytes moved: ',BytesToMove);
|
||||
WriteLn(' Stream.Size: ',FRowSize);
|
||||
//WriteLn(' Stream Value: ',FFieldRow);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
@ -334,30 +343,40 @@ begin
|
||||
BookmarkSize := SizeOf(Pointer);
|
||||
FBufferSize := SizeOf(PPDataRecord);
|
||||
FUpdatedItems:= TFPList.Create;
|
||||
FUpdatedItems.Capacity:=20;
|
||||
FAddedItems:= TFPList.Create;
|
||||
FAddedItems.Capacity:=20;
|
||||
FOrphanItems:= TFPList.Create;
|
||||
FOrphanItems.Capacity:=20;
|
||||
FDeletedItems:= TFPList.Create;
|
||||
FDeletedItems.Capacity:=20;
|
||||
FSqlList:=TStringList.Create;
|
||||
inherited Create(AOwner);
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
|
||||
var
|
||||
ActiveItem:PDataRecord;
|
||||
begin
|
||||
if Mode = bmWrite then
|
||||
begin
|
||||
ActiveItem:=PPDataRecord(ActiveBuffer)^;
|
||||
if (ActiveItem <> FCacheItem) and (FUpdatedItems.IndexOf(ActiveItem) = -1) and (FAddedItems.IndexOf(ActiveItem) = -1) then
|
||||
FUpdatedItems.Add(ActiveItem);
|
||||
StrDispose(ActiveItem^.Row[Field.FieldNo - 1]);
|
||||
ActiveItem^.Row[Field.FieldNo - 1]:=nil;
|
||||
end;
|
||||
Result:= TDSStream.Create(PPDataRecord(ActiveBuffer)^,Field.FieldNo - 1);
|
||||
end;
|
||||
|
||||
destructor TCustomSqliteDataset.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
if FSqliteHandle <> nil then
|
||||
InternalCloseHandle;
|
||||
FUpdatedItems.Destroy;
|
||||
FAddedItems.Destroy;
|
||||
FDeletedItems.Destroy;
|
||||
FOrphanItems.Destroy;
|
||||
FMasterLink.Destroy;
|
||||
FIndexFieldList.Destroy;
|
||||
FSqlList.Destroy;
|
||||
// dispose special items
|
||||
Dispose(FBeginItem);
|
||||
Dispose(FCacheItem);
|
||||
@ -377,11 +396,14 @@ var
|
||||
Counter,I:Integer;
|
||||
begin
|
||||
//Todo: insert debug info
|
||||
//Todo: see if FDataAllocated is still necessary
|
||||
FDataAllocated:=False;
|
||||
TempItem:=FBeginItem^.Next;
|
||||
//Todo: see if is necessary to check if TempItem is nil (aparently is not)
|
||||
if TempItem <> nil then
|
||||
while TempItem^.Next <> nil do
|
||||
begin
|
||||
//Todo: Add procedure to Dispose and Free a Row ?
|
||||
for Counter:= 0 to FRowCount - 1 do
|
||||
StrDispose(TempItem^.Row[Counter]);
|
||||
FreeMem(TempItem^.Row,FRowBufferSize);
|
||||
@ -423,7 +445,8 @@ begin
|
||||
Result := PPDataRecord(Buffer)^^.BookmarkFlag;
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
|
||||
function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer;
|
||||
NativeFormat: Boolean): Boolean;
|
||||
var
|
||||
ValError:Word;
|
||||
FieldRow:PChar;
|
||||
@ -461,6 +484,11 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
|
||||
begin
|
||||
Result:=GetFieldData(Field, Buffer, False);
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult;
|
||||
begin
|
||||
Result := grOk;
|
||||
@ -561,11 +589,13 @@ begin
|
||||
DestroyFields;
|
||||
if FDataAllocated then
|
||||
DisposeLinkedList;
|
||||
if FSqliteHandle <> nil then
|
||||
{
|
||||
if (FSqliteHandle <> nil) and not FPersistentHandle then
|
||||
begin
|
||||
SqliteClose(FSqliteHandle);
|
||||
InternalCloseHandle;
|
||||
FSqliteHandle := nil;
|
||||
end;
|
||||
}
|
||||
FAddedItems.Clear;
|
||||
FUpdatedItems.Clear;
|
||||
FDeletedItems.Clear;
|
||||
@ -586,6 +616,9 @@ begin
|
||||
FUpdatedItems.Remove(TempItem);
|
||||
if FAddedItems.Remove(TempItem) = -1 then
|
||||
FDeletedItems.Add(TempItem);
|
||||
//Todo: see if FOrphanItems is necessary:
|
||||
// in ApplyUpdates a check could be done
|
||||
// to avoid "delete" the AddedItems
|
||||
FOrphanItems.Add(TempItem);
|
||||
TempItem^.Next^.Previous:=TempItem^.Previous;
|
||||
TempItem^.Previous^.Next:=TempItem^.Next;
|
||||
@ -640,28 +673,38 @@ begin
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.InternalOpen;
|
||||
var
|
||||
i:Integer;
|
||||
begin
|
||||
FAutoIncFieldNo:=-1;
|
||||
if not FileExists(FFileName) then
|
||||
DatabaseError('File "'+ExpandFileName(FFileName)+'" not found',Self);
|
||||
if (FTablename = '') and not (FSqlMode) then
|
||||
DatabaseError('Tablename not set',Self);
|
||||
|
||||
if MasterSource <> nil then
|
||||
begin
|
||||
//todo: retrieve only necessary fields
|
||||
FSql := 'Select * from '+FTableName+';'; // forced to obtain all fields
|
||||
FMasterLink.FieldNames:=FMasterLink.FieldNames; //workaround to fill MasterLinks.Fields
|
||||
//if FMasterLink.Fields.Count = 0 MasterChanged will not be called anyway so ignore it
|
||||
end;
|
||||
|
||||
FSqliteHandle:=GetSqliteHandle;
|
||||
|
||||
if FSql = '' then
|
||||
begin
|
||||
if FTablename = '' then
|
||||
DatabaseError('Tablename not set',Self);
|
||||
FSql := 'Select * from '+FTableName+';';
|
||||
end;
|
||||
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
|
||||
InternalInitFieldDefs;
|
||||
//todo: move this to InitFieldDefs
|
||||
FSelectSqlStr:='SELECT ';
|
||||
for i:= 0 to FieldDefs.Count - 2 do
|
||||
FSelectSqlStr:=FSelectSqlStr+FieldDefs[i].Name+',';
|
||||
FSelectSqlStr:=FSelectSqlStr+FieldDefs[FieldDefs.Count - 1].Name+
|
||||
' FROM '+FTableName;
|
||||
//writeln(FSelectSqlStr);
|
||||
|
||||
if DefaultFields then
|
||||
CreateFields;
|
||||
|
||||
BindFields(True);
|
||||
|
||||
UpdateIndexFields;
|
||||
@ -708,7 +751,7 @@ var
|
||||
begin
|
||||
Result:=nil;
|
||||
// Now, it allows to search only one field and ignores options
|
||||
AField:=Fields.FieldByName(KeyFields); //FieldByName raises an exeception if field not found
|
||||
AField:=Fields.FieldByName(KeyFields); //FieldByName raises an exception if field not found
|
||||
AFieldIndex:=AField.FieldNo - 1;
|
||||
//get float types in appropriate format
|
||||
if not (AField.DataType in [ftFloat,ftDateTime,ftTime,ftDate]) then
|
||||
@ -719,10 +762,10 @@ begin
|
||||
AValue:=Trim(AValue);
|
||||
end;
|
||||
{$ifdef DEBUG}
|
||||
writeln('=FindRecord=');
|
||||
writeln('keyfields: ',keyfields);
|
||||
writeln('keyvalues: ',keyvalues);
|
||||
writeln('AValue: ',AValue);
|
||||
writeln('##TCustomSqliteDataset.FindRecordItem##');
|
||||
writeln(' KeyFields: ',keyfields);
|
||||
writeln(' KeyValues: ',keyvalues);
|
||||
writeln(' AValue: ',AValue);
|
||||
{$endif}
|
||||
//Search the list
|
||||
TempItem:=StartItem;
|
||||
@ -745,6 +788,14 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.GetSqliteHandle;
|
||||
begin
|
||||
if FFileName = '' then
|
||||
DatabaseError ('Filename not set',Self);
|
||||
//todo:Handle opening non db files
|
||||
FSqliteHandle:=InternalGetHandle;
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions) : boolean;
|
||||
begin
|
||||
Result:=FindRecordItem(FBeginItem^.Next,KeyFields,KeyValues,Options,True) <> nil;
|
||||
@ -792,7 +843,8 @@ begin
|
||||
FOrphanItems.Capacity:=AValue;
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
|
||||
procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer;
|
||||
NativeFormat: Boolean);
|
||||
var
|
||||
TempStr:String;
|
||||
ActiveItem:PDataRecord;
|
||||
@ -813,25 +865,27 @@ begin
|
||||
begin
|
||||
Str(LongInt(Buffer^),TempStr);
|
||||
ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
|
||||
StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
|
||||
Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
|
||||
end;
|
||||
ftBoolean,ftWord:
|
||||
begin
|
||||
Str(Word(Buffer^),TempStr);
|
||||
ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
|
||||
StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
|
||||
Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
|
||||
end;
|
||||
ftFloat,ftDateTime,ftDate,ftTime,ftCurrency:
|
||||
begin
|
||||
Str(Double(Buffer^),TempStr);
|
||||
ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
|
||||
StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
|
||||
ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr));
|
||||
//Skips the first space that str returns
|
||||
//todo: make a custom Str?
|
||||
Move((PChar(TempStr)+1)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr));
|
||||
end;
|
||||
ftLargeInt:
|
||||
begin
|
||||
Str(Int64(Buffer^),TempStr);
|
||||
ActiveItem^.Row[Pred(Field.FieldNo)]:=StrAlloc(Length(TempStr)+1);
|
||||
StrPCopy(ActiveItem^.Row[Pred(Field.FieldNo)],TempStr);
|
||||
Move(PChar(TempStr)^,(ActiveItem^.Row[Pred(Field.FieldNo)])^,Length(TempStr)+1);
|
||||
end;
|
||||
end;// case
|
||||
end//if
|
||||
@ -841,6 +895,11 @@ begin
|
||||
DataEvent(deFieldChange, Ptrint(Field));
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.SetFieldData(Field: TField; Buffer: Pointer);
|
||||
begin
|
||||
SetFieldData(Field, Buffer, False);
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.SetRecNo(Value: Integer);
|
||||
var
|
||||
Counter:Integer;
|
||||
@ -858,7 +917,7 @@ end;
|
||||
// Specific functions
|
||||
|
||||
procedure TCustomSqliteDataset.SetDetailFilter;
|
||||
function GetSqlStr(AField:TField):String;
|
||||
function FieldToSqlStr(AField:TField):String;
|
||||
begin
|
||||
case AField.DataType of
|
||||
ftString,ftMemo: Result:='"'+AField.AsString+'"';//todo: handle " caracter properly
|
||||
@ -879,7 +938,7 @@ begin
|
||||
AFilter:=' where ';
|
||||
for i:= 0 to FMasterLink.Fields.Count - 1 do
|
||||
begin
|
||||
AFilter:=AFilter + IndexFields[i].FieldName +' = '+ GetSqlStr(TField(FMasterLink.Fields[i]));
|
||||
AFilter:=AFilter + IndexFields[i].FieldName +' = '+ FieldToSqlStr(TField(FMasterLink.Fields[i]));
|
||||
if i <> FMasterLink.Fields.Count - 1 then
|
||||
AFilter:= AFilter + ' and ';
|
||||
end;
|
||||
@ -891,8 +950,9 @@ procedure TCustomSqliteDataset.MasterChanged(Sender: TObject);
|
||||
begin
|
||||
SetDetailFilter;
|
||||
{$ifdef DEBUG}
|
||||
writeln('Sql used to filter detail dataset:');
|
||||
writeln(FSql);
|
||||
writeln('##TCustomSqliteDataset.MasterChanged##');
|
||||
writeln(' SQL used to filter detail dataset:');
|
||||
writeln(' ',FSql);
|
||||
{$endif}
|
||||
RefetchData;
|
||||
end;
|
||||
@ -919,7 +979,6 @@ begin
|
||||
Result:=FMasterLink.FieldNames;
|
||||
end;
|
||||
|
||||
|
||||
procedure TCustomSqliteDataset.UpdateIndexFields;
|
||||
begin
|
||||
FIndexFieldList.Clear;
|
||||
@ -939,41 +998,62 @@ begin
|
||||
Result := FMasterLink.DataSource;
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.SetFileName(Value: String);
|
||||
begin
|
||||
if Value <> FFileName then
|
||||
begin
|
||||
if Active then
|
||||
DatabaseError('It''s not allowed to change Filename in an open dataset',Self);
|
||||
if FSqliteHandle <> nil then
|
||||
InternalCloseHandle;
|
||||
FFileName:=Value;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.SetMasterSource(Value: TDataSource);
|
||||
begin
|
||||
FMasterLink.DataSource := Value;
|
||||
end;
|
||||
|
||||
|
||||
function TCustomSqliteDataset.ExecSQL(const ASql:String):Integer;
|
||||
var
|
||||
AHandle: Pointer;
|
||||
procedure TCustomSqliteDataset.ExecSQL(const ASql:String);
|
||||
begin
|
||||
Result:=0;
|
||||
//Todo check if Filename exists
|
||||
if FSqliteHandle <> nil then
|
||||
AHandle:=FSqliteHandle
|
||||
else
|
||||
if FFileName <> '' then
|
||||
AHandle := GetSqliteHandle
|
||||
else
|
||||
DatabaseError ('ExecSql - FileName not set',Self);
|
||||
FSqliteReturnId:= SqliteExec(AHandle,PChar(ASql));
|
||||
//todo: add a way to get the num of changes
|
||||
//Result:=sqlite_changes(AHandle);
|
||||
if AHandle <> FSqliteHandle then
|
||||
SqliteClose(AHandle);
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.ExecSQL:Integer;
|
||||
begin
|
||||
Result:=ExecSQL(FSql);
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
ExecuteDirect(ASql);
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.ExecSQLList;
|
||||
begin
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
SqliteExec(FSqliteHandle,PChar(FSqlList.Text));
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.ExecSQL;
|
||||
begin
|
||||
ExecSQL(FSql);
|
||||
end;
|
||||
|
||||
function GetSqlStr(IsString: boolean; APChar: PChar): String;
|
||||
begin
|
||||
if APChar = nil then
|
||||
begin
|
||||
Result:='NULL';
|
||||
Exit;
|
||||
end;
|
||||
Result:=StrPas(APChar);
|
||||
if IsString then
|
||||
begin
|
||||
if Pos('''',Result) > 0 then
|
||||
Result:=AnsiReplaceStr(Result,'''','''''');
|
||||
Result:=''''+Result+'''';
|
||||
end;
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.ApplyUpdates:Boolean;
|
||||
var
|
||||
CounterFields,CounterItems,StatementsCounter:Integer;
|
||||
SqlTemp,KeyName,ASqlLine,TemplateStr:String;
|
||||
SqlTemp,WhereKeyNameEqual,ASqlLine,TemplateStr:String;
|
||||
begin
|
||||
if not UpdatesPending then
|
||||
begin
|
||||
@ -981,49 +1061,59 @@ begin
|
||||
Exit;
|
||||
end;
|
||||
Result:=False;
|
||||
if (FPrimaryKeyNo <> -1) and not FSqlMode then
|
||||
if FPrimaryKeyNo <> -1 then
|
||||
begin
|
||||
StatementsCounter:=0;
|
||||
KeyName:=Fields[FPrimaryKeyNo].FieldName;
|
||||
WhereKeyNameEqual:=' WHERE '+Fields[FPrimaryKeyNo].FieldName+' = ';
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('ApplyUpdates called');
|
||||
WriteLn('##TCustomSqliteDataset.ApplyUpdates##');
|
||||
if FPrimaryKeyNo = FAutoIncFieldNo then
|
||||
WriteLn('Using an AutoInc field as primary key');
|
||||
WriteLn('PrimaryKey: ',KeyName);
|
||||
WriteLn('PrimaryKeyNo: ',FPrimaryKeyNo);
|
||||
WriteLn(' Using an AutoInc field as primary key');
|
||||
WriteLn(' PrimaryKey: ',WhereKeyNameEqual);
|
||||
WriteLn(' PrimaryKeyNo: ',FPrimaryKeyNo);
|
||||
{$endif}
|
||||
SqlTemp:='BEGIN TRANSACTION;';
|
||||
SqlTemp:='BEGIN;';
|
||||
// Delete Records
|
||||
if FDeletedItems.Count > 0 then
|
||||
TemplateStr:='DELETE FROM '+FTableName+WhereKeyNameEqual;
|
||||
for CounterItems:= 0 to FDeletedItems.Count - 1 do
|
||||
begin
|
||||
SqlTemp:=SqlTemp+(TemplateStr+
|
||||
StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FPrimaryKeyNo])+';');
|
||||
inc(StatementsCounter);
|
||||
//ApplyUpdates each 400 statements
|
||||
if StatementsCounter = 400 then
|
||||
begin
|
||||
SqlTemp:=SqlTemp+'COMMIT;';
|
||||
FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
|
||||
StatementsCounter:=0;
|
||||
SqlTemp:='BEGIN;';
|
||||
end;
|
||||
end;
|
||||
// Update changed records
|
||||
if FUpdatedItems.Count > 0 then
|
||||
TemplateStr:='UPDATE '+FTableName+' SET ';
|
||||
for CounterItems:= 0 to FUpdatedItems.Count - 1 do
|
||||
begin
|
||||
ASqlLine:=TemplateStr;
|
||||
for CounterFields:= 0 to Fields.Count - 1 do
|
||||
for CounterFields:= 0 to Fields.Count - 2 do
|
||||
begin
|
||||
if PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields] <> nil then
|
||||
begin
|
||||
ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = ';
|
||||
if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
|
||||
ASqlLine:=ASqlLine+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+ ','
|
||||
else
|
||||
ASqlLine:=ASqlLine+''''+
|
||||
AnsiReplaceStr(StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields]),'''','''''')+''',';
|
||||
end
|
||||
else
|
||||
ASqlLine:=ASqlLine + Fields[CounterFields].FieldName +' = NULL,';
|
||||
ASqlLine:=ASqlLine + (Fields[CounterFields].FieldName +' = '+
|
||||
GetSqlStr((Fields[CounterFields].DataType in [ftString,ftMemo]),
|
||||
PDataRecord(FUpdatedItems[CounterItems])^.Row[CounterFields])+',');
|
||||
end;
|
||||
//Todo: see if system.delete trunks AnsiString
|
||||
system.delete(ASqlLine,Length(ASqlLine),1);
|
||||
SqlTemp:=SqlTemp + ASqlLine+' WHERE '+KeyName+' = '+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
|
||||
ASqlLine:=ASqlLine + (Fields[Fields.Count - 1].FieldName +' = '+
|
||||
GetSqlStr((Fields[Fields.Count - 1].DataType in [ftString,ftMemo]),PDataRecord(FUpdatedItems[CounterItems])^.Row[Fields.Count - 1])+
|
||||
WhereKeyNameEqual+StrPas(PDataRecord(FUpdatedItems[CounterItems])^.Row[FPrimaryKeyNo])+';');
|
||||
SqlTemp:=SqlTemp + ASqlLine;
|
||||
inc(StatementsCounter);
|
||||
//ApplyUpdates each 400 statements
|
||||
if StatementsCounter = 400 then
|
||||
begin
|
||||
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||
SqlTemp:=SqlTemp+'COMMIT;';
|
||||
FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
|
||||
StatementsCounter:=0;
|
||||
SqlTemp:='BEGIN TRANSACTION;';
|
||||
SqlTemp:='BEGIN;';
|
||||
end;
|
||||
end;
|
||||
// Add new records
|
||||
@ -1042,53 +1132,27 @@ begin
|
||||
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 - 2 do
|
||||
begin
|
||||
if PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields] <> nil then
|
||||
begin
|
||||
if not (Fields[CounterFields].DataType in [ftString,ftMemo]) then
|
||||
ASqlLine:=ASqlLine+StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])
|
||||
else
|
||||
ASqlLine:=ASqlLine+''''+
|
||||
AnsiReplaceStr(StrPas(PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields]),'''','''''')+'''';
|
||||
end
|
||||
else
|
||||
ASqlLine:=ASqlLine + 'NULL';
|
||||
//Todo: see if delete ASqline is faster
|
||||
if CounterFields <> Fields.Count - 1 then
|
||||
ASqlLine:=ASqlLine+',';
|
||||
ASqlLine:=ASqlLine + (GetSqlStr((Fields[CounterFields].DataType in [ftString,ftMemo]),
|
||||
PDataRecord(FAddedItems[CounterItems])^.Row[CounterFields])+',');
|
||||
end;
|
||||
SqlTemp:=SqlTemp+ASqlLine+');';
|
||||
ASqlLine:=ASqlLine + (GetSqlStr((Fields[Fields.Count -1].DataType in [ftString,ftMemo]),
|
||||
PDataRecord(FAddedItems[CounterItems])^.Row[Fields.Count - 1])+');');
|
||||
SqlTemp:=SqlTemp + ASqlLine;
|
||||
inc(StatementsCounter);
|
||||
//ApplyUpdates each 400 statements
|
||||
if StatementsCounter = 400 then
|
||||
begin
|
||||
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||
SqlTemp:=SqlTemp+'COMMIT;';
|
||||
FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
|
||||
StatementsCounter:=0;
|
||||
SqlTemp:='BEGIN TRANSACTION;';
|
||||
SqlTemp:='BEGIN;';
|
||||
end;
|
||||
end;
|
||||
// Delete Items
|
||||
if FDeletedItems.Count > 0 then
|
||||
TemplateStr:='DELETE FROM '+FTableName+ ' WHERE '+KeyName+' = ';
|
||||
for CounterItems:= 0 to FDeletedItems.Count - 1 do
|
||||
begin
|
||||
SqlTemp:=SqlTemp+TemplateStr+
|
||||
StrPas(PDataRecord(FDeletedItems[CounterItems])^.Row[FPrimaryKeyNo])+';';
|
||||
inc(StatementsCounter);
|
||||
//ApplyUpdates each 400 statements
|
||||
if StatementsCounter = 400 then
|
||||
begin
|
||||
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||
FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
|
||||
StatementsCounter:=0;
|
||||
SqlTemp:='BEGIN TRANSACTION;';
|
||||
end;
|
||||
end;
|
||||
SqlTemp:=SqlTemp+'END TRANSACTION;';
|
||||
SqlTemp:=SqlTemp+'COMMIT;';
|
||||
{$ifdef DEBUG}
|
||||
writeln('ApplyUpdates Sql: ',SqlTemp);
|
||||
writeln(' SQL: ',SqlTemp);
|
||||
{$endif}
|
||||
FAddedItems.Clear;
|
||||
FUpdatedItems.Clear;
|
||||
@ -1097,37 +1161,45 @@ begin
|
||||
Result:= FSqliteReturnId = SQLITE_OK;
|
||||
end;
|
||||
{$ifdef DEBUG}
|
||||
writeln('ApplyUpdates Result: ',Result);
|
||||
writeln(' Result: ',Result);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.CreateTable: Boolean;
|
||||
begin
|
||||
Result:=CreateTable(FTableName);
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.CreateTable(const ATableName: String): Boolean;
|
||||
var
|
||||
SqlTemp:String;
|
||||
Counter:Integer;
|
||||
i:Integer;
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
if FTableName = '' then
|
||||
WriteLn('CreateTable : TableName Not Set');
|
||||
if FieldDefs.Count = 0 then
|
||||
WriteLn('CreateTable : FieldDefs Not Initialized');
|
||||
writeln('##TCustomSqliteDataset.CreateTable##');
|
||||
if ATableName = '' then
|
||||
WriteLn(' TableName Not Set');
|
||||
if FieldDefs.Count = 0 then
|
||||
WriteLn(' FieldDefs Not Initialized');
|
||||
{$endif}
|
||||
if (FTableName <> '') and (FieldDefs.Count > 0) then
|
||||
if (ATableName <> '') and (FieldDefs.Count > 0) then
|
||||
begin
|
||||
FSqliteHandle:= GetSqliteHandle;
|
||||
SqlTemp:='CREATE TABLE '+FTableName+' (';
|
||||
for Counter := 0 to FieldDefs.Count-1 do
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
SqlTemp:='CREATE TABLE '+ATableName+' (';
|
||||
for i := 0 to FieldDefs.Count-1 do
|
||||
begin
|
||||
SqlTemp:=SqlTemp + FieldDefs[Counter].Name;
|
||||
case FieldDefs[Counter].DataType of
|
||||
//todo: add index to autoinc field
|
||||
SqlTemp:=SqlTemp + FieldDefs[i].Name;
|
||||
case FieldDefs[i].DataType of
|
||||
ftInteger:
|
||||
SqlTemp:=SqlTemp + ' INTEGER';
|
||||
ftString:
|
||||
SqlTemp:=SqlTemp + ' VARCHAR';
|
||||
ftBoolean:
|
||||
SqlTemp:=SqlTemp + ' BOOLEAN';
|
||||
SqlTemp:=SqlTemp + ' BOOL_INT';
|
||||
ftFloat:
|
||||
SqlTemp:=SqlTemp + ' FLOAT';
|
||||
SqlTemp:=SqlTemp + ' FLOAT';
|
||||
ftWord:
|
||||
SqlTemp:=SqlTemp + ' WORD';
|
||||
ftDateTime:
|
||||
@ -1135,33 +1207,33 @@ begin
|
||||
ftDate:
|
||||
SqlTemp:=SqlTemp + ' DATE';
|
||||
ftTime:
|
||||
SqlTemp:=SqlTemp + ' TIME';
|
||||
SqlTemp:=SqlTemp + ' TIME';
|
||||
ftLargeInt:
|
||||
SqlTemp:=SqlTemp + ' LARGEINT';
|
||||
ftCurrency:
|
||||
SqlTemp:=SqlTemp + ' CURRENCY';
|
||||
SqlTemp:=SqlTemp + ' CURRENCY';
|
||||
ftAutoInc:
|
||||
SqlTemp:=SqlTemp + ' AUTOINC';
|
||||
SqlTemp:=SqlTemp + ' AUTOINC_INT';
|
||||
ftMemo:
|
||||
SqlTemp:=SqlTemp + ' MEMO';
|
||||
SqlTemp:=SqlTemp + ' TEXT';
|
||||
else
|
||||
DatabaseError('Field type "'+FieldTypeNames[FieldDefs[Counter].DataType]+'" not supported',Self);
|
||||
DatabaseError('Field type "'+FieldTypeNames[FieldDefs[i].DataType]+'" not supported',Self);
|
||||
end;
|
||||
if Counter <> FieldDefs.Count - 1 then
|
||||
SqlTemp:=SqlTemp+ ' , ';
|
||||
if UpperCase(FieldDefs[i].Name) = UpperCase(FPrimaryKey) then
|
||||
SqlTemp:=SqlTemp + ' PRIMARY KEY';
|
||||
if i <> FieldDefs.Count - 1 then
|
||||
SqlTemp:=SqlTemp+ ' , ';
|
||||
end;
|
||||
SqlTemp:=SqlTemp+');';
|
||||
{$ifdef DEBUG}
|
||||
writeln('CreateTable Sql: ',SqlTemp);
|
||||
{$endif}
|
||||
writeln(' SQL: ',SqlTemp);
|
||||
{$endif}
|
||||
FSqliteReturnId:=SqliteExec(FSqliteHandle,PChar(SqlTemp));
|
||||
Result:= FSqliteReturnId = SQLITE_OK;
|
||||
SqliteClose(FSqliteHandle);
|
||||
FSqliteHandle:=nil;
|
||||
end
|
||||
else
|
||||
Result:=False;
|
||||
end;
|
||||
Result:=False;
|
||||
end;
|
||||
|
||||
procedure TCustomSqliteDataset.RefetchData;
|
||||
var
|
||||
@ -1182,8 +1254,12 @@ begin
|
||||
for i := 0 to BufferCount - 1 do
|
||||
PPDataRecord(Buffers[i])^:=FBeginItem;
|
||||
Resync([]);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.TableExists: Boolean;
|
||||
begin
|
||||
Result:=TableExists(FTableName);
|
||||
end;
|
||||
|
||||
function TCustomSqliteDataset.UpdatesPending: Boolean;
|
||||
begin
|
||||
|
@ -36,15 +36,18 @@ type
|
||||
TSqlite3Dataset = class (TCustomSqliteDataset)
|
||||
private
|
||||
function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
|
||||
function GetSqliteHandle: Pointer; override;
|
||||
function InternalGetHandle: Pointer; override;
|
||||
function GetSqliteVersion: String; override;
|
||||
procedure SqliteClose(AHandle: Pointer);override;
|
||||
procedure InternalCloseHandle;override;
|
||||
procedure BuildLinkedList; override;
|
||||
protected
|
||||
procedure InternalCancel;override;
|
||||
procedure InternalInitFieldDefs; override;
|
||||
function GetRowsAffected:Integer; override;
|
||||
public
|
||||
procedure ExecuteDirect(const ASql: String);override;
|
||||
function SqliteReturnString: String; override;
|
||||
function TableExists: Boolean;override;
|
||||
function TableExists(const ATableName:String): Boolean;override;
|
||||
function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
|
||||
end;
|
||||
|
||||
@ -75,14 +78,15 @@ begin
|
||||
Result:=sqlite3_exec(AHandle, ASql, nil, nil, nil);
|
||||
end;
|
||||
|
||||
procedure TSqlite3Dataset.SqliteClose(AHandle: Pointer);
|
||||
procedure TSqlite3Dataset.InternalCloseHandle;
|
||||
begin
|
||||
sqlite3_close(AHandle);
|
||||
sqlite3_close(FSqliteHandle);
|
||||
FSqliteHandle:=nil;
|
||||
//todo:handle return data
|
||||
end;
|
||||
|
||||
|
||||
function TSqlite3Dataset.GetSqliteHandle: Pointer;
|
||||
function TSqlite3Dataset.InternalGetHandle: Pointer;
|
||||
begin
|
||||
FSqliteReturnId:=sqlite3_open(PChar(FFileName),@Result);
|
||||
end;
|
||||
@ -91,35 +95,47 @@ procedure TSqlite3Dataset.InternalInitFieldDefs;
|
||||
var
|
||||
vm:Pointer;
|
||||
ColumnStr:String;
|
||||
Counter,FieldSize:Integer;
|
||||
i,FieldSize:Integer;
|
||||
AType:TFieldType;
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('##TSqlite3Dataset.InternalInitFieldDefs##');
|
||||
{$endif}
|
||||
FAutoIncFieldNo:=-1;
|
||||
FieldDefs.Clear;
|
||||
sqlite3_prepare(FSqliteHandle,PChar(FSql),-1,@vm,nil);
|
||||
sqlite3_step(vm);
|
||||
for Counter:= 0 to sqlite3_column_count(vm) - 1 do
|
||||
begin
|
||||
ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,Counter)));
|
||||
if (ColumnStr = 'INTEGER') then
|
||||
sqlite3_step(vm);
|
||||
for i:= 0 to sqlite3_column_count(vm) - 1 do
|
||||
begin
|
||||
ColumnStr:= UpperCase(StrPas(sqlite3_column_decltype(vm,i)));
|
||||
if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
|
||||
begin
|
||||
AType:= ftInteger;
|
||||
if AutoIncrementKey and (UpperCase(StrPas(sqlite3_column_name(vm,i))) = UpperCase(PrimaryKey)) then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
FAutoIncFieldNo:=i;
|
||||
end
|
||||
else
|
||||
AType:= ftInteger;
|
||||
FieldSize:=SizeOf(LongInt);
|
||||
end else if (ColumnStr = 'VARCHAR') then
|
||||
end else if Pos('VARCHAR',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftString;
|
||||
FieldSize:=10;//??
|
||||
end else if (ColumnStr = 'BOOLEAN') then
|
||||
FieldSize:=0;
|
||||
end else if Pos('BOOL',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftBoolean;
|
||||
FieldSize:=SizeOf(Boolean);
|
||||
end else if (ColumnStr = 'FLOAT') then
|
||||
FieldSize:=SizeOf(WordBool);
|
||||
end else if Pos('AUTOINC',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
FieldSize:=SizeOf(LongInt);
|
||||
if FAutoIncFieldNo = -1 then
|
||||
FAutoIncFieldNo:= i;
|
||||
end else if (Pos('FLOAT',ColumnStr)=1) or (Pos('NUMERIC',ColumnStr)=1) then
|
||||
begin
|
||||
AType:= ftFloat;
|
||||
FieldSize:=SizeOf(Double);
|
||||
end else if (ColumnStr = 'WORD') then
|
||||
begin
|
||||
AType:= ftWord;
|
||||
FieldSize:=SizeOf(Word);
|
||||
end else if (ColumnStr = 'DATETIME') then
|
||||
begin
|
||||
AType:= ftDateTime;
|
||||
@ -132,41 +148,55 @@ begin
|
||||
begin
|
||||
AType:= ftLargeInt;
|
||||
FieldSize:=SizeOf(Int64);
|
||||
end else if (ColumnStr = 'CURRENCY') then
|
||||
begin
|
||||
AType:= ftCurrency;
|
||||
FieldSize:=SizeOf(Double);
|
||||
end else if (ColumnStr = 'TIME') then
|
||||
begin
|
||||
AType:= ftTime;
|
||||
FieldSize:=SizeOf(TDateTime);
|
||||
end else if (ColumnStr = 'MEMO') then
|
||||
end else if (ColumnStr = 'TEXT') then
|
||||
begin
|
||||
AType:= ftMemo;
|
||||
FieldSize:=10;//??
|
||||
end else if (ColumnStr = 'AUTOINC') then
|
||||
FieldSize:=0;
|
||||
end else if (ColumnStr = 'CURRENCY') then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
FieldSize:=SizeOf(Integer);
|
||||
if FAutoIncFieldNo = -1 then
|
||||
FAutoIncFieldNo:= Counter;
|
||||
AType:= ftCurrency;
|
||||
FieldSize:=SizeOf(Double);
|
||||
end else if (ColumnStr = 'WORD') then
|
||||
begin
|
||||
AType:= ftWord;
|
||||
FieldSize:=SizeOf(Word);
|
||||
end else
|
||||
begin
|
||||
DatabaseError('Field type "'+ColumnStr+'" not recognized',Self);
|
||||
end;
|
||||
FieldDefs.Add(StrPas(sqlite3_column_name(vm,Counter)), AType, FieldSize, False);
|
||||
FieldDefs.Add(StrPas(sqlite3_column_name(vm,i)), AType, FieldSize, False);
|
||||
{$ifdef DEBUG}
|
||||
writeln('Field Name: ',sqlite3_column_name(vm,Counter));
|
||||
writeln('Field Type: ',sqlite3_column_decltype(vm,Counter));
|
||||
writeln(' Field[',i,'] Name: ',sqlite3_column_name(vm,i));
|
||||
writeln(' Field[',i,'] Type: ',sqlite3_column_decltype(vm,i));
|
||||
{$endif}
|
||||
end;
|
||||
sqlite3_finalize(vm);
|
||||
sqlite3_finalize(vm);
|
||||
FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
|
||||
{$ifdef DEBUG}
|
||||
writeln('FieldDefs.Count: ',FieldDefs.Count);
|
||||
writeln(' FieldDefs.Count: ',FieldDefs.Count);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function TSqlite3Dataset.GetRowsAffected: Integer;
|
||||
begin
|
||||
Result:=sqlite3_changes(FSqliteHandle);
|
||||
end;
|
||||
|
||||
procedure TSqlite3Dataset.ExecuteDirect(const ASql: String);
|
||||
var
|
||||
vm:Pointer;
|
||||
begin
|
||||
FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
FSqliteReturnId:=sqlite3_step(vm);
|
||||
sqlite3_finalize(vm);
|
||||
end;
|
||||
|
||||
procedure TSqlite3Dataset.BuildLinkedList;
|
||||
var
|
||||
TempItem:PDataRecord;
|
||||
@ -180,12 +210,7 @@ begin
|
||||
|
||||
FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(FSql),-1,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
case FSqliteReturnId of
|
||||
SQLITE_ERROR:
|
||||
DatabaseError('Invalid SQL',Self);
|
||||
else
|
||||
DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
|
||||
end;
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
|
||||
FDataAllocated:=True;
|
||||
|
||||
@ -200,7 +225,7 @@ begin
|
||||
TempItem^.Next^.Previous:=TempItem;
|
||||
TempItem:=TempItem^.Next;
|
||||
GetMem(TempItem^.Row,FRowBufferSize);
|
||||
For Counter := 0 to FRowCount - 1 do
|
||||
for Counter := 0 to FRowCount - 1 do
|
||||
TempItem^.Row[Counter]:=StrNew(sqlite3_column_text(vm,Counter));
|
||||
FSqliteReturnId:=sqlite3_step(vm);
|
||||
end;
|
||||
@ -210,92 +235,128 @@ begin
|
||||
TempItem^.Next:=FEndItem;
|
||||
FEndItem^.Previous:=TempItem;
|
||||
|
||||
// Alloc item used in append/insert
|
||||
// Alloc temporary item used in append/insert
|
||||
GetMem(FCacheItem^.Row,FRowBufferSize);
|
||||
for Counter := 0 to FRowCount - 1 do
|
||||
FCacheItem^.Row[Counter]:=nil;
|
||||
// Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
|
||||
GetMem(FBeginItem^.Row,FRowBufferSize);
|
||||
//Todo: see if is better to nullif using FillDWord
|
||||
for Counter := 0 to FRowCount - 1 do
|
||||
FBeginItem^.Row[Counter]:=nil;
|
||||
end;
|
||||
|
||||
function TSqlite3Dataset.TableExists: Boolean;
|
||||
procedure TSqlite3Dataset.InternalCancel;
|
||||
{
|
||||
var
|
||||
AHandle,vm:Pointer;
|
||||
vm:Pointer;
|
||||
i:Integer;
|
||||
ActiveItem:PDataRecord;
|
||||
ASql:String;
|
||||
}
|
||||
begin
|
||||
{
|
||||
//WriteLn('InternalCancel called');
|
||||
if FPrimaryKeyNo <> - 1 then //requires a primarykey
|
||||
begin
|
||||
ActiveItem:=PPDataRecord(ActiveBuffer)^;
|
||||
if ActiveItem = FBeginItem then //Dataset is empty
|
||||
Exit;
|
||||
for i:= 0 to FRowCount -1 do
|
||||
StrDispose(ActiveItem^.Row[i]);
|
||||
|
||||
if FAddedItems.IndexOf(ActiveItem) <> -1 then //the record is not in the database
|
||||
begin
|
||||
for i:= 0 to FRowCount - 1 do
|
||||
begin
|
||||
ActiveItem^.Row[i]:=nil;
|
||||
//DataEvent(deFieldChange, Ptrint(Fields[i]));
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
ASql:=FSelectSqlStr+' Where '+Fields[FPrimaryKeyNo].FieldName+
|
||||
' = '+StrPas(ActiveItem^.Row[FPrimaryKeyNo]);
|
||||
//writeln(Asql);
|
||||
sqlite3_prepare(FSqliteHandle,PChar(ASql),-1,@vm,nil);
|
||||
if sqlite3_step(vm) = SQLITE_ROW then
|
||||
begin
|
||||
for i:= 0 to FRowCount - 1 do
|
||||
begin
|
||||
ActiveItem^.Row[i]:=StrNew(sqlite3_column_text(vm,i));
|
||||
//DataEvent(deFieldChange, Ptrint(Fields[i]));
|
||||
end;
|
||||
end;
|
||||
sqlite3_finalize(vm);
|
||||
end;
|
||||
}
|
||||
end;
|
||||
|
||||
function TSqlite3Dataset.TableExists(const ATableName:String): Boolean;
|
||||
var
|
||||
vm:Pointer;
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
writeln('##TSqlite3Dataset.TableExists##');
|
||||
{$endif}
|
||||
Result:=False;
|
||||
if not (FTableName = '') and FileExists(FFileName) then
|
||||
if not (ATableName = '') and FileExists(FFileName) then
|
||||
begin
|
||||
if FSqliteHandle = nil then
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
writeln('TableExists - FSqliteHandle=nil : Opening a file');
|
||||
{$endif}
|
||||
AHandle:=GetSqliteHandle;
|
||||
end
|
||||
else
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
writeln('TableExists - FSqliteHandle<>nil : Using FSqliteHandle');
|
||||
{$endif}
|
||||
AHandle:=FSqliteHandle;
|
||||
end;
|
||||
FSqliteReturnId:=sqlite3_prepare(AHandle,
|
||||
Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ FTableName+ ''';'),
|
||||
GetSqliteHandle;
|
||||
FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,
|
||||
Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';'),
|
||||
-1,@vm,nil);
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists.sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
|
||||
WriteLn(' sqlite3_prepare - SqliteReturnString:',SqliteReturnString);
|
||||
{$endif}
|
||||
FSqliteReturnId:=sqlite3_step(vm);
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists.sqlite3_step - SqliteReturnString:',SqliteReturnString);
|
||||
WriteLn(' sqlite3_step - SqliteReturnString:',SqliteReturnString);
|
||||
{$endif}
|
||||
Result:=FSqliteReturnId = SQLITE_ROW;
|
||||
sqlite3_finalize(vm);
|
||||
if (FSqliteHandle = nil) then
|
||||
sqlite3_close(AHandle);
|
||||
end;
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists ('+FTableName+') Result:',Result);
|
||||
WriteLn(' Table '+ATableName+' exists: ',Result);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function TSqlite3Dataset.SqliteReturnString: String;
|
||||
begin
|
||||
case FSqliteReturnId of
|
||||
SQLITE_OK : Result := 'SQLITE_OK ';
|
||||
SQLITE_ERROR : Result := 'SQLITE_ERROR ';
|
||||
SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
|
||||
SQLITE_PERM : Result := 'SQLITE_PERM ';
|
||||
SQLITE_ABORT : Result := 'SQLITE_ABORT ';
|
||||
SQLITE_BUSY : Result := 'SQLITE_BUSY ';
|
||||
SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
|
||||
SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
|
||||
SQLITE_READONLY : Result := 'SQLITE_READONLY ';
|
||||
SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
|
||||
SQLITE_IOERR : Result := 'SQLITE_IOERR ';
|
||||
SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
|
||||
SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
|
||||
SQLITE_FULL : Result := 'SQLITE_FULL ';
|
||||
SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
|
||||
SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
|
||||
SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
|
||||
SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
|
||||
SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
|
||||
SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
|
||||
SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
|
||||
SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
|
||||
SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
|
||||
SQLITE_AUTH : Result := 'SQLITE_AUTH ';
|
||||
SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
|
||||
SQLITE_RANGE : Result := 'SQLITE_RANGE ';
|
||||
SQLITE_ROW : Result := 'SQLITE_ROW ';
|
||||
SQLITE_NOTADB : Result := 'SQLITE_NOTADB ';
|
||||
SQLITE_DONE : Result := 'SQLITE_DONE ';
|
||||
SQLITE_OK : Result := 'SQLITE_OK';
|
||||
SQLITE_ERROR : Result := 'SQLITE_ERROR';
|
||||
SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL';
|
||||
SQLITE_PERM : Result := 'SQLITE_PERM';
|
||||
SQLITE_ABORT : Result := 'SQLITE_ABORT';
|
||||
SQLITE_BUSY : Result := 'SQLITE_BUSY';
|
||||
SQLITE_LOCKED : Result := 'SQLITE_LOCKED';
|
||||
SQLITE_NOMEM : Result := 'SQLITE_NOMEM';
|
||||
SQLITE_READONLY : Result := 'SQLITE_READONLY';
|
||||
SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT';
|
||||
SQLITE_IOERR : Result := 'SQLITE_IOERR';
|
||||
SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT';
|
||||
SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND';
|
||||
SQLITE_FULL : Result := 'SQLITE_FULL';
|
||||
SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN';
|
||||
SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL';
|
||||
SQLITE_EMPTY : Result := 'SQLITE_EMPTY';
|
||||
SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA';
|
||||
SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG';
|
||||
SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT';
|
||||
SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH';
|
||||
SQLITE_MISUSE : Result := 'SQLITE_MISUSE';
|
||||
SQLITE_NOLFS : Result := 'SQLITE_NOLFS';
|
||||
SQLITE_AUTH : Result := 'SQLITE_AUTH';
|
||||
SQLITE_FORMAT : Result := 'SQLITE_FORMAT';
|
||||
SQLITE_RANGE : Result := 'SQLITE_RANGE';
|
||||
SQLITE_ROW : Result := 'SQLITE_ROW';
|
||||
SQLITE_NOTADB : Result := 'SQLITE_NOTADB';
|
||||
SQLITE_DONE : Result := 'SQLITE_DONE';
|
||||
else
|
||||
Result:='Unknow Return Value';
|
||||
end;
|
||||
Result:=Result+' - '+sqlite3_errmsg(FSqliteHandle);
|
||||
end;
|
||||
|
||||
function TSqlite3Dataset.GetSqliteVersion: String;
|
||||
@ -305,7 +366,7 @@ end;
|
||||
|
||||
function TSqlite3Dataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
|
||||
var
|
||||
vm,AHandle:Pointer;
|
||||
vm:Pointer;
|
||||
|
||||
procedure FillStrings;
|
||||
begin
|
||||
@ -324,20 +385,12 @@ var
|
||||
end;
|
||||
end;
|
||||
begin
|
||||
if FSqliteHandle <> nil then
|
||||
AHandle:=FSqliteHandle
|
||||
else
|
||||
if FileExists(FFileName) then
|
||||
AHandle:=GetSqliteHandle
|
||||
else
|
||||
DatabaseError('File "'+FFileName+'" not Exists',Self);
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
Result:='';
|
||||
// It's up to the caller clear or not the list
|
||||
//if AStrList <> nil then
|
||||
// AStrList.Clear;
|
||||
FSqliteReturnId:=sqlite3_prepare(AHandle,Pchar(ASql),-1,@vm,nil);
|
||||
FSqliteReturnId:=sqlite3_prepare(FSqliteHandle,Pchar(ASql),-1,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
|
||||
FSqliteReturnId:=sqlite3_step(vm);
|
||||
if (FSqliteReturnId = SQLITE_ROW) and (sqlite3_column_count(vm) > 0) then
|
||||
@ -352,8 +405,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
sqlite3_finalize(vm);
|
||||
if FSqliteHandle = nil then
|
||||
sqlite3_close(AHandle);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -36,16 +36,18 @@ type
|
||||
TSqliteDataset = class (TCustomSqliteDataset)
|
||||
private
|
||||
function SqliteExec(AHandle: Pointer; ASql:PChar):Integer;override;
|
||||
function GetSqliteHandle: Pointer; override;
|
||||
function InternalGetHandle: Pointer; override;
|
||||
function GetSqliteEncoding: String;
|
||||
function GetSqliteVersion: String; override;
|
||||
procedure SqliteClose(AHandle: Pointer);override;
|
||||
procedure InternalCloseHandle;override;
|
||||
procedure BuildLinkedList; override;
|
||||
protected
|
||||
procedure InternalInitFieldDefs; override;
|
||||
function GetRowsAffected:Integer; override;
|
||||
public
|
||||
procedure ExecuteDirect(const ASql: String);override;
|
||||
function SqliteReturnString: String; override;
|
||||
function TableExists: Boolean;override;
|
||||
function TableExists(const ATableName:String): Boolean;override;
|
||||
function QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;override;
|
||||
property SqliteEncoding: String read GetSqliteEncoding;
|
||||
end;
|
||||
@ -58,6 +60,8 @@ uses
|
||||
var
|
||||
DummyAutoIncFieldNo:Integer;
|
||||
|
||||
//function sqlite_last_statement_changes(dbhandle:Pointer):longint;cdecl;external 'sqlite' name 'sqlite_last_statement_changes';
|
||||
|
||||
function GetAutoIncValue(NextValue: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
|
||||
var
|
||||
CodeError, TempInt: Integer;
|
||||
@ -76,7 +80,7 @@ end;
|
||||
function GetFieldDefs(TheDataset: Pointer; Columns: Integer; ColumnValues: PPChar; ColumnNames: PPChar): integer; cdecl;
|
||||
var
|
||||
FieldSize:Word;
|
||||
Counter:Integer;
|
||||
i:Integer;
|
||||
AType:TFieldType;
|
||||
ColumnStr:String;
|
||||
begin
|
||||
@ -84,30 +88,39 @@ begin
|
||||
// regardless of what is in Create Table, but returns
|
||||
// exactly what is in Create Table statement
|
||||
// here is a trick to get the datatype.
|
||||
// If the field contains another type, there will be problems
|
||||
for Counter:= 0 to Columns - 1 do
|
||||
// If the field contains another type, may have problems
|
||||
for i:= 0 to Columns - 1 do
|
||||
begin
|
||||
ColumnStr:= UpperCase(StrPas(ColumnNames[Counter + Columns]));
|
||||
if (ColumnStr = 'INTEGER') then
|
||||
ColumnStr:= UpperCase(StrPas(ColumnNames[i + Columns]));
|
||||
if (ColumnStr = 'INTEGER') or (ColumnStr = 'INT') then
|
||||
begin
|
||||
AType:= ftInteger;
|
||||
if TCustomSqliteDataset(TheDataset).AutoIncrementKey and
|
||||
(UpperCase(StrPas(ColumnNames[i])) = UpperCase(TCustomSqliteDataset(TheDataset).PrimaryKey)) then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
DummyAutoIncFieldNo:=i;
|
||||
end
|
||||
else
|
||||
AType:= ftInteger;
|
||||
FieldSize:=SizeOf(LongInt);
|
||||
end else if (ColumnStr = 'VARCHAR') then
|
||||
end else if Pos('VARCHAR',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftString;
|
||||
FieldSize:=10;//??
|
||||
end else if (ColumnStr = 'BOOLEAN') then
|
||||
FieldSize:=0;
|
||||
end else if Pos('BOOL',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftBoolean;
|
||||
FieldSize:=SizeOf(Boolean);
|
||||
end else if (ColumnStr = 'FLOAT') then
|
||||
FieldSize:=SizeOf(WordBool);
|
||||
end else if Pos('AUTOINC',ColumnStr) = 1 then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
FieldSize:=SizeOf(LongInt);
|
||||
if DummyAutoIncFieldNo = -1 then
|
||||
DummyAutoIncFieldNo:= i;
|
||||
end else if (Pos('FLOAT',ColumnStr)=1) or (Pos('NUMERIC',ColumnStr)=1) then
|
||||
begin
|
||||
AType:= ftFloat;
|
||||
FieldSize:=SizeOf(Double);
|
||||
end else if (ColumnStr = 'WORD') then
|
||||
begin
|
||||
AType:= ftWord;
|
||||
FieldSize:=SizeOf(Word);
|
||||
end else if (ColumnStr = 'DATETIME') then
|
||||
begin
|
||||
AType:= ftDateTime;
|
||||
@ -124,27 +137,26 @@ begin
|
||||
begin
|
||||
AType:= ftLargeInt;
|
||||
FieldSize:=SizeOf(LargeInt);
|
||||
end else if (ColumnStr = 'TEXT') then
|
||||
begin
|
||||
AType:= ftMemo;
|
||||
FieldSize:=0;
|
||||
end else if (ColumnStr = 'CURRENCY') then
|
||||
begin
|
||||
AType:= ftCurrency;
|
||||
FieldSize:=SizeOf(Double);
|
||||
end else if (ColumnStr = 'MEMO') then
|
||||
end else if (ColumnStr = 'WORD') then
|
||||
begin
|
||||
AType:= ftMemo;
|
||||
FieldSize:=10;//??
|
||||
end else if (ColumnStr = 'AUTOINC') then
|
||||
begin
|
||||
AType:= ftAutoInc;
|
||||
FieldSize:=SizeOf(Integer);
|
||||
if DummyAutoIncFieldNo = -1 then
|
||||
DummyAutoIncFieldNo:= Counter;
|
||||
AType:= ftWord;
|
||||
FieldSize:=SizeOf(Word);
|
||||
end else
|
||||
begin
|
||||
DatabaseError('Field type "'+ColumnStr+'" not recognized',TDataset(TheDataset));
|
||||
AType:=ftString;
|
||||
FieldSize:=0;
|
||||
end;
|
||||
TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[Counter]), AType, FieldSize, False);
|
||||
TDataset(TheDataset).FieldDefs.Add(StrPas(ColumnNames[i]), AType, FieldSize, False);
|
||||
end;
|
||||
result:=-1;
|
||||
Result:=-1;
|
||||
end;
|
||||
|
||||
|
||||
@ -155,20 +167,19 @@ begin
|
||||
Result:=sqlite_exec(AHandle, ASql, nil, nil, nil);
|
||||
end;
|
||||
|
||||
procedure TSqliteDataset.SqliteClose(AHandle: Pointer);
|
||||
procedure TSqliteDataset.InternalCloseHandle;
|
||||
begin
|
||||
sqlite_close(AHandle);
|
||||
sqlite_close(FSqliteHandle);
|
||||
FSqliteHandle:=nil;
|
||||
end;
|
||||
|
||||
|
||||
function TSqliteDataset.GetSqliteHandle: Pointer;
|
||||
function TSqliteDataset.InternalGetHandle: Pointer;
|
||||
begin
|
||||
Result:=sqlite_open(PChar(FFileName),0,nil);
|
||||
end;
|
||||
|
||||
procedure TSqliteDataset.InternalInitFieldDefs;
|
||||
begin
|
||||
|
||||
FieldDefs.Clear;
|
||||
sqlite_exec(FSqliteHandle,PChar('PRAGMA empty_result_callbacks = ON;PRAGMA show_datatypes = ON;'),nil,nil,nil);
|
||||
DummyAutoIncFieldNo:=-1;
|
||||
@ -181,6 +192,27 @@ begin
|
||||
FRowBufferSize:=(SizeOf(PPChar)*FieldDefs.Count);
|
||||
end;
|
||||
|
||||
function TSqliteDataset.GetRowsAffected: Integer;
|
||||
begin
|
||||
Result:=sqlite_changes(FSqliteHandle);
|
||||
//Result:=sqlite_last_statement_changes(FSqliteHandle);
|
||||
end;
|
||||
|
||||
procedure TSqliteDataset.ExecuteDirect(const ASql: String);
|
||||
var
|
||||
vm:Pointer;
|
||||
ColumnNames,ColumnValues:PPChar;
|
||||
ColCount:Integer;
|
||||
begin
|
||||
FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(ASql),nil,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
|
||||
FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
|
||||
|
||||
sqlite_finalize(vm, nil);
|
||||
end;
|
||||
|
||||
procedure TSqliteDataset.BuildLinkedList;
|
||||
var
|
||||
TempItem:PDataRecord;
|
||||
@ -195,12 +227,7 @@ begin
|
||||
|
||||
FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(FSql),nil,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
case FSqliteReturnId of
|
||||
SQLITE_ERROR:
|
||||
DatabaseError('Invalid SQL',Self);
|
||||
else
|
||||
DatabaseError('Error returned by sqlite while retrieving data: '+SqliteReturnString,Self);
|
||||
end;
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
|
||||
FDataAllocated:=True;
|
||||
|
||||
@ -214,7 +241,7 @@ begin
|
||||
TempItem^.Next^.Previous:=TempItem;
|
||||
TempItem:=TempItem^.Next;
|
||||
GetMem(TempItem^.Row,FRowBufferSize);
|
||||
For Counter := 0 to FRowCount - 1 do
|
||||
for Counter := 0 to FRowCount - 1 do
|
||||
TempItem^.Row[Counter]:=StrNew(ColumnValues[Counter]);
|
||||
FSqliteReturnId:=sqlite_step(vm,@FRowCount,@ColumnValues,@ColumnNames);
|
||||
end;
|
||||
@ -234,83 +261,73 @@ begin
|
||||
FBeginItem^.Row[Counter]:=nil;
|
||||
end;
|
||||
|
||||
function TSqliteDataset.TableExists: Boolean;
|
||||
function TSqliteDataset.TableExists(const ATableName:String): Boolean;
|
||||
var
|
||||
AHandle,vm:Pointer;
|
||||
vm:Pointer;
|
||||
ColumnNames,ColumnValues:PPChar;
|
||||
AInt:Integer;
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('##TSqliteDataset.TableExists##');
|
||||
{$endif}
|
||||
Result:=False;
|
||||
if not (FTableName = '') and FileExists(FFileName) then
|
||||
if not (ATableName = '') and FileExists(FFileName) then
|
||||
begin
|
||||
if FSqliteHandle = nil then
|
||||
begin
|
||||
{$ifdef DEBUG}
|
||||
writeln('TableExists - FSqliteHandle=nil : Opening a file');
|
||||
{$endif}
|
||||
AHandle:=GetSqliteHandle;
|
||||
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+ ''';'),
|
||||
GetSqliteHandle;
|
||||
FSqliteReturnId:=sqlite_compile(FSqliteHandle,
|
||||
Pchar('SELECT name FROM SQLITE_MASTER WHERE type = ''table'' AND name LIKE '''+ ATableName+ ''';'),
|
||||
nil,@vm,nil);
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists.sqlite_compile - SqliteReturnString:',SqliteReturnString);
|
||||
WriteLn(' sqlite_compile - SqliteReturnString:',SqliteReturnString);
|
||||
{$endif}
|
||||
FSqliteReturnId:=sqlite_step(vm,@AInt,@ColumnValues,@ColumnNames);
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists.sqlite_step - SqliteReturnString:',SqliteReturnString);
|
||||
WriteLn(' sqlite_step - SqliteReturnString:',SqliteReturnString);
|
||||
{$endif}
|
||||
Result:=FSqliteReturnId = SQLITE_ROW;
|
||||
sqlite_finalize(vm, nil);
|
||||
if FSqliteHandle = nil then
|
||||
SqliteClose(AHandle);
|
||||
end;
|
||||
{$ifdef DEBUG}
|
||||
WriteLn('TableExists ('+FTableName+') Result:',Result);
|
||||
WriteLn(' Table '+ATableName+' exists:',Result);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
function TSqliteDataset.SqliteReturnString: String;
|
||||
begin
|
||||
case FSqliteReturnId of
|
||||
SQLITE_OK : Result := 'SQLITE_OK ';
|
||||
SQLITE_ERROR : Result := 'SQLITE_ERROR ';
|
||||
SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL ';
|
||||
SQLITE_PERM : Result := 'SQLITE_PERM ';
|
||||
SQLITE_ABORT : Result := 'SQLITE_ABORT ';
|
||||
SQLITE_BUSY : Result := 'SQLITE_BUSY ';
|
||||
SQLITE_LOCKED : Result := 'SQLITE_LOCKED ';
|
||||
SQLITE_NOMEM : Result := 'SQLITE_NOMEM ';
|
||||
SQLITE_READONLY : Result := 'SQLITE_READONLY ';
|
||||
SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT ';
|
||||
SQLITE_IOERR : Result := 'SQLITE_IOERR ';
|
||||
SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT ';
|
||||
SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND ';
|
||||
SQLITE_FULL : Result := 'SQLITE_FULL ';
|
||||
SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN ';
|
||||
SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL ';
|
||||
SQLITE_EMPTY : Result := 'SQLITE_EMPTY ';
|
||||
SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA ';
|
||||
SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG ';
|
||||
SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT ';
|
||||
SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH ';
|
||||
SQLITE_MISUSE : Result := 'SQLITE_MISUSE ';
|
||||
SQLITE_NOLFS : Result := 'SQLITE_NOLFS ';
|
||||
SQLITE_AUTH : Result := 'SQLITE_AUTH ';
|
||||
SQLITE_FORMAT : Result := 'SQLITE_FORMAT ';
|
||||
SQLITE_RANGE : Result := 'SQLITE_RANGE ';
|
||||
SQLITE_ROW : Result := 'SQLITE_ROW ';
|
||||
SQLITE_DONE : Result := 'SQLITE_DONE ';
|
||||
SQLITE_OK : Result := 'SQLITE_OK';
|
||||
SQLITE_ERROR : Result := 'SQLITE_ERROR';
|
||||
SQLITE_INTERNAL : Result := 'SQLITE_INTERNAL';
|
||||
SQLITE_PERM : Result := 'SQLITE_PERM';
|
||||
SQLITE_ABORT : Result := 'SQLITE_ABORT';
|
||||
SQLITE_BUSY : Result := 'SQLITE_BUSY';
|
||||
SQLITE_LOCKED : Result := 'SQLITE_LOCKED';
|
||||
SQLITE_NOMEM : Result := 'SQLITE_NOMEM';
|
||||
SQLITE_READONLY : Result := 'SQLITE_READONLY';
|
||||
SQLITE_INTERRUPT : Result := 'SQLITE_INTERRUPT';
|
||||
SQLITE_IOERR : Result := 'SQLITE_IOERR';
|
||||
SQLITE_CORRUPT : Result := 'SQLITE_CORRUPT';
|
||||
SQLITE_NOTFOUND : Result := 'SQLITE_NOTFOUND';
|
||||
SQLITE_FULL : Result := 'SQLITE_FULL';
|
||||
SQLITE_CANTOPEN : Result := 'SQLITE_CANTOPEN';
|
||||
SQLITE_PROTOCOL : Result := 'SQLITE_PROTOCOL';
|
||||
SQLITE_EMPTY : Result := 'SQLITE_EMPTY';
|
||||
SQLITE_SCHEMA : Result := 'SQLITE_SCHEMA';
|
||||
SQLITE_TOOBIG : Result := 'SQLITE_TOOBIG';
|
||||
SQLITE_CONSTRAINT : Result := 'SQLITE_CONSTRAINT';
|
||||
SQLITE_MISMATCH : Result := 'SQLITE_MISMATCH';
|
||||
SQLITE_MISUSE : Result := 'SQLITE_MISUSE';
|
||||
SQLITE_NOLFS : Result := 'SQLITE_NOLFS';
|
||||
SQLITE_AUTH : Result := 'SQLITE_AUTH';
|
||||
SQLITE_FORMAT : Result := 'SQLITE_FORMAT';
|
||||
SQLITE_RANGE : Result := 'SQLITE_RANGE';
|
||||
SQLITE_ROW : begin Result := 'SQLITE_ROW - not an error'; Exit; end;
|
||||
SQLITE_DONE : begin Result := 'SQLITE_DONE - not an error'; Exit; end;
|
||||
else
|
||||
Result:='Unknow Return Value';
|
||||
end;
|
||||
Result:=Result+' - '+sqlite_error_string(FSqliteReturnId);
|
||||
end;
|
||||
|
||||
function TSqliteDataset.GetSqliteEncoding: String;
|
||||
@ -325,7 +342,7 @@ end;
|
||||
|
||||
function TSqliteDataset.QuickQuery(const ASql:String;const AStrList: TStrings;FillObjects:Boolean):String;
|
||||
var
|
||||
vm,AHandle:Pointer;
|
||||
vm:Pointer;
|
||||
ColumnNames,ColumnValues:PPChar;
|
||||
ColCount:Integer;
|
||||
|
||||
@ -347,20 +364,12 @@ var
|
||||
end;
|
||||
end;
|
||||
begin
|
||||
if FSqliteHandle <> nil then
|
||||
AHandle:=FSqliteHandle
|
||||
else
|
||||
if FileExists(FFileName) then
|
||||
AHandle:=GetSqliteHandle
|
||||
else
|
||||
DatabaseError('File '+FFileName+' not Exists',Self);
|
||||
if FSqliteHandle = nil then
|
||||
GetSqliteHandle;
|
||||
Result:='';
|
||||
// It's up to the caller clear or not the list
|
||||
//if AStrList <> nil then
|
||||
// AStrList.Clear;
|
||||
FSqliteReturnId:=sqlite_compile(AHandle,Pchar(ASql),nil,@vm,nil);
|
||||
FSqliteReturnId:=sqlite_compile(FSqliteHandle,Pchar(ASql),nil,@vm,nil);
|
||||
if FSqliteReturnId <> SQLITE_OK then
|
||||
DatabaseError('Error returned by sqlite in QuickQuery: '+SqliteReturnString,Self);
|
||||
DatabaseError(SqliteReturnString,Self);
|
||||
|
||||
FSqliteReturnId:=sqlite_step(vm,@ColCount,@ColumnValues,@ColumnNames);
|
||||
if (FSqliteReturnId = SQLITE_ROW) and (ColCount > 0) then
|
||||
@ -375,8 +384,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
sqlite_finalize(vm, nil);
|
||||
if FSqliteHandle = nil then
|
||||
sqlite_close(AHandle);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user