Joel Solon · Nov 5, 2021 go to post

Very strange! I was never on the standards committee. I started using MUMPS in 1987, and I remember it differently ($next being replaced by $order). Let me check the archives...

  • My 1981 copy of the Standard MUMPS Pocket Guide states that variables can have only non-negative integer subscripts, and lists $next (returning -1 if no more subscripts exist), but there's no mention of $order.
  • My 1983 copy of the Pocket Guide (based on the 1977 ANSI Standard) states that variables can use any string as a subscript and lists $order (both noted as approved extensions of the Standard; $order using "" as both the seed and the flag for no more subscripts), and $next is still listed.
  • My 1987 copy of the Pocket Guide (based on the 1984 ANSI Standard) has the same info as the 1983 Guide, except the approved extensions are now part of the Standard.
  • My 1995 copy of the Pocket Guide (based on the 1995 ANSI Standard) lists the 2 argument form of $order, but $next (having been deprecated) is not listed.

Public viewing of the Solon Archives available by appointment only ;-)

https://www.youtube.com/watch?v=rIz_xhYK2Mo

Joel Solon · Nov 10, 2021 go to post

I added Steve's suggestion, not because he asked twice, but because I owe him some money. wink 

Joel Solon · Dec 21, 2021 go to post

Nice article about an important question: when should you touch the storage definition? Some comments on the scenarios (with sound effects!). The only modifications I'd make to the bookshelf analogy is that there are gazillions of shelves (every person gets their own shelf) using the mapping of what type of book goes in each slot.

  1. Scenario 1: You are creating a class, and adding, deleting, and renaming properties. If there's any data for the class, it is test/temporary data and can be deleted at will. As Vivian saw, deleting a property doesn't change the storage definition (the slot for the deleted property is left unused), and renaming a property is really a deletion (old slot unused) followed by an addition (new slot). Once you get the class the way you want it for release, you can safely delete the storage definition (since there's no real data) and the next compilation will create a fresh one, with no unused slots. No danger (cue happy music).
  2. Scenario 2: You are editing an existing class with real stored data. If you rename any property (new or existing, with or without data) it's best practice to rename the storage definition slot also before your next compile. If it's a newly added property without data and you don't bother to rename the slot, it's OK, but you now have a wasted slot that will never be used, and a new slot for the data you'll eventually start saving. Why not rename the slot right away? On the other hand, if it's a property that has data, you must rename the storage slot, or the data for that property will no longer be accessible, and new data for the renamed property will go in a different slot. Any danger is prevented by renaming the property and storage definition slot at the same time (cue triumphant marching music).
  3. Scenario 3: You delete a property with real stored data. As before, the old slot is left unused. Any new property added later will not use that slot (no double-booking; new data won't ever be added there). But the real question is: Why are you deleting the property? Do you not care about that data anymore? If so, why aren't you deleting the data first? And by deleting the data, I don't mean editing the global directly (which I think is what Vivian meant by "don't do this."). But it's not dangerous to the table to use SQL to delete Book B from every bookshelf before deleting the property:update schema.table set property = null(cue computer bleeps).

Editing the name of any unused slot in the storage definition and adding "-unused" to make things clear could be OK but it's definitely not intended for that use. I'm not sure it's necessary to do anything in this case; If I see a slot in the storage definition named XYZ and there's no property XYZ, I know it's an unused slot. Maybe preceding the storage definition with a comment section would be cleaner:

/* unused slots
   XYZ (as of 1/1/2020)
   ABC (as of 6/1/2021)
*/

Please let me know if it's not clear, or if I missed a scenario.

Joel Solon · Dec 29, 2021 go to post

VS Code - ObjectScript doesn't prevent <SUBSCRIPT> errors. For example, the statement

set b(x) = c

can't be checked by the IDE since it doesn't know if the value of x will be "" or not at runtime.

Joel Solon · Dec 30, 2021 go to post

:alias sql do $system.SQL.Shell()

:alias err do $system.Status.DisplayError($1)

Joel Solon · Jan 3, 2022 go to post

Here's some info that was inadvertently removed from the docs, but will be returning soon:


On Unix systems, if a file named ~/.iris_init exists, it is read when a terminal session is initiated. Only lines starting with :<command> are processed. All other lines are ignored. This file accepts only :alias commands, which enables you to store favorite command abbreviations there.
Example of a ~/.iris_init file:

# Initial aliases
:alias %  zn "%SYS"
:alias load Do $System.OBJ.Load("$1",$S("$2"="":"ck",1:"$2"))
:alias ut Do ##class(%UnitTest.Manager).RunTest("$1")
Joel Solon · Jan 4, 2022 go to post

I have one older IRIS instance (unreleased v2021.1 with some Python extras) that has the following aliases built in. The :sql alias doesn't run Execute() like it seems to do on yours.

:py          Do $system.Python.Shell()
:mdx         Do $system.DeepSee.Shell()
:sql         Do $system.SQL.Shell()
:tsql        Do $system.SQL.TSQLShell()
Joel Solon · Jan 4, 2022 go to post

Ah, I misinterpreted "this alias" in your post! So you meant (I added italicized text): "I like to define :sql as a way to launch the shell and execute a statement - so that means this built-in :sql alias should be renamed by InterSystems to something else!! or be removed."

At least aliases defined in .iris_init override any built-in aliases.

Joel Solon · Mar 17, 2022 go to post

...and it appears that Mr C is also trying to sneak in a new abbreviation for ObjectScript: ISOS. indecision

Joel Solon · Mar 25, 2022 go to post

A little bit more detail. The queries in %SYSTEM.License are not defined as [SqlProc]s. Therefore, they can't be called from outside IRIS, including from Python. To get the information returned by these queries in Python, you should write your own method in ObjectScript, that runs the query with the (deprecated) %Library.ResultSet class. As you loop through the results:

Joel Solon · Jul 27, 2022 go to post

So this thread has clarified that the transaction that you read about in the docs is the transaction inside %Save(), and %OnSaveFinally() is a callback for %Save(). Your original question seems to be asking if the TCOMMIT command itself triggers a callback, but it doesn't. You could of course add your own code right after the TCOMMIT, but I guess that's not what you're looking for with this question.

Joel Solon · Aug 15, 2022 go to post

I love the Dev Community and how we all help each other learn more and more!

Joel Solon · Nov 1, 2022 go to post

@Alex Woodhead Why are you using $sortbegin on the ^D global? Just to show it's possible in this demo? Or are you recommending that general approach? It seems to me that the solution to the original question would be allowing the data to save normally (not using $sortbegin on the ^D global), and using $sortbegin on the ^I global and discarding those updates.
@Michael Fortunato Please tell us the reason for wanting to do this. It can't be that you want the index to be permanently out of sync with the data. Perhaps it's for performance for many inserts? If so, switching to a SQL approach with %NOINDEX (as @David.Satorres6134 suggested) is a good idea.

Joel Solon · Nov 1, 2022 go to post

Thanks Michael.

You could code it the oop way, and use $sortbegin on all the index globals for your multiple tables, which means you'd have to know or look up what the index global names are (since they're not always ^D and ^I anymore), and test 1000000 inserts (main table and referenced tables) with the index build deferred to the end, and time it to see how long it takes. 

And then code it the sql way, inserting the same data into the multiple tables using %NOINDEX, and calling %BuildIndices() on all the classes at the end, and time it to see how long that takes. The sql way is supposed to be faster...

Joel Solon · Dec 2, 2022 go to post

I think you get everything from secondary superclass(es) except indexes, storage, and relationships.

Joel Solon · Dec 2, 2022 go to post

Since Trigger code is called after validation and constraint checking, a trigger should not be used to change column values. David's solution is better.

Joel Solon · Dec 8, 2022 go to post

It's not a big difference between Studio and VS Code - ObjectScript here. Studio automatically does the #dim for you when you do a %New() or %OpenId(), and VS Code - ObjectScript doesn't. But #dim is still necessary in both IDEs for referenced or returned objects:

set person = ##class(Simple.Person).%New()  // Studio WILL provide code completion for person, VS Code WON'T

#dim address as Simple.Address  // without #dim, neither Studio nor VS Code will provide code completion for address

set address = person.Address 

#dim rs as %SQL.StatementResult

set rs = statement.%Execute(args)  // you'll get code completion for rs thanks to #dim
Joel Solon · Dec 16, 2022 go to post

True. I personally am not a fan of using #dim in any way other than:

#dim variable as objectclassname

because it makes it clear that #dim provides code completion for object variables, which is the functionality I care about. Yes, I know it also provides variable documentation for developers who are used to declaring datatypes in other languages

The other documented options:

  • #dim variable as objectclassname = initialvalue (ok, but I prefer #dim followed by set)
  • #dim variable = initialvalue (there's no reason to use #dim to do what set does)
  • #dim variable as non-objectclassname (this is documentation only; no code completion is provided)
  • #dim variable as list/array of objectclassname (again, documentation only; no code completion)
Joel Solon · Dec 21, 2022 go to post

If you make a loooonnnnng $piece-delimited string, and a lonnnnnnnnng $list, and you loop 1000000 times, accessing random pieces and random $list items, and sum up the time, and divide by 1000000, you'll find that for access, $lists are faster than delimited strings. At least that's what I saw when $list first appeared. But my mentor from that time said "Yes Joel, they are faster, but $list was added to ObjectScript so that we wouldn't have to worry anymore about delimiters, and sub-delimiters, and sub-sub-delimiters, etc."

Joel Solon · Feb 14, 2023 go to post

@Nicholai Mitchko's example above is Python calling an ObjectScript method that has pass-by-ref arguments, and that works as he described. I wondered if Python methods could be written to return values in their arguments, despite the fact that Python doesn't have pass-by-ref arguments. The answer is "yes," but only if the caller passes in a data structure (like a list or a dict) that the Python method modifies. Otherwise, arguments to a Python method called from either ObjectScript or Python can't return values.

Joel Solon · Apr 10, 2023 go to post

LAST_IDENTITY() returns the ID of the last record inserted, updated, or deleted. This is very useful from ODBC/JDBC. From within an ObjectScript method, you can access this directly instead of running an additional SELECT statement:

  • For Embedded SQL, you can use the %ROWID variable.
  • For Dynamic SQL, you can use the resultset.%ROWID property.
Joel Solon · May 23, 2023 go to post

Why are unit test methods instance methods? Since a running unit test is an instantiated object (%RegisteredObject), the unit test class itself can have custom properties, and the instance methods can use those properties as a way to share information. For example, initialize the properties in %OnBeforeAllTests(), and then access/change the properties in the test methods. 

Joel Solon · May 24, 2023 go to post

In the interest of accuracy, Studio does allow looking at OREFS to see their properties (View As > Object, or Dump object).

 

Joel Solon · May 24, 2023 go to post

I've also used Studio for 20+ years. I can still remember how much better it was than what we had before. We can all still use Studio if we want; it's not a forced divorce. But we hope that VS Code -- ObjectScript's features will make you comfortable enough to decide to do a conscious uncoupling. And as Frank Sinatra sang: "Love's much lovelier, the second time around." And he could have sung that at the Diplomat Hotel in Fort Lauderdale in 1974, where coincidentally InterSystems is hosting our Global Summit this year!

Joel Solon · Jun 8, 2023 go to post

Anything can be achieved without instance methods. The point here is that instance methods exist in object-oriented systems because they are considered a good, straightforward way to achieve certain things. In the case of unit tests sharing information using properties, that approach saves you from having to pass info around as method arguments, or declaring a list of public variables.

Joel Solon · Jul 31, 2023 go to post

%Library.ResultSet (aka %ResultSet) is deprecated. You should switch to using %SQL.Statement instead. It's better in every way. Doc is here: Dynamic SQL and within that there's a section on Metadata.