1. If you replace:

set sc=gc.SetParameter(hstmt,$LB(gc.PutData(hstmt,.temp)),2)

with:

set sc = gc.PutData(hstmt, "TEXT")

or with:

set sc = gc.SetParameter(hstmt,$LB("TEXT"),2)

What do you get?

2. Also in your original 4-parameter generated code the stream is bound via:

 s err=$zf(-5,%SQLGateway,62,QHandle,4,1,1,12,500,0,500)

which is equal to

set sc=gc.BindParameter(hstmt, 4,1,1,12,500,0,500)

In your new code you bind the stream with:

Set sc=gc.BindParameter(hstmt,2,1,1,12,0,0,-2)

Is it from some other generated code?

Connection is not license.  From docs:

  • The Caché licensing system attempts to identify distinct users and to allocate one license unit per user. A user is identified by a license user ID, which can be an IP address, a username, a CSP session ID, or some other identifier depending on how the user connects.
  • Multiple processes started by or for a single user share a license unit up to the maximum number of processes per user. If the number of processes exceeds this maximum, a transition occurs and Caché begins allocating one license unit per process for that user ID. The system assumes that if the number of processes associated with a user ID exceeds the maximum, multiple users are accessing Caché through an intermediary (for example, a firewall system), so additional license units are required. (Processes started by the Job command are counted under the user ID invoking the command.)
  • Even if the number of processes under the user ID drops back under the maximum, Caché continues to allocate one license unit per process for that user ID. Only when all connections by the user ID are closed and there are no more processes under the user ID does license allocation reset to one unit for that user ID.

Maximum number of processes (connections) per user is 12.

Will this affect our own internal tasks?

It shouldn't as that would be one connection and license slot.Still, I'd recommend contacting WRC to clarify the issue completely.

Maybe remove $zf calls?

