go to post Julius Kavay · Sep 28 Oh yes, you are able to create an instance of Class Your.Page Extends %CSP.Page { Property Name As %String [ InitialExpression = "Joe" ]; } class because the inheritance goes as follows:Your.Page <-- %CSP.Page <-- %Library.Base <-- %Library.SystemBaseand the %Library.SystemBase donates you the %New() method.
go to post Julius Kavay · Sep 28 My guess is, because abstract classes (%CSP.Page is an abstract class) can't be instantiated, your subclass lacks the generator methos for property initialisation. But there is a simple solution: Class Your.Page Extends (%RegisteredObject, %CSP.Page) { Property Name As %String [ InitialExpression = "Joe" ]; } Well, the world is right again set page=##class(Your.Page).%New() write page.Name --> Joe
go to post Julius Kavay · Aug 18 Ha ha ha 😂, that's a big mistake. Those are old functions (for even older applications, maintained for backward compatibility only) in the mean time all replaced by the $list...() functions.
go to post Julius Kavay · Aug 18 Try it this way... Class DC.OldStuff Extends %Persistent [ StorageStrategy = NewStorage1 ] { Property Rec As %Integer [ Identity ]; Property Name As %String; Property City As %String; Property Phone As %String; Storage NewStorage1 { <SQLMap name="Map1"> <Data name="City"> <RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),2)</RetrievalCode> </Data> <Data name="Name"> <RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),1)</RetrievalCode> </Data> <Data name="Phone"> <RetrievalCode>s {*}=$zel(^myGlo("R",{L2}),3)</RetrievalCode> </Data> <Global>^myGlo</Global> <Subscript name="1"> <Expression>"R"</Expression> </Subscript> <Subscript name="2"> <Expression>{Rec}</Expression> </Subscript> <Type>data</Type> </SQLMap> <StreamLocation>^DC.OldS</StreamLocation> <Type>%CacheSQLStorage</Type> } } A short test shows, it works USER>k ^myGlo USER>s ^myGlo("R",1)=$zlp("John,Boston,11-22-33") USER>s ^myGlo("R",5)=$zlp("Laura,New York,333-444-555") USER>s ^myGlo("R",7)=$zlp("Paul,Chicago,556-666-777") USER>d $system.SQL.Shell() SQL Command Line Shell ---------------------------------------------------- The command prefix is currently set to: <<nothing>>. Enter q to quit, ? for help. USER>>select * from DC.OldStuff 3. select * from DC.OldStuff ID City Name Phone Rec 1 Boston John 11-22-33 1 5 New York Laura 333-444-555 5 7 Chicago Paul 556-666-777 7 3 Rows(s) Affected statement prepare time(s)/globals/lines/disk: 0.0003s/5/159/0ms execute time(s)/globals/lines/disk: 0.0003s/13/1136/0ms cached query class: %sqlcq.USER.cls43 --------------------------------------------------------------------------- USER>>quit or as objects USER>s obj=##class(DC.OldStuff).%OpenId(7) USER>w obj.Name,!,obj.City,!,obj.Phone Paul Chicago 556-666-777 USER>
go to post Julius Kavay · Aug 14 First step: create your own method, for example Class DC.Unix [ Abstract ] { /// Convert Posix time into Timestamp /// /// posix: posix time /// df: date format /// tf: time format /// dp: decimal places ClassMethod PosixToTimeStamp(posix, df = 3, tf = 1, dp = 0) { set posix=posix-1152921504606846976/1E6 quit $zdt(posix\86400+47117_","_(posix#86400),df,tf,dp) } } Next step, if you need more speed, instead of the parameters <df> and <tf> use constants and remove <dp>. The very last step: shorten the method name from PosixToTimeStamp() to P2TS() This way I got the (nearly) the same times as with (%Library.PosixTime).LogicalToTimeStamp, but without the need for string manipulation ICINDY:USER>k ICINDY:USER>set posix = 1154669852181849976 ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t .902538 ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(%Library.PosixTime).LogicalToTimeStamp(posix) } w $zh-t .90609 ICINDY:USER> ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t .934834 ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).PosixToTimeStamp(posix) } w $zh-t .944418 ICINDY:USER> ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t .913609 ICINDY:USER>while $zh#1 {} s t=$zh f i=1:1:1E6 { s x=##class(DC.Unix).P2TS(posix) } w $zh-t .905303 ICINDY:USER> ICINDY:USER>w $zv IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST ICINDY:USER>w x 2025-05-27 12:06:15 ICINDY:USER>
go to post Julius Kavay · Jul 20 You have a status (or error code), OK, now take that code and make it more readable: set code=<put here your code> write $system.Status.GetErrorText(code) I think, it will be some kind of authentication issue...
go to post Julius Kavay · Jul 7 In SystemManagementPortal goto: SystemAdministration --> Configuration --> AdditionalSettings --> StartupSettings: JobServers
go to post Julius Kavay · Jul 7 I think, Enrico was more aiming for the point that “concatenation is the gateway for SQL injection.”
go to post Julius Kavay · Jul 3 An easy and simple (assuming standard storage strategy) way to get those informations (the slot numbers, where a property is stored) could be achieved with a simple classmethod Class DC.PropInfo [ Abstract ] { /// Info about properties of a class: /// - list of all properties stored in a list /// - slot number for a given property /// /// 1) add this class to your class definition /// class some.class extends (%Persistent, DC.StorageInfo) or /// class some.class extends (%SerialClass, DC.StorageInfo) /// /// 2) then use it as follows /// write ##class(some.class).PropInfo() --> list of property names /// write ##class(some.class).PropInfo("Age") --> slot number for the Age property /// /// write ##class( ClassMethod PropInfo(name = "") As %String [ CodeMode = objectgenerator ] { set sto=%compiledclass.Storages, prp=0, list="" if sto.Count()=1 { set dat=sto.GetAt(1).Data for i=1:1:dat.Count() if dat.GetAt(i).Structure="listnode" set prp=dat.GetAt(i) quit if prp { if %compiledclass.ClassType="serial" { set list="", i=1 } else { set list=$lb(""), i=2 } for i=i:1:prp.Values.Count() set list=list_$lb(prp.Values.GetAt(i).Value) } do %code.WriteLine(" if name="""" quit """_$lts(list)_"""") do %code.WriteLine(" quit $lf($lfs("""_$lts(list)_"""),name)") } if list="" write !,"*** No properties found! ***" quit $$$OK } } Two test classes Class DC.TestPerson Extends (%Persistent, DC.PropInfo) { Property Name As %String; Property Addr As DC.Address; Property Salary As %Numeric; Property Expertise As list Of %String; } Class DC.Address Extends (%SerialObject,DC.PropInfo) { Property Street As %String; Property City As %String; } Now you can do things like: write ##class(DC.TestPerson).PropInfo() --> ,Name,Addr,Salary,Expertise write ##class(DC.TestPerson).PropInfo("Salary") --> 4 // you can use the slot number for direct data access: write $list(^DC.TestPersonD(id),slotnumber) gives you the same value as write ##class(DC.TestPerson).SalaryGetStored(id) // the same game for serial classes write ##class(DC.Address).PropInfo() --> Street,City write ##class(DC.Address).PropInfo("City") --> 2 // in case you have an instance set pers=##class(DC.TestPerson).%OpenId(id) write pers.PropInfo() --> ,Name,Addr,Salary,Expertise write pers.Addr.PropInfo() --> Street,City // etc.
go to post Julius Kavay · Jun 30 Another reason for the <CLASS or ROUTINE DOES NOT EXISTS> message is that you're in the wrong namespace. Your installation has four namespaces by default: %SYS, DOCBOOK, SAMPLES, and USER. Another possibility could be misleading YouTube videos that use classes from newer Cache or IRIS versions without notice.
go to post Julius Kavay · Jun 8 First, measuring execution times on modern operating systems where multiple processes run in parallel (on multiple CPUs) is challenging. The following demo application assigns a value to a variable in four different ways:– in a single method– in two methods, both in the same class– in two methods where one method code is in an inherited class, and– in two methods where one method is in a different class As expected, the first is the fastest (keyword: loop unrolling) and the last is the slowest, the other two take about the same time. Class DC.Times Extends (%RegisteredObject, TimesAbstract) { ClassMethod ShowTimes() { while $zh#1 {} set t1=$zh for i=1:1:1E6 { do ..Complete() } set t1=$zh-t1 while $zh#1 {} set t2=$zh for i=1:1:1E6 { do ..OneClass() } set t2=$zh-t2 while $zh#1 {} set t3=$zh for i=1:1:1E6 { do ..InhClass() } set t3=$zh-t3 while $zh#1 {} set t4=$zh for i=1:1:1E6 { do ..TwoClass() } set t4=$zh-t4 write $j(t1,9,5), $j(t2,9,5), $j(t3,9,5), $j(t4,9,5),! } /// The complete application is carried out in one method ClassMethod Complete() { set x=12345 set y=12345 } /// The entire application is done in the same class, but with different methods /// Both methods are local (OneClass + LocTask) ClassMethod OneClass() { set x=..LocTask() set y=..LocTask() } /// The entire application is done in the same class, but with different methods /// One method is local (InhClass) the other is inherited (InhTask) ClassMethod InhClass() { set x=..InhTask() set y=..InhTask() } /// The entire application uses two methods in two different classes ClassMethod TwoClass() { set x=##class(DC.Times2).ExtTask() set y=##class(DC.Times2).ExtTask() } /// As an "application" we simply return a constant value ClassMethod LocTask(val) { quit 12345 } } Class DC.Times2 Extends %RegisteredObject { /// As an "application" we simply return a constant value ClassMethod ExtTask(val) { quit 12345 } } Class DC.TimesAbstract [ Abstract ] { /// As an "application" we simply return a constant value ClassMethod InhTask(val) { quit 12345 } } Some time values USER> USER>f i=1:1:3 d ##class(DC.Times).ShowTimes() 0.10833 0.19660 0.19649 0.22001 0.10837 0.19657 0.19608 0.22000 0.10826 0.19661 0.19603 0.21992 USER> USER>f i=1:1:3 d ##class(DC.Times).ShowTimes() 0.10998 0.19711 0.19643 0.22006 0.10830 0.19657 0.19624 0.22013 0.10822 0.19684 0.19628 0.22139 USER> USER>w $zv IRIS for UNIX (Ubuntu Server 22.04 LTS for x86-64) 2025.1 (Build 225_1U) Fri May 16 2025 12:18:04 EDT USER> Second, the choice of development method (use of include files, class inheritance, multiple classes, a large method in an even larger class, etc.) depends on other factors such as maintainability, runtime priority, etc.
go to post Julius Kavay · Jun 5 If you want to work with class parameters, you can choose between two basic variants Parameter GlobalName1 = "^Global"; Parameter GlobalName2 = {$name(^Global)}; ClassMethod Test() { set @$name(@..#GlobalName1)@("index")="abc" // using variante 1 write @..#GlobalName2@("index") // using variante 2 }
go to post Julius Kavay · May 22 If you have set the "handling of undefined" settings to 2 then in either case (you set x to a nullstring OR x is undefined) in statements like if $d(@x@(whatever)) set d=$d(@x@(whatever)) you get a <SYNTAX> error, because nullstring (the value of x) is not a valid name for a local or global variable. In a terminal session issue the following commands and check each output with mine: kill // kill all local variables write x="" // the output is 1 (because of your setting) if $d(@x) // the output is a <SYNTAX> because "" is not a valid name set d=$d(@x) // the output is a <SYNATX> because "" not a valid name If your output is different from the above, than please contact the WRCMy $ZV is IRIS for UNIX (Ubuntu Server LTS for x86-64) 2021.2 (Build 649U) Thu Jan 20 2022 08:49:51 EST
go to post Julius Kavay · May 22 I keep on nagging...I expected to see the environment of your IF-line: is it in a block structure or not. Which variables are initialized, etc. But you always give me the same expressiveness information: just the IF-Statement. Nevertheless, I think, I know, why you get the <SYNTAX> error: - under "default conditions", executing the following lines gives you an <UNDEFINED> error kill set a=1,b=2 // x is not defined if $d(@x@(a,b)) - but the very same lines gives you a <SYNTAX> error if the setting, how undefined variables should be handled is set to 1 or 2 (I suppose, in your case it is set to 2) because x as nullstring is not a valid variable or global name. To see the setting in question, go to:ManagementPortal-->SystemAdministration-->AdditionalSettings-->Compatibility and look for the setting "Undefined". I'm pretty sure, there is a 2 (or maybe 1). The default setting is 0. Hint: the above mentioned setting is a per job setting, you must start a new job to have the new setting in effect
go to post Julius Kavay · May 22 I think you are leaving out some important information: you have a terminal session, but you are calling either a routine (.mac) or a (class) method, since a standard terminal session does not accept multi-lines (as you wrote above).And when you call a routine or method, it would be nice to see that function/method. By the way, for indirection to work, you need variables with global visibility. So I'm assuming you're mixing some existing variables from your terminal session with variables from your routine/method.
go to post Julius Kavay · May 21 It's not a permission issue!A simple check tells you: write ##class(%SYS.Namespace).GetRoutineDest(,"%Test") --> ^/opt/isc/icindy/mgr/irislib/ a routine named %Test will bes stored in the "irislib" database.Now, if you take a look at the irislib database, you will see, it's a READONLY database, hence you get the <PROTECT> error. A readonly means read only, even for the Superuser! If you absolutely need to name that routine with %-something (except %Z* and %z*) than:- 1) remove the readonly flag from the database in question 2) save your routine 3) turn on the readonly flag 4) for every change in that routine repeat the abowe game: 1), 2) and 3)- and don't forget to make a copy of your %-something routine because each IRIS update REMOVES ALL %-routines except those %Z* and %z*
go to post Julius Kavay · May 14 Despite the misleading error message, the solution (with the help of WRC) is: IRIS native API requires a %Developer role
go to post Julius Kavay · May 12 Just to put things into a correct light 1) java came out mid 90esat the time ObjectScript (or mumps or m) already had it's lock a quarter centuy. That said, one could ask, why didn't implemented Java the lock the same way as M? Maybe they didn't know about mumps, maybe they targeted something else, who knows... On the other hand, imagine, all the programming lanuages have the same commands, functions, syntaxes etc. Then all the time we just had one programming lanuage. 2) as you said, lock is a 'convention'. A convention, as many other things.It's a convention too, if you take a tramway you buy a ticket - and most people do buy a ticket, but some are fare dodger. And nobody says, that's the problem of the convention. 3) using a device as a program interlock did not targeted the lock command but setting a global.If you set a global node to mark something as 'occupied' and during this time your process dies, then the global won't be cleaned up by the system - except you set a process-private-global, but that mark is invisible to external processes.