Skip to main content

Extents

Extents

The term extent refers to all the records, on disk, for a given persistent class. As shown in the next chapter, the %PersistentOpens in a new tab class provides several methods that operate on the extent of class.

Caché uses an unconventional and powerful interpretation of the object-table mapping. By default, the extent of a given persistent class includes the extents of any subclasses. Therefore:

  • If the persistent class Person has the subclass Employee, the Person extent includes all instances of Person and all instances of Employee.

  • For any given instance of class Employee, that instance is included in the Person extent and in the Employee extent.

Indices automatically span the entire extent of the class in which they are defined. The indices defined in Person contain both Person instances and Employee instances. Indices defined in the Employee extent contain only Employee instances.

The subclass can define additional properties not defined in its superclass. These are available in the extent of the subclass, but not in the extent of the superclass. For example, the Employee extent might include the Department field, which is not included in the Person extent.

The preceding points mean that it is comparatively easy in Caché to write a query that retrieves all records of the same type. For example, if you want to count people of all types, you can run a query against the Person table. If you want to count only employees, run the same query against the Employee table. In contrast, with other object databases, to count people of all types, it would be necessary to write a more complex query that combined the tables, and it would be necessary to update this query whenever another subclass was added.

Similarly, the methods that use the ID all behave polymorphically. That is, they can operate on different types of objects depending on the ID value it is passed.

For example, the extent for Sample.PersonOpens in a new tab objects includes instances of Sample.PersonOpens in a new tab as well as instances of Sample.EmployeeOpens in a new tab. When you call %OpenId() for the Sample.PersonOpens in a new tab class, the resulting OREF could be an instance of either Sample.PersonOpens in a new tab or Sample.EmployeeOpens in a new tab, depending on what is stored within the database:

 // Open person "10"
 Set obj = ##class(Sample.Person).%OpenId(10)

 Write $ClassName(obj),!    // Sample.Person
 

 // Open person "110"
 Set obj = ##class(Sample.Person).%OpenId(110)

 Write $ClassName(obj),!    // Sample.Employee

Note that the %OpenId() method for the Sample.EmployeeOpens in a new tab class will not return an object if we try to open ID 10, because the ID 10 is not the Sample.EmployeeOpens in a new tab extent:

 // Open employee "10"
 Set obj = ##class(Sample.Employee).%OpenId(10)

 Write $IsObject(obj),!  // 0

 // Open employee "110"
 Set obj = ##class(Sample.Employee).%OpenId(110)

 Write $IsObject(obj),!  // 1

Extent Management

For classes that use the default storage class (%Library.CacheStorageOpens in a new tab), Caché maintains extent definitions and globals that those extents have registered for use with its Extent Manager. The interface to the Extent Manager is through the %ExtentMgr.UtilOpens in a new tab class. This registration process occurs during class compilation. If there are any errors or name conflicts, these cause the compile to fail. For compilation to succeed, resolve the conflicts; this usually involves either changing the name of the index or adding explicit storage locations for the data.

The MANAGEDEXTENT class parameter has a default value of 1; this value causes global name registration and a conflicting use check. A value of 0 specifies that there is neither registration nor conflict checking.

Note:

If an application has multiple classes intentionally sharing a global reference, specify that MANAGEDEXTENT equals 0 for all the relevant classes, if they use default storage. Otherwise, recompilation will generate the error such as

ERROR #5564: Storage reference: '^This.App.Global used in 'User.ClassA.cls' 
is already registered for use by 'User.ClassB.cls'

To delete extent metadata, there are multiple approaches:

  • Use the ##class(%ExtentMgr.Util).DeleteExtentDefinition(extent,extenttype) call, where extent is typically the class name and extenttype is the type of extent (for classes, this is cls, which is also the default value for this argument).

  • Use one of the following calls:

    • $SYSTEM.OBJ.Delete(classname,flags) where classname is the class to delete and flags includes e.

    • $SYSTEM.OBJ.DeletePackage(packagename,flags) where packagename is the class to delete and flags includes e.

    • $SYSTEM.OBJ.DeleteAll(flags) where flags includes e.

    These calls are methods of the %SYSTEM.OBJOpens in a new tab class.

Extent Queries

Every persistent class automatically includes a class query called "Extent" that provides a set of all the IDs in the extent.

For general information on using class queries, see the chapter “Defining and Using Class Queries.” The following example uses a class query to display all the IDs for the Sample.PersonOpens in a new tab class:

 set query = ##class(%SQL.Statement).%New()
 set status= query.%PrepareClassQuery("Sample.Person","Extent")
 if 'status {
   do $system.OBJ.DisplayError(status)
 }
 set rset=query.%Execute()

 While (rset.%Next()) {
     Write rset.%Get("ID"),!
 }

The Sample.PersonOpens in a new tab extent include all instances of Sample.PersonOpens in a new tab as well as its subclasses. For an explanation of this, see the chapter “Defining Persistent Classes.”

The "Extent" query is equivalent to the following SQL query:

SELECT %ID FROM Sample.Person

Note that you cannot rely on the order in which ID values are returned using either of these methods: Caché may determine that it is more efficient to use an index that is ordered using some other property value to satisfy this request. You can add an ORDER BY %ID clause to the SQL query if you need to.

FeedbackOpens in a new tab