Vitaliy Serdtsev · Aug 21, 2023 go to post

You can enable SQL Gateway Logging. Also make sure that the necessary JDK/JRE version is installed and configured correctly.

For simplicity, I would check the connection outside of IRIS, for example through DbVisualizer/DBeaver/SQuirreL/etc.

Vitaliy Serdtsev · Aug 14, 2023 go to post

In this case, it will be easier to temporarily dump the data into globals. Then you can access this data at any time, including through %ScrollableResultSet.

Here is a small example:

ClassMethod tt() [ Language = tsql, ReturnResultsetsSqlName myclsSqlProc ]
{
  drop table if exists mytemp1,mytemp2
  select name,dob,spouse into mytemp1 from Sample.Person where name like 'A%'
  select name,age,home_city,home_state into mytemp2 from Sample.Person where home_state 'MA'
}

ClassMethod Test()
{
  ##class(%SQL.Statement).%ExecDirect(,"call dc.mycls()")
  
  ##class(%SQL.Statement).%ExecDirect(,"select * from mytemp1").%Display()
  ##class(%SQL.Statement).%ExecDirect(,"select * from mytemp2").%Display()

  ; and even so
  ##class(%SQL.Statement).%ExecDirect(,"select * from mytemp1 cross join mytemp2").%Display()
}
Vitaliy Serdtsev · Aug 11, 2023 go to post

You are right, this is a mistake in the documentation.

Here is part of my code for IRIS 2023.2:

var connection =
new OdbcConnection(
"DRIVER={InterSystems IRIS ODBC35};SERVER=localhost;PORT=1972;DATABASE=USER;UID=_system;PWD=SYS;Unicode SQLTypes=1;Query Timeout=1");

Connection and display of the query result were successful.

Vitaliy Serdtsev · Aug 11, 2023 go to post

One of the possible options:

