Jon Willeke · Jun 14, 2019 go to post

Interesting question. I think the idea is that the output is not just a number, but the second field of a $horolog/$ztimestamp string, where the convention is to include the leading zero if necessary.

If you repeat the experiment with $zdth, or you catch the value of $zts just after midnight UTC, you should see a zero after the comma.

Jon Willeke · May 9, 2019 go to post

The %SYSTEM.SQL::Execute() method returns an %SQL.StatementResult object. I'm not that familiar with the iris.node module, but it appears that invoke_classmethod() does not return an object that you can pass to invoke_method() or get_property(). However, it seems that you can fake it by copying the "result" field into a new object as "oref":

> result = data.invoke_classmethod({class: '%SYSTEM.SQL', method: 'Execute', arguments: ['select 1']})
{ ok: 1,
  class: '%SYSTEM.SQL',
  method: 'Execute',
  arguments: [ 'select 1' ],
  result: '12' }
> data.get_property(result, '%SQLCODE')
Error: No object reference provided
> o = {oref: result.result}
{ oref: '12' }
> data.get_property(o, '%SQLCODE')
{ ok: 1, oref: 12, property: '%SQLCODE', value: '0' }
> data.invoke_method(o, '%GetData', 1)
{ ok: 1, oref: 12, method: '%GetData', result: '1' }
Jon Willeke · May 8, 2019 go to post

The main thing I can suggest is to verify that the client and server agree on the encoding. It's been years since I've used Reflection; hopefully it supports UTF-8. Check your settings and/or documentation.

When you're in a Caché session, look at the value of the $zmode special variable:

USER>w $zm
RY\Latin1\K\UTF8\

I'm not sure offhand what determines the default I/O translation table for a terminal, but if you see "RAW" instead of "UTF8", you can set it manually:

USER>u 0:(/IOT="UTF8")

USER>w $zm
RY\Latin1\K\UTF8\
Jon Willeke · Apr 19, 2019 go to post

$toJSON() escaped forward slash unnecessarily in 2016.1. It's valid JSON, but unconventional. %ToJSON() doesn't do this in 2016.2 and later. Depending on your data, you may be able to get away with $replace(json,"/","/"). To handle all cases would be a bit more work.

Jon Willeke · Apr 12, 2019 go to post

I thought there was a method to do just this in the TestInstance class, but it's actually in %UnitTest.Portal package:

USER>w ##class(%UnitTest.Portal.standardPage).GetTestStatus(^UnitTest.Result)
0

Up to you if you want to depend on an implementation detail of the portal. It is a public method that's been there since 2012.

Jon Willeke · Apr 9, 2019 go to post

So the input is Windows-1252, and the output is Windows-1252 in which certain characters are mapped to their numerical escape sequence? You could do this with XSLT 2.0 using character maps.

Given this input (presented here as UTF-8 for visibility on the forum):

<?xml version="1.0"?>
<Recordset>
• coffee €5,• tea €4
</Recordset>

This stylesheet will escape the bullets and euro signs:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:character-map name="a">
    <xsl:output-character character="€" string="&amp;#128;"/>
    <xsl:output-character character="•" string="&amp;#149;"/>
  </xsl:character-map>
  <xsl:output encoding="Windows-1252" indent="yes" use-character-maps="a"/>
  <xsl:template match="/">
    <Recordset>
      <xsl:value-of select="/Recordset"/>
    </Recordset>
  </xsl:template>
</xsl:stylesheet>

Output:

<?xml version="1.0" encoding="Windows-1252"?>
<Recordset>
&#149; coffee &#128;5,&#149; tea &#128;4
</Recordset>
Jon Willeke · Apr 9, 2019 go to post

My hunch is that XSLT is not the right tool for the job, but it's not clear to me what you're trying to do. What is the input encoding, what is the desired output encoding, and what do you mean by non-standard characters? Are these characters that don't exist in the output encoding?

Jon Willeke · Apr 1, 2019 go to post

The first $zf(-100) call doesn't work, because you're trying to redirect with the /STDOUT flag and the ">>" operator. You can do one or the other, but not both.

