Vitaliy Serdtsev · Oct 9, 2017 go to post

The following code has been tested on versions 2015.2 and 2017.2:

Class dc.demo Extends %RegisteredObject
{

Property f1 As %String;

Property f2 As %Integer;

Property f3 As %ListOfDataTypes;

Method GetTarget(
  ByRef pParms As %String,
  Output pObj As %RegisteredObjectAs %Status
{
  pObj = ..%New()
  
  i $g(pParms(1))="a" {
    pObj.f1 "Peter"
    pObj.f2 = 21
  }else{
    pObj.f1 "Bob"
    pObj.f2 = 12
    pObj.f3 ##class(%ListOfDataTypes).%New()
    pObj.f3.InsertList($lb("a","b","c"))
  }
  q $$$OK
}

ClassMethod Obj2Json()
{
  ;d ##class(dc.demo).Obj2Json()
  
  parms(1)="a"
  ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,..%New(),"GetTarget",.parms,$$$YES,"aeloq")
  "1) ",stream.Read($$$MaxLocalLength),!
  
  parms
  ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,..%New(),"GetTarget",.parms,$$$YES,"aeloq")
  "2) ",stream.Read($$$MaxLocalLength),!
}

ClassMethod Json2Obj()
{
  ;d ##class(dc.demo).Json2Obj()
  
  ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject("{""f1"":""Peter"",""f2"":21,""f3"":[]}",$classname(),.obj)
  obj.f1," : ",obj.f3.Count(),!

  ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject("{""f1"":""Bob"",""f2"":12,""f3"":[""a"",""b"",""c""]}",$classname(),.obj)
  obj.f1," : ",obj.f3.Count(),!
}

}

Result:

USER>##class(dc.demo).Obj2Json()
1) {"f1":"Peter","f2":21,"f3":[]}
2) {"f1":"Bob","f2":12,"f3":["a","b","c"]}
 
USER>##class(dc.demo).Json2Obj()
Peter : 0
Bob : 3
Vitaliy Serdtsev · Oct 9, 2017 go to post

