An object Id in Apex that matches no objects

SOQL and SOSL both support the “in” operator in their “where” clauses. I was recently using this mechanism where a small number of Id values were being generated in some code and passed about as Set<Id>. But in one case I wanted the set to contain only an Id that would not match any of the objects. Where this is done directly in SOQL or SOQL, I have seen the string ‘0000000000000000’ (15 zeroes) used and so assumed that would be ok. But when such a String is cast to an Id you get an exception that contains the message “Invalid id”. It appears that the Apex code that handles Ids is a little fussier than the SOQL and SOSL engines.

The good news though is that there appears to be a simple solution to the problem as illustrated in the testOk method of the code below. The getKeyPrefix method “Returns the three-character prefix code for the object” and this combined with 12 zeros can be converted to an Id and appears to never match any objects. But it does cost an expensive and governor limited describe call per object type.

@isTest
private class IdTest {
    @isTest
    static void testOk() {
        ok(Account.SObjectType.getDescribe().getKeyPrefix() + '000000000000', Account.SObjectType);
        ok(Contact.SObjectType.getDescribe().getKeyPrefix() + '000000000000', Contact.SObjectType);
        ok(Task.SObjectType.getDescribe().getKeyPrefix() + '000000000000', Task.SObjectType);
    }
    @isTest
    static void testNotOk() {
        notOk('000000000000000', Account.SObjectType);
        notOk('123456789012345', Account.SObjectType);
        notOk(Account.SObjectType.getDescribe().getKeyPrefix() + '123456789012', Account.SObjectType);
        notOk(Account.SObjectType.getDescribe().getKeyPrefix() + '222222222222', Account.SObjectType);
    }
    private static void ok(String idString, SObjectType sobType) {
        Id id = (Id) idString;
        System.assertEquals(0, Database.countQuery('select Count() from ' + sobType + ' where Id = \'' + id + '\''));
    }
    private static void notOk(String idString, SObjectType sobType) {
        try {
            Id id = (Id) idString;
            System.assert(false, 'exception expected');
        } catch (Exception e) {
            System.debug('exception=' + e);
            System.assert(e.getMessage().contains('Invalid id'));
        }
        // While the above cast fails, the string value can be use in SOQL
        System.assertEquals(0, Database.countQuery('select Count() from ' + sobType + ' where Id = \'' + idString + '\''));
    }
}

Free Force.com Security Source Code Scanner Revisited

A Force.com IDE (Eclipse-based) alternative to the PDF output scan results has been available for a couple of months as described in this blog post Source Code Scanning. The “Scanning a project” section of the User Guide illustrates the benefit of having the scan results within the IDE: a perspective is provided that lets you click through the results and open the class or trigger at the lines that are significant.

A couple of things to note about the IDE-based version:

  • One step of the installation process took several minutes to complete and I was tempted to just cancel the installation so be patient.
  • The scan is still done remotely and takes many minutes to complete unlike similar tools for other languages in Eclipse. So it only makes sense to run the scan occasionally.

It is a few months since I have run the scanner and it identified about a dozen issues so was well worth the effort of running again. But the results do include a lot of false positives making it fairly time consuming to separate out the real issues. Here are two examples that caused many false positives.

The first is a pattern to automatically initialize custom settings to their default values by indirecting through a class and so avoid users having to click “Manage” on all the custom settings before the application can be used. The null guard ensures that there is only one DML operation performed but the scanner (understandably) sees this as a case of “DML statements inside loops” when the custom setting is referenced inside a loop in a trigger:

global with sharing class CustomSettings {
    global static DateConversionFactors__c getDateConversionFactors() {
        if (DateConversionFactors__c.getInstance() == null) {
            upsert new DateConversionFactors__c(SetupOwnerId = UserInfo.getOrganizationId());
        }
        return DateConversionFactors__c.getInstance();
    }
    // Other similar methods for other custom settings
}

The second is something that perhaps should be addressed in the scanner and concerns the “list or set iteration for loop”. In this example some SOQL is executed in the ProcessUtil.withoutApprovalProcess which is called once before the loop. But the scanner treats it as if it is within the loop and so incorrectly reports it as a “SOQL/SOSL statements inside loops” issue:

for (Id anId : ProcessUtil.withoutApprovalProcess(ids)) {
    // Other logic goes here
}

Auto number fields – you had better get them right in version 1.0

Here is another item to add to the list of painful restrictions in managed packages:

For the sake of Google search matches the error message text is: “Error: Auto number fields cannot be added to a custom object when the custom object has already been uploaded in a Managed – Released package”.