Daniel Lee · Feb 18, 2019 go to post

I just found the $MATCH method which works. 

if $MATCH(p, "^%sqlcq.*"){
                    write "Skipping "_p,!
                    continue
                }
Daniel Lee · Feb 21, 2019 go to post

Good question.

I tried comparing our production namespaces with default databases but because of mappings, the results were not accurate. This lead me to just compare database to database. This allowed me to accurately identify any missing files and changed files. 

So my routine currently does not know the namespace in which the routine lives, it just knows which database the code lives in. For instance, I have over 1200 changes in mgr\hslib\cache.dat. 

Below is an example of the results my code reveals:

isdifferent("HS.AU.IHE.XDSb.DocumentSource.Operations.1")="this production routine is DIFFERENT from default routine. Size1:61135, size2:48533"
isdifferent("HS.AU.Message.IHE.XDSb.ProvideAndRegisterRequest.1")="this production routine is DIFFERENT from default routine. Size1:268366, size2:263389"

I add names like HS.AU.IHE.XDSb.DocumentSource.Operations.1 to an array.  After I have compared the two databases, now want to export each item from the production database and the default database for each item in the array. I would export each item, HS.AU.IHE.XDSb.DocumentSource.Operations.1.*, to an XML. Production and Default XML files go to different directories. 

Daniel Lee · Feb 21, 2019 go to post

The function you mention, ##class(%Studio.SourceControl.ISC).BaselineExport(), seems tied to something that our production system isn't using. I tried this in terminal and got the following. 

Creating Source Control Object...
Failed!  Quiting BaselineExport().

We do not have any source control tied to our production system. 

Daniel Lee · Feb 22, 2019 go to post

Thank you for the reply Adrian. I updated the original post with a clearly stated goal and code segment. 

filename is set to c:\InterSystems\testfile.txt.  The error occurs when I call set myLogFile.FileName = filename. 

Daniel Lee · Feb 22, 2019 go to post

I tried this process in terminal as well. I get a similar error. "<PROPERTY DOES NOT EXIST> *FileName,%Stream.FileCharacter". I have no idea what I am missing. Why would the property for the class not exist? I have checked and rechecked the documentation and it should exist. 

Daniel Lee · Feb 22, 2019 go to post

There must be something wrong with my development system Scott. I am using 2013.1 and I get the same problem trying your suggestion. I may try running repair on this instance to see if that fixes the problem. 

Daniel Lee · Jul 16, 2019 go to post

Thank you. That worked. I was operating under the false assumption that I had to provide a variable for the output parameter and when I used the editor window to right-click and select the classmethod it did so without parameters. 

Daniel Lee · Oct 4, 2019 go to post

In my case, we already have standard purge tasks but were still left with orphaned messages. I seem to recall InterSystems support telling me that the standard purge tasks work from the header. 

Daniel Lee · Nov 8, 2019 go to post

Thank you for your reply. 

I had to add EnsHL7 to the list of includes. It is interesting that this compiled previously without the reference. 

Daniel Lee · Feb 27, 2019 go to post

Thank you for all the excellent responses. The solution was far simpler. 

  #;Do not include Cache.DAT as part of the sourceDB path, just folder path
    set impliedNamespace = "^^"_sourceDB
    New $NAMESPACE
    set $NAMESPACE = impliedNamespace

This changes the namespace to an implied namespace and allows me to export the classes and routines. 

This documentation is what got me on the right path: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cl…

Daniel Lee · Oct 2, 2019 go to post

Scott, 

I have the same problem as well. The EnsLib_HL7.Messages had records going back years. While I have not found the offending code that creates the orphaned records, I do have a task that purges the records. We happen to have multiple databases and namespaces, so I added this to a general library database called ACLIB. By doing this I can use the same task code to target multiple namespaces. 

I based my code by tracing through some of the InterSystems purge code. 

Include (Ensemble, %sySite)