If you add the /LOGCMD flag to the second $zf(-100) call, you should see something like the following in messages.log:

    $ZF(-100) cmd=type "" file1.txt

I suggest that you not put an empty string in your options array.

Jon Willeke · Feb 14, 2019 go to post

I'm not that familiar with adapters, but the documentation suggests that you need to use the SkipBodyAttrs property to send a header like Content-Type. Also, I think you need to decide whether you're sending form data or a body. When tFormVar is "Content-Type,apikey", the documentation says that your third data argument will be assigned to the last form variable, apikey, which is almost certainly not what you want. Your second try with three variables looks more likely to work, depending on what the service expects in the body.

I don't know anything about the duplicate apikey. That's presumably specific to the service you're calling.

Jon Willeke · Feb 6, 2019 go to post

Take heart, I think you're getting close. I think all you need now is to quote the arguments to the --before switch, as shown in the documentation:

    --before "/usr/irissys/dev/Cloud/ICM/changePassword.sh /IRIS/pwd.txt"

If you're still having trouble, build up the command incrementally, as I previously suggested, being careful to follow the correct form of the command.

Dmitry's answer about looking at log files will also give you more information than simply noticing in docker ps that the container has exited.

Jon Willeke · Feb 5, 2019 go to post

I'm finding it hard to read the output images, but every one of the commands that you listed has a Docker option after the image name. The form of the command should be:

    docker run <Docker opts> image_name <IRIS args>

To be clear, the -e (--env), -p (--publish), and -v (--volume) switches are Docker options; they go before the image name. The --key and --before switches are IRIS arguments; they go after the image name.

Jon Willeke · Feb 1, 2019 go to post

You're sort of back to where you were before, with Docker switches occurring after the image name, although you now have the image name in there twice. I don't have your environment, so I can't test this exact command, but I think you want something like this:

    docker run -d --privileged -v /nfs/IRIS:/IRIS \
    --env ISC_DATA_DIRECTORY=/IRIS/iconfig \
    --env ICM_SENTINEL_DIR=/license \
    -p 52774:52774 --name IRIS4 efca5c59cbb7 \
    --key /IRIS/iris.key ...

If you're still having trouble, back up and build the command line incrementally. Start simple:

    docker run -d --name IRIS4 efca5c59cbb7

Then add in your Docker switches (-v, -e, -p, etc.), and finally add in the IRIS arguments (--key, etc.). That way you can tell which switch or argument is causing a problem.

Jon Willeke · Feb 1, 2019 go to post

It looks like you're trying to run an image identified as efca5c59cbb7, but in this segment of the command line, iris is taken as the image name:

    --env ICM_SENTINEL_DIR=/license iris
Jon Willeke · Jan 31, 2019 go to post

The --volume and --env switches are handled by the docker command. They should come before the image name.

Jon Willeke · Jan 18, 2019 go to post

Even if you store the current password encrypted, consider storing just the hashes for old passwords. You might use $system.Encryption.PBKDF2() for this purpose, perhaps with fewer iterations than you'd otherwise use for a live password.

Jon Willeke · Dec 4, 2018 go to post

I don't think that your test method is being run. I'm pretty sure that it has to start with "Test" (with a capital "T") for the manager to discover it.

Jon Willeke · Nov 19, 2018 go to post

You've set the TranslateTable for both stream and tNewStream to "cp1252". If the input file is UTF-8, then stream.TranslateTable should be "UTF8". Otherwise, each UTF-8 code unit (i.e., byte) is read in separately as a CP1252 character.

Jon Willeke · Oct 29, 2018 go to post

Apart from just redirecting to a file, and then reading from that file, the closest you can get is to use a command pipe device:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GIOD_interproccomm#GIOD_ipc_pipes

The main drawback, relative to $zf(-1) or $zf(-100), has been that you couldn't get the exit status of the command. I think that is now possible, but I'm not sure offhand in what versions.

Note that command pipes are not supported in Cache for VMS.

Jon Willeke · Oct 24, 2018 go to post

%DynamicAbstractObject is not intended to be subclassed by end users. It serves as a base for %DynamicArray and %DynamicObject, which provide JSON-style dynamic objects without a fixed schema.

