Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 3.0.2

March 6, 2021

1. Date functions were not properly parsed when used in order by clauses. (#139)
2. Modified names of functions / types (internal)
3. Removed improper import of `isString` from node utils

Changes also released to 2.5.6

## 3.0.1

January 7, 20201
Expand Down
6 changes: 3 additions & 3 deletions docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"gh-pages": "^2.2.0",
"highlight.js": "^9.18.1",
"prismjs": "^1.19.0",
"soql-parser-js": "^3.0.1",
"soql-parser-js": "^3.0.2",
"tailwindcss": "^1.2.0"
},
"devDependencies": {
Expand Down
5 changes: 4 additions & 1 deletion docs/src/resources/sample-queries-json.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,8 @@
"SELECT ProductCode FROM Product2 GROUP BY ProductCode HAVING COUNT(Id) > 1 ORDER BY COUNT(Id) DESC",
"SELECT AnnualRevenue FROM Account WHERE NOT (AnnualRevenue > 0 AND AnnualRevenue < 200000)",
"SELECT AnnualRevenue FROM Account WHERE ((NOT AnnualRevenue > 0) AND AnnualRevenue < 200000)",
"SELECT Id FROM Account WHERE NOT Id = '2'"
"SELECT Id FROM Account WHERE NOT Id = '2'",
"SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate)",
"SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate) DESC NULLS FIRST",
"SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate) DESC NULLS LAST, SUM(amount) ASC NULLS LAST"
]
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "soql-parser-js",
"version": "3.0.1",
"version": "3.0.2",
"description": "Salesforce.com SOQL parser and composer",
"main": "dist/index.js",
"module": "dist_esm/index.js",
Expand Down
7 changes: 4 additions & 3 deletions src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,16 @@ export interface OrderByExpressionContext extends WithIdentifier {
nulls?: IToken[];
}

export interface OrderByFunctionExpressionContext extends WithIdentifier {
export interface OrderByGroupingFunctionExpressionContext extends WithIdentifier {
fn: IToken[];
order?: IToken[];
nulls?: IToken[];
}

export interface orderByAggregateOrLocationExpressionContext {
locationFunction?: CstNode[];
export interface OrderBySpecialFunctionExpressionContext {
aggregateFunction?: CstNode[];
dateFunction?: CstNode[];
locationFunction?: CstNode[];
order?: IToken[];
nulls?: IToken[];
}
Expand Down
14 changes: 9 additions & 5 deletions src/parser/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ export class SoqlParser extends CstParser {
SEP: lexer.Comma,
DEF: () => {
this.OR([
{ ALT: () => this.SUBRULE(this.orderByFunctionExpression, { LABEL: 'orderByExpressionOrFn' }) },
{ ALT: () => this.SUBRULE(this.orderByAggregateOrLocationExpression, { LABEL: 'orderByExpressionOrFn' }) },
{ ALT: () => this.SUBRULE(this.orderByGroupingFunctionExpression, { LABEL: 'orderByExpressionOrFn' }) },
{ ALT: () => this.SUBRULE(this.orderBySpecialFunctionExpression, { LABEL: 'orderByExpressionOrFn' }) },
{ ALT: () => this.SUBRULE(this.orderByExpression, { LABEL: 'orderByExpressionOrFn' }) },
]);
},
Expand All @@ -358,13 +358,17 @@ export class SoqlParser extends CstParser {
});
});

