go to post Alexey Maslov · Aug 2, 2018 Our case differed from yours as we had full access to Caché DB and did all this development on the Caché side.If you need to implement triggers, etc, you need full access to Caché DB.Otherwise, if time_stamp_of_modification field presents in Caché table, the table can be queried with SQL SELECT statement based on the time of previous query on regular basis (e.g. each 5 seconds). Anyway, I have no idea how to achieve your goal without some development on the Caché side.
go to post Alexey Maslov · Aug 2, 2018 Hi Mark,Several years ago we faced the similar problem. External system needed to pull new requests from our Caché DB and to push back the responses. We maintained a transition table in Caché where we placed new requests, external system polled the table each N seconds fetching the requests from the table and placing the responses back. Communication was implemented via ODBC.You can do something like this filling transition table on remote Caché side using triggers associated with the "main" table.
go to post Alexey Maslov · Jul 13, 2018 If you prefer to code global size calculation by yourself rather than amend ^%GSIZE, the feasible option is to call set bSize=$$AllocatedSize^%GSIZE(global)which returns size in bytes for a global mapped to the current namespace. It recognizes the database the global is mapped from, so you don't need to do it yourself. The only thing you need is a global list for the namespace, which can be fetched in several ways, e.g. using $Order(^$G(global)). It can be used on per database basis as well. Pros of this approach:- speed, as it neither runs query nor instantiates %SYS.GlobalQuery objects;- AFAIR, there was an error in global size calculation with %SYS.GlobalQuery::Size() query in old Caché versions, up to 2015.1;- starting from 2015.1, it can be used with subglobals.Cons:- this $$-function is not documented;- not sure if it existed in 2010.1.
go to post Alexey Maslov · Jul 5, 2018 Agree, it's trivial in most cases, except that one, when there is a series of commands depending on previous ones, e.g. (not from the production code))):set rc=$zf(-1,"[ -f /etc/environment ] && . /etc/environment && export TZ")$zf(-1) allows to execute such series at the whole, while $zf(-100) needs to split it into parts, moving checkup logic to COS code. It's trivial as well, but ruins the idea of (semi-)formal substitution of $zf(-1) calls with $zf(-100) ones.
go to post Alexey Maslov · Jul 5, 2018 It seems that the docs is ambiguous here as it's not clear when one can use "" as a <null> value: in comma separated options list only, or in options array as well.As to possible ways to exploit $zf(-1), there is some clue in ISC's announcement. It can be compromised if its arguments come from user input. Similar vulnerabilities are usually associated with dynamic SQL, not only in Caché. Other (Caché specific) samples: Xecute, $Xecute, argument indirection. This stuff is well-known, is it a secret for anybody?It seems that if we never use such coding style, we are safe enough. As to our company's code base, we rarely use $zf(-1), and all its usage is encapsulated in a couple of class methods.We'll follow ISC's security recommendations, as we always do, while I don't feel myself comfortable when I don't understand the reasons of doing something. "Don't repair, if it works", as it was said by some wise man. Does it need any comment?
go to post Alexey Maslov · Jul 5, 2018 Using a comma-delimited list of arguments works fine, even with a null argNull arg is not the same as an empty string arg (""), as usual in COS. Therefore the settings ofset options(1)=""made your first argument an empty string, and the whole command behaved asdir "" e:\nbupg\webserver\Didn't check your *nix version, just noticed that "NUL" should be spelled as "/dev/null".P.S. May I ask you in turn :):Why did you undertake this task, changing of $zf(-1) to $zf(-100), at all? Do you clearly understand the kind of treat you try to eliminate?
go to post Alexey Maslov · Jun 18, 2018 do %SYSTEM.Process:Terminate() I'm just curious: since which version this exotic form of call is supported? In Caché 2015.1...2017.2 only traditional forms are possible, e.g. do $SYSTEM.Process.Terminate(pid)
go to post Alexey Maslov · Jun 16, 2018 The solution I've found is rather simple than smart: to start a dejournalizer as a JOB with an 'answer file' specified as a principal-input device. The code prototype looks like this: set fin=$zu(12)_"Temp\fin.txt" set fout=$zu(12)_"Temp\fout.txt" open fin:("NW"):1 if '$t {write "not opened!",! quit} use fin write "N",!,"N",!,"Y",! close fin job jrnrest^ztestFF("20180614.006"):(::fin:fout):1 if '$t { w "not started!" q} Its execution resulted in "fout.txt" file like this: 20180614.006 to 20180614.006; c:\intersystems\cache\mgr\user\ => c:\intersystems\cache\mgr\test\ Do you want to rename your journal filter? o Do you want to delete your journal filter? o c:\intersystems\cache\mgr\journal\20180614.006 8.88% 14.29% 15.26% 16.79% 18.08% 19.31% 20.35% 21.32% .... ***Journal file finished at 16:39:36 Do you want to rename your journal filter? es Journal filter ZJRNFILT renamed to XJRNFILT [journal operation completed] "o" and "es" were provided by the auto-completion of "N" and "Y" answers. Cons of this approach is that ISC can change the ^JRNRESTO dialog in some future version.Pros: no need in reverse engineering of ^JRNREST stuff to derive a non-interactive journal restore utility. Hope my writing will be useful for somebody besides myself. Happy coding!
go to post Alexey Maslov · Jun 7, 2018 Thank you, Ed.So it seems that I can ignore records of this type as processing of KILL record of the top node would be enough.
go to post Alexey Maslov · Jun 7, 2018 ...2017 has issues with ODBCHi Kurt,could you drop a few words on the subject: what kind of issues it has?(Just started moving the clients to 2017.2.1...)
go to post Alexey Maslov · May 31, 2018 WRC answered that:1) The reason of the error with "CSP.StudioTemplateMgr_Templates" is because the "CSP.StudioTemplateMgr" class is owned (has Owner = "...") by %Developer. So a user with %Developer had no issue executing this stored procedure "Templates". Prior to 2016.2 the class did not have the Owner specified.and2) This change was intentionally added for security reasons when Atelier support was added to Cache'.(1) reassured me that I had taken the right way to solve the issue.I didn't understand (2), but... There are more things in heaven and earth, Horatio, Than are dreamt of in your philosophy.
go to post Alexey Maslov · May 28, 2018 As a remedy, you can process compilation in a separated job, something like this:- Create environment { }- JOB ImportAndCompileStuff- Wait for the job completion- Destroy environment { }
go to post Alexey Maslov · May 8, 2018 In this very case Update.Import was the single class being compiled, so no parallel compiling was involved.But its old version was running by the "parent" process, which jobbed the "child" that tried to compile a new one.AFAIK, it's possible to compile a class which old version is running by another process, isn't it?
go to post Alexey Maslov · May 8, 2018 The developer insists that the attempt to compile the class was before its first call. Here is a fragment of his internal log. Alas, no idea how to reproduce the error as so called Updater is not a new project and runs without any problem at many production sites.***[06.05.2018 15:54:11.897] Starting importing classes[06.05.2018 15:54:11.897] &runUpdateCommon.int,Update.ImportError while importing classes: ERROR #5123: Could not find an entry point 'zguiUpdateFileAction' in routine 'Update.Import.1' > ERROR #5030: Error compiling class Update.ImportERROR #5123: Could not find an entry point 'zguiUpdateFileAction' in routine 'Update.Import.1' > ERROR #5030: Error compiling class Update.Import[06.05.2018 15:54:12.382] Classes sucessfully imported[06.05.2018 15:54:12.382] Starting importing globals[06.05.2018 15:54:12.382] There is no globalList[06.05.2018 15:54:12.382] Starting processing afterAction[06.05.2018 15:54:12.382] d ##class(Update.Import).update(0)Error while processing afterAction: <METHOD DOES NOT EXIST>processAfterAction+30^updaterV201712261
go to post Alexey Maslov · May 8, 2018 Thank you, Dima.Could I reproduce, I'd call WRC.Ther were some errors stipulated by the fact that the class was not compiled. Posting them in another comment.
go to post Alexey Maslov · May 8, 2018 Thank you for taking part, Robert.BTW. oops is really new styleNot so new: I met them ~ 5 years ago during bulk classes recompilation task that was scheduled in our development environment. It was one of many temporary tasks, so I don't remember the details.
go to post Alexey Maslov · May 8, 2018 Thank you, Pete, I will pass your answer to the class developer who hesitated to write here by himself.
go to post Alexey Maslov · May 7, 2018 Hi Joan,It seems that the right way to perform this task would be using MemberStatusList query of the class SYS.Mirror. See Documatic for details.HTH.
go to post Alexey Maslov · May 4, 2018 No, I didn't as I solved it by myself.It can be easily reproduced, BTW.