home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1997 April
/
Chip_1997-04_cd.bin
/
prezent
/
cb
/
data.z
/
DB.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1997-01-16
|
216KB
|
7,926 lines
{*******************************************************}
{ }
{ Delphi Visual Component Library }
{ }
{ Copyright (c) 1995,96 Borland International }
{ }
{*******************************************************}
unit DB;
{$N+,P+,S-,R-}
interface
uses SysUtils, Windows, Bde, Classes;
const
{ TDataSet maximum number of record buffers }
dsMaxBufferCount = 1024;
{ Maximum string field size }
dsMaxStringSize = 8192;
{ SQL Trace buffer size }
smTraceBufSize = 8192 + SizeOf(TraceDesc);
{ TDBDataSet flags }
dbfOpened = 0;
dbfPrepared = 1;
dbfExecSQL = 2;
dbfTable = 3;
dbfFieldList = 4;
dbfIndexList = 5;
dbfStoredProc = 6;
dbfExecProc = 7;
dbfProcDesc = 8;
type
{ Forward declarations }
TDBError = class;
TSession = class;
TDatabase = class;
TFieldDefs = class;
TDataSet = class;
TDBDataSet = class;
TField = class;
TDataSource = class;
TDataLink = class;
{ Generic types }
PFieldDescList = ^TFieldDescList;
TFieldDescList = array[0..1023] of FLDDesc;
PIndexDescList = ^TIndexDescList;
TIndexDescList = array[0..63] of IDXDesc;
{ Exception classes }
EDatabaseError = class(Exception);
EDBEngineError = class(EDatabaseError)
private
FErrors: TList;
function GetError(Index: Integer): TDBError;
function GetErrorCount: Integer;
public
constructor Create(ErrorCode: DBIResult);
destructor Destroy; override;
property ErrorCount: Integer read GetErrorCount;
property Errors[Index: Integer]: TDBError read GetError;
end;
{ BDE error information type }
TDBError = class
private
FErrorCode: DBIResult;
FNativeError: Longint;
FMessage: string;
function GetCategory: Byte;
function GetSubCode: Byte;
public
constructor Create(Owner: EDBEngineError; ErrorCode: DBIResult;
NativeError: Longint; Message: PChar);
property Category: Byte read GetCategory;
property ErrorCode: DBIResult read FErrorCode;
property SubCode: Byte read GetSubCode;
property Message: string read FMessage;
property NativeError: Longint read FNativeError;
end;
{ TLocale }
TLocale = Pointer;
{ TBDECallback }
TBDECallbackEvent = function(CBInfo: Pointer): CBRType of Object;
TBDECallback = class
private
FHandle: hDBICur;
FOwner: TObject;
FCBType: CBType;
FOldCBData: Longint;
FOldCBBuf: Pointer;
FOldCBBufLen: Word;
FOldCBFunc: pfDBICallBack;
FInstalled: Boolean;
FCallbackEvent: TBDECallbackEvent;
protected
function Invoke(CallType: CBType; CBInfo: Pointer): CBRType;
public
constructor Create(AOwner: TObject; Handle: hDBICur; CBType: CBType;
CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TBDECallbackEvent;
Chain: Boolean);
destructor Destroy; override;
end;
{ TSessionList }
TSessionList = class(TObject)
private
FSessions: TList;
procedure AddSession(ASession: TSession);
procedure CloseAll;
function GetCount: Integer;
function GetSession(Index: Integer): TSession;
function GetCurrentSession: TSession;
function GetSessionByName(const SessionName: string): TSession;
procedure SetCurrentSession(Value: TSession);
public
constructor Create;
destructor Destroy; override;
property CurrentSession: TSession read GetCurrentSession write SetCurrentSession;
function FindSession(const SessionName: string): TSession;
procedure GetSessionNames(List: TStrings);
function OpenSession(const SessionName: string): TSession;
property Count: Integer read GetCount;
property Sessions[Index: Integer]: TSession read GetSession; default;
property List[const SessionName: string]: TSession read GetSessionByName;
end;
{ TSession }
TConfigMode = (cmPersistent, cmSession, cmAll);
TPasswordEvent = procedure(Sender: TObject; var Continue: Boolean) of Object;
TDatabaseEvent = (dbOpen, dbClose, dbAdd, dbRemove, dbAddAlias, dbDeleteAlias);
TDatabaseNotifyEvent = procedure(DBEvent: TDatabaseEvent; const Param) of object;
TBDEInitProc = procedure(Session: TSession);
TTraceFlag = (tfQPrepare, tfQExecute, tfError, tfStmt, tfConnect,
tfTransact, tfBlob, tfMisc, tfVendor);
TTraceFlags = set of TTraceFlag;
TWriteProc = function (Client: TObject; Data: PChar; Len: Integer): LongBool; StdCall;
TSMRegProc = function (Handle: Integer; ClientName: PChar;
var WriteProc: TWriteProc; Instance: TObject;
const SignalProc: Pointer): TObject; StdCall;
TSession = class(TComponent)
private
FHandle: HDBISes;
FDefault: Boolean;
FDatabases: TList;
FCallbacks: TList;
FLocale: TLocale;
FClientLib: THandle;
FSMRegProc: TSMRegProc;
FSMWriteProc: TWriteProc;
FSMBuffer: PTraceDesc;
FSMClient: TObject;
FTraceFlags: TTraceFlags;
FStreamedActive: Boolean;
FKeepConnections: Boolean;
FSessionName: string;
FNetFileDir: string;
FPrivateDir: string;
FCBSCType: CBSCType;
FDLLDetach: Boolean;
FBDEOwnsLoginCbDb: Boolean;
FLockCount: Integer;
FCBDBLogin: TCBDBLogin;
FOnPassword: TPasswordEvent;
FOnStartup: TNotifyEvent;
FOnDBNotify: TDatabaseNotifyEvent;
procedure AddDatabase(Value: TDatabase);
procedure AddConfigRecord(const Path, Node: string; List: TStrings);
procedure CallBDEInitProcs;
procedure CheckInactive;
procedure CheckConfigMode(CfgMode: TConfigMode);
function DBLoginCallback(CBInfo: Pointer): CBRType;
procedure DBNotification(DBEvent: TDatabaseEvent; const Param);
procedure DeleteConfigPath(const Path, Node: string);
function GetActive: Boolean;
function GetConfigMode: TConfigMode;
function GetDatabase(Index: Integer): TDatabase;
function GetDatabaseCount: Integer;
function GetHandle: HDBISes;
function GetNetFileDir: string;
function GetPrivateDir: string;
procedure InitializeBDE;
procedure InternalAddAlias(const Name, Driver: string; List: TStrings;
CfgMode: TConfigMode; RestoreMode: Boolean);
procedure InternalDeleteAlias(const Name: string; CfgMode: TConfigMode;
RestoreMode: Boolean);
procedure LockSession;
procedure MakeCurrent;
procedure ModifyConfigParams(const Path, Node: string; List: TStrings);
procedure RegisterCallbacks(Value: Boolean);
procedure RemoveDatabase(Value: TDatabase);
function ServerCallback(CBInfo: Pointer): CBRType;
procedure SetActive(Value: Boolean);
procedure SetConfigMode(Value: TConfigMode);
procedure SetConfigParams(const Path, Node: string; List: TStrings);
procedure SetNetFileDir(const Value: string);
procedure SetPrivateDir(const Value: string);
procedure SetSessionName(const Value: string);
procedure SetTraceFlags(Value: TTraceFlags);
procedure SMClientSignal(Sender: TObject; Data: Integer);
function SqlTraceCallback(CBInfo: Pointer): CBRType;
procedure StartSession(Value: Boolean);
procedure UnlockSession;
protected
procedure Loaded; override;
property OnDBNotify: TDatabaseNotifyEvent read FOnDBNotify write FOnDBNotify;
property BDEOwnsLoginCbDb: Boolean read FBDEOwnsLoginCbDb write FBDEOwnsLoginCbDb;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AddAlias(const Name, Driver: string; List: TStrings);
procedure AddStandardAlias(const Name, Path, DefaultDriver: string);
property ConfigMode: TConfigMode read GetConfigMode write SetConfigMode;
procedure AddPassword(const Password: string);
procedure Close;
procedure CloseDatabase(Database: TDatabase);
procedure DeleteAlias(const Name: string);
procedure DropConnections;
function FindDatabase(const DatabaseName: string): TDatabase;
procedure GetAliasNames(List: TStrings);
procedure GetAliasParams(const AliasName: string; List: TStrings);
function GetAliasDriverName(const AliasName: string): string;
procedure GetConfigParams(const Path, Section: string; List: TStrings);
procedure GetDatabaseNames(List: TStrings);
procedure GetDriverNames(List: TStrings);
procedure GetDriverParams(const DriverName: string; List: TStrings);
function GetPassword: Boolean;
procedure GetTableNames(const DatabaseName, Pattern: string;
Extensions, SystemTables: Boolean; List: TStrings);
procedure GetStoredProcNames(const DatabaseName: string; List: TStrings);
function IsAlias(const Name: string): Boolean;
procedure ModifyAlias(Name: string; List: TStrings);
procedure Open;
function OpenDatabase(const DatabaseName: string): TDatabase;
procedure RemoveAllPasswords;
procedure RemovePassword(const Password: string);
procedure SaveConfigFile;
property DatabaseCount: Integer read GetDatabaseCount;
property Databases[Index: Integer]: TDatabase read GetDatabase;
property Handle: HDBISES read GetHandle;
property Locale: TLocale read FLocale;
property TraceFlags: TTraceFlags read FTraceFlags write SetTraceFlags;
published
property Active: Boolean read GetActive write SetActive default False;
property KeepConnections: Boolean read FKeepConnections write FKeepConnections default True;
property NetFileDir: string read GetNetFileDir write SetNetFileDir;
property PrivateDir: string read GetPrivateDir write SetPrivateDir;
property SessionName: string read FSessionName write SetSessionName;
property OnPassword: TPasswordEvent read FOnPassword write FOnPassword;
property OnStartup: TNotifyEvent read FOnStartup write FOnStartup;
end;
{ TParamList }
TParamList = class(TObject)
private
FFieldCount: Integer;
FBufSize: Word;
FFieldDescs: PFieldDescList;
FBuffer: PChar;
public
constructor Create(Params: TStrings);
destructor Destroy; override;
property Buffer: PChar read FBuffer;
property FieldCount: Integer read FFieldCount;
property FieldDescs: PFieldDescList read FFieldDescs;
end;
{ TDatabase }
TTransIsolation = (tiDirtyRead, tiReadCommitted, tiRepeatableRead);
TLoginEvent = procedure(Database: TDatabase;
LoginParams: TStrings) of object;
TDatabase = class(TComponent)
private
FDataSets: TList;
FTransIsolation: TTransIsolation;
FLoginPrompt: Boolean;
FKeepConnection: Boolean;
FTemporary: Boolean;
FSessionAlias: Boolean;
FStreamedConnected: Boolean;
FLocaleLoaded: Boolean;
FAliased: Boolean;
FReserved: Byte;
FRefCount: Integer;
FHandle: HDBIDB;
FSQLBased: Boolean;
FTransHandle: HDBIXAct;
FLocale: TLocale;
FSession: TSession;
FSessionName: string;
FParams: TStrings;
FDatabaseName: string;
FDatabaseType: string;
FAcquiredHandle: Boolean;
FOnLogin: TLoginEvent;
procedure CheckActive;
procedure CheckInactive;
procedure CheckDatabaseName;
procedure CheckDatabaseAlias(var Password: string);
procedure CheckSessionName(Required: Boolean);
procedure EndTransaction(TransEnd: EXEnd);
function GetAliasName: string;
function GetConnected: Boolean;
function GetDataSet(Index: Integer): TDBDataSet;
function GetDataSetCount: Integer;
function GetDirectory: string;
function GetDriverName: string;
function GetIsSQLBased: Boolean;
function GetInTransaction: Boolean;
function GetTraceFlags: TTraceFlags;
procedure LoadLocale;
procedure Login(LoginParams: TStrings);
procedure ParamsChanging(Sender: TObject);
procedure SetAliasName(const Value: string);
procedure SetConnected(Value: Boolean);
procedure SetDatabaseName(const Value: string);
procedure SetDatabaseType(const Value: string; Aliased: Boolean);
procedure SetDirectory(const Value: string);
procedure SetDriverName(const Value: string);
procedure SetHandle(Value: HDBIDB);
procedure SetKeepConnection(Value: Boolean);
procedure SetParams(Value: TStrings);
procedure SetTraceFlags(Value: TTraceFlags);
procedure SetSessionName(const Value: string);
protected
procedure Loaded; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure ApplyUpdates(const DataSets: array of TDBDataSet);
procedure Close;
procedure CloseDataSets;
procedure Commit;
procedure FlushSchemaCache(const TableName: string);
procedure Open;
procedure Rollback;
procedure StartTransaction;
procedure ValidateName(const Name: string);
property DataSetCount: Integer read GetDataSetCount;
property DataSets[Index: Integer]: TDBDataSet read GetDataSet;
property Directory: string read GetDirectory write SetDirectory;
property Handle: HDBIDB read FHandle write SetHandle;
property IsSQLBased: Boolean read FSQLBased;
property InTransaction: Boolean read GetInTransaction;
property Locale: TLocale read FLocale;
property Session: TSession read FSession;
property Temporary: Boolean read FTemporary write FTemporary;
property SessionAlias: Boolean read FSessionAlias;
property TraceFlags: TTraceFlags read GetTraceFlags write SetTraceFlags;
published
property AliasName: string read GetAliasName write SetAliasName;
property Connected: Boolean read GetConnected write SetConnected default False;
property DatabaseName: string read FDatabaseName write SetDatabaseName;
property DriverName: string read GetDriverName write SetDriverName;
property KeepConnection: Boolean read FKeepConnection write SetKeepConnection default True;
property LoginPrompt: Boolean read FLoginPrompt write FLoginPrompt default True;
property Params: TStrings read FParams write SetParams;
property SessionName: string read FSessionName write SetSessionName;
property TransIsolation: TTransIsolation read FTransIsolation write FTransIsolation default tiReadCommitted;
property OnLogin: TLoginEvent read FOnLogin write FOnLogin;
end;
{ TDataSetDesigner }
TDataEvent = (deFieldChange, deRecordChange, deDataSetChange,
deDataSetScroll, deLayoutChange, deUpdateRecord, deUpdateState,
deCheckBrowseMode, dePropertyChange, deFieldListChange,
deFocusControl);
TDataSetDesigner = class(TObject)
private
FDataSet: TDataSet;
FSaveActive: Boolean;
FReserved: Byte;
public
constructor Create(DataSet: TDataSet);
destructor Destroy; override;
procedure BeginDesign;
procedure DataEvent(Event: TDataEvent; Info: Longint); virtual;
procedure EndDesign;
property DataSet: TDataSet read FDataSet;
end;
{ TFieldDef }
TFieldClass = class of TField;
TFieldType = (ftUnknown, ftString, ftSmallint, ftInteger, ftWord,
ftBoolean, ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime,
ftBytes, ftVarBytes, ftAutoInc, ftBlob, ftMemo, ftGraphic,
ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary);
TFieldDef = class
private
FOwner: TFieldDefs;
FName: string;
FDataType: TFieldType;
FRequired: Boolean;
FBDECalcField: Boolean;
FSize: Word;
FFieldNo: Integer;
function GetFieldClass: TFieldClass;
public
constructor Create(Owner: TFieldDefs; const Name: string;
DataType: TFieldType; Size: Word; Required: Boolean; FieldNo: Integer);
destructor Destroy; override;
function CreateField(Owner: TComponent): TField;
property BDECalcField: Boolean read FBDECalcField;
property DataType: TFieldType read FDataType;
property FieldClass: TFieldClass read GetFieldClass;
property FieldNo: Integer read FFieldNo;
property Name: string read FName;
property Required: Boolean read FRequired;
property Size: Word read FSize;
end;
{ TFieldDefs }
TFieldDefs = class
private
FDataSet: TDataSet;
FItems: TList;
FUpdated: Boolean;
FReserved: Byte;
function GetCount: Integer;
function GetItem(Index: Integer): TFieldDef;
public
constructor Create(DataSet: TDataSet);
destructor Destroy; override;
procedure Add(const Name: string; DataType: TFieldType; Size: Word;
Required: Boolean);
procedure AddFieldDesc(FieldDesc: FLDDesc; Required: Boolean;
FieldNo: Word);
procedure Assign(FieldDefs: TFieldDefs);
procedure Clear;
function Find(const Name: string): TFieldDef;
function IndexOf(const Name: string): Integer;
procedure Update;
property Count: Integer read GetCount;
property Items[Index: Integer]: TFieldDef read GetItem; default;
end;
{ TDataSet }
TBookmark = Pointer;
TBookmarkStr = String;
PBufferList = ^TBufferList;
TBufferList = array[0..dsMaxBufferCount - 1] of PChar;
TDataSetState = (dsInactive, dsBrowse, dsEdit, dsInsert,
dsSetKey, dsCalcFields, dsUpdateNew, dsUpdateOld, dsFilter);
TGetMode = (gmCurrent, gmNext, gmPrior);
TFilterOption = (foCaseInsensitive, foNoPartialCompare);
TFilterOptions = set of TFilterOption;
TLocateOption = (loCaseInsensitive, loPartialKey);
TLocateOptions = set of TLocateOption;
TResyncMode = set of (rmExact, rmCenter);
TKeyIndex = (kiLookup, kiRangeStart, kiRangeEnd, kiCurRangeStart,
kiCurRangeEnd, kiSave);
PKeyBuffer = ^TKeyBuffer;
TKeyBuffer = record
Modified: Boolean;
Exclusive: Boolean;
FieldCount: Integer;
Data: record end;
end;
TDataAction = (daFail, daAbort, daRetry);
TDataSetNotifyEvent = procedure(DataSet: TDataSet) of object;
TDataSetErrorEvent = procedure(DataSet: TDataSet; E: EDatabaseError;
var Action: TDataAction) of object;
TUpdateStatus = (usUnmodified, usModified, usInserted, usDeleted);
TUpdateKind = (ukModify, ukInsert, ukDelete);
TUpdateAction = (uaFail, uaAbort, uaSkip, uaRetry, uaApplied);
TUpdateRecordTypes = set of (rtModified, rtInserted, rtDeleted, rtUnmodified);
TUpdateErrorEvent = procedure(DataSet: TDataSet; E: EDatabaseError;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction) of object;
TUpdateRecordEvent = procedure(DataSet: TDataSet; UpdateKind: TUpdateKind;
var UpdateAction: TUpdateAction) of object;
TOnServerYieldEvent = procedure(DataSet: TDataSet; var AbortQuery: Boolean) of object;
TDataSetUpdateObject = class(TComponent)
protected
function GetDataSet: TDataSet; virtual; abstract;
procedure SetDataSet(ADataSet: TDataSet); virtual; abstract;
procedure Apply(UpdateKind: TUpdateKind); virtual; abstract;
property DataSet: TDataSet read GetDataSet write SetDataSet;
end;
TFilterRecordEvent = procedure(DataSet: TDataSet;
var Accept: Boolean) of object;
TRecNoStatus = (rnDbase, rnParadox, rnNotSupported);
PRecInfo = ^TRecInfo;
TRecInfo = record
UpdateStatus: TUpdateStatus;
RecordNumber: Longint;
end;
TDataOperation = function: DBIResult of object;
TDataSet = class(TComponent)
private
FFields: TList;
FDataSources: TList;
FFieldDefs: TFieldDefs;
FBuffers: PBufferList;
FBufListSize: Integer;
FBufferCount: Integer;
FRecordCount: Integer;
FActiveRecord: Integer;
FCurrentRecord: Integer;
FHandle: HDBICur;
FBOF: Boolean;
FEOF: Boolean;
FState: TDataSetState;
FAutoCalcFields: Boolean;
FDefaultFields: Boolean;
FCanModify: Boolean;
FModified: Boolean;
FStreamedActive: Boolean;
FInfoQueryMode: Boolean;
FDisableState: TDataSetState;
FEnableEvent: TDataEvent;
FFiltered: Boolean;
FFound: Boolean;
FRecProps: RecProps;
FRawFieldCount: Integer;
FRecordSize: Word;
FBookmarkSize: Word;
FRecInfoOfs: Word;
FBookmarkOfs: Word;
FRecNoStatus: TRecNoStatus;
FKeySize: Word;
FExpIndex: Boolean;
FCaseInsIndex: Boolean;
FCalcFieldsSize: Word;
FRecBufSize: Word;
FDisableCount: Integer;
FFirstDataLink: TDataLink;
FLocale: TLocale;
FDesigner: TDataSetDesigner;
FKeyBuffers: array[TKeyIndex] of PKeyBuffer;
FKeyBuffer: PKeyBuffer;
FCalcBuffer: PChar;
FFilterText: string;
FFilterOptions: TFilterOptions;
FExprFilter: HDBIFilter;
FFuncFilter: HDBIFilter;
FFilterBuffer: PChar;
FIndexFieldCount: Integer;
FIndexFieldMap: DBIKey;
FBDECalcFields: Boolean;
FCachedUpdates: Boolean;
FUpdateCBBuf: PDELAYUPDCbDesc;
FUpdateCallback: TBDECallback;
FInUpdateCallback: Boolean;
FUpdateErrCode: DBIResult;
FAsyncCallback: TBDECallback;
FCBYieldStep: CBYieldStep;
FOnServerYield: TOnServerYieldEvent;
FUpdateObject: TDataSetUpdateObject;
FBeforeOpen: TDataSetNotifyEvent;
FAfterOpen: TDataSetNotifyEvent;
FBeforeClose: TDataSetNotifyEvent;
FAfterClose: TDataSetNotifyEvent;
FBeforeInsert: TDataSetNotifyEvent;
FAfterInsert: TDataSetNotifyEvent;
FBeforeEdit: TDataSetNotifyEvent;
FAfterEdit: TDataSetNotifyEvent;
FBeforePost: TDataSetNotifyEvent;
FAfterPost: TDataSetNotifyEvent;
FBeforeCancel: TDataSetNotifyEvent;
FAfterCancel: TDataSetNotifyEvent;
FBeforeDelete: TDataSetNotifyEvent;
FAfterDelete: TDataSetNotifyEvent;
FOnNewRecord: TDataSetNotifyEvent;
FOnCalcFields: TDataSetNotifyEvent;
FOnUpdateError: TUpdateErrorEvent;
FOnUpdateRecord: TUpdateRecordEvent;
FOnFilterRecord: TFilterRecordEvent;
FOnEditError: TDataSetErrorEvent;
FOnPostError: TDataSetErrorEvent;
FOnDeleteError: TDataSetErrorEvent;
procedure ActivateBuffers;
procedure ActivateFilters;
procedure AddDataSource(DataSource: TDataSource);
procedure AddField(Field: TField);
procedure AddRecord(const Values: array of const; Append: Boolean);
procedure AllocKeyBuffers;
procedure AllocDelUpdCBBuf(Allocate: Boolean);
procedure BeginInsertAppend;
procedure BindFields(Binding: Boolean);
function CachedUpdateCallBack(CBInfo: Pointer): CBRType;
procedure CalculateBDEFields;
procedure CalculateFields;
procedure CheckCanModify;
procedure CheckCachedUpdateMode;
procedure CheckFieldName(const FieldName: string);
procedure CheckFieldNames(const FieldNames: string);
procedure CheckOperation(Operation: TDataOperation;
ErrorEvent: TDataSetErrorEvent);
procedure CheckRequiredFields;
procedure CheckSetKeyMode;
procedure CopyBuffer(SourceIndex, DestIndex: Integer);
function CreateExprFilter(const Expr: string;
Options: TFilterOptions; Priority: Integer): HDBIFilter;
procedure CreateFields;
function CreateFuncFilter(FilterFunc: Pointer;
Priority: Integer): HDBIFilter;
function CreateLookupFilter(Fields: TList; const Values: Variant;
Options: TLocateOptions; Priority: Integer): HDBIFilter;
procedure DeactivateFilters;
function DeleteRecord: DBIResult;
procedure DestroyFields;
function EditRecord: DBIResult;
procedure EndInsertAppend;
function FieldByNumber(FieldNo: Integer): TField;
function FindRecord(Restart, GoForward: Boolean): Boolean;
procedure FreeFieldBuffers;
procedure FreeKeyBuffers;
function GetActive: Boolean;
function GetBookmarkStr: TBookmarkStr;
procedure GetCalcFields(Index: Integer);
function GetField(Index: Integer): TField;
function GetFieldCount: Integer;
function GetFieldValue(const FieldName: string): Variant;
procedure GetIndexInfo;
function GetNextRecord: Boolean;
function GetNextRecords: Integer;
function GetPriorRecord: Boolean;
function GetPriorRecords: Integer;
function GetRecord(Index: Integer; GetMode: TGetMode): DBIResult;
function GetRecordCount: Longint;
function GetUpdatesPending: Boolean;
function InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer;
procedure InitRecord(Buffer: PChar);
procedure InternalClose;
procedure InternalOpen;
function LocateRecord(const KeyFields: string; const KeyValues: Variant;
Options: TLocateOptions; SyncCursor: Boolean): Boolean;
function MapsToIndex(Fields: TList; CaseInsensitive: Boolean): Boolean;
procedure MoveBuffer(CurIndex, NewIndex: Integer);
procedure PostKeyBuffer(Commit: Boolean);
function PostRecord: DBIResult;
function RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint; stdcall;
procedure RemoveDataSource(DataSource: TDataSource);
procedure RemoveField(Field: TField);
procedure SetActive(Value: Boolean);
procedure SetBookmarkStr(const Value: TBookmarkStr);
procedure SetBufferCount(Value: Integer);
procedure SetBufListSize(Value: Integer);
procedure SetCurrentRecord(Index: Integer);
procedure SetField(Index: Integer; Value: TField);
procedure SetFieldDefs(Value: TFieldDefs);
procedure SetFieldValue(const FieldName: string; const Value: Variant);
procedure SetFilterData(const Text: string; Options: TFilterOptions);
procedure SetFiltered(Value: Boolean);
procedure SetFilterHandle(var Filter: HDBIFilter; Value: HDBIFilter);
procedure SetFilterOptions(Value: TFilterOptions);
procedure SetFilterText(const Value: string);
procedure SetOnFilterRecord(const Value: TFilterRecordEvent);
procedure SetOnUpdateError(UpdateEvent: TUpdateErrorEvent);
procedure SetState(Value: TDataSetState);
procedure UpdateBufferCount;
function UpdateCallbackRequired: Boolean;
procedure UpdateFieldDefs;
function YieldCallBack(CBInfo: Pointer): CBRType;
protected
procedure CheckInactive;
procedure ClearBuffers;
procedure CloseCursor; virtual;
function CreateHandle: HDBICur; virtual;
procedure DataEvent(Event: TDataEvent; Info: Longint); virtual;
procedure DestroyHandle; virtual;
procedure DestroyLookupCursor; virtual;
procedure DoAfterCancel; virtual;
procedure DoAfterClose; virtual;
procedure DoAfterDelete; virtual;
procedure DoAfterEdit; virtual;
procedure DoAfterInsert; virtual;
procedure DoAfterOpen; virtual;
procedure DoAfterPost; virtual;
procedure DoBeforeCancel; virtual;
procedure DoBeforeClose; virtual;
procedure DoBeforeDelete; virtual;
procedure DoBeforeEdit; virtual;
procedure DoBeforeInsert; virtual;
procedure DoBeforeOpen; virtual;
procedure DoBeforePost; virtual;
procedure DoOnCalcFields; virtual;
procedure DoOnNewRecord; virtual;
function GetCanModify: Boolean; virtual;
function GetDataSource: TDataSource; virtual;
function GetIndexField(Index: Integer): TField;
function GetIndexFieldCount: Integer;
function GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer;
function GetKeyExclusive: Boolean;
function GetKeyFieldCount: Integer;
function GetLookupCursor(const KeyFields: string;
CaseInsensitive: Boolean): HDBICur; virtual;
function GetRecordNumber: Longint; virtual;
procedure InitFieldDefs; virtual;
procedure Loaded; override;
procedure OpenCursor; virtual;
procedure PrepareCursor; virtual;
function ResetCursorRange: Boolean;
function SetCursorRange: Boolean;
procedure SetIndexField(Index: Integer; Value: TField);
procedure SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean);
procedure SetKeyExclusive(Value: Boolean);
procedure SetKeyFieldCount(Value: Integer);
procedure SetKeyFields(KeyIndex: TKeyIndex; const Values: array of const);
procedure SetLinkRanges(MasterFields: TList);
procedure SetLocale(Value: TLocale);
procedure SetName(const Value: TComponentName); override;
procedure SwitchToIndex(const IndexName, TagName: string);
procedure GetChildren(Proc: TGetChildProc); override;
procedure SetChildOrder(Component: TComponent; Order: Integer); override;
property InfoQueryMode: Boolean read FInfoQueryMode;
procedure SetCachedUpdates(Value: Boolean);
procedure SetupCallBack(Value: Boolean);
function ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): DBIResult;
function GetUpdateRecordSet: TUpdateRecordTypes;
procedure SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes);
procedure SetUpdateObject(Value: TDataSetUpdateObject);
function ForceUpdateCallback: Boolean;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function ActiveBuffer: PChar;
procedure Append;
procedure AppendRecord(const Values: array of const);
procedure Cancel;
procedure CheckBrowseMode;
procedure ClearFields;
procedure Close;
function ControlsDisabled: Boolean;
procedure CursorPosChanged;
procedure Delete;
procedure DisableControls;
procedure Edit;
procedure EnableControls;
procedure FetchAll;
function FieldByName(const FieldName: string): TField;
function FindField(const FieldName: string): TField;
function FindFirst: Boolean;
function FindLast: Boolean;
function FindNext: Boolean;
function FindPrior: Boolean;
procedure First;
procedure FreeBookmark(Bookmark: TBookmark);
function GetBookmark: TBookmark;
function GetCurrentRecord(Buffer: PChar): Boolean;
procedure GetFieldList(List: TList; const FieldNames: string);
procedure GetFieldNames(List: TStrings);
procedure GotoBookmark(Bookmark: TBookmark);
procedure Insert;
procedure InsertRecord(const Values: array of const);
function IsLinkedTo(DataSource: TDataSource): Boolean;
procedure Last;
function Locate(const KeyFields: string; const KeyValues: Variant;
Options: TLocateOptions): Boolean;
function Lookup(const KeyFields: string; const KeyValues: Variant;
const ResultFields: string): Variant;
function MoveBy(Distance: Integer): Integer;
procedure Next;
procedure Open;
procedure Post;
procedure Prior;
procedure Refresh;
procedure Resync(Mode: TResyncMode);
procedure SetFields(const Values: array of const);
procedure SetDetailFields(MasterFields: TList);
procedure UpdateCursorPos;
procedure UpdateRecord;
procedure ApplyUpdates;
procedure CommitUpdates;
procedure CancelUpdates;
procedure RevertRecord;
function UpdateStatus: TUpdateStatus;
property Bof: Boolean read FBOF;
property Bookmark: TBookmarkStr read GetBookmarkStr write SetBookmarkStr;
property CanModify: Boolean read GetCanModify;
property DataSource: TDataSource read GetDataSource;
property DefaultFields: Boolean read FDefaultFields;
property Designer: TDataSetDesigner read FDesigner;
property Eof: Boolean read FEOF;
property ExpIndex: Boolean read FExpIndex;
property FieldCount: Integer read GetFieldCount;
property FieldDefs: TFieldDefs read FFieldDefs write SetFieldDefs;
property Fields[Index: Integer]: TField read GetField write SetField;
property FieldValues[const FieldName: string]: Variant read GetFieldValue write SetFieldValue; default;
property Found: Boolean read FFound;
property Handle: HDBICur read FHandle;
property KeySize: Word read FKeySize;
property Locale: TLocale read FLocale;
property Modified: Boolean read FModified;
property RecordCount: Longint read GetRecordCount;
property RecNo: Longint read GetRecordNumber;
property RecordSize: Word read FRecordSize;
property State: TDataSetState read FState;
property UpdateObject: TDataSetUpdateObject read FUpdateObject write SetUpdateObject;
property UpdateRecordTypes: TUpdateRecordTypes read GetUpdateRecordSet write SetUpdateRecordSet;
property UpdatesPending: Boolean read GetUpdatesPending;
published
property Active: Boolean read GetActive write SetActive default False;
property AutoCalcFields: Boolean read FAutoCalcFields write FAutoCalcFields default True;
property CachedUpdates: Boolean read FCachedUpdates write SetCachedUpdates default False;
property Filter: string read FFilterText write SetFilterText;
property Filtered: Boolean read FFiltered write SetFiltered default False;
property FilterOptions: TFilterOptions read FFilterOptions write SetFilterOptions default [];
property BeforeOpen: TDataSetNotifyEvent read FBeforeOpen write FBeforeOpen;
property AfterOpen: TDataSetNotifyEvent read FAfterOpen write FAfterOpen;
property BeforeClose: TDataSetNotifyEvent read FBeforeClose write FBeforeClose;
property AfterClose: TDataSetNotifyEvent read FAfterClose write FAfterClose;
property BeforeInsert: TDataSetNotifyEvent read FBeforeInsert write FBeforeInsert;
property AfterInsert: TDataSetNotifyEvent read FAfterInsert write FAfterInsert;
property BeforeEdit: TDataSetNotifyEvent read FBeforeEdit write FBeforeEdit;
property AfterEdit: TDataSetNotifyEvent read FAfterEdit write FAfterEdit;
property BeforePost: TDataSetNotifyEvent read FBeforePost write FBeforePost;
property AfterPost: TDataSetNotifyEvent read FAfterPost write FAfterPost;
property BeforeCancel: TDataSetNotifyEvent read FBeforeCancel write FBeforeCancel;
property AfterCancel: TDataSetNotifyEvent read FAfterCancel write FAfterCancel;
property BeforeDelete: TDataSetNotifyEvent read FBeforeDelete write FBeforeDelete;
property AfterDelete: TDataSetNotifyEvent read FAfterDelete write FAfterDelete;
property OnNewRecord: TDataSetNotifyEvent read FOnNewRecord write FOnNewRecord;
property OnCalcFields: TDataSetNotifyEvent read FOnCalcFields write FOnCalcFields;
property OnFilterRecord: TFilterRecordEvent read FOnFilterRecord write SetOnFilterRecord;
property OnServerYield: TOnServerYieldEvent read FOnServerYield write FOnServerYield;
property OnUpdateError: TUpdateErrorEvent read FOnUpdateError write SetOnUpdateError;
property OnUpdateRecord: TUpdateRecordEvent read FOnUpdateRecord write FOnUpdateRecord;
property OnEditError: TDataSetErrorEvent read FOnEditError write FOnEditError;
property OnPostError: TDataSetErrorEvent read FOnPostError write FOnPostError;
property OnDeleteError: TDataSetErrorEvent read FOnDeleteError write FOnDeleteError;
end;
{ TDBDataSet }
TUpdateMode = (upWhereAll, upWhereChanged, upWhereKeyOnly);
TDBFlags = set of 0..15;
TDBDataSet = class(TDataSet)
private
FDBFlags: TDBFlags;
FUpdateMode: TUpdateMode;
FReserved: Byte;
FDatabase: TDatabase;
FDatabaseName: string;
FSessionName: string;
procedure CheckDBSessionName;
function GetDBFlag(Flag: Integer): Boolean;
function GetDBHandle: HDBIDB;
function GetDBLocale: TLocale;
function GetDBSession: TSession;
procedure SetDatabaseName(const Value: string);
procedure SetSessionName(const Value: string);
procedure SetUpdateMode(const Value: TUpdateMode);
protected
procedure CloseCursor; override;
procedure Disconnect; virtual;
procedure OpenCursor; override;
procedure SetDBFlag(Flag: Integer; Value: Boolean); virtual;
property DBFlags: TDBFlags read FDBFlags;
property UpdateMode: TUpdateMode read FUpdateMode write SetUpdateMode default upWhereAll;
public
function CheckOpen(Status: DBIResult): Boolean;
property Database: TDatabase read FDatabase;
property DBHandle: HDBIDB read GetDBHandle;
property DBLocale: TLocale read GetDBLocale;
property DBSession: TSession read GetDBSession;
published
property DatabaseName: string read FDatabaseName write SetDatabaseName;
property SessionName: string read FSessionName write SetSessionName;
end;
{ TDataSource }
TDataChangeEvent = procedure(Sender: TObject; Field: TField) of object;
TDataSource = class(TComponent)
private
FDataSet: TDataSet;
FDataLinks: TList;
FEnabled: Boolean;
FAutoEdit: Boolean;
FState: TDataSetState;
FReserved: Byte;
FOnStateChange: TNotifyEvent;
FOnDataChange: TDataChangeEvent;
FOnUpdateData: TNotifyEvent;
procedure AddDataLink(DataLink: TDataLink);
procedure DataEvent(Event: TDataEvent; Info: Longint);
procedure NotifyDataLinks(Event: TDataEvent; Info: Longint);
procedure RemoveDataLink(DataLink: TDataLink);
procedure SetDataSet(ADataSet: TDataSet);
procedure SetEnabled(Value: Boolean);
procedure SetState(Value: TDataSetState);
procedure UpdateState;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Edit;
function IsLinkedTo(DataSet: TDataSet): Boolean;
property State: TDataSetState read FState;
published
property AutoEdit: Boolean read FAutoEdit write FAutoEdit default True;
property DataSet: TDataSet read FDataSet write SetDataSet;
property Enabled: Boolean read FEnabled write SetEnabled default True;
property OnStateChange: TNotifyEvent read FOnStateChange write FOnStateChange;
property OnDataChange: TDataChangeEvent read FOnDataChange write FOnDataChange;
property OnUpdateData: TNotifyEvent read FOnUpdateData write FOnUpdateData;
end;
{ TField }
TFieldKind = (fkData, fkCalculated, fkLookup);
TFieldNotifyEvent = procedure(Sender: TField) of object;
TFieldGetTextEvent = procedure(Sender: TField; var Text: string;
DisplayText: Boolean) of object;
TFieldSetTextEvent = procedure(Sender: TField; const Text: string) of object;
TFieldRef = ^TField;
TField = class(TComponent)
private
FDataSet: TDataSet;
FFieldName: string;
FDataType: TFieldType;
FReadOnly: Boolean;
FFieldKind: TFieldKind;
FAlignment: TAlignment;
FVisible: Boolean;
FRequired: Boolean;
FValidating: Boolean;
FSize: Word;
FDataSize: Word;
FFieldNo: Integer;
FOffset: Word;
FDisplayWidth: Integer;
FDisplayLabel: string;
FEditMask: string;
FValueBuffer: Pointer;
FLookupDataSet: TDataSet;
FKeyFields: string;
FLookupKeyFields: string;
FLookupResultField: string;
FAttributeSet: string;
FOnChange: TFieldNotifyEvent;
FOnValidate: TFieldNotifyEvent;
FOnGetText: TFieldGetTextEvent;
FOnSetText: TFieldSetTextEvent;
procedure Bind(Binding: Boolean);
procedure CalcLookupValue;
function GetBDECalcField: Boolean;
function GetCalculated: Boolean;
function GetDisplayLabel: string;
function GetDisplayName: string;
function GetDisplayText: string;
function GetDisplayWidth: Integer;
function GetEditText: string;
function GetIndex: Integer;
function GetIsIndexField: Boolean;
function GetIsNull: Boolean;
function GetLookup: Boolean;
function GetNewValue: Variant;
function GetOldValue: Variant;
function GetUpdateValue(ValueState: TDataSetState): Variant;
function IsDisplayLabelStored: Boolean;
function IsDisplayWidthStored: Boolean;
procedure ReadAttributeSet(Reader: TReader);
procedure SetAlignment(Value: TAlignment);
procedure SetCalculated(Value: Boolean);
procedure SetDataSet(ADataSet: TDataSet);
procedure SetDisplayLabel(Value: string);
procedure SetDisplayWidth(Value: Integer);
procedure SetEditMask(const Value: string);
procedure SetEditText(const Value: string);
procedure SetFieldKind(Value: TFieldKind);
procedure SetFieldName(const Value: string);
procedure SetIndex(Value: Integer);
procedure SetLookup(Value: Boolean);
procedure SetLookupDataSet(Value: TDataSet);
procedure SetLookupKeyFields(const Value: string);
procedure SetLookupResultField(const Value: string);
procedure SetKeyFields(const Value: string);
procedure SetNewValue(const Value: Variant);
procedure SetVisible(Value: Boolean);
procedure UpdateDataSize;
procedure WriteAttributeSet(Writer: TWriter);
protected
procedure AccessError(const TypeName: string);
procedure CheckInactive;
procedure Change; virtual;
procedure DataChanged;
procedure DefineProperties(Filer: TFiler); override;
procedure FreeBuffers; virtual;
function GetAsBoolean: Boolean; virtual;
function GetAsCurrency: Currency; virtual;
function GetAsDateTime: TDateTime; virtual;
function GetAsFloat: Double; virtual;
function GetAsInteger: Longint; virtual;
function GetAsString: string; virtual;
function GetAsVariant: Variant; virtual;
function GetCanModify: Boolean;
function GetDefaultWidth: Integer; virtual;
function GetParentComponent: TComponent; override;
procedure GetText(var Text: string; DisplayText: Boolean); virtual;
function HasParent: Boolean; override;
procedure Notification(AComponent: TComponent;
Operation: TOperation); override;
procedure PropertyChanged(LayoutAffected: Boolean);
procedure ReadState(Reader: TReader); override;
procedure SetAsBoolean(Value: Boolean); virtual;
procedure SetAsCurrency(Value: Currency); virtual;
procedure SetAsDateTime(Value: TDateTime); virtual;
procedure SetAsFloat(Value: Double); virtual;
procedure SetAsInteger(Value: Longint); virtual;
procedure SetAsString(const Value: string); virtual;
procedure SetAsVariant(const Value: Variant); virtual;
procedure SetDataType(Value: TFieldType);
procedure SetSize(Value: Word);
procedure SetParentComponent(AParent: TComponent); override;
procedure SetText(const Value: string); virtual;
procedure SetVarValue(const Value: Variant); virtual;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure AssignValue(const Value: TVarRec);
procedure Clear; virtual;
procedure FocusControl;
function GetData(Buffer: Pointer): Boolean;
function IsValidChar(InputChar: Char): Boolean; virtual;
procedure SetData(Buffer: Pointer);
procedure SetFieldType(Value: TFieldType); virtual;
property AsBoolean: Boolean read GetAsBoolean write SetAsBoolean;
property AsCurrency: Currency read GetAsCurrency write SetAsCurrency;
property AsDateTime: TDateTime read GetAsDateTime write SetAsDateTime;
property AsFloat: Double read GetAsFloat write SetAsFloat;
property AsInteger: Longint read GetAsInteger write SetAsInteger;
property AsString: string read GetAsString write SetAsString;
property AsVariant: Variant read GetAsVariant write SetAsVariant;
property AttributeSet: string read FAttributeSet write FAttributeSet;
property BDECalcField: Boolean read GetBDECalcField;
property CanModify: Boolean read GetCanModify;
property DataSet: TDataSet read FDataSet write SetDataSet stored False;
property DataSize: Word read FDataSize;
property DataType: TFieldType read FDataType;
property DisplayName: string read GetDisplayName;
property DisplayText: string read GetDisplayText;
property EditMask: string read FEditMask write SetEditMask;
property EditMaskPtr: string read FEditMask;
property FieldKind: TFieldKind read FFieldKind write SetFieldKind;
property FieldNo: Integer read FFieldNo;
property IsIndexField: Boolean read GetIsIndexField;
property IsNull: Boolean read GetIsNull;
property Size: Word read FSize write SetSize;
property Text: string read GetEditText write SetEditText;
property Value: Variant read GetAsVariant write SetAsVariant;
property NewValue: Variant read GetNewValue write SetNewValue;
property OldValue: Variant read GetOldValue;
published
property Alignment: TAlignment read FAlignment write SetAlignment default taLeftJustify;
property Calculated: Boolean read GetCalculated write SetCalculated default False;
property DisplayLabel: string read GetDisplayLabel write SetDisplayLabel
stored IsDisplayLabelStored;
property DisplayWidth: Integer read GetDisplayWidth write SetDisplayWidth
stored IsDisplayWidthStored;
property FieldName: string read FFieldName write SetFieldName;
property Index: Integer read GetIndex write SetIndex stored False;
property Lookup: Boolean read GetLookup write SetLookup default False;
property LookupDataSet: TDataSet read FLookupDataSet write SetLookupDataSet;
property LookupKeyFields: string read FLookupKeyFields write SetLookupKeyFields;
property LookupResultField: string read FLookupResultField write SetLookupResultField;
property KeyFields: string read FKeyFields write SetKeyFields;
property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
property Required: Boolean read FRequired write FRequired default False;
property Visible: Boolean read FVisible write SetVisible default True;
property OnChange: TFieldNotifyEvent read FOnChange write FOnChange;
property OnGetText: TFieldGetTextEvent read FOnGetText write FOnGetText;
property OnSetText: TFieldSetTextEvent read FOnSetText write FOnSetText;
property OnValidate: TFieldNotifyEvent read FOnValidate write FOnValidate;
end;
{ TDataLink }
TDataLink = class(TPersistent)
private
FDataSource: TDataSource;
FNext: TDataLink;
FBufferCount: Integer;
FFirstRecord: Integer;
FReadOnly: Boolean;
FActive: Boolean;
FEditing: Boolean;
FUpdating: Boolean;
FDataSourceFixed: Boolean;
procedure DataEvent(Event: TDataEvent; Info: Longint);
function GetActiveRecord: Integer;
function GetDataSet: TDataSet;
function GetRecordCount: Integer;
procedure SetActive(Value: Boolean);
procedure SetActiveRecord(Value: Integer);
procedure SetBufferCount(Value: Integer);
procedure SetDataSource(ADataSource: TDataSource);
procedure SetEditing(Value: Boolean);
procedure SetReadOnly(Value: Boolean);
procedure UpdateRange;
procedure UpdateState;
protected
procedure ActiveChanged; virtual;
procedure CheckBrowseMode; virtual;
procedure DataSetChanged; virtual;
procedure DataSetScrolled(Distance: Integer); virtual;
procedure FocusControl(Field: TFieldRef); virtual;
procedure EditingChanged; virtual;
procedure LayoutChanged; virtual;
procedure RecordChanged(Field: TField); virtual;
procedure UpdateData; virtual;
public
constructor Create;
destructor Destroy; override;
function Edit: Boolean;
procedure UpdateRecord;
property Active: Boolean read FActive;
property ActiveRecord: Integer read GetActiveRecord write SetActiveRecord;
property BufferCount: Integer read FBufferCount write SetBufferCount;
property DataSet: TDataSet read GetDataSet;
property DataSource: TDataSource read FDataSource write SetDataSource;
property DataSourceFixed: Boolean read FDataSourceFixed write FDataSourceFixed;
property Editing: Boolean read FEditing;
property ReadOnly: Boolean read FReadOnly write SetReadOnly;
property RecordCount: Integer read GetRecordCount;
end;
const
dsEditModes = [dsEdit, dsInsert, dsSetKey];
function AnsiToNative(Locale: TLocale; const AnsiStr: string;
NativeStr: PChar; MaxLen: Integer): PChar;
procedure NativeToAnsi(Locale: TLocale; NativeStr: PChar;
var AnsiStr: string);
procedure AnsiToNativeBuf(Locale: TLocale; Source, Dest: PChar; Len: Integer);
procedure NativeToAnsiBuf(Locale: TLocale; Source, Dest: PChar; Len: Integer);
function NativeCompareStr(Locale: TLocale; const S1, S2: string; Len: Integer): Integer;
function NativeCompareStrBuf(Locale: TLocale; const S1, S2: PChar; Len: Integer): Integer;
function NativeCompareText(Locale: TLocale; const S1, S2: string; Len: Integer): Integer;
function NativeCompareTextBuf(Locale: TLocale; const S1, S2: PChar; Len: Integer): Integer;
function ExtractFieldName(const Fields: string; var Pos: Integer): string;
procedure RegisterFields(const FieldClasses: array of TFieldClass);
procedure DatabaseError(const Message: string);
procedure DBError(Ident: Word);
procedure DBErrorFmt(Ident: Word; const Args: array of const);
procedure DbiError(ErrorCode: DBIResult);
procedure Check(Status: DBIResult);
procedure RegisterBDEInitProc(const InitProc: TBDEInitProc);
var
Session: TSession;
Sessions: TSessionList;
const
RegisterFieldsProc: procedure(const FieldClassess: array of TFieldClass) = nil;
implementation
uses Controls, Forms, DBConsts, DBPWDlg, DBLogDlg, DBTables;
var
FCSect: TRTLCriticalSection;
StartTime: LongInt = 0;
TimerID: Word;
AcquiredTimer: Boolean = False;
BDEInitProcs: TList;
procedure RegisterBDEInitProc(const InitProc: TBDEInitProc);
begin
if not Assigned(BDEInitProcs) then
BDEInitProcs := TList.Create;
BDEInitProcs.Add(@InitProc);
end;
procedure FreeTimer;
begin
if AcquiredTimer then
begin
KillTimer(0, TimerID);
AcquiredTimer := False;
StartTime := 0;
Screen.Cursor := crDefault;
end;
end;
{ Timer callback function }
procedure TimerCallBack(hWnd: HWND; Message: Word; TimerID: Word;
SysTime: LongInt); stdcall;
begin
FreeTimer;
end;
{ BdeCallbacks }
function BdeCallBack(CallType: CBType; Data: Longint;
CBInfo: Pointer): CBRType; stdcall;
begin
if (Data <> 0) then
Result := TBDECallback(Data).Invoke(CallType, CBInfo) else
Result := cbrUSEDEF;
end;
function DLLDetachCallBack(CallType: CBType; Data: Longint;
CBInfo: Pointer): CBRType; stdcall;
begin
DB.Session.FDLLDetach := True;
Sessions.CloseAll;
end;
constructor TBDECallback.Create(AOwner: TObject; Handle: hDBICur; CBType: CBType;
CBBuf: Pointer; CBBufSize: Integer; CallbackEvent: TBDECallbackEvent;
Chain: Boolean);
begin
FOwner := AOwner;
FHandle := Handle;
FCBType := CBType;
FCallbackEvent := CallbackEvent;
DbiGetCallBack(Handle, FCBType, @FOldCBData, @FOldCBBufLen, @FOldCBBuf, FOldCBFunc);
if not Assigned(FOldCBFunc) or Chain then
begin
Check(DbiRegisterCallback(FHandle, FCBType, Longint(Self), CBBufSize,
CBBuf, BdeCallBack));
FInstalled := True;
end;
end;
destructor TBDECallback.Destroy;
begin
if FInstalled then
begin
if Assigned(FOldCBFunc) then
try
DbiRegisterCallback(FHandle, FCBType, FOldCBData, FOldCBBufLen,
FOldCBBuf, FOldCBFunc);
except
end
else
DbiRegisterCallback(FHandle, FCBType, 0, 0, nil, nil);
end;
end;
function TBDECallback.Invoke(CallType: CBType; CBInfo: Pointer): CBRType;
begin
if CallType = FCBType then
Result := FCallbackEvent(CBInfo) else
Result := cbrUSEDEF;
if Assigned(FOldCBFunc)
then Result := FOldCBFunc(CallType, FOldCBData, CBInfo);
end;
{ Utility routines }
procedure DisposeMem(var Buffer; Size: Word);
begin
if Pointer(Buffer) <> nil then
begin
FreeMem(Pointer(Buffer), Size);
Pointer(Buffer) := nil;
end;
end;
function BuffersEqual(Buf1, Buf2: Pointer; Size: Cardinal): Boolean; assembler;
asm
PUSH EDI
PUSH ESI
MOV ESI,Buf1
MOV EDI,Buf2
XOR EAX,EAX
JECXZ @@1
CLD
REPE CMPSB
JNE @@1
INC EAX
@@1: POP ESI
POP EDI
end;
function StrToOem(const AnsiStr: string): string;
begin
SetLength(Result, Length(AnsiStr));
if Length(Result) > 0 then
CharToOem(PChar(AnsiStr), PChar(Result));
end;
function AnsiToNative(Locale: TLocale; const AnsiStr: string;
NativeStr: PChar; MaxLen: Integer): PChar;
var
Len: Integer;
begin
Len := Length(AnsiStr);
if Len > MaxLen then Len := MaxLen;
if Len > 0 then AnsiToNativeBuf(Locale, Pointer(AnsiStr), NativeStr, Len);
NativeStr[Len] := #0;
if StrByteType(NativeStr, Len-1) = mbLeadByte then NativeStr[Len-1] := #0;
Result := NativeStr;
end;
procedure NativeToAnsi(Locale: TLocale; NativeStr: PChar;
var AnsiStr: string);
var
Len: Integer;
begin
Len := StrLen(NativeStr);
SetString(AnsiStr, nil, Len);
if Len > 0 then NativeToAnsiBuf(Locale, NativeStr, Pointer(AnsiStr), Len);
end;
procedure AnsiToNativeBuf(Locale: TLocale; Source, Dest: PChar; Len: Integer);
var
DataLoss: LongBool;
begin
if Len > 0 then
if Locale <> nil then
DbiAnsiToNative(Locale, Dest, Source, Len, DataLoss) else
CharToOemBuff(Source, Dest, Len);
end;
procedure NativeToAnsiBuf(Locale: TLocale; Source, Dest: PChar; Len: Integer);
var
DataLoss: LongBool;
begin
if Len > 0 then
if Locale <> nil then
DbiNativeToAnsi(Locale, Dest, Source, Len, DataLoss) else
OemToCharBuff(Source, Dest, Len)
end;
function NativeCompareStr(Locale: TLocale; const S1, S2: string; Len: Integer): Integer;
begin
Result := NativeCompareStrBuf(Locale, PChar(S1), PChar(S2), Len);
end;
function NativeCompareStrBuf(Locale: TLocale; const S1, S2: PChar; Len: Integer): Integer;
begin
if Len > 0 then
Result := OsLdStrnCmp(Locale, S1, S2, Len) else
Result := OsLdStrCmp(Locale, S1, S2);
end;
function NativeCompareText(Locale: TLocale; const S1, S2: string; Len: Integer): Integer;
begin
Result := NativeCompareTextBuf(Locale, PChar(S1), PChar(S2), Len);
end;
function NativeCompareTextBuf(Locale: TLocale; const S1, S2: PChar; Len: Integer): Integer;
begin
if Len > 0 then
Result := OsLdStrnCmpi(Locale, S1, S2, Len) else
Result := OsLdStrCmpi(Locale, S1, S2);
end;
function ExtractFieldName(const Fields: string; var Pos: Integer): string;
var
I: Integer;
begin
I := Pos;
while (I <= Length(Fields)) and (Fields[I] <> ';') do Inc(I);
Result := Copy(Fields, Pos, I - Pos);
if (I <= Length(Fields)) and (Fields[I] = ';') then Inc(I);
Pos := I;
end;
function IsDirectory(const DatabaseName: string): Boolean;
begin
Result := (DatabaseName = '') or (Pos(':', DatabaseName) <> 0) or
(Pos('\', DatabaseName) <> 0);
end;
procedure MergeStrings(Dest, Source: TStrings);
var
DI, I, P: Integer;
S: string;
begin
for I := 0 to Source.Count - 1 do
begin
S := Source[I];
P := Pos('=', S);
if P > 1 then
begin
DI := Dest.IndexOfName(Copy(S, 1, P - 1));
if DI > -1 then Dest[DI] := S;
end;
end;
end;
procedure CheckTypeSize(DataType: TFieldType; Size: Word);
begin
case DataType of
ftString: if (Size >= 1) and (Size <= dsMaxStringSize) then Exit;
ftBCD: if Size <= 32 then Exit;
ftBytes, ftVarBytes: if Size > 0 then Exit;
ftBlob, ftMemo, ftGraphic, ftFmtMemo, ftParadoxOle, ftDBaseOle,
ftTypedBinary: Exit;
else
if Size = 0 then Exit;
end;
DBError(SInvalidFieldSize);
end;
function FieldTypeToVarType(DataType: TFieldType): Integer;
const
TypeMap: array[TFieldType] of Word = (
varEmpty, varString, varInteger, varInteger, varInteger, varBoolean,
varDouble, varCurrency, varCurrency, varDate, varDate, varDate,
varEmpty, varEmpty, varInteger, varEmpty, varEmpty, varEmpty,
varEmpty, varEmpty, varEmpty, varEmpty);
begin
Result := TypeMap[DataType];
end;
procedure RegisterFields(const FieldClasses: array of TFieldClass);
begin
if Assigned(RegisterFieldsProc) then
RegisterFieldsProc(FieldClasses) else
DBError(SInvalidFieldRegistration);
end;
function GetIntProp(const Handle: Pointer; PropName: Integer): Integer;
var
Length: Word;
Value: Integer;
begin
Value := 0;
Check(DbiGetProp(HDBIObj(Handle), propName, @Value, SizeOf(Value), Length));
Result := Value;
end;
{ Error and exception handling routines }
procedure DatabaseError(const Message: string);
begin
raise EDatabaseError.Create(Message);
end;
procedure DBError(Ident: Word);
begin
DatabaseError(LoadStr(Ident));
end;
procedure DBErrorFmt(Ident: Word; const Args: array of const);
begin
DatabaseError(FmtLoadStr(Ident, Args));
end;
procedure DbiError(ErrorCode: DBIResult);
begin
if AcquiredTimer then FreeTimer;
raise EDBEngineError.Create(ErrorCode);
end;
procedure Check(Status: DBIResult);
begin
if Status <> 0 then DbiError(Status);
end;
{ TDBError }
constructor TDBError.Create(Owner: EDBEngineError; ErrorCode: DBIResult;
NativeError: Longint; Message: PChar);
begin
Owner.FErrors.Add(Self);
FErrorCode := ErrorCode;
FNativeError := NativeError;
FMessage := Message;
end;
function TDBError.GetCategory: Byte;
begin
Result := Hi(FErrorCode);
end;
function TDBError.GetSubCode: Byte;
begin
Result := Lo(FErrorCode);
end;
{ EDBEngineError }
function TrimMessage(Msg: PChar): PChar;
var
Blank: Boolean;
Source, Dest: PChar;
begin
Source := Msg;
Dest := Msg;
Blank := False;
while Source^ <> #0 do
begin
if Source^ <= ' ' then Blank := True else
begin
if Blank then
begin
Dest^ := ' ';
Inc(Dest);
Blank := False;
end;
Dest^ := Source^;
Inc(Dest);
end;
Inc(Source);
end;
if (Dest > Msg) and ((Dest - 1)^ = '.') then Dec(Dest);
Dest^ := #0;
Result := Msg;
end;
constructor EDBEngineError.Create(ErrorCode: DBIResult);
var
ErrorIndex: Integer;
NativeError: Longint;
Msg, LastMsg: DBIMSG;
begin
inherited Create('');
FErrors := TList.Create;
ErrorIndex := 1;
if not Session.Active then
begin
Message := FmtLoadStr(SInitError, [ErrorCode]);
TDBError.Create(Self, ErrorCode, 0, PChar(Message));
end
else begin
DbiGetErrorString(ErrorCode, Msg);
TDBError.Create(Self, ErrorCode, 0, Msg);
TrimMessage(Msg);
if Msg[0] = #0 then Message := FmtLoadStr(SBDEError, [ErrorCode])
else Message := Msg;
while True do
begin
StrCopy(LastMsg, Msg);
ErrorCode := DbiGetErrorEntry(ErrorIndex, NativeError, Msg);
if (ErrorCode = DBIERR_NONE) or
(ErrorCode = DBIERR_NOTINITIALIZED) then Break;
TDBError.Create(Self, ErrorCode, NativeError, Msg);
TrimMessage(Msg);
if (Msg[0] <> #0) and (StrComp(Msg, LastMsg) <> 0) then
Message := Format('%s. %s', [Message, Msg]);
Inc(ErrorIndex);
end;
end;
end;
destructor EDBEngineError.Destroy;
var
I: Integer;
begin
if FErrors <> nil then
begin
for I := FErrors.Count - 1 downto 0 do TDBError(FErrors[I]).Free;
FErrors.Free;
end;
inherited Destroy;
end;
function EDBEngineError.GetError(Index: Integer): TDBError;
begin
Result := FErrors[Index];
end;
function EDBEngineError.GetErrorCount: Integer;
begin
Result := FErrors.Count;
end;
{ TSessionList }
constructor TSessionList.Create;
begin
inherited Create;
FSessions := TList.Create;
InitializeCriticalSection(FCSect);
end;
destructor TSessionList.Destroy;
begin
CloseAll;
DeleteCriticalSection(FCSect);
inherited Destroy;
end;
procedure TSessionList.AddSession(ASession: TSession);
begin
if FSessions.Count = 0 then ASession.FDefault := True;
FSessions.Add(ASession);
end;
procedure TSessionList.CloseAll;
var
I: Integer;
begin
for I := FSessions.Count-1 downto 0 do
TSession(FSessions[I]).Free;
end;
function TSessionList.GetCount: Integer;
begin
Result := FSessions.Count;
end;
function TSessionList.GetCurrentSession: TSession;
var
Handle: HDBISes;
I: Integer;
begin
Check(DbiGetCurrSession(Handle));
for I := 0 to FSessions.Count - 1 do
if TSession(FSessions[I]).Handle = Handle then
begin
Result := TSession(FSessions[I]);
Exit;
end;
Result := nil;
end;
function TSessionList.GetSession(Index: Integer): TSession;
begin
Result := TSession(FSessions[Index]);
end;
function TSessionList.GetSessionByName(const SessionName: string): TSession;
begin
if SessionName = '' then
Result := DB.Session
else
Result := FindSession(SessionName);
if Result = nil then
DBErrorFmt(SInvalidSessionName, [SessionName]);
end;
function TSessionList.FindSession(const SessionName: string): TSession;
var
I: Integer;
begin
if SessionName = '' then
Result := DB.Session
else
begin
for I := 0 to FSessions.Count - 1 do
begin
Result := FSessions[I];
if AnsiCompareText(Result.SessionName, SessionName) = 0 then Exit;
end;
Result := nil;
end;
end;
procedure TSessionList.GetSessionNames(List: TStrings);
var
I: Integer;
begin
List.BeginUpdate;
try
List.Clear;
for I := 0 to FSessions.Count - 1 do
with TSession(FSessions[I]) do
List.Add(SessionName);
finally
List.EndUpdate;
end;
end;
function TSessionList.OpenSession(const SessionName: string): TSession;
begin
Result := FindSession(SessionName);
if Result = nil then
begin
Result := TSession.Create(nil);
Result.SessionName := SessionName;
end;
Result.SetActive(True);
end;
procedure TSessionList.SetCurrentSession(Value: TSession);
begin
Check(DbiSetCurrSession(Value.FHandle))
end;
{ TSession }
constructor TSession.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Exclude(FComponentStyle, csInheritable);
FDatabases := TList.Create;
FCallbacks := TList.Create;
FKeepConnections := True;
Sessions.AddSession(Self);
FHandle := nil;
end;
destructor TSession.Destroy;
procedure ResetDBSessionRefs;
var
I: Integer;
begin
for I := 0 to FDatabases.Count - 1 do
with TDatabase(FDatabases[I]) do
if FSession = Self then
begin
FSession := DB.Session;
FSession.AddDatabase(FDatabases[I]);
end;
end;
begin
SetActive(False);
Sessions.FSessions.Remove(Self);
if not FDefault and Assigned(FDatabases) then ResetDBSessionRefs;
FDatabases.Free;
FCallbacks.Free;
inherited Destroy;
end;
procedure TSession.AddAlias(const Name, Driver: string; List: TStrings);
begin
InternalAddAlias(Name, Driver, List, ConfigMode, True);
end;
procedure TSession.AddDatabase(Value: TDatabase);
begin
FDatabases.Add(Value);
DBNotification(dbAdd, Value);
end;
procedure TSession.AddConfigRecord(const Path, Node: string; List: TStrings);
var
ParamList: TParamList;
begin
ParamList := TParamList.Create(List);
try
with ParamList do
Check(DbiCfgAddRecord(nil, PChar(Format(Path, [Node])), FieldCount,
PFLDDesc(FieldDescs), Buffer));
finally
ParamList.Free;
end;
end;
procedure TSession.AddStandardAlias(const Name, Path, DefaultDriver: string);
var
AliasParams: TStringList;
begin
AliasParams := TStringList.Create;
try
AliasParams.Add(Format('%s=%s', [szCFGDBPATH, Path]));
AliasParams.Add(Format('%s=%s', [szCFGDBDEFAULTDRIVER, DefaultDriver]));
AddAlias(Name, szCFGDBSTANDARD, AliasParams);
finally
AliasParams.Free;
end;
end;
procedure TSession.AddPassword(const Password: string);
var
Buffer: array[0..255] of Char;
begin
LockSession;
try
if Password <> '' then
Check(DbiAddPassword(AnsiToNative(Locale, Password, Buffer,
SizeOf(Buffer) - 1)));
finally
UnlockSession;
end;
end;
procedure TSession.CallBDEInitProcs;
var
I: Integer;
begin
if Assigned(BDEInitProcs) then
for I := 0 to BDEInitProcs.Count - 1 do
TBDEInitProc(BDEInitProcs[I])(Self);
end;
procedure TSession.CheckInactive;
begin
if Active then
DBError(SSessionActive);
end;
procedure TSession.CheckConfigMode(CfgMode: TConfigMode);
begin
if CfgMode = cmAll then CfgMode := cmPersistent;
ConfigMode := CfgMode;
end;
procedure TSession.Close;
begin
SetActive(False);
end;
// bug fix from delphi team (mark e.)
procedure TSession.CloseDatabase(Database: TDatabase);
begin
with Database do
begin
if FRefCount <> 0 then Dec(FRefCount);
if (FRefCount = 0) and not KeepConnection then
if not Temporary then Close else
if not (csDestroying in ComponentState) then Free;
end;
end;
function TSession.DBLoginCallback(CBInfo: Pointer): CBRType;
var
Database: TDatabase;
UserName, Password: string;
AliasParams: TStringList;
begin
Result := cbrYES;
with PCBDBLogin(CBInfo)^ do
try
if hDB = nil then
begin
if not FBDEOwnsLoginCbDb then
begin
hDb := OpenDatabase(szDbName).Handle;
if not Assigned(hDb) then
Result := cbrAbort
else
bCallbackToClose := True;
end else
begin
AliasParams := TStringList.Create;
try
GetAliasParams(szDbName, AliasParams);
UserName := AliasParams.Values[szUSERNAME];
finally
AliasParams.Free;
end;
Password := '';
if LoginDialogEx(szDbName, UserName, Password, True) then
begin
AnsiToNative(Locale, Password, szPassword, SizeOf(szPassword) - 1);
bCallbackToClose := False;
end
else
Result :=cbrAbort;
end
end else
begin
Database := FindDatabase(szDbName);
if Assigned(Database) and (hDB = Database.Handle) then
CloseDatabase(Database);
end;
except
Result := cbrAbort;
end;
end;
procedure TSession.DBNotification(DBEvent: TDatabaseEvent; const Param);
begin
if Assigned(FOnDBNotify) then FOnDBNotify(DBEvent, Param);
end;
procedure TSession.DeleteAlias(const Name: string);
begin
InternalDeleteAlias(Name, ConfigMode, True);
end;
procedure TSession.DeleteConfigPath(const Path, Node: string);
var
CfgPath: string;
begin
CfgPath := Format(Path, [Node]);
if DbiCfgPosition(nil, PChar(CfgPath)) = 0 then
Check(DbiCfgDropRecord(nil, PChar(CfgPath)));
end;
procedure TSession.DropConnections;
var
I: Integer;
begin
for I := FDatabases.Count - 1 downto 0 do
with TDatabase(FDatabases[I]) do
if Temporary and (FRefCount = 0) then Free;
end;
function TSession.FindDatabase(const DatabaseName: string): TDatabase;
var
I: Integer;
begin
for I := 0 to FDatabases.Count - 1 do
begin
Result := FDatabases[I];
if ((Result.DatabaseName <> '') or Result.Temporary) and
(AnsiCompareText(Result.DatabaseName, DatabaseName) = 0) then Exit;
end;
Result := nil;
end;
function TSession.GetActive: Boolean;
begin
Result := FHandle <> nil;
end;
function TSession.GetAliasDriverName(const AliasName: string): string;
var
Desc: DBDesc;
begin
LockSession;
try
if DbiGetDatabaseDesc(PChar(StrToOem(AliasName)), @Desc) <> 0 then
DBErrorFmt(SInvalidAliasName, [AliasName]);
finally
UnlockSession;
end;
if Desc.szDBType[sizeOf(Desc.szDBType) - 1] <> #0 then
Desc.szDBType[sizeOf(Desc.szDBType) - 1] := #0;
OemToChar(Desc.szDBType, Desc.szDBType);
Result := Desc.szDBType;
end;
procedure TSession.GetAliasNames(List: TStrings);
var
Cursor: HDBICur;
Desc: DBDesc;
begin
List.BeginUpdate;
try
List.Clear;
LockSession;
try
Check(DbiOpenDatabaseList(Cursor));
finally
UnlockSession;
end;
try
while DbiGetNextRecord(Cursor, dbiNOLOCK, @Desc, nil) = 0 do
begin
OemToChar(Desc.szName, Desc.szName);
List.Add(Desc.szName);
end;
finally
DbiCloseCursor(Cursor);
end;
finally
List.EndUpdate;
end;
end;
procedure TSession.GetAliasParams(const AliasName: string; List: TStrings);
var
SAlias: DBIName;
Desc: DBDesc;
begin
List.BeginUpdate;
try
List.Clear;
StrPLCopy(SAlias, AliasName, SizeOf(SAlias) - 1);
CharToOEM(SAlias, SAlias);
LockSession;
try
Check(DbiGetDatabaseDesc(SAlias, @Desc));
finally
UnlockSession;
end;
if Desc.szDBType[sizeOf(Desc.szDBType) - 1] <> #0 then
Desc.szDBType[sizeOf(Desc.szDBType) - 1] := #0;
if StrIComp(Desc.szDbType, szCFGDBSTANDARD) = 0 then
begin
GetConfigParams('\DATABASES\%s\DB INFO', SAlias, List);
List.Values[szCFGDBTYPE] := '';
end
else
GetConfigParams('\DATABASES\%s\DB OPEN', SAlias, List);
finally
List.EndUpdate;
end;
end;
procedure TSession.GetConfigParams(const Path, Section: string; List: TStrings);
var
Cursor: HDBICur;
ConfigDesc: CFGDesc;
begin
LockSession;
try
Check(DbiOpenCfgInfoList(nil, dbiREADONLY, cfgPERSISTENT, PChar(Format(Path,
[Section])), Cursor));
finally
UnlockSession;
end;
try
while DbiGetNextRecord(Cursor, dbiNOLOCK, @ConfigDesc, nil) = 0 do
with ConfigDesc do
begin
OemToChar(szValue, szValue);
List.Add(Format('%s=%s', [szNodeName, szValue]));
end;
finally
DbiCloseCursor(Cursor);
end;
end;
function TSession.GetDatabase(Index: Integer): TDatabase;
begin
Result := FDatabases[Index];
end;
function TSession.GetDatabaseCount: Integer;
begin
Result := FDatabases.Count;
end;
procedure TSession.GetDatabaseNames(List: TStrings);
var
I: Integer;
Names: TStringList;
begin
Names := TStringList.Create;
try
Names.Sorted := True;
GetAliasNames(Names);
for I := 0 to FDatabases.Count - 1 do
with TDatabase(FDatabases[I]) do
if not IsDirectory(DatabaseName) then Names.Add(DatabaseName);
List.Assign(Names);
finally
Names.Free;
end;
end;
procedure TSession.GetDriverNames(List: TStrings);
var
Cursor: HDBICur;
Name: array[0..255] of Char;
begin
List.BeginUpdate;
try
List.Clear;
List.Add(szCFGDBSTANDARD);
LockSession;
try
Check(DbiOpenDriverList(Cursor));
finally
UnlockSession;
end;
try
while DbiGetNextRecord(Cursor, dbiNOLOCK, @Name, nil) = 0 do
if (StrIComp(Name, szPARADOX) <> 0) and
(StrIComp(Name, szDBASE) <> 0) then
begin
OemToChar(Name, Name);
List.Add(Name);
end;
finally
DbiCloseCursor(Cursor);
end;
finally
List.EndUpdate;
end;
end;
procedure TSession.GetDriverParams(const DriverName: string;
List: TStrings);
begin
List.BeginUpdate;
try
List.Clear;
if AnsiCompareText(DriverName, szCFGDBSTANDARD) = 0 then
begin
List.Add(Format('%s=', [szCFGDBPATH]));
List.Add(Format('%s=%s', [szCFGDBDEFAULTDRIVER, szPARADOX]));
List.Add(Format('%s=%s', [szCFGDBENABLEBCD, szCFGFALSE]));
end
else
GetConfigParams('\DRIVERS\%s\DB OPEN', StrToOem(DriverName), List);
finally
List.EndUpdate;
end;
end;
function TSession.GetHandle: HDBISes;
begin
if FHandle <> nil then
Check(DbiSetCurrSession(FHandle))
else
SetActive(True);
Result := FHandle;
end;
function TSession.GetNetFileDir: string;
var
Length: Word;
Buffer: array[0..255] of Char;
begin
if Active and not (csWriting in ComponentState) then
begin
LockSession;
try
Check(DbiGetProp(HDBIOBJ(FHandle), sesNETFILE, @Buffer, SizeOf(Buffer),
Length));
finally
UnLockSession;
end;
NativeToAnsi(nil, Buffer, Result);
end else
Result := FNetFileDir;
Result := AnsiUpperCase(Result);
end;
function TSession.GetPrivateDir: string;
var
SessionInfo: SESInfo;
begin
if Active and not (csWriting in ComponentState) then
begin
LockSession;
try
Check(DbiGetSesInfo(SessionInfo));
finally
UnlockSession;
end;
NativeToAnsi(nil, SessionInfo.szPrivDir, Result);
end else
Result := FPrivateDir;
Result := AnsiUpperCase(Result);
end;
function TSession.GetPassword: Boolean;
begin
if Assigned(FOnPassword) then
begin
Result := False;
FOnPassword(Self, Result)
end else
Result := PasswordDialog(Self);
end;
procedure TSession.GetTableNames(const DatabaseName, Pattern: string;
Extensions, SystemTables: Boolean; List: TStrings);
var
Database: TDatabase;
Cursor: HDBICur;
WildCard: PChar;
Name: string;
SPattern: array[0..127] of Char;
Desc: TBLBaseDesc;
begin
List.BeginUpdate;
try
List.Clear;
Database := OpenDatabase(DatabaseName);
try
WildCard := nil;
if Pattern <> '' then
WildCard := AnsiToNative(Database.Locale, Pattern, SPattern,
SizeOf(SPattern) - 1);
Check(DbiOpenTableList(Database.Handle, False, SystemTables,
WildCard, Cursor));
try
while DbiGetNextRecord(Cursor, dbiNOLOCK, @Desc, nil) = 0 do
with Desc do
begin
if Extensions and (szExt[0] <> #0) then
StrCat(StrCat(szName, '.'), szExt);
NativeToAnsi(Database.Locale, szName, Name);
List.Add(Name);
end;
finally
DbiCloseCursor(Cursor);
end;
finally
CloseDatabase(Database);
end;
finally
List.EndUpdate;
end;
end;
procedure TSession.GetStoredProcNames(const DatabaseName: string; List: TStrings);
var
Database: TDatabase;
Cursor: HDBICur;
Name: string;
Desc: SPDesc;
begin
List.BeginUpdate;
try
List.Clear;
Database := OpenDatabase(DatabaseName);
try
Check(DbiOpenSPList(Database.Handle, False, True, nil, Cursor));
try
while DbiGetNextRecord(Cursor, dbiNOLOCK, @Desc, nil) = 0 do
with Desc do
begin
NativeToAnsi(Database.Locale, szName, Name);
List.Add(Name);
end;
finally
DbiCloseCursor(Cursor);
end;
finally
CloseDatabase(Database);
end;
finally
List.EndUpdate;
end;
end;
procedure TSession.InitializeBDE;
const
StartFlags: LongInt = 0;
var
Status: DBIResult;
Env: DbiEnv;
ClientHandle: hDBIObj;
SetCursor: Boolean;
begin
SetCursor := GetCurrentThreadID = MainThreadID;
if SetCursor then
Screen.Cursor := crHourGlass;
try
FillChar(Env, SizeOf(Env), 0);
StrPLCopy(Env.szLang, LoadStr(SIDAPILangID), SizeOf(Env.szLang) - 1);
Status := DbiInit(@Env);
if (Status <> DBIERR_NONE) and (Status <> DBIERR_MULTIPLEINIT) then
DBErrorFmt(SInitError, [Status]);
Check(DbiGetCurrSession(FHandle));
if DbiGetObjFromName(objCLIENT, nil, ClientHandle) = 0 then
DbiSetProp(ClientHandle, clSQLRESTRICT, StartFlags);
if IsLibrary then
DbiRegisterCallback(nil, cbDETACHNOTIFY, 0, 0, nil, DLLDetachCallBack);
finally
if SetCursor then
Screen.Cursor := crDefault;
end;
end;
procedure TSession.InternalAddAlias(const Name, Driver: string; List: TStrings;
CfgMode: TConfigMode; RestoreMode: Boolean);
var
Standard: Boolean;
DefaultDriver: string;
OemName: string;
CfgModeSave: TConfigMode;
procedure ValidateAliasName;
const
ValidChars = ['0'..'9','A'..'Z','a'..'z','_',#127..#255];
var
I, Len: Integer;
ValidName: Boolean;
begin
Len := Length(Name);
ValidName := Len > 0;
if ValidName then
begin
OemName := StrToOem(Name);
for I := 1 to Len do
begin
ValidName := OemName[I] in ValidChars;
if not ValidName then break;
end;
end;
if not ValidName then
DBErrorFmt(SInvalidAliasName, [Name]);
end;
procedure AddDBInfo;
var
DBInfo: TStringList;
EnableBCD: string;
begin
DBInfo := TStringList.Create;
try
if Standard then
DBInfo.Add(Format('%s=%s', [szCFGDBTYPE, szCFGDBSTANDARD])) else
DBInfo.Add(Format('%s=%s', [szCFGDBTYPE, Driver]));
DBInfo.Add(Format('%s=%s', [szCFGDBPATH, List.Values[szCFGDBPATH]]));
if Standard then
begin
if DefaultDriver = '' then
DefaultDriver := List.Values[szCFGDBDEFAULTDRIVER];
DBInfo.Add(Format('%s=%s', [szCFGDBDEFAULTDRIVER, DefaultDriver]));
EnableBCD := List.Values[szCFGDBENABLEBCD];
if EnableBCD = '' then EnableBCD := szCFGFALSE;
DBInfo.Add(Format('%s=%s', [szCFGDBENABLEBCD, EnableBCD]));
end;
AddConfigRecord('\DATABASES\%s\DB INFO', OemName, DBInfo);
finally
DBInfo.Free;
end;
end;
procedure AddDBOpen;
var
DBOpen: TStringList;
begin
try
DBOpen := TStringList.Create;
try
GetDriverParams(Driver, DBOpen);
MergeStrings(DBOpen, List);
AddConfigRecord('\DATABASES\%s\DB OPEN', OemName, DBOpen);
finally
DBOpen.Free;
end;
except
DbiCfgDropRecord(nil, PChar(Format('\DATABASES\%s\DB INFO', [Name])));
raise;
end;
end;
begin
LockSession;
try
DefaultDriver := '';
Standard := (Driver = '') or (AnsiCompareText(Driver, szCFGDBSTANDARD) = 0);
if not Standard and ((AnsiCompareText(Driver, szPARADOX) = 0) or
(AnsiCompareText(Driver, szDBASE) = 0) or
(AnsiCompareText(Driver, szASCII) = 0)) then
begin
Standard := True;
DefaultDriver := Driver;
end;
ValidateAliasName;
CfgModeSave := ConfigMode;
try
CheckConfigMode(CfgMode);
AddDBInfo;
if not Standard then AddDBOpen;
finally
if RestoreMode then ConfigMode := CfgModeSave;
end;
finally
UnlockSession;
end;
DBNotification(dbAddAlias, Pointer(Name));
end;
procedure TSession.InternalDeleteAlias(const Name: string;
CfgMode: TConfigMode; RestoreMode: Boolean);
var
CfgModeSave: TConfigMode;
begin
DBNotification(dbDeleteAlias, Pointer(Name));
LockSession;
try
CfgModeSave := ConfigMode;
try
CheckConfigMode(CfgMode);
DeleteConfigPath('\DATABASES\%s', StrToOem(Name));
finally
if RestoreMode then ConfigMode := cfgModeSave;
end;
finally
UnlockSession;
end;
end;
function TSession.IsAlias(const Name: string): Boolean;
begin
MakeCurrent;
Result := DbiCfgPosition(nil, PChar(Format('\DATABASES\%s', [Name]))) = 0;
end;
procedure TSession.Loaded;
begin
inherited Loaded;
try
if FStreamedActive then SetActive(True);
except
if csDesigning in ComponentState then
Application.HandleException(Self)
else
raise;
end;
end;
procedure TSession.LockSession;
begin
if FLockCount = 0 then
begin
EnterCriticalSection(FCSect);
Inc(FLockCount);
MakeCurrent;
end
else
Inc(FLockCount);
end;
procedure TSession.UnLockSession;
begin
Dec(FLockCount);
if FLockCount = 0 then
LeaveCriticalSection(FCSect);
end;
procedure TSession.MakeCurrent;
begin
if FHandle <> nil then
Check(DbiSetCurrSession(FHandle))
else
SetActive(True);
end;
procedure TSession.ModifyAlias(Name: string; List: TStrings);
var
DriverName: string;
OemName: string;
CfgModeSave: TConfigMode;
begin
LockSession;
try
CfgModeSave := ConfigMode;
try
CheckConfigMode(ConfigMode);
DriverName := GetAliasDriverName(Name);
OemName := StrToOem(Name);
ModifyConfigParams('\DATABASES\%s\DB INFO', OemName, List);
if DriverName <> szCFGDBSTANDARD then
ModifyConfigParams('\DATABASES\%s\DB OPEN', OemName, List);
finally
ConfigMode := CfgModeSave;
end;
finally
UnLockSession;
end;
end;
procedure TSession.ModifyConfigParams(const Path, Node: string; List: TStrings);
var
I, J, C: Integer;
Params: TStrings;
begin
Params := TStringList.Create;
try
GetConfigParams(Path, Node, Params);
C := 0;
for I := 0 to Params.Count - 1 do
begin
J := List.IndexOfName(Params.Names[I]);
if J >= 0 then
begin
Params[I] := List[J];
Inc(C);
end;
end;
if C > 0 then SetConfigParams(Path, Node, Params);
finally
Params.Free;
end;
end;
procedure TSession.Open;
begin
SetActive(True);
end;
function TSession.OpenDatabase(const DatabaseName: string): TDatabase;
var
TempDatabase: TDatabase;
begin
MakeCurrent;
TempDatabase := nil;
try
Result := FindDatabase(DatabaseName);
if Result = nil then
begin
TempDatabase := TDatabase.Create(Self);
TempDatabase.DatabaseName := DatabaseName;
TempDatabase.KeepConnection := FKeepConnections;
TempDatabase.Temporary := True;
Result := TempDatabase;
end;
Result.Open;
Inc(Result.FRefCount);
except
TempDatabase.Free;
raise;
end;
end;
procedure TSession.RegisterCallbacks(Value: Boolean);
procedure UnloadSMClient;
begin
try
FreeMem(FSMBuffer, smTraceBufSize);
FSMClient.Free;
FreeLibrary(FClientLib);
except
end;
end;
function LoadSMClient: Boolean;
var
FM: THandle;
ClientLibPath: PChar;
ClientName: string;
FOldCBFunc: pfDBICallBack;
begin
Result := False;
try
if DbiGetCallBack(nil, cbTrace, nil, nil, nil,
FOldCBFunc) = DBIERR_NONE then Exit;
FM := OpenFileMapping(FILE_MAP_READ, False, 'SMClientLib');
if FM <> 0 then
try
ClientLibPath := MapViewOfFile(FM, FILE_MAP_READ, 0, 0, MAX_PATH);
FClientLib := LoadLibrary(ClientLibPath);
if FClientLib > 32 then
try
FSMRegProc := GetProcAddress(FClientLib, 'RegisterClient');
if not Assigned(FSMRegProc) then SysUtils.Abort;
ClientName := Application.Title;
if ClientName = '' then ClientName := LoadStr(DB_SUntitled);
if not FDefault then
ClientName := Format('%s.%s', [ClientName, SessionName]);
FSMClient := FSMRegProc(Integer(FHandle), PChar(ClientName),
FSMWriteProc, Self, @TSession.SMClientSignal);
if not Assigned(FSMClient) then SysUtils.Abort;
GetMem(FSMBuffer, smTraceBufSize);
Result := True;
except
UnloadSMClient;
FClientLib := 0;
end;
finally
CloseHandle(FM);
end;
except
end;
end;
var
I: Integer;
begin
if Value then
begin
FCallbacks.Add(TBDECallback.Create(Self, nil, cbSERVERCALL,
@FCBSCType, SizeOf(CBSCType), ServerCallBack, False));
FCallbacks.Add(TBDECallback.Create(Self, nil, cbDBLOGIN,
@FCBDBLogin, SizeOf(TCBDBLogin), DBLoginCallBack, False));
if LoadSMClient then
FCallbacks.Add(TBDECallback.Create(Self, nil, cbTRACE,
FSMBuffer, smTraceBufSize, SqlTraceCallBack, False));
end else
begin
for I := FCallbacks.Count - 1 downto 0 do
TBDECallback(FCallbacks[I]).Free;
FCallbacks.Clear;
if (FClientLib <> 0) then UnloadSMClient;
end;
end;
procedure TSession.RemoveDatabase(Value: TDatabase);
begin
FDatabases.Remove(Value);
DBNotification(dbRemove, Value);
end;
procedure TSession.RemoveAllPasswords;
begin
LockSession;
try
DbiDropPassword(nil);
finally
UnlockSession;
end;
end;
procedure TSession.RemovePassword(const Password: string);
var
Buffer: array[0..255] of Char;
begin
LockSession;
try
if Password <> '' then
DbiDropPassword(AnsiToNative(Locale, Password, Buffer,
SizeOf(Buffer) - 1));
finally
UnlockSession;
end;
end;
procedure TSession.SaveConfigFile;
var
CfgModeSave: TConfigMode;
begin
CfgModeSave := ConfigMode;
try
ConfigMode := cmPersistent;
Check(DbiCfgSave(nil, nil, False));
finally
ConfigMode := CfgModeSave;
end;
end;
function TSession.ServerCallBack(CBInfo: Pointer): CBRType;
const
MinWait = 500;
begin
Result := cbrUSEDEF;
if (FCBSCType = cbscSQL) and (GetCurrentThreadID = MainThreadID) then
begin
if StartTime = 0 then
begin
TimerID := SetTimer(0, 0, 1000, @TimerCallBack);
AcquiredTimer := TimerID <> 0;
StartTime := GetTickCount;
end
else if AcquiredTimer and (GetTickCount - StartTime > MinWait) then
Screen.Cursor := crSQLWait;
end;
end;
procedure TSession.SetActive(Value: Boolean);
begin
if csReading in ComponentState then
FStreamedActive := Value
else
if Active <> Value then
StartSession(Value);
end;
function TSession.GetConfigMode: TConfigMode;
begin
LockSession;
try
Result := TConfigMode(GetIntProp(FHandle, sesCfgMode));
finally
UnlockSession;
end;
end;
procedure TSession.SetConfigMode(Value: TConfigMode);
begin
LockSession;
try
Check(DbiSetProp(hDBIObj(FHandle), sesCFGMODE, Longint(Value)));
finally
UnlockSession;
end;
end;
procedure TSession.SetConfigParams(const Path, Node: string; List: TStrings);
var
ParamList: TParamList;
begin
ParamList := TParamList.Create(List);
try
with ParamList do
Check(DbiCfgModifyRecord(nil, PChar(Format(Path, [Node])), FieldCount,
PFLDDesc(FieldDescs), Buffer));
finally
ParamList.Free;
end;
end;
procedure TSession.SetNetFileDir(const Value: string);
var
Buffer: array[0..255] of Char;
begin
if Active then
begin
LockSession;
try
Check(DbiSetProp(HDBIOBJ(Handle), sesNETFILE, Longint(AnsiToNative(nil,
Value, Buffer, SizeOf(Buffer) - 1))));
finally
UnLockSession;
end;
end;
FNetFileDir := Value;
end;
procedure TSession.SetPrivateDir(const Value: string);
var
Buffer: array[0..255] of Char;
begin
if Active then
begin
LockSession;
try
Check(DbiSetPrivateDir(AnsiToNative(nil, Value, Buffer,
SizeOf(Buffer) - 1)));
finally
UnlockSession;
end;
end;
FPrivateDir := Value;
end;
procedure TSession.SetSessionName(const Value: string);
var
Ses: TSession;
begin
CheckInActive;
if Value <> '' then
begin
Ses := Sessions.FindSession(Value);
if not ((Ses = nil) or (Ses = Self)) then
DBErrorFmt(SDuplicateSessionName, [Value]);
end;
FSessionName := Value
end;
procedure TSession.SetTraceFlags(Value: TTraceFlags);
var
I: Integer;
begin
FTraceFlags := Value;
for I := FDatabases.Count - 1 downto 0 do
with TDatabase(FDatabases[I]) do
TraceFlags := FTraceFlags;
end;
procedure TSession.SMClientSignal(Sender: TObject; Data: Integer);
begin
SetTraceFlags(TTraceFlags(Word(Data)));
end;
function TSession.SqlTraceCallBack(CBInfo: Pointer): CBRType;
var
Len: Integer;
Data: PChar;
begin
Result := cbrUSEDEF;
try
Data := @PTraceDesc(CBInfo).pszTrace;
Len := StrLen(Data);
if not FSMWriteProc(FSMClient, Data, Len) then SysUtils.abort;
except
SetTraceFlags([]);
end;
end;
procedure TSession.StartSession(Value: Boolean);
var
I: Integer;
begin
EnterCriticalSection(FCSect);
try
if Value then
begin
if Assigned(FOnStartup) then FOnStartup(Self);
if FSessionName = '' then DBError(SSessionNameMissing);
if (DB.Session <> Self) then DB.Session.Active := True;
if FDefault then
InitializeBDE
else
Check(DbiStartSession(nil, FHandle, nil));
try
RegisterCallbacks(True);
if FNetFileDir <> '' then SetNetFileDir(FNetFileDir);
if FPrivateDir <> '' then SetPrivateDir(FPrivateDir);
ConfigMode := cmAll;
CallBDEInitProcs;
except
StartSession(False);
raise;
end;
end else
begin
DbiSetCurrSession(FHandle);
for I := FDatabases.Count - 1 downto 0 do
with TDatabase(FDatabases[I]) do
if Temporary then Free else Close;
RegisterCallbacks(False);
if FDefault then
begin
if not FDLLDetach then
begin
if IsLibrary then
begin
DbiRegisterCallback(nil, cbDETACHNOTIFY, 0, 0, @DLLDetachCallBack, nil);
DbiDLLExit;
end;
DbiExit;
end;
end
else
begin
Check(DbiCloseSession(FHandle));
DbiSetCurrSession(Session.FHandle);
end;
FHandle := nil;
end;
finally
LeaveCriticalSection(FCSect);
end;
end;
{ TParamList }
constructor TParamList.Create(Params: TStrings);
var
I, P, FieldNo: Integer;
BufPtr: PChar;
S: string;
begin
for I := 0 to Params.Count - 1 do
begin
S := Params[I];
P := Pos('=', S);
if P <> 0 then
begin
Inc(FFieldCount);
Inc(FBufSize, Length(S) - P + 1);
end;
end;
if FFieldCount > 0 then
begin
FFieldDescs := AllocMem(FFieldCount * SizeOf(FLDDesc));
FBuffer := AllocMem(FBufSize);
FieldNo := 0;
BufPtr := FBuffer;
for I := 0 to Params.Count - 1 do
begin
S := Params[I];
P := Pos('=', S);
if P <> 0 then
with FFieldDescs^[FieldNo] do
begin
Inc(FieldNo);
iFldNum := FieldNo;
StrPLCopy(szName, Copy(S, 1, P - 1), SizeOf(szName) - 1);
iFldType := fldZSTRING;
iOffset := BufPtr - FBuffer;
iLen := Length(S) - P + 1;
StrCopy(BufPtr, PChar(Copy(S, P + 1, 255)));
CharToOem(BufPtr, BufPtr);
Inc(BufPtr, iLen);
end;
end;
end;
end;
destructor TParamList.Destroy;
begin
DisposeMem(FFieldDescs, FFieldCount * SizeOf(FLDDesc));
DisposeMem(FBuffer, FBufSize);
end;
{ TDatabase }
constructor TDatabase.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Exclude(FComponentStyle, csInheritable);
if AOwner is TSession then
FSession := TSession(AOwner) else
FSession := DB.Session;
SessionName := FSession.SessionName;
FSession.AddDatabase(Self);
FDataSets := TList.Create;
FParams := TStringList.Create;
TStringList(FParams).OnChanging := ParamsChanging;
FLoginPrompt := True;
FKeepConnection := True;
FLocale := FSession.Locale;
FTransIsolation := tiReadCommitted;
end;
// bug fix from delphi team (mark e.)
destructor TDatabase.Destroy;
begin
Destroying;
Close;
FParams.Free;
FDataSets.Free;
if FSession <> nil then
FSession.RemoveDatabase(Self);
inherited Destroy;
end;
procedure TDatabase.ApplyUpdates(const DataSets: array of TDBDataSet);
var
I: Integer;
DS: TDBDataSet;
begin
StartTransaction;
try
for I := 0 to High(DataSets) do
begin
DS := DataSets[I];
if DS.Database <> Self then
DatabaseError(FmtLoadStr(SUpdateWrongDB, [DS.Name, Name]));
DataSets[I].ApplyUpdates;
end;
Commit;
except
Rollback;
raise;
end;
for I := 0 to High(DataSets) do
DataSets[I].CommitUpdates;
end;
procedure TDatabase.CheckActive;
begin
if FHandle = nil then DBError(SDatabaseClosed);
end;
procedure TDatabase.CheckInactive;
begin
if FHandle <> nil then DBError(SDatabaseOpen);
end;
procedure TDatabase.CheckDatabaseName;
begin
if (FDatabaseName = '') and not Temporary then
DBError(SDatabaseNameMissing);
end;
procedure TDatabase.CheckSessionName(Required: Boolean);
var
NewSession: TSession;
begin
if Required then
NewSession := Sessions.List[FSessionName]
else
NewSession := Sessions.FindSession(FSessionName);
if (NewSession <> nil) and (NewSession <> FSession) then
begin
FSession.RemoveDatabase(Self);
FSession := NewSession;
FSession.AddDatabase(Self);
end;
if Required then FSession.Active := True;
end;
procedure TDatabase.Close;
begin
if FHandle <> nil then
begin
Session.DBNotification(dbClose, Self);
CloseDataSets;
if FLocaleLoaded then OsLdUnloadObj(FLocale);
FLocaleLoaded := False;
FLocale := DB.Session.Locale;
if not FAcquiredHandle then
DbiCloseDatabase(FHandle)
else
FAcquiredHandle := False;
FSQLBased := False;
FHandle := nil;
FRefCount := 0;
if FSessionAlias then
begin
FSession.InternalDeleteAlias(FDatabaseName, cmSession, True);
FSessionAlias := False;
end;
end;
end;
procedure TDatabase.CloseDataSets;
begin
while FDataSets.Count <> 0 do TDBDataSet(FDataSets.Last).Disconnect;
end;
procedure TDatabase.Commit;
begin
CheckActive;
EndTransaction(xendCOMMIT);
end;
procedure TDatabase.EndTransaction(TransEnd: EXEnd);
begin
if FTransHandle = nil then DBErrorFmt(SEndTransError, [FDatabaseName]);
Check(DbiEndTran(FHandle, FTransHandle, TransEnd));
FTransHandle := nil;
end;
function TDatabase.GetAliasName: string;
begin
if FAliased then Result := FDatabaseType else Result := '';
end;
function TDatabase.GetConnected: Boolean;
begin
Result := FHandle <> nil;
end;
function TDatabase.GetDataSet(Index: Integer): TDBDataSet;
begin
Result := FDataSets[Index];
end;
function TDatabase.GetDataSetCount: Integer;
begin
Result := FDataSets.Count;
end;
function TDatabase.GetDirectory: string;
var
SDirectory: DBIPATH;
begin
Check(DbiGetDirectory(Handle, False, SDirectory));
SetLength(Result, StrLen(SDirectory));
OemToChar(SDirectory, PChar(Result));
end;
function TDatabase.GetDriverName: string;
begin
if FAliased then Result := '' else Result := FDatabaseType;
end;
function TDatabase.GetIsSQLBased: Boolean;
var
Length: Word;
Buffer: array[0..63] of Char;
begin
Result := False;
if FHandle <> nil then
begin
Check(DbiGetProp(HDBIOBJ(FHandle), dbDATABASETYPE, @Buffer,
SizeOf(Buffer), Length));
Result := StrIComp(Buffer, szCFGDBSTANDARD) <> 0;
end;
end;
function TDatabase.GetTraceFlags: TTraceFlags;
begin
if Connected and IsSQLBased then
Result := TTraceFlags(Word(GetIntProp(FHandle, dbTraceMode)))
else
Result := [];
end;
function TDatabase.GetInTransaction: Boolean;
var
X: XInfo;
begin
Result := (Handle <> nil) and (DbiGetTranInfo(Handle, nil, @X) = DBIERR_NONE)
and (X.exState = xsActive);
end;
procedure TDatabase.Loaded;
begin
inherited Loaded;
try
if FStreamedConnected then Open
else CheckSessionName(False);
except
if csDesigning in ComponentState then
Application.HandleException(Self)
else
raise;
end;
end;
procedure TDatabase.LoadLocale;
var
LName: DBIName;
DBLocale: TLocale;
begin
if IsSQLBased and (DbiGetLdNameFromDB(FHandle, nil, LName) = 0) and
(OsLdLoadBySymbName(LName, DBLocale) = 0) then
begin
FLocale := DBLocale;
FLocaleLoaded := True;
end;
end;
procedure TDatabase.Login(LoginParams: TStrings);
var
UserName, Password: string;
begin
if Assigned(FOnLogin) then FOnLogin(Self, LoginParams) else
begin
UserName := LoginParams.Values[szUSERNAME];
if not LoginDialogEx(DatabaseName, UserName, Password, False) then
DBErrorFmt(SLoginError, [DatabaseName]);
LoginParams.Values[szUSERNAME] := UserName;
LoginParams.Values[szPASSWORD] := Password;
end;
end;
procedure TDatabase.CheckDatabaseAlias(var Password: string);
var
Desc: DBDesc;
Aliased: Boolean;
DBName: string;
DriverType: string;
AliasParams: TStringList;
LoginParams: TStringList;
function NeedsDBAlias: Boolean;
var
I: Integer;
PName: String;
begin
Result := not Aliased or ((FDatabaseType <> '') and
(FDatabaseName <> FDatabaseType));
for I := 0 to FParams.Count - 1 do
begin
if AliasParams.IndexOf(FParams[I]) > -1 then continue;
PName := FParams.Names[I];
if (AnsiCompareText(PName, szPASSWORD) = 0) then continue;
if AliasParams.IndexOfName(PName) > -1 then
begin
Result := True;
AliasParams.Values[PName] := FParams.Values[PName];
end;
end;
end;
begin
Password := '';
FSessionAlias := False;
AliasParams := TStringList.Create;
try
begin
if FDatabaseType <> '' then
begin
DBName := FDatabaseType;
Aliased := FAliased;
end else
begin
DBName := FDatabaseName;
Aliased := True;
end;
if Aliased then
begin
if DbiGetDatabaseDesc(PChar(StrToOem(DBName)), @Desc) <> 0 then Exit;
if Desc.szDBType[sizeOf(Desc.szDBType) - 1] <> #0 then
Desc.szDBType[sizeOf(Desc.szDBType) - 1] := #0;
OemToChar(Desc.szDbType, Desc.szDbType);
DriverType := Desc.szDbType;
FSession.GetAliasParams(DBName, AliasParams);
end else
begin
FSession.GetDriverParams(DBName, AliasParams);
DriverType := FDatabaseType;
end;
if (DriverType <> szCFGDBSTANDARD) then
begin
if LoginPrompt then
begin
LoginParams := TStringList.Create;
try
if FParams.Values[szUSERNAME] = '' then
FParams.Values[szUSERNAME] := AliasParams.Values[szUSERNAME];
LoginParams.Values[szUSERNAME] := FParams.Values[szUSERNAME];
Login(LoginParams);
Password := LoginParams.Values[szPASSWORD];
FParams.Values[szUSERNAME] := LoginParams.Values[szUSERNAME];
finally
LoginParams.Free;
end;
end else
Password := FParams.Values[szPASSWORD];
end;
end;
if NeedsDBAlias then
begin
FSession.InternalAddAlias(FDatabaseName, DriverType, AliasParams,
cmSession, False);
FSessionAlias := True;
end;
finally
AliasParams.Free;
end;
end;
procedure TDatabase.Open;
var
DBName: string;
DBPassword: string;
CfgModeSave: TConfigMode;
begin
if FHandle = nil then
begin
CheckDatabaseName;
CheckSessionName(True);
FSession.LockSession;
try
CfgModeSave := FSession.ConfigMode;
try
CheckDatabaseAlias(DBPassword);
try
if (FDatabaseType = '') and IsDirectory(FDatabaseName) then
DBName := '' else
DBName := StrToOem(FDatabaseName);
Check(DbiOpenDatabase(Pointer(DBName), nil, dbiREADWRITE, dbiOPENSHARED,
Pointer(StrToOem(DBPassword)), 0, nil, nil, FHandle));
if DBName = '' then SetDirectory(FDatabaseName);
DbiSetProp(HDBIOBJ(FHandle), dbUSESCHEMAFILE, Longint(True));
DbiSetProp(HDBIOBJ(FHandle), dbPARAMFMTQMARK, Longint(True));
FSQLBased := GetIsSQLBased;
LoadLocale;
TraceFlags := FSession.FTraceFlags;
Session.DBNotification(dbOpen, Self);
except
if FSessionAlias then
FSession.InternalDeleteAlias(FDatabaseName, cmSession, False);
raise;
end;
finally
FSession.ConfigMode := CfgModeSave;
end;
finally
FSession.UnlockSession;
end;
end;
end;
procedure TDatabase.ParamsChanging(Sender: TObject);
begin
CheckInactive;
end;
procedure TDatabase.Rollback;
begin
CheckActive;
EndTransaction(xendABORT);
end;
procedure TDatabase.SetAliasName(const Value: string);
begin
SetDatabaseType(Value, True);
end;
procedure TDatabase.SetConnected(Value: Boolean);
begin
if csReading in ComponentState then
FStreamedConnected := Value
else
if Value then Open else Close;
end;
procedure TDatabase.SetDatabaseName(const Value: string);
begin
if FDatabaseName <> Value then
begin
CheckInactive;
ValidateName(Value);
FDatabaseName := Value;
end;
end;
procedure TDatabase.SetDatabaseType(const Value: string;
Aliased: Boolean);
begin
CheckInactive;
FDatabaseType := Value;
FAliased := Aliased;
end;
procedure TDatabase.SetDirectory(const Value: string);
begin
Check(DbiSetDirectory(Handle, Pointer(StrToOem(Value))));
end;
procedure TDatabase.SetDriverName(const Value: string);
begin
SetDatabaseType(Value, False);
end;
procedure TDatabase.SetHandle(Value: HDBIDB);
var
DBSession: HDBISes;
begin
if Connected then Close;
if Value <> nil then
begin
Check(DbiGetObjFromObj(HDBIObj(Value), objSESSION, HDBIObj(DBSession)));
CheckDatabaseName;
CheckSessionName(True);
if FSession.Handle <> DBSession then DBError(SDatabaseHandleSet);
FHandle := Value;
FSQLBased := GetIsSQLBased;
LoadLocale;
Session.DBNotification(dbOpen, Self);
FAcquiredHandle := True;
end;
end;
procedure TDatabase.SetKeepConnection(Value: Boolean);
begin
if FKeepConnection <> Value then
begin
FKeepConnection := Value;
if not Value and (FRefCount = 0) then Close;
end;
end;
procedure TDatabase.SetParams(Value: TStrings);
begin
CheckInactive;
FParams.Assign(Value);
end;
procedure TDatabase.SetSessionName(const Value: string);
begin
CheckInactive;
if FSessionName <> Value then
begin
FSessionName := Value;
CheckSessionName(False);
end;
end;
procedure TDatabase.SetTraceFlags(Value: TTraceFlags);
begin
if Connected and IsSQLBased then
DbiSetProp(hDBIObj(FHandle), dbTraceMode, Integer(Word(Value)));
end;
procedure TDatabase.StartTransaction;
begin
CheckActive;
if FTransHandle <> nil then DBErrorFmt(SBeginTransError, [FDatabaseName]);
if not IsSQLBased and (TransIsolation <> tiDirtyRead) then
DBError(SLocalTransDirty);
Check(DbiBeginTran(FHandle, EXILType(FTransIsolation), FTransHandle));
end;
procedure TDatabase.ValidateName(const Name: string);
var
Database: TDatabase;
begin
if Name <> '' then
begin
Database := FSession.FindDatabase(Name);
if (Database <> nil) and (Database <> Self) then
begin
if not Database.Temporary or (Database.FRefCount <> 0) then
DBErrorFmt(SDuplicateDatabaseName, [Name]);
Database.Free;
end;
end;
end;
procedure TDatabase.FlushSchemaCache(const TableName: string);
begin
if Connected and IsSQLBased then
Check(DbiSchemaCacheFlush(FHandle, PChar(TableName)));
end;
{ TDataSetDesigner }
constructor TDataSetDesigner.Create(DataSet: TDataSet);
begin
FDataSet := DataSet;
FDataSet.FDesigner := Self;
end;
destructor TDataSetDesigner.Destroy;
begin
FDataSet.FDesigner := nil;
end;
procedure TDataSetDesigner.BeginDesign;
begin
FSaveActive := FDataSet.Active;
if FSaveActive then
begin
FDataSet.InternalClose;
FDataSet.SetState(dsInactive);
end;
FDataSet.DisableControls;
end;
procedure TDataSetDesigner.DataEvent(Event: TDataEvent; Info: Longint);
begin
end;
procedure TDataSetDesigner.EndDesign;
begin
FDataSet.EnableControls;
if FSaveActive then
begin
try
FDataSet.InternalOpen;
FDataSet.SetState(dsBrowse);
except
FDataSet.SetState(dsInactive);
FDataSet.CloseCursor;
raise;
end;
end;
end;
{ TFieldDef }
constructor TFieldDef.Create(Owner: TFieldDefs; const Name: string;
DataType: TFieldType; Size: Word; Required: Boolean; FieldNo: Integer);
begin
CheckTypeSize(DataType, Size);
if Owner <> nil then
begin
Owner.FItems.Add(Self);
Owner.FUpdated := False;
FOwner := Owner;
end;
FName := Name;
FDataType := DataType;
FSize := Size;
FRequired := Required;
FFieldNo := FieldNo;
end;
destructor TFieldDef.Destroy;
begin
if FOwner <> nil then
begin
FOwner.FItems.Remove(Self);
FOwner.FUpdated := False;
end;
end;
function TFieldDef.CreateField(Owner: TComponent): TField;
var
FieldClass: TFieldClass;
begin
FieldClass := GetFieldClass;
if FieldClass = nil then DBErrorFmt(SUnknownFieldType, [Name]);
Result := FieldClass.Create(Owner);
try
Result.FieldName := Name;
Result.Size := FSize;
Result.Required := FRequired;
Result.SetFieldType(FDataType);
if FOwner <> nil then Result.DataSet := FOwner.FDataSet;
except
Result.Free;
raise;
end;
end;
function TFieldDef.GetFieldClass: TFieldClass;
const
FieldClasses: array[TFieldType] of TFieldClass = (
nil, { ftUnknown }
TStringField, { ftString }
TSmallintField, { ftSmallint }
TIntegerField, { ftInteger }
TWordField, { ftWord }
TBooleanField, { ftBoolean }
TFloatField, { ftFloat }
TCurrencyField, { ftCurrency }
TBCDField, { ftBCD }
TDateField, { ftDate }
TTimeField, { ftTime }
TDateTimeField, { ftDateTime }
TBytesField, { ftBytes }
TVarBytesField, { ftVarBytes }
TAutoIncField, { ftAutoInc }
TBlobField, { ftBlob }
TMemoField, { ftMemo }
TGraphicField, { ftGraphic }
TBlobField, { ftFmtMemo }
TBlobField, { ftParadoxOle }
TBlobField, { ftDBaseOle }
TBlobField); { ftTypedBinary }
begin
Result := FieldClasses[FDataType];
end;
{ TFieldDefs }
constructor TFieldDefs.Create(DataSet: TDataSet);
begin
FDataSet := DataSet;
FItems := TList.Create;
end;
destructor TFieldDefs.Destroy;
begin
if FItems <> nil then Clear;
FItems.Free;
end;
procedure TFieldDefs.Add(const Name: string; DataType: TFieldType;
Size: Word; Required: Boolean);
begin
if Name = '' then DBError(SFieldNameMissing);
if IndexOf(Name) >= 0 then DBErrorFmt(SDuplicateFieldName, [Name]);
TFieldDef.Create(Self, Name, DataType, Size, Required, FItems.Count + 1);
end;
procedure TFieldDefs.AddFieldDesc(FieldDesc: FLDDesc; Required: Boolean;
FieldNo: Word);
const
TypeMap: array[0..MAXLOGFLDTYPES - 1] of TFieldType = (
ftUnknown, ftString, ftDate, ftBlob, ftBoolean, ftSmallint,
ftInteger, ftFloat, ftBCD, ftBytes, ftTime, ftDateTime,
ftWord, ftInteger, ftUnknown, ftVarBytes, ftUnknown);
BlobTypeMap: array[fldstMEMO..fldstTYPEDBINARY] of TFieldType = (
ftMemo, ftBlob, ftFmtMemo, ftParadoxOle, ftGraphic,
ftDBaseOle, ftTypedBinary);
var
DataType: TFieldType;
Size: Word;
I: Integer;
FieldName, Name: string;
begin
with FieldDesc do
begin
NativeToAnsi(FDataSet.Locale, szName, FieldName);
I := 0;
Name := FieldName;
while IndexOf(Name) >= 0 do
begin
Inc(I);
Name := Format('%s_%d', [FieldName, I]);
end;
if iFldType < MAXLOGFLDTYPES then
DataType := TypeMap[iFldType] else
DataType := ftUnknown;
Size := 0;
case iFldType of
fldZSTRING:
Size := iUnits1;
fldINT16, fldUINT16:
if iLen <> 2 then DataType := ftUnknown;
fldINT32:
if iSubType = fldstAUTOINC then DataType := ftAutoInc;
fldFLOAT:
if iSubType = fldstMONEY then DataType := ftCurrency;
fldBCD:
Size := Abs(iUnits2);
fldBYTES, fldVARBYTES:
Size := iUnits1;
fldBLOB:
begin
Size := iUnits1;
if (iSubType >= fldstMEMO) and (iSubType <= fldstTYPEDBINARY) then
DataType := BlobTypeMap[iSubType];
end;
end;
if DataType <> ftUnknown then
with TFieldDef.Create(Self, Name, DataType, Size, Required, FieldNo) do
FBDECalcField := bCalcField;
end;
end;
procedure TFieldDefs.Assign(FieldDefs: TFieldDefs);
var
I: Integer;
begin
Clear;
for I := 0 to FieldDefs.Count - 1 do
with FieldDefs[I] do Add(Name, DataType, Size, Required);
end;
procedure TFieldDefs.Clear;
begin
while FItems.Count > 0 do TFieldDef(FItems.Last).Free;
end;
function TFieldDefs.Find(const Name: string): TFieldDef;
var
I: Integer;
begin
I := IndexOf(Name);
if I < 0 then DBErrorFmt(SFieldNotFound, [Name]);
Result := FItems[I];
end;
function TFieldDefs.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TFieldDefs.GetItem(Index: Integer): TFieldDef;
begin
Result := FItems[Index];
end;
function TFieldDefs.IndexOf(const Name: string): Integer;
begin
for Result := 0 to FItems.Count - 1 do
if AnsiCompareText(TFieldDef(FItems[Result]).Name, Name) = 0 then Exit;
Result := -1;
end;
procedure TFieldDefs.Update;
begin
FDataSet.UpdateFieldDefs;
end;
{ TFilterExpr }
type
TExprNodeKind = (enField, enConst, enOperator);
PExprNode = ^TExprNode;
TExprNode = record
FNext: PExprNode;
FKind: TExprNodeKind;
FPartial: Boolean;
FOperator: CanOp;
FData: Variant;
FLeft: PExprNode;
FRight: PExprNode;
end;
TFilterExpr = class
private
FDataSet: TDataSet;
FOptions: TFilterOptions;
FNodes: PExprNode;
FExprBuffer: PCANExpr;
FExprBufSize: Integer;
FExprNodeSize: Integer;
FExprDataSize: Integer;
function FieldFromNode(Node: PExprNode): TField;
function GetExprData(Pos, Size: Integer): PChar;
function PutCompareNode(Node: PExprNode): Integer;
function PutConstBCD(const Value: Variant; Decimals: Integer): Integer;
function PutConstDate(const Value: Variant): Integer;
function PutConstDateTime(const Value: Variant): Integer;
function PutConstFloat(const Value: Variant): Integer;
function PutConstInt(DataType: Integer; const Value: Variant): Integer;
function PutConstNode(DataType: Integer; Data: PChar;
Size: Integer): Integer;
function PutConstStr(const Value: string): Integer;
function PutConstTime(const Value: Variant): Integer;
function PutData(Data: PChar; Size: Integer): Integer;
function PutExprNode(Node: PExprNode): Integer;
function PutFieldNode(Field: TField): Integer;
function PutNode(NodeType: NodeClass; OpType: CanOp;
OpCount: Integer): Integer;
procedure SetNodeOp(Node, Index, Data: Integer);
public
constructor Create(DataSet: TDataSet; Options: TFilterOptions);
destructor Destroy; override;
function NewCompareNode(Field: TField; Operator: CanOp;
const Value: Variant): PExprNode;
function NewNode(Kind: TExprNodeKind; Operator: CanOp;
const Data: Variant; Left, Right: PExprNode): PExprNode;
function GetFilterData(Root: PExprNode): PCANExpr;
end;
constructor TFilterExpr.Create(DataSet: TDataSet; Options: TFilterOptions);
begin
FDataSet := DataSet;
FOptions := Options;
end;
destructor TFilterExpr.Destroy;
var
Node: PExprNode;
begin
FreeMem(FExprBuffer, FExprBufSize);
while FNodes <> nil do
begin
Node := FNodes;
FNodes := Node^.FNext;
Dispose(Node);
end;
end;
function TFilterExpr.FieldFromNode(Node: PExprNode): TField;
begin
Result := FDataSet.FieldByName(Node^.FData);
if Result.FieldKind <> fkData then
DBErrorFmt(SExprBadField, [Result.FieldName]);
end;
function TFilterExpr.GetExprData(Pos, Size: Integer): PChar;
begin
ReallocMem(FExprBuffer, FExprBufSize + Size);
Move(PChar(FExprBuffer)[Pos], PChar(FExprBuffer)[Pos + Size],
FExprBufSize - Pos);
Inc(FExprBufSize, Size);
Result := PChar(FExprBuffer) + Pos;
end;
function TFilterExpr.GetFilterData(Root: PExprNode): PCANExpr;
begin
FExprBufSize := SizeOf(CANExpr);
GetMem(FExprBuffer, FExprBufSize);
PutExprNode(Root);
with FExprBuffer^ do
begin
iVer := CANEXPRVERSION;
iTotalSize := FExprBufSize;
iNodes := $FFFF;
iNodeStart := SizeOf(CANExpr);
iLiteralStart := FExprNodeSize + SizeOf(CANExpr);
end;
Result := FExprBuffer;
end;
function TFilterExpr.NewCompareNode(Field: TField; Operator: CanOp;
const Value: Variant): PExprNode;
begin
Result := NewNode(enOperator, Operator, Unassigned,
NewNode(enField, canNOTDEFINED, Field.FieldName, nil, nil),
NewNode(enConst, canNOTDEFINED, Value, nil, nil));
end;
function TFilterExpr.NewNode(Kind: TExprNodeKind; Operator: CanOp;
const Data: Variant; Left, Right: PExprNode): PExprNode;
begin
New(Result);
with Result^ do
begin
FNext := FNodes;
FKind := Kind;
FPartial := False;
FOperator := Operator;
FData := Data;
FLeft := Left;
FRight := Right;
end;
FNodes := Result;
end;
function TFilterExpr.PutCompareNode(Node: PExprNode): Integer;
const
ReverseOperator: array[canEQ..canLE] of CanOp = (
canEQ, canNE, canLT, canGT, canLE, canGE);
var
Operator: CanOp;
Left, Right, Temp: PExprNode;
Field: TField;
FieldPos, ConstPos, CaseInsensitive, PartialLength, L: Integer;
S: string;
begin
Operator := Node^.FOperator;
Left := Node^.FLeft;
Right := Node^.FRight;
if Right^.FKind = enField then
begin
Temp := Left;
Left := Right;
Right := Temp;
Operator := ReverseOperator[Operator];
end;
if (Left^.FKind <> enField) or (Right^.FKind <> enConst) then
DBError(SExprBadCompare);
Field := FieldFromNode(Left);
if VarIsNull(Right^.FData) then
begin
case Operator of
canEQ: Operator := canISBLANK;
canNE: Operator := canNOTBLANK;
else
DBError(SExprBadNullTest);
end;
Result := PutNode(nodeUNARY, Operator, 1);
SetNodeOp(Result, 0, PutFieldNode(Field));
end else
begin
if ((Operator = canEQ) or (Operator = canNE)) and
(Field.DataType = ftString) then
begin
S := Right^.FData;
L := Length(S);
if L <> 0 then
begin
CaseInsensitive := 0;
PartialLength := 0;
if foCaseInsensitive in FOptions then CaseInsensitive := 1;
if Node^.FPartial then PartialLength := L else
if not (foNoPartialCompare in FOptions) and (L > 1) and
(S[L] = '*') then
begin
Delete(S, L, 1);
PartialLength := L - 1;
end;
if (CaseInsensitive <> 0) or (PartialLength <> 0) then
begin
Result := PutNode(nodeCOMPARE, Operator, 4);
SetNodeOp(Result, 0, CaseInsensitive);
SetNodeOp(Result, 1, PartialLength);
SetNodeOp(Result, 2, PutFieldNode(Field));
SetNodeOp(Result, 3, PutConstStr(S));
Exit;
end;
end;
end;
Result := PutNode(nodeBINARY, Operator, 2);
FieldPos := PutFieldNode(Field);
case Field.DataType of
ftString:
ConstPos := PutConstStr(Right^.FData);
ftSmallint:
ConstPos := PutConstInt(fldINT16, Right^.FData);
ftInteger, ftAutoInc:
ConstPos := PutConstInt(fldINT32, Right^.FData);
ftWord:
ConstPos := PutConstInt(fldUINT16, Right^.FData);
ftFloat, ftCurrency:
ConstPos := PutConstFloat(Right^.FData);
ftBCD:
ConstPos := PutConstBCD(Right^.FData, Field.Size);
ftDate:
ConstPos := PutConstDate(Right^.FData);
ftTime:
ConstPos := PutConstTime(Right^.FData);
ftDateTime:
ConstPos := PutConstDateTime(Right^.FData);
else
DBErrorFmt(SExprBadField, [Field.FieldName]);
end;
SetNodeOp(Result, 0, FieldPos);
SetNodeOp(Result, 1, ConstPos);
end;
end;
function TFilterExpr.PutConstBCD(const Value: Variant;
Decimals: Integer): Integer;
var
C: Currency;
BCD: FMTBcd;
begin
if VarType(Value) = varString then
C := StrToCurr(string(TVarData(Value).VString)) else
C := Value;
CurrToBCD(C, BCD, 32, Decimals);
Result := PutConstNode(fldBCD, @BCD, 18);
end;
function TFilterExpr.PutConstDate(const Value: Variant): Integer;
var
DateTime: TDateTime;
TimeStamp: TTimeStamp;
begin
if VarType(Value) = varString then
DateTime := StrToDate(string(TVarData(Value).VString)) else
DateTime := VarToDateTime(Value);
TimeStamp := DateTimeToTimeStamp(DateTime);
Result := PutConstNode(fldDATE, @TimeStamp.Date, 4);
end;
function TFilterExpr.PutConstDateTime(const Value: Variant): Integer;
var
DateTime: TDateTime;
DateData: Double;
begin
if VarType(Value) = varString then
DateTime := StrToDateTime(string(TVarData(Value).VString)) else
DateTime := VarToDateTime(Value);
DateData := TimeStampToMSecs(DateTimeToTimeStamp(DateTime));
Result := PutConstNode(fldTIMESTAMP, @DateData, 8);
end;
function TFilterExpr.PutConstFloat(const Value: Variant): Integer;
var
F: Double;
begin
if VarType(Value) = varString then
F := StrToFloat(string(TVarData(Value).VString)) else
F := Value;
Result := PutConstNode(fldFLOAT, @F, SizeOf(Double));
end;
function TFilterExpr.PutConstInt(DataType: Integer;
const Value: Variant): Integer;
var
I, Size: Integer;
begin
if VarType(Value) = varString then
I := StrToInt(string(TVarData(Value).VString)) else
I := Value;
Size := 2;
case DataType of
fldINT16:
if (I < -32768) or (I > 32767) then DBError(SExprRangeError);
fldUINT16:
if (I < 0) or (I > 65535) then DBError(SExprRangeError);
else
Size := 4;
end;
Result := PutConstNode(DataType, @I, Size);
end;
function TFilterExpr.PutConstNode(DataType: Integer; Data: PChar;
Size: Integer): Integer;
begin
Result := PutNode(nodeCONST, canCONST2, 3);
SetNodeOp(Result, 0, DataType);
SetNodeOp(Result, 1, Size);
SetNodeOp(Result, 2, PutData(Data, Size));
end;
function TFilterExpr.PutConstStr(const Value: string): Integer;
var
Buffer: array[0..255] of Char;
begin
AnsiToNative(FDataSet.Locale, Value, Buffer, SizeOf(Buffer) - 1);
Result := PutConstNode(fldZSTRING, Buffer, StrLen(Buffer) + 1);
end;
function TFilterExpr.PutConstTime(const Value: Variant): Integer;
var
DateTime: TDateTime;
TimeStamp: TTimeStamp;
begin
if VarType(Value) = varString then
DateTime := StrToTime(string(TVarData(Value).VString)) else
DateTime := VarToDateTime(Value);
TimeStamp := DateTimeToTimeStamp(DateTime);
Result := PutConstNode(fldTIME, @TimeStamp.Time, 4);
end;
function TFilterExpr.PutData(Data: PChar; Size: Integer): Integer;
begin
Move(Data^, GetExprData(FExprBufSize, Size)^, Size);
Result := FExprDataSize;
Inc(FExprDataSize, Size);
end;
function TFilterExpr.PutExprNode(Node: PExprNode): Integer;
const
BoolFalse: WordBool = False;
var
Field: TField;
begin
case Node^.FKind of
enField:
begin
Field := FieldFromNode(Node);
if Field.DataType <> ftBoolean then
DBErrorFmt(SExprNotBoolean, [Field.FieldName]);
Result := PutNode(nodeBINARY, canNE, 2);
SetNodeOp(Result, 0, PutFieldNode(Field));
SetNodeOp(Result, 1, PutConstNode(fldBOOL, @BoolFalse,
SizeOf(WordBool)));
end;
enOperator:
case Node^.FOperator of
canEQ..canLE:
Result := PutCompareNode(Node);
canAND, canOR:
begin
Result := PutNode(nodeBINARY, Node^.FOperator, 2);
SetNodeOp(Result, 0, PutExprNode(Node^.FLeft));
SetNodeOp(Result, 1, PutExprNode(Node^.FRight));
end;
else
Result := PutNode(nodeUNARY, canNOT, 1);
SetNodeOp(Result, 0, PutExprNode(Node^.FLeft));
end;
else
DBError(SExprIncorrect);
end;
end;
function TFilterExpr.PutFieldNode(Field: TField): Integer;
var
Buffer: array[0..255] of Char;
begin
AnsiToNative(FDataSet.Locale, Field.FieldName, Buffer, SizeOf(Buffer) - 1);
Result := PutNode(nodeFIELD, canFIELD2, 2);
SetNodeOp(Result, 0, Field.FieldNo);
SetNodeOp(Result, 1, PutData(Buffer, StrLen(Buffer) + 1));
end;
function TFilterExpr.PutNode(NodeType: NodeClass; OpType: CanOp;
OpCount: Integer): Integer;
var
Size: Integer;
begin
Size := SizeOf(CANHdr) + OpCount * SizeOf(Word);
with PCANHdr(GetExprData(SizeOf(CANExpr) + FExprNodeSize, Size))^ do
begin
nodeClass := NodeType;
canOp := OpType;
end;
Result := FExprNodeSize;
Inc(FExprNodeSize, Size);
end;
procedure TFilterExpr.SetNodeOp(Node, Index, Data: Integer);
begin
PWordArray(PChar(FExprBuffer) + (SizeOf(CANExpr) + Node +
SizeOf(CANHdr)))^[Index] := Data;
end;
{ TExprParser }
type
TExprToken = (etEnd, etSymbol, etName, etLiteral, etLParen, etRParen,
etEQ, etNE, etGE, etLE, etGT, etLT);
TExprParser = class
private
FFilter: TFilterExpr;
FText: string;
FSourcePtr: PChar;
FTokenPtr: PChar;
FTokenString: string;
FToken: TExprToken;
FFilterData: PCANExpr;
procedure NextToken;
function ParseExpr: PExprNode;
function ParseExpr2: PExprNode;
function ParseExpr3: PExprNode;
function ParseExpr4: PExprNode;
function ParseExpr5: PExprNode;
function TokenName: string;
function TokenSymbolIs(const S: string): Boolean;
public
constructor Create(DataSet: TDataSet; const Text: string;
Options: TFilterOptions);
destructor Destroy; override;
property FilterData: PCANExpr read FFilterData;
end;
constructor TExprParser.Create(DataSet: TDataSet; const Text: string;
Options: TFilterOptions);
var
Root: PExprNode;
begin
FFilter := TFilterExpr.Create(DataSet, Options);
FText := Text;
FSourcePtr := PChar(Text);
NextToken;
Root := ParseExpr;
if FToken <> etEnd then DBError(SExprTermination);
FFilterData := FFilter.GetFilterData(Root);
end;
destructor TExprParser.Destroy;
begin
FFilter.Free;
end;
procedure TExprParser.NextToken;
var
P, TokenStart: PChar;
L: Integer;
StrBuf: array[0..255] of Char;
begin
FTokenString := '';
P := FSourcePtr;
while (P^ <> #0) and (P^ <= ' ') do Inc(P);
FTokenPtr := P;
case P^ of
'A'..'Z', 'a'..'z', '_':
begin
TokenStart := P;
Inc(P);
while P^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(P);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etSymbol;
end;
'[':
begin
Inc(P);
TokenStart := P;
while (P^ <> ']') and (P^ <> #0) do
begin
if P^ in LeadBytes then
begin
if (P+1)^ = #0 then DBErrorFmt(SExprInvalidChar, [IntToHex(byte(P^), 2)] )
else Inc(P);
end;
Inc(P);
end;
if P^ = #0 then DBError(SExprNameError);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etName;
Inc(P);
end;
'''':
begin
Inc(P);
L := 0;
while True do
begin
if P^ = #0 then DBError(SExprStringError);
if P^ = '''' then
begin
Inc(P);
if P^ <> '''' then Break;
end;
if L < SizeOf(StrBuf) then
begin
StrBuf[L] := P^;
Inc(L);
end;
Inc(P);
end;
SetString(FTokenString, StrBuf, L);
FToken := etLiteral;
end;
'-', '0'..'9':
begin
TokenStart := P;
Inc(P);
while P^ in ['0'..'9', '.', 'e', 'E', '+', '-'] do Inc(P);
SetString(FTokenString, TokenStart, P - TokenStart);
FToken := etLiteral;
end;
'(':
begin
Inc(P);
FToken := etLParen;
end;
')':
begin
Inc(P);
FToken := etRParen;
end;
'<':
begin
Inc(P);
case P^ of
'=':
begin
Inc(P);
FToken := etLE;
end;
'>':
begin
Inc(P);
FToken := etNE;
end;
else
FToken := etLT;
end;
end;
'=':
begin
Inc(P);
FToken := etEQ;
end;
'>':
begin
Inc(P);
if P^ = '=' then
begin
Inc(P);
FToken := etGE;
end else
FToken := etGT;
end;
#0:
FToken := etEnd;
else
DBErrorFmt(SExprInvalidChar, [P^]);
end;
FSourcePtr := P;
end;
function TExprParser.ParseExpr: PExprNode;
begin
Result := ParseExpr2;
while TokenSymbolIs('OR') do
begin
NextToken;
Result := FFilter.NewNode(enOperator, canOR, Unassigned,
Result, ParseExpr2);
end;
end;
function TExprParser.ParseExpr2: PExprNode;
begin
Result := ParseExpr3;
while TokenSymbolIs('AND') do
begin
NextToken;
Result := FFilter.NewNode(enOperator, canAND, Unassigned,
Result, ParseExpr3);
end;
end;
function TExprParser.ParseExpr3: PExprNode;
begin
if TokenSymbolIs('NOT') then
begin
NextToken;
Result := FFilter.NewNode(enOperator, canNOT, Unassigned,
ParseExpr4, nil);
end else
Result := ParseExpr4;
end;
function TExprParser.ParseExpr4: PExprNode;
const
Operators: array[etEQ..etLT] of CanOp = (
canEQ, canNE, canGE, canLE, canGT, canLT);
var
Operator: CanOp;
begin
Result := ParseExpr5;
if FToken in [etEQ..etLT] then
begin
Operator := Operators[FToken];
NextToken;
Result := FFilter.NewNode(enOperator, Operator, Unassigned,
Result, ParseExpr5);
end;
end;
function TExprParser.ParseExpr5: PExprNode;
begin
case FToken of
etSymbol:
if TokenSymbolIs('NULL') then
Result := FFilter.NewNode(enConst, canNOTDEFINED, System.Null, nil, nil) else
Result := FFilter.NewNode(enField, canNOTDEFINED, FTokenString, nil, nil);
etName:
Result := FFilter.NewNode(enField, canNOTDEFINED, FTokenString, nil, nil);
etLiteral:
Result := FFilter.NewNode(enConst, canNOTDEFINED, FTokenString, nil, nil);
etLParen:
begin
NextToken;
Result := ParseExpr;
if FToken <> etRParen then DBErrorFmt(SExprNoRParen, [TokenName]);
end;
else
DBErrorFmt(SExprExpected, [TokenName]);
end;
NextToken;
end;
function TExprParser.TokenName: string;
begin
if FSourcePtr = FTokenPtr then Result := LoadStr(SExprNothing) else
begin
SetString(Result, FTokenPtr, FSourcePtr - FTokenPtr);
Result := '''' + Result + '''';
end;
end;
function TExprParser.TokenSymbolIs(const S: string): Boolean;
begin
Result := (FToken = etSymbol) and (AnsiCompareText(FTokenString, S) = 0);
end;
{ TDataSet }
constructor TDataSet.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FFieldDefs := TFieldDefs.Create(Self);
FFields := TList.Create;
FDataSources := TList.Create;
FAutoCalcFields := True;
ClearBuffers;
SetLocale(DB.Session.Locale);
end;
destructor TDataSet.Destroy;
begin
Destroying;
Close;
SetUpdateObject(nil);
FDesigner.Free;
while FDataSources.Count > 0 do RemoveDataSource(FDataSources.Last);
FDataSources.Free;
DestroyFields;
FFields.Free;
FFieldDefs.Free;
FAsyncCallback.Free;
inherited Destroy;
end;
procedure TDataSet.SetName(const Value: TComponentName);
var
I: Integer;
OldName, FieldName, NamePrefix: TComponentName;
Field: TField;
begin
OldName := Name;
inherited SetName(Value);
if (csDesigning in ComponentState) and (Name <> OldName) then
{ In design mode the name of the fields should track the data set name }
for I := 0 to FFields.Count - 1 do
begin
Field := FFields[I];
if Field.Owner = Owner then
begin
FieldName := Field.Name;
NamePrefix := FieldName;
if Length(NamePrefix) > Length(OldName) then
begin
SetLength(NamePrefix, Length(OldName));
if AnsiCompareText(OldName, NamePrefix) = 0 then
begin
System.Delete(FieldName, 1, Length(OldName));
System.Insert(Value, FieldName, 1);
try
Field.Name := FieldName;
except
on EComponentError do {Ignore rename errors };
end;
end;
end;
end;
end;
end;
procedure TDataSet.GetChildren(Proc: TGetChildProc);
var
I: Integer;
Field: TField;
begin
for I := 0 to FFields.Count - 1 do
begin
Field := FFields[I];
if Field.Owner <> Self then Proc(Field);
end;
end;
procedure TDataSet.SetChildOrder(Component: TComponent; Order: Integer);
begin
if FFields.IndexOf(Component) >= 0 then
(Component as TField).Index := Order;
end;
procedure TDataSet.Loaded;
begin
inherited Loaded;
try
if FStreamedActive then Active := True;
except
if csDesigning in ComponentState then
Application.HandleException(Self)
else
raise;
end;
end;
procedure TDataSet.SetState(Value: TDataSetState);
begin
if FState <> Value then
begin
FState := Value;
FModified := False;
DataEvent(deUpdateState, 0);
end;
end;
procedure TDataSet.Open;
begin
Active := True;
end;
procedure TDataSet.Close;
begin
Active := False;
end;
procedure TDataSet.CheckInactive;
begin
if Active then
if csUpdating in ComponentState then
Close else
DBError(SDataSetOpen);
end;
function TDataSet.GetActive: Boolean;
begin
Result := State <> dsInactive;
end;
procedure TDataSet.SetActive(Value: Boolean);
begin
if (csReading in ComponentState) then
begin
if Value then FStreamedActive := Value;
end
else
if Active <> Value then
begin
if Value then
begin
DoBeforeOpen;
try
OpenCursor;
SetState(dsBrowse);
except
SetState(dsInactive);
CloseCursor;
raise;
end;
DoAfterOpen;
end else
begin
if not (csDestroying in ComponentState) then DoBeforeClose;
SetState(dsInactive);
CloseCursor;
if not (csDestroying in ComponentState) then DoAfterClose;
end;
end;
end;
procedure TDataSet.SetLocale(Value: TLocale);
begin
FLocale := Value;
end;
procedure TDataSet.OpenCursor;
var
CursorLocale: TLocale;
begin
if FAsyncCallback = nil then
FAsyncCallback := TBDECallback.Create(Self, nil, cbYIELDCLIENT,
@FCBYieldStep, SizeOf(CBYieldStep), YieldCallBack, False);
FHandle := CreateHandle;
if FHandle = nil then DBError(SHandleError);
if DbiGetLdObj(FHandle, CursorLocale) = 0 then SetLocale(CursorLocale);
InternalOpen;
end;
procedure TDataSet.CloseCursor;
begin
InternalClose;
SetLocale(DB.Session.Locale);
if FHandle <> nil then
begin
DestroyHandle;
FHandle := nil;
end;
end;
function TDataSet.CreateHandle: HDBICur;
begin
Result := nil;
end;
procedure TDataSet.DestroyHandle;
begin
DbiRelRecordLock(FHandle, False);
DbiCloseCursor(FHandle);
end;
procedure TDataSet.InternalOpen;
var
I: Integer;
FieldDescs: PFieldDescList;
RequiredFields: set of 0..255;
CursorProps: CurProps;
ValCheckDesc: VCHKDesc;
begin
if not InfoQueryMode and CachedUpdates then
begin
DbiGetCursorProps(FHandle, CursorProps);
Check(DbiBeginDelayedUpdates(FHandle));
end;
DbiGetCursorProps(FHandle, CursorProps);
FRecordSize := CursorProps.iRecBufSize;
FBookmarkSize := CursorProps.iBookmarkSize;
FCanModify := (CursorProps.eOpenMode = dbiReadWrite) and
not CursorProps.bTempTable;
FRecNoStatus := TRecNoStatus(CursorProps.ISeqNums);
RequiredFields := [];
for I := 1 to CursorProps.iValChecks do
begin
DbiGetVChkDesc(FHandle, I, @ValCheckDesc);
if ValCheckDesc.bRequired and not ValCheckDesc.bHasDefVal then
Include(RequiredFields, ValCheckDesc.iFldNum - 1);
end;
FieldDescs := AllocMem(CursorProps.iFields * SizeOf(FLDDesc));
try
DbiGetFieldDescs(FHandle, PFLDDesc(FieldDescs));
FieldDefs.Clear;
for I := 0 to CursorProps.iFields - 1 do
FieldDefs.AddFieldDesc(FieldDescs^[I], I in RequiredFields, I + 1);
finally
FreeMem(FieldDescs, CursorProps.iFields * SizeOf(FLDDesc));
end;
if not InfoQueryMode then
begin
GetIndexInfo;
FDefaultFields := FFields.Count = 0;
if FDefaultFields then CreateFields;
BindFields(True);
FRecInfoOfs := FRecordSize + FCalcFieldsSize;
FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo);
FRecBufSize := FBookmarkOfs + 1 + FBookmarkSize;
if CachedUpdates then
begin
AllocDelUpdCBBuf(True);
SetupCallBack(UpdateCallBackRequired);
end;
AllocKeyBuffers;
DbiSetToBegin(FHandle);
PrepareCursor;
if FFilterText <> '' then
FExprFilter := CreateExprFilter(FFilterText, FFilterOptions, 0);
if Assigned(FOnFilterRecord) then
FFuncFilter := CreateFuncFilter(@TDataSet.RecordFilter, 1);
if FFiltered then ActivateFilters;
UpdateBufferCount;
FBOF := True;
end;
end;
procedure TDataSet.InternalClose;
begin
if not InfoQueryMode then
begin
FreeFieldBuffers;
SetBufListSize(0);
FBufferCount := 0;
ClearBuffers;
FFuncFilter := nil;
FExprFilter := nil;
FreeKeyBuffers;
if CachedUpdates then
begin
SetupCallBack(False);
AllocDelUpdCBBuf(False);
DbiEndDelayedUpdates(FHandle);
end;
BindFields(False);
if FDefaultFields then DestroyFields;
FDefaultFields := False;
FIndexFieldCount := 0;
FKeySize := 0;
FExpIndex := False;
FCaseInsIndex := False;
end;
FCanModify := False;
end;
procedure TDataSet.GetIndexInfo;
var
IndexDesc: IDXDesc;
begin
if DbiGetIndexDesc(FHandle, 0, IndexDesc) = 0 then
begin
FExpIndex := IndexDesc.bExpIdx;
FCaseInsIndex := IndexDesc.bCaseInsensitive;
if not ExpIndex then
begin
FIndexFieldCount := IndexDesc.iFldsInKey;
FIndexFieldMap := IndexDesc.aiKeyFld;
end;
FKeySize := IndexDesc.iKeyLen;
end;
end;
procedure TDataSet.PrepareCursor;
begin
end;
procedure TDataSet.ActivateFilters;
begin
if FExprFilter <> nil then DbiActivateFilter(FHandle, FExprFilter);
if FFuncFilter <> nil then DbiActivateFilter(FHandle, FFuncFilter);
end;
procedure TDataSet.DeactivateFilters;
begin
if FFuncFilter <> nil then DbiDeactivateFilter(FHandle, FFuncFilter);
if FExprFilter <> nil then DbiDeactivateFilter(FHandle, FExprFilter);
end;
procedure TDataSet.CreateFields;
var
I: Integer;
begin
for I := 0 to FFieldDefs.Count - 1 do
with FFieldDefs[I] do
if DataType <> ftUnknown then CreateField(Self);
end;
procedure TDataSet.DestroyFields;
var
Field: TField;
begin
while FFields.Count > 0 do
begin
Field := FFields.Last;
RemoveField(Field);
Field.Free;
end;
end;
procedure TDataSet.BindFields(Binding: Boolean);
const
CalcFieldTypes = [ftString, ftSmallint, ftInteger, ftWord, ftBoolean,
ftFloat, ftCurrency, ftBCD, ftDate, ftTime, ftDateTime];
BaseTypes: array[TFieldType] of TFieldType = (
ftUnknown, ftString, ftSmallint, ftInteger, ftWord,
ftBoolean, ftFloat, ftFloat, ftBCD, ftDate, ftTime, ftDateTime,
ftBytes, ftVarBytes, ftInteger, ftBlob, ftBlob, ftBlob,
ftBlob, ftBlob, ftBlob, ftBlob);
var
I: Integer;
FieldDef: TFieldDef;
begin
FCalcFieldsSize := 0;
FBDECalcFields := False;
for I := 0 to FFields.Count - 1 do
with TField(FFields[I]) do
if Binding then
begin
if FieldKind <> fkData then
begin
if not (DataType in CalcFieldTypes) then
DBErrorFmt(SInvalidCalcType, [DisplayName]);
FFieldNo := -1;
FOffset := FCalcFieldsSize;
Inc(FCalcFieldsSize, DataSize + 1);
end else
begin
FieldDef := FieldDefs.Find(FFieldName);
if (BaseTypes[DataType] <> BaseTypes[FieldDef.DataType]) or
(Size <> FieldDef.Size) then
DBErrorFmt(SFieldTypeMismatch, [DisplayName]);
FFieldNo := FieldDef.FieldNo;
if FieldDef.BDECalcField and not FBDECalcFields then
FBDECalcFields := True;
end;
Bind(True);
end else
begin
Bind(False);
FFieldNo := 0;
end;
end;
procedure TDataSet.SwitchToIndex(const IndexName, TagName: string);
var
Status: DBIResult;
CursorProps: CurProps;
begin
UpdateCursorPos;
Status := DbiSwitchToIndex(FHandle, PChar(IndexName),
PChar(TagName), 0, True);
if Status = DBIERR_NOCURRREC then
Status := DbiSwitchToIndex(FHandle, PChar(IndexName),
PChar(TagName), 0, False);
Check(Status);
SetBufListSize(0);
FIndexFieldCount := 0;
FKeySize := 0;
FExpIndex := False;
FCaseInsIndex := False;
DbiGetCursorProps(FHandle, CursorProps);
FBookmarkSize := CursorProps.iBookmarkSize;
FRecBufSize := FBookmarkOfs + FBookmarkSize + 1;
try
SetBufListSize(FBufferCount + 1);
except
SetState(dsInactive);
CloseCursor;
raise;
end;
GetIndexInfo;
end;
procedure TDataSet.FetchAll;
begin
if not EOF then
begin
CheckBrowseMode;
Check(DbiSetToEnd(Handle));
Check(DbiGetPriorRecord(FHandle, dbiNoLock, nil, nil));
UpdateCursorPos;
end;
end;
procedure TDataSet.FreeFieldBuffers;
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do TField(FFields[I]).FreeBuffers;
end;
procedure TDataSet.SetFieldDefs(Value: TFieldDefs);
begin
FFieldDefs.Assign(Value);
end;
procedure TDataSet.UpdateFieldDefs;
begin
if not FFieldDefs.FUpdated then
begin
InitFieldDefs;
FFieldDefs.FUpdated := True;
end;
end;
procedure TDataSet.InitFieldDefs;
begin
if not Active then
try
FInfoQueryMode := True;
OpenCursor;
finally
CloseCursor;
FInfoQueryMode := False;
end;
end;
procedure TDataSet.AddField(Field: TField);
begin
FFields.Add(Field);
Field.FDataSet := Self;
DataEvent(deFieldListChange, 0)
end;
procedure TDataSet.RemoveField(Field: TField);
begin
Field.FDataSet := nil;
FFields.Remove(Field);
if not (csDestroying in ComponentState) then
DataEvent(deFieldListChange, 0)
end;
function TDataSet.GetFieldCount: Integer;
begin
Result := FFields.Count;
end;
function TDataSet.GetField(Index: Integer): TField;
begin
Result := FFields[Index];
end;
procedure TDataSet.SetField(Index: Integer; Value: TField);
begin
TField(FFields[Index]).Assign(Value);
end;
function TDataSet.GetFieldValue(const FieldName: string): Variant;
var
I: Integer;
Fields: TList;
begin
if Pos(';', FieldName) <> 0 then
begin
Fields := TList.Create;
try
GetFieldList(Fields, FieldName);
Result := VarArrayCreate([0, Fields.Count - 1], varVariant);
for I := 0 to Fields.Count - 1 do
Result[I] := TField(Fields[I]).Value;
finally
Fields.Free;
end;
end else
Result := FieldByName(FieldName).Value
end;
procedure TDataSet.SetFieldValue(const FieldName: string;
const Value: Variant);
var
I: Integer;
Fields: TList;
begin
if Pos(';', FieldName) <> 0 then
begin
Fields := TList.Create;
try
GetFieldList(Fields, FieldName);
for I := 0 to Fields.Count - 1 do
TField(Fields[I]).Value := Value[I];
finally
Fields.Free;
end;
end else
FieldByName(FieldName).Value := Value;
end;
function TDataSet.FieldByName(const FieldName: string): TField;
begin
Result := FindField(FieldName);
if Result = nil then DBErrorFmt(SFieldNotFound, [FieldName]);
end;
function TDataSet.FieldByNumber(FieldNo: Integer): TField;
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do
begin
Result := Fields[I];
if Result.FieldNo = FieldNo then Exit;
end;
Result := nil;
end;
function TDataSet.FindField(const FieldName: string): TField;
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do
begin
Result := FFields[I];
if AnsiCompareText(Result.FFieldName, FieldName) = 0 then Exit;
end;
Result := nil;
end;
procedure TDataSet.CheckFieldName(const FieldName: string);
begin
if FieldName = '' then DBError(SFieldNameMissing);
if FindField(FieldName) <> nil then
DBErrorFmt(SDuplicateFieldName, [FieldName]);
end;
procedure TDataSet.CheckFieldNames(const FieldNames: string);
var
Pos: Integer;
begin
Pos := 1;
while Pos <= Length(FieldNames) do
FieldByName(ExtractFieldName(FieldNames, Pos));
end;
function TDataSet.GetIndexField(Index: Integer): TField;
var
FieldNo: Integer;
begin
if (Index < 0) or (Index >= FIndexFieldCount) then
DBError(SFieldIndexError);
FieldNo := FIndexFieldMap[Index];
Result := FieldByNumber(FieldNo);
if Result = nil then
DBErrorFmt(SIndexFieldMissing, [FFieldDefs[FieldNo - 1].Name]);
end;
procedure TDataSet.SetIndexField(Index: Integer; Value: TField);
begin
GetIndexField(Index).Assign(Value);
end;
function TDataSet.GetIndexFieldCount: Integer;
begin
Result := FIndexFieldCount;
end;
procedure TDataSet.GetFieldNames(List: TStrings);
var
I: Integer;
begin
List.BeginUpdate;
try
List.Clear;
if FFields.Count > 0 then
for I := 0 to FFields.Count - 1 do
List.Add(TField(FFields[I]).FFieldName)
else
begin
UpdateFieldDefs;
for I := 0 to FFieldDefs.Count - 1 do
List.Add(FFieldDefs[I].Name);
end;
finally
List.EndUpdate;
end;
end;
function TDataSet.GetDataSource: TDataSource;
begin
Result := nil;
end;
function TDataSet.IsLinkedTo(DataSource: TDataSource): Boolean;
var
DataSet: TDataSet;
begin
Result := True;
while DataSource <> nil do
begin
DataSet := DataSource.DataSet;
if DataSet = nil then Break;
if DataSet = Self then Exit;
DataSource := DataSet.DataSource;
end;
Result := False;
end;
procedure TDataSet.AddDataSource(DataSource: TDataSource);
begin
FDataSources.Add(DataSource);
DataSource.FDataSet := Self;
UpdateBufferCount;
DataSource.UpdateState;
end;
procedure TDataSet.RemoveDataSource(DataSource: TDataSource);
begin
DataSource.FDataSet := nil;
FDataSources.Remove(DataSource);
DataSource.UpdateState;
UpdateBufferCount;
end;
procedure TDataSet.SetBufListSize(Value: Integer);
var
I: Integer;
NewList: PBufferList;
begin
if FBufListSize <> Value then
begin
GetMem(NewList, Value * SizeOf(Pointer));
if FBufListSize > Value then
begin
if Value <> 0 then
Move(FBuffers^, NewList^, Value * SizeOf(Pointer));
for I := Value to FBufListSize - 1 do
FreeMem(FBuffers^[I], FRecBufSize);
end else
begin
if FBufListSize <> 0 then
Move(FBuffers^, NewList^, FBufListSize * SizeOf(Pointer));
I := FBufListSize;
try
while I < Value do
begin
GetMem(NewList^[I], FRecBufSize);
Inc(I);
end;
except
while I > FBufListSize do
begin
FreeMem(NewList^[I], FRecBufSize);
Dec(I);
end;
FreeMem(NewList, Value * SizeOf(Pointer));
raise;
end;
end;
FreeMem(FBuffers, FBufListSize * SizeOf(Pointer));
FBuffers := NewList;
FBufListSize := Value;
end;
end;
procedure TDataSet.SetBufferCount(Value: Integer);
var
I, Delta: Integer;
DataLink: TDataLink;
procedure AdjustFirstRecord(Delta: Integer);
var
DataLink: TDataLink;
begin
if Delta <> 0 then
begin
DataLink := FFirstDataLink;
while DataLink <> nil do
begin
if DataLink.Active then Inc(DataLink.FFirstRecord, Delta);
DataLink := DataLink.FNext;
end;
end;
end;
begin
if FBufferCount <> Value then
begin
if (FBufferCount > Value) and (FRecordCount > 0) then
begin
Delta := FActiveRecord;
DataLink := FFirstDataLink;
while DataLink <> nil do
begin
if DataLink.Active and (DataLink.FFirstRecord < Delta) then
Delta := DataLink.FFirstRecord;
DataLink := DataLink.FNext;
end;
for I := 0 to Value - 1 do MoveBuffer(I + Delta, I);
Dec(FActiveRecord, Delta);
if FCurrentRecord <> -1 then Dec(FCurrentRecord, Delta);
if FRecordCount > Value then FRecordCount := Value;
AdjustFirstRecord(-Delta);
end;
SetBufListSize(Value + 1);
FBufferCount := Value;
GetNextRecords;
AdjustFirstRecord(GetPriorRecords);
end;
end;
procedure TDataSet.UpdateBufferCount;
var
I, J, MaxBufferCount: Integer;
DataLink: TDataLink;
begin
if FHandle <> nil then
begin
MaxBufferCount := 1;
FFirstDataLink := nil;
for I := FDataSources.Count - 1 downto 0 do
with TDataSource(FDataSources[I]) do
for J := FDataLinks.Count - 1 downto 0 do
begin
DataLink := FDataLinks[J];
DataLink.FNext := FFirstDataLink;
FFirstDataLink := DataLink;
if DataLink.FBufferCount > MaxBufferCount then
MaxBufferCount := DataLink.FBufferCount;
end;
SetBufferCount(MaxBufferCount);
end;
end;
procedure TDataSet.InitRecord(Buffer: PChar);
begin
DbiInitRecord(FHandle, Buffer);
FillChar(Buffer[FRecordSize], FCalcFieldsSize, 0);
with PRecInfo(Buffer + FRecInfoOfs)^ do
begin
UpdateStatus := TUpdateStatus(usInserted);
RecordNumber := -1;
end;
end;
procedure TDataSet.AllocKeyBuffers;
var
KeyIndex: TKeyIndex;
begin
try
for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do
FKeyBuffers[KeyIndex] := InitKeyBuffer(
AllocMem(SizeOf(TKeyBuffer) + FRecordSize));
except
FreeKeyBuffers;
raise;
end;
end;
procedure TDataSet.FreeKeyBuffers;
var
KeyIndex: TKeyIndex;
begin
for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do
DisposeMem(FKeyBuffers[KeyIndex], SizeOf(TKeyBuffer) + FRecordSize);
end;
function TDataSet.InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer;
begin
FillChar(Buffer^, SizeOf(TKeyBuffer) + FRecordSize, 0);
DbiInitRecord(FHandle, PChar(Buffer) + SizeOf(TKeyBuffer));
Result := Buffer;
end;
procedure TDataSet.DataEvent(Event: TDataEvent; Info: Longint);
var
I: Integer;
begin
case Event of
deFieldChange:
begin
if TField(Info).FieldKind = fkData then FModified := True;
if State <> dsSetKey then
begin
if FBDECalcFields and (TField(Info).FieldKind = fkData) and
not TField(Info).BDECalcField then
CalculateBDEFields
else if (FCalcFieldsSize <> 0) and FAutoCalcFields and
(TField(Info).FieldKind = fkData) then
begin
FillChar(ActiveBuffer[FRecordSize], FCalcFieldsSize, 0);
CalculateFields;
end;
TField(Info).Change;
end;
end;
dePropertyChange:
FFieldDefs.FUpdated := False;
end;
if FDisableCount = 0 then
begin
for I := 0 to FDataSources.Count - 1 do
TDataSource(FDataSources[I]).DataEvent(Event, Info);
if FDesigner <> nil then FDesigner.DataEvent(Event, Info);
end else
if (Event = deUpdateState) and (State = dsInactive) or
(Event = deLayoutChange) then FEnableEvent := deLayoutChange;
end;
function TDataset.ControlsDisabled: Boolean;
begin
Result := FDisableCount <> 0;
end;
procedure TDataSet.DisableControls;
begin
if FDisableCount = 0 then
begin
FDisableState := FState;
FEnableEvent := deDataSetChange;
end;
Inc(FDisableCount);
end;
procedure TDataSet.EnableControls;
begin
if FDisableCount <> 0 then
begin
Dec(FDisableCount);
if FDisableCount = 0 then
begin
if FDisableState <> FState then DataEvent(deUpdateState, 0);
if (FDisableState <> dsInactive) and (FState <> dsInactive) then
DataEvent(FEnableEvent, 0);
end;
end;
end;
procedure TDataSet.UpdateRecord;
begin
if not (State in dsEditModes) then DBError(SNotEditing);
DataEvent(deUpdateRecord, 0);
end;
procedure TDataSet.MoveBuffer(CurIndex, NewIndex: Integer);
var
Buffer: PChar;
begin
if CurIndex <> NewIndex then
begin
Buffer := FBuffers^[CurIndex];
if CurIndex < NewIndex then
Move(FBuffers^[CurIndex + 1], FBuffers^[CurIndex],
(NewIndex - CurIndex) * SizeOf(Pointer))
else
Move(FBuffers^[NewIndex], FBuffers^[NewIndex + 1],
(CurIndex - NewIndex) * SizeOf(Pointer));
FBuffers^[NewIndex] := Buffer;
end;
end;
procedure TDataSet.CopyBuffer(SourceIndex, DestIndex: Integer);
begin
Move(FBuffers^[SourceIndex]^, FBuffers^[DestIndex]^, FRecBufSize);
end;
function TDataSet.ActiveBuffer: PChar;
begin
Result := FBuffers^[FActiveRecord];
end;
procedure TDataSet.ClearBuffers;
begin
FRecordCount := 0;
FActiveRecord := 0;
FCurrentRecord := -1;
FBOF := True;
FEOF := True;
end;
procedure TDataSet.ActivateBuffers;
begin
FRecordCount := 1;
FActiveRecord := 0;
FCurrentRecord := 0;
FBOF := False;
FEOF := False;
end;
procedure TDataSet.GetCalcFields(Index: Integer);
var
SaveState: TDataSetState;
begin
if FCalcFieldsSize <> 0 then
begin
SaveState := FState;
FState := dsCalcFields;
try
FCalcBuffer := FBuffers^[Index];
FillChar(FCalcBuffer[FRecordSize], FCalcFieldsSize, 0);
CalculateFields;
finally
FState := SaveState;
end;
end;
end;
procedure TDataSet.CalculateFields;
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do
with TField(FFields[I]) do
if FieldKind = fkLookup then CalcLookupValue;
DoOnCalcFields;
end;
procedure TDataSet.CalculateBDEFields;
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do
with TField(FFields[I]) do
if BDECalcField then Value := Value;
end;
function TDataSet.GetCanModify: Boolean;
begin
Result := FCanModify or ForceUpdateCallback;
end;
function TDataSet.GetRecord(Index: Integer; GetMode: TGetMode): DBIResult;
var
Buffer: PChar;
begin
Buffer := FBuffers^[Index];
case GetMode of
gmCurrent:
Result := DbiGetRecord(FHandle, dbiNoLock, Buffer, @FRecProps);
gmNext:
Result := DbiGetNextRecord(FHandle, dbiNoLock, Buffer, @FRecProps);
gmPrior:
Result := DbiGetPriorRecord(FHandle, dbiNoLock, Buffer, @FRecProps);
else
Result := 0;
end;
if Result = 0 then
begin
with PRecInfo(Buffer + FRecInfoOfs)^ do
begin
UpdateStatus := TUpdateStatus(FRecProps.iRecStatus);
case FRecNoStatus of
rnParadox: RecordNumber := FRecProps.iSeqNum;
rnDBase: RecordNumber := FRecProps.iPhyRecNum;
else
RecordNumber := -1;
end;
end;
GetCalcFields(Index);
Buffer[FBookmarkOfs] := #0;
Check(DbiGetBookmark(FHandle, Buffer + FBookmarkOfs + 1));
end;
end;
procedure TDataSet.SetCurrentRecord(Index: Integer);
var
Buffer: PChar;
begin
if FCurrentRecord <> Index then
begin
Buffer := FBuffers^[Index];
case Buffer[FBookmarkOfs] of
#0,#255: Check(DbiSetToBookmark(FHandle, Buffer + FBookmarkOfs + 1));
#1: Check(DbiSetToBegin(FHandle));
#2: Check(DbiSetToEnd(FHandle));
end;
FCurrentRecord := Index;
end;
end;
procedure TDataSet.UpdateCursorPos;
begin
if FRecordCount > 0 then SetCurrentRecord(FActiveRecord);
end;
procedure TDataSet.CursorPosChanged;
begin
FCurrentRecord := -1;
end;
function TDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
begin
Result := False;
if (FActiveRecord < FRecordCount) and
(FBuffers^[FActiveRecord][FBookmarkOfs] = #0) then
begin
if FCurrentRecord <> FActiveRecord then
begin
if DbiSetToBookmark(FHandle, FBuffers^[FActiveRecord] +
FBookmarkOfs + 1) <> 0 then Exit;
FCurrentRecord := FActiveRecord;
end;
Result := DbiGetRecord(FHandle, dbiNoLock, Buffer, nil) = 0;
end;
end;
function TDataSet.GetNextRecord: Boolean;
var
GetMode: TGetMode;
Status: DBIResult;
begin
GetMode := gmNext;
if FRecordCount > 0 then
begin
SetCurrentRecord(FRecordCount - 1);
if (State = dsInsert) and (FCurrentRecord = FActiveRecord) and
(ActiveBuffer[FBookmarkOfs] = #0) then GetMode := gmCurrent;
end;
Status := GetRecord(FRecordCount, GetMode);
case Status of
DBIERR_NONE:
begin
if FRecordCount = 0 then
ActivateBuffers
else
if FRecordCount < FBufferCount then
Inc(FRecordCount)
else
MoveBuffer(0, FRecordCount);
FCurrentRecord := FRecordCount - 1;
Result := True;
end;
DBIERR_EOF:
begin
FCurrentRecord := -1;
Result := False;
end;
else
DbiError(Status);
end;
end;
function TDataSet.GetPriorRecord: Boolean;
var
Status: DBIResult;
begin
if FRecordCount > 0 then SetCurrentRecord(0);
Status := GetRecord(FRecordCount, gmPrior);
case Status of
DBIERR_NONE:
begin
if FRecordCount = 0 then
ActivateBuffers
else
begin
MoveBuffer(FRecordCount, 0);
if FRecordCount < FBufferCount then
begin
Inc(FRecordCount);
Inc(FActiveRecord);
end;
end;
FCurrentRecord := 0;
Result := True;
end;
DBIERR_BOF:
begin
FCurrentRecord := -1;
Result := False;
end;
else
DbiError(Status);
end;
end;
function TDataSet.GetNextRecords: Integer;
begin
Result := 0;
try
while (FRecordCount < FBufferCount) and GetNextRecord do Inc(Result);
except
end;
end;
function TDataSet.GetPriorRecords: Integer;
begin
Result := 0;
try
while (FRecordCount < FBufferCount) and GetPriorRecord do Inc(Result);
except
end;
end;
procedure TDataSet.Resync(Mode: TResyncMode);
var
Count: Integer;
begin
if rmExact in Mode then
begin
FCurrentRecord := -1;
Check(GetRecord(FRecordCount, gmCurrent));
end else
if (GetRecord(FRecordCount, gmCurrent) <> 0) and
(GetRecord(FRecordCount, gmNext) <> 0) and
(GetRecord(FRecordCount, gmPrior) <> 0) then
begin
ClearBuffers;
DataEvent(deDataSetChange, 0);
Exit;
end;
if rmCenter in Mode then
Count := (FBufferCount - 1) div 2 else
Count := FActiveRecord;
MoveBuffer(FRecordCount, 0);
ActivateBuffers;
try
while (Count > 0) and GetPriorRecord do Dec(Count);
GetNextRecords;
GetPriorRecords;
except
end;
DataEvent(deDataSetChange, 0);
end;
procedure TDataSet.CheckBrowseMode;
begin
if State = dsInactive then DBError(SDataSetClosed);
DataEvent(deCheckBrowseMode, 0);
case State of
dsEdit, dsInsert:
begin
UpdateRecord;
if Modified then Post else Cancel;
end;
dsSetKey:
Post;
end;
end;
procedure TDataSet.CheckSetKeyMode;
begin
if State <> dsSetKey then DBError(SNotEditing);
end;
procedure TDataSet.CheckCanModify;
begin
if not CanModify then DBError(SDataSetReadOnly);
end;
procedure TDataSet.CheckCachedUpdateMode;
begin
if not CachedUpdates then DBError(SNoCachedUpdates);
end;
procedure TDataSet.First;
begin
CheckBrowseMode;
ClearBuffers;
try
Check(DbiSetToBegin(FHandle));
GetNextRecord;
GetNextRecords;
finally
FBOF := True;
DataEvent(deDataSetChange, 0);
end;
end;
procedure TDataSet.Last;
begin
CheckBrowseMode;
ClearBuffers;
try
Check(DbiSetToEnd(FHandle));
GetPriorRecord;
GetPriorRecords;
finally
FEOF := True;
DataEvent(deDataSetChange, 0);
end;
end;
function TDataSet.MoveBy(Distance: Integer): Integer;
var
I, ScrollCount: Integer;
begin
CheckBrowseMode;
Result := 0;
if ((Distance > 0) and not FEOF) or ((Distance < 0) and not FBOF) then
begin
FBOF := False;
FEOF := False;
ScrollCount := 0;
try
while Distance > 0 do
begin
if FActiveRecord < FRecordCount - 1 then Inc(FActiveRecord) else
begin
if FRecordCount < FBufferCount then I := 0 else I := 1;
if GetNextRecord then Dec(ScrollCount, I) else
begin
FEOF := True;
Break;
end;
end;
Dec(Distance);
Inc(Result);
end;
while Distance < 0 do
begin
if FActiveRecord > 0 then Dec(FActiveRecord) else
begin
if FRecordCount < FBufferCount then I := 0 else I := 1;
if GetPriorRecord then Inc(ScrollCount, I) else
begin
FBOF := True;
Break;
end;
end;
Inc(Distance);
Dec(Result);
end;
finally
DataEvent(deDataSetScroll, ScrollCount);
end;
end;
end;
procedure TDataSet.Next;
begin
MoveBy(1);
end;
procedure TDataSet.Prior;
begin
MoveBy(-1);
end;
procedure TDataSet.Refresh;
begin
CheckBrowseMode;
UpdateCursorPos;
Check(DbiForceReread(FHandle));
Resync([]);
end;
procedure TDataSet.SetFields(const Values: array of const);
var
I: Integer;
begin
for I := 0 to High(Values) do Fields[I].AssignValue(Values[I]);
end;
procedure TDataSet.Insert;
var
Buffer: PChar;
begin
BeginInsertAppend;
MoveBuffer(FRecordCount, FActiveRecord);
Buffer := ActiveBuffer;
InitRecord(Buffer);
if FRecordCount = 0 then
Buffer[FBookmarkOfs] := #1
else
begin
Move(FBuffers^[FActiveRecord + 1][FBookmarkOfs], Buffer[FBookmarkOfs],
FBookmarkSize + 1);
Buffer[FBookmarkOfs] := #255;
end;
if FRecordCount < FBufferCount then Inc(FRecordCount);
EndInsertAppend;
end;
procedure TDataSet.Append;
var
Buffer: PChar;
begin
BeginInsertAppend;
ClearBuffers;
Buffer := FBuffers^[0];
InitRecord(Buffer);
Buffer[FBookmarkOfs] := #2;
FRecordCount := 1;
FBOF := False;
GetPriorRecords;
EndInsertAppend;
end;
procedure TDataSet.BeginInsertAppend;
begin
CheckBrowseMode;
CheckCanModify;
DoBeforeInsert;
end;
procedure TDataSet.EndInsertAppend;
begin
SetState(dsInsert);
try
DoOnNewRecord;
except
UpdateCursorPos;
FreeFieldBuffers;
SetState(dsBrowse);
Resync([]);
raise;
end;
FModified := False;
DataEvent(deDataSetChange, 0);
DoAfterInsert;
end;
procedure TDataSet.AddRecord(const Values: array of const; Append: Boolean);
var
Buffer: PChar;
begin
BeginInsertAppend;
if not Append then UpdateCursorPos;
DisableControls;
try
MoveBuffer(FRecordCount, FActiveRecord);
try
Buffer := ActiveBuffer;
InitRecord(Buffer);
FState := dsInsert;
try
DoOnNewRecord;
DoAfterInsert;
SetFields(Values);
DoBeforePost;
if Append then
Check(DbiAppendRecord(FHandle, Buffer)) else
Check(DbiInsertRecord(FHandle, dbiNoLock, Buffer));
finally
FreeFieldBuffers;
FState := dsBrowse;
FModified := False;
end;
except
MoveBuffer(FActiveRecord, FRecordCount);
raise;
end;
Resync([]);
DoAfterPost;
finally
EnableControls;
end;
end;
procedure TDataSet.InsertRecord(const Values: array of const);
begin
AddRecord(Values, False);
end;
procedure TDataSet.AppendRecord(const Values: array of const);
begin
AddRecord(Values, True);
end;
procedure TDataSet.CheckOperation(Operation: TDataOperation;
ErrorEvent: TDataSetErrorEvent);
var
Done: Boolean;
Action: TDataAction;
begin
Done := False;
repeat
try
UpdateCursorPos;
Check(Operation);
Done := True;
except
on E: EDatabaseError do
begin
Action := daFail;
if Assigned(ErrorEvent) then ErrorEvent(Self, E, Action);
if Action = daFail then raise;
if Action = daAbort then SysUtils.Abort;
end;
end;
until Done;
end;
function TDataSet.EditRecord: DBIResult;
begin
Result := DbiGetRecord(FHandle, dbiWriteLock, ActiveBuffer, nil);
end;
procedure TDataSet.Edit;
begin
if not (State in [dsEdit, dsInsert]) then
if FRecordCount = 0 then Insert else
begin
CheckBrowseMode;
CheckCanModify;
DoBeforeEdit;
CheckOperation(EditRecord, FOnEditError);
GetCalcFields(FActiveRecord);
SetState(dsEdit);
DataEvent(deRecordChange, 0);
DoAfterEdit;
end;
end;
procedure TDataSet.ClearFields;
begin
if not (State in dsEditModes) then DBError(SNotEditing);
DataEvent(deCheckBrowseMode, 0);
DbiInitRecord(FHandle, ActiveBuffer);
if State <> dsSetKey then GetCalcFields(FActiveRecord);
DataEvent(deRecordChange, 0);
end;
procedure TDataSet.CheckRequiredFields;
const
CheckTypes = [ftString, ftSmallint, ftInteger, ftWord, ftBoolean, ftFloat,
ftCurrency, ftBCD, ftDate, ftTime, ftDateTime, ftBytes, ftVarBytes];
var
I: Integer;
begin
for I := 0 to FFields.Count - 1 do
with TField(FFields[I]) do
if Required and not ReadOnly and (FieldKind = fkData) and
(DataType in CheckTypes) and IsNull then
begin
FocusControl;
DBErrorFmt(SFieldRequired, [DisplayName]);
end;
end;
function TDataSet.PostRecord: DBIResult;
begin
if State = dsEdit then
Result := DbiModifyRecord(FHandle, ActiveBuffer, True) else
Result := DbiInsertRecord(FHandle, dbiNoLock, ActiveBuffer);
end;
procedure TDataSet.Post;
begin
UpdateRecord;
case State of
dsEdit, dsInsert:
begin
DataEvent(deCheckBrowseMode, 0);
CheckRequiredFields;
DoBeforePost;
CheckOperation(PostRecord, FOnPostError);
FreeFieldBuffers;
SetState(dsBrowse);
Resync([]);
DoAfterPost;
end;
dsSetKey:
PostKeyBuffer(True);
end;
end;
procedure TDataSet.Cancel;
begin
case State of
dsEdit, dsInsert:
begin
DataEvent(deCheckBrowseMode, 0);
DoBeforeCancel;
UpdateCursorPos;
if State = dsEdit then DbiRelRecordLock(FHandle, False);
FreeFieldBuffers;
SetState(dsBrowse);
Resync([]);
DoAfterCancel;
end;
dsSetKey:
PostKeyBuffer(False);
end;
end;
function TDataSet.DeleteRecord: DBIResult;
begin
Result := DbiDeleteRecord(FHandle, nil);
if Hi(Result) = ERRCAT_NOTFOUND then Result := 0;
end;
procedure TDataSet.Delete;
begin
if State = dsInactive then DBError(SDataSetClosed);
if State in [dsInsert, dsSetKey] then Cancel else
begin
if FRecordCount = 0 then DBError(SDataSetEmpty);
DataEvent(deCheckBrowseMode, 0);
DoBeforeDelete;
CheckOperation(DeleteRecord, FOnDeleteError);
FreeFieldBuffers;
SetState(dsBrowse);
Resync([]);
DoAfterDelete;
end;
end;
function TDataSet.GetBookmark: TBookmark;
begin
Result := nil;
if (State in [dsBrowse, dsEdit, dsInsert]) and (FRecordCount > 0)
and (ActiveBuffer[FBookmarkOfs] = #0) then
begin
Result := StrAlloc(FBookmarkSize);
Move(ActiveBuffer[FBookmarkOfs + 1], Result^, FBookmarkSize);
end;
end;
function TDataset.GetBookmarkStr: TBookmarkStr;
begin
Result := '';
if (State in [dsBrowse, dsEdit, dsInsert]) and (FRecordCount > 0)
and (ActiveBuffer[FBookmarkOfs] = #0) then
begin
SetString(Result, PChar(@ActiveBuffer[FBookmarkOfs + 1]), FBookmarkSize);
end;
end;
procedure TDataSet.GotoBookmark(Bookmark: TBookmark);
begin
if Bookmark <> nil then
begin
CheckBrowseMode;
Check(DbiSetToBookmark(FHandle, Bookmark));
Resync([rmExact, rmCenter]);
end;
end;
procedure TDataset.SetBookmarkStr(const Value: TBookmarkStr);
begin
GotoBookmark(Pointer(Value));
end;
procedure TDataSet.FreeBookmark(Bookmark: TBookmark);
begin
StrDispose(Bookmark);
end;
function TDataSet.GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer;
begin
Result := FKeyBuffers[KeyIndex];
end;
procedure TDataSet.SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean);
begin
CheckBrowseMode;
FKeyBuffer := FKeyBuffers[KeyIndex];
Move(FKeyBuffer^, FKeyBuffers[kiSave]^, SizeOf(TKeyBuffer) + FRecordSize);
if Clear then InitKeyBuffer(FKeyBuffer);
SetState(dsSetKey);
DataEvent(deDataSetChange, 0);
end;
procedure TDataSet.PostKeyBuffer(Commit: Boolean);
begin
DataEvent(deCheckBrowseMode, 0);
if Commit then
FKeyBuffer^.Modified := FModified
else
Move(FKeyBuffers[kiSave]^, FKeyBuffer^, SizeOf(TKeyBuffer) + FRecordSize);
SetState(dsBrowse);
DataEvent(deDataSetChange, 0);
end;
function TDataSet.GetKeyExclusive: Boolean;
begin
CheckSetKeyMode;
Result := FKeyBuffer^.Exclusive;
end;
procedure TDataSet.SetKeyExclusive(Value: Boolean);
begin
CheckSetKeyMode;
FKeyBuffer^.Exclusive := Value;
end;
function TDataSet.GetKeyFieldCount: Integer;
begin
CheckSetKeyMode;
Result := FKeyBuffer^.FieldCount;
end;
procedure TDataSet.SetKeyFieldCount(Value: Integer);
begin
CheckSetKeyMode;
FKeyBuffer^.FieldCount := Value;
end;
procedure TDataSet.SetKeyFields(KeyIndex: TKeyIndex;
const Values: array of const);
var
I: Integer;
begin
if ExpIndex then DBError(SCompositeIndexError);
if FIndexFieldCount = 0 then DBError(SNoFieldIndexes);
Inc(FDisableCount);
FState := dsSetKey;
FModified := False;
FKeyBuffer := InitKeyBuffer(FKeyBuffers[KeyIndex]);
try
for I := 0 to High(Values) do GetIndexField(I).AssignValue(Values[I]);
FKeyBuffer^.FieldCount := High(Values) + 1;
FKeyBuffer^.Modified := FModified;
finally
FState := dsBrowse;
FModified := False;
Dec(FDisableCount);
end;
end;
procedure TDataSet.SetDetailFields(MasterFields: TList);
var
SaveState: TDataSetState;
I: Integer;
begin
Inc(FDisableCount);
SaveState := FState;
FState := dsSetKey;
try
FKeyBuffer := InitKeyBuffer(FKeyBuffers[kiLookup]);
FKeyBuffer^.Modified := True;
for I := 0 to MasterFields.Count - 1 do
GetIndexField(I).Assign(TField(MasterFields[I]));
FKeyBuffer^.FieldCount := MasterFields.Count;
finally
FState := SaveState;
FModified := False;
Dec(FDisableCount);
end;
end;
function TDataSet.SetCursorRange: Boolean;
var
RangeStart, RangeEnd: PKeyBuffer;
StartKey, EndKey: PChar;
IndexBuffer: PChar;
UseStartKey, UseEndKey, UseKey: Boolean;
begin
Result := False;
if not (
BuffersEqual(FKeyBuffers[kiRangeStart], FKeyBuffers[kiCurRangeStart],
SizeOf(TKeyBuffer) + FRecordSize) and
BuffersEqual(FKeyBuffers[kiRangeEnd], FKeyBuffers[kiCurRangeEnd],
SizeOf(TKeyBuffer) + FRecordSize)) then
begin
IndexBuffer := AllocMem(KeySize * 2);
try
UseStartKey := True;
UseEndKey := True;
RangeStart := FKeyBuffers[kiRangeStart];
if RangeStart^.Modified then
begin
StartKey := PChar(RangeStart) + SizeOf(TKeyBuffer);
UseStartKey := DbiExtractKey(Handle, StartKey, IndexBuffer) = 0;
end
else StartKey := nil;
RangeEnd := FKeyBuffers[kiRangeEnd];
if RangeEnd^.Modified then
begin
EndKey := PChar(RangeEnd) + SizeOf(TKeyBuffer);
UseEndKey := DbiExtractKey(Handle, EndKey, IndexBuffer + KeySize) = 0;
end
else EndKey := nil;
UseKey := UseStartKey and UseEndKey;
if UseKey then
begin
if StartKey <> nil then StartKey := IndexBuffer;
if EndKey <> nil then EndKey := IndexBuffer + KeySize;
end;
Check(DbiSetRange(FHandle, UseKey,
RangeStart^.FieldCount, 0, StartKey, not RangeStart^.Exclusive,
RangeEnd^.FieldCount, 0, EndKey, not RangeEnd^.Exclusive));
Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiCurRangeStart]^,
SizeOf(TKeyBuffer) + FRecordSize);
Move(FKeyBuffers[kiRangeEnd]^, FKeyBuffers[kiCurRangeEnd]^,
SizeOf(TKeyBuffer) + FRecordSize);
DestroyLookupCursor;
Result := True;
finally
FreeMem(IndexBuffer, KeySize * 2);
end;
end;
end;
function TDataSet.ResetCursorRange: Boolean;
begin
Result := False;
if FKeyBuffers[kiCurRangeStart]^.Modified or
FKeyBuffers[kiCurRangeEnd]^.Modified then
begin
Check(DbiResetRange(FHandle));
InitKeyBuffer(FKeyBuffers[kiCurRangeStart]);
InitKeyBuffer(FKeyBuffers[kiCurRangeEnd]);
DestroyLookupCursor;
Result := True;
end;
end;
procedure TDataSet.SetLinkRanges(MasterFields: TList);
var
SaveState: TDataSetState;
I: Integer;
begin
Inc(FDisableCount);
SaveState := FState;
FState := dsSetKey;
try
FKeyBuffer := InitKeyBuffer(FKeyBuffers[kiRangeStart]);
FKeyBuffer^.Modified := True;
for I := 0 to MasterFields.Count - 1 do
GetIndexField(I).Assign(TField(MasterFields[I]));
FKeyBuffer^.FieldCount := MasterFields.Count;
finally
FState := SaveState;
FModified := False;
Dec(FDisableCount);
end;
Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiRangeEnd]^,
SizeOf(TKeyBuffer) + FRecordSize);
end;
function TDataSet.GetRecordCount: Longint;
begin
if State = dsInactive then DBError(SDataSetClosed);
Check(DbiGetExactRecordCount(FHandle, Result));
end;
function TDataSet.GetRecordNumber: Longint;
var
BufPtr: PChar;
begin
case State of
dsInactive: DBError(SDataSetClosed);
dsCalcFields: BufPtr := FCalcBuffer
else
BufPtr := ActiveBuffer;
end;
Result := PRecInfo(BufPtr + FRecInfoOfs).RecordNumber;
end;
procedure TDataSet.AllocDelUpdCBBuf(Allocate: Boolean);
begin
if Allocate then
begin
FUpdateCBBuf := AllocMem(SizeOf(DELAYUPDCbDesc));
FUpdateCBBuf.pNewRecBuf := StrAlloc(FRecBufSize);
FUpdateCBBuf.pOldRecBuf := StrAlloc(FRecBufSize);
FUpdateCBBuf.iRecBufSize := FRecordSize;
end else
begin
if Assigned(FUpdateCBBuf) then
begin
StrDispose(FUpdateCBBuf.pNewRecBuf);
StrDispose(FUpdateCBBuf.pOldRecBuf);
DisposeMem(FUpdateCBBuf, SizeOf(DELAYUPDCbDesc));
end;
end;
end;
function TDataSet.UpdateCallbackRequired: Boolean;
begin
Result := FCachedUpdates and (Assigned(FOnUpdateError) or
Assigned(FOnUpdateRecord) or Assigned(FUpdateObject));
end;
function TDataSet.ForceUpdateCallback: Boolean;
begin
Result := FCachedUpdates and (Assigned(FOnUpdateRecord) or
Assigned(FUpdateObject));
end;
procedure TDataSet.SetCachedUpdates(Value: Boolean);
procedure ReAllocBuffers;
var
CursorProps: CurProps;
begin
FreeFieldBuffers;
FreeKeyBuffers;
SetBufListSize(0);
DbiGetCursorProps(FHandle, CursorProps);
FRecordSize := CursorProps.iRecBufSize;
FBookmarkSize := CursorProps.iBookmarkSize;
FRecInfoOfs := FRecordSize + FCalcFieldsSize;
FBookmarkOfs := FRecordSize + FCalcFieldsSize + SizeOf(TRecInfo);
FRecBufSize := FBookmarkOfs + FBookmarkSize + 1;
try
SetBufListSize(FBufferCount + 1);
AllocKeyBuffers;
except
SetState(dsInactive);
CloseCursor;
raise;
end;
end;
begin
if State = dsInActive then
FCachedUpdates := Value
else if FCachedUpdates <> Value then
begin
CheckBrowseMode;
UpdateCursorPos;
if FCachedUpdates then
Check(DbiEndDelayedUpdates(FHandle))
else
Check(DbiBeginDelayedUpdates(FHandle));
FCachedUpdates := Value;
ReAllocBuffers;
AllocDelUpdCBBuf(Value);
SetupCallBack(UpdateCallBackRequired);
Resync([]);
end;
end;
procedure TDataSet.SetupCallBack(Value: Boolean);
begin
if Value then
begin
if (csDesigning in ComponentState) then Exit;
if not Assigned(FUpdateCallback) then
FUpdateCallback := TBDECallback.Create(Self, Self.Handle, cbDELAYEDUPD,
FUpdateCBBuf, SizeOf(DELAYUPDCbDesc), CachedUpdateCallBack, True);
end
else
begin
if Assigned(FUpdateCallback) then
begin
FUpdateCallback.Free;
FUpdateCallback := nil;
end;
end;
end;
function TDataSet.ProcessUpdates(UpdCmd: DBIDelayedUpdCmd): DBIResult;
begin
CheckCachedUpdateMode;
UpdateCursorPos;
Result := DbiApplyDelayedUpdates(Handle, UpdCmd);
Resync([]);
end;
procedure TDataSet.ApplyUpdates;
var
Status: DBIResult;
begin
if State <> dsBrowse then Post;
Status := ProcessUpdates(dbiDelayedUpdPrepare);
if Status <> DBIERR_NONE then
if Status = DBIERR_UPDATEABORT then SysUtils.Abort
else DbiError(Status);
end;
procedure TDataSet.CommitUpdates;
begin
Check(ProcessUpdates(dbiDelayedUpdCommit));
end;
procedure TDataSet.CancelUpdates;
begin
Cancel;
ProcessUpdates(dbiDelayedUpdCancel);
end;
procedure TDataSet.RevertRecord;
var
Status: DBIResult;
begin
if State in dsEditModes then Cancel;
Status := ProcessUpdates(dbiDelayedUpdCancelCurrent);
if not ((Status = DBIERR_NONE) or (Status = DBIERR_NOTSUPPORTED)) then
Check(Status);
end;
function TDataSet.UpdateStatus: TUpdateStatus;
var
BufPtr: PChar;
begin
CheckCachedUpdateMode;
if FState = dsCalcFields then
BufPtr := FCalcBuffer
else
BufPtr := ActiveBuffer;
Result := PRecInfo(BufPtr + FRecInfoOfs).UpdateStatus;
end;
function TDataSet.CachedUpdateCallBack(CBInfo: Pointer): CBRType;
const
CBRetCode: array[TUpdateAction] of CBRType = (cbrAbort, cbrAbort,
cbrSkip, cbrRetry, cbrPartialAssist);
var
UpdateAction: TUpdateAction;
UpdateKind: TUpdateKind;
begin
try
Result := cbrUSEDEF;
FInUpdateCallBack := True;
UpdateAction := uaFail;
UpdateKind := TUpdateKind(ord(FUpdateCBBuf.eDelayUpdOpType)-1);
try
if Assigned(FOnUpdateRecord) then
FOnUpdateRecord(Self, UpdateKind, UpdateAction)
else
if Assigned(FUpdateObject) then
begin
FUpdateObject.Apply(UpdateKind);
UpdateAction := uaApplied;
end
else
DbiError(FUpdateCBBuf.iErrCode);
except
on E: EDatabaseError do
begin
if Assigned(FOnUpdateError) then
FOnUpdateError(Self, E, UpdateKind, UpdateAction)
else
begin
Application.HandleException(Self);
UpdateAction := uaAbort;
end;
end;
end;
Result := CBRetCode[UpdateAction];
if UpdateAction = uaAbort then FUpdateCBBuf.iErrCode := DBIERR_UPDATEABORT;
except
Application.HandleException(Self);
end;
FInUpdateCallBack := False;
end;
function TDataSet.GetUpdateRecordSet: TUpdateRecordTypes;
begin
if Active then
begin
CheckCachedUpdateMode;
Result := TUpdateRecordTypes(Byte(GetIntProp(FHandle, curDELAYUPDDISPLAYOPT)));
end
else
Result := [];
end;
procedure TDataSet.SetUpdateRecordSet(RecordTypes: TUpdateRecordTypes);
begin
CheckCachedUpdateMode;
CheckBrowseMode;
UpdateCursorPos;
Check(DbiSetProp(hDbiObj(Handle), curDELAYUPDDISPLAYOPT, Longint(Byte(RecordTypes))));
Resync([]);
end;
procedure TDataSet.SetUpdateObject(Value: TDataSetUpdateObject);
begin
if Value <> FUpdateObject then
begin
if Assigned(FUpdateObject) and (FUpdateObject.DataSet = Self) then
FUpdateObject.DataSet := nil;
FUpdateObject := Value;
if Assigned(FUpdateObject) then
begin
{ If another dataset already references this updateobject, then
remove the reference }
if Assigned(FUpdateObject.DataSet) and
(FUpdateObject.DataSet <> Self) then
FUpdateObject.DataSet.UpdateObject := nil;
FUpdateObject.DataSet := Self;
end;
end;
end;
procedure TDataSet.SetOnUpdateError(UpdateEvent: TUpdateErrorEvent);
begin
if Active then SetupCallback(UpdateCallBackRequired);
FOnUpdateError := UpdateEvent;
end;
function TDataSet.GetUpdatesPending: Boolean;
begin
Result := GetIntProp(FHandle, curDELAYUPDNUMUPDATES) > 0;
end;
function TDataSet.CreateExprFilter(const Expr: string;
Options: TFilterOptions; Priority: Integer): HDBIFilter;
var
Parser: TExprParser;
begin
Parser := TExprParser.Create(Self, Expr, Options);
try
Check(DbiAddFilter(FHandle, 0, Priority, False, Parser.FilterData,
nil, Result));
finally
Parser.Free;
end;
end;
function TDataSet.CreateFuncFilter(FilterFunc: Pointer;
Priority: Integer): HDBIFilter;
begin
Check(DbiAddFilter(FHandle, Integer(Self), Priority, False, nil,
PFGENFilter(FilterFunc), Result));
end;
function TDataSet.CreateLookupFilter(Fields: TList; const Values: Variant;
Options: TLocateOptions; Priority: Integer): HDBIFilter;
var
I: Integer;
Filter: TFilterExpr;
Expr, Node: PExprNode;
FilterOptions: TFilterOptions;
begin
if loCaseInsensitive in Options then
FilterOptions := [foNoPartialCompare, foCaseInsensitive] else
FilterOptions := [foNoPartialCompare];
Filter := TFilterExpr.Create(Self, FilterOptions);
try
if Fields.Count = 1 then
begin
Node := Filter.NewCompareNode(TField(Fields[0]), canEQ, Values);
Expr := Node;
end else
for I := 0 to Fields.Count - 1 do
begin
Node := Filter.NewCompareNode(TField(Fields[I]), canEQ, Values[I]);
if I = 0 then
Expr := Node else
Expr := Filter.NewNode(enOperator, canAND, Unassigned, Expr, Node);
end;
if loPartialKey in Options then Node^.FPartial := True;
Check(DbiAddFilter(FHandle, 0, Priority, False,
Filter.GetFilterData(Expr), nil, Result));
finally
Filter.Free;
end;
end;
procedure TDataSet.SetFilterHandle(var Filter: HDBIFilter;
Value: HDBIFilter);
begin
if FFiltered then
begin
CursorPosChanged;
DestroyLookupCursor;
DbiSetToBegin(FHandle);
if Filter <> nil then DbiDropFilter(FHandle, Filter);
Filter := Value;
if Filter <> nil then DbiActivateFilter(FHandle, Filter);
end else
Filter := Value;
end;
procedure TDataSet.SetFilterData(const Text: string; Options: TFilterOptions);
var
Filter: HDBIFilter;
begin
if Active then
begin
CheckBrowseMode;
if (FFilterText <> Text) or (FFilterOptions <> Options) then
begin
if Text <> '' then
Filter := CreateExprFilter(Text, Options, 0) else
Filter := nil;
SetFilterHandle(FExprFilter, Filter);
end;
end;
FFilterText := Text;
FFilterOptions := Options;
if Active and FFiltered then First;
end;
procedure TDataSet.SetFilterText(const Value: string);
begin
SetFilterData(Value, FFilterOptions);
end;
procedure TDataSet.SetFilterOptions(Value: TFilterOptions);
begin
SetFilterData(FFilterText, Value);
end;
procedure TDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent);
var
Filter: HDBIFilter;
begin
if Active then
begin
CheckBrowseMode;
if Assigned(FOnFilterRecord) <> Assigned(Value) then
begin
if Assigned(Value) then
Filter := CreateFuncFilter(@TDataSet.RecordFilter, 1) else
Filter := nil;
SetFilterHandle(FFuncFilter, Filter);
end;
FOnFilterRecord := Value;
if FFiltered then First;
end else
FOnFilterRecord := Value;
end;
procedure TDataSet.SetFiltered(Value: Boolean);
begin
if Active then
begin
CheckBrowseMode;
if FFiltered <> Value then
begin
DestroyLookupCursor;
DbiSetToBegin(FHandle);
if Value then ActivateFilters else DeactivateFilters;
FFiltered := Value;
end;
First;
end else
FFiltered := Value;
end;
function TDataSet.FindRecord(Restart, GoForward: Boolean): Boolean;
var
Status: DBIResult;
begin
CheckBrowseMode;
FFound := False;
UpdateCursorPos;
CursorPosChanged;
if not FFiltered then ActivateFilters;
try
if GoForward then
begin
if Restart then Check(DbiSetToBegin(FHandle));
Status := DbiGetNextRecord(FHandle, dbiNoLock, nil, nil);
end else
begin
if Restart then Check(DbiSetToEnd(FHandle));
Status := DbiGetPriorRecord(FHandle, dbiNoLock, nil, nil);
end;
finally
if not FFiltered then DeactivateFilters;
end;
if Status = DBIERR_NONE then
begin
Resync([rmExact, rmCenter]);
FFound := True;
end;
Result := FFound;
end;
function TDataSet.FindFirst: Boolean;
begin
Result := FindRecord(True, True);
end;
function TDataSet.FindLast: Boolean;
begin
Result := FindRecord(True, False);
end;
function TDataSet.FindNext: Boolean;
begin
Result := FindRecord(False, True);
end;
function TDataSet.FindPrior: Boolean;
begin
Result := FindRecord(False, False);
end;
function TDataSet.RecordFilter(RecBuf: Pointer; RecNo: Integer): Smallint;
var
SaveState: TDataSetState;
Accept: Boolean;
begin
SaveState := FState;
FState := dsFilter;
FFilterBuffer := RecBuf;
try
Accept := True;
FOnFilterRecord(Self, Accept);
except
Application.HandleException(Self);
end;
FState := SaveState;
Result := Ord(Accept);
end;
procedure TDataSet.GetFieldList(List: TList; const FieldNames: string);
var
Pos: Integer;
begin
Pos := 1;
while Pos <= Length(FieldNames) do
List.Add(FieldByName(ExtractFieldName(FieldNames, Pos)));
end;
function TDataSet.MapsToIndex(Fields: TList;
CaseInsensitive: Boolean): Boolean;
var
I: Integer;
begin
Result := False;
if CaseInsensitive and not FCaseInsIndex then Exit;
if Fields.Count > FIndexFieldCount then Exit;
for I := 0 to Fields.Count - 1 do
if TField(Fields[I]).FieldNo <> FIndexFieldMap[I] then Exit;
Result := True;
end;
function TDataSet.LocateRecord(const KeyFields: string;
const KeyValues: Variant; Options: TLocateOptions;
SyncCursor: Boolean): Boolean;
var
I, FieldCount, PartialLength: Integer;
Buffer: PChar;
Fields: TList;
LookupCursor: HDBICur;
Filter: HDBIFilter;
Status: DBIResult;
CaseInsensitive: Boolean;
begin
CheckBrowseMode;
CursorPosChanged;
Buffer := FBuffers^[FRecordCount];
Fields := TList.Create;
try
GetFieldList(Fields, KeyFields);
CaseInsensitive := loCaseInsensitive in Options;
if CachedUpdates then
LookupCursor := nil
else
if MapsToIndex(Fields, CaseInsensitive) then
LookupCursor := FHandle else
LookupCursor := GetLookupCursor(KeyFields, CaseInsensitive);
if (LookupCursor <> nil) then
begin
FState := dsFilter;
FFilterBuffer := Buffer;
try
DbiInitRecord(FHandle, Buffer);
FieldCount := Fields.Count;
if FieldCount = 1 then
TField(Fields.First).Value := KeyValues
else
for I := 0 to FieldCount - 1 do
TField(Fields[I]).Value := KeyValues[I];
PartialLength := 0;
if (loPartialKey in Options) and
(TField(Fields.Last).DataType = ftString) then
begin
Dec(FieldCount);
PartialLength := Length(TField(Fields.Last).AsString);
end;
Status := DbiGetRecordForKey(LookupCursor, False, FieldCount,
PartialLength, Buffer, Buffer);
finally
FState := dsBrowse;
end;
if (Status = DBIERR_NONE) and SyncCursor and
(LookupCursor <> FHandle) then
Check(DbiSetToCursor(FHandle, LookupCursor));
end else
begin
Check(DbiSetToBegin(FHandle));
Filter := CreateLookupFilter(Fields, KeyValues, Options, 2);
DbiActivateFilter(FHandle, Filter);
Status := DbiGetNextRecord(FHandle, dbiNoLock, Buffer, nil);
DbiDropFilter(FHandle, Filter);
end;
finally
Fields.Free;
end;
Result := Status = DBIERR_NONE;
end;
function TDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
const ResultFields: string): Variant;
begin
Result := Null;
if LocateRecord(KeyFields, KeyValues, [], False) then
begin
FState := dsCalcFields;
try
FCalcBuffer := FBuffers^[FRecordCount];
FillChar(FCalcBuffer[FRecordSize], FCalcFieldsSize, 0);
CalculateFields;
Result := FieldValues[ResultFields];
finally
FState := dsBrowse;
end;
end;
end;
function TDataSet.Locate(const KeyFields: string;
const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
Result := LocateRecord(KeyFields, KeyValues, Options, True);
if Result then Resync([rmExact, rmCenter]);
end;
function TDataSet.GetLookupCursor(const KeyFields: string;
CaseInsensitive: Boolean): HDBICur;
begin
Result := nil;
end;
procedure TDataSet.DestroyLookupCursor;
begin
end;
procedure TDataSet.DoAfterCancel;
begin
if Assigned(FAfterCancel) then FAfterCancel(Self);
end;
procedure TDataSet.DoAfterClose;
begin
if Assigned(FAfterClose) then FAfterClose(Self);
end;
procedure TDataSet.DoAfterDelete;
begin
if Assigned(FAfterDelete) then FAfterDelete(Self);
end;
procedure TDataSet.DoAfterEdit;
begin
if Assigned(FAfterEdit) then FAfterEdit(Self);
end;
procedure TDataSet.DoAfterInsert;
begin
if Assigned(FAfterInsert) then FAfterInsert(Self);
end;
procedure TDataSet.DoAfterOpen;
begin
if Assigned(FAfterOpen) then FAfterOpen(Self);
end;
procedure TDataSet.DoAfterPost;
begin
if Assigned(FAfterPost) then FAfterPost(Self);
end;
procedure TDataSet.DoBeforeCancel;
begin
if Assigned(FBeforeCancel) then FBeforeCancel(Self);
end;
procedure TDataSet.DoBeforeClose;
begin
if Assigned(FBeforeClose) then FBeforeClose(Self);
end;
procedure TDataSet.DoBeforeDelete;
begin
if Assigned(FBeforeDelete) then FBeforeDelete(Self);
end;
procedure TDataSet.DoBeforeEdit;
begin
if Assigned(FBeforeEdit) then FBeforeEdit(Self);
end;
procedure TDataSet.DoBeforeInsert;
begin
if Assigned(FBeforeInsert) then FBeforeInsert(Self);
end;
procedure TDataSet.DoBeforeOpen;
begin
if Assigned(FBeforeOpen) then FBeforeOpen(Self);
end;
procedure TDataSet.DoBeforePost;
begin
if Assigned(FBeforePost) then FBeforePost(Self);
end;
procedure TDataSet.DoOnCalcFields;
begin
if Assigned(FOnCalcFields) then FOnCalcFields(Self);
end;
procedure TDataSet.DoOnNewRecord;
begin
if Assigned(FOnNewRecord) then FOnNewRecord(Self);
end;
function TDataSet.YieldCallBack(CBInfo: Pointer): CBRType;
var
AbortQuery: Boolean;
begin
AbortQuery := False;
if Assigned(OnServerYield) and (FCBYieldStep <> cbYieldLast) then
OnServerYield(Self, AbortQuery);
if AbortQuery then
Result := cbrABORT else
Result := cbrUSEDEF;
end;
{ TDBDataSet }
procedure TDBDataSet.OpenCursor;
begin
SetDBFlag(dbfOpened, True);
inherited OpenCursor;
SetUpdateMode(FUpdateMode);
end;
procedure TDBDataSet.CloseCursor;
begin
inherited CloseCursor;
SetDBFlag(dbfOpened, False);
end;
procedure TDBDataSet.CheckDBSessionName;
var
S: TSession;
Database: TDatabase;
begin
if (SessionName <> '') and (DatabaseName <> '') then
begin
S := Sessions.FindSession(SessionName);
if Assigned(S) and not Assigned(S.FindDatabase(DatabaseName)) then
begin
Database := DB.Session.FindDatabase(DatabaseName);
if Assigned(Database) then Database.CheckSessionName(True);
end;
end;
end;
function TDBDataSet.CheckOpen(Status: DBIResult): Boolean;
begin
case Status of
DBIERR_NONE:
Result := True;
DBIERR_NOTSUFFTABLERIGHTS:
begin
if not FDatabase.Session.GetPassword then DbiError(Status);
Result := False;
end;
else
DbiError(Status);
end;
end;
procedure TDBDataSet.Disconnect;
begin
Close;
end;
function TDBDataSet.GetDBFlag(Flag: Integer): Boolean;
begin
Result := Flag in FDBFlags;
end;
procedure TDBDataSet.SetDBFlag(Flag: Integer; Value: Boolean);
begin
if Value then
begin
if not (Flag in FDBFlags) then
begin
if FDBFlags = [] then
begin
CheckDBSessionName;
FDatabase := Sessions.List[SessionName].OpenDatabase(FDatabaseName);
FDatabase.FDataSets.Add(Self);
SetLocale(FDatabase.Locale);
end;
Include(FDBFlags, Flag);
end;
end else
begin
if Flag in FDBFlags then
begin
Exclude(FDBFlags, Flag);
if FDBFlags = [] then
begin
SetLocale(DBLocale);
FDatabase.FDataSets.Remove(Self);
FDatabase.Session.CloseDatabase(FDatabase);
FDatabase := nil;
end;
end;
end;
end;
function TDBDataSet.GetDBHandle: HDBIDB;
begin
if FDatabase <> nil then
Result := FDatabase.Handle else
Result := nil;
end;
function TDBDataSet.GetDBLocale: TLocale;
begin
if Database <> nil then
Result := Database.Locale else
Result := nil;
end;
function TDBDataSet.GetDBSession: TSession;
begin
if (FDatabase <> nil) then
Result := FDatabase.Session else
Result := Sessions.FindSession(SessionName);
if Result = nil then Result := DB.Session;
end;
procedure TDBDataSet.SetDatabaseName(const Value: string);
begin
if FDatabaseName <> Value then
begin
CheckInactive;
if FDatabase <> nil then DBError(SDatabaseOpen);
FDatabaseName := Value;
DataEvent(dePropertyChange, 0);
end;
end;
procedure TDBDataSet.SetSessionName(const Value: string);
begin
CheckInactive;
FSessionName := Value;
DataEvent(dePropertyChange, 0);
end;
procedure TDBDataSet.SetUpdateMode(const Value: TUpdateMode);
begin
if (FHandle <> nil) and Database.IsSQLBased and CanModify then
Check(DbiSetProp(hDbiObj(FHandle), curUPDLOCKMODE, Longint(Value)));
FUpdateMode := Value;
end;
{ TField }
constructor TField.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FVisible := True;
end;
destructor TField.Destroy;
begin
if FDataSet <> nil then
begin
FDataSet.Close;
FDataSet.RemoveField(Self);
end;
inherited Destroy;
end;
procedure TField.AccessError(const TypeName: string);
begin
DBErrorFmt(SFieldAccessError, [DisplayName, TypeName]);
end;
procedure TField.Assign(Source: TPersistent);
begin
if Source = nil then
begin
Clear;
Exit;
end;
if Source is TField then
begin
Value := TField(Source).Value;
Exit;
end;
inherited Assign(Source);
end;
procedure TField.AssignValue(const Value: TVarRec);
procedure Error;
begin
DBErrorFmt(SFieldValueError, [DisplayName]);
end;
begin
with Value do
case VType of
vtInteger:
AsInteger := VInteger;
vtBoolean:
AsBoolean := VBoolean;
vtChar:
AsString := VChar;
vtExtended:
AsFloat := VExtended^;
vtString:
AsString := VString^;
vtPointer:
if VPointer <> nil then Error;
vtPChar:
AsString := VPChar;
vtObject:
if (VObject = nil) or (VObject is TPersistent) then
Assign(TPersistent(VObject))
else
Error;
vtAnsiString:
AsString := string(VAnsiString);
vtCurrency:
AsCurrency := VCurrency^;
vtVariant:
if not VarIsEmpty(VVariant^) then AsVariant := VVariant^;
else
Error;
end;
end;
procedure TField.Bind(Binding: Boolean);
begin
if FFieldKind = fkLookup then
if Binding then
begin
if (FLookupDataSet = nil) or (FKeyFields = '') or
(FLookupKeyFields = '') or (FLookupResultField = '') then
DBErrorFmt(SLookupInfoError, [DisplayName]);
FDataSet.CheckFieldNames(FKeyFields);
FLookupDataSet.Open;
FLookupDataSet.CheckFieldNames(FLookupKeyFields);
FLookupDataSet.FieldByName(FLookupResultField);
end;
end;
procedure TField.CalcLookupValue;
begin
if (FLookupDataSet <> nil) and FLookupDataSet.Active then
Value := FLookupDataSet.Lookup(FLookupKeyFields,
FDataSet.FieldValues[FKeyFields], FLookupResultField);
end;
procedure TField.Change;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TField.CheckInactive;
begin
if FDataSet <> nil then FDataSet.CheckInactive;
end;
procedure TField.Clear;
begin
SetData(nil);
end;
procedure TField.DataChanged;
begin
FDataSet.DataEvent(deFieldChange, Longint(Self));
end;
procedure TField.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Assigned(Filer.Ancestor) then
Result := AnsiCompareText(FAttributeSet, TField(Filer.Ancestor).FAttributeSet) <> 0
else
Result := FAttributeSet <> '';
end;
begin
Filer.DefineProperty('AttributeSet', ReadAttributeSet, WriteAttributeSet,
DoWrite);
end;
procedure TField.FocusControl;
var
Field: TField;
begin
if (FDataSet <> nil) and FDataSet.Active then
begin
Field := Self;
FDataSet.DataEvent(deFocusControl, Longint(@Field));
end;
end;
procedure TField.FreeBuffers;
begin
end;
function TField.GetAsBoolean: Boolean;
begin
AccessError('Boolean');
end;
function TField.GetAsCurrency: Currency;
begin
Result := GetAsFloat;
end;
function TField.GetAsDateTime: TDateTime;
begin
AccessError('DateTime');
end;
function TField.GetAsFloat: Double;
begin
AccessError('Float');
end;
function TField.GetAsInteger: Longint;
begin
AccessError('Integer');
end;
function TField.GetAsString: string;
var
I, L: Integer;
S: string[63];
begin
S := ClassName;
I := 1;
L := Length(S);
if S[1] = 'T' then I := 2;
if (L >= 5) and (CompareText(Copy(S, L - 4, 5), 'FIELD') = 0) then Dec(L, 5);
FmtStr(Result, '(%s)', [Copy(S, I, L + 1 - I)]);
end;
function TField.GetAsVariant: Variant;
begin
AccessError('Variant');
end;
function TField.GetCalculated: Boolean;
begin
Result := FFieldKind = fkCalculated;
end;
function TField.GetBDECalcField: Boolean;
begin
if FieldNo >= 0 then
Result := DataSet.FieldDefs.Find(FieldName).BDECalcField
else Result := False;
end;
function TField.GetCanModify: Boolean;
begin
if FieldNo > 0 then
if DataSet.State <> dsSetKey then
Result := not ReadOnly and DataSet.CanModify
else
Result := (DataSet.FIndexFieldCount = 0) or IsIndexField
else
Result := False;
end;
function TField.GetData(Buffer: Pointer): Boolean;
var
IsBlank: LongBool;
RecBuf: PChar;
begin
if FDataSet = nil then DBErrorFmt(SDataSetMissing, [DisplayName]);
Result := False;
with FDataSet do
begin
case State of
dsSetKey: RecBuf := PChar(FKeyBuffer) + SizeOf(TKeyBuffer);
dsCalcFields: RecBuf := FCalcBuffer;
dsUpdateNew: RecBuf := FUpdateCBBuf.pNewRecBuf;
dsUpdateOld: RecBuf := FUpdateCBBuf.pOldRecBuf;
dsFilter: RecBuf := FFilterBuffer;
else
if FActiveRecord >= FRecordCount then Exit;
RecBuf := FBuffers^[FActiveRecord];
end;
if FieldNo > 0 then
if FValidating then
begin
Result := LongBool(FValueBuffer);
if Result and (Buffer <> nil) then
Move(FValueBuffer^, Buffer^, DataSize);
end else
begin
Check(DbiGetField(FHandle, FieldNo, RecBuf, Buffer, IsBlank));
Result := not IsBlank;
end
else
if (FieldNo < 0) and (State <> dsSetKey) then
begin
Inc(RecBuf, FRecordSize + FOffset);
Result := Boolean(RecBuf[0]);
if Result and (Buffer <> nil) then
Move(RecBuf[1], Buffer^, DataSize);
end;
end;
end;
function TField.GetDefaultWidth: Integer;
begin
Result := 10;
end;
function TField.GetDisplayLabel: string;
begin
Result := GetDisplayName;
end;
function TField.GetDisplayName: string;
begin
if FDisplayLabel <> '' then
Result := FDisplayLabel else
Result := FFieldName;
end;
function TField.GetDisplayText: string;
begin
Result := '';
if Assigned(FOnGetText) then
FOnGetText(Self, Result, True) else
GetText(Result, True);
end;
function TField.GetDisplayWidth: Integer;
begin
if FDisplayWidth > 0 then
Result := FDisplayWidth else
Result := GetDefaultWidth;
end;
function TField.GetEditText: string;
begin
Result := '';
if Assigned(FOnGetText) then
FOnGetText(Self, Result, False) else
GetText(Result, False);
end;
function TField.GetIndex: Integer;
begin
if FDataSet <> nil then
Result := FDataSet.FFields.IndexOf(Self) else
Result := -1;
end;
function TField.GetIsIndexField: Boolean;
var
I: Integer;
begin
Result := False;
if FFieldNo > 0 then
for I := 0 to FDataSet.FIndexFieldCount - 1 do
if FDataSet.FIndexFieldMap[I] = FFieldNo then
begin
Result := True;
Exit;
end;
end;
function TField.GetIsNull: Boolean;
begin
Result := not GetData(nil);
end;
function TField.GetLookup: Boolean;
begin
Result := FFieldKind = fkLookup;
end;
procedure TField.GetText(var Text: string; DisplayText: Boolean);
begin
Text := GetAsString;
end;
function TField.HasParent: Boolean;
begin
HasParent := True;
end;
function TField.GetNewValue: Variant;
begin
FDataSet.CheckCachedUpdateMode;
if FDataSet.FInUpdateCallBack then
Result := GetUpdateValue(dsUpdateNew)
else
Result := Value;
end;
function TField.GetOldValue: Variant;
begin
with FDataSet do
begin
CheckCachedUpdateMode;
if FInUpdateCallBack and not (Self is TBlobField) then
Result := GetUpdateValue(dsUpdateOld)
else
begin
UpdateCursorPos;
Check(DbiSetProp(hDbiObj(Handle), curDELAYUPDGETOLDRECORD, Longint(True)));
try
Check(DbiGetRecord(FHandle, dbiNoLock, FUpdateCBBuf.pOldRecBuf, nil));
Result := GetUpdateValue(dsUpdateOld);
finally
DbiSetProp(hDbiObj(Handle), curDELAYUPDGETOLDRECORD, Longint(False));
end;
end;
end;
end;
function TField.GetUpdateValue(ValueState: TDataSetState): Variant;
var
SaveState: TDataSetState;
begin
if FieldKind <> fkData then
DBErrorFmt(SOldNewNonData, [FieldName]);
SaveState := FDataset.FState;
FDataSet.FState := ValueState;
try
Result := GetAsVariant;
finally
FDataSet.FState := SaveState;
end;
end;
function TField.GetParentComponent: TComponent;
begin
Result := DataSet;
end;
procedure TField.SetParentComponent(AParent: TComponent);
begin
if not (csLoading in ComponentState) then DataSet := AParent as TDataSet;
end;
function TField.IsValidChar(InputChar: Char): Boolean;
begin
Result := True;
end;
function TField.IsDisplayLabelStored: Boolean;
begin
Result := FDisplayLabel <> '';
end;
function TField.IsDisplayWidthStored: Boolean;
begin
Result := FDisplayWidth > 0;
end;
procedure TField.Notification(AComponent: TComponent;
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FLookupDataSet) then
FLookupDataSet := nil;
end;
procedure TField.PropertyChanged(LayoutAffected: Boolean);
const
Events: array[Boolean] of TDataEvent = (deDataSetChange, deLayoutChange);
begin
if (FDataSet <> nil) and FDataSet.Active then
FDataSet.DataEvent(Events[LayoutAffected], 0);
end;
procedure TField.ReadAttributeSet(Reader: TReader);
begin
FAttributeSet := Reader.ReadString;
end;
procedure TField.ReadState(Reader: TReader);
begin
inherited ReadState(Reader);
if Reader.Parent is TDataSet then DataSet := TDataSet(Reader.Parent);
end;
procedure TField.SetAsBoolean(Value: Boolean);
begin
AccessError('Boolean');
end;
procedure TField.SetAsCurrency(Value: Currency);
begin
SetAsFloat(Value);
end;
procedure TField.SetAsDateTime(Value: TDateTime);
begin
AccessError('DateTime');
end;
procedure TField.SetAsFloat(Value: Double);
begin
AccessError('Float');
end;
procedure TField.SetAsInteger(Value: Longint);
begin
AccessError('Integer');
end;
procedure TField.SetAsString(const Value: string);
begin
AccessError('String');
end;
procedure TField.SetAsVariant(const Value: Variant);
begin
if TVarData(Value).VType = varNull then
Clear
else
try
SetVarValue(Value);
except
on EVariantError do DBErrorFmt(SFieldValueError, [DisplayName]);
end;
end;
procedure TField.SetAlignment(Value: TAlignment);
begin
if FAlignment <> Value then
begin
FAlignment := Value;
PropertyChanged(False);
end;
end;
procedure TField.SetCalculated(Value: Boolean);
begin
if Value then
FieldKind := fkCalculated
else if not Lookup then FieldKind := fkData;
end;
procedure TField.SetData(Buffer: Pointer);
var
RecBuf: PChar;
begin
if FDataSet = nil then DBErrorFmt(SDataSetMissing, [DisplayName]);
with FDataSet do
begin
case State of
dsEdit, dsInsert: RecBuf := FBuffers^[FActiveRecord];
dsSetKey:
begin
RecBuf := PChar(FKeyBuffer) + SizeOf(TKeyBuffer);
if (FieldNo < 0) or (FIndexFieldCount > 0) and not IsIndexField then
DBErrorFmt(SNotIndexField, [DisplayName]);
end;
dsCalcFields: RecBuf := FCalcBuffer;
dsUpdateNew: RecBuf := FUpdateCBBuf.pNewRecBuf;
dsUpdateOld: DBError(SNoOldValueUpdate);
dsFilter: RecBuf := FFilterBuffer;
else
DBError(SNotEditing);
end;
if FieldNo > 0 then
begin
if (State <> dsSetKey) and (State <> dsFilter) and ReadOnly then
DBErrorFmt(SFieldReadOnly, [DisplayName]);
if State = dsCalcFields then DBError(SNotEditing);
if Assigned(FOnValidate) then
begin
FValueBuffer := Buffer;
FValidating := True;
try
FOnValidate(Self);
finally
FValidating := False;
end;
end;
if not BDECalcField then
Check(DbiPutField(FHandle, FieldNo, RecBuf, Buffer));
end else
begin
Inc(RecBuf, FRecordSize + FOffset);
Boolean(RecBuf[0]) := LongBool(Buffer);
if Boolean(RecBuf[0]) then Move(Buffer^, RecBuf[1], DataSize);
end;
if (State <> dsCalcFields) and (State <> dsFilter) then
DataEvent(deFieldChange, Longint(Self));
end;
end;
procedure TField.SetDataSet(ADataSet: TDataSet);
begin
if ADataset <> FDataset then
begin
if FDataSet <> nil then FDataSet.CheckInactive;
if ADataSet <> nil then
begin
ADataSet.CheckInactive;
ADataSet.CheckFieldName(FFieldName);
end;
if FDataSet <> nil then FDataSet.RemoveField(Self);
if ADataSet <> nil then ADataSet.AddField(Self);
end;
end;
procedure TField.SetDataType(Value: TFieldType);
begin
FDataType := Value;
UpdateDataSize;
end;
procedure TField.SetDisplayLabel(Value: string);
begin
if Value = FFieldName then Value := '';
if FDisplayLabel <> Value then
begin
FDisplaylabel := Value;
PropertyChanged(True);
end;
end;
procedure TField.SetDisplayWidth(Value: Integer);
begin
if FDisplayWidth <> Value then
begin
FDisplayWidth := Value;
PropertyChanged(True);
end;
end;
procedure TField.SetEditMask(const Value: string);
begin
FEditMask := Value;
PropertyChanged(False);
end;
procedure TField.SetEditText(const Value: string);
begin
if Assigned(FOnSetText) then FOnSetText(Self, Value) else SetText(Value);
end;
procedure TField.SetFieldKind(Value: TFieldKind);
begin
if FFieldKind <> Value then
begin
CheckInactive;
FFieldKind := Value;
end;
end;
procedure TField.SetFieldName(const Value: string);
begin
CheckInactive;
if FDataSet <> nil then FDataSet.CheckFieldName(Value);
FFieldName := Value;
if FDisplayLabel = Value then FDisplayLabel := '';
if FDataSet <> nil then FDataSet.DataEvent(deFieldListChange, 0);
end;
procedure TField.SetFieldType(Value: TFieldType);
begin
end;
procedure TField.SetIndex(Value: Integer);
var
CurIndex, Count: Integer;
begin
CurIndex := GetIndex;
if CurIndex >= 0 then
begin
Count := FDataSet.FFields.Count;
if Value < 0 then Value := 0;
if Value >= Count then Value := Count - 1;
if Value <> CurIndex then
begin
FDataSet.FFields.Delete(CurIndex);
FDataSet.FFields.Insert(Value, Self);
PropertyChanged(True);
FDataSet.DataEvent(deFieldListChange, 0);
end;
end;
end;
procedure TField.SetLookup(Value: Boolean);
begin
if Value then
FieldKind := fkLookup
else if not Calculated then FieldKind := fkData;
end;
procedure TField.SetLookupDataSet(Value: TDataSet);
begin
CheckInactive;
if (Value <> nil) and (Value = FDataSet) then DBError(SCircularDataLink);
FLookupDataSet := Value;
end;
procedure TField.SetLookupKeyFields(const Value: string);
begin
CheckInactive;
FLookupKeyFields := Value;
end;
procedure TField.SetLookupResultField(const Value: string);
begin
CheckInactive;
FLookupResultField := Value;
end;
procedure TField.SetKeyFields(const Value: string);
begin
CheckInactive;
FKeyFields := Value;
end;
procedure TField.SetNewValue(const Value: Variant);
begin
FDataSet.FState := dsUpdateNew;
try
SetAsVariant(Value);
finally
FDataSet.FState := dsBrowse;
end;
end;
procedure TField.SetSize(Value: Word);
begin
CheckInactive;
CheckTypeSize(DataType, Value);
FSize := Value;
UpdateDataSize;
end;
procedure TField.SetText(const Value: string);
begin
SetAsString(Value);
end;
procedure TField.SetVarValue(const Value: Variant);
begin
AccessError('Variant');
end;
procedure TField.SetVisible(Value: Boolean);
begin
if FVisible <> Value then
begin
FVisible := Value;
PropertyChanged(True);
end;
end;
procedure TField.UpdateDataSize;
begin
case FDataType of
ftSmallint, ftWord, ftBoolean:
FDataSize := 2;
ftInteger, ftDate, ftTime, ftAutoInc:
FDataSize := 4;
ftFloat, ftCurrency, ftDateTime:
FDataSize := 8;
ftBCD:
FDataSize := 34;
ftBytes:
FDataSize := Size;
ftVarBytes:
FDataSize := Size + 2;
ftString:
FDataSize := Size + 1;
else
FDataSize := 0;
end;
end;
procedure TField.WriteAttributeSet(Writer: TWriter);
begin
Writer.WriteString(FAttributeSet);
end;
{ TDataSource }
constructor TDataSource.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDataLinks := TList.Create;
FEnabled := True;
FAutoEdit := True;
end;
destructor TDataSource.Destroy;
begin
FOnStateChange := nil;
SetDataSet(nil);
while FDataLinks.Count > 0 do RemoveDataLink(FDataLinks.Last);
FDataLinks.Free;
inherited Destroy;
end;
procedure TDataSource.Edit;
begin
if AutoEdit and (State = dsBrowse) then DataSet.Edit;
end;
procedure TDataSource.SetState(Value: TDataSetState);
var
PriorState: TDataSetState;
begin
if FState <> Value then
begin
PriorState := FState;
FState := Value;
NotifyDataLinks(deUpdateState, 0);
if not (csDestroying in ComponentState) then
begin
if Assigned(FOnStateChange) then FOnStateChange(Self);
if PriorState = dsInactive then
if Assigned(FOnDataChange) then FOnDataChange(Self, nil);
end;
end;
end;
procedure TDataSource.UpdateState;
begin
if Enabled and (DataSet <> nil) then
SetState(DataSet.State) else
SetState(dsInactive);
end;
function TDataSource.IsLinkedTo(DataSet: TDataSet): Boolean;
var
DataSource: TDataSource;
begin
Result := True;
while DataSet <> nil do
begin
DataSource := DataSet.GetDataSource;
if DataSource = nil then Break;
if DataSource = Self then Exit;
DataSet := DataSource.DataSet;
end;
Result := False;
end;
procedure TDataSource.SetDataSet(ADataSet: TDataSet);
begin
if IsLinkedTo(ADataSet) then DBError(SCircularDataLink);
if FDataSet <> nil then FDataSet.RemoveDataSource(Self);
if ADataSet <> nil then ADataSet.AddDataSource(Self);
end;
procedure TDataSource.SetEnabled(Value: Boolean);
begin
FEnabled := Value;
UpdateState;
end;
procedure TDataSource.AddDataLink(DataLink: TDataLink);
begin
FDataLinks.Add(DataLink);
DataLink.FDataSource := Self;
if DataSet <> nil then DataSet.UpdateBufferCount;
DataLink.UpdateState;
end;
procedure TDataSource.RemoveDataLink(DataLink: TDataLink);
begin
DataLink.FDataSource := nil;
FDataLinks.Remove(DataLink);
DataLink.UpdateState;
if DataSet <> nil then DataSet.UpdateBufferCount;
end;
procedure TDataSource.NotifyDataLinks(Event: TDataEvent; Info: Longint);
var
I: Integer;
begin
for I := 0 to FDataLinks.Count - 1 do
with TDataLink(FDataLinks[I]) do
if FBufferCount = 1 then DataEvent(Event, Info);
for I := 0 to FDataLinks.Count - 1 do
with TDataLink(FDataLinks[I]) do
if FBufferCount > 1 then DataEvent(Event, Info);
end;
procedure TDataSource.DataEvent(Event: TDataEvent; Info: Longint);
begin
if Event = deUpdateState then UpdateState else
if FState <> dsInactive then
begin
NotifyDataLinks(Event, Info);
case Event of
deFieldChange:
if Assigned(FOnDataChange) then FOnDataChange(Self, TField(Info));
deRecordChange, deDataSetChange, deDataSetScroll, deLayoutChange:
if Assigned(FOnDataChange) then FOnDataChange(Self, nil);
deUpdateRecord:
if Assigned(FOnUpdateData) then FOnUpdateData(Self);
end;
end;
end;
{ TDataLink }
constructor TDataLink.Create;
begin
inherited Create;
FBufferCount := 1;
end;
destructor TDataLink.Destroy;
begin
FActive := False;
FEditing := False;
FDataSourceFixed := False;
SetDataSource(nil);
inherited Destroy;
end;
procedure TDataLink.UpdateRange;
var
Min, Max: Integer;
begin
Min := DataSet.FActiveRecord - FBufferCount + 1;
if Min < 0 then Min := 0;
Max := DataSet.FBufferCount - FBufferCount;
if Max < 0 then Max := 0;
if Max > DataSet.FActiveRecord then Max := DataSet.FActiveRecord;
if FFirstRecord < Min then FFirstRecord := Min;
if FFirstRecord > Max then FFirstRecord := Max;
end;
function TDataLink.GetDataSet: TDataSet;
begin
if DataSource <> nil then Result := DataSource.DataSet else Result := nil;
end;
procedure TDataLink.SetDataSource(ADataSource: TDataSource);
begin
if FDataSource <> ADataSource then
begin
if FDataSourceFixed then DBError(SDataSourceChange);
if FDataSource <> nil then FDataSource.RemoveDataLink(Self);
if ADataSource <> nil then ADataSource.AddDataLink(Self);
end;
end;
procedure TDataLink.SetReadOnly(Value: Boolean);
begin
if FReadOnly <> Value then
begin
FReadOnly := Value;
UpdateState;
end;
end;
procedure TDataLink.SetActive(Value: Boolean);
begin
if FActive <> Value then
begin
FActive := Value;
if Value then UpdateRange else FFirstRecord := 0;
ActiveChanged;
end;
end;
procedure TDataLink.SetEditing(Value: Boolean);
begin
if FEditing <> Value then
begin
FEditing := Value;
EditingChanged;
end;
end;
procedure TDataLink.UpdateState;
begin
SetActive((DataSource <> nil) and (DataSource.State <> dsInactive));
SetEditing((DataSource <> nil) and (DataSource.State in dsEditModes) and
not FReadOnly);
end;
procedure TDataLink.UpdateRecord;
begin
FUpdating := True;
try
UpdateData;
finally
FUpdating := False;
end;
end;
function TDataLink.Edit: Boolean;
begin
if not FReadOnly and (DataSource <> nil) then DataSource.Edit;
Result := FEditing;
end;
function TDataLink.GetActiveRecord: Integer;
begin
if DataSource.State = dsSetKey then
Result := 0 else
Result := DataSource.DataSet.FActiveRecord - FFirstRecord;
end;
procedure TDataLink.SetActiveRecord(Value: Integer);
begin
if DataSource.State <> dsSetKey then
DataSource.DataSet.FActiveRecord := Value + FFirstRecord;
end;
procedure TDataLink.SetBufferCount(Value: Integer);
begin
if FBufferCount <> Value then
begin
FBufferCount := Value;
if Active then
begin
UpdateRange;
DataSet.UpdateBufferCount;
UpdateRange;
end;
end;
end;
function TDataLink.GetRecordCount: Integer;
begin
if DataSource.State = dsSetKey then Result := 1 else
begin
Result := DataSource.DataSet.FRecordCount;
if Result > FBufferCount then Result := FBufferCount;
end;
end;
procedure TDataLink.DataEvent(Event: TDataEvent; Info: Longint);
var
Active, First, Last, Count: Integer;
begin
if Event = deUpdateState then UpdateState else
if FActive then
case Event of
deFieldChange, deRecordChange:
if not FUpdating then RecordChanged(TField(Info));
deDataSetChange, deDataSetScroll, deLayoutChange:
begin
Count := 0;
if DataSource.State <> dsSetKey then
begin
Active := DataSource.DataSet.FActiveRecord;
First := FFirstRecord + Info;
Last := First + FBufferCount - 1;
if Active > Last then Count := Active - Last else
if Active < First then Count := Active - First;
FFirstRecord := First + Count;
end;
case Event of
deDataSetChange: DataSetChanged;
deDataSetScroll: DataSetScrolled(Count);
deLayoutChange: LayoutChanged;
end;
end;
deUpdateRecord:
UpdateRecord;
deCheckBrowseMode:
CheckBrowseMode;
deFocusControl:
FocusControl(TFieldRef(Info));
end;
end;
procedure TDataLink.ActiveChanged;
begin
end;
procedure TDataLink.CheckBrowseMode;
begin
end;
procedure TDataLink.DataSetChanged;
begin
RecordChanged(nil);
end;
procedure TDataLink.DataSetScrolled(Distance: Integer);
begin
DataSetChanged;
end;
procedure TDataLink.EditingChanged;
begin
end;
procedure TDataLink.FocusControl(Field: TFieldRef);
begin
end;
procedure TDataLink.LayoutChanged;
begin
DataSetChanged;
end;
procedure TDataLink.RecordChanged(Field: TField);
begin
end;
procedure TDataLink.UpdateData;
begin
end;
initialization
Sessions := TSessionList.Create;
Session := TSession.Create(nil);
Session.SessionName := 'Default';
finalization
Sessions.Free;
BDEInitProcs.Free;
FreeTimer;
end.