Vitaliy Serdtsev · Mar 1, 2024 go to post

Try this:

ClassMethod test2()
{
  set tKP = ..%New()
  set tIdentifierSerial ##class(HS.Message.AddUpdateHubRequest).%OpenId(21986071).Identifiers
  zw tIdentifierSerial
  set tKP.IsSerial = 1
  set tKP.IsList = 1
  set tSerial tIdentifierSerial.GetObjectNext(.tKey)
  for {
    if tKey="" {quit}
    do tKP.SerialList.InsertObject(tSerial)
    set tSerial tIdentifierSerial.GetObjectNext(.tKey)
  }
  tKP.%Save()
}
Vitaliy Serdtsev · Feb 20, 2024 go to post

As far as I understand, the author wants to calculate the value of a field based on some list of fields whose names he takes from some table. Right?

Vitaliy Serdtsev · Feb 20, 2024 go to post

Try inserting an apostrophe through concatenation, something like

INSERT INTO MyText
    (text)
VALUES
    ('I visited O' || CHAR(39) || 'Brien before heading out of town.')

Vitaliy Serdtsev · Feb 8, 2024 go to post

I'm getting an error:

ERROR #9406: Unexpected format for value of field, searchCriteria, using class base mapping

After fixing the JSON, the error disappeared:

{"searchCriteria"{..}} ==> {"searchCriteria"[{..}]}

Also note the mapping between the fields professionalId/professionalNif and preparation/actPreparation

Vitaliy Serdtsev · Feb 8, 2024 go to post

Your code does not take into account the numbers, so an incorrect result is given for "L33T C0d3r".

Vitaliy Serdtsev · Feb 7, 2024 go to post

SELECT JSON_ARRAYAGG(json_obj)
  FROM (SELECT TOP 5
            JSON_OBJECT(
              'Name':name
              ,'Age':age
              ,'DOB':to_char(dob,'Day DD Month YYYY')
            ) json_obj
           FROM sample.person
       )
SELECT JSON_ARRAYAGG(json_obj)
  FROM (SELECT JSON_OBJECT(
                'Name':name
                ,'Age':age
                ,'DOB':to_char(dob,'Day DD Month YYYY')
                ) json_obj
       FROM sample.person
       )
  WHERE %VID BETWEEN 1 AND 5

Vitaliy Serdtsev · Jan 30, 2024 go to post
 

Reduced your code to 127:

