Would it be possible for you to share your reason for wanting to do this with the community?
- Log in to post comments
Would it be possible for you to share your reason for wanting to do this with the community?
After reading through this thread, it seems like there are two questions:
I think it would be great if you would explain the use case to the community.
Does this example make it clear? Does this meet your needs?
USER>set human = ##class(Simple.Human).%OpenId(1)
USER>zw human
human=20@Simple.Human ; <OREF>
+----------------- general information ---------------
| oref value: 20
| class name: Simple.Human
| %%OID: $lb("1","Simple.Human")
| reference count: 2
+----------------- attribute values ------------------
| %Concurrency = 1 <Set>
| Company = "GlobaDyne Inc."
| Name = "Smith,John"
| Phone = "265-288-5681"
| Version = 2
+----------------- swizzled references ---------------
| i%Home = $lb("6489 Clinton Street","Denver","NJ",26882) <Set>
| r%Home = "" <Set>
| i%Work = $lb("9353 Main Drive","Hialeah","MI",72997) <Set>
| r%Work = "" <Set>
+-----------------------------------------------------
USER>write human.%IsModified()
0
USER>write human.PhoneIsModified()
0
USER>set human.Phone = "111-222-3333"
USER>write human.%IsModified()
1
USER>write human.PhoneIsModified()
1
USER>write human.Home.Street
6489 Clinton Street
USER>write human.Home.StreetIsModified()
0
USER>set human.Home.Street = "111 High Street"
USER>write human.Home.StreetIsModified()
1
USER>So...if Person #1 in the above example references demo.intersystems.Car #54, the "Dan P big secretly loaded" copy of that Person object also references the same Car #54, right? @Timothy Leavitt, is that good or bad for your use case? Doesn't %ConstructClone(1) give you what you need? If not, where does it fall short?
Thanks Mihoko!
Also note: the argument with ... after its name doesn't have to be the only argument to the method; it must be the last argument to the method. So NewMethod1()'s signature could be:
ClassMethod NewMethod1(a as %String, b as %String, c... as %String)
{
kill ^a, ^b, ^c
set ^a = a, ^b = b
merge ^c = c
}Running it like this:
USER>DO ##class(TEST.ARGTEST1).NewMethod1(1,2,3,4,5,6,7) USER>ZWRITE ^a, ^b, ^c ^a=1 ^b=2 ^c=5 ^c(1)=3 ^c(2)=4 ^c(3)=5 ^c(4)=6 ^c(5)=7
The three techniques for using SQL in your code (Class queries, Dynamic SQL, Embedded SQL) each have their pros and cons. The %ObjectSelectMode and tResult.<column> (instead of tResult.%Get("<column>") ) features only work with Dynamic SQL.
I think it's really important that you find out why the tool is written to start a transaction before every query.
It is ideal to stop IRIS before stopping the OS. However, if you stop the OS while IRIS is still running, IRIS can handle it. Check the IRIS messages.log after you shut down the OS, and you'll see lines like these:
10/16/23-18:11:04.649 (5940) 1 [Generic.Event] Operating System shutdown! InterSystems IRIS performing fast shutdown. 10/16/23-18:11:06.001 (5940) 1 [Generic.Event] Fast shutdown complete
If your plan is to stop the OS and change the machine name, then definitely shut down IRIS in advance of stopping the OS.
I'm not exactly sure what fast shutdown is so I don't know the answer. I'm also not sure if "iris force" is the same as fast shutdown. I suppose they could be similar. When you do "iris force" IRIS runs an irisstat to capture the pre-force state and writes it to messages.log; fast shutdown doesn't do that.
Edit: I also learned that fast shutdown waits for our system daemons to quiesce whereas iris force doesn't wait. @Eduard Lebedyuk: despite that behavior, maybe iris force takes longer because it runs irisstat.
Very interesting question! In case anyone is wondering, in Studio you can access this info in the Workspace View on the Namespace tab or the Project tab. Right-click on the package name, and click Package Information. The information entered here is stored in the ^oddPKG global, subscripted by package name.
There doesn't appear to be a way to access this information in VS Code. I think it's worth creating an issue for this here (VS Code ObjectScript issues) and see what happens.
I edited this post and made some corrections below.
I think the Community needs more information in order to help you. Data for %Persistent classes is stored in globals. Data for %SerialObject classes is also stored in globals. Properties of %Persistent classes can reference %SerialObject classes, and when the %Persistent data is saved, the referenced %SerialObject data is also stored in globals.
The decision about whether to create %Persistent or %SerialObject classes for storing data is an object modeling decision, unrelated to the need to purge data on some schedule. Changing classes from %Persistent to %SerialObject (or vice versa) for an existing application is a major design change which would probably require plenty of code rewrites. I don't think anyone would do that work just for the sake of purging. There must be something else going on.
Semi-educated guess: Maybe when you say "purging" you're talking about purging messages in interoperability productions. These messages have message headers, message bodies, and the bodies may have properties that reference %Persistent or %SerialObject objects. When you purge those messages, you have the option to purge message bodies, but if the bodies reference %Persistent objects, the referenced objects are not purged. Maybe that's what you're seeing, and maybe that's why you're thinking of switching storage definitions. There is certainly a way to make sure that any referenced objects get purged when messages are purged, documented (briefly) here. The WRC can also offer assistance.
Please tell us if my semi-educated guess is correct, or if not, please give us more information. Happy New Year!
Some more thoughts on this:
A few thoughts on this lovelypost:
Oh, and one more little tidbit about the 2nd argument to %ExecDirect(), the one containing the SQL text. Instead of being a single long string, it can be an array of strings, passed by reference with a leading dot (not variadic). So the first few lines of GetTransactions() could look like the example below. This approach has the nice benefit of adding spaces between each line; notice I removed the extra spaces at the end of each line of SQL.
set sql = 1set sql(1) = "select Product->Name, Outlet->City, AmountOfSale, UnitsSold "_
"from HoleFoods.SalesTransaction where Actual = 1"if (product '= "") {
set sql($increment(sql)) = "and Product = ?"set args($increment(args)) = product
}
if (channel '= "") {
set sql($increment(sql)) = "and Channel %INLIST ?"set args($increment(args)) = channel
// ...and so on...and eventually...set result = ##class(%SQL.Statement).%ExecDirect(, .sql, args...)It's a leading dot and trailing dots festival.
Steve, I've conveyed your message about Class Queries since the first time you mentioned it to me, oh so long ago. However, if you wanted to use Class Queries for Tim's example, you'd have to write 17 different queries, and call the right one based on the input you got. Every feature has its uses.
Very nice. Please remove "not" from "...its associated searches does not open up a world of opportunities..."
I never encountered this issue. I assume it's because AutoSave is off by default, and Steve and others that have commented turned it on. I noticed that there IS a setting called "Auto Save When No Errors."
$increment is (or used to be) unique in that it was the only function that changed its argument. It reminded me of learning about "destructive functions" in LISP a long time ago. No matter where/how you use it, it increments. So "set a = $increment(a)" is redundant, but still OK to use. As you saw, developers started to use "if $increment(a)" alone, because that was shorter but also works fine, even thought it looks strange. I wasn't aware of "do $increment(a)" being allowed, which does look nicer, so I also learned something new from this thread, thanks.
And let's not forget to mention the popular and useful "set a($increment(a)) = something"
Common Table Expressions are now supported starting with v2024.1: https://docs.intersystems.com/irisforhealth20241/csp/docbook/DocBook.UI…
I didn't even think to check if other %variables were handled automatically. I will add the request for %sqlcontext right away.
I have a comment on the sentence "I don't want to use Xecute because I am in need of really high performance code."
I have always thought of Xecute and @ (indirection) as similar in performance characteristics. But I don't know what the current reality is. Is it possible that omer was told that Xecute is slow and indirection wasn't mentioned?
Here are some quotes from the docs. I don't see anything here that indicates Xecute is slower or faster than indirection. Maybe someone else on this thread has more detailed knowledge on the difference if any.
Thanks Robert! And there's another one!
As others in this thread have said, custom audit events from applications are added to the Audit Log using a call to $system.Security.Audit(). The events must be defined ahead of time in the Portal, using Configure User Events. All events (system and custom) have a 3-part name: Source, Type, and Name. I think of these parts as Main Category, Subcategory, and Name.
The person(s) defining the custom events can choose all 3 parts of the name, in order to categorize all the custom events they're defining. So you could use Source = ABC, Type = DEF, and Name = XYZ. The names are totally up to you. Once defined, the event gets added by some code calling $system.Security.Audit("ABC", "DEF", "XYZ").
Thanks for posting this Joel! I've been wondering about this for a while too! 🤣
Thanks for the thoughts! To clarify, I was primarily asking about where to install IRIS itself. Within the context of that question, it's always good to include information about directories for the other important stuff, such as Journals and databases, so thanks for that.
I think the other commenters clarified it all, but I thought I'd add a little more.
Try this variant, with the variable x intentionally undefined.
write (1=0) && (x = 0)
0
Since 1=0 is 0, you don't get an <UNDEFINED> for x, since the right-hand (x=0) expression is ignored, and the entire statement is 0. Now try this:
write 1=0 && x=0
1
which, as @Roger Merchberger showed, is really
write (((1=0) && x) = 0)
Since 1=0 is 0, you again don't get an <UNDEFINED> for x, since the x expression (only) is ignored, and the ((1=0) && x) expression is 0. Finally, 0=0 is 1.
As @Yaron Munz and @Alexander Koblov correctly pointed out, you can use Embedded SQL or Dynamic SQL. You can also use Class Queries.
Using Embedded SQL, host variables in IRIS (:varname) can be inputs or outputs, just like host variables in SQL Server and Oracle, but without any DECLARE statement. Host variables used as inputs are automatically sanitized. Examples:
WHERE amount > :var1 INSERT INTO ... VALUES (:var1, :var2, :var3) SELECT ... INTO :var1, :var2 FROM ...
Dynamic SQL allows ? placeholders for inputs only instead of host variables, also automatically sanitized. Examples:
WHERE amount > ? INSERT INTO ... VALUES (?, ?, ?)
Dynamic SQL returns output values in the result set object it returns, which you can access and copy into variables. Examples:
set resultSet = ##class(%SQL.Statement).%ExecDirect(, sql, values for any placeholders)
while resultSet.%Next() {
set var1 = resultSet.column1
set var2= resultSet.column2
}Class queries use host variables (:varname) for inputs (like Embedded SQL), and return output values in the result set object (like Dynamic SQL).
...and it makes sense that using $listbuild to add at the end would be better than the other two options, which are general purpose ("find the position in the list and put something there").