private orderByFunctionExpression = this.RULE('orderByFunctionExpression', () => {
private orderByGroupingFunctionExpression = this.RULE('orderByGroupingFunctionExpression', () => {
this.CONSUME(lexer.Grouping, { LABEL: 'fn' });
this.SUBRULE(this.functionExpression);
});

private orderByAggregateOrLocationExpression = this.RULE('orderByAggregateOrLocationExpression', () => {
this.OR([{ ALT: () => this.SUBRULE(this.locationFunction) }, { ALT: () => this.SUBRULE(this.aggregateFunction) }]);
private orderBySpecialFunctionExpression = this.RULE('orderBySpecialFunctionExpression', () => {
this.OR([
{ ALT: () => this.SUBRULE(this.aggregateFunction) },
{ ALT: () => this.SUBRULE(this.dateFunction) },
{ ALT: () => this.SUBRULE(this.locationFunction) },
]);
this.OPTION(() => {
this.OR1([{ ALT: () => this.CONSUME(lexer.Asc, { LABEL: 'order' }) }, { ALT: () => this.CONSUME(lexer.Desc, { LABEL: 'order' }) }]);
});
Expand Down
55 changes: 28 additions & 27 deletions src/parser/visitor.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import { IToken } from 'chevrotain';
import {
Condition,
ConditionWithValueQuery,
DateLiteral,
DateNLiteral,
Field,
FieldFunctionExpression,
FieldRelationship,
FieldRelationshipWithAlias,
FieldSubquery,
FieldType,
FieldTypeOf,
FieldTypeOfCondition,
FieldWithAlias,
FunctionExp,
GroupByClause,
GroupByFieldClause,
GroupByFnClause,
HavingClause,
HavingClauseWithRightCondition,
LiteralType,
NullsOrder,
OrderByClause,
OrderByCriterion,
OrderByFnClause,
Query,
Subquery,
WhereClause,
WithDataCategoryCondition,
Field,
FieldRelationshipWithAlias,
FieldWithAlias,
WhereClauseWithRightCondition,
GroupByFnClause,
GroupByFieldClause,
HavingClauseWithRightCondition,
OrderByFnClause,
ConditionWithValueQuery,
ValueFunctionCondition,
ValueCondition,
ValueFunctionCondition,
ValueQueryCondition,
ValueWithDateNLiteralCondition,
WhereClause,
WhereClauseWithRightCondition,
WithDataCategoryCondition,
} from '../api/api-models';
import {
ApexBindVariableExpressionContext,
Expand All @@ -46,15 +47,20 @@ import {
FieldFunctionContext,
FromClauseContext,
FunctionExpressionContext,
GeoLocationFunctionContext,
GroupByClauseContext,
GroupByFieldListContext,
HavingClauseContext,
LiteralTypeWithSubquery,
LocationFunctionContext,
OperatorContext,
OrderByClauseContext,
OrderByExpressionContext,
OrderByFunctionExpressionContext,
OrderByGroupingFunctionExpressionContext,
OrderBySpecialFunctionExpressionContext,
SelectClauseContext,
SelectClauseFunctionIdentifierContext,
SelectClauseIdentifierContext,
SelectClauseSubqueryIdentifierContext,
SelectClauseTypeOfContext,
SelectClauseTypeOfElseContext,
Expand All @@ -66,16 +72,9 @@ import {
WhereClauseSubqueryContext,
WithClauseContext,
WithDateCategoryContext,
LocationFunctionContext,
GeoLocationFunctionContext,
orderByAggregateOrLocationExpressionContext,
GroupByFieldListContext,
SelectClauseIdentifierContext,
} from '../models';
import { isSubqueryFromFlag, isToken } from '../utils';
import { isString, isSubqueryFromFlag, isToken } from '../utils';
import { parse, ParseQueryConfig, SoqlParser } from './parser';
import { isString, isNull } from 'util';
import { IToken } from 'chevrotain';

const parser = new SoqlParser();

Expand Down Expand Up @@ -481,7 +480,7 @@ class SOQLVisitor extends BaseSoqlVisitor {
return orderByClause;
}

orderByFunctionExpression(ctx: OrderByFunctionExpressionContext): OrderByClause {
orderByGroupingFunctionExpression(ctx: OrderByGroupingFunctionExpressionContext): OrderByClause {
const orderByClause: OrderByClause = {
fn: this.$_getFieldFunction(ctx, false, false),
};
Expand All @@ -494,12 +493,14 @@ class SOQLVisitor extends BaseSoqlVisitor {
return orderByClause;
}

orderByAggregateOrLocationExpression(ctx: orderByAggregateOrLocationExpressionContext): OrderByClause {
orderBySpecialFunctionExpression(ctx: OrderBySpecialFunctionExpressionContext): OrderByClause {
const orderByClause: Partial<OrderByClause> = {};
if (ctx.locationFunction) {
(orderByClause as OrderByFnClause).fn = this.visit(ctx.locationFunction, { includeType: false });
} else {
if (ctx.aggregateFunction) {
(orderByClause as OrderByFnClause).fn = this.visit(ctx.aggregateFunction, { includeType: false });
} else if (ctx.dateFunction) {
(orderByClause as OrderByFnClause).fn = this.visit(ctx.dateFunction, { includeType: false });
} else if (ctx.locationFunction) {
(orderByClause as OrderByFnClause).fn = this.visit(ctx.locationFunction, { includeType: false });
}
if (ctx.order && ctx.order[0]) {
orderByClause.order = ctx.order[0].tokenType.name as OrderByCriterion;
Expand Down Expand Up @@ -736,7 +737,7 @@ class SOQLVisitor extends BaseSoqlVisitor {
const arrayValues: ArrayExpressionWithType[] = this.visit(ctx.arrayExpression);
value = arrayValues.map((item: any) => item.value);
const dateLiteralTemp = arrayValues.map((item: any) => item.variable || null);
const hasDateLiterals = dateLiteralTemp.some(item => !isNull(item));
const hasDateLiterals = dateLiteralTemp.some(item => item !== null);
if (new Set(arrayValues.map((item: any) => item.type)).size === 1) {
literalType = this.$_getLiteralTypeFromTokenType(arrayValues[0].type);
} else {
Expand Down
123 changes: 123 additions & 0 deletions test/test-cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2012,6 +2012,129 @@ export const testCases: TestCase[] = [
},
},
},
{
testCase: 106,
soql: `SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate)`,
output: {
fields: [
{
type: 'FieldFunctionExpression',
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
{
type: 'FieldFunctionExpression',
functionName: 'SUM',
parameters: ['amount'],
isAggregateFn: true,
rawValue: 'SUM(amount)',
},
],
sObject: 'Opportunity',
groupBy: {
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
},
orderBy: {
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
},
},
},
{
testCase: 107,
soql: `SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate) DESC NULLS FIRST`,
output: {
fields: [
{
type: 'FieldFunctionExpression',
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
{
type: 'FieldFunctionExpression',
functionName: 'SUM',
parameters: ['amount'],
isAggregateFn: true,
rawValue: 'SUM(amount)',
},
],
sObject: 'Opportunity',
groupBy: {
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
},
orderBy: {
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
order: 'DESC',
nulls: 'FIRST',
},
},
},
{
testCase: 108,
soql: `SELECT WEEK_IN_YEAR(CloseDate), SUM(amount) FROM Opportunity GROUP BY WEEK_IN_YEAR(CloseDate) ORDER BY WEEK_IN_YEAR(CloseDate) DESC NULLS LAST, SUM(amount) ASC NULLS LAST`,
output: {
fields: [
{
type: 'FieldFunctionExpression',
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
{
type: 'FieldFunctionExpression',
functionName: 'SUM',
parameters: ['amount'],
isAggregateFn: true,
rawValue: 'SUM(amount)',
},
],
sObject: 'Opportunity',
groupBy: {
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
},
orderBy: [
{
fn: {
functionName: 'WEEK_IN_YEAR',
parameters: ['CloseDate'],
rawValue: 'WEEK_IN_YEAR(CloseDate)',
},
order: 'DESC',
nulls: 'LAST',
},
{
fn: {
functionName: 'SUM',
parameters: ['amount'],
rawValue: 'SUM(amount)',
},
order: 'ASC',
nulls: 'LAST',
},
],
},
},
];

export default testCases;