Skip to content

Data Dictionaries in 18.1

Data Dictionary Enhancements

Extended DD Fields

Changed the way extended field data is stored in data dictionaries. Up until now, DF_TEXT and DF_BINARY data was stored in a memory heap. When the DD extended field was initialized, the maximum length of the data (DF_FIELD_LENGTH) was allocated for this heap. This memory was not reclaimed until the DDO was destroyed, usually during shutdown. This means that every single DDO instance in every single view with extended fields would allocate the maximum amount of memory possible in the local DD buffer. In cases where very large SQL fields were used, this could add up.

Consider an SQL table with a 2Mb varchar(max) field which is used in 100 views. This was one of the reasons we do not automatically enable extended field support in DDOs.

The DD's extended fields now store the extended data in a UChar array, which only allocates the actual memory needed for the current data. If that 2Mb varchar(max) column only uses 1000 bytes, only 1000 bytes are used. If the data is empty (cleared or blank), no memory is used. This memory saving can be significant.

Attach_Main_File Logic

Attach_Main_File's logic has been moved into the runtime to make it faster when dealing with tables with many columns. When called, all columns need to be checked for a parent relationship and that the parent DDO actually exists. If "attachable," other special testing occurs to determine if an attach may be applied. This was all done in Flex code. Now the runtime does all of this checking and only makes a Flex call for the field candidates that actually need the special testing. It does this by calling Get ShouldFieldAttach. This entire process was, and still is, private. The behavior is faster but unchanged.

The pbDDAttach property is unchanged but has been moved to the runtime. This is a legacy property that I doubt if anyone uses.

If for some reason you have augmented the DD method DDAttach, you will need to change your code. This was a private method that should never be called or altered. If you have done this, contact us and we will advise.

Performance Improvement

The handling of the DD's local buffer has been moved to a new faster class. The private Record_Buffer class has been replaced with a new private cRecordBuffer class. As long as you have respected the privacy of this class and its interface, no changes are required.

This change was made to speed up DD operations. The buffer class is responsible for moving data between the Table buffers and the DD buffers. This has always been a fast process but now it is much faster. Because this has always been fast, you are not likely to notice the change when working with data entry views. This will be most noticeable when performing batch DD updates involving the finding and saving of many records through a DD. In particular, tables with large numbers of fields will benefit most from this change.

For our testing purposes, you can choose to run an application using the new or old DD Buffer logic. By default, it uses the new logic. If you insert the following line:

Define Use$Old$DD$Buffer

above the first line of your application, it will use the old logic. It must be your first line and it applies to your entire application! This is only for testing and will probably not exist when released.

Controlling Cascade Deletes at the Child Level

DDs now have the ability to control cascade deletes at the child record level.

Until now, Cascade_Delete_State would be set to specify if a main record delete should stop the delete if child records exist or attempt to delete the children (and all of their children, etc.). Cascade_Delete_State was only checked once at the main DD level and if allowed, delete cascade is attempted. This behavior has not changed. In addition, you can now exercise control over the cascade delete at the child table level.

CascadeDeleteAllowed

Child DDs can now specify if they will allow themselves to be deleted as part of a cascade delete. The child DD specifies this on a parent table by parent table basis. If any child table disallows cascade delete anywhere within the cascade delete chain, the delete will be cancelled and rolled back. This is controlled by setting CascadeDeleteAllowed.

Set CascadeDeleteAllowed hParentTable to True|False

By default, a DDO allows a cascade delete from any parent so the default (for all parents) is True. If you wish to stop cascade deletes, set this property to False for the specified parent table. For example, in order entry:

// for orderhea
Set CascadeDeleteAllowed Customer.File_Number to False

This would make it impossible to delete a Customer record if Orderhea records exist. Because a Customer is a critical part of an order, this rule would make good business sense. Once set, you would never have to worry that deleting a customer or any parent of customer might delete the order. If an order exists, the delete would be cancelled and rolled back.

IsCascadeDeleteAllowed

Function IsCascadeDeleteAllowed integer iTable returns Boolean

When a delete operation has found a child record, it will see if it can be deleted by calling IsCascadeDeleteAllowed, passing the parent table that is being deleted. By default, this checks CascadeDeleteAllowed for that parent table and returns its value. This could be augmented.

This will eventually be modeled in the Studio's DataDictionary modeler.

Setting a Null Relationship During a Cascade Delete

Normally you should never be able to delete a record if it has children. When children exist, a DD should either disallow the delete or perform a cascade delete. An alternate strategy to actually deleting the children could be to switch the child record's parent relationship to null. This is supported using the CascadeDeleteNull and IsCascadeDeleteNull interfaces.

