2

I have a simple piece of batch code that iterates over all Location__c records that don't already have Folders. It then calls a method to create the missing folders.

public with sharing class S3LinkAddBuildingFoldersBatch implements Database.Batchable<sObject>,  Database.AllowsCallouts
{
    public Database.QueryLocator start(Database.BatchableContext bc)
    {
        return Database.getQueryLocator([SELECT Id, Name 
                                        FROM Location__c 
                                        WHERE Id NOT IN (SELECT Location__c 
                                                        FROM NEILON__Folder__c 
                                                        WHERE Name LIKE '0%|%')]);
    }

    public void execute(Database.BatchableContext bc, List<Location__c> buildings)
    {
        set<Id> buildingIds = new set<Id>();
        for(Location__c bld : buildings)
        {
            buildingIds.add(bld.Id);
        }

        boolean isAsyncJob = true;
        S3LinkAddBuildingFolders.addFolders(buildingIds,isAsyncJob);
    }

    public void finish(Database.BatchableContext bc)
    {
    }
}

I have created a Test Class that creates some Location__c records, some with folders and some without. I have debugged the start query before calling the batch, and indeed, it returns one Location__c record.

DEBUG|(Location__c:{Id=a002500000FeIKEAA3, Name=006560 | 034 Somewhere Heights LE10 7PG})

When running the Test, I get the Start did not return a valid iterable object error.

I cannot see what is wrong. Can anyone help point out what I've missed?

Thanks, Kevin

UPDATE ...

Tried rewriting to simplify the iterator query. Added a constructor to get list of folders. Changed Start to just get all location__c records. Added a filter in the execute to only consider Location__c if not in folders.

However, all to no avail. Still get the same error !! I'd appreciate any insight into what is causing the error.

public with sharing class S3LinkAddBuildingFoldersBatch implements Database.Batchable<sObject>,  Database.AllowsCallouts
{
    set<Id> foldLocIds = new set<Id>();

    public S3LinkAddBuildingFoldersBatch()
    {
        this.foldLocIds = foldLocIds;
        for(NEILON__Folder__c fold : [SELECT Location__c FROM NEILON__Folder__c WHERE Name LIKE '0%|%'])
        {
            foldLocIds.add(fold.Location__c);
        }
    }

    public Database.QueryLocator start(Database.BatchableContext bc)
    {
        return Database.getQueryLocator([SELECT Id, Name FROM Location__c]);
    }

    public void execute(Database.BatchableContext bc, List<Location__c> buildings)
    {
        
        set<Id> buildingIds = new set<Id>();
        for(Location__c bld : buildings)
        {
            if(!foldLocIds.contains(bld.Id))
            {
                buildingIds.add(bld.Id);
            }
        }

        boolean isAsyncJob = true;
        S3LinkAddBuildingFolders.addFolders(buildingIds,isAsyncJob);
    }

    public void finish(Database.BatchableContext bc)
    {
    }
}

2 Answers 2

0

I'm thinking that the QueryLocator has an issue with the use of a subquery here and is returning something invalid like null as a result.

The first thing I'd try is simply splitting it up:

        List<String> locations = [SELECT Location__c 
                                  FROM NEILON__Folder__c 
                                  WHERE Name LIKE '0%|%'];

        return Database.getQueryLocator([SELECT Id, Name 
                                        FROM Location__c 
                                        WHERE Id NOT IN : locations]);
3
  • Thanks Matt. Great thought, but I've already tried that and it makes no difference.
    – Kevin
    Dec 16, 2022 at 11:54
  • @Kevin How about changing it to not use a query locator? could just return an iterable (list of locations) if the numbers aren't going to be crazy high
    – Matt Lacey
    Dec 16, 2022 at 11:59
  • 1
    True, that could be an option ... except in this instance I've 30k records to run it over. Hence my preference for Querylocator. I'm wondering if perhaps it doesn't like the NOT IN. I think I'll try running it without filter, on all Location__c records, then filter within the execute. It means the execute will have to run the folder query every iteration, but if I keep the batch size small that should be OK.
    – Kevin
    Dec 16, 2022 at 12:18
0

Turned out there was nothing wrong with the code. It called a class which in turn was using a global method from a Managed Package ... however it seems that package depended on certain data to be present in the system.

As I don't know what data is missing, by using SeeAlldata I was able to have the test class run without the above error.

1
  • glad this was the answer as your queryLocator looked just fine
    – cropredy
    Dec 19, 2022 at 18:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .