{*********************************************************} {* FlashFiler: Remote Server Engine Classes *} {*********************************************************} (* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is TurboPower FlashFiler * * The Initial Developer of the Original Code is * TurboPower Software * * Portions created by the Initial Developer are Copyright (C) 1996-2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * ***** END LICENSE BLOCK ***** *) {$I ffdefine.inc} unit ffclreng; interface uses Windows, dialogs, Classes, SysUtils, ffllbase, fflldict, ffdtmsgq, ffllcomm, ffllcomp, fflleng, ffllexcp, ffllreq, ffnetmsg, ffsrbde, ffsrintm, ffdbbase; type {forward declarations} TFFRemoteServerEngine = class; {The TffRemoteServerEngine implements the TFFBaseServerEngine abstract methods. It's method calls will initiate the process that will format a message request to be sent to a remote server via a transport. The TffRemoteServerEngine methods sometimes pass buffers without passing the buffer length. However, the length must be known in order for the message to be sent. It is also possible for the TffRemoteServerEngine to be accessed by multiple threads. We want to make sure that messages for one thread don't wind up with another thread. To handle cases such as these, the TffRemoteServerEngine needs to track information specific to a cursor and client, respectively. To this end we have created proxy classes to hold the information. For example, a TffProxyCursor holds information specific to an open cursor. A TffProxyClient holds information specific to an open client. The TffRemoteServerEngine creates an instance of a proxy class when its equivalent server-side object is opened. Instead of returning the server-side object's ID to the object(s) using the remote engine, the remote engine returns the pointers to its proxy objects. This scheme allows TffRemoteServerEngine to derive a server-side ID from its proxy object and allows it to maintain information required for its operation. In general, all calls to remote server engine wind up calling a method on a TffProxy class which in turn formats a request and sends it through TffProxyClient.} TFFProxyClientList = class; TFFProxySession = class; TFFProxySessionList = class; TFFProxyDatabase = class; TFFProxyDatabaseList = class; TFFProxyCursor = class; TFFProxyCursorList = class; TffProxySQLStmt = class; TffProxySQLStmtList = class; {-End forward declarations} {Creating/destroying and ownership issues. The TFFProxyClient object will be created/destroyed and owned by it's parent, a TFFRemoteServerEngine. The TFFRemoteServerEngine will be responsible for keeping a list of the afore mentioned object. The TFFProxySession object, and the TFFProxyDatabase object will be created/destroyed and owned by it's parent, a TFFProxyClient. The TFFProxyClient will be responsible for keeping a list of all instances of the afore mentioned objects. The TFFProxyCursor object will be created/destroyed and owned by it's parent, a TFFProxyDatabase. The TFFProxyDatabase will be responsible for keeping a list of all instances of the afore mentioned object. The constructor for each of the client classes is resposible for contacting the server, and retrieving an ID from the server. The parent class will not manipulate the ServerID directly. The destructor for each of the client classes is resposible for tellint the server to release it's associated object. If a proxy class "owns" any other classes then any owned classes must be destroyed first. In the end there should be no manipulation of ServerID's except in the objects constructor. And no way to free a parent class without first freeing dependent classes. } {TFFProxyClient The proxy client controls interaction between the remote server engine and the transport. This class contains a message queue associated with a specific client. All requests for data must go through this class' ProcessRequest method. Instances where a reply from the server isn't necessary can use the ProcessRequestNoReply method. } TFFProxyClient = class(TffObject) protected pcSrClientID : TffClientID; {An ID pointing to the associated TFFSrClient class on the server} pcMsgQueue : TffDataMessageQueue; {The message queue used to store replies to this client. } pcCallbackMethod : TffReplyCallback; {A Method pointer that will be passed to the transport when a reply is requested.} pcCurrentSession : TffProxySession; {The current session as set by the SessionSetCurrent method} pcDatabases : TFFProxyDatabaseList; {The databases that are managed by the client} pcForceClosed : Boolean; pcTransport : TffBaseTransport; {A reference to the RemoteServerEngine's transport. Added here for purposes of speed, and readability.} pcSessions : TFFProxySessionList; {The sessions that are registered with the client. } pcTimeout : Longint; {The current timeout setting for the TFFBaseConnection Class. The TFFBaseConnection class is resposible for updating this object when it's published timeout value is changed.} public constructor Create(aTransport : TffBaseTransport; aUserName : TFFName; aPasswordHash : Longint; aTimeOut : Longint); destructor Destroy; override; function IsReadOnly : Boolean; function ProcessRequest(aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint; aRequestDataType : TffNetMsgDataType; var aReply : Pointer; var aReplyLen : Longint; aReplyType : TffNetMsgDataType) : TffResult; { Use the ProxessRequest method to submit a request that is routed to the transport. This method does the following: 1. Calls TffBaseTransport.Request with transportID = 0 and cookie equal to Pointer(Self). At this point, the calling thread is blocked until a reply is received from the server or a timeout occurs. 2. When the calling thread returns to this method, the reply has been received and placed in the message queue by the ProxyClientCallback procedure. 3. Verify the message is the type that we expected. 4. Put the message into the MessageQueue and exit.} function ProcessRequestNoReply(aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint) : TffResult; { Use the ProxessRequestNoReply method to submit a request that is routed to the transport. This method does the following: 1. Calls TffBaseTransport.Post with transportID = 0 and reply mode to waituntilsent. At this point, the calling thread is blocked until the request has been sent to the server.} function DatabaseClose(aDatabase : TffProxyDatabase) : TffResult; function DatabaseOpen(const aAlias : TffName; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aDatabaseID : TffDatabaseID) : TffResult; {Add a database to the pcDatabases list. The client will take care of creating} function DatabaseOpenNoAlias(const aPath : TffPath; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aDatabaseID : TffDatabaseID ) : TffResult; function GetRebuildStatus(const aRebuildID : Longint; var aIsPresent : Boolean; var aStatus : TffRebuildStatus) : TffResult; function SetTimeout(const aTimeout : Longint) : TffResult; function SessionAdd(var aSessionID : TffSessionID; const aTimeout : Longint) : TffResult; {Add a session to the pcSessions list. The client will take care of creating the TFFProxySession object, whose ID will be returned via aSessionID.} function SessionCloseInactiveTables : TffResult; {!!.06} { Close the inactive tables on the server. } function SessionCount : Longint; {Retrieve the number of sessions the client is managing.} function SessionGetCurrent : TffProxySession; {Retrieve the current session} function SessionRemove(aSession : TFFProxySession) : TffResult; {Remove the session from the list. The client will take destroy the session, and remove it from the list} function SessionSetCurrent(aSession : TFFProxySession) : TffResult; {Set the current session} function DatabaseAddAlias(const aAlias : TffName; const aPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; function DatabaseAliasList(aList : TList) : TffResult; function DatabaseChgAliasPath(const aAlias : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; function DatabaseDeleteAlias(const aAlias : TffName) : TffResult; function DatabaseGetAliasPath(const aAlias : TffName; var aPath : TffPath) : TffResult; function DatabaseModifyAlias(const aAlias : TffName; const aNewName : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; function GetServerDateTime(var aDateTime : TDateTime) : TffResult; {begin !!.10} function GetServerSystemTime(var aSystemTime : TSystemTime) : TffResult; function GetServerGUID(var aGUID : TGUID) : TffResult; function GetServerID(var aUniqueID : TGUID) : TffResult; function GetServerStatistics(var Stats : TffServerStatistics) : TffResult; function GetCommandHandlerStatistics(const CmdHandlerIdx : Integer; var Stats : TffCommandHandlerStatistics) : TffResult; function GetTransportStatistics(const CmdHandlerIdx : Integer; const Transportidx : Integer; var Stats : TffTransportStatistics) : TffResult; {end !!.10} {Begin !!.01} function RemoteRestart : TffResult; { Tell the remote server to restart. } function RemoteStart : TffResult; { Tell the remote server to startup. } function RemoteStop : TffResult; { Tell the remote server to stop. } {End !!.01} {ReadOnly properties for the protected fields} property CurrentSession : TFFProxySession read SessionGetCurrent; property Databases : TFFProxyDatabaseList read pcDatabases; property ForceClosed : Boolean read pcForceClosed write pcForceClosed; property MsgQueue : TFFDataMessageQueue read pcMsgQueue; property Sessions : TFFProxySessionList read pcSessions; property SrClientID : TffClientID read pcSrClientID; property Transport : TFFBaseTransport read pcTransport; property Timeout : Longint read pcTimeout; end; {List containing a reference for every ProxyClient owned by a TFFRemoteServerEngine component.} TFFProxyClientList = class(TffThreadList); {The TFFProxySession is used primarily to keep track of the the current Timeout setting, and the Server CursorID. Unlike the TFFSession, the ProxySession does not manage a set of Databases. TFFProxyDatabases, instead, are managed by the ProxyClient class} TFFProxySession = class(TFFObject) protected psSrSessionID : TFFSessionID; {An ID pointing to the TFFSrSession object on the remote server} psClient : TFFProxyClient; {A reference to the client who owns this object} psTimeout : Longint; {Local storage for the current Session timeout setting. The TFFSession object is resposible for keeping this value up to date.} public constructor Create(aClient : TFFProxyClient; aTimeout : Longint); destructor Destroy; override; function SetTimeout(aTimeout : Longint) : TffResult; {ReadOnly properties for the protected fields} property SrSessionID : TFFSessionID read psSrSessionID; property Client : TFFProxyClient read psClient; property Timeout : LongInt read psTimeout; end; {List containing a reference for every ProxySesion owned by a TFFProxyClient object.} TFFProxySessionList = class(TffThreadList); {The TFFProxyDatabase is responsible for basic Table maintenance. It also keeps track of the the current Timeout setting, and the Server CursorID. TFFProxyDatabase maintains a list of TFFProxyCursor objects.} TFFProxyDatabase = class(TffObject) protected pdSrDatabaseID : TffDatabaseID; {An ID pointing to the TffSrDatabase object on the remote server} pdClient : TFFProxyClient; {A reference to the client who owns this object} pdInTrans : Boolean; {Have we instantiated a tranaction? } pdStmts : TffProxySQLStmtList; {The SQL statements managed by this database} pdTables : TFFProxyCursorList; {The tables that are managed by the database} pdTimeout : Longint; {Local storage for the current Database timeout setting. The TFFDatabase object is resposible for keeping this value up to date.} public constructor Create(aClient : TFFProxyClient; aLocation : string; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; aIsAlias : Boolean); destructor Destroy; override; function GetDBFreeSpace(var aFreeSpace : Longint) : TffResult; function QueryOpen(aCursorID : TffCursorID; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : longInt; aStream : TStream; var aFinalCursorID : TffCursorID) : TffResult; function SetTimeout(const aTimeout : Longint) : TffResult; function SQLAlloc(const aTimeout : longInt; var aStmtID : TffSqlStmtID) : TffResult; function SQLExecDirect(aQueryText : PChar; aOpenMode : TffOpenMode; aTimeout : longInt; var aCursorID : TffCursorID; aStream : TStream) : TffResult; function TableExists(const aTableName : TffTableName; var aExists : Boolean) : TffResult; function TableList(const aMask : TffFileNameExt; aList : TList) : TffResult; function TableLockedExclusive(const aTableName : TffTableName; var aLocked : Boolean) : TffResult; function TableAddIndex(const aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexDesc : TffIndexDescriptor) : TffResult; function TableBuild(aOverWrite : Boolean; const aTableName : TffTableName; aForServer : Boolean; aDictionary : TffDataDictionary) : TffResult; function TableDelete(const aTableName : TffTableName) : TffResult; function TableDropIndex(aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexName : TffDictItemName; aIndexID : longint) : TffResult; function TableEmpty(aCursorID : TffCursorID; const aTableName : TffTableName) : TffResult; function TableGetDictionary(const aTableName : TffTableName; aForServer : Boolean; aStream : TStream) : TffResult; function TableClose(aCursor : TFFProxyCursor) : TffResult; function TableOpen(const aTableName : TffTableName; aForServer : Boolean; aIndexName : TffName; aIndexID : Longint; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aCursorID : TffCursorID; aStream : TStream) : TffResult; function TablePack(const aTableName : TffTableName; var aRebuildID : Longint) : TffResult; function TableRebuildIndex(const aTableName : TffTableName; const aIndexName : TffName; aIndexID : Longint; var aRebuildID : Longint) : TffResult; function TableRename(const aOldName : TffName; const aNewName : TffName) : TffResult; function TableRestructure(const aTableName : TffTableName; aDictionary : TffDataDictionary; aFieldMap : TffStringList; var aRebuildID : Longint) : TffResult; function TransactionStart(aFailSafe : Boolean) : TffResult; {Begin !!.10} function TransactionStartWith(const aFailSafe : Boolean; const aCursorIDs : TffPointerList) : TffResult; {End !!.10} function TransactionCommit : TffResult; function TransactionRollback : TffResult; property Client : TFFProxyClient read pdClient; property InTrans : Boolean read pdInTrans; property SrDatabaseID : TFFDatabaseID read pdSrDatabaseID; property Tables : TffProxyCursorList read pdTables; property Timeout : Longint read pdTimeout; end; TFFProxyDatabaseList = class(TffThreadList); TFFProxyCursor = class(TffObject) protected prSrCursorID : TffCursorID; prClient : TFFProxyClient; prForServer : Boolean; prShareMode : TffShareMode; prTableName : TFFTableName; prTimeout : Longint; prDatabase : TFFProxyDatabase; {State Variables} prDictionary : TffDataDictionary; prIndexID : Longint; prIndexName : string; prIsSQLCursor : boolean; prPhyRecSize : Longint; protected function prGetBookmarkSize : Longint; public constructor Create(aDatabase : TFFProxyDatabase; aCursorID : TffCursorID; {used by CursorClone, otherwise set to 0} aTableName : string; aForServer : Boolean; aIndexName : string; aIndexID : Longint; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : LongInt; aStream : TStream); constructor CreateSQL(aDatabase : TffProxyDatabase; aCursorID : TffCursorID; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : longInt; aStream : TStream); { This constructor is used to construct a proxy cursor for an executed SQL statement. } destructor Destroy; override; function BlobCreate(var aBlobNr : TFFInt64) : TFFResult; function BLOBDelete(aBlobNr : TFFInt64) : TffResult; function BLOBFree(aBlobNr : TffInt64; aReadOnly : Boolean) : TFFResult; function BLOBGetLength(aBlobNr : TffInt64; var aLength : Longint) : TffResult; {Begin !!.03} function BLOBListSegments(aBLOBNr : TffInt64; aStream : TStream) : TffResult; {End !!.03} function BLOBRead(aBlobNr : TffInt64; aOffset : TffWord32; {!!.06} aLen : TffWord32; {!!.06} var aBLOB; var aBytesRead : TffWord32) {!!.06} : TffResult; function BLOBTruncate(aBlobNr : TffInt64; aBLOBLength : Longint) : TffResult; function BLOBWrite(aBlobNr : TffInt64; aOffset : Longint; aLen : Longint; var aBLOB) : TFFResult; function CursorClone(aOpenMode : TFFOpenMode; var aNewCursorID : TFFCursorID) : TFFResult; function CompareBookmarks(aBookmark1 : PffByteArray; aBookmark2 : PffByteArray; var aCompResult : Longint) : TffResult; function CopyRecords(aSrcCursor : TffProxyCursor; {!!.02} aCopyBLOBs : Boolean) : TffResult; {!!.02} function DeleteRecords : TffResult; {!!.06} function GetBookmark(aBookmark : PffByteArray) : TffResult; function GetBookmarkSize(var aSize : Longint) : TffResult; {Begin !!.03} function ListBLOBFreeSpace(const aInMemory : Boolean; aStream : TStream) : TffResult; {End !!.03} function OverrideFilter(aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; function ResetRange : TffResult; function RestoreFilter : TffResult; function SetFilter(aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; function SetRange(aDirectKey : Boolean; aFieldCount1 : Longint; aPartialLen1 : Longint; aKeyData1 : PffByteArray; aKeyIncl1 : Boolean; aFieldCount2 : Longint; aPartialLen2 : Longint; aKeyData2 : PffByteArray; aKeyIncl2 : Boolean) : TffResult; function SetTimeout(aTimeout : Longint) : TffResult; function SetToBegin : TffResult; function SetToBookmark(aBookmark : PffByteArray) : TffResult; function SetToCursor(aSourceCursor : TFFProxyCursor) : TffResult; function SetToEnd : TffResult; function SetToKey(aSearchAction : TffSearchKeyAction; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray) : TffResult; function SwitchToIndex(aIndexName : TffDictItemName; aIndexID : Longint; aPosnOnRec : Boolean) : TffResult; function FileBLOBAdd(const aFileName : TffFullFileName; var aBlobNr : TffInt64) : TffResult; function RecordDelete(aData : PffByteArray) : TffResult; function RecordDeleteBatch(aBMCount : Longint; aBMLen : Longint; aData : PffByteArray; aErrors : PffLongintArray) : TffResult; function RecordExtractKey(aData : PffByteArray; aKey : PffByteArray) : TffResult; function RecordGet(aLockType : TffLockType; aData : PffByteArray) : TffResult; function RecordGetBatch(aRecCount : Longint; aRecLen : Longint; var aRecRead : Longint; aData : PffByteArray; var aError : TffResult) : TffResult; function RecordGetForKey(aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray; aData : PffByteArray; aFirstCall : Boolean) : TffResult; function RecordGetNext(aLockType : TffLockType; aData : PffByteArray) : TffResult; function RecordGetPrior(aLockType : TffLockType; aData : PffByteArray) : TffResult; function RecordInsert(aLockType : TffLockType; aData : PffByteArray) : TffResult; function RecordInsertBatch(aRecCount : Longint; aRecLen : Longint; aData : PffByteArray; aErrors : PffLongintArray) : TffResult; function RecordIsLocked(aLockType : TffLockType; var aIsLocked : boolean) : TffResult; function RecordModify(aData : PffByteArray; aRelLock : Boolean) : TffResult; function RecordRelLock(aAllLocks : Boolean) : TffResult; function TableGetAutoInc(var aValue : TffWord32) : TffResult; function TableGetRecCount(var aRecCount : Longint) : TffResult; function TableGetRecCountAsync(var aTaskID : Longint) : TffResult; {!!.07} function TableIsLocked(aLockType : TffLockType; var aIsLocked : Boolean) : TffResult; function TableLockAcquire(aLockType : TffLockType) : TffResult; function TableLockRelease(aAllLocks : Boolean) : TffResult; function TableSetAutoInc(aValue : TffWord32) : TffResult; property Client : TFFProxyClient read prClient; property SrCursorID : TffCursorID read prSrCursorID; property Timeout : Longint read prTimeout; property BookmarkSize : longint read prGetBookmarkSize; property Database : TFFProxyDatabase read prDatabase; property Dictionary : TffDataDictionary read prDictionary; property IndexID : Longint read prIndexID; property PhysicalRecordSize : Longint read prPhyRecSize; end; TFFProxyCursorList = class(TffThreadList); TffProxySQLStmt = class(TffObject) protected {private} psClient : TffProxyClient; { The proxy client through which requests are routed. } psDatabase : TffProxyDatabase; { The proxy database with which the SQL statement is associated. } psSrStmtID : TffSqlStmtID; { The actual statement ID. } psTimeout : longInt; { The SQL statement's timeout (in milliseconds). } public {creation/destruction} constructor Create(aDatabase : TffProxyDatabase; const aTimeout : longInt); destructor Destroy; override; function Exec(aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; function Prepare(aQueryText: PChar; aStream : TStream) : TffResult; function SetParams(aNumParams : Word; aParamDescs : Pointer; aDataBuffer : PffByteArray; aDataLen : Longint; aStream : TStream) : TffResult; property Database : TffProxyDatabase read psDatabase; property SrStmtID : TffSqlStmtID read psSrStmtID; { The statement ID returned by the server engine. } end; TffProxySQLStmtList = class(TffThreadList); TFFRemoteServerEngine = class(TffIntermediateServerEngine) private protected {private} rsClientList : TFFProxyClientList; rsTimeout : TffWord32; rsTransport : TffBaseTransport; {Begin !!.06} function ProcessRequest(aClientID : TffClientID; aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint; aRequestDataType : TffNetMsgDataType; var aReply : Pointer; var aReplyLen : Longint; aReplyType : TffNetMsgDataType) : TffResult; override; { Backdoor method for sending a request to a server engine. Should only be implemented by remote server engines. } function ProcessRequestNoReply(aClientID : TffClientID; aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint ) : TffResult; override; { Backdoor method for sending a request, no reply expected, to a server engine. Should only be implemented by remote server engines. } {End !!.06} procedure rsSetTransport(const Value : TFFBaseTransport); // protected {!!.01 - Start - Made public} // {validation and checking} // function CheckClientIDAndGet(aClientID : TffClientID; // var aClient : TffProxyClient) : TffResult; // function CheckSessionIDAndGet(aClientID : TffClientID; // aSessionID : TffSessionID; // var aClient : TffProxyClient; // var aSession : TffProxySession) : TffResult; // function CheckDatabaseIDAndGet(aDatabaseID : TffDatabaseID; // var aDatabase : TffProxyDatabase) : TffResult; // {-Find the database specified by aDatabaseID. } // // function CheckCursorIDAndGet(aCursorID : TffCursorID; // var aCursor : TffProxyCursor) : TffResult; // {-Find the cursor specified by aCursorID. } // // function CheckStmtIDAndGet(aStmtID : TffSqlStmtID; // var aStmt : TffProxySQLStmt) : TffResult; // {-Find the statement specified by aStmtID. } {!!.01 - End} protected {State methods} procedure scInitialize; override; procedure scPrepareForShutdown; override; procedure scShutdown; override; procedure scStartup; override; function bseGetAutoSaveCfg : Boolean; override; function bseGetReadOnly : Boolean; override; procedure bseSetAutoSaveCfg(aValue : Boolean); override; {!!.01} procedure bseSetReadOnly(aValue : Boolean); override; {!!.01} public {Begin !!.07} { Event logging } procedure Log(const aMsg : string); override; {-Use this method to log a string to the event log. } procedure LogAll(const Msgs : array of string); override; {-Use this method to log multiple strings to the event log. } procedure LogFmt(const aMsg : string; args : array of const); override; {-Use this method to log a formatted string to the event log. } {End !!.07} {Begin !!.01 - moved from protected section} {validation and checking} function CheckClientIDAndGet(aClientID : TffClientID; var aClient : TffProxyClient) : TffResult; function CheckSessionIDAndGet(aClientID : TffClientID; aSessionID : TffSessionID; var aClient : TffProxyClient; var aSession : TffProxySession) : TffResult; function CheckDatabaseIDAndGet(aDatabaseID : TffDatabaseID; var aDatabase : TffProxyDatabase) : TffResult; {-Find the database specified by aDatabaseID. } function CheckCursorIDAndGet(aCursorID : TffCursorID; var aCursor : TffProxyCursor) : TffResult; {-Find the cursor specified by aCursorID. } function CheckStmtIDAndGet(aStmtID : TffSqlStmtID; var aStmt : TffProxySQLStmt) : TffResult; {-Find the statement specified by aStmtID. } {End !!.01} {creation/destruction} constructor Create(aOwner : TComponent); override; destructor Destroy; override; procedure FFNotificationEx(const AOp : Byte; AFrom : TffComponent; const AData : TffWord32); override; function GetDefaultClient : TFFProxyClient; procedure GetServerNames(aList : TStrings; aTimeout : Longint); override; procedure ForceClosing(const aClientID : TffClientID); {Begin !!.01} function RemoteRestart(const aClientID : TffClientID) : TffResult; { Tell the remote server to shutdown and startup. } function RemoteStart(const aClientID : TffClientID) : TffResult; { Tell the remote server to startup. Only works if the remote server is in a stopped state (i.e., transports & cmd handlers still listening. } function RemoteStop(const aClientID : TffClientID) : TffResult; { Tell the remote server to stop. The server engine shuts down but the transport and cmd handlers will still be listening. } {End !!.01} {transaction tracking} function TransactionCommit(const aDatabaseID : TffDatabaseID ) : TffResult; override; function TransactionRollback(const aDatabaseID : TffDatabaseID ) : TffResult; override; function TransactionStart(const aDatabaseID : TffDatabaseID; const aFailSafe : Boolean ) : TffResult; override; {Begin !!.10} function TransactionStartWith(const aDatabaseID : TffDatabaseID; const aFailSafe : Boolean; const aCursorIDs : TffPointerList ) : TffResult; override; {End !!.10} {client related stuff} function ClientAdd(var aClientID : TffClientID; const aClientName : TffNetName; const aUserID : TffName; const aTimeout : Longint; var aHash : TffWord32) : TffResult; override; {Begin !!.11} function ClientAddEx(var aClientID : TffClientID; const aClientName : TffNetName; const aUserID : TffName; const aTimeout : Longint; const aClientVersion : Longint; var aHash : TffWord32) : TffResult; override; { Same as ClientAdd but client version is supplied via the aClientVersion parameter. } {End !!.11} function ClientRemove(aClientID : TffClientID) : TffResult; override; function ClientSetTimeout(const aClientID : TffClientID; const aTimeout : longInt) : TffResult; override; {client session related stuff} function SessionAdd(const aClientID : TffClientID; const aTimeout : Longint; var aSessionID : TffSessionID) : TffResult; override; function SessionCloseInactiveTables(aClientID : TffClientID) : TffResult; override; {!!.06} function SessionCount(aClientID : TffClientID; var aCount : Longint) : TffResult; override; function SessionGetCurrent(aClientID : TffClientID; var aSessionID : TffSessionID ) : TffResult; override; function SessionRemove(aClientID : TffClientID; aSessionID : TffSessionID) : TffResult; override; function SessionSetTimeout(const aClientID : TffClientID; const aSessionID : TffSessionID; const aTimeout : Longint) : TffResult; override; function SessionSetCurrent(aClientID : TffClientID; aSessionID : TffSessionID ) : TffResult; override; {database related stuff} function DatabaseAddAlias(const aAlias : TffName; const aPath : TffPath; aCheckSpace : Boolean; {!!.11} const aClientID : TffClientID) : TffResult; override; function DatabaseAliasList(aList : TList; aClientID : TffClientID) : TffResult; override; function RecoveryAliasList(aList : TList; aClientID : TffClientID) : TffResult; override; function DatabaseChgAliasPath(aAlias : TffName; aNewPath : TffPath; aCheckSpace : Boolean; {!!.11} aClientID : TffClientID) : TffResult; override; function DatabaseClose(aDatabaseID : TffDatabaseID) : TffResult; override; function DatabaseDeleteAlias(aAlias : TffName; aClientID : TffClientID) : TffResult; override; function DatabaseGetAliasPath(aAlias : TffName; var aPath : TffPath; aClientID : TffClientID) : TffResult; override; function DatabaseGetFreeSpace(const aDatabaseID : TffDatabaseID; var aFreeSpace : Longint) : TffResult; override; function DatabaseModifyAlias(const aClientID : TffClientID; const aAlias : TffName; const aNewName : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; override; function DatabaseOpen(aClientID : TffClientID; const aAlias : TffName; const aOpenMode : TffOpenMode; const aShareMode : TffShareMode; const aTimeout : Longint; var aDatabaseID : TffDatabaseID) : TffResult; override; function DatabaseOpenNoAlias(aClientID : TffClientID; const aPath : TffPath; const aOpenMode : TffOpenMode; const aShareMode : TffShareMode; const aTimeout : Longint; var aDatabaseID : TffDatabaseID ) : TffResult; override; function DatabaseSetTimeout(const aDatabaseID : TffDatabaseID; const aTimeout : Longint) : TffResult; override; function DatabaseTableExists(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aExists : Boolean) : TffResult; override; function DatabaseTableList(aDatabaseID : TffDatabaseID; const aMask : TffFileNameExt; aList : TList) : TffResult; override; function DatabaseTableLockedExclusive(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aLocked : Boolean) : TffResult; override; {rebuild status related stuff} function RebuildGetStatus(aRebuildID : longint; const aClientID : TffClientID; var aIsPresent : Boolean; var aStatus : TffRebuildStatus ) : TffResult; override; {table related stuff} function TableAddIndex(const aDatabaseID : TffDatabaseID; const aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexDesc : TffIndexDescriptor ) : TffResult; override; function TableBuild(aDatabaseID : TffDatabaseID; aOverWrite : Boolean; const aTableName : TffTableName; aForServer : Boolean; aDictionary : TffDataDictionary ) : TffResult; override; function TableDelete(aDatabaseID : TffDatabaseID; const aTableName : TffTableName) : TffResult; override; function TableDropIndex(aDatabaseID : TffDatabaseID; aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexName : TffDictItemName; aIndexID : longint) : TffResult; override; function TableEmpty(aDatabaseID : TffDatabaseID; aCursorID : TffCursorID; const aTableName : TffTableName) : TffResult; override; function TableGetAutoInc(aCursorID : TffCursorID; var aValue : TffWord32) : TffResult; override; function TableGetDictionary(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; aForServer : Boolean; aStream : TStream) : TffResult; override; function TableGetRecCount(aCursorID : TffCursorID; var aRecCount : longint) : TffResult; override; function TableGetRecCountAsync(aCursorID : TffCursorID; {!!.07} var aTaskID : Longint) : TffResult; override; {!!.07} function TableOpen(const aDatabaseID : TffDatabaseID; const aTableName : TffTableName; const aForServer : Boolean; const aIndexName : TffName; aIndexID : longint; const aOpenMode : TffOpenMode; aShareMode : TffShareMode; const aTimeout : Longint; var aCursorID : TffCursorID; aStream : TStream) : TffResult; override; function TablePack(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aRebuildID : longint) : TffResult; override; function TableRebuildIndex(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; const aIndexName : TffName; aIndexID : longint; var aRebuildID : longint) : TffResult; override; function TableRename(aDatabaseID : TffDatabaseID; const aOldName : TffName; const aNewName : TffName) : TffResult; override; function TableRestructure(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; aDictionary : TffDataDictionary; aFieldMap : TffStringList; var aRebuildID : longint) : TffResult; override; function TableSetAutoInc(aCursorID : TffCursorID; aValue : TffWord32) : TffResult; override; {Begin !!.11} function TableVersion(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aVersion : Longint) : TffResult; override; {End !!.11} {table locks via cursor} function TableIsLocked(aCursorID : TffCursorID; aLockType : TffLockType; var aIsLocked : Boolean) : TffResult; override; function TableLockAcquire(aCursorID : TffCursorID; aLockType : TffLockType) : TffResult; override; function TableLockRelease(aCursorID : TffCursorID; aAllLocks : Boolean) : TffResult; override; {cursor stuff} function CursorClone(aCursorID : TffCursorID; aOpenMode : TffOpenMode; var aNewCursorID : TffCursorID) : TffResult; override; function CursorClose(aCursorID : TffCursorID) : TffResult; override; function CursorCompareBookmarks(aCursorID : TffCursorID; aBookmark1, aBookmark2 : PffByteArray; var aCompResult : longint) : TffResult; override; {Begin !!.02} function CursorCopyRecords(aSrcCursorID, aDestCursorID : TffCursorID; aCopyBLOBs : Boolean) : TffResult; override; {End !!.02} function CursorDeleteRecords(aCursorID : TffCursorID) : TffResult; override; {!!.06} function CursorGetBookmark(aCursorID : TffCursorID; aBookmark : PffByteArray) : TffResult; override; function CursorGetBookmarkSize(aCursorID : TffCursorID; var aSize : Longint) : TffResult; override; {Begin !!.03} function CursorListBLOBFreeSpace(aCursorID : TffCursorID; const aInMemory : Boolean; aStream : TStream) : TffResult; override; {End !!.03} function CursorOverrideFilter(aCursorID : Longint; aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; override; function CursorResetRange(aCursorID : TffCursorID) : TffResult; override; function CursorRestoreFilter(aCursorID : longInt) : TffResult; override; function CursorSetRange(aCursorID : TffCursorID; aDirectKey : Boolean; aFieldCount1 : Longint; aPartialLen1 : Longint; aKeyData1 : PffByteArray; aKeyIncl1 : Boolean; aFieldCount2 : Longint; aPartialLen2 : Longint; aKeyData2 : PffByteArray; aKeyIncl2 : Boolean) : TffResult; override; function CursorSetTimeout(const aCursorID : TffCursorID; const aTimeout : Longint) : TffResult; override; function CursorSetToBegin(aCursorID : TffCursorID) : TffResult; override; function CursorSetToBookmark(aCursorID : TffCursorID; aBookmark : PffByteArray ) : TffResult; override; function CursorSetToCursor(aDestCursorID : TffCursorID; aSrcCursorID : TffCursorID ) : TffResult; override; function CursorSetToEnd(aCursorID : TffCursorID) : TffResult; override; function CursorSetToKey(aCursorID : TffCursorID; aSearchAction : TffSearchKeyAction; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray ) : TffResult; override; function CursorSwitchToIndex(aCursorID : TffCursorID; aIndexName : TffDictItemName; aIndexID : Longint; aPosnOnRec : Boolean) : TffResult; override; function CursorSetFilter(aCursorID : TffCursorID; aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; override; {record stuff} function RecordDelete(aCursorID : TffCursorID; aData : PffByteArray) : TffResult; override; function RecordDeleteBatch(aCursorID : TffCursorID; aBMCount : Longint; aBMLen : Longint; aData : PffByteArray; aErrors : PffLongintArray) : TffResult; override; function RecordExtractKey(aCursorID : TffCursorID; aData : PffByteArray; aKey : PffByteArray) : TffResult; override; function RecordGet(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray) : TffResult; override; function RecordGetBatch(aCursorID : TffCursorID; aRecCount : longint; aRecLen : longint; var aRecRead : longint; aData : PffByteArray; var aError : TffResult) : TffResult; override; function RecordGetForKey(aCursorID : TffCursorID; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray; aData : PffByteArray; aFirstCall : Boolean ) : TffResult; override; function RecordGetNext(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray) : TffResult; override; function RecordGetPrior(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray) : TffResult; override; function RecordInsert(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray) : TffResult; override; function RecordInsertBatch(aCursorID : TffCursorID; aRecCount : longint; aRecLen : longint; aData : PffByteArray; aErrors : PffLongintArray) : TffResult; override; function RecordIsLocked(aCursorID : TffCursorID; aLockType : TffLockType; var aIsLocked : boolean) : TffResult; override; function RecordModify(aCursorID : TffCursorID; aData : PffByteArray; aRelLock : Boolean) : TffResult; override; function RecordRelLock(aCursorID : TffCursorID; aAllLocks : Boolean) : TffResult; override; {BLOB stuff} function BLOBCreate(aCursorID : TffCursorID; var aBlobNr : TffInt64) : TffResult; override; function BLOBDelete(aCursorID : TffCursorID; aBlobNr : TffInt64) : TffResult; override; {Begin !!.03} function BLOBListSegments(aCursorID : TffCursorID; aBLOBNr : TffInt64; aStream : TStream) : TffResult; override; {End !!.03} function BLOBRead(aCursorID : TffCursorID; aBlobNr : TffInt64; aOffset : TffWord32; {!!.06} aLen : TffWord32; {!!.06} var aBLOB; var aBytesRead : TffWord32) {!!.06} : TffResult; override; function BLOBFree(aCursorID : TffCursorID; aBlobNr : TffInt64; readOnly : Boolean) : TffResult; override; function BLOBGetLength(aCursorID : TffCursorID; aBlobNr : TffInt64; var aLength : longint) : TffResult; override; function BLOBTruncate(aCursorID : TffCursorID; aBlobNr : TffInt64; aBLOBLength : longint) : TffResult; override; function BLOBWrite(aCursorID : TffCursorID; aBlobNr : TffInt64; aOffset : longint; aLen : longint; var aBLOB) : TffResult; override; function FileBLOBAdd(aCursorID : TffCursorID; const aFileName : TffFullFileName; var aBlobNr : TffInt64) : TffResult; override; {query stuff} function SQLAlloc(aClientID : TffClientID; aDatabaseID : TffDatabaseID; aTimeout : longInt; var aStmtID : TffSqlStmtID) : TffResult; override; function SQLExec(aStmtID : TffSqlStmtID; aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; override; function SQLExecDirect(aClientID : TffClientID; aDatabaseID : TffDatabaseID; aQueryText : PChar; aTimeout : longInt; aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; override; function SQLFree(aStmtID : TffSqlStmtID) : TffResult; override; function SQLPrepare(aStmtID : TffSqlStmtID; aQueryText : PChar; aStream : TStream) : TffResult; override; function SQLSetParams(aStmtID : TffSqlStmtID; aNumParams : word; aParamDescs : pointer; aDataBuffer : PffByteArray; aDataLen : Longint; aStream : TStream) : TffResult; override; {misc stuff} function GetServerDateTime(var aDateTime : TDateTime ) : TffResult; override; {begin !!.07} function GetServerSystemTime(var aSystemTime : TSystemTime) : TffResult; override; function GetServerGUID(var aGUID : TGUID) : TffResult; override; function GetServerID(var aUniqueID : TGUID) : TffResult; override; function GetServerStatistics(var Stats : TffServerStatistics) : TffResult; override; function GetCommandHandlerStatistics(const CmdHandlerIdx : Integer; var Stats : TffCommandHandlerStatistics) : TffResult; override; function GetTransportStatistics(const CmdHandlerIdx : Integer; const TransportIdx : Integer; var Stats : TffTransportStatistics) : TffResult; override; {end !!.07} {properties} property ClientList : TFFProxyClientList read rsClientList; property TimeOut : TFFWord32 read rsTimeout write rsTimeout; published property Transport : TFFBaseTransport read rsTransport write rsSetTransport; end; {Callback method used by the transport to notify us when the request is complete.} procedure ProxyRequestCallback(aMsgID : Longint; aErrorCode : TffResult; aReply : Pointer; aReplyLen : Longint; aReplyCookie : Longint); var RemoteServerEngines : TFFThreadList; implementation uses ActiveX, ffsqlbas; {--Internal helper routines--} function ResultOK(aResult : TffResult) : Boolean; begin Result := aResult = DBIERR_NONE; end; {------------------------------------------------------------------------------} {--Callback routine--} procedure ProxyRequestCallback(aMsgID : Longint; aErrorCode : TffResult; aReply : Pointer; aReplyLen : Longint; aReplyCookie : Longint); var Client : TFFProxyClient absolute aReplyCookie; begin { hand-off the response from the transport to the ProxyClient } Client.pcMsgQueue.Append(aMsgID, aReplyCookie, 0, {RequestID} 0, {Timeout} aErrorCode, aReply, aReplyLen, aReplyLen); end; {------------------------------------------------------------------------------} {-TffProxyClient---------------------------------------------------------------} constructor TFFProxyClient.Create(aTransport : TffBaseTransport; aUserName : TFFName; aPasswordHash : Longint; aTimeOut : Longint); begin inherited Create; {Initialize internals} pcSrClientID := 0; pcCurrentSession := nil; pcForceClosed := False; pcTransport := aTransport; pcTimeout := aTimeOut; {Create internal classes} pcMsgQueue := TffDataMessageQueue.Create; pcSessions := TFFProxySessionList.Create; pcDatabases := TFFProxyDatabaseList.Create; {Set the CallbackMethod that will be used by the transport to return data} pcCallbackMethod := ProxyRequestCallback; {Let the ServerEngine know that we are here. Set our SrClientID for later reference, as we will need it often.} Check(pcTransport.EstablishConnection(aUserName, aPasswordHash, pcTimeOut, pcSrClientID)); end; {----------} function TFFProxyClient.DatabaseAddAlias(const aAlias : TffName; const aPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; var Request : TffnmDatabaseAddAliasReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize the request record } Request.Alias := aAlias; Request.Path := aPath; Request.CheckDisk := aCheckSpace; {!!.11} Reply := nil; Result := ProcessRequest(ffnmDatabaseAddAlias, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); { Calling ffnmDatabaseAddAlias only returns an error code to Result. } if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.DatabaseAliasList(aList: TList) : TffResult; var Stream : TMemoryStream; ReplyLen : Longint; Count : Longint; AliasDes : PffAliasDescriptor; DesSize : Longint; begin Stream := TMemoryStream.Create; try { We have no data to send. } Result := ProcessRequest(ffnmDatabaseAliasList, Timeout, nil, 0, nmdByteArray, Pointer(Stream), ReplyLen, nmdStream); if ResultOK(Result) then begin aList.Clear; Stream.Position := 0; DesSize := SizeOf(TffAliasDescriptor); for Count := 1 to (ReplyLen div DesSize) do begin { Move the alias data from the stream, to a PffAliasDescriptor. Each descriptor will be an entry in aList. The caller must free this data when it is done using it. } FFGetMem(AliasDes, DesSize); Stream.Read(AliasDes^, DesSize); aList.Add(AliasDes); end; end; finally Stream.Free; end; end; {----------} function TFFProxyClient.DatabaseChgAliasPath(const aAlias : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; var Request : TffnmDatabaseChgAliasPathReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize the request record } Request.Alias := aAlias; Request.NewPath := aNewPath; Request.CheckDisk := aCheckSpace; {!!.11} Reply := nil; Result := ProcessRequest(ffnmDatabaseChgAliasPath, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); { Calling ffnmDatabaseChgAliasPath only returns an error code to Result. } if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.DatabaseClose(aDatabase : TffProxyDatabase) : TffResult; begin Result := DBIERR_NONE; with pcDatabases.BeginWrite do try Delete(aDatabase); {!!.01} finally EndWrite; end; aDatabase.Free; aDatabase := nil; end; {----------} function TFFProxyClient.DatabaseDeleteAlias(const aAlias : TffName) : TffResult; var Request : TffnmDatabaseDeleteAliasReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize the request record } Request.Alias := aAlias; Reply := nil; Result := ProcessRequest(ffnmDatabaseDeleteAlias, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); { Calling ffnmDatabaseDeleteAlias only returns an error code to Result. } if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.DatabaseGetAliasPath(const aAlias : TffName; var aPath : TffPath ) : TffResult; var Request : TffnmDatabaseGetAliasPathReq; Reply : PffnmDatabaseGetAliasPathRpy; ReplyLen : Longint; begin { Initialize the request record } Request.Alias := aAlias; Reply := nil; Result := ProcessRequest(ffnmDatabaseGetAliasPath, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aPath := Reply^.Path; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyClient.DatabaseModifyAlias(const aAlias : TffName; const aNewName : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; var Request : TffnmDatabaseModifyAliasReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize the request record } Request.ClientID := SrClientID; Request.Alias := aAlias; Request.NewName := aNewName; Request.NewPath := aNewPath; Request.CheckDisk := aCheckSpace; {!!.11} Reply := nil; Result := ProcessRequest(ffnmDatabaseModifyAlias, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.DatabaseOpen(const aAlias : TffName; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aDatabaseID : TffDatabaseID) : TffResult; var Database : TFFProxyDatabase; ListItem : TffIntListItem; begin Database := nil; Result := DBIERR_NONE; try Database := TFFProxyDatabase.Create(Self, aAlias, aOpenMode, aShareMode, aTimeout, True); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Database) then begin {Add Database to the internal list} ListItem := TffIntListItem.Create(Longint(Database)); with pcDatabases.BeginWrite do try Insert(ListItem); finally EndWrite; end; aDatabaseID := Longint(Database); end; end; {----------} function TFFProxyClient.DatabaseOpenNoAlias(const aPath : TffPath; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aDatabaseID : TffDatabaseID ) : TffResult; var Database : TFFProxyDatabase; ListItem : TffIntListItem; begin Database := nil; Result := DBIERR_NONE; try Database := TFFProxyDatabase.Create(Self, aPath, aOpenMode, aShareMode, aTimeout, False); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Database) then begin {Add Database to the internal list} ListItem := TffIntListItem.Create(Longint(Database)); with pcDatabases.BeginWrite do try Insert(ListItem); finally EndWrite; end; aDatabaseID := Longint(Database); end; end; {----------} destructor TFFProxyClient.Destroy; {Begin !!.03} //var // Idx : Longint; begin {Destroy managed objects} pcMsgQueue.Free; pcMsgQueue := nil; pcSessions.Free; pcSessions := nil; pcDatabases.Free; pcDatabases := nil; // with pcDatabases.BeginWrite do // try // for Idx := 0 to Pred(Count) do // TFFProxyDatabase(Items[Idx]).Free; // finally // EndWrite; // end; // with pcSessions.BeginWrite do // try // for Idx := 0 to Pred(Count) do // TFFProxySession(Items[Idx]).Free; // finally // EndWrite; // end; {Tell the server that we are disconnecting.} if not ForceClosed then if SrClientID > 0 then pcTransport.TerminateConnection(SrClientID, Timeout); // {Destroy internal classes} // pcMsgQueue.Free; // pcMsgQueue := nil; // pcSessions.Free; // pcSessions := nil; // pcDatabases.Free; // pcDatabases := nil; {End !!.03} {Re-Initialize internals for completeness} pcCurrentSession := nil; pcTransport := nil; pcCallbackMethod := nil; inherited Destroy; end; {----------} function TffProxyClient.IsReadOnly : Boolean; var Reply : PffnmServerIsReadOnlyRpy; ReplyLen : Longint; ErrorCode : TffResult; begin Reply := nil; ErrorCode := ProcessRequest(ffnmServerIsReadOnly, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(ErrorCode) then Result := Reply^.IsReadOnly else Result := False; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetServerDateTime(var aDateTime : TDateTime ) : TffResult; var Reply : PffnmGetServerDateTimeRpy; ReplyLen : Longint; begin { Just in case } aDateTime := Now; { We have no data to send } Reply := nil; Result := ProcessRequest(ffnmGetServerDateTime, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aDateTime := Reply^.ServerNow; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} {begin !!.07} function TFFProxyClient.GetServerSystemTime(var aSystemTime : TSystemTime) : TffResult; var Reply : PffnmGetServerSystemTimeRpy; ReplyLen : Longint; begin { Just in case } GetSystemTime(aSystemTime); { We have no data to send } Reply := nil; Result := ProcessRequest(ffnmGetServerSystemTime, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aSystemTime := Reply^.ServerNow; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetServerGUID(var aGUID : TGUID) : TffResult; var Reply : PffnmGetServerGUIDRpy; ReplyLen : Longint; begin { Just in case } CoCreateGuid(aGUID); { We have no data to send } Reply := nil; Result := ProcessRequest(ffnmGetServerGUID, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aGUID := Reply^.GUID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetServerID(var aUniqueID : TGUID) : TffResult; var Reply : PffnmGetServerIDRpy; ReplyLen : Longint; begin { We have no data to send } Reply := nil; Result := ProcessRequest(ffnmGetServerID, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aUniqueID := Reply^.UniqueID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetServerStatistics(var Stats : TffServerStatistics) : TffResult; var Reply : PffnmServerStatisticsRpy; ReplyLen : Longint; begin { We have no data to send } Reply := nil; Result := ProcessRequest(ffnmServerStatistics, Timeout, nil, 0, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then Stats := Reply^.Stats; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetCommandHandlerStatistics(const CmdHandlerIdx : Integer; var Stats : TffCommandHandlerStatistics) : TffResult; var Request : TffnmCmdHandlerStatisticsReq; Reply : PffnmCmdHandlerStatisticsRpy; ReplyLen : Longint; begin { Initiailize Request } Request.CmdHandlerIdx := CmdHandlerIdx; Reply := nil; Result := ProcessRequest(ffnmCmdHandlerStatistics, pcTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then Stats := Reply^.Stats; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.GetTransportStatistics(const CmdHandlerIdx : Integer; const Transportidx : Integer; var Stats : TffTransportStatistics) : TffResult; var Request : TffnmTransportStatisticsReq; Reply : PffnmTransportStatisticsRpy; ReplyLen : Longint; begin { Initiailize Request } Request.CmdHandlerIdx := CmdHandlerIdx; Request.TransportIdx := Transportidx; Reply := nil; Result := ProcessRequest(ffnmTransportStatistics, pcTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then Stats := Reply^.Stats; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} {end !!.07} function TFFProxyClient.ProcessRequest(aMsgID : longInt; aTimeout : longInt; aRequestData : Pointer; aRequestDataLen : longInt; aRequestDataType : TffNetMsgDataType; var aReply : Pointer; var aReplyLen : longInt; aReplyType : TffNetMsgDataType ) : TffResult; var ReplyAsStream : TStream absolute aReply; ReplyMsg : PffDataMessage; begin if ForceClosed then begin Result := DBIERR_NONE; aReply := nil; aReplyLen := 0; Exit; end; Result := DBIERR_NA; {A Respose from the server is expected. This call will not return until the complete reply has been sent to the transport, and the Client callback method has been called.} { Use the ProxessRequest method to submit a request that is routed to the transport. This method does the following: 1. Calls TffBaseTransport.Request with transportID = 0 and cookie equal to Pointer(Self). At this point, the calling thread is blocked until a reply is received from the server or a timeout occurs. 2. When the calling thread returns to this method, the reply has been received and placed in the message queue by the ProxyClientCallback procedure. 3. Get the first message off the queue and verify it is what we expected. 4. Put the message into the Reply variables and exit. } { Is our reply already in the queue (e.g., came back as part of a multi-part message? Assumption: We can get rid of any replies that don't match the message we are requesting. } ReplyMsg := pcMsgQueue.SoftPop; while Assigned(ReplyMsg) and (ReplyMsg^.dmMsg <> aMsgID) do begin FFFreeMem(ReplyMsg^.dmData, ReplyMsg^.dmDataLen); FFFreeMem(ReplyMsg, SizeOf(TFFDataMessage)); ReplyMsg := pcMsgQueue.SoftPop; end; if not Assigned(ReplyMsg) then begin pcTransport.Request(0, {For use by future protocols.} SrClientID, aMsgID, aTimeout, aRequestData, aRequestDataLen, pcCallbackMethod, Longint(Self)); {Process the reply from the server. Get the reply message off the queue and verify that is what we expected} Assert(pcMsgQueue.Count <= 1, 'Too many messages in the queue'); ReplyMsg := pcMsgQueue.SoftPop; end; if Assigned(ReplyMsg) then begin if (ReplyMsg^.dmMsg <> aMsgID) then begin Result := DBIERR_NOTSAMESESSION; FFFreeMem(ReplyMsg^.dmData, ReplyMsg^.dmDataLen); {!!.03} FFFreeMem(ReplyMsg, SizeOf(TFFDataMessage)); Exit; end; aReplyLen := ReplyMsg^.dmDataLen; if aReplyType = nmdStream then begin Assert(Assigned(ReplyAsStream)); ReplyAsStream.Position := 0; if (aReplyLen > 0) then begin ReplyAsStream.Write(ReplyMsg^.dmData^, aReplyLen); FFFreeMem(ReplyMsg^.dmData, aReplyLen); end; end else aReply := ReplyMsg^.dmData; Result := ReplyMsg^.dmErrorCode; { Free the ReplyMsg, but leave RequestData alone. The caller is responsible for releasing data. We expect the caller to free the reply data.} FFFreeMem(ReplyMsg, SizeOf(TFFDataMessage)); end; end; {----------} function TFFProxyClient.ProcessRequestNoReply(aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint ) : TffResult; begin if ForceClosed then begin Result := DBIERR_NONE; Exit; end; {No response from the server is expected, so this call will return as soon as the request has been sent from the transport's queue} pcTransport.Post(0, {For use by future protocols.} SrClientID, aMsgID, aRequestData, aRequestDataLen, aTimeout, ffrmNoReplyWaitUntilSent); Result := DBIERR_NONE; end; {Begin !!.01} {----------} function TffProxyClient.RemoteRestart : TffResult; begin Result := ProcessRequestNoReply(ffnmServerRestart, Timeout, nil, 0); end; {----------} function TffProxyClient.RemoteStart : TffResult; begin Result := ProcessRequestNoReply(ffnmServerStartup, Timeout, nil, 0); end; {----------} function TffProxyClient.RemoteStop : TffResult; begin Result := ProcessRequestNoReply(ffnmServerStop, Timeout, nil, 0); end; {End !!.01} {----------} function TFFProxyClient.SessionAdd(var aSessionID : TffSessionID; const aTimeout : Longint) : TffResult; var Session : TFFProxySession; ListItem : TffIntListItem; begin Session := nil; Result := DBIERR_NONE; try Session := TFFProxySession.Create(Self, aTimeout); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Session) then begin {Add Session to the internal list} ListItem := TffIntListItem.Create(Longint(Session)); with pcSessions.BeginWrite do try Insert(ListItem); finally EndWrite; end; aSessionID := Longint(Session); {Set the current session if it is nil} if not Assigned(pcCurrentSession) then pcCurrentSession := Session; end; end; {Begin !!.06} {----------} function TFFProxyClient.SessionCloseInactiveTables : TffResult; var Request : TffnmSessionCloseInactiveTblReq; Reply : Pointer; ReplyLen : Longint; begin { Initiailize Request } Request.SessionID := pcCurrentSession.psSrSessionID; Reply := nil; Result := ProcessRequest(ffnmSessionCloseInactTbl, pcTimeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {End !!.06} {----------} function TFFProxyClient.SessionCount : Longint; begin {Retun the number of sessions managed by the ProxyClient} with pcSessions.BeginRead do try Result := Count; finally EndRead; end; end; {----------} function TFFProxyClient.SessionGetCurrent : TffProxySession; begin {Return the current session. This value will be nil if no sessions exist} if Assigned(pcCurrentSession) then Result := pcCurrentSession else begin if SessionCount > 0 then {Return the first session in the list} with pcSessions.BeginRead do try Result := TFFProxySession(Items[0]); finally EndRead; end else {no sessions available} Result := nil; end; end; {----------} function TFFProxyClient.SessionRemove(aSession : TFFProxySession) : TffResult; begin {Remove session from the internal list, and destroy.} if not Assigned(aSession) then begin {aSession parameter is invalid} Result := DBIERR_INVALIDHNDL; Exit; end; Result := DBIERR_NONE; with pcSessions.BeginWrite do try Delete(aSession); {!!.01} finally EndWrite; end; aSession.Free; end; {----------} function TFFProxyClient.SessionSetCurrent(aSession : TFFProxySession ) : TffResult; var Request : TffnmSessionSetCurrentReq; Reply : PffnmSessionSetCurrentReq; ReplyLen : Longint; begin {Set the Client's CurrentSession. This function will accept nil as a valid option} Request.SessionID := aSession.psSrSessionID; Reply := nil; Result := ProcessRequest(ffnmSessionSetCurrent, pcTimeOut, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); // Result := DBIERR_NONE; pcCurrentSession := aSession; end; {----------} function TffProxyClient.GetRebuildStatus(const aRebuildID : Longint; var aIsPresent : Boolean; var aStatus : TffRebuildStatus) : TffResult; var Request : TffnmGetRebuildStatusReq; Reply : PffnmGetRebuildStatusRpy; ReplyLen : Longint; begin { Initiailize Request } Request.RebuildID := aRebuildID; Reply := nil; Result := ProcessRequest(ffnmGetRebuildStatus, pcTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then begin aIsPresent := Reply^.IsPresent; aStatus := Reply^.Status; end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyClient.SetTimeout(const aTimeout : Longint) : TffResult; var Request : TffnmClientSetTimeoutReq; Reply : Pointer; ReplyLen : Longint; begin Result := DBIERR_NONE; if pcTimeout = aTimeout then Exit; pcTimeout := aTimeout; { Initialize request } Request.Timeout := pcTimeout; Reply := nil; Result := ProcessRequest(ffnmClientSetTimeout, pcTimeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); { Calling ffnmClientSetTimeout only returns an error code to Result. } if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {------------------------------------------------------------------------------} {-TFFProxySession--------------------------------------------------------------} constructor TFFProxySession.Create(aClient : TFFProxyClient; aTimeout : Longint); var Request : TffnmSessionAddReq; Reply : PffnmSessionAddRpy; ReplyLen : Longint; Result : TFFResult; begin inherited Create; {Initalize the object} psClient := aClient; psSrSessionID := 0; psTimeout := aTimeout; { Initiailize Request } Request.Timeout := aTimeout; {Create a session object, and add it to the list} Reply := nil; Result := psClient.ProcessRequest(ffnmSessionAdd, psTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); {Make sure that result was valid before we continue} Check(Result); psSrSessionID := Reply^.SessionID; FFFreeMem(Reply, ReplyLen); end; {----------} destructor TFFProxySession.Destroy; var Request : TffnmSessionCloseReq; Reply : Pointer; ReplyLen : Longint; begin if SrSessionID > 0 then begin { Initiailize Request } Request.SessionID := SrSessionID; Reply := nil; Client.ProcessRequest(ffnmSessionClose, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; psClient := nil; inherited Destroy; end; {----------} function TFFProxySession.SetTimeout(aTimeout : Longint) : TffResult; var Request : TffnmSessionSetTimeoutReq; Reply : Pointer; ReplyLen : Longint; begin Result := DBIERR_NONE; if psTimeout = aTimeout then Exit; psTimeout := aTimeout; { Initiailize Request } Request.SessionID := psSrSessionID; Request.Timeout := psTimeout; Reply := nil; Result := Client.ProcessRequest(ffnmSessionSetTimeout, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {------------------------------------------------------------------------------} {-TFFProxyDatabase-------------------------------------------------------------} constructor TFFProxyDatabase.Create(aClient : TFFProxyClient; aLocation : string; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; aIsAlias : Boolean); var RequestAlias : TffnmDatabaseOpenReq; RequestPath : TffnmDatabaseOpenNoAliasReq; ReplyAlias : PffnmDatabaseOpenRpy; ReplyPath : PffnmDatabaseOpenNoAliasRpy; ReplyLen : Longint; Result : TffResult; begin inherited Create; pdInTrans := False; pdSrDatabaseID := 0; pdClient := aClient; pdTimeout := aTimeout; pdStmts := TffProxySQLStmtList.Create; pdTables := TFFProxyCursorList.Create; if aIsAlias then begin { Initiailize Request } RequestAlias.Alias := aLocation; RequestAlias.OpenMode := aOpenMode; RequestAlias.ShareMode := aShareMode; RequestAlias.Timeout := aTimeout; ReplyAlias := nil; Result := Client.ProcessRequest(ffnmDatabaseOpen, pdTimeout, @RequestAlias, SizeOf(RequestAlias), nmdByteArray, Pointer(ReplyAlias), ReplyLen, nmdByteArray); Check(Result); pdSrDatabaseID := ReplyAlias^.DatabaseID; FFFreeMem(ReplyAlias, ReplyLen); end else begin { Initiailize Request } RequestPath.Path := aLocation; RequestPath.OpenMode := aOpenMode; RequestPath.ShareMode := aShareMode; RequestPath.Timeout := aTimeout; ReplyPath := nil; Result := Client.ProcessRequest(ffnmDatabaseOpenNoAlias, pdTimeout, @RequestPath, SizeOf(RequestPath), nmdByteArray, Pointer(ReplyPath), ReplyLen, nmdByteArray); Check(Result); pdSrDatabaseID := ReplyPath^.DatabaseID; FFFreeMem(ReplyPath, ReplyLen); end; end; {----------} destructor TFFProxyDatabase.Destroy; var // Idx : Longint; {!!.03} Request : TffnmDatabaseCloseReq; Reply : Pointer; ReplyLen : Longint; begin {Destroy dependent objects} if InTrans then TransactionRollback; {Begin !!.03} // with pdTables.BeginWrite do // try // for Idx := 0 to Pred(Count) do // TFFProxyCursor(Items[Idx]).Free; // finally // EndWrite; // end; pdTables.Free; pdTables := nil; // with pdStmts.BeginWrite do // try // for Idx := 0 to Pred(Count) do // TffProxySQLStmt(Items[Idx]).Free; // finally // EndWrite; // end; {End !!.03} pdStmts.Free; pdStmts := nil; {Let the server know that we are leaving} if SrDatabaseID > 0 then begin { Initiailize Request } Request.DatabaseID := SrDatabaseID; Reply := nil; Client.ProcessRequest(ffnmDatabaseClose, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {Reset internals} pdSrDatabaseID := 0; pdClient := nil; inherited; end; {----------} function TffProxyDatabase.GetDbFreeSpace(var aFreeSpace : Longint) : TffResult; var Request : TffnmDatabaseGetFreeSpaceReq; Reply : PffnmDatabaseGetFreeSpaceRpy; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := pdSrDatabaseID; Reply := nil; Result := Client.ProcessRequest(ffnmDatabaseGetFreeSpace, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aFreeSpace := Reply^.FreeSpace; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyDatabase.QueryOpen(aCursorID : TffCursorID; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : longInt; aStream : TStream; var aFinalCursorID : TffCursorID) : TffResult; var Cursor : TFFProxyCursor; ListItem : TffIntListItem; begin Cursor := nil; Result := DBIERR_NONE; try Cursor := TFFProxyCursor.CreateSQL(Self, aCursorID, aOpenMode, aShareMode, aTimeout, aStream); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Cursor) then begin ListItem := TffIntListItem.Create(Longint(Cursor)); ListItem.MaintainLinks := False; {!!.02} with pdTables.BeginWrite do try Insert(ListItem); finally EndWrite; end; aFinalCursorID := Longint(Cursor); end; end; {----------} function TFFProxyDatabase.SetTimeout(const aTimeout : Longint) : TffResult; var Request : TffnmDatabaseSetTimeoutReq; Reply : pointer; ReplyLen : Longint; begin Result := DBIERR_NONE; if pdTimeout = aTimeout then Exit; pdTimeout := aTimeout; { Initialize Request } Request.DatabaseID := pdSrDatabaseID; Request.Timeout := aTimeout; Reply := nil; Result := Client.ProcessRequest(ffnmDatabaseSetTimeout, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyDatabase.SQLAlloc(const aTimeout : longInt; var aStmtID : TffSqlStmtID) : TffResult; var ListItem : TffIntListItem; Statement : TffProxySQLStmt; begin Statement := nil; Result := DBIERR_NONE; try Statement := TffProxySQLStmt.Create(Self, aTimeout); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Statement) then begin ListItem := TffIntListItem.Create(Longint(Statement)); with pdStmts.BeginWrite do try Insert(ListItem); finally EndWrite; end; aStmtID := Longint(Statement); end; end; {----------} function TffProxyDatabase.SQLExecDirect(aQueryText : PChar; aOpenMode : TffOpenMode; aTimeout : longInt; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var QueryLen : Longint; ReplyLen : Longint; Request : PffnmSQLExecDirectReq; ReqLen : Longint; SvrCursorID : TffCursorID; begin Assert(Assigned(aStream)); QueryLen := StrLen(aQueryText); ReqLen := SizeOf(TffnmSQLExecDirectReq) - sizeOf(TffVarMsgField) + {!!.05} QueryLen + 1; {!!.05} FFGetZeroMem(Request, ReqLen); try { Prepare the request. } Move(aQueryText^, Request^.Query, QueryLen); Request^.DatabaseID := pdSrDatabaseID; Request^.Timeout := aTimeout; Request^.OpenMode := aOpenMode; Result := pdClient.ProcessRequest(ffnmSQLExecDirect, pdTimeout, Request, ReqLen, nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); { Was the execution successful? } if Result = DBIERR_NONE then begin { Yes. Get the cursorID from the stream & open a proxy cursor. } aStream.Position := 0; aStream.Read(SvrCursorID, sizeOf(SvrCursorID)); if SvrCursorID <> 0 then {!!.11} Result := QueryOpen(SvrCursorID, aOpenMode, smShared, aTimeout, aStream, aCursorID); end; { Assumption: Upper levels are responsible for Stream contents. } finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyDatabase.TableAddIndex(const aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexDesc : TffIndexDescriptor ) : TffResult; var Request : TffnmAddIndexReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; if aCursorID > 0 then Request.CursorID := TFFProxyCursor(aCursorID).SrCursorID else Request.CursorID := 0; Request.TableName := aTableName; Request.IndexDesc := aIndexDesc; Reply := nil; Result := Client.ProcessRequest(ffnmAddIndex, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableBuild(aOverWrite : Boolean; const aTableName : TffTableName; aForServer : Boolean; aDictionary : TffDataDictionary ) : TffResult; var Request : TMemoryStream; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request := TMemoryStream.Create; try Request.Write(pdSrDatabaseID, SizeOf(pdSRDatabaseID)); {!!.10} Request.Write(aOverWrite, SizeOf(aOverWrite)); Request.Write(aTableName, SizeOf(aTableName)); aDictionary.WriteToStream(Request); Reply := nil; Result := Client.ProcessRequest(ffnmBuildTable, Timeout, Request.Memory, Request.Size, nmdStream, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally Request.Free; end; end; {----------} function TFFProxyDatabase.TableClose(aCursor : TFFProxyCursor) : TffResult; begin Result := DBIERR_NONE; with pdTables.BeginWrite do try Delete(aCursor); {!!.01} finally EndWrite; end; aCursor.Free; aCursor := nil; end; {----------} function TFFProxyDatabase.TableDelete(const aTableName : TffTableName ) : TffResult; var Request : TffnmDeleteTableReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.TableName := aTableName; Reply := nil; Result := Client.ProcessRequest(ffnmDeleteTable, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyDatabase.TableDropIndex(aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexName : TffDictItemName; aIndexID : longint) : TffResult; var Request : TffnmDropIndexReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; if aCursorID > 0 then Request.CursorID := TFFProxyCursor(aCursorID).SrCursorID else Request.CursorID := aCursorID; Request.TableName := aTableName; Request.IndexName := aIndexName; Request.IndexNumber := aIndexID; Reply := nil; Result := Client.ProcessRequest(ffnmDropIndex, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyDatabase.TableEmpty(aCursorID : TffCursorID; const aTableName : TffTableName) : TffResult; var Request : TffnmEmptyTableReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; if aCursorID > 0 then Request.CursorID := TFFProxyCursor(aCursorID).SrCursorID else Request.CursorID := aCursorID; Request.TableName := aTableName; Reply := nil; Result := Client.ProcessRequest(ffnmEmptyTable, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableGetDictionary(const aTableName : TffTableName; aForServer : Boolean; aStream : TStream ) : TffResult; var Request : TffnmGetTableDictionaryReq; ReplyLen : Longint; begin Assert(Assigned(aStream)); { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.TableName := FFExtractFileName(aTableName); aStream.Position := 0; Result := Client.ProcessRequest(ffnmGetTableDictionary, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); end; {----------} function TffProxyDatabase.TableExists(const aTableName : TffTableName; var aExists : Boolean) : TffResult; var Request : TffnmDatabaseTableExistsReq; Reply : PffnmDatabaseTableExistsRpy; ReplyLen : Longint; begin Request.DatabaseID := SrDatabaseID; Request.TableName := aTableName; Reply := nil; Result := Client.ProcessRequest(ffnmDatabaseTableExists, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aExists := Reply^.Exists; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableList(const aMask : TffFileNameExt; aList : TList) : TffResult; var Request : TffnmDatabaseTableListReq; ReplyLen : Longint; Stream : TStream; TableDescr : PffTableDescriptor; Count : Longint; begin Stream := TMemoryStream.Create; try { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.Mask := aMask; Result := Client.ProcessRequest(ffnmDatabaseTableList, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Stream), ReplyLen, nmdStream); if ResultOK(Result) then begin {Build the list} Stream.Position := 0; aList.Clear; for Count := 1 to (Stream.Size div SizeOf(TffTableDescriptor)) do begin FFGetMem(TableDescr, SizeOf(TFFTableDescriptor)); Stream.Read(TableDescr^, SizeOf(TffTableDescriptor)); aList.Add(TableDescr); end; end; finally Stream.Free; end; end; function TffProxyDatabase.TableLockedExclusive(const aTableName : TffTableName; var aLocked : Boolean ) : TffResult; var Request : TffnmDatabaseTableLockedExclusiveReq; Reply : PffnmDatabaseTableLockedExclusiveRpy; ReplyLen : Longint; begin Request.DatabaseID := SrDatabaseID; Request.TableName := aTableName; Reply := nil; Result := Client.ProcessRequest(ffnmDatabaseTableLockedExclusive, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aLocked := Reply^.Locked; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableOpen(const aTableName : TffTableName; aForServer : Boolean; aIndexName : TffName; aIndexID : Longint; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var Cursor : TFFProxyCursor; ListItem : TffIntListItem; begin Assert(Assigned(aStream)); Cursor := nil; Result := DBIERR_NONE; try Cursor := TFFProxyCursor.Create(Self, 0, aTableName, aForServer, aIndexName, aIndexID, aOpenMode, aShareMode, aTimeout, aStream); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Cursor) then begin ListItem := TffIntListItem.Create(Longint(Cursor)); ListItem.MaintainLinks := False; {!!.02} with pdTables.BeginWrite do try Insert(ListItem); finally EndWrite; end; aCursorID := Longint(Cursor); end; end; {----------} function TFFProxyDatabase.TablePack(const aTableName : TffTableName; var aRebuildID : Longint) : TffResult; var Request : TffnmPackTableReq; Reply : PffnmPackTableRpy; ReplyLen : Longint; begin aRebuildID := -1; { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.TableName := aTableName; Reply := nil; Result := Client.ProcessRequest(ffnmPackTable, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aRebuildID := Reply^.RebuildID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableRebuildIndex(const aTableName : TffTableName; const aIndexName : TffName; aIndexID : Longint; var aRebuildID : Longint ) : TffResult; var Request : TffnmReindexTableReq; Reply : PffnmReindexTableRpy; ReplyLen : Longint; begin aRebuildID := -1; { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.TableName := aTableName; Request.IndexName := aIndexName; Request.IndexNumber := aIndexID; Reply := nil; Result := Client.ProcessRequest(ffnmReindexTable, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aRebuildID := Reply^.RebuildID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableRename(const aOldName : TffName; const aNewName : TffName) : TffResult; var Request : TffnmRenameTableReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.OldTableName := aOldName; Request.NewTableName := aNewName; Reply := nil; Result := Client.ProcessRequest(ffnmRenameTable, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TableRestructure( const aTableName : TffTableName; aDictionary : TffDataDictionary; aFieldMap : TffStringList; var aRebuildID : Longint ) : TffResult; var I : Longint; NullByte : Byte; Request : TMemoryStream; Reply : PffnmRestructureTableRpy; FieldMapEntry : TffShStr; ReplyLen : Longint; begin NullByte := 0; aRebuildID := -1; { Initialize Request } Request := TMemoryStream.Create; try Request.Write(SrDatabaseID, SizeOf(LongInt)); Request.Write(aTableName, SizeOf(aTableName)); aDictionary.WriteToStream(Request); if Assigned(aFieldMap) then for I := 0 to aFieldMap.Count - 1 do begin FieldMapEntry := aFieldMap[I]; Request.Write(FieldMapEntry, Length(FieldMapEntry) + 1); end; Request.Write(NullByte, SizeOf(NullByte)); Reply := nil; Result := Client.ProcessRequest(ffnmRestructureTable, Timeout, Request.Memory, Request.Size, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aRebuildID := Reply^.RebuildID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally Request.Free; end; end; {----------} function TFFProxyDatabase.TransactionCommit : TffResult; var Request : TffnmEndTransactionReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.ToBeCommitted := True; Reply := nil; Result := Client.ProcessRequest(ffnmEndTransaction, pdTimeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TransactionRollback : TffResult; var Request : TffnmEndTransactionReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.ToBeCommitted := False; Reply := nil; Result := Client.ProcessRequest(ffnmEndTransaction, pdTimeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyDatabase.TransactionStart(aFailSafe : Boolean) : TffResult; var Request : TffnmStartTransactionReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DatabaseID := SrDatabaseID; Request.FailSafe := aFailSafe; Reply := nil; Result := Client.ProcessRequest(ffnmStartTransaction, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); Check(Result); end; //soner FlashBufferHack type TBinaryObjectWriterHack = class(TBinaryObjectWriter) public //procedure FlushBuffer; end; //end soner FlashBufferHack {Start !!.10} {----------} function TFFProxyDatabase.TransactionStartWith(const aFailSafe : Boolean; const aCursorIDs : TffPointerList ) : TffResult; var Reply : Pointer; Inx, aCount, ReplyLen : Longint; Request : TMemoryStream; Writer : TWriter; begin { Initialize Request } Request := TMemoryStream.Create; Writer := TWriter.Create(Request, 4096); try Writer.WriteInteger(pdSrDatabaseID); Writer.WriteBoolean(aFailSafe); aCount := aCursorIDs.Count; Writer.WriteInteger(aCount); for Inx := 0 to Pred(aCount) do { Get the cursorID of the proxy cursor. } Writer.WriteInteger(TffProxyCursor(aCursorIDs[Inx]).SrCursorID); {$ifdef fpc} TBinaryObjectWriterHack(Writer.Driver).FlushBuffer; //soner {$else} Writer.FlushBuffer; {$endif} Reply := nil; Result := Client.ProcessRequest(ffnmStartTransactionWith, Timeout, Request.Memory, Request.Size, nmdStream, Reply, ReplyLen, nmdByteArray); finally Writer.Free; Request.Free; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; // Check(Result); {Deleted !!.11} end; {End !!.10} {------------------------------------------------------------------------------} {-TFFProxyCursor---------------------------------------------------------------} function TFFProxyCursor.BlobCreate(var aBlobNr : TFFInt64) : TffResult; var Request : TffnmCreateBLOBReq; Reply : PffnmCreateBLOBRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCreateBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aBlobNr := Reply^.BLOBNr; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.BLOBDelete(aBlobNr : TFFInt64) : TffResult; var Request : TffnmDeleteBLOBReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BLOBNr := aBlobNr; Reply := nil; Result := Client.ProcessRequest(ffnmDeleteBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.BLOBFree(aBlobNr : TffInt64; aReadOnly : Boolean) : TffResult; var Request : TffnmFreeBLOBReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BLOBNr := aBLOBNr; Request.ReadOnly := aReadOnly; Reply := nil; Result := Client.ProcessRequest(ffnmFreeBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.BLOBGetLength(aBlobNr : TffInt64; var aLength : Longint) : TffResult; var Request : TffnmGetBLOBLengthReq; Reply : PffnmGetBLOBLengthRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BLOBNr := aBLOBNr; Reply := nil; Result := Client.ProcessRequest(ffnmGetBLOBLength, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aLength := Reply^.BLOBLength; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {Begin !!.03} {----------} function TffProxyCursor.BLOBListSegments(aBLOBNr : TffInt64; aStream : TStream) : TffResult; var Request : TffnmListBLOBSegmentsReq; ReplyLen : Longint; begin Request.CursorID := SrCursorID; Request.BLOBNr := aBLOBNr; Result := Client.ProcessRequest(ffnmListBLOBSegments, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); if ResultOK(Result) then aStream.Position := 0; end; {End !!.03} {----------} function TFFProxyCursor.BLOBRead(aBlobNr : TffInt64; aOffset : TffWord32; {!!.06} aLen : TffWord32; {!!.06} var aBLOB; var aBytesRead : TffWord32) {!!.06} : TffResult; var Request : TffnmReadBLOBReq; Reply : PffnmReadBLOBRpy; ReplyLen : longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BLOBNr := aBLOBNr; Request.Offset := aOffset; Request.Len := aLen; Reply := nil; Result := Client.ProcessRequest(ffnmReadBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then begin aBytesRead := Reply^.BytesRead; Move(Reply^.BLOB, aBLOB, aBytesRead); end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.BLOBTruncate(aBlobNr : TffInt64; aBLOBLength : Longint) : TffResult; var Request : TffnmTruncateBLOBReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BLOBNr := aBLOBNr; Request.BLOBLength := aBLOBLength; Reply := nil; Result := Client.ProcessRequest(ffnmTruncateBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.BLOBWrite(aBlobNr : TffInt64; aOffset : Longint; aLen : Longint; var aBLOB) : TffResult; var Request : PffnmWriteBLOBReq; ReqLen : longint; Reply : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(TffnmWriteBLOBReq) - 2 + aLen; FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.BLOBNr := aBLOBNr; Request^.Offset := aOffSet; Request^.Len := aLen; Move(aBLOB, Request^.BLOB, aLen); Reply := nil; Result := Client.ProcessRequest(ffnmWriteBLOB, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.CompareBookmarks(aBookmark1 : PffByteArray; aBookmark2 : PffByteArray; var aCompResult : Longint) : TffResult; var Request : PffnmCursorCompareBMsReq; ReqLen : Longint; Reply : PffnmCursorCompareBMsRpy; pBM2 : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(TffnmCursorCompareBMsReq) - 4 + (2 * BookmarkSize); FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.BookmarkSize := BookmarkSize; Move(aBookMark1^, Request^.Bookmark1, BookmarkSize); pBM2 := PffByteArray(PAnsiChar(@Request^.BookMark1) + BookmarkSize); Move(aBookMark2^, pBM2^, BookmarkSize); Reply := nil; Result := Client.ProcessRequest(ffnmCursorCompareBMs, Timeout, Request, ReqLen, nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aCompResult := Reply^.CompareResult; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} constructor TFFProxyCursor.Create(aDatabase : TFFProxyDatabase; aCursorID : TffCursorID; aTableName : string; aForServer : Boolean; aIndexName : string; aIndexID : Longint; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : Longint; aStream : TStream); var Request : TffnmOpenTableReq; ReplyLen : Longint; Result : TffResult; begin inherited Create; prClient := aDatabase.Client; prDatabase := aDatabase; prSrCursorID := aCursorID; prTableName := aTableName; prForServer := aForServer; prDictionary := TffDataDictionary.Create(4096); prIndexName := aIndexName; prIndexID := aIndexID; prIsSQLCursor := false; prShareMode := aShareMode; prPhyRecSize := 0; prTimeout := aTimeout; if prSrCursorID <> 0 then Exit; {CursorClone operation, nothing more to do} Assert(Assigned(aStream)); { Initialize Request } Request.DatabaseID := Database.SrDatabaseID; Request.TableName := FFExtractTableName(aTableName); Request.IndexName := aIndexName; Request.IndexNumber := aIndexID; Request.OpenMode := aOpenMode; Request.ShareMode := aShareMode; Request.Timeout := prTimeout; Result := Client.ProcessRequest(ffnmOpenTable, prTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); Check(Result); aStream.Position := 0; aStream.Read(prSrCursorID, SizeOf(prSrCursorID)); {save the data dictionary for this table as well} Dictionary.ReadFromStream(aStream); aStream.Read(prIndexID, SizeOf(prIndexID)); prIndexName := prDictionary.IndexName[prIndexID]; prPhyRecSize := prDictionary.RecordLength; end; {----------} constructor TffProxyCursor.CreateSQL(aDatabase : TffProxyDatabase; aCursorID : TffCursorID; aOpenMode : TffOpenMode; aShareMode : TffShareMode; aTimeout : longInt; aStream : TStream); begin inherited Create; Assert(Assigned(aStream)); prClient := aDatabase.Client; prDatabase := aDatabase; prTableName := ''; prForServer := false; prDictionary := TffDataDictionary.Create(ffcl_64k); prIsSQLCursor := True; prShareMode := aShareMode; prTimeout := aTimeout; aStream.Position := 0; aStream.Read(prSrCursorID, SizeOf(prSrCursorID)); { Save the data dictionary for this table. } Dictionary.ReadFromStream(aStream); // aStream.Read(prIndexID, SizeOf(prIndexID)); {Deleted !!.10} prIndexID := 0; {!!.10} prIndexName := prDictionary.IndexName[0]; {!!.10} prPhyRecSize := prDictionary.RecordLength; end; {----------} function TFFProxyCursor.CursorClone(aOpenMode : TFFOpenMode; var aNewCursorID : TFFCursorID) : TffResult; var Request : TffnmCursorCloneReq; Reply : PffnmCursorCloneRpy; ReplyLen : Longint; NewCursor : TffProxyCursor; begin { Initialize Request } Request.CursorID := SrCursorID; Request.OpenMode := aOpenMode; Reply := nil; Result := Client.ProcessRequest(ffnmCursorClone, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then begin {Create a new proxy cursor with the appropriate information} NewCursor := TffProxyCursor.Create(prDatabase, Reply^.CursorID, ''{tableName}, False, {forserver} prIndexName, prIndexID, aOpenMode, smShared, {share mode} prTimeout, nil); NewCursor.prDictionary.Assign(prDictionary); NewCursor.prIndexName := prIndexName; NewCursor.prPhyRecSize := NewCursor.prDictionary.RecordLength; aNewCursorID := Longint(NewCursor); end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} destructor TFFProxyCursor.Destroy; var Request : TffnmCursorCloseReq; Reply : Pointer; ReplyLen : Longint; begin if SrCursorID > 0 then begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Client.ProcessRequest(ffnmCursorClose, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; prSrCursorID := 0; prDictionary.Free; prDictionary := nil; prDatabase := nil; prClient := nil; inherited Destroy; end; {----------} function TFFProxyCursor.FileBLOBAdd(const aFileName : TffFullFileName; var aBlobNr : TffInt64) : TffResult; var Request : TffnmAddFileBLOBReq; Reply : PffnmAddFileBLOBRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.FileName := aFileName; Reply := nil; Result := Client.ProcessRequest(ffnmAddFileBLOB, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aBlobNr := Reply^.BLOBNr; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {Begin !!.02} {----------} function TffProxyCursor.CopyRecords(aSrcCursor : TffProxyCursor; aCopyBLOBs : Boolean) : TffResult; var Request : TffnmCursorCopyRecordsReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.SrcCursorID := aSrcCursor.SrCursorID; Request.DestCursorID := SrCursorID; Request.CopyBLOBs := aCopyBLOBs; Reply := nil; Result := Client.ProcessRequest(ffnmCursorCopyRecords, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {End !!.02} {Begin !!.06} {----------} function TffProxyCursor.DeleteRecords : TffResult; var Request : TffnmCursorDeleteRecordsReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorDeleteRecords, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {End !!.06} {----------} function TFFProxyCursor.GetBookmark(aBookmark : PffByteArray) : TffResult; var Request : TffnmCursorGetBookMarkReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.BookMarkSize := BookMarkSize; Reply := nil; Result := Client.ProcessRequest(ffnmCursorGetBookMark, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if ResultOK(Result) then Move(Reply^, aBookmark^, BookmarkSize); {!!.05} if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.GetBookmarkSize(var aSize : Longint) : TffResult; begin Result := DBIERR_NONE; if prIsSQLCursor then aSize := ffcl_FixedBookmarkSize else aSize := ffcl_FixedBookmarkSize + Dictionary.IndexKeyLength[IndexID]; end; {----------} function TFFProxyCursor.prGetBookmarkSize : Longint; begin GetBookmarkSize(Result); end; {----------} function TFFProxyCursor.RecordDelete(aData : PffByteArray) : TffResult; var Request : TffnmRecordDeleteReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; if aData = nil then Request.RecLen := 0 else Request.RecLen := PhysicalRecordSize; Reply := nil; Result := Client.ProcessRequest(ffnmRecordDelete, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if ((ResultOK(Result)) and {!!.06} (Assigned(aData))) then {!!.06} Move(Reply^, aData^, ReplyLen); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyCursor.RecordDeleteBatch(aBMCount : Longint; aBMLen : Longint; aData : PffByteArray; aErrors : PffLongintArray ) : TffResult; var Request : PffnmRecordDeleteBatchReq; MaxRecs : LongInt; ReqLen : LongInt; iErr : Longint; Reply : Pointer; ReplyLen : Longint; begin MaxRecs := 65500 div aBMLen; if aBMCount > MaxRecs then begin Result := DBIERR_ROWFETCHLIMIT; Exit; end; ReqLen := SizeOf(Request^) - 2 + (aBMLen * aBMCount); FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.BMLen := aBMLen; Request^.BMCount := aBMCount; Move(aData^, Request^.BMArray, aBMCount * aBMLen); Reply := nil; Result := Client.ProcessRequest(ffnmRecordDeleteBatch, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if ResultOK(Result) then begin Move(Reply^, aErrors^, ReplyLen); for iErr := 0 to Pred(aBMCount) do if aErrors^[iErr] <> DBIERR_NONE then begin Result := aErrors^[iErr]; Break; end; end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.RecordExtractKey(aData : PffByteArray; aKey : PffByteArray) : TffResult; var Request : PffnmRecordExtractKeyReq; ReqLen : Longint; Reply : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(TffnmRecordExtractKeyReq) - 2 + PhysicalRecordSize; FFGetZeroMem(Request, ReqLen); try { Initialize Request} Request^.CursorID := SrCursorID; Request^.KeyLen := Dictionary.IndexKeyLength[IndexID]; if aData = nil then Request^.ForCurrentRecord := True else begin Move(aData^, Request^.Data, PhysicalRecordSize); Request^.ForCurrentRecord := False; end; Reply := nil; Result := Client.ProcessRequest(ffnmRecordExtractKey, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if ((ResultOK(Result)) and {!!.06} (Assigned(aKey))) then {!!.06} Move(Reply^, aKey^, ReplyLen); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); {!!.06} end; end; {----------} function TFFProxyCursor.RecordGet(aLockType : TffLockType; aData : PffByteArray) : TffResult; var Request : TffnmRecordGetReq; Reply : Pointer; RpyLen : TffMemSize; begin { Initialize Request } Request.CursorID := SrCursorID; Request.LockType := aLockType; Request.RecLen := PhysicalRecordSize; {server needs it no matter what} Request.BookMarkSize := BookMarkSize; if (aData = nil) then RpyLen := 0 else RpyLen := Request.RecLen; Reply := nil; Result := Client.ProcessRequest(ffnmRecordGet, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, RpyLen, nmdByteArray); if ((Assigned(Reply)) and {!!.06} (Assigned(aData))) then begin {!!.06} Move(Reply^, aData^, RpyLen); FFFreeMem(Reply, RpyLen); end; end; {----------} function TFFProxyCursor.RecordGetBatch(aRecCount : Longint; aRecLen : Longint; var aRecRead : Longint; aData : PffByteArray; var aError : TffResult) : TffResult; var Request : TffnmRecordGetBatchReq; Reply : PffnmRecordGetBatchRpy; ReplyLen : LongInt; begin aRecRead := 0; ReplyLen := SizeOf(Reply^) - 2 + (aRecLen * aRecCount); Request.CursorID := SrCursorID; Request.RecLen := aRecLen; Request.RecCount := aRecCount; Reply := nil; Result := Client.ProcessRequest(ffnmRecordGetBatch, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then begin aRecRead := Reply^.RecCount; Move(Reply^.RecArray, aData^, aRecRead * aRecLen); aError := Reply^.Error; end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.RecordGetForKey(aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray; aData : PffByteArray; aFirstCall : Boolean) : TffResult; var Request : PffnmRecordGetForKeyReq; ReqLen : longint; Reply : Pointer; RpyLen : longint; DataLen : Longint; DictRecLen : Longint; begin DictRecLen := PhysicalRecordSize; if aDirectKey then DataLen := Dictionary.IndexKeyLength[IndexID] else DataLen := DictRecLen; ReqLen := SizeOf(TffnmRecordGetForKeyReq) - 2 + DataLen; FFGetZeroMem(Request, ReqLen); if (aData = nil) then RpyLen := 0 else RpyLen := DictRecLen; try { Initialize Request } Request^.CursorID := SrCursorID; Request^.BookMarkSize := BookMarkSize; Request^.DirectKey := aDirectKey; Request^.FieldCount := aFieldCount; Request^.PartialLen := aPartialLen; Request^.RecLen := DictRecLen; Request^.KeyDataLen := DataLen; Move(aKeyData^, Request^.KeyData, DataLen); Reply := nil; Result := Client.ProcessRequest(ffnmRecordGetForKey, Timeout, Request, ReqLen, nmdByteArray, Reply, RpyLen, nmdByteArray); if ((Assigned(Reply)) and {!!.06} (Assigned(aData))) then begin {!!.06} Move(Reply^, aData^, RpyLen); FFFreeMem(Reply, RpyLen); end; finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.RecordGetNext(aLockType : TffLockType; aData : PffByteArray) : TffResult; var Request : TffnmRecordGetNextReq; ReplyLen : Longint; Reply : Pointer; begin { Initialize Request } Request.CursorID := SrCursorID; Request.LockType := aLockType; if (aData <> nil) then begin Request.RecLen := PhysicalRecordSize; Request.BookMarkSize := BookMarkSize; end else begin Request.RecLen := 0; Request.BookMarkSize := 0; end; Reply := nil; Result := Client.ProcessRequest(ffnmRecordGetNext, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then begin Move(Reply^, aData^, ReplyLen); FFFreeMem(Reply, ReplyLen); end; end; {----------} function TFFProxyCursor.RecordGetPrior(aLockType : TffLockType; aData : PffByteArray) : TffResult; var Request : TffnmRecordGetPrevReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.LockType := aLockType; if (aData <> nil) then begin Request.RecLen := PhysicalRecordSize; Request.BookMarkSize := BookMarkSize; end else begin Request.RecLen := 0; Request.BookMarkSize := 0; end; Reply := nil; Result := Client.ProcessRequest(ffnmRecordGetPrev, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then begin Move(Reply^, aData^, ReplyLen); FFFreeMem(Reply, ReplyLen); end; end; {----------} function TFFProxyCursor.RecordInsert(aLockType : TffLockType; aData : PffByteArray) : TffResult; var Request : PffnmRecordInsertReq; ReqLen : Longint; Reply : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(Request^) - 2 + PhysicalRecordSize; FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.LockType := aLockType; Request^.RecLen := PhysicalRecordSize; Request^.BookMarkSize := BookMarkSize; Move(aData^, Request^.Data, PhysicalRecordSize); Reply := nil; Result := Client.ProcessRequest(ffnmRecordInsert, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.RecordInsertBatch(aRecCount : Longint; aRecLen : Longint; aData : PffByteArray; aErrors : PffLongintArray ) : TffResult; var Request : PffnmRecordInsertBatchReq; MaxRecs : LongInt; ReqLen : LongInt; iErr : Longint; Reply : Pointer; ReplyLen : Longint; begin MaxRecs := 65500 div aRecLen; if aRecCount > MaxRecs then begin Result := DBIERR_ROWFETCHLIMIT; Exit; end; ReqLen := SizeOf(Request^) - 2 + (aRecLen * aRecCount); FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.RecLen := aRecLen; Request^.RecCount := aRecCount; Move(aData^, Request^.RecArray, aRecCount * aRecLen); Reply := nil; Result := Client.ProcessRequest(ffnmRecordInsertBatch, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if ResultOK(Result) then begin Move(Reply^, aErrors^, ReplyLen); for iErr := 0 to Pred(aRecCount) do if aErrors^[iErr] <> DBIERR_NONE then begin Result := aErrors^[iErr]; Break; end; end; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TffProxyCursor.RecordIsLocked(aLockType : TffLockType; var aIsLocked : boolean) : TffResult; var Request : TffnmRecordIsLockedReq; Reply : PffnmRecordIsLockedRpy; ReplyLen : Longint; begin Request.CursorID := SrCursorID; Request.LockType := aLockType; Reply := nil; Result := Client.ProcessRequest(ffnmRecordIsLocked, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aIsLocked := Reply^.IsLocked; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.RecordModify(aData : PffByteArray; aRelLock : Boolean) : TffResult; var Request : PffnmRecordModifyReq; ReqLen : Longint; Reply : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(Request^) - 2 + PhysicalRecordSize; FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.RelLock := aRelLock; Request^.RecLen := PhysicalRecordSize; Request^.BookMarkSize := BookMarkSize; Move(aData^, Request^.Data, PhysicalRecordSize); Reply := nil; Result := Client.ProcessRequest(ffnmRecordModify, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.RecordRelLock(aAllLocks : Boolean) : TffResult; var Request : TffnmRecordRelLockReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.AllLocks := aAllLocks; Reply := nil; Result := Client.ProcessRequest(ffnmRecordRelLock, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyCursor.TableGetAutoInc(var aValue : TffWord32) : TffResult; var Request : TffnmGetTableAutoIncValueReq; Reply : PffnmGetTableAutoIncValueRpy; ReplyLen : Longint; begin Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmGetTableAutoIncValue, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aValue := Reply^.AutoIncValue; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {Begin !!.03} {----------} function TffProxyCursor.ListBLOBFreeSpace(const aInMemory : Boolean; aStream : TStream) : TffResult; var Request : TffnmGetBLOBFreeSpaceReq; ReplyLen : Longint; begin Request.CursorID := SrCursorID; Request.InMemory := aInMemory; Result := Client.ProcessRequest(ffnmListBLOBFreeSpace, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); if ResultOK(Result) then aStream.Position := 0; end; {End !!.03} {----------} function TffProxyCursor.OverrideFilter(aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; var ReqSize : Longint; Request : PffnmCursorOverrideFilterReq; ExprTree : CANExpr; Reply : Pointer; ReplyLen : Longint; begin if not Assigned(aExpression) then begin aExpression := @ExprTree; FillChar(ExprTree, SizeOf(ExprTree), 0); ExprTree.iVer := CANEXPRVERSION; ExprTree.iTotalSize := SizeOf(ExprTree); end; ReqSize := (SizeOf(Request^) - 2 + aExpression^.iTotalSize); FFGetMem(Request, ReqSize); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.Timeout := aTimeout; Move(aExpression^, Request^.ExprTree, aExpression^.iTotalSize); Reply := nil; Result := Client.ProcessRequest(ffnmCursorOverrideFilter, Timeout, Pointer(Request), ReqSize, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqSize); end; end; {----------} function TFFProxyCursor.ResetRange : TffResult; var Request : TffnmCursorResetRangeReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorResetRange, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TffProxyCursor.RestoreFilter : TffResult; var Request : TffnmCursorRestoreFilterReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorRestoreFilter, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.SetFilter(aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; var ReqSize : Longint; Request : PffnmCursorSetFilterReq; ExprTree : CANExpr; Reply : Pointer; ReplyLen : Longint; begin if not Assigned(aExpression) then begin aExpression := @ExprTree; FillChar(ExprTree, SizeOf(ExprTree), 0); ExprTree.iVer := CANEXPRVERSION; ExprTree.iTotalSize := SizeOf(ExprTree); end; ReqSize := (SizeOf(Request^) - 2 + aExpression^.iTotalSize); FFGetMem(Request, ReqSize); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.Timeout := aTimeout; Move(aExpression^, Request^.ExprTree, aExpression^.iTotalSize); Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetFilter, Timeout, Pointer(Request), ReqSize, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqSize); end; end; {----------} function TFFProxyCursor.SetRange(aDirectKey : Boolean; aFieldCount1 : Longint; aPartialLen1 : Longint; aKeyData1 : PffByteArray; aKeyIncl1 : Boolean; aFieldCount2 : Longint; aPartialLen2 : Longint; aKeyData2 : PffByteArray; aKeyIncl2 : Boolean) : TffResult; var Request : PffnmCursorSetRangeReq; ReqLen : Longint; KeyLen1 : Longint; KeyLen2 : Longint; Reply : Pointer; ReplyLen : Longint; ReqKeyData2 : pointer; begin {calculate sizes} if aKeyData1 = nil then KeyLen1 := 0 else if aDirectKey then KeyLen1 := Dictionary.IndexKeyLength[ IndexID ] else KeyLen1 := PhysicalRecordSize; if aKeyData2 = nil then KeyLen2 := 0 else if aDirectKey then KeyLen2 := Dictionary.IndexKeyLength[ IndexID ] else KeyLen2 := PhysicalRecordSize; {now, we know how large the Request is} ReqLen := SizeOf(Request^) - 4 + KeyLen1 + KeyLen2; {allocate and clear it} FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.DirectKey := aDirectKey; Request^.FieldCount1 := aFieldCount1; Request^.PartialLen1 := aPartialLen1; Request^.KeyLen1 := KeyLen1; Request^.KeyIncl1 := aKeyIncl1; Request^.FieldCount2 := aFieldCount2; Request^.PartialLen2 := aPartialLen2; Request^.KeyLen2 := KeyLen2; Request^.KeyIncl2 := aKeyIncl2; Move(aKeyData1^, Request^.KeyData1, KeyLen1); ReqKeyData2 := PffByteArray(PAnsiChar(@Request^.KeyData1) + KeyLen1); Move(akeyData2^, ReqKeyData2^, KeyLen2); Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetRange, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.SetTimeout(aTimeout : Longint) : TffResult; var Request : TffnmCursorSetTimeoutReq; Reply : Pointer; ReplyLen : Longint; begin Result := DBIERR_NONE; if prTimeout = aTimeout then Exit; prTimeout := aTimeout; { Initialize Request } Request.CursorID := SrCursorID; Request.Timeout := prTimeout; Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetTimeout, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.SetToBegin : TffResult; var Request : TffnmCursorSetToBeginReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetToBegin, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.SetToBookmark(aBookmark : PffByteArray) : TffResult; var Request : PffnmCursorSetToBookmarkReq; ReqLen : Longint; Reply : Pointer; ReplyLen : Longint; begin ReqLen := SizeOf(Request^) - 2 + BookMarkSize; FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.BookmarkSize := BookMarkSize; Move(aBookmark^, Request^.Bookmark, BookMarkSize); Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetToBookmark, Timeout, Request, ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.SetToCursor(aSourceCursor : TFFProxyCursor ) : TffResult; var Request : TffnmCursorSetToCursorReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.DestCursorID := SrCursorID; Request.SrcCursorID := aSourceCursor.SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetToCursor, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.SetToEnd : TffResult; var Request : TffnmCursorSetToEndReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetToEnd, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.SetToKey(aSearchAction : TffSearchKeyAction; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray) : TffResult; var Request : PffnmCursorSetToKeyReq; ReqLen : Longint; KeyDataLen : Longint; Reply : Pointer; ReplyLen : Longint; begin if aDirectKey then KeyDataLen := Dictionary.IndexKeyLength[IndexID] else KeyDataLen := PhysicalRecordSize; ReqLen := SizeOf(TffnmCursorSetToKeyReq) - 2 + KeyDataLen; FFGetZeroMem(Request, ReqLen); try { Initialize Request } Request^.CursorID := SrCursorID; Request^.Action := aSearchAction; Request^.DirectKey := aDirectKey; Request^.FieldCount := aFieldCount; Request^.PartialLen := aPartialLen; Request^.KeyDataLen := KeyDataLen; Move(aKeyData^, Request^.KeyData, KeyDataLen); Reply := nil; Result := Client.ProcessRequest(ffnmCursorSetToKey, Timeout, Pointer(Request), ReqLen, nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); finally FFFreeMem(Request, ReqLen); end; end; {----------} function TFFProxyCursor.SwitchToIndex(aIndexName : TffDictItemName; aIndexID : Longint; aPosnOnRec : Boolean) : TffResult; var Request : TffnmCursorSwitchToIndexReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.IndexName := aIndexName; Request.IndexNumber := aIndexID; Request.PosnOnRec := aPosnOnRec; Reply := nil; Result := Client.ProcessRequest(ffnmCursorSwitchToIndex, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); if (Request.IndexName <> '') then begin prIndexID := Dictionary.GetIndexFromName(Request.IndexName); prIndexName := aIndexName; end else begin prIndexID := aIndexID; prIndexName := Dictionary.IndexName[aIndexID]; end; end; {----------} function TFFProxyCursor.TableGetRecCount(var aRecCount : Longint) : TffResult; var Request : TffnmGetTableRecCountReq; Reply : PffnmGetTableRecCountRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmGetTableRecCount, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aRecCount := Reply^.RecCount; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {Begin !!.07} {----------} function TFFProxyCursor.TableGetRecCountAsync(var aTaskID : Longint) : TffResult; var Request : TffnmGetTableRecCountAsyncReq; Reply : PffnmGetTableRecCountAsyncRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Reply := nil; Result := Client.ProcessRequest(ffnmGetTableRecCountAsync, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aTaskID := Reply^.RebuildID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {End !!.07} {----------} function TFFProxyCursor.TableIsLocked(aLockType : TffLockType; var aIsLocked : Boolean) : TffResult; var Request : TffnmIsTableLockedReq; Reply : PffnmIsTableLockedRpy; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.LockType := aLockType; Reply := nil; Result := Client.ProcessRequest(ffnmIsTableLocked, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aIsLocked := Reply^.IsLocked; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.TableLockAcquire(aLockType : TffLockType) : TffResult; var Request : TffnmAcqTableLockReq; Reply : Pointer; ReplyLen : Longint; begin { Initialzie Request } Request.CursorID := SrCursorID; Request.LockType := aLockType; Reply := nil; Result := Client.ProcessRequest(ffnmAcqTableLock, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.TableLockRelease(aAllLocks : Boolean) : TffResult; var Request : TffnmRelTableLockReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.AllLocks := aAllLocks; Reply := nil; Result := Client.ProcessRequest(ffnmRelTableLock, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} function TFFProxyCursor.TableSetAutoInc(aValue : TffWord32) : TffResult; var Request : TffnmSetTableAutoIncValueReq; Reply : Pointer; ReplyLen : Longint; begin { Initialize Request } Request.CursorID := SrCursorID; Request.AutoIncValue := aValue; Reply := nil; Result := Client.ProcessRequest(ffnmSetTableAutoIncValue, Timeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {------------------------------------------------------------------------------} {-TffProxySQLStmt--------------------------------------------------------------} constructor TffProxySQLStmt.Create(aDatabase : TffProxyDatabase; const aTimeout : longInt); var Request : TffnmSQLAllocReq; Reply : PffnmSQLAllocRpy; ReplyLen : Longint; Result : TffResult; begin inherited Create; psClient := aDatabase.Client; psDatabase := aDatabase; psTimeout := aTimeout; { Initialize Request } Request.DatabaseID := aDatabase.SrDatabaseID; Request.Timeout := aTimeout; Reply := nil; Result := psClient.ProcessRequest(ffnmSQLAlloc, psTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); Check(Result); psSrStmtID := Reply^.StmtID; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; {----------} destructor TffProxySQLStmt.Destroy; var Request : TffnmSQLFreeReq; Reply : Pointer; ReplyLen : Longint; begin if psSrStmtID > 0 then begin { Initialize Request } Request.StmtID := psSrStmtID; Reply := nil; psClient.ProcessRequest(ffnmSQLFree, psTimeout, @Request, SizeOf(Request), nmdByteArray, Reply, ReplyLen, nmdByteArray); if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; psSrStmtID := 0; psDatabase := nil; inherited Destroy; end; {----------} function TffProxySQLStmt.Exec(aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var Request : TffnmSQLExecReq; ReplyLen : Longint; SvrCursorID : TffCursorID; begin Assert(Assigned(aStream)); { Initialize Request } Request.StmtID := psSrStmtID; Request.OpenMode := aOpenMode; Result := psClient.ProcessRequest(ffnmSQLExec, psTimeout, @Request, SizeOf(Request), nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); { Was the execution successful? } if Result = DBIERR_NONE then begin { Yes. Get the cursorID from the stream & open a proxy cursor. } aStream.Position := 0; aStream.Read(SvrCursorID, sizeOf(SvrCursorID)); aCursorID := SvrCursorID; if aCursorID <> 0 then Result := psDatabase.QueryOpen(SvrCursorID, aOpenMode, smShared, psTimeout, aStream, aCursorID); end; { Assumption: If an error occurs then the TffQuery component is responsible for displaying the error message returned from the server. } end; {----------} function TffProxySQLStmt.Prepare(aQueryText: PChar; aStream : TStream) : TffResult; var QueryLen : Longint; ReqLen : Longint; Request : PffnmSQLPrepareReq; ReplyLen : Longint; begin Assert(Assigned(aStream)); QueryLen := StrLen(aQueryText); ReqLen := SizeOf(TffnmSQLPrepareReq) - SizeOf(TffVarMsgField) + QueryLen + 1; FFGetZeroMem(Request, ReqLen); try { Prepare the request. } Request.StmtID := psSrStmtID; Move(aQueryText^, Request^.Query, QueryLen); Result := psClient.ProcessRequest(ffnmSQLPrepare, psTimeout, Request, ReqLen, nmdByteArray, Pointer(aStream), ReplyLen, nmdStream); { Assumption: Upper levels are responsible for Stream contents. } finally FFFreeMem(Request, ReqLen); end; end; {----------} function TffProxySQLStmt.SetParams(aNumParams : word; aParamDescs : pointer; aDataBuffer : PffByteArray; aDataLen : Longint; aStream : TStream) : TffResult; var ReplyLen : Longint; Stream : TMemoryStream; begin Assert(Assigned(aStream)); { Output stream is expected to be: StmtID (longint) NumParams (word) ParamList (array of TffSqlParamInfo) BufLen (longint; size of DataBuffer) DataBuffer (data buffer) } Stream := TMemoryStream.Create; try Stream.Write(psSrStmtID, SizeOf(psSrStmtID)); Stream.Write(aNumParams, SizeOf(aNumParams)); Stream.Write(aParamDescs^, aNumParams * SizeOf(TffSqlParamInfo)); Stream.Write(aDataLen, sizeOf(aDataLen)); Stream.Write(aDataBuffer^, aDataLen); Stream.Position := 0; Result := psClient.ProcessRequest(ffnmSQLSetParams, psTimeout, Stream.Memory, Stream.Size, nmdStream, Pointer(aStream), ReplyLen, nmdStream); finally Stream.Free; end; end; {------------------------------------------------------------------------------} {-TFFRemoteServerEngine--------------------------------------------------------} function TFFRemoteServerEngine.BLOBCreate(aCursorID : TffCursorID; var aBlobNr : TffInt64) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BlobCreate(aBlobNr); end; {----------} function TFFRemoteServerEngine.BLOBDelete(aCursorID : TffCursorID; aBlobNr : TffInt64) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBDelete(aBlobNr); end; {----------} function TFFRemoteServerEngine.BLOBFree(aCursorID : TffCursorID; aBlobNr : TffInt64; readOnly : Boolean) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBFree(aBlobNr, ReadOnly); end; {----------} function TFFRemoteServerEngine.BLOBGetLength(aCursorID : TffCursorID; aBlobNr : TffInt64; var aLength : Longint) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBGetLength(aBlobNr, aLength); end; {Begin !!.03} {----------} function TffRemoteServerEngine.BLOBListSegments(aCursorID : TffCursorID; aBLOBNr : TffInt64; aStream : TStream) : TffResult; var Cursor : TffProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBListSegments(aBLOBNr, aStream); end; {End !!.03} {----------} function TFFRemoteServerEngine.BLOBRead(aCursorID : TffCursorID; aBlobNr : TffInt64; aOffset : TffWord32; {!!.06} aLen : TffWord32; {!!.06} var aBLOB; var aBytesRead : TffWord32) {!!.06} : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBRead(aBlobNr, aOffset, aLen, aBLOB, aBytesRead); end; {----------} function TFFRemoteServerEngine.BLOBTruncate(aCursorID : TffCursorID; aBlobNr : TffInt64; aBLOBLength : Longint) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBTruncate(aBlobNr, aBLOBLength); end; {----------} function TFFRemoteServerEngine.BLOBWrite(aCursorID : TffCursorID; aBlobNr : TffInt64; aOffset : Longint; aLen : Longint; var aBLOB) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.BLOBWrite(aBlobNr, aOffset, aLen, aBLOB); end; {Begin !!.01} {----------} function TffRemoteServerEngine.RemoteRestart(const aClientID : TffClientID) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.RemoteRestart; end; {----------} function TffRemoteServerEngine.RemoteStart(const aClientID : TffClientID) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.RemoteStart; end; {----------} function TffRemoteServerEngine.RemoteStop(const aClientID : TffClientID) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.RemoteStop; end; {End !!.01} {----------} procedure TFFRemoteServerEngine.scInitialize; begin { do nothing } end; {----------} procedure TffRemoteServerEngine.scPrepareForShutdown; begin { do nothing } end; {----------} procedure TffRemoteServerEngine.scShutdown; begin { do nothing } end; {----------} procedure TffRemoteServerEngine.scStartup; begin { do nothing } end; {----------} function TffRemoteServerEngine.bseGetAutoSaveCfg : Boolean; begin {This is here to kill warnings. Clients shouldn't care about the RSE's NoAutoSaveCfg setting.} Result := False; end; {----------} function TFFRemoteServerEngine.bseGetReadOnly : Boolean; var Client : TffProxyClient; begin Client := GetDefaultClient; if Assigned(Client) then Result := Client.IsReadOnly else Result := False; end; {--------} procedure TFFRemoteServerEngine.bseSetAutoSaveCfg(aValue : Boolean); {!!.01 - Start} begin {do nothing} end; {--------} procedure TFFRemoteServerEngine.bseSetReadOnly(aValue : Boolean); begin {do nothing} end; {--------} {!!.01 - End} procedure TFFRemoteServerEngine.FFNotificationEx(const AOp : Byte; AFrom : TffComponent; const AData : TffWord32); var CL : TFFProxyClient; ClIdx : Longint; ClFound : Boolean; begin inherited; {!!.11} if (AFrom = Transport) then if ((AOp = ffn_Destroy) or (AOp = ffn_Remove)) then begin FFNotifyDependents(ffn_Deactivate); rsTransport := nil; end else if (AOp = ffn_Deactivate) then FFNotifyDependents(ffn_Deactivate) else if (AOp = ffn_ConnectionLost) then begin { If we manage this client, then notify depenents that connection is lost. It is up to the baseclient dependents to check the data parameter to see if this notification affects them.} CL := nil; ClFound := False; with ClientList.BeginRead do try for ClIdx := 0 to Pred(ClientList.Count) do begin CL := TFFProxyClient(ClientList[ClIdx].Key^); if CL.pcSrClientID = AData then begin ClFound := True; Break; end; end; finally EndRead; end; if CLFound then begin ForceClosing(Longint(CL)); ClientRemove(Longint(CL)); FFNotifyDependentsEx(ffn_ConnectionLost, Longint(CL)) end; end; end; {Begin !!.07} {--------} procedure TffRemoteServerEngine.Log(const aMsg : string); begin FEventLog.WriteString(aMsg); end; {--------} procedure TffRemoteServerEngine.LogAll(const Msgs : array of string); begin FEventLog.WriteStrings(Msgs); end; {--------} procedure TffRemoteServerEngine.LogFmt(const aMsg : string; args : array of const); begin FEventLog.WriteString(format(aMsg, args)); end; {End !!.07} {--------} function TFFRemoteServerEngine.CheckClientIDAndGet(aClientID : TffClientID; var aClient : TffProxyClient ) : TffResult; begin Result := DBIERR_INVALIDHNDL; aClient := nil; try if (TObject(aClientID) is TFFProxyClient) then begin aClient := TffProxyClient(aClientID); Result := DBIERR_NONE; end; except { An exception may be raised if the ID is bogus. Swallow the exception.} end; end; {----------} function TFFRemoteServerEngine.CheckCursorIDAndGet(aCursorID : TffCursorID; var aCursor : TffProxyCursor ) : TffResult; begin Result := DBIERR_INVALIDHNDL; aCursor := nil; try if (TObject(aCursorID) is TFFProxyCursor) then begin aCursor := TffProxyCursor(aCursorID); Result := DBIERR_NONE; end; except { An exception may be raised if the ID is bogus. Swallow the exception.} end; end; {----------} function TffRemoteServerEngine.CheckStmtIDAndGet(aStmtID : TffSqlStmtID; var aStmt : TffProxySQLStmt) : TffResult; begin Result := DBIERR_INVALIDHNDL; aStmt := nil; try if (TObject(aStmtID) is TffProxySQLStmt) then begin aStmt := TffProxySQLStmt(aStmtID); Result := DBIERR_NONE; end; except { An exception may be raised if the ID is bogus. Swallow the exception.} end; end; {----------} function TFFRemoteServerEngine.CheckDatabaseIDAndGet( aDatabaseID : TffDatabaseID; var aDatabase : TffProxyDatabase ) : TffResult; begin Result := DBIERR_INVALIDHNDL; aDatabase := nil; try if (TObject(aDatabaseID) is TFFProxyDatabase) then begin aDatabase := TffProxyDatabase(aDatabaseID); Result := DBIERR_NONE; end; except { An exception may be raised if the ID is bogus. Swallow the exception.} end; end; {----------} function TFFRemoteServerEngine.CheckSessionIDAndGet(aClientID : TffClientID; aSessionID : TffSessionID; var aClient : TffProxyClient; var aSession : TffProxySession ) : TffResult; begin aSession := nil; aClient := nil; Result := CheckClientIDAndGet(aClientID, aClient); if (Result = DBIERR_NONE) then begin try if (TObject(aSessionID) is TFFProxySession) then begin aSession := TffProxySession(aSessionID) end; except { An exception may be raised if the ID is bogus. Swallow the exception.} end; end; end; {----------} function TFFRemoteServerEngine.ClientAdd(var aClientID : TffClientID; const aClientName : TffNetName; const aUserID : TffName; const aTimeout : Longint; var aHash : TffWord32 ) : TffResult; var Client : TFFProxyClient; ListItem : TffIntListItem; begin Result := DBIERR_NONE; Client := nil; {Create client object} try Client := TFFProxyClient.Create(rsTransport, aUserID, aHash, aTimeOut); except on E:Exception do if (E is EffException) or (E is EffDatabaseError) or (E is EffServerComponentError) then Result := EffException(E).ErrorCode; end; if ResultOK(Result) and Assigned(Client) then begin {Add to the internal list} ListItem := TffIntListItem.Create(Longint(Client)); with rsClientList.BeginWrite do try Insert(ListItem); finally EndWrite; end; {Set the return value} aClientID := Longint(Client); end; end; {Begin !!.11} function TffRemoteServerEngine.ClientAddEx(var aClientID : TffClientID; const aClientName : TffNetName; const aUserID : TffName; const aTimeout : Longint; const aClientVersion : Longint; var aHash : TffWord32) : TffResult; begin Result := ClientAdd(aClientID, aClientName, aUserID, aTimeout, aHash); end; {End !!.11} {----------} function TFFRemoteServerEngine.ClientRemove(aClientID : TffClientID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then {Remove from the internal list, and free} with rsClientList.BeginWrite do try Delete(Client); {!!.01} Client.Free; finally EndWrite; end; end; {----------} function TFFRemoteServerEngine.ClientSetTimeout(const aClientID : TffClientID; const aTimeout : Longint ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.SetTimeout(aTimeout); end; {----------} constructor TFFRemoteServerEngine.Create(aOwner : TComponent); begin inherited Create(aOwner); rsClientList := TFFProxyClientList.Create; rsTimeout := 0; rsTransport := nil; with RemoteServerEngines.BeginWrite do try Insert(TffIntListItem.Create(Longint(Self))); finally EndWrite; end; end; {----------} function TFFRemoteServerEngine.CursorClone(aCursorID : TffCursorID; aOpenMode : TffOpenMode; var aNewCursorID : TffCursorID ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.CursorClone(aOpenMode, aNewCursorID); end; {----------} function TFFRemoteServerEngine.CursorClose(aCursorID : TffCursorID) : TffResult; var Cursor : TFFProxyCursor; Database : TFFProxyDatabase; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then begin Database := Cursor.Database; Result := Database.TableClose(Cursor); end; end; {----------} function TFFRemoteServerEngine.CursorCompareBookmarks( aCursorID : TffCursorID; aBookmark1 : PffByteArray; aBookmark2 : PffByteArray; var aCompResult : Longint ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.CompareBookmarks(aBookmark1, aBookmark2, aCompResult); end; {Begin !!.02} {----------} function TffRemoteServerEngine.CursorCopyRecords(aSrcCursorID, aDestCursorID : TffCursorID; aCopyBLOBs : Boolean) : TffResult; var DestCursor, SrcCursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aDestCursorID, DestCursor); if ResultOK(Result) then begin Result := CheckCursorIDAndGet(aSrcCursorID, SrcCursor); if ResultOK(Result) then Result := DestCursor.CopyRecords(SrcCursor, aCopyBLOBs); end; end; {End !!.02} {Begin !!.06} {----------} function TffRemoteServerEngine.CursorDeleteRecords(aCursorID : TffCursorID) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.DeleteRecords; end; {End !!.06} {----------} function TFFRemoteServerEngine.CursorGetBookmark(aCursorID : TffCursorID; aBookmark : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.GetBookmark(aBookmark); end; {Begin !!.03} {----------} function TffRemoteServerEngine.CursorListBLOBFreeSpace(aCursorID : TffCursorID; const aInMemory : Boolean; aStream : TStream) : TffResult; var Cursor : TffProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.ListBLOBFreeSpace(aInMemory, aStream); end; {End !!.03} {----------} function TffRemoteServerEngine.CursorOverrideFilter(aCursorID : longint; aExpression : pCANExpr; aTimeout : TffWord32) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.OverrideFilter(aExpression, aTimeout); end; {----------} function TffRemoteServerEngine.CursorRestoreFilter(aCursorID : longInt) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RestoreFilter; end; {----------} function TFFRemoteServerEngine.CursorGetBookmarkSize(aCursorID : TffCursorID; var aSize : Longint ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.GetBookmarkSize(aSize); end; {----------} function TFFRemoteServerEngine.CursorResetRange(aCursorID : TffCursorID ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.ResetRange; end; {----------} function TFFRemoteServerEngine.CursorSetFilter(aCursorID : TffCursorID; aExpression : pCANExpr; aTimeout : TffWord32 ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetFilter(aExpression, aTimeout); end; {----------} function TFFRemoteServerEngine.CursorSetRange(aCursorID : TffCursorID; aDirectKey : Boolean; aFieldCount1 : Longint; aPartialLen1 : Longint; aKeyData1 : PffByteArray; aKeyIncl1 : Boolean; aFieldCount2 : Longint; aPartialLen2 : Longint; aKeyData2 : PffByteArray; aKeyIncl2 : Boolean ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetRange(aDirectKey, aFieldCount1, aPartialLen1, aKeyData1, aKeyIncl1, aFieldCount2, aPartialLen2, aKeyData2, aKeyIncl2); end; {----------} function TFFRemoteServerEngine.CursorSetTimeout(const aCursorID : TffCursorID; const aTimeout : Longint ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetTimeout(aTimeout); end; {----------} function TFFRemoteServerEngine.CursorSetToBegin(aCursorID : TffCursorID ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetToBegin; end; {----------} function TFFRemoteServerEngine.CursorSetToBookmark(aCursorID : TffCursorID; aBookmark : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetToBookmark(aBookmark); end; {----------} function TFFRemoteServerEngine.CursorSetToCursor(aDestCursorID : TffCursorID; aSrcCursorID : TffCursorID ) : TffResult; var DestCursor : TFFProxyCursor; SourceCursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aDestCursorID, DestCursor); if ResultOK(Result) then Result := CheckCursorIDAndGet(aSrcCursorID, SourceCursor); if ResultOK(Result) then Result := DestCursor.SetToCursor(SourceCursor); end; {----------} function TFFRemoteServerEngine.CursorSetToEnd(aCursorID : TffCursorID ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetToEnd; end; {----------} function TFFRemoteServerEngine.CursorSetToKey( aCursorID : TffCursorID; aSearchAction : TffSearchKeyAction; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SetToKey(aSearchAction, aDirectKey, aFieldCount, aPartialLen, aKeyData); end; {----------} function TFFRemoteServerEngine.CursorSwitchToIndex(aCursorID : TffCursorID; aIndexName : TffDictItemName; aIndexID : Longint; aPosnOnRec : Boolean ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.SwitchToIndex(aIndexName, aIndexID, aPosnOnRec); end; {----------} function TFFRemoteServerEngine.DatabaseAddAlias(const aAlias : TffName; const aPath : TffPath; aCheckSpace : Boolean; {!!.11} const aClientID : TffClientID) : TffResult; var Client : TffProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseAddAlias(aAlias, aPath, aCheckSpace); {!!.11} end; {----------} function TFFRemoteServerEngine.DatabaseAliasList(aList : TList; aClientID : TffClientID) : TffResult; var Client : TffProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseAliasList(aList); end; {----------} function TFFRemoteServerEngine.RecoveryAliasList(aList : TList; aClientID : TffClientID) : TffResult; begin Assert(False, 'RecoveryAliasList unsupported for TffRemoteServerEngine.'); Result := DBIERR_NOTSUPPORTED; end; {----------} function TFFRemoteServerEngine.DatabaseChgAliasPath(aAlias : TffName; aNewPath : TffPath; aCheckSpace : Boolean; {!!.11} aClientID : TffClientID) : TffResult; var Client : TffProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseChgAliasPath(aAlias, aNewPath, aCheckSpace) {!!.11} end; {----------} function TFFRemoteServerEngine.DatabaseClose(aDatabaseID : TffDatabaseID ) : TffResult; var Database : TFFProxyDatabase; Client : TFFProxyClient; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then begin Client := Database.Client; Result := Client.DatabaseClose(Database); end; end; {----------} function TFFRemoteServerEngine.DatabaseDeleteAlias(aAlias : TffName; aClientID : TffClientID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseDeleteAlias(aAlias) end; {----------} function TFFRemoteServerEngine.DatabaseGetAliasPath(aAlias : TffName; var aPath : TffPath; aClientID : TffClientID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseGetAliasPath(aAlias, aPath) end; {----------} function TFFRemoteServerEngine.DatabaseGetFreeSpace(const aDatabaseID : TffDatabaseID; var aFreeSpace : Longint ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.GetDbFreeSpace(aFreeSpace); end; {----------} function TffRemoteServerEngine.DatabaseModifyAlias(const aClientID : TffClientID; const aAlias : TffName; const aNewName : TffName; const aNewPath : TffPath; aCheckSpace : Boolean) {!!.11} : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseModifyAlias(aAlias, aNewName, aNewPath, aCheckSpace) {!!.11} end; {----------} function TFFRemoteServerEngine.DatabaseOpen(aClientID : TffClientID; const aAlias : TffName; const aOpenMode : TffOpenMode; const aShareMode : TffShareMode; const aTimeout : Longint; var aDatabaseID : TffDatabaseID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseOpen(aAlias, aOpenMode, aShareMode, aTimeout, aDatabaseID); end; {----------} function TFFRemoteServerEngine.DatabaseOpenNoAlias(aClientID : TffClientID; const aPath : TffPath; const aOpenMode : TffOpenMode; const aShareMode : TffShareMode; const aTimeout : Longint; var aDatabaseID : TffDatabaseID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.DatabaseOpenNoAlias(aPath, aOpenMode, aShareMode, aTimeout, aDatabaseID); end; {----------} function TFFRemoteServerEngine.DatabaseSetTimeout( const aDatabaseID : TffDatabaseID; const aTimeout : Longint ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.SetTimeout(aTimeout); end; {----------} function TffRemoteServerEngine.DatabaseTableExists(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aExists : Boolean ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableExists(aTableName, aExists); end; {----------} function TFFRemoteServerEngine.DatabaseTableList(aDatabaseID : TffDatabaseID; const aMask : TffFileNameExt; aList : TList ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableList(aMask, aList); end; {----------} function TffRemoteServerEngine.DatabaseTableLockedExclusive( aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aLocked : Boolean ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableLockedExclusive(aTableName, aLocked); end; {----------} destructor TFFRemoteServerEngine.Destroy; //var {!!.03} // Idx : Longint; {!!.03} begin FFNotifyDependents(ffn_Destroy); { Make sure we are shutdown. } State := ffesInactive; {Begin !!.03} // {Free dependent objects} // with rsClientList.BeginWrite do // try // for Idx := 0 to Pred(Count) do // TFFProxyClient(Items[Idx]).Free; // finally // EndWrite; // end; {End !!.03} with RemoteServerEngines.BeginWrite do try Delete(Longint(Self)); {!!.01} finally EndWrite; end; {Free and nil internal lists} rsClientList.Free; rsClientList := nil; {Clear the transport} Transport := nil; inherited Destroy; end; {----------} function TFFRemoteServerEngine.FileBLOBAdd(aCursorID : TffCursorID; const aFileName : TffFullFileName; var aBlobNr : TffInt64) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.FileBLOBAdd(aFileName, aBlobNr); end; {----------} function TFFRemoteServerEngine.GetDefaultClient: TFFProxyClient; begin Result := nil; with rsClientList.BeginRead do try if Count > 0 then Result := TFFProxyClient(TffIntListItem(Items[0]).KeyAsInt); {!!.01} finally EndRead; end; end; {----------} function TFFRemoteServerEngine.GetServerDateTime(var aDateTime : TDateTime ) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetServerDateTime(aDateTime) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetServerSystemTime(var aSystemTime : TSystemTime) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetServerSystemTime(aSystemTime) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetServerGUID(var aGUID : TGUID) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetServerGUID(aGUID) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetServerID(var aUniqueID : TGUID) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetServerID(aUniqueID) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetServerStatistics(var Stats : TffServerStatistics) : TffResult; begin; if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetServerStatistics(Stats) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetCommandHandlerStatistics(const CmdHandlerIdx : Integer; var Stats : TffCommandHandlerStatistics) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetCommandHandlerStatistics(CmdHandlerIdx, Stats) else Result := DBIERR_INVALIDHNDL; end; {----------} function TFFRemoteServerEngine.GetTransportStatistics(const CmdHandlerIdx : Integer; const TransportIdx : Integer; var Stats : TffTransportStatistics) : TffResult; begin if (GetDefaultClient <> nil) then Result := GetDefaultClient.GetTransportStatistics(CmdHandlerIdx, TransportIdx, Stats) else Result := DBIERR_INVALIDHNDL; end; {----------} {end !!.07} procedure TFFRemoteServerEngine.GetServerNames(aList: TStrings; aTimeout : Longint); begin Transport.GetServerNames(aList, aTimeout); end; {----------} procedure TFFRemoteServerEngine.ForceClosing(const aClientID : TffClientID); var Client : TFFProxyClient; begin if CheckClientIDAndGet(aClientID, Client) = DBIERR_NONE then Client.ForceClosed := True; end; {Begin !!.06} {--------} function TffRemoteServerEngine.ProcessRequest(aClientID : TffClientID; aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint; aRequestDataType : TffNetMsgDataType; var aReply : Pointer; var aReplyLen : Longint; aReplyType : TffNetMsgDataType) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.ProcessRequest(aMsgID, aTimeout, aRequestData, aRequestDataLen, aRequestDataType, aReply, aReplyLen, aReplyType); end; {--------} function TffRemoteServerEngine.ProcessRequestNoReply(aClientID : TffClientID; aMsgID : Longint; aTimeout : Longint; aRequestData : Pointer; aRequestDataLen : Longint ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.ProcessRequestNoReply(aMsgID, aTimeout, aRequestData, aRequestDataLen); end; {End !!.06} {----------} function TFFRemoteServerEngine.RebuildGetStatus(aRebuildID : Longint; const aClientID : TffClientID; var aIsPresent : Boolean; var aStatus : TffRebuildStatus ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.GetRebuildStatus(aRebuildID, aIsPresent, aStatus); end; {----------} function TFFRemoteServerEngine.RecordDelete(aCursorID : TffCursorID; aData : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordDelete(aData); end; {----------} function TffRemoteServerEngine.RecordDeleteBatch(aCursorID : TffCursorID; aBMCount : Longint; aBMLen : Longint; aData : PffByteArray; aErrors : PffLongintArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordDeleteBatch(aBMCount, aBMLen, aData, aErrors); end; {----------} function TFFRemoteServerEngine.RecordExtractKey(aCursorID : TffCursorID; aData : PffByteArray; aKey : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordExtractKey(aData, aKey); end; {----------} function TFFRemoteServerEngine.RecordGet(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordGet(aLockType, aData); end; {----------} function TFFRemoteServerEngine.RecordGetBatch(aCursorID : TffCursorID; aRecCount : Longint; aRecLen : Longint; var aRecRead : Longint; aData : PffByteArray; var aError : TffResult ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordGetBatch(aRecCount, aRecLen, aRecRead, aData, aError); end; {----------} function TFFRemoteServerEngine.RecordGetForKey(aCursorID : TffCursorID; aDirectKey : Boolean; aFieldCount : Longint; aPartialLen : Longint; aKeyData : PffByteArray; aData : PffByteArray; aFirstCall : Boolean ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordGetForKey(aDirectKey, aFieldCount, aPartialLen, aKeyData, aData, aFirstCall); end; {----------} function TFFRemoteServerEngine.RecordGetNext(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordGetNext(aLockType, aData); end; {----------} function TFFRemoteServerEngine.RecordGetPrior(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordGetPrior(aLockType, aData); end; {----------} function TFFRemoteServerEngine.RecordInsert(aCursorID : TffCursorID; aLockType : TffLockType; aData : PffByteArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordInsert(aLockType, aData); end; {----------} function TFFRemoteServerEngine.RecordInsertBatch(aCursorID : TffCursorID; aRecCount : Longint; aRecLen : Longint; aData : PffByteArray; aErrors : PffLongintArray ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordInsertBatch(aRecCount, aRecLen, aData, aErrors); end; {----------} function TffRemoteServerEngine.RecordIsLocked(aCursorID : TffCursorID; aLockType : TffLockType; var aIsLocked : boolean) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordIsLocked(aLockType, aIsLocked); end; {----------} function TFFRemoteServerEngine.RecordModify(aCursorID : TffCursorID; aData : PffByteArray; aRelLock : Boolean) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordModify(aData, aRelLock); end; {----------} function TFFRemoteServerEngine.RecordRelLock(aCursorID : TffCursorID; aAllLocks : Boolean) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.RecordRelLock(aAllLocks); end; {----------} procedure TFFRemoteServerEngine.rsSetTransport(const Value : TFFBaseTransport); begin if rsTransport = Value then Exit; FFNotifyDependents(ffn_Deactivate); if Assigned(rsTransport) then rsTransport.FFRemoveDependent(Self); rsTransport := Value; if Assigned(rsTransport) then rsTransport.FFAddDependent(Self); end; {----------} function TFFRemoteServerEngine.SessionAdd(const aClientID : TffClientID; const aTimeout : Longint; var aSessionID : TffSessionID ) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Result := Client.SessionAdd(aSessionID, aTimeout); end; {----------} function TFFRemoteServerEngine.SessionCount(aClientID : TffClientID; var aCount : Longint) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then aCount := Client.SessionCount; end; {----------} function TFFRemoteServerEngine.SessionGetCurrent(aClientID : TffClientID; var aSessionID : TffSessionID ) : TffResult; var Client : TFFProxyClient; Session : TFFProxySession; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then begin Session := Client.CurrentSession; aSessionID := Longint(Session); end; end; {Begin !!.06} {----------} function TFFRemoteServerEngine.SessionCloseInactiveTables(aClientID : TffClientID) : TffResult; var Client : TFFProxyClient; begin Result := CheckClientIDAndGet(aClientID, Client); if ResultOK(Result) then Client.SessionCloseInactiveTables; end; {End !!.06} {----------} function TFFRemoteServerEngine.SessionRemove(aClientID : TffClientID; aSessionID : TffSessionID ) : TffResult; var Client : TFFProxyClient; Session : TFFProxySession; begin Result := CheckSessionIDAndGet(aClientID, aSessionID, Client, Session); if ResultOK(Result) then Client.SessionRemove(Session); end; {----------} function TFFRemoteServerEngine.SessionSetCurrent(aClientID : TffClientID; aSessionID : TffSessionID ) : TffResult; var Client : TFFProxyClient; Session : TFFProxySession; begin Result := CheckSessionIDAndGet(aClientID, aSessionID, Client, Session); if ResultOK(Result) then Client.SessionSetCurrent(Session); end; {----------} function TFFRemoteServerEngine.SessionSetTimeout( const aClientID : TffClientID; const aSessionID : TffSessionID; const aTimeout : Longint ) : TffResult; var Client : TFFProxyClient; Session : TFFProxySession; begin Result := CheckSessionIDAndGet(aClientID, aSessionID, Client, Session); if ResultOK(Result) then Result := Session.SetTimeout(aTimeout); end; {----------} function TFFRemoteServerEngine.SQLAlloc(aClientID : TffClientID; aDatabaseID : TffDatabaseID; aTimeout : longInt; var aStmtID : TffSqlStmtID) : TffResult; var Database : TffProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.SQLAlloc(aTimeout, aStmtID); end; {----------} function TFFRemoteServerEngine.SQLExec(aStmtID : TffSqlStmtID; aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var Statement : TffProxySQLStmt; begin Assert(Assigned(aStream)); Result := CheckStmtIDAndGet(aStmtID, Statement); if ResultOK(Result) then Result := Statement.Exec(aOpenMode, aCursorID, aStream); end; {----------} function TFFRemoteServerEngine.SQLExecDirect(aClientID : TffClientID; aDatabaseID : TffDatabaseID; aQueryText : PChar; aTimeout : longInt; aOpenMode : TffOpenMode; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var Database : TffProxyDatabase; begin Assert(Assigned(aStream)); Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.SQLExecDirect(aQueryText, aOpenMode, aTimeout, aCursorID, aStream); end; {----------} function TFFRemoteServerEngine.SQLFree(aStmtID : TffSqlStmtID) : TffResult; var Statement : TffProxySQLStmt; begin { Assumption: The cursor associated with the SQL statement has already been closed. } Result := CheckStmtIDAndGet(aStmtID, Statement); if Result = DBIERR_NONE then Statement.Free; end; {----------} function TFFRemoteServerEngine.SQLPrepare(aStmtID : TffSqlStmtID; aQueryText : PChar; aStream : TStream) : TffResult; var Statement : TffProxySQLStmt; begin Assert(Assigned(aStream)); Result := CheckStmtIDAndGet(aStmtID, Statement); if Result = DBIERR_NONE then Result := Statement.Prepare(aQueryText, aStream); end; {----------} function TFFRemoteServerEngine.SQLSetParams(aStmtID : TffSqlStmtID; aNumParams : word; aParamDescs : pointer; aDataBuffer : PffByteArray; aDataLen : Longint; aStream : TStream ) : TffResult; var Statement : TffProxySQLStmt; begin Assert(Assigned(aStream)); Result := CheckStmtIDAndGet(aStmtID, Statement); if Result = DBIERR_NONE then Result := Statement.SetParams(aNumParams, aParamDescs, aDataBuffer, aDataLen, aStream); end; {----------} function TFFRemoteServerEngine.TableAddIndex( const aDatabaseID : TffDatabaseID; const aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexDesc: TffIndexDescriptor ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableAddIndex(aCursorID, aTableName, aIndexDesc); end; {----------} function TFFRemoteServerEngine.TableBuild(aDatabaseID : TffDatabaseID; aOverWrite : Boolean; const aTableName : TffTableName; aForServer : Boolean; aDictionary : TffDataDictionary ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableBuild(aOverWrite, aTableName, aForServer, aDictionary); end; {----------} function TFFRemoteServerEngine.TableDelete(aDatabaseID : TffDatabaseID; const aTableName : TffTableName ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableDelete(aTableName); end; {----------} function TFFRemoteServerEngine.TableDropIndex(aDatabaseID : TffDatabaseID; aCursorID : TffCursorID; const aTableName : TffTableName; const aIndexName : TffDictItemName; aIndexID : Longint ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableDropIndex(aCursorID, aTablename, aIndexName, aIndexID); end; {----------} function TFFRemoteServerEngine.TableEmpty(aDatabaseID : TffDatabaseID; aCursorID : TffCursorID; const aTableName : TffTableName ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableEmpty(aCursorID, aTableName); end; {----------} function TffRemoteServerEngine.TableGetAutoInc(aCursorID : TffCursorID; var aValue : TffWord32) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableGetAutoInc(aValue); end; {----------} function TFFRemoteServerEngine.TableGetDictionary(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; aForServer : Boolean; aStream : TStream ) : TffResult; var Database : TFFProxyDatabase; begin Assert(Assigned(aStream)); Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableGetDictionary(aTableName, aForServer, aStream); end; {----------} function TFFRemoteServerEngine.TableGetRecCount(aCursorID : TffCursorID; var aRecCount : Longint ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableGetRecCount(aRecCount); end; {Begin !!.07} {----------} function TFFRemoteServerEngine.TableGetRecCountAsync(aCursorID : TffCursorID; var aTaskID : Longint) : TffResult; var Cursor : TffProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableGetRecCountAsync(aTaskID); end; {End !!.07} {----------} function TFFRemoteServerEngine.TableIsLocked(aCursorID : TffCursorID; aLockType : TffLockType; var aIsLocked : Boolean) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableIsLocked(aLockType, aIsLocked); end; {----------} function TFFRemoteServerEngine.TableLockAcquire(aCursorID : TffCursorID; aLockType : TffLockType ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableLockAcquire(aLockType); end; {----------} function TFFRemoteServerEngine.TableLockRelease(aCursorID : TffCursorID; aAllLocks : Boolean ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableLockRelease(aAllLocks); end; {----------} function TFFRemoteServerEngine.TableOpen(const aDatabaseID : TffDatabaseID; const aTableName : TffTableName; const aForServer : Boolean; const aIndexName : TffName; aIndexID : Longint; const aOpenMode : TffOpenMode; aShareMode : TffShareMode; const aTimeout : Longint; var aCursorID : TffCursorID; aStream : TStream) : TffResult; var Database : TFFProxyDatabase; begin Assert(Assigned(aStream)); Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableOpen(aTableName, aForServer, aIndexName, aIndexID, aOpenMode, aShareMode, aTimeout, aCursorID, aStream); end; {----------} function TFFRemoteServerEngine.TablePack(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aRebuildID : Longint) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TablePack(aTableName, aRebuildID); end; {----------} function TFFRemoteServerEngine.TableRebuildIndex(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; const aIndexName : TffName; aIndexID : Longint; var aRebuildID : Longint ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableRebuildIndex(aTableName, aIndexName, aIndexID, aRebuildID); end; {----------} function TFFRemoteServerEngine.TableRename(aDatabaseID : TffDatabaseID; const aOldName : TffName; const aNewName : TffName) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableRename(aOldName, aNewName); end; {----------} function TFFRemoteServerEngine.TableRestructure(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; aDictionary : TffDataDictionary; aFieldMap : TffStringList; var aRebuildID : Longint ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TableRestructure(aTableName, aDictionary, aFieldMap, aRebuildID); end; {----------} function TFFRemoteServerEngine.TableSetAutoInc(aCursorID : TffCursorID; aValue : TffWord32 ) : TffResult; var Cursor : TFFProxyCursor; begin Result := CheckCursorIDAndGet(aCursorID, Cursor); if ResultOK(Result) then Result := Cursor.TableSetAutoInc(aValue); end; {Begin !!.11} {----------} function TFFRemoteServerEngine.TableVersion(aDatabaseID : TffDatabaseID; const aTableName : TffTableName; var aVersion : Longint) : TffResult; var Database : TFFProxyDatabase; Request : TffnmGetTableVersionReq; Reply : PffnmGetTableVersionRpy; ReplyLen : Longint; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then begin aVersion := 0; { Initialize Request } Request.DatabaseID := Database.SrDatabaseID; Request.TableName := aTableName; Reply := nil; Result := Database.pdClient.ProcessRequest(ffnmGetTableVersion, Timeout, @Request, SizeOf(Request), nmdByteArray, Pointer(Reply), ReplyLen, nmdByteArray); if ResultOK(Result) then aVersion := Reply^.Version; if Assigned(Reply) then FFFreeMem(Reply, ReplyLen); end; { if } end; {End !!.11} {----------} function TFFRemoteServerEngine.TransactionCommit( const aDatabaseID : TffDatabaseID ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TransactionCommit; end; {----------} function TFFRemoteServerEngine.TransactionRollback( const aDatabaseID : TffDatabaseID ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TransactionRollback; end; {----------} function TFFRemoteServerEngine.TransactionStart( const aDatabaseID : TffDatabaseID; const aFailSafe : Boolean ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TransactionStart(aFailSafe); end; {Begin !!.10} {----------} function TFFRemoteServerEngine.TransactionStartWith( const aDatabaseID : TffDatabaseID; const aFailSafe : Boolean; const aCursorIDs : TffPointerList ) : TffResult; var Database : TFFProxyDatabase; begin Result := CheckDatabaseIDAndGet(aDatabaseID, Database); if ResultOK(Result) then Result := Database.TransactionStartWith(aFailSafe, aCursorIDs); end; {End !!.10} {----------} initialization RemoteServerEngines := TffThreadList.Create; finalization RemoteServerEngines.Free; RemoteServerEngines := nil; end.