diff --git a/CHANGELOG.md b/CHANGELOG.md index 31b1e43..7ba3f90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # apex-parser - Changelog +## Unreleased + +- Fix the `dataCategoryName` grammar rule so parenthesized SOQL data category lists close with `RPAREN`; previously, valid multi-category `WITH DATA CATEGORY` filters failed to parse. + ## 5.1.0 - 2026-07-03 - Allow functions in `GROUP BY` clause of SOQL queries diff --git a/antlr/BaseApexParser.g4 b/antlr/BaseApexParser.g4 index 03bc5d2..44ec10b 100644 --- a/antlr/BaseApexParser.g4 +++ b/antlr/BaseApexParser.g4 @@ -762,7 +762,7 @@ dataCategorySelection dataCategoryName : soqlId - | LPAREN soqlId (COMMA soqlId)* LPAREN; + | LPAREN soqlId (COMMA soqlId)* RPAREN; filteringSelector : AT | ABOVE | BELOW | ABOVE_OR_BELOW; diff --git a/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java b/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java index efbcb8e..3f8727c 100644 --- a/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java +++ b/jvm/src/test/java/io/github/apexdevtools/apexparser/SOQLParserTest.java @@ -43,6 +43,36 @@ void testSOQL() { assertEquals(0, parserAndCounter.getValue().getNumErrors()); } + @Test + void testSOQLWhereWithSingleDataCategoryFilter() { + Map.Entry parserAndCounter = createParser( + "SELECT Title FROM KnowledgeArticleVersion WHERE PublishStatus='online' WITH DATA CATEGORY Geography__c ABOVE usa__c" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testSOQLWhereWithDataCategoryListFilter() { + Map.Entry parserAndCounter = createParser( + "SELECT Title FROM Question WHERE LastReplyDate > 2005-10-08T01:02:03Z WITH DATA CATEGORY Geography__c AT (usa__c, uk__c)" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + + @Test + void testSOQLWhereWithMultipleDataCategoryFilters() { + Map.Entry parserAndCounter = createParser( + "SELECT UrlName FROM KnowledgeArticleVersion WHERE PublishStatus='draft' WITH DATA CATEGORY Geography__c AT usa__c AND Product__c ABOVE_OR_BELOW mobile_phones__c" + ); + ApexParser.QueryContext context = parserAndCounter.getKey().query(); + assertNotNull(context); + assertEquals(0, parserAndCounter.getValue().getNumErrors()); + } + @Test void testCurrencyLiteral() { Map.Entry parserAndCounter = createParser( diff --git a/npm/test/SOQLParserTest.ts b/npm/test/SOQLParserTest.ts index 3b89fbb..7d8416b 100644 --- a/npm/test/SOQLParserTest.ts +++ b/npm/test/SOQLParserTest.ts @@ -26,6 +26,36 @@ test("SOQL Query", () => { expect(errorCounter.getNumErrors()).toEqual(0); }); +test("SOQL Where with single data category filter", () => { + const [parser, errorCounter] = createParser( + "SELECT Title FROM KnowledgeArticleVersion WHERE PublishStatus='online' WITH DATA CATEGORY Geography__c ABOVE usa__c" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + +test("SOQL Where with data category list filter", () => { + const [parser, errorCounter] = createParser( + "SELECT Title FROM Question WHERE LastReplyDate > 2005-10-08T01:02:03Z WITH DATA CATEGORY Geography__c AT (usa__c, uk__c)" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + +test("SOQL Where with multiple data category filters", () => { + const [parser, errorCounter] = createParser( + "SELECT UrlName FROM KnowledgeArticleVersion WHERE PublishStatus='draft' WITH DATA CATEGORY Geography__c AT usa__c AND Product__c ABOVE_OR_BELOW mobile_phones__c" + ); + const context = parser.query(); + + expect(context).toBeInstanceOf(QueryContext); + expect(errorCounter.getNumErrors()).toEqual(0); +}); + test("SOQL Query Using Field function", () => { const [parser, errorCounter] = createParser( "Select Fields(All) from Account"