Skip to content

Commit 48dc76d

Browse files
committed
Add support for partial parse and compose #182
Added better support for partial queries. resolves #182
1 parent b130199 commit 48dc76d

File tree

13 files changed

+468
-98
lines changed

13 files changed

+468
-98
lines changed

CHANGELOG.md

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

3+
## 4.4
4+
5+
March 11, 2022
6+
7+
- Add support for partial parse and compose #182
8+
- Added support for parsing and composing partial queries. When parsing, the new option `allowPartialQuery` enables this functionality.
9+
- Added a third argument for `formatQuery`, allowing `ParseQueryConfig` options to be provided.
10+
- Some types on the `Query` interface were made optional to support partial queries
11+
- Updated CLI to include additional commands
12+
313
## 4.3
414

515
September 19, 2021

README.md

Lines changed: 111 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
3. Validate a query to check if the syntax is valid.
1616
1. _Even if a query is returned as valid, it might still be invalid based on your Salesforce configuration_
1717

18-
This library uses [Chevrotain](https://github.com/SAP/chevrotain) to parse queries. Prior to version 2.0.0, [antlr4](https://github.com/antlr/antlr4) was used.
19-
2018
Migrating from version 1 to version 2? [Check out the changelog](CHANGELOG.md#200) for a full list of changes.
2119

2220
Migrating from version 2 to version 3? [Check out the changelog](CHANGELOG.md#300) for a full list of changes.
@@ -58,7 +56,7 @@ isQueryValid('SELECT Id Foo FROM Baz'); // false
5856

5957
**General Utility**
6058

61-
Many of hte utility functions are provided to easily determine the shape of specific data since there are many variations. If you are using Typescript in strict mode, you can use these to narrow types with if statements.
59+
Many of hte utility functions are provided to easily determine the shape of specific data since there are many variants. If you are using Typescript in strict mode, you can use these to narrow your types.
6260

6361
| Function | Description | Arguments |
6462
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
@@ -86,6 +84,7 @@ Many of hte utility functions are provided to easily determine the shape of spec
8684
| Property | Type | Description | required | default |
8785
| ---------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- |
8886
| allowApexBindVariables | boolean | Determines if apex variables are allowed in parsed query. Example: `WHERE Id IN :accountIds`. Only simple Apex is supported. Function calls are not supported. (e.x. `accountMap.keyset()` is not supported) | FALSE | FALSE |
87+
| allowPartialQuery | boolean | If provided, you can provide an incomplete soql query. This is useful if you need to parse WHERE clauses, for example. Subqueries are required to be valid. | FALSE | FALSE |
8988
| ignoreParseErrors | boolean | If set to true, then queries with partially invalid syntax will still be parsed, but any clauses with invalid parts will be omitted. The SELECT clause and FROM clause must always be valid, but all other clauses can contain invalid parts. | FALSE | FALSE |
9089
| logErrors | boolean | If true, parsing and lexing errors will be logged to the console. | FALSE | FALSE |
9190

@@ -117,7 +116,6 @@ Parsing a SOQL query can be completed by calling `parseQuery(soqlQueryString)`.
117116

118117
```typescript
119118
import { parseQuery } from 'soql-parser-js';
120-
// var soqlParserJs = require('soql-parser-js'); // node's require format - usage: soqlParserJs.parseQuery()
121119

122120
const soql = `
123121
SELECT UserId, COUNT(Id)
@@ -127,8 +125,6 @@ const soql = `
127125
GROUP BY UserId
128126
`;
129127

130-
const soqlQuery = parseQuery(soql);
131-
132128
console.log(JSON.stringify(soqlQuery, null, 2));
133129
```
134130

@@ -176,6 +172,54 @@ console.log(JSON.stringify(soqlQuery, null, 2));
176172

177173
</details>
178174

175+
### Parsing a partial query
176+
177+
Added support for `allowPartialQuery` in version `4.4.0`
178+
179+
```typescript
180+
import { parseQuery } from 'soql-parser-js';
181+
182+
const soql = `
183+
WHERE LoginTime > 2010-09-20T22:16:30.000Z
184+
AND LoginTime < 2010-09-21T22:16:30.000Z
185+
GROUP BY UserId
186+
`;
187+
188+
const soqlQuery = parseQuery(soql, { allowPartialQuery: true });
189+
190+
console.log(JSON.stringify(soqlQuery, null, 2));
191+
```
192+
193+
<details>
194+
<summary><b>Results (click to show)</b></summary>
195+
196+
```json
197+
{
198+
"where": {
199+
"left": {
200+
"field": "LoginTime",
201+
"operator": ">",
202+
"value": "2010-09-20T22:16:30.000Z",
203+
"literalType": "DATETIME"
204+
},
205+
"operator": "AND",
206+
"right": {
207+
"left": {
208+
"field": "LoginTime",
209+
"operator": "<",
210+
"value": "2010-09-21T22:16:30.000Z",
211+
"literalType": "DATETIME"
212+
}
213+
}
214+
},
215+
"groupBy": {
216+
"field": "UserId"
217+
}
218+
}
219+
```
220+
221+
</details>
222+
179223
### Validating Queries
180224

181225
```typescript
@@ -278,6 +322,41 @@ LIMIT 150
278322

279323
### Composing a partial query
280324

325+
Starting in version `4.4`, compose will not fail if there are missing `SELECT` and `FROM` clauses in your query.
326+
327+
Partial compose support it supported without any additional steps.
328+
329+
```typescript
330+
import { Compose, parseQuery } from 'soql-parser-js';
331+
332+
const soql = `WHERE Name LIKE 'A%' AND MailingCity = 'California`;
333+
const parsedQuery = parseQuery(soql, { allowPartialQuery: true });
334+
335+
// Results of Parsed Query:
336+
/**
337+
{
338+
where: {
339+
left: { field: 'Name', operator: 'LIKE', value: "'A%'", literalType: 'STRING' },
340+
operator: 'AND',
341+
right: { left: { field: 'MailingCity', operator: '=', value: "'California'", literalType: 'STRING' } },
342+
},
343+
}
344+
*/
345+
346+
const composedQuery = composeQuery(soqlQuery, { format: true });
347+
348+
console.log(composedQuery);
349+
```
350+
351+
**Results**
352+
353+
```sql
354+
WHERE Name LIKE 'A%' AND MailingCity = 'California
355+
```
356+
357+
<details>
358+
<summary><b>See the alternate way to compose partial queries by calling the Compose class directly</b></summary>
359+
281360
If you need to compose just a part of a query instead of the entire query, you can create an instance of the Compose class directly.
282361
283362
For example, if you just need the `WHERE` clause from a query as a string, you can do the following:
@@ -331,6 +410,8 @@ parseOrderBy(orderBy: OrderByClause | OrderByClause[]): string;
331410
parseWithDataCategory(withDataCategory: WithDataCategoryClause): string;
332411
```
333412
413+
</details>
414+
334415
## Format Query
335416
336417
This function is provided as a convenience and just calls parse and compose.
@@ -418,11 +499,11 @@ WHERE Name LIKE 'a%'
418499
419500
## CLI
420501
421-
If you install globally, you can use the cli
502+
Install globally or use `npx` to interact with the cli.
422503
423504
### Available Commands
424505
425-
- `soql-parser-js --help`
506+
- `soql-parser-js --help` (or using `npx`: `npx soql-parser-js --help`)
426507
- `soql-parser-js parse --help`
427508
- `soql-parser-js compose --help`
428509
- `soql-parser-js format --help`
@@ -431,29 +512,29 @@ If you install globally, you can use the cli
431512
432513
#### Parse
433514
434-
`soql-parser-js parse "SELECT Id FROM Account"`
515+
`npx soql-parser-js parse "SELECT Id FROM Account"`
435516
436517
```bash
437518
{"fields":[{"type":"Field","field":"Id"}],"sObject":"Account"}
438519
```
439520
440521
#### Compose
441522
442-
`soql-parser-js compose "{\"fields\":[{\"type\":\"Field\",\"field\":\"Id\"}],\"sObject\":\"Account\"}"`
523+
`npx soql-parser-js compose "{\"fields\":[{\"type\":\"Field\",\"field\":\"Id\"}],\"sObject\":\"Account\"}"`
443524
444525
```bash
445526
SELECT Id FROM Account
446527
```
447528
448-
`soql-parser-js compose "{\"fields\":[{\"type\":\"Field\",\"field\":\"Id\"}],\"sObject\":\"Account\"}" --json` or -j
529+
`npx soql-parser-js compose "{\"fields\":[{\"type\":\"Field\",\"field\":\"Id\"}],\"sObject\":\"Account\"}" --json` or -j
449530
450531
```json
451532
{ "query": "SELECT Id FROM Account" }
452533
```
453534
454535
#### Format
455536
456-
`soql-parser-js format "SELECT Name, COUNT(Id) FROM Account GROUP BY Name HAVING COUNT(Id) > 1"`
537+
`npx soql-parser-js format "SELECT Name, COUNT(Id) FROM Account GROUP BY Name HAVING COUNT(Id) > 1"`
457538
458539
```bash
459540
SELECT Name, COUNT(Id)
@@ -462,35 +543,35 @@ GROUP BY Name
462543
HAVING COUNT(Id) > 1
463544
```
464545
465-
`soql-parser-js format "SELECT Name, COUNT(Id) FROM Account GROUP BY Name HAVING COUNT(Id) > 1 -j`
546+
`npx soql-parser-js format "SELECT Name, COUNT(Id) FROM Account GROUP BY Name HAVING COUNT(Id) > 1 -j`
466547
467548
```json
468549
{ "query": "SELECT Name, COUNT(Id)\nFROM Account\nGROUP BY Name\nHAVING COUNT(Id) > 1" }
469550
```
470551
471552
#### Is Valid
472553
473-
`soql-parser-js valid "SELECT Id FROM Account"`
554+
`npx soql-parser-js valid "SELECT Id FROM Account"`
474555
475556
```bash
476557
true
477558
```
478559
479-
`soql-parser-js valid "SELECT Id invalid FROM Account"`
560+
`npx soql-parser-js valid "SELECT Id invalid FROM Account"`
480561
481562
ℹ️ this returns an exit code of 1
482563
483564
```bash
484565
false
485566
```
486567
487-
`soql-parser-js valid "SELECT Id FROM Account -j`
568+
`npx soql-parser-js valid "SELECT Id FROM Account" -j`
488569
489570
```json
490571
{ "isValid": true }
491572
```
492573
493-
`soql-parser-js valid "SELECT Id invalid invalid FROM Account -j`
574+
`npx soql-parser-js valid "SELECT Id invalid invalid FROM Account" -j`
494575
495576
ℹ️ this returns an exit code of 0
496577
@@ -509,19 +590,20 @@ Options:
509590
-h, --help output usage information
510591
511592
Commands:
512-
parse [options] <query>
593+
parse [options] <sql>
513594
compose [options] <query>
514-
format [options] <query>
515-
valid <query>
595+
format [options] <sql>
596+
valid <sql>
516597
```
517598
518599
`soql-parser-js parse --help`
519600
520601
```bash
521-
Usage: parse [options] <query>
602+
Usage: parse [options] <sql>
522603
523604
Options:
524605
-a, --allow-apex allow apex bind variables
606+
-p, --allow-partial allow partial queries
525607
-i, --ignore-errors ignore parse errors, return as much of query as possible
526608
-h, --help output usage information
527609
```
@@ -544,9 +626,11 @@ Options:
544626
`soql-parser-js format --help`
545627
546628
```bash
547-
Usage: format [options] <query>
629+
Usage: format [options] <sql>
548630
549631
Options:
632+
-a, --allow-apex allow apex bind variables
633+
-p, --allow-partial allow partial queries
550634
-i --indent <chars> number of tab characters to indent (default: 1)
551635
-m --line-length <chars> max number of characters per lins (default: 60)
552636
-s --subquery-parens-new-line subquery parens on own line
@@ -558,9 +642,11 @@ Options:
558642
`soql-parser-js valid --help`
559643
560644
```bash
561-
Usage: valid [options] <query>
645+
Usage: valid [options] <sql>
562646
563647
Options:
648+
-a, --allow-apex allow apex bind variables
649+
-p, --allow-partial allow partial queries
564650
-j, --json output as JSON
565651
-h, --help output usage information
566652
```
@@ -699,7 +785,7 @@ export interface FieldTypeOfCondition {
699785
}
700786
701787
export interface QueryBase {
702-
fields: FieldType[];
788+
fields?: FieldType[];
703789
sObjectAlias?: string;
704790
usingScope?: string;
705791
where?: WhereClause;
@@ -714,7 +800,7 @@ export interface QueryBase {
714800
}
715801
716802
export interface Query extends QueryBase {
717-
sObject: string;
803+
sObject?: string;
718804
}
719805
720806
export interface Subquery extends QueryBase {

0 commit comments

Comments
 (0)