Skip to content

Commit ac2d098

Browse files
committed
Add support for FIELDS() #136
Added Fields() support updated test-cases resolves #136
1 parent 1e54196 commit ac2d098

File tree

7 files changed

+143
-2
lines changed

7 files changed

+143
-2
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 3.0.3
4+
5+
March 27, 2021
6+
7+
1. Added support for the `FIELDS()` function
8+
39
## 3.0.2
410

511
March 6, 2021

src/models.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ export interface FieldFunctionContext {
177177
fn: IToken[];
178178
}
179179

180+
export interface FieldsFunctionContext {
181+
fn: IToken[];
182+
params: IToken[];
183+
}
184+
180185
export interface LocationFunctionContext {
181186
location1: IToken[];
182187
location2: IToken[] | CstNode[];

src/parser/lexer.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ export const LocationFunction = createToken({
2929
pattern: Lexer.NA,
3030
});
3131

32+
export const FieldsFunction = createToken({
33+
name: 'FieldsFunction',
34+
pattern: Lexer.NA,
35+
});
36+
37+
export const FieldsFunctionParamIdentifier = createToken({
38+
name: 'FieldsFunctionParamIdentifier',
39+
pattern: Lexer.NA,
40+
});
41+
3242
export const OtherFunction = createToken({
3343
name: 'OtherFunction',
3444
pattern: Lexer.NA,
@@ -393,6 +403,14 @@ export const Geolocation = createToken({
393403
categories: [LocationFunction, Identifier],
394404
});
395405

406+
// FIELDS FUNCTIONS
407+
export const Fields = createToken({
408+
name: 'FIELDS',
409+
pattern: /FIELDS/i,
410+
longer_alt: Identifier,
411+
categories: [FieldsFunction, Identifier],
412+
});
413+
396414
// OTHER FUNCTIONS
397415
export const Format = createToken({
398416
name: 'FORMAT',
@@ -425,6 +443,28 @@ export const Grouping = createToken({
425443
categories: [OtherFunction, Identifier],
426444
});
427445

446+
// FIELDS() PARAMETERS
447+
export const All = createToken({
448+
name: 'ALL',
449+
pattern: /ALL/i,
450+
longer_alt: Identifier,
451+
categories: [FieldsFunctionParamIdentifier, Identifier],
452+
});
453+
454+
export const Custom = createToken({
455+
name: 'CUSTOM',
456+
pattern: /CUSTOM/i,
457+
longer_alt: Identifier,
458+
categories: [FieldsFunctionParamIdentifier, Identifier],
459+
});
460+
461+
export const Standard = createToken({
462+
name: 'STANDARD',
463+
pattern: /STANDARD/i,
464+
longer_alt: Identifier,
465+
categories: [FieldsFunctionParamIdentifier, Identifier],
466+
});
467+
428468
// DATE LITERALS
429469
export const Yesterday = createToken({
430470
name: 'YESTERDAY',
@@ -908,6 +948,7 @@ export const allTokens = [
908948
Sum,
909949
Distance,
910950
Geolocation,
951+
Fields,
911952
Format,
912953
Tolabel,
913954
ConvertTimeZone,
@@ -959,6 +1000,10 @@ export const allTokens = [
9591000

9601001
GeolocationUnit,
9611002

1003+
All,
1004+
Custom,
1005+
Standard,
1006+
9621007
In,
9631008
NotIn,
9641009
For,

src/parser/parser.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export class SoqlParser extends CstParser {
131131
{ ALT: () => this.SUBRULE(this.dateFunction, { LABEL: 'fn' }) },
132132
{ ALT: () => this.SUBRULE(this.aggregateFunction, { LABEL: 'fn', ARGS: [true] }) },
133133
{ ALT: () => this.SUBRULE(this.locationFunction, { LABEL: 'fn' }) },
134+
{ ALT: () => this.SUBRULE(this.fieldsFunction, { LABEL: 'fn' }) },
134135
{ ALT: () => this.SUBRULE(this.otherFunction, { LABEL: 'fn' }) },
135136
]),
136137
);
@@ -412,7 +413,7 @@ export class SoqlParser extends CstParser {
412413
this.SUBRULE(this.functionExpression);
413414
});
414415

415-
private aggregateFunction = this.RULE('aggregateFunction', allowAlias => {
416+
private aggregateFunction = this.RULE('aggregateFunction', () => {
416417
this.OR(
417418
this.$_aggregateFunction ||
418419
(this.$_aggregateFunction = [
@@ -427,6 +428,13 @@ export class SoqlParser extends CstParser {
427428
this.SUBRULE(this.functionExpression, { ARGS: [true] });
428429
});
429430

431+
private fieldsFunction = this.RULE('fieldsFunction', () => {
432+
this.CONSUME(lexer.Fields, { LABEL: 'fn' });
433+
this.CONSUME(lexer.LParen);
434+
this.CONSUME(lexer.FieldsFunctionParamIdentifier, { LABEL: 'params' });
435+
this.CONSUME(lexer.RParen);
436+
});
437+
430438
private otherFunction = this.RULE('otherFunction', () => {
431439
this.OR(
432440
this.$_otherFunction ||

src/parser/visitor.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
ExpressionOperatorContext,
4646
ExpressionTree,
4747
FieldFunctionContext,
48+
FieldsFunctionContext,
4849
FromClauseContext,
4950
FunctionExpressionContext,
5051
GeoLocationFunctionContext,
@@ -550,6 +551,22 @@ class SOQLVisitor extends BaseSoqlVisitor {
550551
return this.$_getFieldFunction(ctx, true, options.includeType);
551552
}
552553

554+
fieldsFunction(ctx: FieldsFunctionContext, options: { includeType: boolean } = { includeType: true }) {
555+
let output: any = {};
556+
if (options.includeType) {
557+
output.type = 'FieldFunctionExpression';
558+
}
559+
output = {
560+
...output,
561+
...{
562+
functionName: 'FIELDS',
563+
parameters: [ctx.params[0].image],
564+
},
565+
};
566+
567+
output.rawValue = `FIELDS(${output.parameters[0]})`;
568+
return output;
569+
}
553570
otherFunction(ctx: FieldFunctionContext, options: { includeType: boolean } = { includeType: true }) {
554571
return this.$_getFieldFunction(ctx, false, options.includeType);
555572
}

test/test-cases.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,66 @@ export const testCases: TestCase[] = [
21352135
],
21362136
},
21372137
},
2138+
{
2139+
testCase: 109,
2140+
soql: `SELECT FIELDS(ALL) FROM Account`,
2141+
output: {
2142+
fields: [
2143+
{
2144+
type: 'FieldFunctionExpression',
2145+
functionName: 'FIELDS',
2146+
parameters: ['ALL'],
2147+
rawValue: 'FIELDS(ALL)',
2148+
},
2149+
],
2150+
sObject: 'Account',
2151+
},
2152+
},
2153+
{
2154+
testCase: 110,
2155+
soql: `SELECT FIELDS(CUSTOM), FIELDS(STANDARD) FROM Account`,
2156+
output: {
2157+
fields: [
2158+
{
2159+
type: 'FieldFunctionExpression',
2160+
functionName: 'FIELDS',
2161+
parameters: ['CUSTOM'],
2162+
rawValue: 'FIELDS(CUSTOM)',
2163+
},
2164+
{
2165+
type: 'FieldFunctionExpression',
2166+
functionName: 'FIELDS',
2167+
parameters: ['STANDARD'],
2168+
rawValue: 'FIELDS(STANDARD)',
2169+
},
2170+
],
2171+
sObject: 'Account',
2172+
},
2173+
},
2174+
{
2175+
testCase: 110,
2176+
soql: `SELECT Id, (SELECT FIELDS(ALL) FROM Contacts) FROM Account`,
2177+
output: {
2178+
fields: [
2179+
{ type: 'Field', field: 'Id' },
2180+
{
2181+
type: 'FieldSubquery',
2182+
subquery: {
2183+
fields: [
2184+
{
2185+
type: 'FieldFunctionExpression',
2186+
functionName: 'FIELDS',
2187+
parameters: ['ALL'],
2188+
rawValue: 'FIELDS(ALL)',
2189+
},
2190+
],
2191+
relationshipName: 'Contacts',
2192+
},
2193+
},
2194+
],
2195+
sObject: 'Account',
2196+
},
2197+
},
21382198
];
21392199

21402200
export default testCases;

test/test.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const replacements = [{ matching: / last /i, replace: ' LAST ' }];
1212
// Uncomment these to easily test one specific query - useful for troubleshooting/bugfixing
1313

1414
// describe.only('parse queries', () => {
15-
// const testCase = testCases.find(tc => tc.testCase === 87);
15+
// const testCase = testCases.find(tc => tc.testCase === 109);
1616
// it(`should correctly parse test case ${testCase.testCase} - ${testCase.soql}`, () => {
1717
// const soqlQuery = parseQuery(testCase.soql, testCase.options);
1818
// console.log(soqlQuery);

0 commit comments

Comments
 (0)