CascadeDeleteNull

Child DDs can now specify if they will be deleted or have their child-to-parent relationship set to null. This is controlled on a parent by parent basis by setting CascadeDeleteNull.

Set CascadeDeleteNull hParentTable to True|False

By default, this is False, so cascade deletes actually delete the record. If set to True and ParentNullAllowed has been set to True, then instead of deleting the record, it will change the child parent to a null record, adjusting all Backout and Update values as needed. Note that ParentNullAllowed must be set to True for this to even be tested. If parent null relationships are not allowed, it would not make sense to do this.

For example, in OrderHea, null SalesP relationships are allowed. Therefore, when deleting a SalesP record, it might make sense to switch all Order's SalesP parent to null.

// for orderhea
Set ParentNullAllowed SalesP.File_Number to True
Set CascadeDeleteNull SalesP.File_Number to True

IsCascadeDeleteNull

Function IsCascadeDeleteNull integer iTable returns Boolean

When a delete operation has found a child record, it must determine if the record should be deleted or changed to a null parent. If null parents are supported between the child and the deleting parent (IsNullParentAllowed), it will call IsCascadeDeleteNull, passing the parent table that is being deleted. By default, this checks CascadeDeleteNull for that parent table and returns its value. This could be augmented.

This will eventually be modeled in the Studio's DataDictionary modeler.

Validate_Cascade_Delete

The validation method is now called before any child records are deleted. This gives the developer more choices when deciding if a cascade validate is valid as you can now inspect information about those child records. This represents a change in behavior, but the chances of this changing your application's behavior are quite small.

When a child record is a candidate for deletion as part of a cascade delete, the following now occurs. Assume that iParent refers to the parent that is causing this particular child delete cascade.

Get IsCascadeDeleteAllowed iParent to bAllowed
If not bAllowed
    raise error and rollback entire delete transaction
Get Validate_Cascade_Delete to bAllowed
if not allowed
    rollback the entire delete transaction
Get IsParentNullAllowed iParent to bNullable
If bNullable
    Get IsCascadeDeleteNull iParent to bNullable
    If bNullable
        Set child to parent relationship to false
else
    Continue cascade delete for all related children in all child tables
if no error
    Delete the record

All of these new behaviors are backwards compatible. The only possible exception is Validate_Cascade_Delete, which is now called at a different time (before child records are cascade deleted instead of after). This should not matter, but you may wish to look at any use of this message, just to be careful.

New DD Method: Field_CommitNoEnterOnIndex

Function Field_CommitNoEnterOnIndex Integer iField Returns Boolean

This is a new method that is called when the DD needs to determine if a committed field that has an index should support entry. By default, it returns False, which means that while the field will be NoPut, you can enter values for finding purposes. This is the most flexible option. Some developers prefer that the DEO treat this as non-enterable (DisplayOnly). If so, you can augment this function to return True.

This was made a function instead of a property so you can easily control this at the sub-class level (same preference for your entire application) and so you can alter this on a field-by-field basis.

For example, adding this to your DD sub-class layer, DD Class, or DD object, would make all indexed, committed fields DisplayOnly.

Function Field_CommitNoEnterOnIndex Integer iField Returns Boolean
    Function_Return True
End_Function

Adding the following to your order entry Order.vw view would make a single field (Order.Order_Date) DisplayOnly while all other committed index fields would be NoPut but allow entry. This assumes Order.Order_Date is indexed, has DD_COMMIT set, and that the OrderHea record is actually committed.

Function Field_CommitNoEnterOnIndex Integer iField Returns Boolean
    // if date field, return True (noEnter) or else return false (can enter for finding but will be noput)
    Function_Return (RefTable(OrderHea.Order_Date) = iField)
End_Function

Diamond Relationships

We have addressed a rather confusing DDO bug with diamond relationships. This started with a report and sample from Sture in 17.1 where he noted diamond relationships did not work properly with WebApp. They don't always work in Windows applications, but we've never seen the error because it is somewhat self-correcting.

A summary of the problem would be:

If a diamond relationship exists with an autofill, relates to a constrained child, finding in the constrained parent may not set the proper CurrentRowId for other DDOs in the diamond structure. For example:

A
| *\
*|   C
|  /
B
* = relates to constraint
B is Autofill

If you find a record in A, B will find the first related child, which will find a record in C. C will not be notified that it has a new record and its CurrentRowId will be wrong. This structure is a very typical structure. As noted, this tends to get corrected as soon as a find request is made to the B level.

See Also