Resolve Field Name Conflicts in Managed Package SOQL Queries

Prevent duplicate field name errors in managed Apex SOQL queries when your package fields and a subscriber’s custom fields share the same name. Set the explicitNamespace property on a Database.QueryOptions object and pass it as a bind variable in the SOQL SET OPTIONS clause. Without requiring an explicit namespace, Apex interprets both fields as belonging to your package namespace, failing the unique field name requirement enforced in API version 34.0 and later.

Where: This feature is available in Lightning Experience and Salesforce Classic in Enterprise, Performance, Unlimited, and Developer editions.

Why: Field name collisions between managed packages and subscriber orgs can cause package failures. Previously, there was no mechanism for managed Apex code to explicitly control namespace resolution in SOQL. For example, if a managed package with the namespace ExPackageNS had an Age__c field and a subscriber created a custom field Age__c, queries for both fields simultaneously resulted in a duplicate field name error. Additionally, there was no mechanism for the managed Apex code to query for just the subscriber’s field. Now, by setting the explicitNamespace property as a query option, the package field resolves to ExPackageNS__Age__c instead. Queries without the SET OPTIONS clause preserve their existing behavior, so the change is backward compatible.

How: Create a Database.QueryOptions object through its builder methods, including .withExplicitNamespace(true). Pass the object as a bind variable in the SET OPTIONS clause, and then pass the query to Database.query() or Database.queryWithBinds().

Here’s a dynamic SOQL query created with Database.query().

Database.QueryOptions opts = Database.QueryOptions.builder()
    .withExplicitNamespace(true)
    .build();
List<Account> results = Database.query(
    'SELECT Id, Name, Age__c, ExPackageNS__Age__c FROM Account SET OPTIONS :opts',
    AccessLevel.USER_MODE);

Here’s a dynamic SOQL query created with Database.queryWithBinds().

Database.QueryOptions opts = Database.QueryOptions.builder()
    .withExplicitNamespace(true)
    .build();
List<Account> results = Database.queryWithBinds(
    'SELECT Id, Name, Age__c, ExPackageNS__Age__c FROM Account SET OPTIONS :opts',
    new Map<String, Object>{ 'opts' => opts },
    AccessLevel.USER_MODE);