So much easier with journalling switched on!

In February 2000 we had to "undelete" a global containing all the cash transaction records at a large investment management institution when one of our colleagues accidentally deleted it near the end of the day. No journals, no backup, no option to re-input a whole day's work. Luckily the data was still there, albeit with no block pointers. It was a DSM system so we modified a program called ^BLCKDMP to read every block on the disk and write every node from our lost global out to an O/S file in %GO format. Took 15 hours to run and we had the system back by 9am the next day.

Needless to say, everything changed from then with regards to journalling and backups and programmer access.

It was an interesting experience in emergency firefighting and a nice learning experience in how the data is stored using the "common characters" space saving mechanism. M is really quite brilliant.

Hi Mark,

When looping through numerics, unless you intervene, it will loop through every number in magnitude order until you reach the end. So, if you start at 8009 and 8010 exists then it will find it. And 8009 is not next to 800900001 by a long way.

When looping through strings, it's going to go through them like a human would when going through a dictionary. All words with the same prefix are next to each other. So you can easily stop your loop immediately when you find a word that doesn't match.

This code will probably do what you want, athough I haven't tested it:

Noddy ;; Find all subscripts with numeric prefix using minimum $ORDER
    ;
Find(Prefix=8009,MaxSubscriptlength=12)
    Prefix=+Prefix {
        // Must be numeric
        MatchLength=$l(Prefix)
        I=MatchLength:1:MaxSubscriptLength {
            // try starting from every number beginning with
            // 8009, thru 80090, 800900, up to say 800900000000
            // or whatever the maximum subscript length is
            Start=Prefix_$E("000000000000",0,I-MatchLength)
            F  {
                // see if we found one, even at the start of the loop
                $D(^REXREF3(1,Start)) {
                    // but it must have our prefix
                    // this test can end the loop!
                    Q:$E(Start,1,MatchLength)'=Prefix
                    // but only looking for numerics in this loop
                    // don't want to find again later
                    Q:Start'=+Start
                    // Now do whatever it is you want to do with it..
                    // <REMOVE MINE AND INSERT YOUR CODE HERE>
                    !,Start
                }
                Start=$O(^REXREF3(1,Start))
                Q:Start=""
            }
        }
    }
    // Now look for strings with that prefix
    // all strings starting with 8009 will immediately follow "8009 "
    // so start there, there won't be an numeric subscripts following a string
    Start=Prefix
    MatchLength=$l(Prefix)
    Prefix=+Prefix Start=Start_" "
    F  {
        // see if we found one, even at the start of the loop
        $D(^REXREF3(1,Start)) {
            // but it must have our prefix
            // this test can end the loop!
            Q:$E(Start,1,MatchLength)'=Prefix
            // Now do whatever it is you want to do with it..
            // <REMOVE MINE AND INSERT YOUR CODE HERE>
            !,Start
        }
        Start=$O(^REXREF3(1,Start))
        Q:Start=""
    }
    q
 

I had to do something like this a few years ago to add a digital signature to a message in XML format. If I remember correctly you have to get your object into a %XML.Document which works in conjunction with %XML.Node. %XML.Node is used to traverse the %XML.Document to get to the section you want in canonical form. Then you pass the Node to ##class(%XML.Writer).Canonicalize(Node) to get the XML as a string which is then passed to the encryption function you use to get your digest/signature. You can pass the whole document or just a subsection to the canonicalize function.

I can't say if it's the only or best way to do it but it was sufficiently quick enough to handle thousands of messages per minute.

Discourage the use of hard coded breaks in the first place! There are alternatives.

  • Use ZBREAK Tag+OffSet^Routine to set a break point just for the current process
  • Use the debugger in Cache´ Studio (under the Debug menu and attach to a process)
  • George James has a decent debugger in Serenjii
  • Put something in your processes to remove hard coded break points when you promote code through the development cycle (you can automate this if you try)