[apex] Support convertCurrency() in SOQL/SOSL (#5237)

Merge pull request #5237 from adangel:issue-5228-apex-convertCurrency
This commit is contained in:
Andreas Dangel 2024-09-27 09:03:32 +02:00
commit ac8c873168
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
6 changed files with 111 additions and 1 deletions

View File

@ -55,6 +55,7 @@ What changes?
* [#5163](https://github.com/pmd/pmd/issues/5163): \[apex] Parser error when using toLabel in SOSL query
* [#5182](https://github.com/pmd/pmd/issues/5182): \[apex] Parser error when using GROUPING in a SOQL query
* [#5218](https://github.com/pmd/pmd/issues/5218): \[apex] Parser error when using nested subqueries in SOQL
* [#5228](https://github.com/pmd/pmd/issues/5228): \[apex] Parser error when using convertCurrency() in SOQL
* core
* [#5059](https://github.com/pmd/pmd/issues/5059): \[core] xml output doesn't escape CDATA inside its own CDATA
* [#5201](https://github.com/pmd/pmd/issues/5201): \[core] PMD sarif schema file points to nonexistent location

View File

@ -96,7 +96,7 @@
<dependency>
<groupId>io.github.apex-dev-tools</groupId>
<artifactId>apex-parser</artifactId>
<version>4.2.0</version>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>com.google.summit</groupId>

View File

@ -77,6 +77,7 @@ public final class ASTSoqlExpression extends AbstractApexNode.Single<SoqlExpress
query = query.replaceAll("(?i)\\bFIELDS\\(standard\\)", "FIELDS(STANDARD)");
query = query.replaceAll("(?i)\\bDISTANCE\\(", "DISTANCE(");
query = query.replaceAll("(?i)\\bconverttimezone\\(", "CONVERTTIMEZONE(");
query = query.replaceAll("(?i)\\bconvertcurrency\\(", "CONVERTCURRENCY(");
// sosl keywords
query = query.replaceAll("(?i)\\bfind\\b", "FIND");

View File

@ -102,4 +102,12 @@ class ApexTreeDumpTest extends BaseTreeDumpTest {
void nestedSubqueries() {
doTest("NestedSubqueries");
}
/**
* @see <a href="https://github.com/pmd/pmd/issues/5228>[apex] Parser error when using convertCurrency() in SOQL</a>
*/
@Test
void convertCurrencyInSoqlAndSosl() {
doTest("ConvertCurrencyInSoqlAndSosl");
}
}

View File

@ -0,0 +1,43 @@
public class PmdTest {
void queryOpportunities() {
List<Opportunity> myList;
myList = [
SELECT convertcurrency(Amount)
FROM Opportunity
];
// with FORMAT()
myList = [
SELECT Amount, FORMAT(amount) Amt, convertCurrency(amount) convertedAmount,
FORMAT(convertCurrency(amount)) convertedCurrency
FROM Opportunity where id = '006R00000024gDtIAI'
];
// FORMAT() with aggregate function
myList = [ SELECT FORMAT(MIN(closedate)) Amt FROM opportunity ];
}
void soslQueries() {
List<List<SObject>> searchResults;
// label with alias
searchResults = [
FIND :searchTerm
IN ALL FIELDS
RETURNING
Account(Id, toLabel(Name) AliasName)
LIMIT 10
];
// convertCurrency
searchResults = [ FIND 'test' RETURNING Opportunity(Name, convertCurrency(Amount), convertCurrency(Amount) AliasCurrency) ];
// with FORMAT()
searchResults = [ FIND 'Acme' RETURNING Account(AnnualRevenue, FORMAT(convertCurrency(AnnualRevenue)) convertedCurrency) ];
// FORMAT() with aggregate function
searchResults = [ FIND 'Acme' RETURNING Account(AnnualRevenue, FORMAT(MIN(CloseDate))) ];
}
}

View File

@ -0,0 +1,57 @@
+- ApexFile[@DefiningType = "PmdTest", @RealLoc = true]
+- UserClass[@DefiningType = "PmdTest", @Image = "PmdTest", @InterfaceNames = (), @Nested = false, @RealLoc = true, @SimpleName = "PmdTest", @SuperClassName = ""]
+- ModifierNode[@Abstract = false, @DefiningType = "PmdTest", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 1, @Override = false, @Private = false, @Protected = false, @Public = true, @RealLoc = true, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- Method[@Arity = 0, @CanonicalName = "queryOpportunities", @Constructor = false, @DefiningType = "PmdTest", @Image = "queryOpportunities", @RealLoc = true, @ReturnType = "void", @StaticInitializer = false]
| +- ModifierNode[@Abstract = false, @DefiningType = "PmdTest", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- BlockStatement[@CurlyBrace = true, @DefiningType = "PmdTest", @RealLoc = true]
| +- VariableDeclarationStatements[@DefiningType = "PmdTest", @RealLoc = true]
| | +- ModifierNode[@Abstract = false, @DefiningType = "PmdTest", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| | +- VariableDeclaration[@DefiningType = "PmdTest", @Image = "myList", @RealLoc = true, @Type = "List<Opportunity>"]
| | +- VariableExpression[@DefiningType = "PmdTest", @Image = "myList", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| | +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| | +- VariableExpression[@DefiningType = "PmdTest", @Image = "myList", @RealLoc = true]
| | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| | +- SoqlExpression[@CanonicalQuery = "SELECT CONVERTCURRENCY(Amount)\n FROM Opportunity", @DefiningType = "PmdTest", @Query = "SELECT convertcurrency(Amount)\n FROM Opportunity", @RealLoc = true]
| +- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| | +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| | +- VariableExpression[@DefiningType = "PmdTest", @Image = "myList", @RealLoc = true]
| | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| | +- SoqlExpression[@CanonicalQuery = "SELECT Amount, FORMAT(amount) Amt, CONVERTCURRENCY(amount) convertedAmount,\n FORMAT(CONVERTCURRENCY(amount)) convertedCurrency\n FROM Opportunity WHERE id = \'006R00000024gDtIAI\'", @DefiningType = "PmdTest", @Query = "SELECT Amount, FORMAT(amount) Amt, convertCurrency(amount) convertedAmount,\n FORMAT(convertCurrency(amount)) convertedCurrency\n FROM Opportunity where id = \'006R00000024gDtIAI\'", @RealLoc = true]
| +- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "myList", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- SoqlExpression[@CanonicalQuery = "SELECT FORMAT(MIN(closedate)) Amt FROM opportunity", @DefiningType = "PmdTest", @Query = "SELECT FORMAT(MIN(closedate)) Amt FROM opportunity", @RealLoc = true]
+- Method[@Arity = 0, @CanonicalName = "soslQueries", @Constructor = false, @DefiningType = "PmdTest", @Image = "soslQueries", @RealLoc = true, @ReturnType = "void", @StaticInitializer = false]
+- ModifierNode[@Abstract = false, @DefiningType = "PmdTest", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
+- BlockStatement[@CurlyBrace = true, @DefiningType = "PmdTest", @RealLoc = true]
+- VariableDeclarationStatements[@DefiningType = "PmdTest", @RealLoc = true]
| +- ModifierNode[@Abstract = false, @DefiningType = "PmdTest", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false]
| +- VariableDeclaration[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true, @Type = "List<List<SObject>>"]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- SoslExpression[@CanonicalQuery = "FIND :tmpVar1\n IN ALL FIELDS\n RETURNING\n Account(Id, TOLABEL(Name) AliasName)\n LIMIT 10", @DefiningType = "PmdTest", @Query = "\n FIND :searchTerm\n IN ALL FIELDS\n RETURNING\n Account(Id, toLabel(Name) AliasName)\n LIMIT 10\n ", @RealLoc = true]
| +- BindExpressions[@DefiningType = "PmdTest", @RealLoc = true]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "searchTerm", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- SoslExpression[@CanonicalQuery = "FIND \'test\' RETURNING Opportunity(Name, CONVERTCURRENCY(Amount), CONVERTCURRENCY(Amount) AliasCurrency)", @DefiningType = "PmdTest", @Query = " FIND \'test\' RETURNING Opportunity(Name, convertCurrency(Amount), convertCurrency(Amount) AliasCurrency) ", @RealLoc = true]
+- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
| +- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
| +- VariableExpression[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true]
| | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
| +- SoslExpression[@CanonicalQuery = "FIND \'Acme\' RETURNING Account(AnnualRevenue, FORMAT(CONVERTCURRENCY(AnnualRevenue)) convertedCurrency)", @DefiningType = "PmdTest", @Query = " FIND \'Acme\' RETURNING Account(AnnualRevenue, FORMAT(convertCurrency(AnnualRevenue)) convertedCurrency) ", @RealLoc = true]
+- ExpressionStatement[@DefiningType = "PmdTest", @RealLoc = true]
+- AssignmentExpression[@DefiningType = "PmdTest", @Op = AssignmentOperator.EQUALS, @RealLoc = true]
+- VariableExpression[@DefiningType = "PmdTest", @Image = "searchResults", @RealLoc = true]
| +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false]
+- SoslExpression[@CanonicalQuery = "FIND \'Acme\' RETURNING Account(AnnualRevenue, FORMAT(MIN(CloseDate)))", @DefiningType = "PmdTest", @Query = " FIND \'Acme\' RETURNING Account(AnnualRevenue, FORMAT(MIN(CloseDate))) ", @RealLoc = true]