-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- IMPLEMENTING RELATIONSHIP TRAVERSAL & OIDS 96/01/26-27 Who knows what? In a join relationship, the dbRelRefBase that performs the relationship needs to know the join fields on both sides, and which dbTable is being affected. In a pointer traversal, the mechanism varies according to the number of members on each side: - if the rhs is 1-ary, take the OID from the lhs rel'n and directly load the rhs - if the rhs is N, do a search on the OID stored in the traversal member space. This implies that at any time, a rel ref must supply an OID for the other side to use as either a search value or direct reference. The OID that is stored is supplied by the other side. We will abstract out OIDS. They will always be an unsigned long and a given table will determine whether this is a record offset or a generated unique OID. This will be hidden behind the currentOID() and gotoOID() methods. Rel Kind A stores B stores 1:1 OID of B OID of A 1:N nothing OID of A N:N OID of rel OID of rel inherit nothing OID of A Note: inheritance chain treated as a set of 1:N relationships, eg: if C inherits B inherits A A contains nothing B contains OID of A C contains OID of B & A There are two situations when it comes to saving data. By careful sequencing of saves, most situations are fairly simple. 1:1 If both sides are new records, one must save twice, first to get an OID for the other side to point back to it, then again after the other side has been saved. 1:N save 1-side first, then broadcast (most common example will be owner-part) N:N get OID of rel, then save order is immaterial inherit save from base class first As we are updating pointers on the rhs, even if it is not a part relationship, this implies that saves should be propagated. It also raises the question of loading a single copy of an object, rather than (as present) allowing a single program to load multiple copies. This may be deferred for now but can probably be done as a simple extension of the current cache. The only issue with that is identifying when to write the cached items, and keeping the current object in the cache (making it current removes it from the cache). HOW TO GET OIDS AND SAVE THEM!!!!! From the above discussion, it can be seen that we have a case out of many possibles, where extra saving action is required. The easiest way to manage this is by making these dependents. Happily, this has already occurred. The inverses of these traversal members, if needing to be saved, are already dependents. Thus, dbRelRefBase dependents that are 1-ary sides need to set their opposite number's OID. This shows that the dbRelRefBase itself needs to be the dependent, NOT a separate class as originally tried. -=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 96/01/30 CLONING RELATIONSHIP FIELDS There's a subtle gotcha in cloning relationship fields, as occurs when we have a join relationship and the table is cloned (eg: for the rhs of a relationship). Assume, as in ooftst02, that the relationship field is the first field in the class. At the time of this field being copied, the other fields will not yet have been copied and so won't have linked themselves. This means the join field will not have been constructed and so won't yet be registered with the cloned database. There are a few ways around this: 1) After the cloning procedure has created a new userTable, it calls something like userTable->postCloneCleanup(). 2) Post a dependency that will have the join field updated before the next action. 3) Have a special value in the join field and check for it before using, so it is setup properly. ISSUES 1) Adds an extra processing step which lengthens clone time. 2) Relies on the dependency having a message broadcast before the join field is needed. 3) Contaminates a lot of code. CHOICE 1) Although a nuisance is by far the cleanest and gives us a frame in which other dbFields could be extended for post-clone cleanups. -=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- SAVING OIDS IN MULTIPLE CACHED RELATED RECORDS 96/02/01 Say we have a typical Owner-part relationship where the rhs is being cached. eg: a listbox on a GUI dialog, where invoice line items are being entered. When the Owner record is saved, the save propagates to relatedTable->saveRecord() which then saves all the cached records. However, as we have delayed setting the OIDs of the relationship fields, we need some way to set these OIDS for *each* record in the cache, not just the current one. ALTERNATIVES 1) For each cached record, have a dependency for each relationship field that needs completing. (This is one/record by the nature of the part records - at the rhs of a relationship 2) Have a "pre-save" pass performed on each cached record. 3) The relationship field, when it receives the saveRecord, does a loop at dbTable level, pulling in records from the cache and setting its value in all of them. ADDITIONAL COMPLICATION In examining these problems, it becomes obvious that there's a flaw in our current split of responsibilities: dbTable - broadcasts status changes & calls backend backend - caches multiple records. When we save, the broadcast happens at dbTable level. However, a two-level relationship causes problems: current Patient -> Visit 96/02/01 -> Drug blah \-> Visit 95/12/20 -> Drug blah \-> Drug blah2 Assume Visit 96/02/01 is the current Visit and 95/12/20 is in the backend cache. If we just broadcast a save at Visit dbTable level, when the cached 95/12/20 is saved it won't broadcast to its dependents. Thus it seems that the backend loop to save cached records must now call dbTable::saveRecord, not its own saveContext. NICE WAY TO GRAB OIDS Initially I thought all relationships had to update their OIDs when you saved a related record. This is of course false - only the relationship managing the link to the lhs need save. The dbTable points to this relationship field with mRelated. So, it is actually trivial to call an updateLink method on that field, from dbTable::saveRecord. This lets us remove the use of dbFieldCopier for join fields, as well as giving us a point at which to save OIDS for other relationships.