go to post Jon Willeke · Jan 9, 2017 I'm not sure I understand the objection to %VID. I've seen a few different recommendations on how best to use it, but I think it does what you want in the following example: select * from ( select top all * from Sample.Person order by DOB ) where %vid between 10 and 19 Regardless of the where or order by clause that you put in the sub-query, %VID refers to the position in the result set.
go to post Jon Willeke · Dec 5, 2016 To handle this in the general case, you would decompose the string, then strip out non-spacing marks. Unicode normalization has been requested previously, and will hopefully make it into the product at some point.
go to post Jon Willeke · Nov 2, 2016 In the following screenshot, I've just typed "prop", a unique prefix of the property template, then pressed Cmd-/ (the Content Assist shortcut for Emacs key bindings on a Mac): Since its auto insert attribute is on, I expected the template to expand immediately, rather than presenting the popup.
go to post Jon Willeke · Oct 14, 2016 Worth mentioning that O/S authentication is simpler and potentially more secure than fiddling with passwords in a script. If you do prompt for a password, consider using the -s option of the read command to hide the input.
go to post Jon Willeke · Aug 26, 2016 I don't understand what you mean when you say that your clinicians aren't database users, but it sounds like you need to give them access to the %Service_SQL service.
go to post Jon Willeke · Aug 23, 2016 $fromJSON(stream) works for me in 2016.1.0. What problem are you having?
go to post Jon Willeke · Jul 29, 2016 Greetings. Did you consult at InterSystems fifteen years ago? Long time no see. My favorite technique to keep myself and others honest is to test with random inputs. This gives rise to two challenges: generating the input, and verifying the output. The input obviously depends on the problem: string, number, list, etc. When it comes to output, I look for invariants: x*y=(y*x), sort(x)=sort(shuffle(x)), etc. I sometimes even write another version of the code under test to act as an oracle that's perhaps slower, or not as general. Caché has at least three ways to generate random numbers: $random(), $system.Encryption.GenCryptRand(), and the Basic Rnd() function. $random(n) returns a number from 0 to n-1, where n'>1E17. GenCryptRand(n) returns n bytes of cryptographically random data. You can convert it to a number using one of the $ascii() functions: $a($system.Encryption.GenCryptRand(1)) $zwa($system.Encryption.GenCryptRand(2)) $zla($system.Encryption.GenCryptRand(4)) $zqa($system.Encryption.GenCryptRand(8)) - may be negative Rnd() is interesting for a tester, because you can seed it with Randomize. If you don't use a seeded PRNG, you'll want to log your inputs somehow. It's frustrating to find a one in a billion bug, but not be able to reproduce it.
go to post Jon Willeke · Jul 29, 2016 You really need to consider it on a case by case basis. Some code may assume that $increment() returns a greater value at time t1 than at time t0; e.g.: https://community.intersystems.com/post/improve-sql-performance-date-ran...
go to post Jon Willeke · Jul 26, 2016 Christian in QD came up with a bit of a hack for this. You can use one of the DSNs that is installed automatically with the product, but override all of its attributes; e.g.: s dsn=dsn_";Database="_$namespace_";Port="_^%SYS("SSPort")_";UID="_usn_";PWD="_pwd To find an existing DSN, use the SQLDataSources query in the %GTWCatalog class. He used this technique to make the TestODBC() method in the %UnitTest.SQLRegression class more reliable. I don't know for sure that it will work with a different DBMS and driver, but it's worth a shot.
go to post Jon Willeke · Jul 26, 2016 Does this call do what you want? USER>w $zstrip(pHL7,">","|^") PID|1|12345||DOE^JOHN
go to post Jon Willeke · Jul 8, 2016 I dug up a pre-dynamic objects version of a utility method from a REST test and cleaned it up a bit (hopefully not introducing any bugs in the process): ClassMethod compareArrays(ByRef actual, ByRef expected) As %Status [ PublicList = (actual, expected) ] { ; compare root node set deix=$d(expected,eval),daix=$d(actual,aval) if deix'=daix { quit $$$ERROR($$$GeneralError,"$d(actual)="_daix_" instead of "_deix) } if deix#2,aval'=eval { quit $$$ERROR($$$GeneralError,"actual="""_aval_""" instead of """_eval_"""") } set status=$$$OK set eix="expected",aix="actual" for i=1:1 { set eix=$q(@eix),aix=$q(@aix) quit:""=eix&&(""=aix) set seix="("_$p(eix,"(",2,*),saix="("_$p(aix,"(",2,*) if seix'=saix { set status=$$$ERROR($$$GeneralError,"found """_aix_""" instead of """_eix_""" at position "_i) quit } set deix=$d(@eix,eval),daix=$d(@aix,aval) if deix'=daix { set status=$$$ERROR($$$GeneralError,"$d(aix)="_daix_" instead of "_deix_" at position "_i) quit } if deix#2,aval'=eval { set status=$$$ERROR($$$GeneralError,"actual("""_aix_"""))="""_aval_""" instead of """_eval_""" at position "_i) quit } } quit status } Comparing them, I only see two things I prefer in my version. First, in this line of your method I would use four-argument $piece with * as the fourth argument, just in case the subscript contains "first" or "second": If ($Piece(tRef1,"first",2) '= $Piece(tRef2,"second",2)) { Second, I would use a public list with first and second, rather than turning off procedure block for the entire method.
go to post Jon Willeke · Jun 23, 2016 If I thought the root might be an integer, I guess I would just check: USER>s n=27,root=n**(1/3),int=$fn(root,"",0) w $s(int**3=n:int,1:root) 3 I was also going to suggest trying logarithms, but someone already suggested that on sql.ru: USER>w 10**($zlog(27)/3) 2.99999999999999997 USER>w $zexp($zln(27)/3) 2.999999999999999998
go to post Jon Willeke · Jun 23, 2016 Unless I've misunderstood the context, it's simpler to use the ERROR macro: $$$ERROR($$$GeneralError,"DXL Testing Run Error") You don't need %occErrors.inc to get GeneralError, because of how ERROR (like ERRORCODE) is defined.
go to post Jon Willeke · Jun 23, 2016 You can use the information from %SYS.LockQuery to graph the locks with their owners and waiters. Then do a depth-first traversal of each node, looking for a cycle. Here's a sketch of building the graph: s rs=##class(%ResultSet).%New("%SYS.LockQuery:Detail") s status=rs.Execute() k graph f i=1:1 q:'rs.%Next() d . s ref="L"_i,graph(ref,rs.Owner)=1 . f j=1:1:$l(rs.WaiterPID," ") d . . s pid=$p(rs.WaiterPID," ",j) s:pid]"" graph(pid,ref)=1 The graph looks something like this: graph(3330,"L5")=1 graph(4380,"L4")=1 graph("L1",3309)=1 graph("L2",3326)=1 graph("L3",3327)=1 graph("L4",3330)=1 graph("L5",4380)=1 I've generated IDs for the locks to avoid a SUBSCRIPT error for long references. You'll want to keep a list of the original lock names. Here's a (minimally tested) traversal method that returns an error if it finds a cycle: ClassMethod dfs(byref graph, node as %String, byref visited) as %Status { s status=$$$OK i $d(node) d . i $d(visited(node)) d q . . s status=$$$ERROR($$$GeneralError,"found a cycle at node "_node) . s visited(node)=1 . s next="" . f s next=$o(graph(node,next)) q:""=next d q:$$$ISERR(status) . . s status=..dfs(.graph,next,.visited) e d . s root="" . f s root=$o(graph(root)) q:""=root d q:$$$ISERR(status) . . k visited . . s status=..dfs(.graph,root,.visited) q status } If you try it on the previous graph, it will return an error like the following: USER>s status=##class(deadlock).dfs(.graph) USER>d $system.OBJ.DisplayError(status) ERROR #5001: found a cycle at node L5
go to post Jon Willeke · Jun 20, 2016 Ugly as they are, naked references can significantly improve performance for repeated references to the same subscript level of a global.
go to post Jon Willeke · Jun 16, 2016 In general, no, you can't apply string functions directly to streams. I found one such enhancement request from eight years ago that has since been closed. (There may have been others.) For the specific case of $replace, it should be straightforward to implement Eduard's suggestion using the FindAt() method.
go to post Jon Willeke · Jun 7, 2016 If you want to wait for either a signal or for termination of a process, you can use a lock: Process A takes out an exclusive lock. Process B attempts to lock the same name. Process A either releases its lock or terminates. Process B will then get its lock. You may need an extra synchronization step between steps 1 and 2 to ensure that A gets the lock before B does. In steps 2 and 4, multiples processes can wait for a shared lock on the same name, and they will all be triggered at the same time.
go to post Jon Willeke · Jun 2, 2016 $lts and $lfs are the abbreviated forms of $listtostring and $listfromstring, respectively. $lb(1,2,3,",",5) returns a string, which seems to be what you want. I don't understand why you are then converting it to a delimited string with $listtostring.
go to post Jon Willeke · Jun 2, 2016 Even after looking at Timur and Eduard's answers, I don't understand the question. $lb(1,2,3,",",5) returns a string. Why are you trying to round trip it through a delimited string using $lts and $lfs?
go to post Jon Willeke · Apr 29, 2016 A quick test suggests that data migration is indeed required after changing the type of a property from %GlobalCharacterStream to %Stream.GlobalCharacter. Populate a class with some instances, each containing stream data. The char_length() function in SQL returns the length of the stream field. Change the type of the property and populate the class with some more instances. The char_length() function returns the length for the new instances, but null for the old ones. I imagine the supported way to migrate is to make a new stream field and copy from the old field using CopyFrom(). I'd be tempted to diddle the stream references to point to the existing data. In any case, if it ain't broke ...