You seem to be looking for a way to map between JSON and plain old registered objects. We haven't yet released such a feature, but you can do something kind of similar using DocDB, which I think first shipped in IRIS 2018.1:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GDOCDB

This feature helps to define a class that stores a JSON document in a persistent object and extract fields from the document into properties of the object.

Jon Willeke · Sep 26, 2018 go to post

I second the recommendation to comb through the upgrade checklist archive. A couple of big ones are that 2014.1 dropped support for database extents, and for 2 KB databases.

Jon Willeke · Sep 10, 2018 go to post

Your code creates a property when getting the property returns a 404. However, I'm getting a 400 when a property doesn't exist:

$ curl --user _system:SYS -w "\n***\n%{http_code}\n" 'http://127.0.0.1:52773/api/docdb/v1/DEMO/prop/DEMO.TEST11/Ergebniszuf'
{"status":{"errors":[{"error":"ERROR #25541: DocDB Property 'Ergebniszuf' does not exist in 'Demo.TEST11'","code":25541,"domain":"%ObjectErrors","id":"DocumentDatabasePropertyNotValid","params":["Demo.TEST11","Ergebniszuf"]}],"summary":"ERROR #25541: DocDB Property 'Ergebniszuf' does not exist in 'Demo.TEST11'"},"content":null}
***
400
Jon Willeke · Aug 22, 2018 go to post

In some cases I prefer the multiple set form for readability and maintainability, as it makes explicit that all of the variables should get the same value. With separate set commands, you could change one without changing the other.

It is known/expected that multiple set is slower than separate set commands, although you shouldn't see as big of a difference when the target is a global or a subscripted local.

You could also abuse set $listbuild for a task like this, although it's probably even slower:

s $lb(v1,v2,v3)=$lb("v1","v1","v2")

For benchmarking, you may want to use a high-precision timer like $zh, rather than $h.

Jon Willeke · Aug 16, 2018 go to post

The %UnitTest package is designed such that each suite stands alone: it is loaded, compiled, run, and deleted, independent of any other suite. Deleting the test classes is just part of cleaning up.

You can pass the /nodelete qualifier, or even set it as a default using $system.OBJ.SetQualifiers(). Given that deletion is the default behavior, however, I suggest that you adjust your workflow accordingly. I edit XML export files directly. Some people maintain a development and test namespace, with the classes being exported from development, and imported into test.

Jon Willeke · Aug 9, 2018 go to post

I'm not sure I'm reading this correctly, but I believe the key difference in the cold runs is 10,399 vs 5,853, again suggesting that ^ListData went to disk more often.

I'm surprised that it makes such a big difference, but I suspect what's happening here is that your copy of ^ListData into ^StringData resulted in a more space-efficient organization. You might want to look at the packing column of a detailed report from the %GSIZE utility.

It's possible that something about your data causes $list to store it less efficiently, but your data hasn't convinced me of that. If you copied ^ListData unchanged into, say, ^ListData2, my guess is that you would see a similar improvement.

Jon Willeke · Aug 8, 2018 go to post

Compared to a delimited string, lists have the overhead of storing the length of each element, typically one extra byte. Numbers and Unicode characters are also stored differently, sometimes more efficiently, sometimes less. Otherwise, there is no difference between fetching a delimited string or a list.

The DataBlkRd and DataBlkBuf columns shows that ^StringGlobal was read entirely from global buffers, whereas ^ListGlobal had to read over 9,000 blocks. In each case, it seems that the global occupies about 17,000 blocks; about 136 MB, assuming 8 KB blocks.

I suggest that you do the following:

  • configure 256 MB or more of global buffers
  • restart the instance
  • run one of the tests twice
  • restart the instance
  • run the other test twice

Based on your numbers, the first runs will be cold, and should take a minute or two. The second runs should be essentially instantaneous.

Jon Willeke · Jul 23, 2018 go to post

When you call %ToJSON() in a DO context, it writes to the current device, which should not cause a MAXSTRING error. However, due to an interaction with I/O redirection, you should preface the call with WRITE "":

write ""
do specimenArray.%ToJSON()

This may be fixed in recent versions.