Robert Cemper · Nov 5, 2020 go to post

In general YES.
I did it myself with preIRIS  Caché.
Data and Tables and Views and Triggers are not demanding.
Migrating Stored Procedures could take a little bit more effort.
But the ability to transform Stored Procedures to Class Methods should allow also to solve tricky situations.

(I'm personally proud that over more than a decade I never lost a benchmark against Oracle)) 

Robert Cemper · Oct 29, 2020 go to post

With your service contract WRC should be able to provide you with a kit for 2015
Once restored the DB should have no problem to upgrade.

Robert Cemper · Oct 25, 2020 go to post

Hi Jeff;

all you have to do is to set the Stream location to a  Global in *TEMP Database.
Example:
 

Ok ... so this actually works:

ClassMethod GetHL7Msg(pId As %String) As %Stream.GlobalCharacter [ SqlName = GetMsg, SqlProc ]
{
    Set tHl7 = ##class(EnsLib.HL7.Message).%OpenId(pId,,.tSC)
    Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" Not Found.")
    Set tMsg = ##class(%Stream.GlobalCharacter).%New()
           SET tMsg.%Location="^mtemp.JEFF"
    Set tSC = tHl7.OutputToLibraryStream(.tMsg)
    Do tHl7.%Close()
    If tSC Set tSC=tMsg.%Save()
    Throw:$$$ISERR(tSC) ..GetErr(-400, $system.Status.GetErrorText(tSC))
    Return tMsg."%%OID"
}

>>>>>

yes

Robert Cemper · Oct 24, 2020 go to post

StreamPrefetch looks related but it is totally related to the .NET end (googled SQL prefetch in .NET), 
while <INVALID OREF> is clearly an issue @Ensemble end.
I'm not too deep in the external ODBC  world but I assume that collecting the resultset and
transmitting it to .NET  [ %SYS.SQLStreamSRV ] could happen asynchronously in different processes.
So I doubted %Stream.TempCharacter which is bound to a process private Global  vs. a standard Global visible also to other processes. And only the final %Save nails it down.
I wonder if you experience the same problem if you run your query from
terminal prompt: do $system.SQL.Shell()  
This can signal if the ODBC connection plays a role at all.

Robert Cemper · Oct 24, 2020 go to post

Jeff,
if this is still happening I suggest contacting WRC. 
They have access to the sources and more diagnostic tools.

Robert Cemper · Oct 24, 2020 go to post

%SYS.SQLStreamSRV.obj  is a deployed routine / class so we can't look into its details.
<INVALID OREF> expects a (stream) Object, but it doesn't exist.
So an additional check might be required.  Or you force some content for testing.
 

ClassMethod GetHL7Msg(pId As %String) As %Stream.TmpCharacter [ SqlName = GetMsg, SqlProc ]
{
    Set tHl7 = ##class(EnsLib.HL7.Message).%OpenId(pId,,.tSC)
    Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" Not Found.")
    Set tMsg = ##class(%Stream.GlobalCharacter).%New()
    Set tSC = tHl7.OutputToLibraryStream(.tMsg)

     ;;  Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" failed.") 
         IF 'tMsg.Size DO tMsg.Write("*** no message for ID:"-pId_" ***")
         SET tSC=tMsg.%Save()

    Do tHl7.%Close()
    Return tMsg
}

So you should have always a Stream Object

Robert Cemper · Oct 24, 2020 go to post

So my next try would be to add %Save  of the Stream Object before closing  tHl7

ClassMethod GetHL7Msg(pId As %String) As %Stream.TmpCharacter [ SqlName = GetMsg, SqlProc ]
{
    Set tHl7 = ##class(EnsLib.HL7.Message).%OpenId(pId,,.tSC)
    Throw:$$$ISERR(tSC) ..GetErr(-400, "HL7 Message with ID "_pId_" Not Found.")
    Set tMsg = ##class(%Stream.GlobalCharacter).%New()
    Set tSC = tHl7.OutputToLibraryStream(.tMsg)

        IF tSC SET tSC=tMsg.%Save()          

    Do tHl7.%Close()
    Return tMsg
}
Robert Cemper · Oct 23, 2020 go to post

to understand what is happening:
Do you get the same error using ?

Set tMsg = ##class(%Stream.GlobalCharacter).%New() 
Robert Cemper · Oct 18, 2020 go to post

this would be a typical exercise to use XSLT support

but for this simple case using $FIND Function in a loop may do it as well.

set a="<Name>ABC</Name><RollNo>45</RollNo><Name>XYZ</Name><RollNo>66</RollNo><Name>xyz</Name>RollNo>89</RollNo>"
set p=1
for  {
      set f=$find(a,"<RollNo>",p) quit:'f
      set t=$find(a,"<",f-1) quit:'t  set p=t-2
      set $e(a,f,p)="**"
     }
Robert Cemper · Oct 10, 2020 go to post

"there is no real advantage to having a temp table unless you are worried about storage."

"That is not as simple as the Microsoft code"

Both observations are correct.
Though there is an implementation of TSQL that mentions SELECT ... INTO explicitly
But you have to declare that your context is TSQL

but this not Cache SQL anymore as initially inquired

Robert Cemper · Oct 9, 2020 go to post

in IRIS you have to do it in 2 steps

  • CREATE GLOBAL TEMPORARY TABLE tempfemale (
        Category, 
        CrewNumber, 
        MedicalCertificationDate, 
        Seat, 
        SeatbeltUsed, 
        Sex, 
        ShoulderHarnessUsed, 
        ToxicologyTestPerformed, 
        childsub
    )

    see: https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=RSQL_createtable#RSQL_createtable_temp
     
  • and
    INSERT  INTO tempfemale 
         SELECT 
         
    Category, CrewNumber, MedicalCertificationDate,Seat,     
                  SeatbeltUsed, Sex, ShoulderHarnessUsed, ToxicologyTestPerformed,                                childsub
                  FROM Aviation.Crew
                  WHERE Sex = 'F'