/// Deletes EnsLib_HL7.Message records without corresponding header resulting in orphaned records
Class ACLIB.Utils.PurgeOrphanedMessages Extends %SYS.Task.Definition
{

/* Task to purge any orphaned messages
Orphaned Message - Message data without corresponding header.Author: Daniel Lee
Date: 2019-07-16
*/
/// How many days of messages should not be purged
Property NumberOfDaysToKeep As %Integer(MINVAL = 0) [ InitialExpression = 30 ];Property MaxCountToPurge As %Integer(MINVAL = 0) [ InitialExpression = 10000 ];/// Which NameSpace to execute the query to delete messages

/* Note that you will want to modify the namespace list or exclude it and modify the code if you only have one namespace*/
Property SourceNamespace As %String(DISPLAYLIST = ",HIE,IMMUNIZATION,PHI,PM,RESULTS", VALUELIST = ",hie,immunization,phi,pm,results") [ Required ];

/// The OnTask() Method is called to execute the task
Method OnTask() As %Status
{

// These time variables are only for logging 
Set tTime = $ZH
Set tDisplayTime = $ZDATETIME($HOROLOG,1,1,9)
Set tDeletedCount = -1


Write tDisplayTime_": Begin purge orphaned messages for "_..SourceNamespace_"."


Set tSC = ..PurgeOrphaned(.tDeletedCount, ..NumberOfDaysToKeep, ..MaxCountToPurge, ..SourceNamespace)
Set tTime = $ZH - tTime
If $$$ISOK(tSC) {
Set msg = "Purged "_tDeletedCount_" records, keeping the last "_..NumberOfDaysToKeep_" days in "_tTime_"s" 
$$$LOGINFO(msg)
Do $$$LOGMSG(msg,0,1)
Write !,msg
}
else
{
Set msg = "Error purging"_..SourceNamespace_" keeping the last "_..NumberOfDaysToKeep_" : "_ $$$StatusDisplayString(tSC)
$$$LOGERROR(msg)
Do $$$LOGMSG(msg,0,3)
Write !,msg
}
Quit tSC
}

ClassMethod PurgeOrphaned(Output pDeletedCount As %Integer, pDaysToKeep As %Integer = 30, pMaxCountToPurge As %Integer = 100000, pNameSpace As %String) As %Status
{
/*
pDeletedCount - number of rows deleted
pDaysToKeep - number of days of records to keep
pMaxCountToPurge - number of records to delete at any one time, added to keep system from timing out*/
New %tID,%tMaxCountToPurge Set %tMaxCountToPurge = pMaxCountToPurge, %tID=""

//This method to get the date was adapted from Ens purge code ... 
New %tDoNotDeleteDate Set %tDoNotDeleteDate = $$$timeUTCHtoUTC($s($ztimezone'<0:($H-pDaysToKeep+1)_","_($ztimezone*60),1:($H-pDaysToKeep)_","_($ztimezone*60+86400)))

Set SQLCODE=0, pDeletedCount=0, tDeletedCount=0, tCursorRowCount=0
Set tNamespace = $NAMESPACE //Used to return to previous namespace
Set $NAMESPACE = pNameSpace//Grab oldest records that exist prior to the time created value as these are orphaned records without headers
 

&sql(DECLARE C1 CURSOR FOR
Select TOP :%tMaxCountToPurge ID, TimeCreated Into :%tID,:%tTimeCreated From EnsLib_HL7.Message 
Where TimeCreated < :%tDoNotDeleteDate 
Order By TimeCreated Asc)&sql(OPEN C1For 
&sql(FETCH C1)  
Quit:SQLCODE


&sql(Delete From EnsLib_HL7.Message Where ID=:%tID)


Set tDeletedCount=tDeletedCount+1
Write !,tDeletedCount_"|"_%tID_"|"_%tTimeCreated
Set tCode=SQLCODE &sql(CLOSE C1Set:'SQLCODE SQLCODE=tCode
Set pDeletedCount=tDeletedCount
Quit:SQLCODE&&(SQLCODE'=100) $$$ERROR($$$EnsErrGeneral,"Purge error at ID "_%tID _"; SQLCODE = "_SQLCODE)
Set tSC = (SQLCODE=100)&sql(CLOSE C1)
Set $NAMESPACE = tNamespaceQuit tSC
}}

Note that I kept my SQL statement very simple. The full SQL statement to check for orphaned messages is here:

SELECT msg.TimeCreated, COUNT(msg.Id) FROM EnsLib_HL7.Message msg LEFT JOIN Ens.MessageHeader hdr ON msg.Id=hdr.MessageBodyId WHERE hdr.MessageBodyId IS NULL AND msg.Name NOT LIKE '%ACK%' ORDER BY msg.TimeCreated ASC

I tried to format this code by using the ObjectScript button but it looked ugly in the preview. 

Hopefully, this helps. 

Daniel Lee · May 26, 2020 go to post

EXCELLENT! That is exactly what I needed. 

Also, you correctly observed that $DATA(pGlobalName) was only evaluating the local parameter and not its value. My return value is now 10. 

Thank you Robert.