I was answering a question and this little inconsistency popped up. Just wondering if anyone has come across it before, or if anyone knows why this happens.
A variable that is declared within some form of scoped element (if
, for
loop, etc.) shouldn't be available outside that scope.
i.e.
{
// accountNames variable is not available outside this scope
Set<String> accountNames = new Set<String> {'Test Account'};
}
system.debug(accountNames);
This debug statement fails with the message:
Variable does not exist: accountNames
However, if this is part of a query string being generated, the Database.query()
call somehow has access to it.
String queryString = 'SELECT Id, Name FROM Account';
if(true)
{
// accountNames variable is not available outside the `if` statement
Set<String> accountNames = new Set<String> {'Test Account'};
queryString += ' WHERE Name IN :accountNames';
}
// this debug will fail if you uncomment it
// system.debug(accountNames);
// however the query somehow has access to it.
// create an account called Test Account to prove it.
system.debug('queryString: ' + queryString);
system.debug('queryString result: ' + database.query(queryString));
But, Salesforce is smart enough to know that it's not just some random string, so the following will fail:
// Alternatively, use a random string as the variable name, and salesforce
// is smart enough to know it's gibberish
String queryString2 = 'SELECT Id, Name FROM Account';
if(true)
{
queryString2 += ' WHERE Name IN :randomString';
}
try
{
system.debug('queryString2 result: ' + database.query(queryString2));
system.assert(false);
}
catch (QueryException qe)
{
system.debug(':randomString is gibberish: ' + qe.getMessage());
}
Edit:
Interestingly, the :randomString
is definitely calculated only when the query is executed.
String queryString3 = 'SELECT Id, Name FROM Account';
String queryString4 = 'SELECT Id, Name FROM Account';
if(true)
{
// assign to query 3 before it has a value
queryString3 += ' WHERE Name IN :randomString';
// assign to query 4 after it has been declared
Set<String> randomString = new Set<String>{'Test Account'};
queryString4 += ' WHERE Name IN :randomString';
}
// both queries return correct results, regardless of when the variable was declared
system.debug('queryString3 result: ' + database.query(queryString3));
system.debug('queryString4 result: ' + database.query(queryString4));
But it's not an open slather. The following throws an error:
String queryString = 'SELECT Id, Name FROM Account WHERE Name = :someField';
someMethod();
system.debug('queryString result: ' + database.query(queryString));
void someMethod()
{
String someField = 'Test Account';
system.debug('someField: ' + someField);
}
Does anyone know why this is the case, or have an explanation why this would work?
As far as I can tell, at the time that the accountNames
variable is in scope, no part of the code knows that it's anything other than a string.
accountNames
by reference using another variable when you added it toqueryString
using the+=
. – crmprogdev yesterday:accountNames
just a string at that point in the code? Or is Salesforce behind the scenes already doing some magic with it at that point? Is there a way to test this? – Nick Cook yesterday