Skip to content

Commit 37440cf

Browse files
committed
feat: improve other sql error msg
1 parent eebcc93 commit 37440cf

16 files changed

+525
-8
lines changed

src/parser/common/basicSQL.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { SQLParserBase } from '../../lib/SQLParserBase';
1515
import { findCaretTokenIndex } from './findCaretTokenIndex';
1616
import { ctxToText, tokenToWord, WordRange, TextSlice } from './textAndWord';
1717
import { CaretPosition, Suggestions, SyntaxSuggestion } from './types';
18-
import { ParseError, ErrorListener, ParseErrorListener } from './parseErrorListener';
18+
import { ParseError, ErrorListener } from './parseErrorListener';
1919
import { ErrorStrategy } from './errorStrategy';
2020
import type { SplitListener } from './splitListener';
2121
import type { EntityCollector } from './entityCollector';
@@ -82,7 +82,7 @@ export abstract class BasicSQL<
8282
/**
8383
* Get a new errorListener instance.
8484
*/
85-
protected abstract createErrorListener(errorListener: ErrorListener<any>): ANTLRErrorListener;
85+
protected abstract createErrorListener(errorListener: ErrorListener): ANTLRErrorListener;
8686

8787
/**
8888
* Get a new entityCollector instance.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { CodeCompletionCore } from 'antlr4-c3';
2+
import { ErrorListener, ParseErrorListener } from '../common/parseErrorListener';
3+
import { Parser, Token } from 'antlr4ng';
4+
import { FlinkSqlParser } from '../../lib/flink/FlinkSqlParser';
5+
6+
export class FlinkErrorListener extends ParseErrorListener {
7+
private preferredRules: Set<number>;
8+
9+
private objectNames: Map<number, string> = new Map([
10+
[FlinkSqlParser.RULE_catalogPath, 'catalog'],
11+
[FlinkSqlParser.RULE_databasePath, 'database'],
12+
[FlinkSqlParser.RULE_databasePathCreate, 'database'],
13+
[FlinkSqlParser.RULE_tablePath, 'table'],
14+
[FlinkSqlParser.RULE_tablePathCreate, 'table'],
15+
[FlinkSqlParser.RULE_viewPath, 'view'],
16+
[FlinkSqlParser.RULE_viewPathCreate, 'view'],
17+
[FlinkSqlParser.RULE_functionName, 'function'],
18+
[FlinkSqlParser.RULE_functionNameCreate, 'function'],
19+
[FlinkSqlParser.RULE_columnName, 'column'],
20+
[FlinkSqlParser.RULE_columnNameCreate, 'column'],
21+
]);
22+
23+
constructor(errorListener: ErrorListener, preferredRules: Set<number>) {
24+
super(errorListener);
25+
this.preferredRules = preferredRules;
26+
}
27+
28+
public getExpectedText(parser: Parser, token: Token) {
29+
let expectedText = '';
30+
31+
let currentContext = parser.context ?? undefined;
32+
while (currentContext?.parent) {
33+
currentContext = currentContext.parent;
34+
}
35+
36+
const core = new CodeCompletionCore(parser);
37+
core.preferredRules = this.preferredRules;
38+
const candidates = core.collectCandidates(token.tokenIndex, currentContext);
39+
40+
if (candidates.rules.size) {
41+
// get expectedText as collect rules first
42+
for (const candidate of candidates.rules) {
43+
const [ruleType] = candidate;
44+
const name = this.objectNames.get(ruleType);
45+
switch (ruleType) {
46+
case FlinkSqlParser.RULE_databasePath:
47+
case FlinkSqlParser.RULE_tablePath:
48+
case FlinkSqlParser.RULE_viewPath:
49+
case FlinkSqlParser.RULE_functionName:
50+
case FlinkSqlParser.RULE_columnName: {
51+
if (!name) {
52+
expectedText = 'a new object name';
53+
} else {
54+
expectedText = `a new ${name} name`;
55+
}
56+
break;
57+
}
58+
case FlinkSqlParser.RULE_databasePathCreate:
59+
case FlinkSqlParser.RULE_tablePathCreate:
60+
case FlinkSqlParser.RULE_functionNameCreate:
61+
case FlinkSqlParser.RULE_viewPathCreate:
62+
case FlinkSqlParser.RULE_columnNameCreate: {
63+
if (!name) {
64+
expectedText = 'an existing object';
65+
} else {
66+
expectedText = `an existing ${name}`;
67+
}
68+
break;
69+
}
70+
}
71+
}
72+
}
73+
if (candidates.tokens.size) {
74+
expectedText += expectedText ? ' or a keyword' : 'a keyword';
75+
}
76+
return expectedText;
77+
}
78+
}

src/parser/flink/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { BasicSQL } from '../common/basicSQL';
77
import { StmtContextType } from '../common/entityCollector';
88
import { FlinkSqlSplitListener } from './flinkSplitListener';
99
import { FlinkEntityCollector } from './flinkEntityCollector';
10+
import { ErrorListener } from '../common/parseErrorListener';
11+
import { FlinkErrorListener } from './flinkErrorListener';
1012

1113
export { FlinkSqlSplitListener, FlinkEntityCollector };
1214

@@ -37,6 +39,10 @@ export class FlinkSQL extends BasicSQL<FlinkSqlLexer, ProgramContext, FlinkSqlPa
3739
return new FlinkSqlSplitListener();
3840
}
3941

42+
protected createErrorListener(_errorListener: ErrorListener) {
43+
return new FlinkErrorListener(_errorListener, this.preferredRules);
44+
}
45+
4046
protected createEntityCollector(input: string, caretTokenIndex?: number) {
4147
return new FlinkEntityCollector(input, caretTokenIndex);
4248
}

src/parser/hive/hiveErrorListener.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { CodeCompletionCore } from 'antlr4-c3';
2+
import { ErrorListener, ParseErrorListener } from '../common/parseErrorListener';
3+
import { Parser, Token } from 'antlr4ng';
4+
import { HiveSqlParser } from '../../lib/hive/HiveSqlParser';
5+
6+
export class HiveErrorListener extends ParseErrorListener {
7+
private preferredRules: Set<number>;
8+
9+
private objectNames: Map<number, string> = new Map([
10+
[HiveSqlParser.RULE_dbSchemaName, 'database'],
11+
[HiveSqlParser.RULE_dbSchemaNameCreate, 'database'],
12+
[HiveSqlParser.RULE_tableName, 'table'],
13+
[HiveSqlParser.RULE_tableNameCreate, 'table'],
14+
[HiveSqlParser.RULE_viewName, 'view'],
15+
[HiveSqlParser.RULE_viewNameCreate, 'view'],
16+
[HiveSqlParser.RULE_functionNameForDDL, 'function'],
17+
[HiveSqlParser.RULE_functionNameForInvoke, 'function'],
18+
[HiveSqlParser.RULE_functionNameCreate, 'function'],
19+
[HiveSqlParser.RULE_columnName, 'column'],
20+
[HiveSqlParser.RULE_columnNameCreate, 'column'],
21+
]);
22+
23+
constructor(errorListener: ErrorListener, preferredRules: Set<number>) {
24+
super(errorListener);
25+
this.preferredRules = preferredRules;
26+
}
27+
28+
public getExpectedText(parser: Parser, token: Token) {
29+
let expectedText = '';
30+
31+
let currentContext = parser.context ?? undefined;
32+
while (currentContext?.parent) {
33+
currentContext = currentContext.parent;
34+
}
35+
36+
const core = new CodeCompletionCore(parser);
37+
core.preferredRules = this.preferredRules;
38+
const candidates = core.collectCandidates(token.tokenIndex, currentContext);
39+
40+
if (candidates.rules.size) {
41+
// get expectedText as collect rules first
42+
for (const candidate of candidates.rules) {
43+
const [ruleType] = candidate;
44+
const name = this.objectNames.get(ruleType);
45+
switch (ruleType) {
46+
case HiveSqlParser.RULE_dbSchemaName:
47+
case HiveSqlParser.RULE_tableName:
48+
case HiveSqlParser.RULE_viewName:
49+
case HiveSqlParser.RULE_functionNameForDDL:
50+
case HiveSqlParser.RULE_functionNameForInvoke:
51+
case HiveSqlParser.RULE_columnName: {
52+
if (!name) {
53+
expectedText = 'a new object name';
54+
} else {
55+
expectedText = `a new ${name} name`;
56+
}
57+
break;
58+
}
59+
case HiveSqlParser.RULE_dbSchemaNameCreate:
60+
case HiveSqlParser.RULE_tableNameCreate:
61+
case HiveSqlParser.RULE_functionNameCreate:
62+
case HiveSqlParser.RULE_viewNameCreate:
63+
case HiveSqlParser.RULE_columnNameCreate: {
64+
if (!name) {
65+
expectedText = 'an existing object';
66+
} else {
67+
expectedText = `an existing ${name}`;
68+
}
69+
break;
70+
}
71+
}
72+
}
73+
}
74+
if (candidates.tokens.size) {
75+
expectedText += expectedText ? ' or a keyword' : 'a keyword';
76+
}
77+
return expectedText;
78+
}
79+
}

src/parser/hive/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { EntityContextType, Suggestions, SyntaxSuggestion } from '../common/type
88
import { StmtContextType } from '../common/entityCollector';
99
import { HiveSqlSplitListener } from './hiveSplitListener';
1010
import { HiveEntityCollector } from './hiveEntityCollector';
11+
import { ErrorListener } from '../common/parseErrorListener';
12+
import { HiveErrorListener } from './hiveErrorListener';
1113

1214
export { HiveEntityCollector, HiveSqlSplitListener };
1315

@@ -38,6 +40,10 @@ export class HiveSQL extends BasicSQL<HiveSqlLexer, ProgramContext, HiveSqlParse
3840
return new HiveSqlSplitListener();
3941
}
4042

43+
protected createErrorListener(_errorListener: ErrorListener) {
44+
return new HiveErrorListener(_errorListener, this.preferredRules);
45+
}
46+
4147
protected createEntityCollector(input: string, caretTokenIndex?: number) {
4248
return new HiveEntityCollector(input, caretTokenIndex);
4349
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { CodeCompletionCore } from 'antlr4-c3';
2+
import { ErrorListener, ParseErrorListener } from '../common/parseErrorListener';
3+
import { Parser, Token } from 'antlr4ng';
4+
import { ImpalaSqlParser } from '../../lib/impala/ImpalaSqlParser';
5+
6+
export class ImpalaErrorListener extends ParseErrorListener {
7+
private preferredRules: Set<number>;
8+
9+
private objectNames: Map<number, string> = new Map([
10+
[ImpalaSqlParser.RULE_databaseNamePath, 'database'],
11+
[ImpalaSqlParser.RULE_databaseNameCreate, 'database'],
12+
[ImpalaSqlParser.RULE_tableNamePath, 'table'],
13+
[ImpalaSqlParser.RULE_tableNameCreate, 'table'],
14+
[ImpalaSqlParser.RULE_viewNamePath, 'view'],
15+
[ImpalaSqlParser.RULE_viewNameCreate, 'view'],
16+
[ImpalaSqlParser.RULE_functionNamePath, 'function'],
17+
[ImpalaSqlParser.RULE_functionNameCreate, 'function'],
18+
[ImpalaSqlParser.RULE_columnNamePath, 'column'],
19+
[ImpalaSqlParser.RULE_columnNamePathCreate, 'column'],
20+
]);
21+
22+
constructor(errorListener: ErrorListener, preferredRules: Set<number>) {
23+
super(errorListener);
24+
this.preferredRules = preferredRules;
25+
}
26+
27+
public getExpectedText(parser: Parser, token: Token) {
28+
let expectedText = '';
29+
30+
let currentContext = parser.context ?? undefined;
31+
while (currentContext?.parent) {
32+
currentContext = currentContext.parent;
33+
}
34+
35+
const core = new CodeCompletionCore(parser);
36+
core.preferredRules = this.preferredRules;
37+
const candidates = core.collectCandidates(token.tokenIndex, currentContext);
38+
39+
if (candidates.rules.size) {
40+
// get expectedText as collect rules first
41+
for (const candidate of candidates.rules) {
42+
const [ruleType] = candidate;
43+
const name = this.objectNames.get(ruleType);
44+
switch (ruleType) {
45+
case ImpalaSqlParser.RULE_databaseNamePath:
46+
case ImpalaSqlParser.RULE_tableNamePath:
47+
case ImpalaSqlParser.RULE_functionNamePath:
48+
case ImpalaSqlParser.RULE_viewNamePath:
49+
case ImpalaSqlParser.RULE_columnNamePath: {
50+
if (!name) {
51+
expectedText = 'a new object name';
52+
} else {
53+
expectedText = `a new ${name} name`;
54+
}
55+
break;
56+
}
57+
case ImpalaSqlParser.RULE_databaseNameCreate:
58+
case ImpalaSqlParser.RULE_tableNameCreate:
59+
case ImpalaSqlParser.RULE_functionNameCreate:
60+
case ImpalaSqlParser.RULE_viewNameCreate:
61+
case ImpalaSqlParser.RULE_columnNamePathCreate: {
62+
if (!name) {
63+
expectedText = 'an existing object';
64+
} else {
65+
expectedText = `an existing ${name}`;
66+
}
67+
break;
68+
}
69+
}
70+
}
71+
}
72+
if (candidates.tokens.size) {
73+
expectedText += expectedText ? ' or a keyword' : 'a keyword';
74+
}
75+
return expectedText;
76+
}
77+
}

src/parser/impala/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { EntityContextType, Suggestions, SyntaxSuggestion } from '../common/type
77
import { StmtContextType } from '../common/entityCollector';
88
import { ImpalaSqlSplitListener } from './impalaSplitListener';
99
import { ImpalaEntityCollector } from './impalaEntityCollector';
10+
import { ErrorListener } from '../common/parseErrorListener';
11+
import { ImpalaErrorListener } from './ImpalaErrorListener';
1012

1113
export { ImpalaEntityCollector, ImpalaSqlSplitListener };
1214

@@ -36,6 +38,10 @@ export class ImpalaSQL extends BasicSQL<ImpalaSqlLexer, ProgramContext, ImpalaSq
3638
return new ImpalaSqlSplitListener();
3739
}
3840

41+
protected createErrorListener(_errorListener: ErrorListener) {
42+
return new ImpalaErrorListener(_errorListener, this.preferredRules);
43+
}
44+
3945
protected createEntityCollector(input: string, caretTokenIndex?: number) {
4046
return new ImpalaEntityCollector(input, caretTokenIndex);
4147
}

src/parser/mysql/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class MySQL extends BasicSQL<MySqlLexer, ProgramContext, MySqlParser> {
3838
return new MysqlSplitListener();
3939
}
4040

41-
protected createErrorListener(_errorListener: ErrorListener<any>) {
41+
protected createErrorListener(_errorListener: ErrorListener) {
4242
return new MysqlErrorListener(_errorListener, this.preferredRules);
4343
}
4444

src/parser/mysql/mysqlErrorListener.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CodeCompletionCore } from 'antlr4-c3';
2-
import { ParseErrorListener } from '../common/parseErrorListener';
2+
import { ErrorListener, ParseErrorListener } from '../common/parseErrorListener';
33
import { Parser, Token } from 'antlr4ng';
44
import { MySqlParser } from '../../lib/mysql/MySqlParser';
55

@@ -19,15 +19,15 @@ export class MysqlErrorListener extends ParseErrorListener {
1919
[MySqlParser.RULE_columnNameCreate, 'column'],
2020
]);
2121

22-
constructor(errorListener, preferredRules) {
22+
constructor(errorListener: ErrorListener, preferredRules: Set<number>) {
2323
super(errorListener);
2424
this.preferredRules = preferredRules;
2525
}
2626

2727
public getExpectedText(parser: Parser, token: Token) {
2828
let expectedText = '';
2929

30-
let currentContext = parser.context;
30+
let currentContext = parser.context ?? undefined;
3131
while (currentContext?.parent) {
3232
currentContext = currentContext.parent;
3333
}
@@ -45,7 +45,8 @@ export class MysqlErrorListener extends ParseErrorListener {
4545
case MySqlParser.RULE_databaseName:
4646
case MySqlParser.RULE_tableName:
4747
case MySqlParser.RULE_functionName:
48-
case MySqlParser.RULE_viewName: {
48+
case MySqlParser.RULE_viewName:
49+
case MySqlParser.RULE_columnName: {
4950
if (!name) {
5051
expectedText = 'a new object name';
5152
} else {
@@ -56,7 +57,8 @@ export class MysqlErrorListener extends ParseErrorListener {
5657
case MySqlParser.RULE_databaseNameCreate:
5758
case MySqlParser.RULE_tableNameCreate:
5859
case MySqlParser.RULE_functionNameCreate:
59-
case MySqlParser.RULE_viewNameCreate: {
60+
case MySqlParser.RULE_viewNameCreate:
61+
case MySqlParser.RULE_columnNameCreate: {
6062
if (!name) {
6163
expectedText = 'an existing object';
6264
} else {

src/parser/plsql.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export class PLSQL extends BasicSQL<PlSqlLexer, ProgramContext, PlSqlParser> {
2424
return null as any;
2525
}
2626

27+
protected createErrorListener() {
28+
return null as any;
29+
}
30+
2731
protected processCandidates(
2832
candidates: CandidatesCollection,
2933
allTokens: Token[],

src/parser/postgresql/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { BasicSQL } from '../common/basicSQL';
88
import { StmtContextType } from '../common/entityCollector';
99
import { PostgreSqlEntityCollector } from './postgreEntityCollector';
1010
import { PostgreSqlSplitListener } from './postgreSplitListener';
11+
import { ErrorListener } from '../common/parseErrorListener';
12+
import { PostgreSqlErrorListener } from './postgreErrorListener';
1113

1214
export { PostgreSqlEntityCollector, PostgreSqlSplitListener };
1315

@@ -41,6 +43,10 @@ export class PostgreSQL extends BasicSQL<PostgreSqlLexer, ProgramContext, Postgr
4143
return new PostgreSqlSplitListener();
4244
}
4345

46+
protected createErrorListener(_errorListener: ErrorListener) {
47+
return new PostgreSqlErrorListener(_errorListener, this.preferredRules);
48+
}
49+
4450
protected createEntityCollector(input: string, caretTokenIndex?: number) {
4551
return new PostgreSqlEntityCollector(input, caretTokenIndex);
4652
}

0 commit comments

Comments
 (0)