ClassMethod ToKeyPad(pAs %String
{
l=$f(" 0   1    ABC2 DEF3 GHI4 JKL5 MNO6 PQRS7TUV8 WXYZ9",$$$UPPER($e(p,$i(i)))) q:l=1 s:$p(r,l-2\5,*+l-2#5+1)="" a
}
Vitaliy Serdtsev · Jan 29, 2024 go to post

Reduced your code to 137:

ClassMethod ToKeyPad(pAs %String
{
 i=1:1:$l(p){j=0:1:9{l=1:1:$f($p(" 0^1^ABC2^DEF3^GHI4^JKL5^MNO6^PQRS7^TUV8^WXYZ9","^",j+1),$e($$$UPPER(p),i))-1 r=$g(r)_j}} r
}
Vitaliy Serdtsev · Dec 20, 2023 go to post

Yes, most likely this is legacy code. For some reason I thought that since the %JSON package classes appeared only in IRIS, the code would also have to be new.

PS: by the way, can you compile an XData block without a name? Studio doesn't highlight the error?

Vitaliy Serdtsev · Dec 19, 2023 go to post

It is strange that the bug has not been fixed yet, because it is easy to fix it by adding one line to ##class(%JSON.Generator).GenerateMappingFromXdata():

<..>
For i=1:1:count {
  Set xdata=compiledclass.XDatas.GetAt(i)
  Set configName=xdata.Name
  If configName="" Continue ; since the Name field is required, how can it be empty?
  If xdata.MimeType'="text/xml" Continue
<..>

PS: by the way, I didn't quite understand why need to check "configName", given that "Name" is a required field. Any ideas?

Vitaliy Serdtsev · Nov 15, 2023 go to post

For versions of Caché 5.0.x, try the following code:

#include %systemInclude

n

s list=$lb(
  "test",
  "for",
  "searching unknown strings here is a very long piece with enough characters to get a lowercase alpha as a $list marker",
  "items",
  "in",
  "aaatArGetwaaaa",
  "lists",
    $lb(
    "/subs",
    "/values",
    "nested list",
    "did you see that ""w"" before the third piece?",
    "Stuart Strickland",
    "Yaron Munz")
)

str=$$$UPPER($$cccvt^%Wpglo(list,$$$MaxStringLength,.warn))
; or
; s str=$$$UPPER($$listDump^%Wprim(list,9))

'warn {
 str=$e(str,3,$l(str)-2) ; remove << & >>
 ;s @("LIST="_str) zw LIST
 
 !,$f(str,$$$UPPER("Targetw"))
}

Result: 162

If you end up searching for globals that use $lb(), then you might find it useful:

Vitaliy Serdtsev · Nov 10, 2023 go to post

You can convert a list to a string (and vice versa), regardless of the number of nestings. Unfortunately, I can't test this code for Caché 5.x, but I think it should work. Here is a small example of searching for a string in a list:

#include %systemInclude

 n    s list=$lb(    "test",    "for",    "searching unknown strings here is a very long piece with enough characters to get a lowercase alpha as a $list marker",    "items",    "in",    "aaatArGetwaaaa",    "lists",      $lb(      "/subs",      "/values",      "nested list",      "did you see that ""w"" before the third piece?",      "Stuart Strickland",      "Yaron Munz")  )    str=$$$UPPER(##class(%Utility).FormatString(list,,.overflow))  'overflow {    ;s @("LIST="_str) zw LIST        !,$f(str,$$$UPPER("Targetw"))  }

Result: 162

Vitaliy Serdtsev · Aug 25, 2023 go to post

There are much more interesting examples in the documentation, for example:

-"2Cats"_"Rats"
"8 apples" "4 oranges"
"8 apples" "four oranges"
Vitaliy Serdtsev · Aug 25, 2023 go to post

To your data storage scheme, I would like to clarify one point. Let's say there is the following data:

Organization
ID
1
2
3

GetOrgUpdatesResponse
ID Organizations
1 2,3
2 1,2
3 1,3

If you delete ID=1 (GetOrgUpdatesResponse), then ID=2 and ID=3 (Organization) will be automatically deleted. But in this case, the rows ID=2 and ID=3 (GetOrgUpdatesResponse) will contain incorrect data and the referential integrity will be violated. Or am I wrong?

Vitaliy Serdtsev · Aug 24, 2023 go to post

Alternatively, you can create a custom task that runs every 15 minutes, in which to enable disabled users. Since there are other ways to connect besides CSP, this will be more universal.

PS: why not disable "Disable account if login limit reached" and reduce Invalid login limit?

Vitaliy Serdtsev · Aug 24, 2023 go to post

What about method %SYS.Audit.Erase(Flags As %Integer = 0)?

Erase the audit file.
Flags: 0 - Erase all contents
1 - Erase and create new audit file
2 - Erase and create new audit file, treat as encryption state changed
Note that bit 1 infers that ALL data in the audit database will be deleted, not just Audit data

Vitaliy Serdtsev · Aug 23, 2023 go to post

Look at the class %SYSTEM.CSP:

CSS>d $system.CSP.DisplayConfig()
...

CSS>w $system.CSP.GetConfig("DefaultPasswordChangePage")
%CSP.PasswordChange.cls

CSS>d $system.CSP.SetConfig("DefaultPasswordChangePage","CSS.CSP.ChangePassword.cls")

CSS>w $system.CSP.GetConfig("DefaultPasswordChangePage")
CSS.CSP.ChangePassword.cls
Vitaliy Serdtsev · Aug 23, 2023 go to post

I did a little test: I created ZAUTHENTICATE.mac from scratch only with your code and got an error

5610 Referenced macro not defined: GetCredentialsFailed

After adding the following lines, the compilation was successful:

GetCredentials(ServiceName,Namespace,Username,Password,Credentials) Public {

#include %occErrors
#include %sySecurity

...
}
Vitaliy Serdtsev · Aug 23, 2023 go to post

Out of curiosity, how would you override that write statement in %CSP.Lookup? Isn't that generated code which one wouldn't want to touch?
The source code of the %CSP.PageLookup class is available in %SYS namespace

Vitaliy Serdtsev · Aug 22, 2023 go to post

I also noticed three things from your screenshots:

  1. instead of the standard program %SYS.Task.RunLegacyTask you are using a non-standard CTools.Task.Legacy. Is this how it should be?
  2. you have disabled "Reschedule task after system restart?". Is this how it should be?
  3. your task is scheduled for 4:00:00 under Task2, but judging by the history, it was launched at 11:52 and 11:56 under Administrator. Something doesn't add up here.

Vitaliy Serdtsev · Aug 22, 2023 go to post

Now it's clearer, you're cleaning up own audit records.
As @Yaron Munz pointed out, it's most likely that the Task2 user does not have some rights.

For simplicity, you can login to Caché Terminal under this user in the ACB namespace and call your code:

ACB:Task2>Do ^CleanAuditLogTables(14)
Vitaliy Serdtsev · Aug 22, 2023 go to post

The problem can be solved in two ways:

  1. use OPTIONS="popup,sortbox"
  2. make a correction to the %CSP.PageLookup
    Write "&nbsp;<a href=""javascript:searchSort("..QuoteJS(value)");"" title=""Sort Results by "alias""">"
    |
    V
    Write "&nbsp;<a href=""javascript:searchSort("i");"" title=""Sort Results by "alias""">"
In both case, the query will take the form "ORDER BY <the ordinal number of the field>", instead of "ORDER BY <field name>"

Vitaliy Serdtsev · Aug 22, 2023 go to post

I am confused by the name of your task "Clean Audit Log Tables" and the fact that for some reason it is in the ACB namespace. This is your own task?

By default, Caché already has a system task PurgeAudit in the %SYS namespace, where you can also specify the number of days (Purge Audit Log)

Vitaliy Serdtsev · Aug 21, 2023 go to post

Hi Ashok. Can you tell in more detail what exactly is the problem of not-keeping the trailing zeros: calculations give an incorrect result, an error is generated or something else?

USER>set json "decimal"(12.000)zw json
json={"decimal":12}  ; <DYNAMIC OBJECT>

USER>set n=12.000 zw n n=12

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.