Deletion of data should be handled with care. To prevent issues, we can use transactions or rely on backups and logs.
Some improvements have been made in 4D 20 R4 to make your selections of records stable and consistent regarding potential deletion of records in this selection.
Keep reading to learn how your 4D code will be so safe out of the box.
4D internal behavior
As you may already know, 4D internally handles an address table to point on physical blocks of data on the disk. Regarding the handling of this address table in the 4D internal architecture, this could lead to an unexpected result.
When a record is deleted, its reference to the physical data is deleted in the address table.
To avoid holes and optimize the use of memory for the address table, when a new record is created, 4D re uses free spaces in this address table.
Once you have built a selection of records (for example with a query), after this, if a record belonging to this selection is deleted from another user and a new record is created, it may be referenced through the same previous record number in the address table.
Thus, in the past, you could find in your selection some data you don’t expect at all.
a concrete example
An employee has a stack of tasks to deal with. When created, the task is affected the TO DO status.
Periodically, a mail is sent to the employee with their TO DO tasks. A selection of those tasks is built and there is a loop on each one to provide the details in the email.
But the administrator has the permission to delete some tasks. If the administrator deletes by mistake a task in the selection above and a new task is created right after for another employee … what happens?
Before 4D 20 R4
The newly created task was found in the loop and as it is a task for another employee, it is completely out of scope!
After 4D 20 R4
The newly created task is not found in the loop and the selection of data is always consistent and you can rely on it safely.
The use case above is not so frequent because most of the time we change a status on a record instead of deleting it directly.
Or maybe you were used to check again the coherence of a record / entity (regarding the selection criteria) before proceeding it.
Anyway, you don’t have to mind about this anymore.
let’s see this in action in some code
In the example below we build the $target entity selection.
After this, we delete an entity belonging to this entity selection (we do this in the same piece of code for a quick understanding, but we can imagine this is done by another user in another process).
The loop processes only the two entities left (internalID 10 and 12).
var $target; $customers : cs.CustomersSelection
var $customer : cs.CustomersEntity
var $newCustomer : cs.CustomersEntity
var $status : Object
// Three customers are retrieved with internal ID 10, 11, 12
$target:=ds.Customers.query("internalID >= 10 and internalID <= 12")
ASSERT($target.length=3)
// We delete the customer with internalID = 11
$customers:=ds.Customers.query("internalID = :1"; 11).drop()
// We create a new customer with internalID = 99
$newCustomer:=ds.Customers.new()
$newCustomer.internalID:=99
$status:=$newCustomer.save()
// We loop on the $target entity selection
// got before the creation of the new customer
For each ($customer; $target)
// The new customer is not in the entity selection ...
ASSERT($customer.internalID#99)
End for each
Important: This feature applies on data handled with classic 4D standard QUERY commands and ORDA.
Keep working with 4D safely as your favourite development platform is taking charge of many concerns to let you focus on your business logic!