Skip to content

Commit

Permalink
Additional maintenance
Browse files Browse the repository at this point in the history
Added support to turn on or off apex bind variables - resolves #76
Added support for USING SCOPE
Added support for currency prefixed numbers in where clause expressions
Added two additional LiteralTypes to support currency prefixed numbers
Fixed bug with date N literals - not all items were working
cleaned up code comments
Added numerous additional test-cases based on SFDC documentation and minor bugfixes to support adjustments
  • Loading branch information
paustint committed Oct 2, 2019
1 parent 3431b11 commit a3d254b
Show file tree
Hide file tree
Showing 11 changed files with 761 additions and 1,344 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ NEW PARSER: ~2.25 seconds for 60K parses
#### General Changes

- The CLI was removed.
- The `parseQuery()` function no longer accepts `options` as a second parameter.
- The `parseQuery()` `options` have changed. The only option allowed is `allowApexBindVariables` with will allow parsing queries with apex variables.
- `rawValue` will always have a space between parameters `GROUPING(Id, BillingCountry)`
- Some `literalType` values may have differing case from prior versions, regardless of the data input.
- `TRUE`, `FALSE`, and all functions except those listed below will always be returned in uppercase, regardless of case of input.
Expand All @@ -47,6 +47,7 @@ NEW PARSER: ~2.25 seconds for 60K parses
- Added new available types for `DateLiteral` and `DateNLiteral`.
- A new `LiteralType` value was added for `APEX_BIND_VARIABLE`.
- When composing functions in a where clause or group by clause, the `rawValue` will be preferred (if exists) (no change here), but if rawValue is not provided, then the function will be composed using the `functionName` and `parameters`.
- A new `LiteralType` value was added for `INTEGER_WITH_CURRENCY_PREFIX` and `DECIMAL_WITH_CURRENCY_PREFIX`. e.x. `USD500.01`

#### Compose Query

Expand All @@ -55,6 +56,7 @@ NEW PARSER: ~2.25 seconds for 60K parses
1. `fn` property is has been deprecated (but still exists), you should now use `functionName` instead.
2. The `from` property has been removed for subqueries. The `relationshipName` is required to be populated to compose a subquery.
- On the FormatOptions interface `fieldMaxLineLen` was renamed to `fieldMaxLineLength`.
- Added support for `usingScope` - https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm?search_text=format()