Here's sample code:

 ClassMethod Test()
{
//Create new Gateway connection object
set gc=##class(%SQLGatewayConnection).%New()
if gc=$$$NULLOREF quit $$$ERROR($$$GeneralError,"Cannot create %SQLGatewayConnection.")
  
//Make connection to target DSN
set pDSN="Cache Samples"
set usr="_system"
set pwd="SYS"
set sc=gc.Connect(pDSN,usr,pwd,0) 
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
if gc.ConnectionHandle="" W !, $$$ERROR($$$GeneralError,"Connection failed") QUIT
  
set sc=gc.AllocateStatement(.hstmt) 
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
  
//Prepare statement for execution
set pQuery= "select Name, DOB from Sample.Person WHERE Name %STARTSWITH ?"
set sc=gc.Prepare(hstmt,pQuery) 
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
//Bind Parameters
set sc=gc.BindParameter(hstmt,1,1,1,12,30,0,30)
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
set var = "A"
set sc=gc.SetParameter(hstmt,$LB(var),1)
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
//Execute statement
set sc=gc.Execute(hstmt)
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
//Get list of columns returned by query
set sc=gc.DescribeColumns(hstmt, .columnlist) 
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
 
//display column headers delimited by ":"
set numcols=$listlength(columnlist)-1 //get number of columns
for colnum=2:1:numcols+1 {
    Write $listget($listget(columnlist,colnum),1),":"
   }
write !
 
//Return first 20 rows 
set sc=gc.Fetch(hstmt)
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
s rownum=1
while((gc.sqlcode'=100) && (rownum<=20)) {
       for ii=1:1:numcols {
       s sc=gc.GetData(hstmt, ii, 1, .val)
       w " "_val
       if $$$ISERR(sc) break
       }
       s rownum=rownum+1
  write !
  set sc=gc.Fetch(hstmt)
if $$$ISERR(sc) break
   }
    
  //Close cursor and then disconnect
set sc=gc.CloseCursor(hstmt)
if $$$ISERR(sc) w !, $SYSTEM.OBJ.DisplayError(sc) QUIT 0
  
set sc=gc.Disconnect()
  
quit sc
}

Check BindParameter method and EnsSQLTypes for SQL types macro definitions. Varchar maybe.

You can also try to call PutData method and similar methods (by passing parts of stream there).

There are two operations:

  • comparing repository/instance
  • bringing one on compliance with other

The first part (comparison) could be easily generalized, as the underlying Git repository allows us to do it  regardless.

The second part, however is very specific and should be implemented as a part of the source control hooks. I don't see how it could be generalized.

In my GitLab code I specify an interface which end user should implement - it's a method that accepts a list of deleted items (filepaths). Implementations of this interface should first translate file paths into internal names (i.e. /src/Utils/Math.cls -> Utils.Math)  and then delete them.

That said, full build from scratch can solve the issue completely. That's a decent approach to take towards more "lively" environments.

Finally, as we're talking about deleting classes, at least for me it's an extremely rare case. Do you encounter it often? How & Why?

Right. Forgot about it.

You can use ghostscript, here's how. In your case command would probably look like this:

Parameter COMMAND = "%1 -dBATCH -dNOPAUSE -sDEVICE=txtwrite -sOutputFile=%2 %3";

ClassMethod pdf2txt(pdf, txt) As %Status
{
    set cmd = $$$FormatText(..#COMMAND, ..getGS(), txt, pdf)
    return ..execute(cmd)
}

/// Get gs binary
ClassMethod getGS()
{
    if $$$isWINDOWS {
        set gs = "gswin64c"
    } else {
        set gs = "gs"
    }
    return gs
}

Execute method code.

Also note, that PDF can contain only images instead of text. in that case you'd need OCR.

If you want to enable/disable/modify several ensemble hosts, it's better to update them without updating production first and after that update production. Maybe your error is caused by racing production updates. Also add longer timeout on production update.

set sc = ##class(Ens.Director).EnableConfigItem("Item1", 1, 0)
write:'sc $System.Status.GetErrorText(sc)
set sc = ##class(Ens.Director).EnableConfigItem("Item2", 1, 0)
write:'sc $System.Status.GetErrorText(sc)
set sc = ##class(Ens.Director).UpdateProduction(60)
write:'sc $System.Status.GetErrorText(sc)

If you want to compare two in-memory objects, you can use method generators, there are several related articles and discussions on that:

Simple comparator on GiitHib - note that it's a runtime comparator, therefore slow. Better solution would be method generators.

If you're comparing objects of different classes you need to find their common ancestor class and compare using that.

If you're comparing stored objects you can calculate hashes and compare that.

All in all it's a very complex topic and you need to determine what requirements do you have:

  • Streams?
  • Lists? Arrays? Position change?
  • Loops/relationships strategy
  • How many levels to compare?
  • Different classes? Do they have common superclass?
  • Do you need to compare dynamic objects/objects from unrelated classes?

And design your comparator based on that.

Here's a simple hasher on GitHub.

That works only for CSP context and CSP pages. You can write a wrapper I suppose, but I think it would be easier to just write your own querybuilder code:

ClassMethod Link(server = "www.example.com")
{
    try {
        set cspContext = $data(%request)
        if 'cspContext {
          set %request = {} // ##class(%CSP.Request).%New()  
          set %response = ##class(%CSP.Response).%New()
          set %session = {} //##class(%CSP.Session).%New(-1,0)
        }
        set query("param") = 1
        set page = "/abcd.csp"
        set url = ##class(%CSP.Page).Link(page,.query)
        set url = $replace(url, page, server)
        write url
        kill:'cspContext %request,%response,%session
    } catch {
        kill:'$g(cspContext) %request,%response,%session
    }
}

With querybuilder:

ClassMethod Link(server = "www.example.com")
{
    set query("param") = 1

    set data = ""
    set param = $order(query(""),1,value)
    while (param'="") {
        set data=data _ $lb($$$URLENCODE(param)_"="_$$$URLENCODE(value))
        set param = $order(query(param),1,value)          
    }
    write server _ "?" _ $lts(data, "&")
}