Nikita Savchenko · Nov 26, 2017

What is the Best Way to Rename Persistent Classes which Already have Data?


Suppose I have a package with classes which I want to rename. All these classes are persistent and they already have a lot of data stored.

The best I can think of to do is the following:

  1. Duplicate class definitions of old classes, rename each of them and save
  2. Write a routine to copy (duplicate) all the data from old classes to new ones. (which is not trivial at some point)
  3. Refactor all the application's code to use new classes. (which may not be trivial at some point too)
  4. Test the things above
  5. Load new code to live environment and launch the script
  6. Hope that 5. finishes successfully. If not, revert the code and jump to 4.

Are there any better approaches than this? Maybe any utilities or better methods of renaming a class. I read this and this, but I am still not sure whether, say, instead of 2. a simple globals export/renaming/import won't corrupt the data (as the column numbers may change, etc).

Thank you for any advice!

0 884
Discussion (10)3
Log in or sign up to continue

A few recommendations:
#2) save you original Global as you do otherwise.  [Just to be Save]
#1)  In Studio Copy class has a checkbox to copy Storage Definition (=Globals) . Set it.
  In the new class add ClassParameter MANAGEDEXTENT=0 ;

/// The <var>MANAGEDEXTENT</var> parameter can be set to 0 (zero) to cause the Extent Manager
/// to ignore this class. If set to 1 then the Extent Manager will register globals used by
/// the class and detect collisions. Unmanaged extents (MANAGEDEXTENT = 0) are not checked.
/// Currently, only classes using default storage (%Library.CacheStorage) can be managed.
Parameter MANAGEDEXTENT As INTEGER [ Constraint = "0,1", Flags = ENUM ] = 1;


So the old and the new class use the same Globals.
Now go on with #3  to #6

If you miss something in refactoring the damage should be limited as you just change names not the Storage in Globals.
Assuming the change of ClassName is all you do.

Interesting about MANAGEDEXTENT. 

I'm curious, what are the typical use cases, when MANAGEDEXTENT can be helpful?

Thank you very much, Robert! Good to know about MANAGEDEXTENT, I have never heard of it.

Do you think it's okay just to copy storage definition/globals as people suggest below? (to duplicate globals data and alter storage definition to use new globals)

Thank you, Dan!
I was pretty sure to miss some cases.
SOID is probably the one nobody else would be aware of that it exists.   

Daniel, thank you for these important points.

When you  want to have two or more classes pointing to the same storage. Sometimes it can be useful to have one persistent class with full definition and another with only a few properties. I used this approach to test indices building on a test class - it does not affect existing class and it's indices.

similar in practice if you  run bookkeeping as a service.
master has full access and has the responsibility  for the content. Using Work class
clients have just read access to most fields. Using Client class.

I didn't say tax control doesn't see everything. wink Using Government class

2.  Write a routine to copy (duplicate) all the data from old classes to new ones. (which is not trivial at some point)

Why is it not trivial? If you're not changing class definition  three merges per class should be enough.

Also check that storage definition in the new class is the same as in old class, only pointing to the new globals.

MANAGEDEXTENT=0 may be easier to do, but it raises the risk of accidentally deleting old globals (what are these globals not corresponding to any class? Let's free some space)

Interesting. Never knew about MANAGEDEXTENT parameter, mentioned by @Robert.Cemper. And yes, backup your globals.

But in your case, @Nikita.Savchenko, I would go with the following. Consider you have class A and rename it to class B.

1 - is yours.

1a. When duplicating be SURE, that you take class storage definition from A, not generate it because of compile. Change places with global A  to global B in storage definition and compile class B.

It's MANDATORY to take class A storage def to B class definition and compile after that and not use generated storage definition in class B.

2. Merge data globals from class A to class B. E.g.


3-4 - yours.

5. Load new scripts and make global merges and test.

6. Is yours.

Renaming a persistent class is complicated a few things:

  • Renaming the globals used by the extent of the class. There is no requirement that you change the global names. If you do not rename the globals and the extent is of type %Library.CacheStorage and MANAGEDEXTENT is true then we will have registered the globals as being "used by" the old class. An attempt to compile the new class could produce an error in that case. This is easily avoided - refer to ##class(%ExtentMgr.Util).DeleteExtentDefinition().
  • Subextents and %%CLASSNAME, if your class is not final and subclasses do exist then instances of the subclasses will have a non-null %%CLASSNAME value containing references to old class names. This value is not only in the data global but it will also be present in index globals. Even if you do not rename the globals you will have to update these values. The simplest way to deal with the values in the index globals is to rebuild the indices of the new class.
  • The structure of the global nodes as defined by the DATA members in the STORAGE definition. If you retain the previous DATA members from the old class then you will not have to alter the global nodes and the merge command can be used.
  • If PARENT/CHILDREN relationships exist and hierarchical storage is used then the children classes will have to be refactored as well.
  • All properties whose type class is a renamed class will have to be updated. If OID format values are used then these OID values will have to be updated as well.
  • Streams are referenced by the container object and can be stored in that container as a simple ID, an OID or as a SOID (includes storage location). Any stream reference stored as an SOID (CLASSNAME=2) will have to be refactored. The default is CLASSNAME = 0. If CLASSNAME = 0 then a simple merge will work for the stream global, assuming no other classes that are not also refactored share the same stream global.