see:  https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View.cls?KEY=RSQL_insert#RSQL_insert_select 

Robert Cemper · Oct 1, 2020 go to post

Further elaborating on the suggestion of @Vic Sun 

set file="c:\whateverfilename.txt"   ;according to file system and access rights
if $data(^%SYS)    ; or whatever global
open file:("WNS")
use file zwrite @$ZR  ; send all output to file
close file

you can even verify it from terminal command line and the result looks like this:

^%SYS("tercap","vt320","bin","hon")=$c(3,155)_"1m"
^%SYS("tercap","vt320","bin","ich")=$c(0,0,155,129,0,0)_"@ÿ"
^%SYS("tercap","vt320","bin","il")=$c(0,0,155,129,0,0)_"Lÿ"
^%SYS("tercap","vt320","bin","il1")=$c(2,155)_"L"
^%SYS("tercap","vt320","bin","ind")=$c(2,27)_"D"
^%SYS("tercap","vt320","bin","init")=$c(18,155)_"!p"_$c(155)_"62""p"_$c(27)_"F"_$c(27)_")0"_$c(155)_"?7h"                                                      ^%SYS("tercap","vt320","bin","poff")=$c(3,155)_"4i"
^%SYS("tercap","vt320","bin","pon")=$c(3,155)_"5i"
^%SYS("tercap","vt320","bin","ri")=$c(2,27)_"M"
^%SYS("tercap","vt320","bin","ron")=$c(3,155)_"7m"
^%SYS("tercap","vt320","bin","uon")=$c(3,155)_"4m"
^%SYS("tercap","vt320","cols")=80
^%SYS("tercap","vt320","flags")=16
^%SYS("tercap","vt320","lins")=24
^%SYS("tercap","vt320","names")="vt320|VT320|DEC-vt320"
^%SYS("tercap","vt320","src","aoff")="$c(155),""0m"""
^%SYS("tercap","vt320","src","bell")="$c(7)"
^%SYS("tercap","vt320","src","bon")="$c(155),""5m"""
^%SYS("tercap","vt320","src","bormap")="1,108,1,108,1,107,1,107,1,109,1,109,1,106,1,106,1,113,1,113,1,120,1,120,1,113,1,113,1,120,1,120,1,108,1,107,1,109,1,106,1,113,1,113,1,120,1,120,1,97,1,97,1,97,1,97,0,42,1,126,1,97"
^%SYS("tercap","vt320","src","clr")="$c(155),""H"",$c(155),""J"""
^%SYS("tercap","vt320","src","coff")="$c(155),""?25l"""

Robert Cemper · Oct 1, 2020 go to post

pure COS solution, split into  steps to allow comments (you may condense it of course)

read  arg    ; get something to work on
"Not  Working Example":"x, y, z, a, b, c",

set name=$piece(arg,":",1)   ; split name from content
set content=$piece(arg,":",2)
if $extract(content,1)=""""  {    ; we got a quuted list
           set value=$piece(content,"""",2)    ;extract first quoted
           set value=$piece(value,",",first,last)  ; pick the relevant pieces
          }
else  {  
       set value=content            ;  no quotes, no pieces
}

 
Robert Cemper · Sep 23, 2020 go to post

"NotIn" is not part of InterSystems ObjectScript. Also not "in" . This is SQL slang
Alternative to the proposal of @Adrian Zeeman  you may try this construct:

if '($LF($LFS("A39,A40,O01,O11,O09,R01"),msgType) { . . . 
Robert Cemper · Sep 23, 2020 go to post

Depending on how you have built your WebAPI every request may run its own session with its own job id.
CSP has the concept of a SessionID to bypass such problem.
But it requires that you pass the Id back to the client and clean it up once done.

You may follow this philosophy using a random unused key [ $D(^SORT(key))=0 ]  instead of $J that you pass back to the client.
Cleaning it up is a good practice to avoid to much waste in your global. [ BTW. also with $JOB ]
 

Robert Cemper · Sep 18, 2020 go to post

Hi  @Dan Pahnke 
The "Red Fire Button"  is a synonym I used over the years with my various teams for an action/decision
that should not be taken by a single person but follows (at least) the 4-eyes-principle.

Inspired by an endless number of Airforce fighting movies from Hollywood and
the old but still incredible song from The Dubliners.
And its best cover version

Robert Cemper · Sep 18, 2020 go to post

#1)  check that ALL code Dbs are part of your Mirror. 
There is a fair chance that not all code you use is in a single code DB but is mapped to other DBs.
I'm not talking about implicitly mapped pieces like all System and %* Utilities.
#2) If  you use code mapping It is highly important that Package mapping AND routine mapping go hand in hand
#3) Whatever Mirror is synchronizing is based on Global Journal. So also all code DBs require Journalling.
    Since every routine or class whether deployed or not is stored in some global.

But my personal preference is not to apply Mirror to code DBs.
Mainly to control the point in time when a Change/Update happens.  
I'm a fan of the Red Fire Button and like to control the moment  of essential changes
 

Robert Cemper · Sep 17, 2020 go to post

It looks to me as if the query optimizer gets confused to have INSERT and UPDATE to the SAME table in sequence.
If they both run well when separated I'd suggest calling both in sequence.
From code or performance, I see no advantage to pack them together. Rather the opposite.