Eduard Lebedyuk · May 9, 2016 go to post

What's the advantage of using $$$ERRORCODE macro? ^%qCacheObjectErrors global contains the same values.

Eduard Lebedyuk · May 12, 2016 go to post

> Pros/Cons for multiple simple deployments vs single more complex deployment)

If you have a virtualization platform and clients are heavy users, it would probably make a lot of sense to go with multiple simple deployments, and if you'll have many clients who can't load a single smallest node then "single complex"  would be better.

Developing "lots of simple" system (so 1 simple system with multiple deploys) would be cheaper (as you don't have tyo think about domains and security would be simpler, maybe some other simplifications would be possible), but hosting would (maybe) be more expensive as there is a lot of overhead in resources (to run OS/Cache for each deploy).

Eduard Lebedyuk · May 12, 2016 go to post

Also

Set a = $List(list, *) 

is slower than

Set a = $List(list, 3) 

for 3-element list,  on the other hand:

Set a = $List(list) 

is faster than

Set a = $List(list, 1) 
Eduard Lebedyuk · May 12, 2016 go to post

Graphs (and measurements in a method) include conversion times.

>$find

I tried this:

$$$START("Using $find (hardcoded delimiter): ")
For i=1:1:pIterations {
    Set tPosition = 0
    Do {
        Set tNewPosition = $Find(tString,",",tPosition)
        Set tValue = $Select(tNewPosition'=0:$Extract(tString,tPosition,tNewPosition-2),1:$Extract(tString,tPosition,*))
        Set tPosition = tNewPosition
    } While tPosition '= 0
}
$$$END

It was slow. Is there any way to write this faster - eg. without select (or other condition for the last piece)?

Eduard Lebedyuk · May 12, 2016 go to post

Tried it again, got the same results as you. $find performs better on both short (20 chars per piece, 100 pieces) and long strings (2000 chars per piece, 100 pieces).

Eduard Lebedyuk · May 13, 2016 go to post

Some comments on code.

  • rename fileToImport to mysqldumpFile
  • remove 2 references to other classes

That said, it does not work on my sql script on the table creation part, despite throwing no errors (data insert throws an error -  Table not found, but previous  statement which creates the table seems to run okay, but the table does not get created)

UPD: Reread your post, only INSERT, ok.

Eduard Lebedyuk · May 19, 2016 go to post

1) I tried all three with the same result.

2) I linked the procedure, and it does not work when I select data from a linked table. When I check the int code for a query, there is no call to my procedure or linked procedure to be found.

Eduard Lebedyuk · Jun 6, 2016 go to post

I knew about the second option (my original question was not clear on that I guess), but I'm really interested in the first option: how do I open *.cls directly?

Here's the sample process:

when I click on ContractFinalApprovalProcess BPL opens instead of cls.

Eduard Lebedyuk · Jun 6, 2016 go to post

> one "extremum" version:

Does not really count. The method signature must be:

ClassMethod ToPhone(t As %String) As %String {}
Eduard Lebedyuk · Jun 6, 2016 go to post

I think only profiler for the exact case can really answer this kind of questions. Too many nuances in the real-life code for one-fits-all solution of any kind to be feasible.

Eduard Lebedyuk · Jun 6, 2016 go to post

Yes.  Use resources instead. Read the docs. $ZPARENT value is also a "resource" that's getting created with the process, so children processes can communicate events to a parent process easily. As processes you want to pass messages between are not parent and child,  you need to create and use explicitly named resources.

Eduard Lebedyuk · Jun 7, 2016 go to post

Wait interval can be very small so it's okay.

You could, I suppose, add some %ZSTOP code but that would be an overkill.

Eduard Lebedyuk · Jun 9, 2016 go to post

Here's CRC hashing method (accepts any number of arguments):

ClassMethod hash(in...)
{
    set delimiter = $c(255)
    set str = delimiter
    for i=1:1:in {
        set str = str _ in(i) _ delimiter
    }
    return $zcrc(str, 7)
}
Eduard Lebedyuk · Jun 9, 2016 go to post

> EnsLib.SQL.Snapshot.GetRowList().

Thank you.

> additional field in the external database

Would have been nice, but not happening unfortunately.

Eduard Lebedyuk · Jun 9, 2016 go to post

A maximum of 254 arguments can be passed to a method:

ClassMethod Gen(count = 255)
{
    set out = "w ..hash("
    for i=1:1:count-1 {
        set out = out _ "1,"
    }
    set out = out _ "1)"
    x out
}

