6

Salesforce documentation gives the following rules for non-mandatory lookup fields:

  • When the target is deleted, by default fields that reference it are set to null.
  • You can tell it to prevent deletion of an object if it is referenced by lookups.
  • If you phone Salesforce you can be allowed to set up cascade delete.

The documentation is not clear about mandatory lookup fields, but I'd hope that Salesforce maintains the constraint by preventing deletion.

It looks as if it is impossible for an ID read from a lookup field to not resolve to a real record, but code I have inherited seems to want to check this by performing a SOQL query to see if the record exists. This costs SOQL governor.

Before I delete this code, is there any situation where it may be needed? When does Salesforce perform these constraint checks? Is it possible for the target to be deleted in the same transaction and the result to be wrong in a trigger? Salesforce performs triggers at the point of the DML operation, so I could only see this happening if either

  • The Lookup is a lookup to self
  • Code in the trigger performs the delete

We'd not do the latter. Maybe some other person will, but it sounds unlikely.

So is there something I'm missing? I assume Salesforce transactions see the state of the database including all changes already made in the transaction, so the code is not trying to determine the state of the system before the transaction.

6
  • 1
    Self lookups and cycles are detected by SF and not allowed. Is the code in "with sharing" scenario? Maybe it tries to check "is current user allowed to see the lookup's target record" rather than "to be sure the record really exists". – eyescream Dec 20 '13 at 12:07
  • Thanks for that pointer. I'll have to check. There are a number of examples. Some of it is in library code so I guess could be called from a mix of scenarios. Others I should be able to work out on a case by case basis. – Richard Corfield Dec 20 '13 at 13:51
  • Where it's in trigger validation and the trigger does not specify sharing does the trigger inherit the sharing-ness of the caller? We use queues, with related objects also owned by the queue. so I wonder if it's checking that related things are in the same queue by virtue of being visible to the user. – Richard Corfield Dec 20 '13 at 14:04
  • 1
  • 1
    Yes, the trigger would switch to a without sharing context, and any methods it calls will respect the class's definition. Of interesting note: a class with no sharing mode specified will inherit from it's caller (acts as without sharing if it's the top level call). – ca_peterson Jan 4 '14 at 0:16
5

It is possible that I can read an ID from a field, and when querying for the item with the given ID I find no result. This will happen when Sharing Rules prevent the user seeing the item and the code is running "With Sharing".

The Sharing-ness of the code switches depending on the call tree, so as code enters a class that is marked with or without sharing permissions change. The permission does not change when execution enters a class that does not have a with or without sharing declaration.

Code generally runs Without Sharing, so this problem is only likely to occur if "With Sharing" has been explicitly turned on somewhere. (Or if the user calls anonymous apex). Trigger code starts Without Sharing, even if the code which called the DML was With Sharing.

For Sharing see Salesforce documentation on Sharing keywords.

3
  • 1
    To save the extra effort of a query specifically to check this you can do a cross-object query, e.g. lookup__r.name as well as lookup__c. If(lookup__c != null && lookup__r.name == null) the user has no sharing access. – ca_peterson Jan 3 '14 at 21:38
  • Can you please edit your comment. It looks like an edit to the question, and not like an answer. – Saariko Jan 5 '14 at 16:08
  • 1
    Would losing the bottom paragraphs help? The first paragraph really gives the answer as it describes the situation where an ID can be apparently invalid. I can add information from the other answers about triggers as that is also useful. – Richard Corfield Jan 5 '14 at 20:24
3

Any value you observe in a lookup field will resolve to a live database record that can be referenced. The instant a record is deleted, all lookups will return a null value instead of the previously referenced ID value. Therefore, it is safe to assume that any ID value you observe in a lookup field is a legitimate record. However, just because the user can see an ID value doesn't mean they have the right to update that record, so one must always check for database save errors by exception or by partial save.

There are times when an ID value may be invalid, such as when a developer uses a text field to store references instead of lookup fields. For example, to store a reference to an event, you can't use a lookup field, so you'd have to necessarily use a normal text field. Opportunity Line Items are another example; in fact, many standard "detail" records can't have a lookup assigned to them. In this case, it's usually better to query before you update, although it's not required, because the database will still inform you of invalid ID values during a create or update call.

However, there is one specific use for querying records before updating: if you're worried about concurrency, it is highly adviseable to at least perform a SELECT ... FOR UPDATE command to force those records to be locked, granting your code exclusive access to those records for the duration of the transaction. This reduces the possibility of your values being overwritten at the same moment that another user is trying to modify those same records.

9
  • I think the explanation concerning sharing rules above is interesting. Maybe a test would prove it. We do have some IDs in text form, which we do have to check. These are lookups. I've read about FOR UPDATE, but it is not used in the code I'm currently looking at. Does it block the other user, or cause them an exception? – Richard Corfield Dec 20 '13 at 17:41
  • 1
    @RichardCorfield It prevents concurrent updates, which can cause an error for the other user in the form of "Another user is currently editing this record. Please try again." It prevents race conditions at the expense that some updates may fail. Ideally, you should minimize locking time, especially on long processes, such as waiting on a callout or processing a large amount of data. It will also cause your queries to appear to execute slower, but you'll gain the peace of mind knowing that you won't have stale data. – sfdcfox Dec 20 '13 at 18:04
  • Thanks - so rather than blocking the other user their request will fail. Fits in with other aspects of the design that seem aimed to reduce time taken. So it will hold the lock during a callout? It doesn't seem to allow suspended transactions as we can't callout after DML, but holding a lock makes sense. – Richard Corfield Dec 22 '13 at 14:03
  • 2
    The documentation on "FOR UPDATE" suggests that you can induce a lock before calling out; the callout limitation applies specifically to callouts performed after an insert, update, delete, merge, convertLead, or undelete operation. While it is well known that you can't callout in a trigger, for example, few developers realize you could, for example, query some records with a lock in a Visualforce page, then perform a callout, and finally perform an update. Similarly, Batch classes, Scheduled classes, and web service methods are not restricted this way. – sfdcfox Dec 22 '13 at 15:36
  • 1
    @RichardCorfield you can write your own answer that summarizes your findings (I didn't do much, it was just a rough idea) and accept it. And separately from that you can upvote sfdcfox's answer. This will improve our statistic of answered questions and get us closer to leaving the beta stage ;) – eyescream Jan 2 '14 at 19:09
0

Yes, It's possible. enter image description here

The synced quote as per screenshot got deleted but field "Synced Quote" at Opp still having reference. This would lead to any further updates on Opportunity with Invalid Cross Reference Field Exception.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.