Background Context:
I'm building a small feature similar to Andrew Fawcett's Declarative Lookup Rollup Summary tool. We will allow users to enter in details for their own WHERE clauses for the Aggregate queries, which are very likely to include string literals; a typical clause is expected to resemble:
WHERE Status__c = 'Complete' OR Status__c = 'In Progress'
SFDC security standards dictate that I should escape single quotes before running the query to mitigate risks of SOQL injection, but that of course causes the queries to explode around these String literals.
What I need
A way to safely run these queries without breaking the literal expressions
What I've tried
I was hoping to pull out all the literals into a collection, substituting them for :collection[index] expressions, but Dynamic SOQL does not support brackets or parenthesis in bind expressions. Here is what I currently have:
String[] criteriaValues = new List<String>();
String criteria = rule.Relationship_Criteria__c;//example: Status__c = 'Complete' OR Status__c = 'In Progress'
Matcher m = Pattern.compile( '\'(.+?)\'').matcher(criteria);
while(m.find()){//iterate through the Criteria string to carefully separate and encode String literals
criteriaValues.add( m.group() );
criteria = criteria.replace(m.group(), '{'+ (criteriaValues.size()-1) +'}');
}
/** 0 -> aggregate selections; 1 -> WHERE conditions; 2 -> GROUP BY **/
String qry = 'SELECT {0} FROM '+objectType.getDescribe().getname()+ ' WHERE {1} GROUP BY {2}';
String[] fmtArgs = new List<String>{
'COUNT(Id), '+rule.Relationship_Field__c,
String.format(criteria, criteriaValues),
rule.Relationship_Field__c
};
//Executed query string should read (or perform identically to)
//'SELECT COUNT(ID), Related__c FROM Custom__c WHERE Status__c = 'Complete' OR Status__c = 'In Progress' GROUP BY Related__c'
return (AggregateResult[])Database.query(String.escapeSingleQuotes(String.format(qry, fmtArgs)));