ClassMethod hash(in...)
{
    set crc = 0
    for i=1:1:in {
        set crc = $zcrc($char(i#256)_in(i), 7, crc)
    }
    return crc
}
Eduard Lebedyuk · Jun 9, 2016 go to post

Yes, I'm aware of that.  There is not that much records (hundreds of thousands tops). Still, decided to do a comparison:

ClassMethod Time(count = 100000000)
{
    Set str = "111111111111111111111111111111111111111111111111111111111"
    Set time1 = $P($h, ",", 2)
    For i=1:1:count {
        s a = $zcrc(str, 7)
    }
    Set time2 = $P($h, ",", 2)
    For i=1:1:count {
        s a = $System.Encryption.MD5Hash(str)
    }
    Set time3 = $P($h, ",", 2)
    For i=1:1:count {
        s a = $System.Encryption.SHA1Hash(str)
    }
    Set time4 = $P($h, ",", 2)
    For i=1:1:count {
        s a = $System.Encryption.SHAHash(256, str)
    }
    Set time5 = $P($h, ",", 2)
    Write !,"CRC: ",time2-time1,!,"MD5: ",time3-time2,!,"SHA1: ",time4-time3,!,"SHA2: ",time5-time4
}

It outputs the following results:

CRC: 14
MD5: 72
SHA1: 119
SHA2: 140
Eduard Lebedyuk · Jun 9, 2016 go to post

Follow up on that.  Can I store MD5/SHA in a database as is or do I need to convert it to base64 first?

Eduard Lebedyuk · Jun 10, 2016 go to post

Both BuildValueArray and LogicalToDisplay work with serialized form of %ListOfDataTypes object - $list string:

do ##class(%ListOfDataTypes).BuildValueArray($lb("a","b","c"), .out)
zw out

Outputs:

out(1)="a"
out(2)="b"
out(3)="c"

To sum up:

  • %ListOfDataTypes - object
  • serialized %ListOfDataTypes  - $list
  • $list - a string (with special properties, but not an object)
  • %List - $list
Eduard Lebedyuk · Jun 12, 2016 go to post

Great article, I find this feature very useful too, here's my %ZLANGC00:

 ; %ZLANGC00
 ; custom commands for ObjectScript
 ; http://localhost:57772/csp/docbook/DocBook.UI.Page.cls?KEY=GSTU_customize
  Quit    

/// Execute Query and display the results
/// Call like this:
/// zsql "SELECT TOP 10 Name FROM Sample.Person"
ZSQL(Query)
    #Dim ResultSet As %SQL.StatementResult
    Set ResultSet = ##class(%SQL.Statement).%ExecDirect(, Query)
    Do ResultSet.%Display()
    Quit

/// Move user to a namespace of his choice
/// Call like this:
/// zm "s"
ZM(Namespace)
    Do MoveToNamespace^%ZSTART(Namespace)
    Quit
    
/// Move to Samples namespace and set a as a Samples.Person object
/// Set b as %ZEN.proxyObject
/// Set c as %Object
ZO(move = 1) Public
    ZN:move "SAMPLES"
    If ##class(%Dictionary.CompiledClass).%ExistsId("Sample.Person") {
        Set p = ##class(Sample.Person).%OpenId(1)
    }
    Set a = ##class(%ZEN.proxyObject).%New()
    Set b = {}
    Quit

and related %ZSTART:

%ZSTART() {
    Quit    
}

/// This entry point is invoked on user login
/// offering user to choose a namespace
LOGIN() Public {
    Set Timeout = 3
    Write "Namespace <" _ $Namespace _ ">: "  
    Read Namespace:Timeout // Get value of a Namespace variable
    Quit:Namespace=""
    Do MoveToNamespace(Namespace)
}    

/// Does actual moving to a chosen namespace
/// This is an entry point, for cases where
/// Namespace value is already aquired
MoveToNamespace(Namespace = "") Public {
    Set Timeout = 3
    #Dim List As %ListOfDataTypes
    Set List = $$GetNamespaceList(Namespace)
    Set Count = List.Count()
    
    If Count = 1 {
        Set Choice = 1
    } ElseIf Count > 1 {
        Do DisplayList(List)

        // If there is less then 10 results, then we need only 1 digit
        // Otherwise we need 2 digits
        // It is assumed that no more then 99 results would be returned
        Read "Select number <1>: ", Choice#$Select(Count < 10:1, 1:2):Timeout

        // If the user entered nothing or not a valid number
        // we select first namespace in a list to go to
        Set:((Choice = "") || ('$IsValidNum(Choice, 0, 1, Count))) Choice = 1
    } Else {
        // No namespaces found
        Quit
    }
    
    Zn List.GetAt(Choice)
}

/// Get all availible namespaces that satisfy
/// "Name %STARTSWITH Namespace" condition
/// as %ListOfDataTypes
GetNamespaceList(Namespace = "") {
    New $Namespace
    Set $Namespace = "%SYS"

    #Dim List As %ListOfDataTypes
    #Dim ResultSet As %SQL.StatementResult
    Set List = ##class(%ListOfDataTypes).%New()

    Set UserCondition = "%UPPER(Name) %STARTSWITH %UPPER(?)"  // Or [ if you wish
    Set Condition="(" _ UserCondition _ ") AND (SectionHeader='Namespaces') AND (%UPPER(Name)!='%ALL')"
    Set SQL = "SELECT Name FROM Config.Namespaces WHERE " _ Condition

    Set ResultSet = ##class(%SQL.Statement).%ExecDirect(, SQL, Namespace)
    While ResultSet.%Next() {
        Do List.Insert(ResultSet.%Get("Name"))
    }

    Quit List
}

/// Display %ListOfDataTypes in a format:
/// 1    item
/// 2    item
/// ...
DisplayList(List) {
    #Dim List As %ListOfDataTypes
    Write !
    For i = 1:1:List.Count() {
        Write i, $C(9), List.GetAt(i), !
    }
}

Github repo.

Eduard Lebedyuk · Jun 13, 2016 go to post

The  easy way to display list object:

for i=1:1:user.Roles.Count() w user.Roles.GetAt(i),!

You can also serialize list object to $list and display it:

zw user.Roles.Serialize()

Also note that lists of datatypes defined as class properties are of %Collection.ListOfDT class (which is somewhat similar but not identical to %ListOfDataTypes class).

You can also get all information on %ListOfDataTypes objects with zw command:

s b=##class(%ListOfDataTypes).%New()
do b.Insert(1)
do b.Insert(2)
do b.Insert(3)
zw b
Eduard Lebedyuk · Jun 14, 2016 go to post

Check if variable is defined beforehand and generate/use a new variable name if required:

ClassMethod GetNewVarName() As %String
{
    Set Name = "Temp"
    While $Data(@Name) {
        Set Name = Name _ $Random(100)
    }
    Return Name
}
Eduard Lebedyuk · Jun 18, 2016 go to post

HTTP debug proxies can and do work with https traffic, for example Charles. You need to install the certificate for that, but it's definitely doable.