jetstreamapp / soql-parser-js

Javascript SOQL parser
https://jetstreamapp.github.io/soql-parser-js/
MIT License
77 stars 20 forks source link

parseQuery fails when bind variable contains parenthesis #153

Closed pgonzaleznetwork closed 3 years ago

pgonzaleznetwork commented 3 years ago
let query = 'select id from account where id in :accountMap.keySet();
parseQuery(query,{allowApexBindVariables:true});

Result

(node:55304) UnhandledPromiseRejectionWarning: NoViableAltException: Expecting: one of these possible Token sequences:
  1. [AVG, L_PAREN]
  2. [COUNT, L_PAREN]
  3. [COUNT_DISTINCT, L_PAREN]
  4. [MIN, L_PAREN]
  5. [MAX, L_PAREN]
  6. [SUM, L_PAREN]
  7. [DISTANCE, L_PAREN]
  8. [CALENDAR_MONTH, L_PAREN]
  9. [CALENDAR_QUARTER, L_PAREN]
  10. [CALENDAR_YEAR, L_PAREN]
  11. [DAY_IN_MONTH, L_PAREN]
  12. [DAY_IN_WEEK, L_PAREN]
  13. [DAY_IN_YEAR, L_PAREN]
  14. [DAY_ONLY, L_PAREN]
  15. [FISCAL_MONTH, L_PAREN]
  16. [FISCAL_QUARTER, L_PAREN]
  17. [FISCAL_YEAR, L_PAREN]
  18. [HOUR_IN_DAY, L_PAREN]
  19. [WEEK_IN_MONTH, L_PAREN]
  20. [WEEK_IN_YEAR, L_PAREN]
  21. [FORMAT, L_PAREN]
  22. [toLabel, L_PAREN]
  23. [convertTimezone, L_PAREN]
  24. [convertCurrency, L_PAREN]
  25. [GROUPING, L_PAREN]
  26. [Identifier]
but found: ')'
at Object.parse (src/parser/parser.ts:704:11)
at Object.parseQuery (src/parser/visitor.ts:847:38)

If we remove the parenthesis from keySet(), then it works, i.e

let query = 'select id from account where id in :accountMap.keySet;//<< PARENTHESIS REMOVED
parseQuery(query,{allowApexBindVariables:true});

No error

Use Case

We are using this library in HappySoup.io to parse queries in apex classes to find where standard fields are used. This error would prevent us from parsing queries to see if a particular standard field is used.

Is there any way to allow for partial success? i.e if you can't parse the WHERE clause, at least we'd like to evaluate the SELECT one.

Thanks!

paustint commented 3 years ago

Thanks for the feedback - I like the idea about partial parsing and that would be useful for me as well.

I will look into it.

paustint commented 3 years ago

@pgonzaleznetwork - I am working on creating a release for the new version with support for partial query parsing.

Even though this query is valid, the apex bind variables are not fully supported - so previously this would have thrown an error.

SELECT Id, (SELECT Id FROM Contacts WHERE Id IN :contactMap.keySet()) FROM Account WHERE Id IN :accountMap.keySet()

parseQuery(soqlQuery, { allowApexBindVariables: true, ignoreParseErrors: true });

Output:

{
  "fields": [
    {
      "type": "Field",
      "field": "Id"
    },
    {
      "type": "FieldSubquery",
      "subquery": {
        "fields": [
          {
            "type": "Field",
            "field": "Id"
          }
        ],
        "relationshipName": "Contacts"
      }
    }
  ],
  "sObject": "Account"
}

I was considering adding some additional parser support for apex bind variables, like accountMap.keySet(), but then it turned out to be more problematic than I was expecting and as I thought about it, there are just too many variations that would need to be supported which would require a massive amount of work.

Example: WHERE Id IN :new Map<Id, SObject>([SELECT Id FROM account]).keySet()

For now, I am going to leave the existing parser logic for apex bind variables as-is