Class dc.SomeClass
{

Parameter SOMENAME = "Name1";

Query SomeQuery(SOMENAME = {..#SOMENAME}) As %SQLQuery {   SELECT ID || :SOMENAME || Name FROM Table1 }

Query SomeQuery1() As %SQLQuery {   #define SOMENAME ##Expression("'"$parameter("dc.SomeClass","SOMENAME")"'")   SELECT ID || $$$SOMENAME || Name FROM Table1 }

Query SomeQuery2() As %SQLQuery {   #define SOMENAME ##Expression("'"##class(dc.SomeClass).#SOMENAME"'")   SELECT ID || $$$SOMENAME || Name FROM Table1 }

ClassMethod Test() {   ;d ##class(dc.SomeClass).Test()      rs=##class(%ResultSet).%New("dc.SomeClass:SomeQuery")   sc=rs.Execute()   i $$$ISERR(sc{     d $System.Status.DisplayError(sc)   else {     rs.%Display()   } } }

Vitaliy Serdtsev · Oct 6, 2017 go to post

It also can be solved.

The behavior of zenPage.launchPopupWindow depends on the setting useSoftModal.

Play around with %OnUseSoftModals in the following example:

Class dc.demo Extends %ZEN.Component.page
{

XData Style
{
<style type="text/css">
.modalGroupCloseButton {
  background-colorgreen;
  filteralpha(opacity=20);
  opacity: 0.2;
}
.modalGroupCloseButtonHover {
  background-colorgreen;
}
</style>
}

XData CSS3Style
{
<style type="text/css">
.modalGroupCloseButton {
  background-colorred;
  opacity: 0.2;
}
.modalGroupCloseButtonHover {
  background-colorred;
}
</style>
}

XData Contents [ XMLNamespace "http://www.intersystems.com/zen" ]
{
<page xmlns="http://www.intersystems.com/zen">
<button caption="Show modal" onclick="zenPage.showModal()"/>
</page>
}

ClientMethod showModal() [ Language = javascript ]
{
  if (!this.isPopup) {
    zenPage.launchPopupWindow(window.location.href,'DEMO MODAL','center=yes,resizable=no,width=320,height=240');
    
    var els=document.body.getElementsByClassName('modalGroupClose');
    if (els.length>0) els[0].onclick=zenPage.onhideGroupHandler;
  }
}

ClientMethod onhideGroupHandler() [ Language = javascript ]
{
  if (confirm('[1] Are you sure you want to leave?')) {
    var modalGroup=zenPage.modalStack[zenPage.modalStack.length - 1];
    modalGroup.hideGroup();
  }
}

ClientMethod onunloadHandler(e) [ Language = javascript ]
{
  var message "[2] Are you sure you want to leave?";

  if (typeof == "undefined") {
      e window.event;
  }
  if (e) {
      e.returnValue message;
  }
  return message;
}

/// Return whether the current page should use soft modal divs.
/// The default behaviour is to return 1 for the current instance, but users may set the
/// ^%ISC.ZEN.useSoftModals global change this system-wide.
/// In some cases, it may be worth modifying the value of the flag based on
/// the user agent, particularly if users are expected to access the application
/// from mobile devices where new windows behave differently.
ClassMethod %OnUseSoftModals() As %Boolean CodeMode = expression ]
{
$$$YES
}

/// This callback method determines lets a page specify level of CSS support is used by this page.
/// The default is to return "", which indicates that the built-in ZEN CSS level
/// detection is used. A page can override this and return 2 or 3.
Method %OnDetermineCSSLevel() As %Integer CodeMode = expression ]
{
3
}

}
Vitaliy Serdtsev · Oct 5, 2017 go to post

Yes, checked and it works in IE11 (see screenshot).
In addition, you can find in the file zenutils.js the following code:

if (zenIsIE && (!(((typeof zenIsHTML5 != 'undefined'&& zenIsHTML5) && ((typeof document.documentMode != 'undefined'&& document.documentMode==9)))) {
  <...>
  window.document.body.onload function(event) { return zenPageEventHandler('onload',event); }
  window.document.body.onbeforeunload function(event) { return zenPageEventHandler('onunload',event); }
  <...>
}

Googling "onbeforeunload не работает" or "onbeforeunload javascript not working".

Vitaliy Serdtsev · Oct 5, 2017 go to post

Client Side Callback Methods for the Page

///  This client event, if present, is fired when the page is unloaded.
///  If this method returns a string value, then that is used as the
///  return value of the HTML page's onbeforeunload handler (if more than
///  one component returns a string, the first one encountered is used).
ClientMethod onunloadHandler() [ Language = javascript ]
{
  return 'Are you sure you want to leave?';
}
Vitaliy Serdtsev · Oct 4, 2017 go to post

The following query produces zero results:
select bar.name from foo join bar on bar.id %INLIST MyBars

This is because MyBars is stored as a $lb($lb(id1),$lb(id2),...,$lb(idN)), and you try to query the data stored in the form $lb(id1,id2,...,idN).

Your query need some change to make it work, namely:

select name from foo join bar b on $listbuild(b.%ID) %INLIST MyBars

Vitaliy Serdtsev · Oct 2, 2017 go to post

For passing array by reference try to use the class $system.WorkMgr instead of job.

Sample:

Class demo.test Abstract ]
{

ClassMethod Test(arrayAs %Status
{
  ^CacheTempUser.dbg
  ^CacheTempUser.dbg($zp)=array
  ^CacheTempUser.dbg($zp,"$j")=$j
  q $$$OK
}

}

demo()
  queue,arr

  arr(1)="qwe"
  arr(2)="asd"
  
  "$j = ",$j,!
  queue=$system.WorkMgr.Initialize("/multicompile=1",,1)
  queue.Queue("##class(demo.test).Test",.arr)
  queue.WaitForComplete()
  
  zw ^CacheTempUser.dbg
  q

Result:

USER>^demo
$j = 7600
^CacheTempUser.dbg(8488,1)="qwe"
^CacheTempUser.dbg(8488,2)="asd"
^CacheTempUser.dbg(8488,"$j")=8348
Vitaliy Serdtsev · Sep 21, 2017 go to post
"$ZTZ=",$ztz,!

#define 2utc(%t) $tr($zdt($zdt($zdth($system.SQL.TOTIMESTAMP(%t,"YYYYMMDDHHMISS"),3),-3),8,1)," :")

s t="20160105125915"
"Winter time: ",t," -> ",$$$2utc(t),!

t="20160705125915"
"Summer time: ",t," -> ",$$$2utc(t)

Result (GMT+02:00, Chisinau):

USER>^test
$ZTZ=-120
Winter time: 20160105125915 -> 20160105105915
Summer time: 20160705125915 -> 20160705095915

Note:

  • Takes into account summer/winter time
  • Is taken into account to option of OS for automatic daylight saving time

For example, if in the OS to disable automatic daylight saving time, the result will be different:

USER>^test
$ZTZ=-120
Winter time: 20160105125915 -> 20160105105915
Summer time: 20160705125915 -> 20160705105915
Vitaliy Serdtsev · Sep 15, 2017 go to post

In addition to the above.

Fill the array tArray can be charged a method %ListOfDataTypes:BuildValueArray(). But I still prefer the next option (unless you count every tick of CPU :) ):

list=##class(%ListOfDataTypes).%New()
list.InsertList(tInputList)
 
; method #1
key="" {value=list.GetPrevious(.key)while (key'="")
 
; method #2 (better way)
key=list.Size:-1:1 value=list.GetAt(key)

Vitaliy Serdtsev · Jul 25, 2017 go to post

You can fix this error directly now, if you don't want to wait for the release of version 2018.1.1.

To do this, follow these steps:

  1. make export the locale "deuw"
    %SYS>Locales("deuw")="" d $system.OBJ.DisplayError(##class(Config.NLS.Locales).ExportList("loc_deuw.xml",.t,.Locales)) zw Locales,t
  2. fix the file loc_deuw.xml (by default located in the folder %CACHEHOME%\Mgr)
    Name of subtable
    (Where to insert)
    New lines
    (That to insert)
    Add the following lines to the appropriate subtables:
    COL-German3-Unicode <FromToItem FromToKey="55,55,1">83,83;</FromToItem>
    <FromToItem FromToKey="55,55,2">7838;</FromToItem>
    <FromToItem FromToKey="55,55,3">83,7838;</FromToItem>
    COL-Unicode-German3 <FromToItem FromToKey="7838">55,55;2</FromToItem>
    <FromToItem FromToKey="83,83">55,55;1</FromToItem>
    <FromToItem FromToKey="83,7838">55,55;3</FromToItem>
    LowerCase-Unicode-Unicode <FromToItem FromToKey="7838">223</FromToItem>
    UpperCase-Unicode-Unicode <FromToItem FromToKey="223">7838</FromToItem>
  3. import fixed loc_deuw.xml:
    %SYS>d $system.OBJ.DisplayError(##class(Config.NLS.Locales).ImportAll("loc_deuw.xml",.t,1+2+4)) zw t
    %SYS>d $system.OBJ.DisplayError(##class(Config.NLS.Locales).Compile("deuw"))
    %SYS>Locale^NLSLOAD("deuw")

    Just in case, restart Caché.

Now, run a small test

#include %systemInclude
#include %occErrors
#include %syNLS
test() public {
  
  #dim ex As %Exception.AbstractException

  try {
    $$$AddAllRoleTemporaryInTry
    n $namespace
    s $namespace="%SYS"
    
    oldLocale=$$$LOCALENAME
    w "Old locale = ",oldLocale,!
    ##class(Config.NLS.Locales).Install("deuw")
    "Current locale = ",$$$LOCALENAME,!!
    
    ^||low,^||up

    w="wei"_$c(223)_"er","weiter","weiser" {
      ^||low($zcvt(w,"L"))=1
      ^||up($zcvt(w,"U"))=1
    }
    zw ^||low,^||up
    
    low=$c(223)
    up=$zcvt(low,"U")
    zw low,up
    zzdump low,up
    
  }catch(ex{
    "Error "ex.DisplayString(),!
  }
  ##class(Config.NLS.Locales).Install(oldLocale)

}

My result:

USER>d ^test
Old locale = rusw
Current locale = deuw
 
^||low("weiser")=1
^||low("weißer")=1
^||low("weiter")=1
^||up("WEISER")=1
^||up("WEIẞER")=1
^||up("WEITER")=1
low="ß"
up="ẞ"
 
0000: DF                                                      ß
0000: 1E9E                                                    ẞ
Vitaliy Serdtsev · Jul 25, 2017 go to post

Try this:

Property Amounts As %ArrayOfDataTypes SqlComputeCode = { set {*} ##class(ContainerSize).GetTotalAmounts({ID})}, SqlComputedTransient ];

ClassMethod GetTotalAmounts(thisIDAs %List
{
  quit $lb(
           $lb("GBP",1.23),
           $lb("EUR",2.45)
          )
}
Vitaliy Serdtsev · Jul 20, 2017 go to post

There is a method $system.Memory.Clean(<cleancache>, <defragment>), but unfortunately it appeared only with version 2011.1.

Try this code (consider that LONGVARCHAR = %Stream.GlobalCharacter) Read a CLOB through JDBC:

try {   ByteArrayOutputStream bos = new ByteArrayOutputStream();   Statement st = dbconn.createStatement();   ResultSet rs = st.executeQuery("Select ID,Text from eprinstance.isegment");     while (rs.next())   {     int len;     Reader reader = rs.getCharacterStream("Text");     while ((len = reader.read()) != -1)       bos.write(len);       System.out.println(bos);       reader.close();     bos.reset();   }     bos.close();   st.close();   rs.close();     System.out.println("OK!"); } finally {   dbconn.close(); }

Vitaliy Serdtsev · Jul 18, 2017 go to post

I didn't know that Caché allowed to use JOINs without specifying FROM.

JOIN or SELECTOptional FROM Clause?

But it should be noted that for JOIN not all usages are supported, for example:

Supported:

SELECT * FROM 
  
(select 'aaa' Column1,'bbb' Column2 union select 'ccc','ccc' union select 'xxx','yyy' union select 'hhh','zzz'Table1
LEFT OUTER JOIN
  
(select 'ggg' Column1,'hhh' Column3 union select 'xxx','zzz'Table2
ON Table1.Column1=Table2.Column3Not supported:

SELECT * FROM 
  
(select 'aaa' Column1,'bbb' Column2 union select 'ccc','ccc' union select 'xxx','yyy' union select 'hhh','zzz'Table1
INNER JOIN 
  
(select 'ggg' Column1,'hhh' Column3 union select 'xxx','zzz'Table2
USING (Column1)

Vitaliy Serdtsev · Jul 18, 2017 go to post

  1. Non-optimal plan does not necessarily mean low speed. I have had cases where the query with the best plan to work longer than a query with worse plan.
  2. Now the optimizer is pretty advanced. Important to periodically make tuning table and add an index on each field involved in the search.
  3. When unknown parameters >5, is easier to do one query than to write a bunch of code.

Vitaliy Serdtsev · Jul 18, 2017 go to post

Possible do on-other, namely to write one "universal" query:

select * from person where 
(lastname=:lastname or :lastname is null) and
(
age :age or :age is null)

That is:
sql "select * from person where (lastname=? or ? is null) and (age > ? or ? is null)"resultset statement.%Execute(param1param1param2param2)

Vitaliy Serdtsev · Jul 18, 2017 go to post

The solution with ##class(%xsd.hexBinary).LogicalToXSD works, but be careful, it only works when all characters in the string have codes <256.

All right, because the function works with an array of bytes (binary).
Therefore pre-to need lead N-byte string to single-byte string and only then do the conversion, for example:

trantable="SAME","UTF8" {
 "-------",!,trantable,!
 xN="π=3.14159..." zzdump xN !
 x1=$zcvt(xN,"O",trantablezzdump x1 !!
     
 hex=##class(%xsd.hexBinary).LogicalToXSD(x1)
 zw hex
     
 w $zcvt(##class(%xsd.hexBinary).XSDToLogical(hex),"I",trantable),!!
}

USER>^test
-------
SAME
 
0000: 03C0 003D 0033 002E 0031 0034 0031 0035                 π=3.1415
0008: 0039 002E 002E 002E                                     9...
 
0000: C0 03 3D 00 33 00 2E 00 31 00 34 00 31 00 35 00         À.=.3...1.4.1.5.
0010: 39 00 2E 00 2E 00 2E 00                                 9.......
 
hex="C0033D0033002E00310034003100350039002E002E002E00"
π=3.14159...
 
-------
UTF8
 
0000: 03C0 003D 0033 002E 0031 0034 0031 0035                 π=3.1415
0008: 0039 002E 002E 002E                                     9...
 
0000: CF 80 3D 33 2E 31 34 31 35 39 2E 2E 2E                  Ï.=3.14159...
 
hex="CF803D332E31343135392E2E2E"
π=3.14159...
Vitaliy Serdtsev · Jul 14, 2017 go to post

l=$lb("",,,,,"",,,"BOOLEAN","0",2,2,"1",,"bla",$lb(,,"bla","20050502123400"),"",1,"bla",,0)
jsonStr=[(l)].%ToJSON()
jsonStr,!!
   
obj=[].%FromJSON(jsonStr)
list=obj."0"
zw list

Result: ["\u0002\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0001\u0001\t\u0001BOOLEAN\u0003\u00010\u0003\u0004\u0002\u0003\u0004\u0002\u0003\u00011\u0001\u0005\u0001bla\u0019\u0001\u0001\u0001\u0005\u0001bla\u0010\u000120050502123400\u0002\u0001\u0003\u0004\u0001\u0005\u0001bla\u0001\u0002\u0004"]

list=$lb("",,,,,"",,,"BOOLEAN","0",2,2,"1",,"bla",$lb(,,"bla","20050502123400"),"",1,"bla",,0)

Vitaliy Serdtsev · Jul 13, 2017 go to post

Hi Uri.

In my opinion, this possible hear only empirically through load testing on specific hardware. It is also important and the interface between Caché and the world .NET, namely the number of calls made to a DLLBetter performance.

Sometimes the performance gain is observed when .NET refers to Caché (ADO.NET, eXTreme .NET Provider), and not vice versa (.NET Gateway). But it always requires a revision of the architecture.

By the way, for .NET Provider/ADO.NET there is Connection Pooling

Vitaliy Serdtsev · Jul 13, 2017 go to post

Personally, I prefer to use all out of the box, so as not to produce zoo libraries/technologies/languages, etc.

Really, both operations possible to execute at a time, for instance so:

w $$$FormatText("Create a signature and convert it to base64 (%1)",file64),!
cmd=$$$FormatText("openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign %1 %2 | openssl base64 -out %3 -nopad",fileKey,fileMsg,file64)
cmd,!!
d $zf(-1,cmd)
Vitaliy Serdtsev · Jul 13, 2017 go to post

What exactly did you do? Quote from doc:

By default, the session timeout is to 900 seconds (15 minutes). You can change this default for a CSP application in the Management Portal; [Home] > [Security] > [Web Applications] page. Select the application and click Edit.

Note that if a session changes CSP applications during its life span, its timeout value will not be updated according to the default timeout defined in the application that the session moved into. For example, if a session starts out in CSP Application A, with a default timeout of 900 seconds, and then moves into CSP Application B, which has a default timeout of 1800 seconds, the session will still timeout after 900 seconds.

Check these points. Still see the page parameter AUTOLOGOUT.

Vitaliy Serdtsev · Jul 13, 2017 go to post

It's the wrong solution, because it works not for all cases such as this:

Set Text(1) = "Planet"
Set Text(2) = "Championship"
Set Text(3) = "2017"
Set Text(4) = "IT"

Here is more optimized code Sean:

ClassMethod Main(ByRef Text)
{
 f j=2:1:$o(Text(""),-1) f k=j:-1:2 s l=$l(Text(k))+1,$e(Text(k),l)=$e(Text(k-1),l,*),$e(Text(k-1),l,*)=""
}