```diff
export interface FormatOptions {
Expand Down Expand Up @@ -86,6 +88,7 @@ export interface FormatOptions {
- `name` was renamed to `functionName`.
- `parameter` was renamed to `parameters` and the type was changed to `(string | FunctionExp)[]` to support nested functions. This will ALWAYS be an array now even if there is only one parameter.
- `fn` was removed, as nested functionParameters are always stored as an entry in the `parameters` array.
- Added support for `usingScope` - https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm?search_text=format()

```diff
export interface Field {
Expand Down Expand Up @@ -125,6 +128,7 @@ export interface FieldSubquery {
export interface QueryBase {
fields: FieldType[];
sObjectAlias?: string;
+ usingScope?: string;
where?: WhereClause;
limit?: number;
offset?: number;
Expand All @@ -148,7 +152,7 @@ export interface Condition {
valueQuery?: Query;
- literalType?: LiteralType;
+ literalType?: LiteralType | LiteralType[];
dateLiteralVariable?: number;parsed
dateLiteralVariable?: number;
}

export interface GroupByClause {
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ isQueryValid('SELECT Id Foo FROM Baz'); // false

| Function | Description | Arguments |
| ------------ | ------------------------------------------------------ | ------------------------------------------ |
| parseQuery | Parse a SOQL query string into a Query data structure. | soql: Query |
| isQueryValid | Returns true if the query was able to be parsed. | soql: Query |
| parseQuery | Parse a SOQL query string into a Query data structure. | soql: Query<br> config?: ParseQueryConfig |
| isQueryValid | Returns true if the query was able to be parsed. | soql: Query<br> config?: ParseQueryConfig |
| composeQuery | Turn a Query object back into a SOQL statement | soql: Query<br> config?: SoqlComposeConfig |
| formatQuery | Format a SOQL query string. | soql: Query<br> config?: FormatOptions |

**ParseQueryConfig**

| Property | Type | Description | required | default |
| ---------------------- | ------- | --------------------------------------------------------------------------------------------- | -------- | ------- |
| allowApexBindVariables | boolean | Determines if apex variables are allowed in parsed query. Example: `WHERE Id IN :accountIds`. | FALSE | FALSE |

**SoqlComposeConfig**

| Property | Type | Description | required | default |
Expand Down Expand Up @@ -386,6 +392,8 @@ export type LiteralType =
| 'STRING'
| 'INTEGER'
| 'DECIMAL'
| 'INTEGER_WITH_CURRENCY_PREFIX'
| 'DECIMAL_WITH_CURRENCY_PREFIX'
| 'BOOLEAN'
| 'NULL'
| 'DATETIME'
Expand Down Expand Up @@ -492,6 +500,7 @@ export interface FieldTypeOfCondition {
export interface QueryBase {
fields: FieldType[];
sObjectAlias?: string;
usingScope?: string;
where?: WhereClause;
limit?: number;
offset?: number;
Expand Down
3 changes: 3 additions & 0 deletions src/api/api-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export type LiteralType =
| 'STRING'
| 'INTEGER'
| 'DECIMAL'
| 'INTEGER_WITH_CURRENCY_PREFIX'
| 'DECIMAL_WITH_CURRENCY_PREFIX'
| 'BOOLEAN'
| 'NULL'
| 'DATETIME'
Expand Down Expand Up @@ -115,6 +117,7 @@ export interface FieldTypeOfCondition {
export interface QueryBase {
fields: FieldType[];
sObjectAlias?: string;
usingScope?: string;
where?: WhereClause;
limit?: number;
offset?: number;
Expand Down
6 changes: 6 additions & 0 deletions src/composer/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ export class Compose {
}
this.log(output);

if (query.usingScope) {
output += this.formatter.formatClause('USING SCOPE');
output += ` ${query.usingScope}`;
this.log(output);
}

if (query.where) {
output += this.formatter.formatClause('WHERE');
output += ` ${this.parseWhereClause(query.where)}`;
Expand Down
9 changes: 8 additions & 1 deletion src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface WithIdentifier {
export interface SelectStatementContext {
selectClause: CstNode[];
fromClause: CstNode[];
usingScopeClause?: CstNode[];
whereClause?: CstNode[];
withClause?: CstNode[];
groupByClause?: CstNode[];
Expand Down Expand Up @@ -75,6 +76,10 @@ export interface SelectClauseTypeOfElseContext extends WithIdentifier {
field: IToken[];
}

export interface usingScopeClauseContext {
UsingScopeEnumeration: IToken[];
}

export interface WhereClauseContext {
whereClauseExpression: CstNode[];
}
Expand Down Expand Up @@ -193,14 +198,16 @@ export interface AtomicExpressionContext {
UnsignedInteger?: IToken[];
SignedInteger?: IToken[];
RealNumber?: IToken[];
CurrencyPrefixedDecimal?: IToken[];
CurrencyPrefixedInteger?: IToken[];
DateIdentifier?: IToken[];
DateTime?: IToken[];
date?: IToken[];
NULL?: IToken[];
StringIdentifier?: IToken[];
Identifier?: IToken[];
booleanValue?: CstNode[];
dateLiteral?: CstNode[];
DateLiteral?: IToken[];
dateNLiteral?: CstNode[];
arrayExpression?: CstNode[];
whereClauseSubqueryIdentifier?: CstNode[];
Expand Down
97 changes: 96 additions & 1 deletion src/parser/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export const IdentifierNotKeyword = createToken({
pattern: Lexer.NA,
});

export const UsingScopeEnumeration = createToken({
name: 'UsingScopeEnumeration',
pattern: Lexer.NA,
});

const identifierRegex = /[a-zA-Z][a-zA-Z0-9_.]*/y;

/**
Expand Down Expand Up @@ -708,7 +713,12 @@ export const DateTime = createToken({

export const DateToken = createToken({ name: 'DATE', pattern: /[0-9]{4}-[0-9]{2}-[0-9]{2}/, categories: [DateIdentifier] });

// export const RealNumber = createToken({ name: 'REAL_NUMBER', pattern: /(\-|\+)?\d+(\.\d+)?/, categories: [NumberIdentifier] });
export const CurrencyPrefixedDecimal = createToken({
name: 'CURRENCY_PREFIXED_DECIMAL',
pattern: /[a-zA-Z]{3}[0-9]+\.\d+/,
longer_alt: Identifier,
categories: [DecimalNumberIdentifier],
});
export const SignedDecimal = createToken({
name: 'SIGNED_DECIMAL',
pattern: /(\-|\+)[0-9]*\.\d+/,
Expand All @@ -719,6 +729,12 @@ export const UnsignedDecimal = createToken({
pattern: /[0-9]*\.\d+/,
categories: [NumberIdentifier, DecimalNumberIdentifier],
});
export const CurrencyPrefixedInteger = createToken({
name: 'CURRENCY_PREFIXED_INTEGER',
pattern: /[a-zA-Z]{3}[0-9]+/,
longer_alt: Identifier,
categories: [DecimalNumberIdentifier],
});
export const SignedInteger = createToken({
name: 'SIGNED_INTEGER',
pattern: /(\-|\+)[0-9]+/,
Expand All @@ -730,6 +746,73 @@ export const UnsignedInteger = createToken({
categories: [NumberIdentifier, IntegerNumberIdentifier],
});

// Using Scope enumeration values
// https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm?search_text=format()
// export const UsingScopeEnumeration = createToken({
// name: 'UsingScopeEnumeration',
// pattern: /DELEGATED|EVERYTHING|MINEANDMYGROUPS|MINE|MY_TERRITORY|MY_TEAM_TERRITORY|TEAM|ALLPRIVATE/i,
// longer_alt: Identifier,
// categories: [Identifier],
// start_chars_hint: ['A', 'D', 'E', 'M', 'T', 'a', 'd', 'e', 'm', 't'],
// });

export const Delegated = createToken({
name: 'Delegated',
pattern: /DELEGATED/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['D', 'd'],
});
export const Everything = createToken({
name: 'Everything',
pattern: /EVERYTHING/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['E', 'e'],
});
export const MineAndMyGroups = createToken({
name: 'MineAndMyGroups',
pattern: /MINEANDMYGROUPS/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['M', 'm'],
});
export const Mine = createToken({
name: 'Mine',
pattern: /MINE/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['M', 'm'],
});
export const MyTerritory = createToken({
name: 'MyTerritory',
pattern: /MY_TERRITORY/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['M', 'm'],
});
export const MyTeamTerritory = createToken({
name: 'MyTeamTerritory',
pattern: /MY_TEAM_TERRITORY/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['M', 'm'],
});
export const Team = createToken({
name: 'Team',
pattern: /TEAM/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['T', 't'],
});
export const AllPrivate = createToken({
name: 'AllPrivate',
pattern: /ALLPRIVATE/i,
longer_alt: Identifier,
categories: [UsingScopeEnumeration, Identifier],
start_chars_hint: ['A', 'a'],
});

export const allTokens = [
// we place WhiteSpace first as it is very common thus it will speed up the lexer.
WhiteSpace,
Expand Down Expand Up @@ -764,6 +847,16 @@ export const allTokens = [

Update,

// https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm?search_text=format()
Delegated,
Everything,
MineAndMyGroups,
Mine,
MyTerritory,
MyTeamTerritory,
Team,
AllPrivate, // https://help.salesforce.com/articleView?id=000334863&language=en_US&type=1&mode=1

AboveOrBelow,
Above,
At,
Expand Down Expand Up @@ -858,6 +951,8 @@ export const allTokens = [
Not,

// The Identifier must appear after the keywords because all keywords are valid identifiers.
CurrencyPrefixedDecimal,
CurrencyPrefixedInteger,
StringIdentifier,
Identifier,
DateTime,
Expand Down
Loading

0 comments on commit a3d254b

Please sign in to comment.