From 74f9d7589723252fceee658aa07a374b08c4dce2 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 26 Sep 2024 11:39:48 +0200 Subject: [PATCH 1/2] [apex] Support convertCurrency() in SOQL/SOSL Fixes #5228 --- docs/pages/release_notes.md | 1 + .../pmd/lang/apex/ast/ASTSoqlExpression.java | 1 + .../pmd/lang/apex/ast/ApexTreeDumpTest.java | 8 +++ .../apex/ast/ConvertCurrencyInSoqlAndSosl.cls | 43 ++++++++++++++ .../apex/ast/ConvertCurrencyInSoqlAndSosl.txt | 57 +++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/ConvertCurrencyInSoqlAndSosl.cls create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/ConvertCurrencyInSoqlAndSosl.txt diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 5cde4e3dcd..a704bd36b2 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -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 diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpression.java index 8ec31a42cd..3b08de7d9b 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTSoqlExpression.java @@ -77,6 +77,7 @@ public final class ASTSoqlExpression extends AbstractApexNode.Single 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> 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))) ]; + + } +} diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/ConvertCurrencyInSoqlAndSosl.txt b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/ConvertCurrencyInSoqlAndSosl.txt new file mode 100644 index 0000000000..f297ddecfc --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/ConvertCurrencyInSoqlAndSosl.txt @@ -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"] + | | +- 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>"] + | +- 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] From c84ffb8e0b0669cf3c265859e79d18a74165f54a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 26 Sep 2024 18:50:00 +0200 Subject: [PATCH 2/2] [apex] Bump apex-parser from 4.2.0 to 4.3.0 --- pmd-apex/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index c616dde033..b045cffa2d 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -96,7 +96,7 @@ io.github.apex-dev-tools apex-parser - 4.2.0 + 4.3.0 com.google.summit