ClassMethod odbcTest() As %Integer ReturnResultsetsSqlName PersonSets2SqlProc ]
{
  #dim %sqlcontext As %ProcedureContext
  if '$isobject($Get(%sqlcontext)) set %sqlcontext ##class(%ProcedureContext).%New() } 

  tReturn = 0

  conn=##class(%SQLGatewayConnection).%New()
  sc=conn.Connect("TEST Samples","_system","SYS"//datasource
  if $$$ISOK(sc{
    conn.AllocateStatement(.h1)
    conn.Prepare(h1,"select name,dob,spouse from sample.person where name %STARTSWITH 'A'")
    conn.Execute(h1)
    %sqlcontext.AddResultSet(conn.getResultSet(h1))
    conn.AllocateStatement(.h2)
    conn.Prepare(h2,"select name,age,home_city,home_state from sample.person where home_state = 'MA'")
    conn.Execute(h2)
    %sqlcontext.AddResultSet(conn.getResultSet(h2))
    tReturn = 1
  }else{
    sqlcode=$system.Status.StatusToSQLCODE(sc,.msg)
    %sqlcontext.%SQLCODE sqlcode%sqlcontext.%Message msg
  }
  tReturn
}

Output:

SAMPLES>##class(%SQL.Statement).%ExecDirect(,"call Sample.PersonSets2()").%Display()
...

Surely there is a way to make it even easier.

Vitaliy Serdtsev · Aug 11, 2023 go to post

Try googling "ssl_error_syscall errno 10053". Perhaps some of the suggestions will help you.

Vitaliy Serdtsev · Aug 11, 2023 go to post

It's clearer now, this is the tricks of Excel, which convert the string 01022000 to the number 1022000. In your case, try changing the cell type from General to Text.

PS: when generating Excel reports, I use the Date cell type (short format DD.MM.YYYY), and from Caché/IRIS I return the %Date type. The components that I use already know how to translate the internal representation of the date 01.02.2000 from the Caché/IRIS format (58105) to Excel (36557) and vice versa.

Vitaliy Serdtsev · Aug 4, 2023 go to post
 

size = 52

ClassMethod IsValid(As %StringAs %Boolean
{
c=$a(s,$i(i))+1 g:1-$i(z,c=42-(c=41))&'z
}
Vitaliy Serdtsev · Aug 3, 2023 go to post

You can make it even easier:

ClassMethod IsValid(As %StringAs %Boolean Language = python ]
{
import regex;return regex.match("^((?>[^()]|\((?1)\))*)$",s) is not None
}

Unfortunately, RegExp ICU does not support recursive patterns, otherwise it would be possible to write

q $match(s,"^((?>[^()]|\((?1)\))*)$")
Vitaliy Serdtsev · Aug 1, 2023 go to post

Thanks for the comment. I tested only on the provided data. The second option requires improvement.

 

size = 72

ClassMethod IsValid(As %StringAs %Boolean
{
 q $l(s,"(")=$l(s,")")&($f(s,"(")<$f(s,")"))&($f(s,")(")-$f(s,"()")<2)
}
 

size = 69

ClassMethod IsValid(As %StringAs %Boolean
{
c=$i(c,$case($e(s,$i(i)),"(":1,")":-1,"":-2,:0)) q:c<0 c=-2 1
}
Vitaliy Serdtsev · Aug 1, 2023 go to post
 

size = 61

ClassMethod IsValid(As %StringAs %Boolean
{
 +##class(%iFind.Utils).TestSearchString($$$URLENCODE(s))
}
Vitaliy Serdtsev · Aug 1, 2023 go to post

It's strange, according to approved method of calculating the length of the solution, your code shows a length of 62, not 61. Have you measured the length of your solution using this method?

Vitaliy Serdtsev · Jul 19, 2023 go to post

Query FilterBy(
  Name As %String "",
  Age As %Integer ""As %SQLQuery(CONTAINID 1SELECTMODE "RUNTIME") [ SqlName SP_Sample_Filter_BySqlProc ]
{
SELECT TOP IDNameAgeSSN FROM %ALLINDEX Sample.Person WHERE 
(:Name IS NULL or Name %STARTSWITH :Name)
AND
(:Age IS NULL or Age >= :Age)
}

This kind of query ends up preventing Caché SQL compiler from optimizing using index based on each of the criteria made optional.
What is your conclusion based on? If you check the plans of this query for different combinations of parameters, then the corresponding indexes are used (it is assumed that the table was previously configured via TuneTable).

select * from Sample.SP_Sample_Filter_By('s')

SELECT TOP IDNameAgeSSN FROM %ALLINDEX Sample.Person WHERE  ('s' IS NULL or Name %STARTSWITH 's') AND (NULL IS NULL or Age >= NULL)

select * from Sample.SP_Sample_Filter_By(,47)

SELECT TOP IDNameAgeSSN FROM %ALLINDEX Sample.Person WHERE  (NULL IS NULL or Name %STARTSWITH NULL) AND (47 IS NULL or Age >= 47)

select * from Sample.SP_Sample_Filter_By('s',47)

SELECT TOP IDNameAgeSSN FROM %ALLINDEX Sample.Person WHERE  ('s' IS NULL or Name %STARTSWITH 's') AND (47 IS NULL or Age >= 47)

Vitaliy Serdtsev · Jul 19, 2023 go to post

Did a little test:

Class dc.test Abstract ]
{

ClassMethod GetObjectFieldValue(   pObject As %DynamicAbstractObject,   pField As %StringAs %String {   quit:pObject="" ""   quit:pField="" "" set tResponse=""   set tResponse pObject.%Get(pField)   return tResponse }

ClassMethod Test() {   p1={     "priority""2",     "lastmodifieduser""PORTAL",     "assignedto""jdoe01 STAFF",     "status""REVIEW",     "documentclass""PATIENTCASE",     "versiontoken""937486",     "departmentid""999",     "patientcaseid""105555424",     "providerusername""jdoe01",     "internalnote""firstMessage",     "subject""first123",     "patientid"9877654321,     "createduser""PORTAL",     "description""patient case - first123",     "documentroute""MDP",     "documentsubclass""PATIENTCASE_CLINICALQUESTION",     "documentsource""PORTAL",     "createddate""07/17/2023",     "lastmodifieddate""07/17/2023"}        p2="documentsubclass"   ..GetObjectFieldValue(p1,p2) }

}

Result:
USER>##class(dc.test).Test()
PATIENTCASE_CLINICALQUESTION

Vitaliy Serdtsev · Mar 31, 2023 go to post

There is nothing specific to IRIS here, except for the connection string. The same code with minimal changes should work with other DBMS: Oracle, SQL Server, PostgreSQL. etc. The Internet is full of examples of how to insert streams into the database via ADO.

Vitaliy Serdtsev · Mar 31, 2023 go to post

I wrote a small test on VBS, but you can easily redo this code for your language. For details on working with streams for ADO, see the documentation.

 

Code on Visual Basic Script

adVarChar=200
adLongVarChar=201
adLongVarBinary=205

adParamInput=1
adCmdText=1
adExecuteNoRecords=128

Set cn=Createobject("ADODB.Connection")

cn.ConnectionString="DRIVER={InterSystems IRIS ODBC35}; SERVER=127.0.0.1; PORT=1972; DATABASE=USER; UID=_system; PWD=SYS;"
cn.open

Set cmd = Createobject("ADODB.Command")

with cmd
  .ActiveConnection = cn

  .CommandText = "CREATE TABLE dc.test(Name VARCHAR(50) NOT NULL,Notes TEXT,Photo IMAGE)"
  .Execute ,,adCmdText + adExecuteNoRecords

  .CommandText = "insert into dc.test(Name,Notes,Photo)values(?,?,?)"

  .Parameters.Append .CreateParameter("pName", adVarChar, adParamInput, 50, "blablabla")
  .Parameters.Append .CreateParameter("pNotes", adLongVarChar, adParamInput, 2147483647, ReadTextFile("C:\bigText.txt","Windows-1251"))
  .Parameters.Append .CreateParameter("pPhoto", adLongVarBinary, adParamInput, 2147483647, ReadBinaryFile("C:\bigImage.png"))

  .Execute ,,adCmdText + adExecuteNoRecords

end with


WScript.Echo "Succesfully!"

Function ReadBinaryFile(FileName)
  Const adTypeBinary = 1

  'Create Stream object
  Dim BinaryStream
  Set BinaryStream = CreateObject("ADODB.Stream")

  'Specify stream type - we want To get binary data.
  BinaryStream.Type = adTypeBinary

  'Open the stream
  BinaryStream.Open

  'Load the file data from disk To stream object
  BinaryStream.LoadFromFile FileName

  'Open the stream And get binary data from the object
  ReadBinaryFile = BinaryStream.Read
End Function

Function ReadTextFile(FileName, CharSet)
  Const adTypeText = 2

  'Create Stream object
  Dim BinaryStream
  Set BinaryStream = CreateObject("ADODB.Stream")

  'Specify stream type - we want To get binary data.
  BinaryStream.Type = adTypeText

  'Specify charset For the source text (unicode) data.
  If Len(CharSet) > 0 Then
    BinaryStream.CharSet = CharSet
  End If

  'Open the stream
  BinaryStream.Open

  'Load the file data from disk To stream object
  BinaryStream.LoadFromFile FileName

  'Open the stream And get binary data from the object
  ReadTextFile = BinaryStream.ReadText
End Function
 
Vitaliy Serdtsev · Mar 9, 2023 go to post

In my opinion, the documentation clearly explains the characteristics of such a property and its purpose, for example:

  • To specify that this property has the characteristics of a multidimensional array
  • That is, the property is also automatically Transient.
  • Multidimensional properties are rare but provide a useful way to temporarily contain information about the state of an object.

Vitaliy Serdtsev · Mar 7, 2023 go to post

CREATE PROCEDURE DeleteSpecimenTwo
  (IN Specno VARCHAR(50))
LANGUAGE OBJECTSCRIPT
{
NEW SQLCODE,%ROWID,%ROWCOUNT
#SQLCOMPILE SELECT=LOGICAL

&sql(DELETE FROM PCONT WHERE PSPECNO :Specno) If $Get(%sqlcontext)'=$$$NULLOREF Set %sqlcontext.%SQLCODE=SQLCODE,%sqlcontext.%ROWCOUNT=%ROWCOUNT Quit:SQLCODE<0

&sql(DELETE FROM PSPEC WHERE PSPECNO :Specno) If $Get(%sqlcontext)'=$$$NULLOREF Set %sqlcontext.%SQLCODE=SQLCODE,%sqlcontext.%ROWCOUNT=%ROWCOUNT }

Vitaliy Serdtsev · Mar 6, 2023 go to post
 

48 (the same example, but with documented feature)

ClassMethod Check(As %StringAs %Boolean
{
 i=1:1:90{ret:$l($$$UPPER(w),$c(i))>2 01
}
Vitaliy Serdtsev · Mar 6, 2023 go to post
 

52

ClassMethod Check(As %StringAs %Boolean
{
w=$zu(28,w,5),l=$l(w,$e(w,$i(i))) g:l=2 'l
}
 

48

ClassMethod Check(As %StringAs %Boolean
{
 i=1:1:90{ret:$l($zu(28,w,5),$c(i))>2 01
}
Vitaliy Serdtsev · Mar 6, 2023 go to post

For an empty string (a=""), the <UNDEFINED> error occurs because the variable r is not defined.

Vitaliy Serdtsev · Feb 23, 2023 go to post

See String-to-Number Conversion and further.

w ("45" < "46") === 45 < 46
w ("45" > "46") === 45 > 46
w ("V45" < "V46") === 0 < 0
w ("V45" > "V46") === 0 > 0
w ("V45" <= "V46") === 0 <= 0
w ("V45" >= "V46") === 0 >= 0
w +"45" -> 45
w +"V45" -> 0

Vitaliy Serdtsev · Feb 20, 2023 go to post

#include %occInclude

alg,algInfo

alg="RSA-OAEP","A256KW","A256CBC-HS512","A256GCM" {   algInfo=##class(%Net.JSON.JWA).GetAlgInfo(alg)   alg," = ",$s(algInfo'="":algInfo.%ToJSON(),1:$system.Status.GetErrorText($$$ERROR($$$UnsupportedJOSEAlg,alg))),! }

Output:

RSA-OAEP = {"alg":"RSA-OAEP","kty":"RSA","use":"enc","key_ops":["wrapKey","unwrapKey"],"keysize":2048,"hashsize":0}
A256KW = {"alg":"A256KW","kty":"oct","use":"enc","key_ops":["wrapKey","unwrapKey"],"keysize":256,"hashsize":0}
A256CBC-HS512 = {"alg":"A256CBC-HS512","kty":"oct","use":"enc","key_ops":["encrypt","decrypt"],"keysize":256,"hashsize":512}
A256GCM = Error #9723: Unsupported JOSE algorithm: A256GCM
Since this algorithm is not supported, it remains either to try to modify the GetAlgInfo method or to work directly with the OpenSSL library or similar.

Vitaliy Serdtsev · Feb 17, 2023 go to post

Try working directly with the private key file, for example:

#include %msql

f=##class(%Stream.FileBinary).%New()
f.Filename="С:\your_private_key.pem"
privateKey=f.Read($$$MaxLocalLength)
 
myString  "text to sign"
signedTxt ##class(%SYSTEM.Encryption).RSASHASign(256, $zcvt(myString,"O","UTF8"), privateKey)
zw signedTxt

This code works for me.