QueryLocator crashes after casting to Iterable and executing .iterator()

So here is some simple code that does work:

Database.QueryLocator ql = Database.getQueryLocator('SELECT Id FROM Account LIMIT 2');
Iterator<SObject> iter = ql.iterator();

Ok. But suppose I have a method that wants to generically be able to take in any kind of sObject Iterable, whether it’s a list or QueryLocator or something else custom. So then I’ll want to work generically with an Iterator<SObject>.

But this code crashes:

Database.QueryLocator ql = Database.getQueryLocator('SELECT Id FROM Account LIMIT 2');
Iterable<SObject> qlIterable = ql;
Iterator<SObject> iter = qlIterable.iterator();

The casting line works. The iterator() line is the last to execute before I get:

Filter

Line: undefined, Column: undefined

Response to EXEC was :

An internal server error has occurred

An error has occurred while processing your request. The
salesforce.com support team has been notified of the problem. If you
believe you have additional information that may be of help in
reproducing or correcting the error, please contact Salesforce
Support. Please indicate the URL of the page you were requesting, any
error id shown on this page as well as any other related information.
We apologize for the inconvenience.

Thank you again for your patience and assistance. And thanks for using
salesforce.com!

Error ID: 594482587-81700 (-1994218094) . HTTP CODE[500]

Any thoughts as to why this might be the case? I ended up working around by having a special overload to handle QueryLocator. Still it’s weird.

EDIT: Why am I assigning it to an Iterable in the first place? The assignment in the snippet above is equivalent to what happens if I create a method that generically accepts an Iterable<SObject> as an input parameter. I created a parsing method that can take in SObjects either as a QueryLocator or an actual List. If I pass in a QueryLocator, it crashes on the line where I obtain the iterator to loop through it. The snippet above is a minimal example of what it takes to make this crash happen.

Answer

I stumbled over the same thing and decided to encapsulate this gack into a small class for the future and quickly wanted to contribute it back.

You simply use it like this:

public void myMethod(Iterable<SObject> iterable) {
    iterable = new GackAwareIterable(iterable);
    ...
    doStuffWith(iterable.iterator());
    ...
}

GackAwareIterable.cls

public class GackAwareIterable implements Iterable<Object> {
    private final Iterable<Object> iterable;
    
    public GackAwareIterable(Iterable<Object> iterable) {
        this.iterable = iterable;
    }
    
    public Iterator<Object> iterator() {
        Iterator<Object> result;
        
        if(iterable instanceof Database.QueryLocator) {
            result = (Iterator<Object>) ((Database.QueryLocator) iterable).iterator();
        }
        else {
            result = iterable.iterator();
        }
        
        return result;
    }
}

What a bummer there are no generics in Apex. If you prefer simply change the types to SObject to get rid of additional casting.
For a simple test, check this Gist

Attribution
Source : Link , Question Author : Charles T , Answer Author : Basti

Leave a Comment