unit BDEClientDataSet;
interface
uses Windows, SysUtils, Variants, Classes, DB, DBCommon, Midas,
SqlTimSt, DBClient, DBLocal, Provider, DBTables;
type
{ TBDEQuery }
TBDEQuery = class(TQuery)
private
FKeyFields: string;
protected
function PSGetDefaultOrder: TIndexDef; override;
end;
{ TBDEClientDataSet }
TBDEClientDataSet = class(TCustomCachedDataSet)
private
FCommandText: string;
FCurrentCommand: string;
FDataSet: TBDEQuery;
FDatabase: TDataBase;
FLocalParams: TParams;
FStreamedActive: Boolean;
procedure CheckMasterSourceActive(MasterSource: TDataSource);
procedure SetDetailsActive(Value: Boolean);
function GetConnection: TDataBase;
function GetDataSet: TDataSet;
function GetMasterSource: TDataSource;
function GetMasterFields: string;
procedure SetConnection(Value: TDataBase);
procedure SetDataSource(Value: TDataSource);
procedure SetLocalParams;
procedure SetMasterFields(const Value: string);
procedure SetParamsFromSQL(const Value: string);
procedure SetSQL(const Value: string);
protected
function GetCommandText: String; override;
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetActive(Value: Boolean); override;
procedure SetCommandText(Value: string); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure CloneCursor(Source: TCustomClientDataSet; Reset: Boolean;
KeepSettings: Boolean = False); override;
procedure GetFieldNames(List: TStrings); override;
function GetQuoteChar: String;
property DataSet: TDataSet read GetDataSet;
published
property Active;
property CommandText: string read GetCommandText write SetCommandText;
property DBConnection: TDataBase read GetConnection write SetConnection;
property MasterFields read GetMasterFields write SetMasterFields;
property MasterSource: TDataSource read GetMasterSource write SetDataSource;
end;
procedure Register;
implementation
uses BDEConst, MidConst;
type
{ TBDECDSParams }
TBDECDSParams = class(TParams)
private
FFieldName: TStrings;
protected
procedure ParseSelect(SQL: string);
public
constructor Create(Owner: TPersistent);
Destructor Destroy; override;
end;
constructor TBDECDSParams.Create(Owner: TPersistent);
begin
inherited;
FFieldName := TStringList.Create;
end;
destructor TBDECDSParams.Destroy;
begin
FreeAndNil(FFieldName);
inherited;
end;
procedure TBDECDSParams.ParseSelect(SQL: string);
const
SSelect = 'select';
var
FWhereFound: Boolean;
Start: PChar;
FName, Value: string;
SQLToken, CurSection, LastToken: TSQLToken;
Params: Integer;
begin
if Pos(' ' + SSelect + ' ', LowerCase(string(PChar(SQL)+8))) 1 then Exit; // can't parse sub queries
Start := PChar(ParseSQL(PChar(SQL), True));
CurSection := stUnknown;
LastToken := stUnknown;
FWhereFound := False;
Params := 0;
repeat
repeat
SQLToken := NextSQLToken(Start, FName, CurSection);
if SQLToken in [stWhere] then
begin
FWhereFound := True;
LastToken := stWhere;
end else if SQLToken in [stTableName] then
begin
{ Check for owner qualified table name }
if Start^ = '.' then
NextSQLToken(Start, FName, CurSection);
end else
if (SQLToken = stValue) and (LastToken = stWhere) then
SQLToken := stFieldName;
if SQLToken in SQLSections then CurSection := SQLToken;
until SQLToken in [stFieldName, stEnd];
if FWhereFound and (SQLToken in [stFieldName]) then
repeat
SQLToken := NextSQLToken(Start, Value, CurSection);
if SQLToken in SQLSections then CurSection := SQLToken;
until SQLToken in [stEnd,stValue,stIsNull,stIsNotNull,stFieldName];
if Value='?' then
begin
FFieldName.Add(FName);
Inc(Params);
end;
until (Params = Count) or (SQLToken in [stEnd]);
end;
{ TBDEQuery }
function TBDEQuery.PSGetDefaultOrder: TIndexDef;
begin
if FKeyFields = '' then
Result := inherited PSGetDefaultOrder
else
begin // detail table default order
Result := TIndexDef.Create(nil);
Result.Options := [ixUnique]; // keyfield is unique
Result.Name := StringReplace(FKeyFields, ';', '_', [rfReplaceAll]);
Result.Fields := FKeyFields;
end;
end;
{ TBDEClientDataSet }
constructor TBDEClientDataSet.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDataSet := TBDEQuery.Create(nil);
FDataSet.Name := Self.Name + 'DataSet1';
Provider.DataSet := FDataSet;
SqlDBType := typeBDE;
FLocalParams := TParams.Create;
end;
destructor TBDEClientDataSet.Destroy;
begin
FreeAndNil(FLocalParams);
FDataSet.Close;
FreeAndNil(FDataSet);
inherited Destroy;
end;
procedure TBDEClientDataSet.GetFieldNames(List: TStrings);
var
Opened: Boolean;
begin
Opened := (Active = False);
try
if Opened then
Open;
inherited GetFieldNames(List);
finally
if Opened then Close;
end;
end;
function TBDEClientDataSet.GetCommandText: string;
begin
Result := FCommandText;
end;
function TBDEClientDataSet.GetDataSet: TDataSet;
begin
Result := FDataSet as TDataSet;
end;
procedure TBDEClientDataSet.CheckMasterSourceActive(MasterSource: TDataSource);
begin
if Assigned(MasterSource) and Assigned(MasterSource.DataSet) then
if not MasterSource.DataSet.Active then
DatabaseError(SMasterNotOpen);
end;
procedure TBDEClientDataSet.SetParamsFromSQL(const Value: string);
var
DataSet: TQuery;
TableName, TempQuery, Q: string;
List: TBDECDSParams;
I: Integer;
Field: TField;
begin
TableName := GetTableNameFromSQL(Value);
if TableName '' then
begin
TempQuery := Value;
List := TBDECDSParams.Create(Self);
try
List.ParseSelect(TempQuery);
List.AssignValues(Params);
for I := 0 to List.Count - 1 do
List[I].ParamType := ptInput;
DataSet := TQuery.Create(nil);
try
DataSet.DatabaseName := FDataSet.DatabaseName;
Q := GetQuoteChar;
DataSet.SQL.Add('select * from ' + Q + TableName + Q + ' where 0 = 1'); { do not localize }
try
DataSet.Open;
for I := 0 to List.Count - 1 do
begin
if List.FFieldName.Count I then
begin
try
Field := DataSet.FieldByName(List.FFieldName[I]);
except
Field := nil;
end;
end else
Field := nil;
if Assigned(Field) then
begin
if Field.DataType ftString then
List[I].DataType := Field.DataType
else if TStringField(Field).FixedChar then
List[I].DataType := ftFixedChar
else
List[I].DataType := ftString;
end;
end;
except
// ignore all exceptions
end;
finally
DataSet.Free;
end;
finally
if List.Count 0 then
Params.Assign(List);
List.Free;
end;
end;
end;
procedure TBDEClientDataSet.SetSQL(const Value: string);
begin
if Assigned(Provider.DataSet) then
begin
TQuery(Provider.DataSet).SQL.Clear;
if Value '' then
TQuery(Provider.DataSet).SQL.Add(Value);
inherited SetCommandText(Value);
end else
DataBaseError(SNoDataProvider);
end;
procedure TBDEClientDataSet.Loaded;
begin
inherited Loaded;
if FStreamedActive then
begin
SetActive(True);
FStreamedActive := False;
end;
end;
function TBDEClientDataSet.GetMasterFields: string;
begin
Result := inherited MasterFields;
end;
procedure TBDEClientDataSet.SetMasterFields(const Value: string);
begin
inherited MasterFields := Value;
if Value '' then
IndexFieldNames := Value;
FDataSet.FKeyFields := '';
end;
procedure TBDEClientDataSet.SetCommandText(Value: String);
begin
inherited SetCommandText(Value);
FCommandText := Value;
if not (csLoading in ComponentState) then
begin
FDataSet.FKeyFields := '';
IndexFieldNames := '';
MasterFields := '';
IndexName := '';
IndexDefs.Clear;
Params.Clear;
if (csDesigning in ComponentState) and (Value '') then
SetParamsFromSQL(Value);
end;
end;
function TBDEClientDataSet.GetConnection: TDatabase;
begin
Result := FDataBase;
end;
procedure TBDEClientDataSet.SetConnection(Value: TDataBase);
begin
if Value = FDatabase then exit;
CheckInactive;
if Assigned(Value) then
begin
if not (csLoading in ComponentState) and (Value.DatabaseName = '') then
DatabaseError(SDatabaseNameMissing);
FDataSet.DatabaseName := Value.DatabaseName;
end else
FDataSet.DataBaseName := '';
FDataBase := Value;
end;
function TBDEClientDataSet.GetQuoteChar: String;
begin
Result := '';
if Assigned(FDataSet) then
Result := FDataSet.PSGetQuoteChar;
end;
procedure TBDEClientDataSet.CloneCursor(Source: TCustomClientDataSet; Reset: Boolean;
KeepSettings: Boolean = False);
begin
if not (Source is TBDEClientDataSet) then
DatabaseError(SInvalidClone);
Provider.DataSet := TBDEClientDataSet(Source).Provider.DataSet;
DBConnection := TBDEClientDataSet(Source).DBConnection;
CommandText := TBDEClientDataSet(Source).CommandText;
inherited CloneCursor(Source, Reset, KeepSettings);
end;
procedure TBDEClientDataSet.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if Operation = opRemove then
if AComponent = FDatabase then
begin
FDataBase := nil;
SetActive(False);
end;
end;
procedure TBDEClientDataSet.SetLocalParams;
procedure CreateParamsFromMasterFields(Create: Boolean);
var
I: Integer;
List: TStrings;
begin
List := TStringList.Create;
try
if Create then
FLocalParams.Clear;
FDataSet.FKeyFields := MasterFields;
List.CommaText := MasterFields;
for I := 0 to List.Count -1 do
begin
if Create then
FLocalParams.CreateParam( ftUnknown, MasterSource.DataSet.FieldByName(List[I]).FieldName,
ptInput);
FLocalParams[I].AssignField(MasterSource.DataSet.FieldByName(List[I]));
end;
finally
List.Free;
end;
end;
begin
if (MasterFields '') and Assigned(MasterSource) and Assigned(MasterSource.DataSet) then
begin
CreateParamsFromMasterFields(True);
FCurrentCommand := AddParamSQLForDetail(FLocalParams, CommandText, True, GetQuoteChar);
end;
end;
procedure TBDEClientDataSet.SetDataSource(Value: TDataSource);
begin
inherited MasterSource := Value;
if Assigned(Value) then
begin
if PacketRecords = -1 then PacketRecords := 0;
end else
begin
if PacketRecords = 0 then PacketRecords := -1;
end;
end;
function TBDEClientDataSet.GetMasterSource: TDataSource;
begin
Result := inherited MasterSource;
end;
procedure TBDEClientDataSet.SetDetailsActive(Value: Boolean);
var
DetailList: TList;
I: Integer;
begin
DetailList := TList.Create;
try
GetDetailDataSets(DetailList);
for I := 0 to DetailList.Count -1 do
if TDataSet(DetailList[I]) is TBDEClientDataSet then
TBDEClientDataSet(TDataSet(DetailList[I])).Active := Value;
finally
DetailList.Free;
end;
end;
procedure TBDEClientDataSet.SetActive(Value: Boolean);
begin
if Value then
begin
if csLoading in ComponentState then
begin
FStreamedActive := True;
exit;
end;
if MasterFields '' then
begin
if not (csLoading in ComponentState) then
CheckMasterSourceActive(MasterSource);
SetLocalParams;
SetSQL(FCurrentCommand);
Params := FLocalParams;
FetchParams;
end else
begin
SetSQL(FCommandText);
if Params.Count 0 then
begin
FDataSet.Params := Params;
FetchParams;
end;
end;
end;
if Value and (FDataSet.ObjectView ObjectView) then
FDataSet.ObjectView := ObjectView;
inherited SetActive(Value);
SetDetailsActive(Value);
end;
procedure Register;
begin
RegisterComponents('BDE', [TBDEClientDataSet]);
end;
end.
//以上经DBLocalB.pas改装而成,可存为任意文件名,当然扩展名是PAS
//然后安装此控件即可