1048 lines
47 KiB
Plaintext
1048 lines
47 KiB
Plaintext
Function Database_Services(@Service, @Params)
|
|
/***********************************************************************************************************************
|
|
|
|
This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
|
|
permission from SRP Computer Solutions, Inc.
|
|
|
|
Name : Database_Services
|
|
|
|
Description : Handler program for all Database services.
|
|
|
|
Notes : Application errors should be logged using the Error Services module. There are a few methodological
|
|
assumptions built into way errors are managed which are important to understand in order to properly
|
|
work with Error Services:
|
|
|
|
Parameters :
|
|
Service [in] -- Name of the service being requested
|
|
Param1-10 [in/out] -- Additional request parameter holders
|
|
Response [out] -- Response to be sent back to the Controller (MCP) or requesting procedure
|
|
|
|
History : (Date, Initials, Notes)
|
|
03/27/17 dmb Original programmer.
|
|
05/02/17 dmb Add error if WriteDatabaseRow is unable to lock row.
|
|
10/31/17 dmb Add CalculateColumn and GetTableCommuter services.
|
|
11/06/17 fjt [SRPFW-195] Added to FW v16.0.12
|
|
05/25/18 dmb Add GetUserLocks service.
|
|
05/29/18 dmb Add IsKeyIDLocked, IsKeyIDSelfLocked, and UnlockKeyID services.
|
|
10/09/18 djs Added ActivateRecord service, which sets @ID, @Record, and @DICT to enable {} shorthand.
|
|
01/22/19 fjt [SRPFW-195] Updated to include codes in error reporting
|
|
02/28/19 dmb [SRPFW-195] Add safety check in GetTableHandle to make sure the table's full
|
|
handle is still in @TABLES before using the cached handle.
|
|
03/12/19 dmb [SRPFW-270] Add ClearTableHandle service so it is easy to remove the cached
|
|
table handle when situations such as aliasing a table from a different
|
|
volume occurs.
|
|
03/12/19 dmb [SRPFW-270] Add SetTableAlias service to alias a table and call the
|
|
ClearTableHandle service if thealias was successful.
|
|
05/03/19 dmb [SRPFW-273] Initialize TableName in the CalculateColumn service to prevent
|
|
potential VNAV errors.
|
|
05/11/20 dmb [SRPFW-313] Add GetTableNames service.
|
|
06/29/20 dmb [SRPFW-282] Update the GetTableNames service to support ExcludeDictionaries and
|
|
ExcludeIndexes arguments.
|
|
08/04/20 djs Commented out UnlockKeyID service. This service causes OpenInsight to crash. Use
|
|
ReleaseKeyIDLock instead.
|
|
|
|
***********************************************************************************************************************/
|
|
|
|
#pragma precomp SRP_PreCompiler
|
|
|
|
$insert LOGICAL
|
|
$insert SERVICE_SETUP
|
|
$insert VOL_TABLE_EQUATES
|
|
$insert RTI_LH_INFO_EQUATES
|
|
|
|
Declare function Memory_Services, Database_Services, SRP_Encode, RetStack, RTI_LH_Info, SRP_Path, SRP_Array
|
|
Declare subroutine Memory_Services, Database_Services, Verify_LH, SRP_Stopwatch, Btree.Extract, Update_Index, Set_Status
|
|
Declare subroutine RTI_LH_Info, Alias_Table, Push.Select, Pop.Select
|
|
|
|
GoToService else
|
|
Error_Services('Add', Service : ' is not a valid service request within the ' : ServiceModule : ' module.')
|
|
end
|
|
|
|
Return Response OR ''
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Service Parameter Options
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
Options BOOLEAN = True$, False$
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Services
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ActivateRecord
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
// KeyID. The KeyID to the database table. - [REQUIRED]
|
|
//
|
|
// Reads a data row for the indicated Key ID and database table. Sets @ID, @Record, and @DICT to enable {} shorthand.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ActivateRecord(TableName, KeyID, NotExpired, ExpirationDuration, IgnoreMFSRoutines)
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
@ID = KeyID
|
|
DictTable = ''
|
|
If TableName[1,5] _EQC 'DICT.' then
|
|
DictTable = TableName
|
|
TableName = TableName[-1, 'B.']
|
|
end else
|
|
DictTable = 'DICT.':TableName
|
|
end
|
|
@DICT = Database_Services('GetTableHandle', DictTable)
|
|
If Error_Services('NoError') then
|
|
@Record = Database_Services('ReadDataRow',TableName,KeyID,NotExpired,ExpirationDuration,IgnoreMFSRoutines)
|
|
If Not(Error_Services('NoError')) then
|
|
Error_Services('Add','Error reading ':KeyID:' from the ':TableName:' table in the ':Service:' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error retrieving handle for the ':TableName:' table in the ':Service:' service.')
|
|
end
|
|
end
|
|
|
|
Response = @Record
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CalculateColumn
|
|
//
|
|
// Called directly from within a calculation column. The name of the table and column is derived from the call stack
|
|
// and the associated table commuter, if it exists, is called with the appropriate arguments.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CalculateColumn()
|
|
|
|
Response = ''
|
|
ColumnName = ''
|
|
TableName = ''
|
|
RetStack = RetStack()
|
|
For Each Item in RetStack using @FM
|
|
If Index(Item, 'DICT.MFS', 1) then
|
|
ColumnName = Item[2, \00\, 1]
|
|
CharPos = BCol2() + 1
|
|
TableHandle = Item[CharPos, GetByteSize(Item), 1]
|
|
Locate TableHandle<1, 2> in @TABLES(TAB_HANDLE$) using @FM setting fPos then
|
|
TableName = @TABLES(0)<fPos> ; // This technically returns the dictionary, but DICT. will be removed.
|
|
TableName[1, 5] = ''
|
|
end
|
|
end
|
|
Until (ColumnName NE '') OR (Item EQ '')
|
|
Next Item
|
|
|
|
If TableName NE '' then
|
|
TableCommuter = Database_Services('GetTableCommuter', TableName)
|
|
If TableCommuter NE '' then
|
|
Response = Function(@TableCommuter('CalculateColumn', ColumnName))
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ClearTableHandle
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
//
|
|
// Clears the table handle array array from cache. This will force the GetTableHandle service to call the Open statement
|
|
// again.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ClearTableHandle(TableName)
|
|
|
|
If TableName NE '' then
|
|
ServiceKeyID = ServiceModule : '*GetTableHandle'
|
|
ServiceKeyID := '*' : TableName
|
|
Memory_Services('SetValue', ServiceKeyID, '')
|
|
end else
|
|
Error_Services('Add', 'TableName was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// DeleteDataRow
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
// KeyID. The KeyID to the database table. - [REQUIRED]
|
|
//
|
|
// Deletes a data row for the indicated Key ID and database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service DeleteDataRow(TableName, KeyID, IgnoreSelfLock, IgnoreMFSRoutines)
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
If IgnoreSelfLock NE True$ then IgnoreSelfLock = False$
|
|
If IgnoreMFSRoutines NE True$ then IgnoreMFSRoutines = False$
|
|
HaveLock = Database_Services('GetKeyIDLock', TableName, KeyID, IgnoreSelfLock)
|
|
If HaveLock EQ True$ then
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If IgnoreMFSRoutines then
|
|
MFSList = TableHandle<1, 1> ; // MFS routines are @SVM delimited.
|
|
NumMFS = DCount(MFSList, @SVM)
|
|
For MFSCnt = NumMFS to 1 Step -1
|
|
MFSRoutine = MFSList<0, 0, MFSCnt>
|
|
If (MFSRoutine NE 'SI.MFS') AND (MFSRoutine NE 'RTP57') then
|
|
MFSList = Delete(MFSList, 0, 0, MFSCnt)
|
|
end
|
|
Next MFSCnt
|
|
TableHandle<1, 1> = MFSList
|
|
end
|
|
|
|
// [SRPFW-195] Updated / Added by GAC 18 May 2018
|
|
If Error_Services('NoError') then
|
|
Delete TableHandle, KeyID then
|
|
Memory_Services('SetValue', ServiceModule : '*' : 'ReadDatarow' : '*' : TableName : '*' : KeyID, '')
|
|
end else
|
|
Error_Services('Add', 'Error deleting ' : KeyID : ' from the ' : TableName : ' table in the ' : Service : ' service. Error = ' : @File_Error<1>)
|
|
end
|
|
end
|
|
* If Error_Services('NoError') then
|
|
Database_Services('ReleaseKeyIDLock', TableName, KeyID)
|
|
* end
|
|
|
|
end else
|
|
Error_Services('Add', 'Unable to lock ' : KeyID : ' for the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetKeyIDLock
|
|
//
|
|
// Attempts to perform a semaphore lock on the indicated tablename and Key ID.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetKeyIDLock(TableName, KeyID, IgnoreSelfLock)
|
|
|
|
HaveLock = False$ ; // Assume false for now.
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
If IgnoreSelfLock NE True$ then IgnoreSelfLock = False$
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
Lock Tablehandle, KeyID then
|
|
HaveLock = True$
|
|
end else
|
|
If IgnoreSelfLock EQ True$ then
|
|
Status = Status()
|
|
If Status EQ 1 then
|
|
HaveLock = True$
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = HaveLock
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTableCommuter
|
|
//
|
|
// Returns the name of the indicated table's commuter module if it exists. If it does not exist then an empty string
|
|
// will be returned.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTableCommuter(TableName)
|
|
|
|
ServiceKeyID := '*' : TableName
|
|
TableCommuter = Memory_Services('GetValue', ServiceKeyID)
|
|
|
|
If TableCommuter EQ '' then
|
|
ObjExists = False$ ; // Assume the object code for the action handler does not exist for now.
|
|
For Each AppID in @AppId
|
|
If AppID _EQC 'SYSPROG' then
|
|
SysObjKey = '$' : TableName : '_ACTIONS'
|
|
end else
|
|
SysObjKey = '$' : TableName : '_ACTIONS' : '*' : AppID
|
|
end
|
|
ObjExists = Memory_Services('KeyExists', SysObjKey)
|
|
Until ObjExists
|
|
Next AppID
|
|
|
|
If Not(ObjExists) then
|
|
For Each AppID in @AppId
|
|
If AppID _EQC 'SYSPROG' then
|
|
SysObjKey = '$' : TableName : '_ACTIONS'
|
|
end else
|
|
SysObjKey = '$' : TableName : '_ACTIONS' : '*' : AppID
|
|
end
|
|
OrigFileError = @FILE.ERROR
|
|
@FILE.ERROR = ''
|
|
BFS = 'RTP57'
|
|
// The handle to SYSOBJ is used to find object code before it gets called
|
|
ActionStatus = ''
|
|
Call @BFS(2, BFS, @FILE_SYSOBJ<1, 2>, SysObjKey, FMC, SysObjRecord, ActionStatus)
|
|
@FILE.ERROR = OrigFileError
|
|
If ActionStatus then ObjExists = True$
|
|
Until ObjExists
|
|
Next AppID
|
|
end
|
|
|
|
If (ObjExists) then
|
|
TableCommuter = TableName : '_ACTIONS'
|
|
Memory_Services('SetValue', ServiceKeyID, TableCommuter)
|
|
end
|
|
end
|
|
|
|
Response = TableCommuter
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTableHandle
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
//
|
|
// Returns an array of information related to the database table being passed in.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTableHandle(TableName)
|
|
|
|
TableHandle = ''
|
|
|
|
If TableName NE '' then
|
|
ServiceKeyID := '*' : TableName
|
|
|
|
Locate TableName in @Tables(tab_name$) using @FM setting fPos then
|
|
If @Tables(tab_handle$)<fPos> NE '' then
|
|
// Only check for a cached handle if the table and its handle is still in @TABLES. Otherwise the handle is
|
|
// probably stale and will hang OI when utilized.
|
|
TableHandle = Memory_Services('GetValue', ServiceKeyID)
|
|
end
|
|
end
|
|
|
|
If TableHandle EQ '' then
|
|
rv = Set_Status(0)
|
|
Open TableName to TableHandle then
|
|
Memory_Services('SetValue', ServiceKeyID, TableHandle)
|
|
end else
|
|
Error_Services('Add', 'Unable to open the ' : TableName : ' table in the ' : Service : ' service. Error = ' : @File_Error<1>)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = TableHandle
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTableNames
|
|
//
|
|
// ApplicationTablesOnly - Boolean flag to determine if only application database tables should be returned (i.e.,
|
|
// exclude SYSTEM and non-RTP57 tables). Default is True.
|
|
// ExcludeDictionaries - Boolean flag to determine if dictionary tables should be excluded. Default is False.
|
|
// ExcludeIndexes - Boolean flag to determine if index tables should be excluded. Default is False.
|
|
//
|
|
// Returns an @FM list of currently attached OpenInsight database tables.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTableNames(ApplicationTablesOnly=BOOLEAN, ExcludeDictionaries=BOOLEAN, ExcludeIndexes=BOOLEAN)
|
|
|
|
TableNames = '' ; // Initialize the return variable.
|
|
|
|
If ApplicationTablesOnly NE False$ then ApplicationTablesOnly = True$
|
|
If ExcludeDictionaries NE True$ then ExcludeDictionaries = False$
|
|
If ExcludeIndexes NE True$ then ExcludeIndexes = False$
|
|
|
|
TableNames = @Tables(tab_name$)
|
|
TableTypes = @Tables(tab_vol_name$)
|
|
If ApplicationTablesOnly EQ True$ then
|
|
Tables = ''
|
|
For Each TableType in TableTypes using @FM setting fPos
|
|
TableName = TableNames<fPos>
|
|
If (TableType[1, 'F*'] EQ 'RTP57') AND Not(TableType EQ 'RTP57*REVBOOT') AND Not(TableType EQ 'RTP57*O4WFILES') AND Not(TableType EQ 'RTP57*AREV_DIR') then
|
|
Tables := TableName : @FM
|
|
end
|
|
Next TableType
|
|
Tables[-1, 1] = ''
|
|
Transfer Tables to TableNames
|
|
end
|
|
If ExcludeDictionaries EQ True$ then
|
|
Tables = ''
|
|
For Each TableName in TableNames using @FM
|
|
If TableName[1, 5] NE 'DICT.' then
|
|
Tables := TableName : @FM
|
|
end
|
|
Next TableType
|
|
Tables[-1, 1] = ''
|
|
Transfer Tables to TableNames
|
|
end
|
|
If ExcludeIndexes EQ True$ then
|
|
Tables = ''
|
|
For Each TableName in TableNames using @FM
|
|
If TableName[1, 1] NE '!' then
|
|
Tables := TableName : @FM
|
|
end
|
|
Next TableType
|
|
Tables[-1, 1] = ''
|
|
Transfer Tables to TableNames
|
|
end
|
|
TableNames = SRP_Array('SortRows', TableNames, 'AL1', 'LIST', @FM, @VM)
|
|
|
|
Response = TableNames
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTableProperties
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
//
|
|
// Returns an array of information related to the database table being passed in.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTableProperties(TableName)
|
|
|
|
TableProperties = ''
|
|
|
|
If TableName NE '' then
|
|
rv = Set_Status(0)
|
|
Locate TableName in @TABLES(tab_name$) using @FM setting fPos then
|
|
TableProperties<1> = @TABLES(tab_account$)<fPos> ; // DatabaseID
|
|
TableProperties<2> = @TABLES(tab_file_sys$)<fPos> ; // MFS/BFS List
|
|
VolumeID = @TABLES(tab_vol_name$)<fPos> ; // Volume ID
|
|
Locate VolumeID in @VOLUMES(vol_name$) using @FM setting fPos then
|
|
TableProperties<3> = @VOLUMES(vol_label$)<fPos> ; // Volume Label
|
|
TableProperties<4> = @VOLUMES(vol_location$)<fPos> ; // Volume Path
|
|
TableProperties<5> = @VOLUMES(vol_file_sys$)<fPos> ; // BFS
|
|
end
|
|
end else
|
|
Error_Services('Add', 'The ' : TableName : ' table is not attached and available to the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = TableProperties
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetUserLocks
|
|
//
|
|
// Returns a dynamic array of user lock information. Note, this can only be done with the UD 5. This can also cause
|
|
// instability with the current session and may require the Task Manager to close the session.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetUserLocks()
|
|
|
|
UserLocks = RTI_LH_Info(CMD_LOCKS_INFO$, '')
|
|
|
|
Response = UserLocks
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsKeyIDLocked
|
|
//
|
|
// Returns a Boolean flag of the lock status for the indicated table and Key ID.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsKeyIDLocked(TableName, KeyID, IgnoreSelfLock=BOOLEAN)
|
|
|
|
If IgnoreSelfLock NE True$ then IgnoreSelfLock = False$
|
|
|
|
KeyIDLocked = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
Lock hTable, KeyID then
|
|
// Able to lock Key ID. Unlock it right away.
|
|
Unlock hTable, KeyID else Null
|
|
end else
|
|
// Unable to lock Key ID. Evaluate the type of lock.
|
|
LockType = Status()
|
|
If LockType EQ 0 then
|
|
// Lock exists on another station.
|
|
KeyIDLocked = True$
|
|
end else
|
|
// Lock exists on this station. Check to see if the Ignore Self Lock flag is set.
|
|
If Not(IgnoreSelfLock) then KeyIDLocked = True$
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = KeyIDLocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsKeyIDSelfLocked
|
|
//
|
|
// Returns a Boolean flag of the self-lock status for the indicated table and Key ID.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsKeyIDSelfLocked(TableName, KeyID)
|
|
|
|
KeyIDSelfLocked = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (KeyID NE '') then
|
|
Open TableName to hTable then
|
|
Lock hTable, KeyID then
|
|
// Able to lock Key ID. Unlock it right away.
|
|
Unlock hTable, KeyID else Null
|
|
end else
|
|
// Unable to lock Key ID. Evaluate the type of lock.
|
|
LockType = Status()
|
|
If LockType EQ 1 then
|
|
// This Key ID is self-locked.
|
|
KeyIDSelfLocked = True$
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = KeyIDSelfLocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ReadDataColumn
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
// KeyID. The KeyID to the database table. - [REQUIRED]
|
|
// ColumnNo. The index of the column that is to be read. - [REQUIRED]
|
|
//
|
|
// Reads a data column for the indicated Key ID and database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ReadDataColumn(TableName, KeyID, ColumnNo, NotExpired, ExpirationDuration, IgnoreMFSRoutines)
|
|
|
|
If NotExpired NE False$ then NotExpired = True$
|
|
If (ExpirationDuration EQ '') OR (Not(Num(ExpirationDuration))) then ExpirationDuration = 0
|
|
If IgnoreMFSRoutines NE True$ then IgnoreMFSRoutines = False$
|
|
|
|
ServiceKeyID := '*' : TableName : '*' : KeyID : '*' : ColumnNo
|
|
DataColumn = Memory_Services('GetValue', ServiceKeyID, NotExpired, ExpirationDuration)
|
|
|
|
If Num(ColumnNo) AND (ColumnNo GT 0) then
|
|
If TableName NE '' AND KeyID NE '' then
|
|
If DataColumn EQ '' then
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
If IgnoreMFSRoutines then
|
|
MFSList = TableHandle<1, 1> ; // MFS routines are @SVM delimited.
|
|
NumMFS = DCount(MFSList, @SVM)
|
|
For MFSCnt = NumMFS to 1 Step -1
|
|
MFSRoutine = MFSList<0, 0, MFSCnt>
|
|
If MFSRoutine NE 'RTP57' then
|
|
MFSList = Delete(MFSList, 0, 0, MFSCnt)
|
|
end
|
|
Next MFSCnt
|
|
TableHandle<1, 1> = MFSList
|
|
end
|
|
ReadV DataColumn from TableHandle, KeyID, ColumnNo then
|
|
Memory_Services('SetValue', ServiceKeyID, DataColumn)
|
|
end else
|
|
ErrorMsg = 'Error reading ' : KeyID : ' column number ' : ColumnNo : ' from the ' |
|
|
: TableName : ' table in the ' : Service : ' service.'
|
|
Error_Services('Add', ErrorMsg)
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ColumnNo was not a number or was not greater than zero in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = DataColumn
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ReadDataRow
|
|
//
|
|
// TableName - The linear hash database table name. - [REQUIRED]
|
|
// KeyID - The KeyID to the database table. - [REQUIRED]
|
|
//
|
|
// Reads a data row for the indicated Key ID and database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ReadDataRow(TableName, KeyID, NotExpired, ExpirationDuration, IgnoreMFSRoutines)
|
|
|
|
If NotExpired NE False$ then NotExpired = True$
|
|
If (ExpirationDuration EQ '') OR (Not(Num(ExpirationDuration))) then ExpirationDuration = 0
|
|
If IgnoreMFSRoutines NE True$ then IgnoreMFSRoutines = False$
|
|
|
|
ServiceKeyID := '*' : TableName : '*' : KeyID
|
|
DataRow = Memory_Services('GetValue', ServiceKeyID, NotExpired, ExpirationDuration)
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
If DataRow EQ '' then
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
If IgnoreMFSRoutines then
|
|
MFSList = TableHandle<1, 1> ; // MFS routines are @SVM delimited.
|
|
NumMFS = DCount(MFSList, @SVM)
|
|
For MFSCnt = NumMFS to 1 Step -1
|
|
MFSRoutine = MFSList<0, 0, MFSCnt>
|
|
If MFSRoutine NE 'RTP57' then
|
|
MFSList = Delete(MFSList, 0, 0, MFSCnt)
|
|
end
|
|
Next MFSCnt
|
|
TableHandle<1, 1> = MFSList
|
|
end
|
|
// [SRPFW-195]
|
|
Read DataRow from TableHandle, KeyID then
|
|
Memory_Services('SetValue', ServiceKeyID, DataRow)
|
|
end else
|
|
Error_Services('Add', 'Error reading ' : KeyID : ' from the ' : TableName : ' table in the ' : Service : ' service. Error = ' : @File_Error<1>)
|
|
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = DataRow
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ReleaseKeyIDLock
|
|
//
|
|
// Attempts to release a semaphore lock on the indicated tablename and Key ID.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ReleaseKeyIDLock(TableName, KeyID)
|
|
|
|
LockReleased = False$ ; // Assume false for now.
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If Error_Services('NoError') then
|
|
UnLock Tablehandle, KeyID then
|
|
LockReleased = True$
|
|
end else
|
|
Error_Services('Add', 'Unable to unlock the ' : KeyID : ' Key ID from the ' : TableName : ' table in the ' : Service : ' service. Error = ' : @File_Error<1>)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = LockReleased
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SearchIndex
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
// ColumnName. The indexed column to search against. - [REQUIRED]
|
|
// SearchValue. The value being searched for. - [REQUIRED]
|
|
// UpdateIndex. Boolean flag to determine if the index for the indicated column should be updated before searching.
|
|
// - [OPTIONAL]
|
|
//
|
|
// Returns an @FM delimited list of Key IDs that match the search value.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SearchIndex(TableName, ColumnName, SearchValue, UpdateIndex)
|
|
|
|
If UpdateIndex NE True$ then UpdateIndex = False$
|
|
|
|
ServiceKeyID := '*' : TableName : '*' : ColumnName : '*' : SearchValue
|
|
ServiceKeyID = SRP_Encode(ServiceKeyID, 'BASE64')
|
|
* KeyIDList = Memory_Services('GetValue', ServiceKeyID, True$, 5)
|
|
KeyIDList = ''
|
|
|
|
If TableName NE '' AND ColumnName NE '' AND SearchValue NE '' then
|
|
If KeyIDList EQ '' then
|
|
DictTableHandle = Database_Services('GetTableHandle', 'DICT.' : TableName)
|
|
If Error_Services('NoError') then
|
|
Set_Status(0)
|
|
If UpdateIndex then Update_Index(TableName, ColumnName 0)
|
|
Set_Status(0)
|
|
Flag = ''
|
|
Btree.Extract(ColumnName : @VM : SearchValue : @FM, Tablename, DictTableHandle, KeyIDList, 'S', Flag)
|
|
If Flag EQ 0 then
|
|
Convert @VM to @FM in KeyIDList
|
|
Memory_Services('SetValue', ServiceKeyID, KeyIDList)
|
|
end else
|
|
Error_Services('Add', 'Error in Btree.Extract search from the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName, ColumnName, or SearchValue argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = KeyIDList
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetTableAlias
|
|
//
|
|
// TableName - The linear hash database table name. - [Required]
|
|
// AliasName - The alias name being set. - [Required]
|
|
// Volume - The location path or volume name where the table exists. - [Required]
|
|
// DatabaseID - Database ID of the table being aliased. Defaults to the current Database ID (@DBID) - [Optional]
|
|
//
|
|
// Attempts to create an alias for the indicated table, volume, and database. It returns a True$ if successful or a
|
|
// False$ if unsuccessful.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetTableAlias(TableName, AliasName, Volume, DatabaseID)
|
|
|
|
Success = False$ ; // Assume not successful for now.
|
|
|
|
If (TableName NE '') AND (AliasName NE '') AND (Volume NE '') then
|
|
Set_Status(0)
|
|
If DatabaseID EQ '' then DatabaseID = @DBID
|
|
Push.Select(F1, F2, F3, F4) ; // Protect cursors in case Alias_Table fails.
|
|
Alias_Table(Volume : '', DatabaseID : '', TableName : '', AliasName : '')
|
|
Pop.Select(F1, F2, F3, F4) ; // Restore cursors.
|
|
StatusCode = ''
|
|
If Get_Status(StatusCode) then
|
|
Error_Services('Add', 'Error calling the Alias_Table routine in the ' : Service : ' service. Status Code : ' : StatusCode)
|
|
end else
|
|
// Setting an alias could changes the handle so clear the cached handle to avoid it being stale.
|
|
Success = True$
|
|
Database_Services('ClearTableHandle', AliasName)
|
|
end
|
|
Set_Status(0)
|
|
end else
|
|
Error_Services('Add', 'TableName, AliasName, or Volume argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = Success
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnlockKeyID
|
|
//
|
|
// Attempts to unlock the indicated Key ID from the indicated Table Name. Note, this can only be done with the UD 5.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
* Service UnlockKeyID(TableName, KeyID)
|
|
*
|
|
* KeyIDUnlocked = False$ ; // Assume false for now.
|
|
*
|
|
* Convert @Lower_Case to @Upper_Case in TableName
|
|
*
|
|
* If (TableName NE '') AND (KeyID NE '') then
|
|
* // First, do a sanity check to confirm that there is a lock on this Key ID.
|
|
* IsKeyIDLocked = Database_Services('IsKeyIDLocked', TableName, KeyID, False$)
|
|
* If Error_Services('NoError') AND (IsKeyIDLocked EQ False$) then
|
|
* // Key ID was not locked.
|
|
* KeyIDUnlocked = True$
|
|
* end
|
|
* If KeyIDUnlocked NE True$ then
|
|
* // Key ID is still presumed to be locked.
|
|
* VolumePath = ''
|
|
* FileName = ''
|
|
*
|
|
* // Resolve the volume path for this table.
|
|
* hTableFull = Database_Services('ReadDataRow', 'SYSTABLES', TableName)
|
|
* VolumeName = hTableFull<1>
|
|
* VolumeRow = Database_Services('ReadDataRow', 'SYSVOLUMES', VolumeName)
|
|
* VolumePath = VolumeRow[-1, 'B' : @FM]
|
|
* VolumePath = VolumePath[-1, 'B' : \0D\]
|
|
* VolumePath[1, 12] = '' ; // Strip off the preceding bytes.
|
|
* If SRP_Path('IsRelative', VolumePath) then
|
|
* VolumePath = SRP_Path('Combine', Drive(), VolumePath)
|
|
* end
|
|
* VolumePath = SRP_Path('RemoveFilename', VolumePath)
|
|
* // Make sure there is a backslash since the RTI_LH_INFO API seems to use this.
|
|
* VolumePath = SRP_Path('AddBackslash', VolumePath)
|
|
*
|
|
* // Resolve the filename for this table.
|
|
* hTableFull = Database_Services('ReadDataRow', 'SYSTABLES', TableName)
|
|
* FileName = hTableFull[-1, 'B' : @FM]
|
|
* FileName = FileName[-1, 'B' : \0D\]
|
|
* FileName = FileName[1, @VM]
|
|
* If FileName _NEC 'SYSREPOS' then FileName[1, 12] = '' ; // Strip off the preceding bytes.
|
|
* FileName = FileName[-1, 'B' : '\']
|
|
* // Make sure the filename is well formed.
|
|
* Begin Case
|
|
* Case (FileName[-3, 3] _EQC '.LK') OR (FileName[-3, 3] _EQC '.OV')
|
|
* FileName[-3, 3] = ''
|
|
* Case FileName _EQC 'SYSREPOS'
|
|
* FileName = 'REVREPOS'
|
|
* End Case
|
|
*
|
|
* If (VolumePath NE '') AND (FileName) NE '' then
|
|
* // Attempt to unlocked the Key ID.
|
|
* RTI_LH_Info(CMD_UNLOCK$, VolumePath, FileName, KeyID, FileName)
|
|
* // Confirm that the Key ID was unlocked.
|
|
* IsKeyIDLocked = Database_Services('IsKeyIDLocked', TableName, KeyID, False$)
|
|
* If Error_Services('NoError') AND (IsKeyIDLocked EQ False$) then
|
|
* // Key ID was not locked.
|
|
* KeyIDUnlocked = True$
|
|
* end else
|
|
* // Unable to unlock the Key ID which could be because it is locked by this station. Try using the
|
|
* // Unlock statement.
|
|
* hTable = Database_Services('GetTableHandle', TableName)
|
|
* Unlock hTable, KeyID then
|
|
* KeyIDUnlocked = True$
|
|
* end else
|
|
* Error_Services('Add', 'Unable to unlock ' : KeyID : ' from the ' : TableName : ' table in the ' : Service : ' service.')
|
|
* end
|
|
* end
|
|
* end else
|
|
* Error_Services('Add', 'No valid VolumePath or FileName was available in the ' : Service : ' service.')
|
|
* end
|
|
* end
|
|
* end else
|
|
* Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
* end
|
|
*
|
|
* Response = KeyIDUnlocked
|
|
*
|
|
* end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// VerifyLH
|
|
//
|
|
// TableNames. One or more linear hash database tables to verify. List should be @FM delimited. - [OPTIONAL]
|
|
// SaveList. Name of a saved selection of linear hash database tables to verify. This argument will be ignored
|
|
// if TableNames is populated. - [OPTIONAL]
|
|
//
|
|
// Performs a health check against the indicated tables and returns back any issues. Note: This uses the Verify_LH
|
|
// subroutine to check for GFEs. All results are stored in the SYSLHVERIFY table with a KeyID of
|
|
// VolumeLabel*DatabaseID*TableName. Returns the list of groups that have GFEs or returns an empty string if there are
|
|
// none. The list of GFEs or empty strings will themselves be @FM delimited to correspond with the tables passed into
|
|
// this service.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service VerifyLH(Tablenames, SaveList)
|
|
|
|
VerifyResults = ''
|
|
|
|
If TableNames EQ '' then
|
|
TableNames = Xlate('SYSLISTS', SaveList, '', 'X')
|
|
If Index(TableNames<1>, @TM, 1) then
|
|
// This saved select has metadata in attribute 1. Remove it.
|
|
TableNames = Delete(TableNames, 1, 0, 0)
|
|
end
|
|
end
|
|
|
|
If TableNames NE '' then
|
|
NumTables = DCount(TableNames, @FM)
|
|
For TableCnt = 1 to NumTables
|
|
TableName = TableNames<TableCnt>
|
|
TableProperties = Database_Services('GetTableProperties', TableName)
|
|
If Error_Services('NoError') then
|
|
rv = Set_Status(0)
|
|
Verify_LH('', TableName, True$, False$, False$)
|
|
Open 'SYSLHVERIFY' to hSysLHVerify then
|
|
DatabaseID = TableProperties<1>
|
|
VolumeLabel = TableProperties<3>
|
|
LHVerifyKeyID = VolumeLabel : '*' : DatabaseID : '*' : TableName
|
|
Read LHVerifyRec from hSysLHVerify, LHVerifyKeyID then
|
|
VerifyResults<TableCnt> = LHVerifyRec<2>
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the SYSLHVERIFY table in the ' : Service : ' service.')
|
|
end
|
|
Begin Case
|
|
Case @File_Error NE ''
|
|
Error_Services('Add', 'Error verifying table ' : TableName : ': FS Error = ' : @File_Error<1>)
|
|
Case Get_Status()
|
|
Error_Services('Add', 'Get_Status error: ' : Get_Status())
|
|
End Case
|
|
end
|
|
Next TableCnt
|
|
end else
|
|
Error_Services('Add', 'The TableName argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = VerifyResults
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// VerifyLHAll
|
|
//
|
|
// Performs a health check against all attached tables and returns back any issues. Returns two lists which are
|
|
// delimited by an @RM. The first list is an @FM list of attached tables. The second list is an @FM list of results
|
|
// (groups that have GFEs or an empty string if there are none). Items in each list correspond which each other based on
|
|
// their list position.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service VerifyLHAll()
|
|
|
|
VerifyResults = ''
|
|
TableNames = @TABLES(0)
|
|
NumTables = DCount(TableNames, @FM)
|
|
|
|
For TableCnt = 1 to NumTables
|
|
TableName = TableNames<TableCnt>
|
|
VerifyResults := Database_Services('VerifyLH', TableName) : @FM
|
|
Next TableCnt
|
|
VerifyResults[-1, 1] = '' ; // Strip off the last @FM
|
|
|
|
Response = TableNames : @RM : VerifyResults
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// WriteDataRow
|
|
//
|
|
// TableName. The linear hash database table name. - [REQUIRED]
|
|
// KeyID. The KeyID to the database table. - [REQUIRED]
|
|
//
|
|
// Writes a data row for the indicated Key ID and database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service WriteDataRow(TableName, KeyID, DataRow, IgnoreSelfLock, IgnoreMFSRoutines, IgnoreAllLocks)
|
|
|
|
If TableName NE '' AND KeyID NE '' then
|
|
If IgnoreSelfLock NE True$ then IgnoreSelfLock = False$
|
|
If IgnoreMFSRoutines NE True$ then IgnoreMFSRoutines = False$
|
|
If IgnoreAllLocks NE True$ then IgnoreAllLocks = False$
|
|
If IgnoreAllLocks then
|
|
HaveLock = True$
|
|
end else
|
|
HaveLock = Database_Services('GetKeyIDLock', TableName, KeyID, IgnoreSelfLock)
|
|
end
|
|
If HaveLock EQ True$ then
|
|
TableHandle = Database_Services('GetTableHandle', TableName)
|
|
If IgnoreMFSRoutines then
|
|
MFSList = TableHandle<1, 1> ; // MFS routines are @SVM delimited.
|
|
NumMFS = DCount(MFSList, @SVM)
|
|
For MFSCnt = NumMFS to 1 Step -1
|
|
MFSRoutine = MFSList<0, 0, MFSCnt>
|
|
If (MFSRoutine NE 'SI.MFS') AND (MFSRoutine NE 'RTP57') then
|
|
MFSList = Delete(MFSList, 0, 0, MFSCnt)
|
|
end
|
|
Next MFSCnt
|
|
TableHandle<1, 1> = MFSList
|
|
end
|
|
|
|
// [SRPFW-195] Updated / added by GAC 18 May 2018
|
|
If Error_Services('NoError') then
|
|
Write DataRow to TableHandle, KeyID then
|
|
Memory_Services('SetValue', ServiceModule : '*' : 'ReadDatarow' : '*' : TableName : '*' : KeyID, DataRow)
|
|
end else
|
|
ErrorMsg = Error_Services('GetMessage')
|
|
Error_Services('Add', 'Error writing ' : KeyID : ' to the ' : TableName : ' table in the ' : Service : ' service. Error message: ':ErrorMsg)
|
|
end
|
|
end
|
|
* If Error_Services('NoError') then
|
|
If IgnoreAllLocks EQ False$ then
|
|
Database_Services('ReleaseKeyIDLock', TableName, KeyID)
|
|
end
|
|
* end
|
|
end else
|
|
Error_Services('Add', 'Unable to lock ' : KeyID : ' for the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnlockKeyID
|
|
//
|
|
// Attempts to unlock the indicated Key ID from the indicated Table Name. Note, this can only be done with the UD 5.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service UnlockKeyID(TableName, KeyID)
|
|
|
|
KeyIDUnlocked = False$ ; // Assume false for now.
|
|
Convert @Lower_Case to @Upper_Case in TableName
|
|
|
|
If (TableName NE '') AND (KeyID NE '') then
|
|
// First, do a sanity check to confirm that there is a lock on this Key ID.
|
|
IsKeyIDLocked = Database_Services('IsKeyIDLocked', TableName, KeyID, False$)
|
|
If Error_Services('NoError') AND (IsKeyIDLocked EQ False$) then
|
|
// Key ID was not locked.
|
|
KeyIDUnlocked = True$
|
|
end
|
|
If KeyIDUnlocked NE True$ then
|
|
// Key ID is still presumed to be locked.
|
|
VolumePath = ''
|
|
FileName = ''
|
|
|
|
// Resolve the volume path for this table.
|
|
hTableFull = Database_Services('ReadDataRow', 'SYSTABLES', TableName)
|
|
VolumeName = hTableFull<1>
|
|
VolumeRow = Database_Services('ReadDataRow', 'SYSVOLUMES', VolumeName)
|
|
VolumePath = VolumeRow[-1, 'B' : @FM]
|
|
VolumePath = VolumePath[-1, 'B' : \0D\]
|
|
VolumePath[1, 12] = '' ; // Strip off the preceding bytes.
|
|
If SRP_Path('IsRelative', VolumePath) then
|
|
VolumePath = SRP_Path('Combine', Drive(), VolumePath)
|
|
end
|
|
VolumePath = SRP_Path('RemoveFilename', VolumePath)
|
|
// Make sure there is a backslash since the RTI_LH_INFO API seems to use this.
|
|
VolumePath = SRP_Path('AddBackslash', VolumePath)
|
|
|
|
// Resolve the filename for this table.
|
|
hTableFull = Database_Services('ReadDataRow', 'SYSTABLES', TableName)
|
|
FileName = hTableFull[-1, 'B' : @FM]
|
|
FileName = FileName[-1, 'B' : \0D\]
|
|
FileName = FileName[1, @VM]
|
|
If FileName _NEC 'SYSREPOS' then FileName[1, 12] = '' ; // Strip off the preceding bytes.
|
|
FileName = FileName[-1, 'B' : '\']
|
|
// Make sure the filename is well formed.
|
|
Begin Case
|
|
Case (FileName[-3, 3] _EQC '.LK') OR (FileName[-3, 3] _EQC '.OV')
|
|
FileName[-3, 3] = ''
|
|
Case FileName _EQC 'SYSREPOS'
|
|
FileName = 'REVREPOS'
|
|
End Case
|
|
|
|
If (VolumePath NE '') AND (FileName) NE '' then
|
|
// Attempt to unlocked the Key ID.
|
|
RTI_LH_Info(CMD_UNLOCK$, VolumePath, FileName, KeyID, FileName)
|
|
// Confirm that the Key ID was unlocked.
|
|
IsKeyIDLocked = Database_Services('IsKeyIDLocked', TableName, KeyID, False$)
|
|
If Error_Services('NoError') AND (IsKeyIDLocked EQ False$) then
|
|
// Key ID was not locked.
|
|
KeyIDUnlocked = True$
|
|
end else
|
|
// Unable to unlock the Key ID which could be because it is locked by this station. Try using the
|
|
// Unlock statement.
|
|
hTable = Database_Services('GetTableHandle', TableName)
|
|
Unlock hTable, KeyID then
|
|
KeyIDUnlocked = True$
|
|
end else
|
|
Error_Services('Add', 'Unable to unlock ' : KeyID : ' from the ' : TableName : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'No valid VolumePath or FileName was available in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or KeyID argument was missing in the ' : Service : ' service.')
|
|
end
|
|
Response = KeyIDUnlocked
|
|
|
|
end service
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Internal GoSubs
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|