dt-sql-parser 4.1.2-beta.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +51 -0
  2. package/dist/lib/flink/FlinkSqlLexer.d.ts +5 -5
  3. package/dist/lib/flink/FlinkSqlLexer.js +1991 -1992
  4. package/dist/lib/flink/FlinkSqlParser.d.ts +240 -230
  5. package/dist/lib/flink/FlinkSqlParser.js +3036 -2978
  6. package/dist/lib/flink/FlinkSqlParserListener.d.ts +81 -70
  7. package/dist/lib/flink/FlinkSqlParserVisitor.d.ts +49 -42
  8. package/dist/lib/hive/HiveSqlParser.d.ts +251 -231
  9. package/dist/lib/hive/HiveSqlParser.js +6264 -5674
  10. package/dist/lib/hive/HiveSqlParserListener.d.ts +32 -10
  11. package/dist/lib/hive/HiveSqlParserVisitor.d.ts +20 -6
  12. package/dist/lib/impala/ImpalaSqlLexer.d.ts +2 -2
  13. package/dist/lib/impala/ImpalaSqlLexer.js +587 -588
  14. package/dist/lib/impala/ImpalaSqlParser.d.ts +151 -113
  15. package/dist/lib/impala/ImpalaSqlParser.js +3464 -3250
  16. package/dist/lib/impala/ImpalaSqlParserListener.d.ts +49 -5
  17. package/dist/lib/impala/ImpalaSqlParserVisitor.d.ts +31 -3
  18. package/dist/lib/mysql/MySqlLexer.d.ts +2 -2
  19. package/dist/lib/mysql/MySqlLexer.js +3515 -3516
  20. package/dist/lib/mysql/MySqlParser.d.ts +138 -138
  21. package/dist/lib/mysql/MySqlParser.js +511 -511
  22. package/dist/lib/mysql/MySqlParserListener.d.ts +105 -105
  23. package/dist/lib/mysql/MySqlParserVisitor.d.ts +63 -63
  24. package/dist/lib/postgresql/PostgreSqlLexer.d.ts +14 -15
  25. package/dist/lib/postgresql/PostgreSqlLexer.js +2077 -2092
  26. package/dist/lib/postgresql/PostgreSqlParser.d.ts +2860 -2980
  27. package/dist/lib/postgresql/PostgreSqlParser.js +20155 -20660
  28. package/dist/lib/postgresql/PostgreSqlParserListener.d.ts +2110 -2181
  29. package/dist/lib/postgresql/PostgreSqlParserVisitor.d.ts +1261 -1301
  30. package/dist/lib/spark/SparkSqlLexer.d.ts +2 -2
  31. package/dist/lib/spark/SparkSqlLexer.js +949 -950
  32. package/dist/lib/spark/SparkSqlParser.d.ts +413 -645
  33. package/dist/lib/spark/SparkSqlParser.js +6840 -8200
  34. package/dist/lib/spark/SparkSqlParserListener.d.ts +68 -356
  35. package/dist/lib/spark/SparkSqlParserVisitor.d.ts +42 -225
  36. package/dist/lib/trino/TrinoSqlLexer.d.ts +2 -2
  37. package/dist/lib/trino/TrinoSqlLexer.js +319 -321
  38. package/dist/lib/trino/TrinoSqlListener.d.ts +67 -23
  39. package/dist/lib/trino/TrinoSqlParser.d.ts +210 -181
  40. package/dist/lib/trino/TrinoSqlParser.js +4854 -4695
  41. package/dist/lib/trino/TrinoSqlVisitor.d.ts +42 -14
  42. package/dist/parser/common/basicSQL.d.ts +43 -9
  43. package/dist/parser/common/basicSQL.js +147 -40
  44. package/dist/parser/common/semanticContextCollector.d.ts +50 -0
  45. package/dist/parser/common/semanticContextCollector.js +202 -0
  46. package/dist/parser/common/tokenUtils.d.ts +12 -0
  47. package/dist/parser/common/tokenUtils.js +38 -0
  48. package/dist/parser/common/types.d.ts +25 -0
  49. package/dist/parser/common/types.js +7 -0
  50. package/dist/parser/flink/flinkErrorListener.js +14 -2
  51. package/dist/parser/flink/flinkSemanticContextCollector.d.ts +9 -0
  52. package/dist/parser/flink/flinkSemanticContextCollector.js +14 -0
  53. package/dist/parser/flink/index.d.ts +4 -2
  54. package/dist/parser/flink/index.js +22 -14
  55. package/dist/parser/hive/hiveErrorListener.js +9 -3
  56. package/dist/parser/hive/hiveSemanticContextCollector.d.ts +9 -0
  57. package/dist/parser/hive/hiveSemanticContextCollector.js +14 -0
  58. package/dist/parser/hive/index.d.ts +4 -2
  59. package/dist/parser/hive/index.js +29 -14
  60. package/dist/parser/impala/ImpalaErrorListener.js +8 -2
  61. package/dist/parser/impala/impalaSemanticContextCollector.d.ts +9 -0
  62. package/dist/parser/impala/impalaSemanticContextCollector.js +14 -0
  63. package/dist/parser/impala/index.d.ts +4 -2
  64. package/dist/parser/impala/index.js +21 -14
  65. package/dist/parser/mysql/index.d.ts +4 -2
  66. package/dist/parser/mysql/index.js +10 -14
  67. package/dist/parser/mysql/mysqlErrorListener.js +6 -2
  68. package/dist/parser/mysql/mysqlSemanticContextCollector.d.ts +9 -0
  69. package/dist/parser/mysql/mysqlSemanticContextCollector.js +14 -0
  70. package/dist/parser/postgresql/index.d.ts +4 -2
  71. package/dist/parser/postgresql/index.js +51 -42
  72. package/dist/parser/postgresql/postgreEntityCollector.d.ts +5 -5
  73. package/dist/parser/postgresql/postgreEntityCollector.js +7 -7
  74. package/dist/parser/postgresql/postgreErrorListener.js +36 -30
  75. package/dist/parser/postgresql/postgreSemanticContextCollector.d.ts +9 -0
  76. package/dist/parser/postgresql/postgreSemanticContextCollector.js +14 -0
  77. package/dist/parser/spark/index.d.ts +4 -2
  78. package/dist/parser/spark/index.js +26 -14
  79. package/dist/parser/spark/sparkErrorListener.js +9 -3
  80. package/dist/parser/spark/sparkSemanticContextCollector.d.ts +9 -0
  81. package/dist/parser/spark/sparkSemanticContextCollector.js +14 -0
  82. package/dist/parser/trino/index.d.ts +4 -2
  83. package/dist/parser/trino/index.js +22 -14
  84. package/dist/parser/trino/trinoErrorListener.js +9 -3
  85. package/dist/parser/trino/trinoSemanticContextCollector.d.ts +9 -0
  86. package/dist/parser/trino/trinoSemanticContextCollector.js +14 -0
  87. package/package.json +2 -1
@@ -0,0 +1,202 @@
1
+ import { ErrorNode, ParserRuleContext, Token } from 'antlr4ng';
2
+ import { findCaretTokenIndex } from '../common/findCaretTokenIndex';
3
+ import { SqlSplitStrategy, } from '../common/types';
4
+ import { SQL_SPLIT_SYMBOL_TEXT } from './basicSQL';
5
+ class SemanticContextCollector {
6
+ constructor(_input, caretPosition, allTokens, options) {
7
+ this.options = {
8
+ sqlSplitStrategy: SqlSplitStrategy.STRICT,
9
+ };
10
+ this._allTokens = [];
11
+ /**
12
+ * If current caret position is in a beginning of statement semantics, it needs to follow some cases:
13
+ * @case1 there is no statement node with an error before the current statement in the parse tree;
14
+ *
15
+ * @case2 if it is an uncomplete keyword, it will be parsed as an `ErrorNode`
16
+ * and need be a direct child node of `program`;
17
+ *
18
+ * @case3 if it is a complete keyword, the parsed TerminalNode or ErrorNode should be
19
+ * the first leaf node of current statement rule;
20
+ *
21
+ * @case4 if it is whiteSpace in caret position, we can't visit it in antlr4 listener,
22
+ * so we find the first unhidden token before the whiteSpace token, and the unhidden token
23
+ * should be the last leaf node of statement its belongs to;
24
+ *
25
+ * @case5 if the previous token is split symbol like `;`, ignore case1 and forcefully judged as beginning of statement.
26
+ */
27
+ this._isStatementBeginning = false;
28
+ // If caretPosition token is whiteSpace, tokenIndex may be undefined.
29
+ const tokenIndex = findCaretTokenIndex(caretPosition, allTokens);
30
+ if (tokenIndex !== undefined) {
31
+ this._tokenIndex = tokenIndex;
32
+ }
33
+ this._allTokens = allTokens;
34
+ this.options = Object.assign(Object.assign({}, this.options), options);
35
+ if (allTokens === null || allTokens === void 0 ? void 0 : allTokens.length) {
36
+ let i = tokenIndex ? tokenIndex - 1 : allTokens.length - 1;
37
+ /**
38
+ * Link to @case4 and @case5
39
+ * Find the previous unhidden token.
40
+ * If can't find tokenIndex or current token is whiteSpace at caretPosition,
41
+ * prevTokenIndex is useful to help us determine if it is beginning of statement.
42
+ */
43
+ while (i >= 0) {
44
+ if (allTokens[i].channel !== Token.HIDDEN_CHANNEL &&
45
+ (allTokens[i].line < caretPosition.lineNumber ||
46
+ (allTokens[i].line === caretPosition.lineNumber &&
47
+ allTokens[i].column < caretPosition.column))) {
48
+ this._prevTokenIndex = allTokens[i].tokenIndex;
49
+ break;
50
+ }
51
+ i--;
52
+ }
53
+ /**
54
+ * We can directly conclude beginning of statement semantics when current token is
55
+ * the first token of tokenStream or the previous token is semicolon
56
+ */
57
+ if (tokenIndex === 0 ||
58
+ i === -1 ||
59
+ (this._prevTokenIndex &&
60
+ this._allTokens[this._prevTokenIndex].text === SQL_SPLIT_SYMBOL_TEXT)) {
61
+ this._isStatementBeginning = true;
62
+ }
63
+ }
64
+ }
65
+ get semanticContext() {
66
+ return {
67
+ isStatementBeginning: this._isStatementBeginning,
68
+ };
69
+ }
70
+ prevStatementHasError(node) {
71
+ let parent = node.parent;
72
+ if (!parent)
73
+ return false;
74
+ const currentNodeIndex = parent.children.findIndex((child) => child === node);
75
+ if (currentNodeIndex <= 0)
76
+ return false;
77
+ for (let i = currentNodeIndex - 1; i >= 0; i--) {
78
+ const prevNode = parent.children[i];
79
+ if (prevNode instanceof ErrorNode ||
80
+ (prevNode instanceof ParserRuleContext && prevNode.exception !== null))
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ /**
86
+ * Most root rule is `program`.
87
+ */
88
+ isRootRule(node) {
89
+ return node instanceof ParserRuleContext && (node === null || node === void 0 ? void 0 : node.parent) === null;
90
+ }
91
+ /**
92
+ * link to @case4
93
+ * It should be called in each language's own `enterStatement`.
94
+ */
95
+ visitStatement(ctx) {
96
+ var _a, _b, _c;
97
+ if (this.options.sqlSplitStrategy === SqlSplitStrategy.STRICT)
98
+ return;
99
+ const isWhiteSpaceToken = this._tokenIndex === undefined ||
100
+ ((_a = this._allTokens[this._tokenIndex]) === null || _a === void 0 ? void 0 : _a.type) === this.getWhiteSpaceRuleType() ||
101
+ // PostgreSQL whiteSpace not inlcudes '\n' symbol
102
+ ((_b = this._allTokens[this._tokenIndex]) === null || _b === void 0 ? void 0 : _b.text) === '\n';
103
+ const isPrevTokenEndOfStatement = this._prevTokenIndex && ((_c = ctx.stop) === null || _c === void 0 ? void 0 : _c.tokenIndex) === this._prevTokenIndex;
104
+ if (isWhiteSpaceToken && isPrevTokenEndOfStatement && ctx.exception === null) {
105
+ this._isStatementBeginning = !this.prevStatementHasError(ctx)
106
+ ? true
107
+ : this._isStatementBeginning;
108
+ }
109
+ }
110
+ /**
111
+ * Uncomplete keyword will be error node
112
+ */
113
+ visitErrorNode(node) {
114
+ var _a, _b, _c, _d;
115
+ if (node.symbol.tokenIndex !== this._tokenIndex ||
116
+ this._isStatementBeginning ||
117
+ this.options.sqlSplitStrategy === SqlSplitStrategy.STRICT)
118
+ return;
119
+ let parent = node.parent;
120
+ let currentNode = node;
121
+ /**
122
+ * Link to @case2
123
+ * The error node is a direct child node of the program node
124
+ */
125
+ if (this.isRootRule(parent)) {
126
+ this._isStatementBeginning = !this.prevStatementHasError(currentNode);
127
+ return;
128
+ }
129
+ /**
130
+ * Link to @case3
131
+ * Error node must be the first leaf node of the statement parse tree.
132
+ **/
133
+ while (parent !== null && parent.ruleIndex !== this.getStatementRuleType()) {
134
+ if (((_a = parent.children) === null || _a === void 0 ? void 0 : _a[0]) !== currentNode) {
135
+ this._isStatementBeginning = false;
136
+ return;
137
+ }
138
+ currentNode = parent;
139
+ parent = currentNode.parent;
140
+ }
141
+ let isStatementBeginning = true;
142
+ /**
143
+ * Link to @case1
144
+ * Previous statement must have no exception
145
+ */
146
+ if ((parent === null || parent === void 0 ? void 0 : parent.ruleIndex) === this.getStatementRuleType()) {
147
+ const programRule = parent.parent;
148
+ const currentStatementRuleIndex = ((_b = programRule === null || programRule === void 0 ? void 0 : programRule.children) === null || _b === void 0 ? void 0 : _b.findIndex((node) => node === parent)) || -1;
149
+ if (currentStatementRuleIndex > 0) {
150
+ /**
151
+ * When you typed a keyword and doesn't match any rule, you will get a EOF error,
152
+ * For example, just typed 'CREATE', 'INSERT'.
153
+ */
154
+ const isStatementEOF = ((_d = (_c = parent.exception) === null || _c === void 0 ? void 0 : _c.offendingToken) === null || _d === void 0 ? void 0 : _d.text) === '<EOF>';
155
+ isStatementBeginning =
156
+ this.prevStatementHasError(parent) && !isStatementEOF
157
+ ? false
158
+ : isStatementBeginning;
159
+ }
160
+ }
161
+ this._isStatementBeginning = isStatementBeginning;
162
+ }
163
+ visitTerminal(node) {
164
+ var _a, _b;
165
+ if (node.symbol.tokenIndex !== this._tokenIndex ||
166
+ this._isStatementBeginning ||
167
+ this.options.sqlSplitStrategy === SqlSplitStrategy.STRICT)
168
+ return;
169
+ let currentNode = node;
170
+ let parent = node.parent;
171
+ /**
172
+ * Link to @case3
173
+ * Current terminal node must be the first leaf node of the statement parse tree.
174
+ **/
175
+ while (parent !== null && parent.ruleIndex !== this.getStatementRuleType()) {
176
+ if (((_a = parent.children) === null || _a === void 0 ? void 0 : _a[0]) !== currentNode) {
177
+ this._isStatementBeginning = false;
178
+ return;
179
+ }
180
+ currentNode = parent;
181
+ parent = currentNode.parent;
182
+ }
183
+ let isStatementBeginning = true;
184
+ /**
185
+ * Link to @case1
186
+ * Previous statement must have no exception
187
+ */
188
+ if ((parent === null || parent === void 0 ? void 0 : parent.ruleIndex) === this.getStatementRuleType()) {
189
+ const programRule = parent.parent;
190
+ const currentStatementRuleIndex = ((_b = programRule === null || programRule === void 0 ? void 0 : programRule.children) === null || _b === void 0 ? void 0 : _b.findIndex((node) => node === parent)) || -1;
191
+ if (currentStatementRuleIndex > 0) {
192
+ isStatementBeginning = this.prevStatementHasError(parent)
193
+ ? false
194
+ : isStatementBeginning;
195
+ }
196
+ }
197
+ this._isStatementBeginning = isStatementBeginning;
198
+ }
199
+ enterEveryRule(_node) { }
200
+ exitEveryRule(_node) { }
201
+ }
202
+ export default SemanticContextCollector;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Utility function for processing SQL tokens and generating keyword suggestions
3
+ */
4
+ import { Parser } from 'antlr4ng';
5
+ import { CandidatesCollection } from 'antlr4-c3';
6
+ /**
7
+ * Process token candidates and generate a list of keyword suggestions
8
+ * @param parser SQL parser instance
9
+ * @param tokens token candidates
10
+ * @returns list of keyword suggestions
11
+ */
12
+ export declare function processTokenCandidates(parser: Parser, tokens: CandidatesCollection['tokens']): string[];
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Utility function for processing SQL tokens and generating keyword suggestions
3
+ */
4
+ /**
5
+ * Process token candidates and generate a list of keyword suggestions
6
+ * @param parser SQL parser instance
7
+ * @param tokens token candidates
8
+ * @returns list of keyword suggestions
9
+ */
10
+ export function processTokenCandidates(parser, tokens) {
11
+ const keywords = [];
12
+ const cleanDisplayName = (displayName) => {
13
+ return displayName && displayName.startsWith("'") && displayName.endsWith("'")
14
+ ? displayName.slice(1, -1)
15
+ : displayName || '';
16
+ };
17
+ const isKeywordToken = (token) => {
18
+ const symbolicName = parser.vocabulary.getSymbolicName(token);
19
+ return Boolean(symbolicName === null || symbolicName === void 0 ? void 0 : symbolicName.startsWith('KW_'));
20
+ };
21
+ for (const [token, followSets] of tokens) {
22
+ const displayName = parser.vocabulary.getDisplayName(token);
23
+ if (!displayName || !isKeywordToken(token))
24
+ continue;
25
+ const keyword = cleanDisplayName(displayName);
26
+ keywords.push(keyword);
27
+ if (followSets.length && followSets.every((s) => isKeywordToken(s))) {
28
+ const followKeywords = followSets
29
+ .map((s) => cleanDisplayName(parser.vocabulary.getDisplayName(s)))
30
+ .filter(Boolean);
31
+ if (followKeywords.length) {
32
+ const combinedKeyword = [keyword, ...followKeywords].join(' ');
33
+ keywords.push(combinedKeyword);
34
+ }
35
+ }
36
+ }
37
+ return keywords;
38
+ }
@@ -64,3 +64,28 @@ export interface Suggestions<T = WordRange> {
64
64
  readonly keywords: string[];
65
65
  }
66
66
  export type LOCALE_TYPE = 'zh_CN' | 'en_US';
67
+ export interface SemanticContext {
68
+ isStatementBeginning: boolean;
69
+ }
70
+ export declare enum SqlSplitStrategy {
71
+ /** Only end the statement with semicolon symbol */
72
+ STRICT = 0,
73
+ /** Based on parse tree to split statements */
74
+ LOOSE = 1
75
+ }
76
+ export interface SemanticCollectOptions {
77
+ /**
78
+ * `sqlSplitStrategy` will affects the result of `isStatementBeginning`;
79
+ *
80
+ * For example:
81
+ *
82
+ * The sql is "select id from t1 create\<cart_position\>"
83
+ *
84
+ * - `SqlSplitStrategy.STRICT`: split symbol `;` is missing after select statement so that it considerd as one statement, and `isStatementBeginning` is false
85
+ *
86
+ * - `SqlSplitStrategy.LOOSE`: in parse tree, it will parse to "select id from t1" and "create" two single statement, so `isStatementBeginning` is true
87
+ *
88
+ * @default SqlSplitStrategy.STRICT
89
+ */
90
+ sqlSplitStrategy?: SqlSplitStrategy;
91
+ }
@@ -32,3 +32,10 @@ export var EntityContextType;
32
32
  /** column name that will be created */
33
33
  EntityContextType["COLUMN_CREATE"] = "columnCreate";
34
34
  })(EntityContextType || (EntityContextType = {}));
35
+ export var SqlSplitStrategy;
36
+ (function (SqlSplitStrategy) {
37
+ /** Only end the statement with semicolon symbol */
38
+ SqlSplitStrategy[SqlSplitStrategy["STRICT"] = 0] = "STRICT";
39
+ /** Based on parse tree to split statements */
40
+ SqlSplitStrategy[SqlSplitStrategy["LOOSE"] = 1] = "LOOSE";
41
+ })(SqlSplitStrategy || (SqlSplitStrategy = {}));
@@ -14,8 +14,12 @@ export class FlinkErrorListener extends ParseErrorListener {
14
14
  [FlinkSqlParser.RULE_viewPath, 'view'],
15
15
  [FlinkSqlParser.RULE_viewPathCreate, 'view'],
16
16
  [FlinkSqlParser.RULE_functionName, 'function'],
17
+ [FlinkSqlParser.RULE_functionNameWithParams, 'function'],
18
+ [FlinkSqlParser.RULE_reservedKeywordsFollowParamsUsedAsFuncName, 'function'],
19
+ [FlinkSqlParser.RULE_reservedKeywordsNoParamsUsedAsFuncName, 'function'],
17
20
  [FlinkSqlParser.RULE_functionNameCreate, 'function'],
18
21
  [FlinkSqlParser.RULE_columnName, 'column'],
22
+ [FlinkSqlParser.RULE_columnNamePath, 'column'],
19
23
  [FlinkSqlParser.RULE_columnNameCreate, 'column'],
20
24
  ]);
21
25
  }
@@ -50,9 +54,15 @@ export class FlinkErrorListener extends ParseErrorListener {
50
54
  case FlinkSqlParser.RULE_tablePath:
51
55
  case FlinkSqlParser.RULE_viewPath:
52
56
  case FlinkSqlParser.RULE_functionName:
57
+ case FlinkSqlParser.RULE_functionNameWithParams:
58
+ case FlinkSqlParser.RULE_reservedKeywordsFollowParamsUsedAsFuncName:
59
+ case FlinkSqlParser.RULE_reservedKeywordsNoParamsUsedAsFuncName:
53
60
  case FlinkSqlParser.RULE_columnName:
61
+ case FlinkSqlParser.RULE_columnNamePath:
54
62
  case FlinkSqlParser.RULE_catalogPath: {
55
- result.push(`{existing}${name}`);
63
+ if (!result.includes(`{existing}${name}`)) {
64
+ result.push(`{existing}${name}`);
65
+ }
56
66
  break;
57
67
  }
58
68
  case FlinkSqlParser.RULE_databasePathCreate:
@@ -61,7 +71,9 @@ export class FlinkErrorListener extends ParseErrorListener {
61
71
  case FlinkSqlParser.RULE_viewPathCreate:
62
72
  case FlinkSqlParser.RULE_columnNameCreate:
63
73
  case FlinkSqlParser.RULE_catalogPathCreate: {
64
- result.push(`{new}${name}`);
74
+ if (!result.includes(`{new}${name}`)) {
75
+ result.push(`{new}${name}`);
76
+ }
65
77
  break;
66
78
  }
67
79
  }
@@ -0,0 +1,9 @@
1
+ import { FlinkSqlParserListener } from '../../lib';
2
+ import { SingleStatementContext } from '../../lib/flink/FlinkSqlParser';
3
+ import SemanticContextCollector from '../common/semanticContextCollector';
4
+ declare class FlinkSemanticContextCollector extends SemanticContextCollector implements FlinkSqlParserListener {
5
+ getWhiteSpaceRuleType(): number;
6
+ getStatementRuleType(): number;
7
+ enterSingleStatement(ctx: SingleStatementContext): void;
8
+ }
9
+ export { FlinkSemanticContextCollector };
@@ -0,0 +1,14 @@
1
+ import { FlinkSqlParser } from '../../lib/flink/FlinkSqlParser';
2
+ import SemanticContextCollector from '../common/semanticContextCollector';
3
+ class FlinkSemanticContextCollector extends SemanticContextCollector {
4
+ getWhiteSpaceRuleType() {
5
+ return FlinkSqlParser.WHITE_SPACE;
6
+ }
7
+ getStatementRuleType() {
8
+ return FlinkSqlParser.RULE_singleStatement;
9
+ }
10
+ enterSingleStatement(ctx) {
11
+ this.visitStatement(ctx);
12
+ }
13
+ }
14
+ export { FlinkSemanticContextCollector };
@@ -2,12 +2,13 @@ import { CandidatesCollection } from 'antlr4-c3';
2
2
  import { CharStream, CommonTokenStream, Token } from 'antlr4ng';
3
3
  import { FlinkSqlLexer } from '../../lib/flink/FlinkSqlLexer';
4
4
  import { FlinkSqlParser, ProgramContext } from '../../lib/flink/FlinkSqlParser';
5
+ import { CaretPosition, SemanticCollectOptions, Suggestions } from '../common/types';
5
6
  import { BasicSQL } from '../common/basicSQL';
6
7
  import { ErrorListener } from '../common/parseErrorListener';
7
- import { Suggestions } from '../common/types';
8
8
  import { FlinkEntityCollector } from './flinkEntityCollector';
9
9
  import { FlinkErrorListener } from './flinkErrorListener';
10
10
  import { FlinkSqlSplitListener } from './flinkSplitListener';
11
+ import { FlinkSemanticContextCollector } from './flinkSemanticContextCollector';
11
12
  export { FlinkEntityCollector, FlinkSqlSplitListener };
12
13
  export declare class FlinkSQL extends BasicSQL<FlinkSqlLexer, ProgramContext, FlinkSqlParser> {
13
14
  protected createLexerFromCharStream(charStreams: CharStream): FlinkSqlLexer;
@@ -16,5 +17,6 @@ export declare class FlinkSQL extends BasicSQL<FlinkSqlLexer, ProgramContext, Fl
16
17
  protected get splitListener(): FlinkSqlSplitListener;
17
18
  protected createErrorListener(_errorListener: ErrorListener): FlinkErrorListener;
18
19
  protected createEntityCollector(input: string, allTokens?: Token[], caretTokenIndex?: number): FlinkEntityCollector;
19
- protected processCandidates(candidates: CandidatesCollection, allTokens: Token[], caretTokenIndex: number, tokenIndexOffset: number): Suggestions<Token>;
20
+ protected createSemanticContextCollector(input: string, caretPosition: CaretPosition, allTokens: Token[], options?: SemanticCollectOptions): FlinkSemanticContextCollector;
21
+ protected processCandidates(candidates: CandidatesCollection, allTokens: Token[], caretTokenIndex: number): Suggestions<Token>;
20
22
  }
@@ -1,10 +1,12 @@
1
+ import { processTokenCandidates } from '../common/tokenUtils';
1
2
  import { FlinkSqlLexer } from '../../lib/flink/FlinkSqlLexer';
2
3
  import { FlinkSqlParser } from '../../lib/flink/FlinkSqlParser';
4
+ import { EntityContextType, } from '../common/types';
3
5
  import { BasicSQL } from '../common/basicSQL';
4
- import { EntityContextType } from '../common/types';
5
6
  import { FlinkEntityCollector } from './flinkEntityCollector';
6
7
  import { FlinkErrorListener } from './flinkErrorListener';
7
8
  import { FlinkSqlSplitListener } from './flinkSplitListener';
9
+ import { FlinkSemanticContextCollector } from './flinkSemanticContextCollector';
8
10
  export { FlinkEntityCollector, FlinkSqlSplitListener };
9
11
  export class FlinkSQL extends BasicSQL {
10
12
  constructor() {
@@ -23,6 +25,7 @@ export class FlinkSQL extends BasicSQL {
23
25
  FlinkSqlParser.RULE_reservedKeywordsNoParamsUsedAsFuncName, // functionName
24
26
  FlinkSqlParser.RULE_functionNameCreate, // functionName that will be created
25
27
  FlinkSqlParser.RULE_columnName,
28
+ FlinkSqlParser.RULE_columnNamePath,
26
29
  FlinkSqlParser.RULE_columnNameCreate,
27
30
  ]);
28
31
  }
@@ -42,13 +45,15 @@ export class FlinkSQL extends BasicSQL {
42
45
  createEntityCollector(input, allTokens, caretTokenIndex) {
43
46
  return new FlinkEntityCollector(input, allTokens, caretTokenIndex);
44
47
  }
45
- processCandidates(candidates, allTokens, caretTokenIndex, tokenIndexOffset) {
48
+ createSemanticContextCollector(input, caretPosition, allTokens, options) {
49
+ return new FlinkSemanticContextCollector(input, caretPosition, allTokens, options);
50
+ }
51
+ processCandidates(candidates, allTokens, caretTokenIndex) {
46
52
  const originalSyntaxSuggestions = [];
47
53
  const keywords = [];
48
54
  for (let candidate of candidates.rules) {
49
55
  const [ruleType, candidateRule] = candidate;
50
- const startTokenIndex = candidateRule.startTokenIndex + tokenIndexOffset;
51
- const tokenRanges = allTokens.slice(startTokenIndex, caretTokenIndex + tokenIndexOffset + 1);
56
+ const tokenRanges = allTokens.slice(candidateRule.startTokenIndex, caretTokenIndex + 1);
52
57
  let syntaxContextType = void 0;
53
58
  switch (ruleType) {
54
59
  case FlinkSqlParser.RULE_catalogPath: {
@@ -98,6 +103,17 @@ export class FlinkSQL extends BasicSQL {
98
103
  syntaxContextType = EntityContextType.COLUMN_CREATE;
99
104
  break;
100
105
  }
106
+ case FlinkSqlParser.RULE_columnNamePath: {
107
+ if (candidateRule.ruleList.includes(FlinkSqlParser.RULE_selectClause) ||
108
+ candidateRule.ruleList.includes(FlinkSqlParser.RULE_whereClause) ||
109
+ candidateRule.ruleList.includes(FlinkSqlParser.RULE_groupByClause) ||
110
+ candidateRule.ruleList.includes(FlinkSqlParser.RULE_limitClause) ||
111
+ candidateRule.ruleList.includes(FlinkSqlParser.RULE_whenClause) ||
112
+ candidateRule.ruleList.includes(FlinkSqlParser.RULE_havingClause)) {
113
+ syntaxContextType = EntityContextType.COLUMN;
114
+ }
115
+ break;
116
+ }
101
117
  default:
102
118
  break;
103
119
  }
@@ -108,16 +124,8 @@ export class FlinkSQL extends BasicSQL {
108
124
  });
109
125
  }
110
126
  }
111
- for (let candidate of candidates.tokens) {
112
- const symbolicName = this._parser.vocabulary.getSymbolicName(candidate[0]);
113
- const displayName = this._parser.vocabulary.getDisplayName(candidate[0]);
114
- if (displayName && symbolicName && symbolicName.startsWith('KW_')) {
115
- const keyword = displayName.startsWith("'") && displayName.endsWith("'")
116
- ? displayName.slice(1, -1)
117
- : displayName;
118
- keywords.push(keyword);
119
- }
120
- }
127
+ const processedKeywords = processTokenCandidates(this._parser, candidates.tokens);
128
+ keywords.push(...processedKeywords);
121
129
  return {
122
130
  syntax: originalSyntaxSuggestions,
123
131
  keywords,
@@ -16,6 +16,7 @@ export class HiveErrorListener extends ParseErrorListener {
16
16
  [HiveSqlParser.RULE_functionNameCreate, 'function'],
17
17
  [HiveSqlParser.RULE_columnName, 'column'],
18
18
  [HiveSqlParser.RULE_columnNameCreate, 'column'],
19
+ [HiveSqlParser.RULE_columnNamePath, 'column'],
19
20
  ]);
20
21
  }
21
22
  getExpectedText(parser, token) {
@@ -50,8 +51,11 @@ export class HiveErrorListener extends ParseErrorListener {
50
51
  case HiveSqlParser.RULE_viewName:
51
52
  case HiveSqlParser.RULE_functionNameForDDL:
52
53
  case HiveSqlParser.RULE_functionNameForInvoke:
53
- case HiveSqlParser.RULE_columnName: {
54
- result.push(`{existing}${name}`);
54
+ case HiveSqlParser.RULE_columnName:
55
+ case HiveSqlParser.RULE_columnNamePath: {
56
+ if (!result.includes(`{existing}${name}`)) {
57
+ result.push(`{existing}${name}`);
58
+ }
55
59
  break;
56
60
  }
57
61
  case HiveSqlParser.RULE_dbSchemaNameCreate:
@@ -59,7 +63,9 @@ export class HiveErrorListener extends ParseErrorListener {
59
63
  case HiveSqlParser.RULE_functionNameCreate:
60
64
  case HiveSqlParser.RULE_viewNameCreate:
61
65
  case HiveSqlParser.RULE_columnNameCreate: {
62
- result.push(`{new}${name}`);
66
+ if (!result.includes(`{new}${name}`)) {
67
+ result.push(`{new}${name}`);
68
+ }
63
69
  break;
64
70
  }
65
71
  }
@@ -0,0 +1,9 @@
1
+ import { HiveSqlParserListener } from '../../lib';
2
+ import { StatementContext } from '../../lib/hive/HiveSqlParser';
3
+ import SemanticContextCollector from '../common/semanticContextCollector';
4
+ declare class HiveSemanticContextCollector extends SemanticContextCollector implements HiveSqlParserListener {
5
+ getWhiteSpaceRuleType(): number;
6
+ getStatementRuleType(): number;
7
+ enterStatement(ctx: StatementContext): void;
8
+ }
9
+ export { HiveSemanticContextCollector };
@@ -0,0 +1,14 @@
1
+ import { HiveSqlParser } from '../../lib/hive/HiveSqlParser';
2
+ import SemanticContextCollector from '../common/semanticContextCollector';
3
+ class HiveSemanticContextCollector extends SemanticContextCollector {
4
+ getWhiteSpaceRuleType() {
5
+ return HiveSqlParser.WHITE_SPACE;
6
+ }
7
+ getStatementRuleType() {
8
+ return HiveSqlParser.RULE_statement;
9
+ }
10
+ enterStatement(ctx) {
11
+ this.visitStatement(ctx);
12
+ }
13
+ }
14
+ export { HiveSemanticContextCollector };
@@ -3,11 +3,12 @@ import { CharStream, CommonTokenStream, Token } from 'antlr4ng';
3
3
  import { HiveSqlLexer } from '../../lib/hive/HiveSqlLexer';
4
4
  import { HiveSqlParser, ProgramContext } from '../../lib/hive/HiveSqlParser';
5
5
  import { BasicSQL } from '../common/basicSQL';
6
+ import { CaretPosition, SemanticCollectOptions, Suggestions } from '../common/types';
6
7
  import { ErrorListener } from '../common/parseErrorListener';
7
- import { Suggestions } from '../common/types';
8
8
  import { HiveEntityCollector } from './hiveEntityCollector';
9
9
  import { HiveErrorListener } from './hiveErrorListener';
10
10
  import { HiveSqlSplitListener } from './hiveSplitListener';
11
+ import { HiveSemanticContextCollector } from './hiveSemanticContextCollector';
11
12
  export { HiveEntityCollector, HiveSqlSplitListener };
12
13
  export declare class HiveSQL extends BasicSQL<HiveSqlLexer, ProgramContext, HiveSqlParser> {
13
14
  protected createLexerFromCharStream(charStreams: CharStream): HiveSqlLexer;
@@ -16,5 +17,6 @@ export declare class HiveSQL extends BasicSQL<HiveSqlLexer, ProgramContext, Hive
16
17
  protected get splitListener(): HiveSqlSplitListener;
17
18
  protected createErrorListener(_errorListener: ErrorListener): HiveErrorListener;
18
19
  protected createEntityCollector(input: string, allTokens?: Token[], caretTokenIndex?: number): HiveEntityCollector;
19
- protected processCandidates(candidates: CandidatesCollection, allTokens: Token[], caretTokenIndex: number, tokenIndexOffset: number): Suggestions<Token>;
20
+ protected createSemanticContextCollector(input: string, caretPosition: CaretPosition, allTokens: Token[], options?: SemanticCollectOptions): HiveSemanticContextCollector;
21
+ protected processCandidates(candidates: CandidatesCollection, allTokens: Token[], caretTokenIndex: number): Suggestions<Token>;
20
22
  }
@@ -1,10 +1,12 @@
1
+ import { processTokenCandidates } from '../common/tokenUtils';
1
2
  import { HiveSqlLexer } from '../../lib/hive/HiveSqlLexer';
2
3
  import { HiveSqlParser } from '../../lib/hive/HiveSqlParser';
3
4
  import { BasicSQL } from '../common/basicSQL';
4
- import { EntityContextType } from '../common/types';
5
+ import { EntityContextType, } from '../common/types';
5
6
  import { HiveEntityCollector } from './hiveEntityCollector';
6
7
  import { HiveErrorListener } from './hiveErrorListener';
7
8
  import { HiveSqlSplitListener } from './hiveSplitListener';
9
+ import { HiveSemanticContextCollector } from './hiveSemanticContextCollector';
8
10
  export { HiveEntityCollector, HiveSqlSplitListener };
9
11
  export class HiveSQL extends BasicSQL {
10
12
  constructor() {
@@ -20,6 +22,7 @@ export class HiveSQL extends BasicSQL {
20
22
  HiveSqlParser.RULE_functionNameForInvoke, // function name
21
23
  HiveSqlParser.RULE_functionNameCreate, // function name that will be created
22
24
  HiveSqlParser.RULE_columnName,
25
+ HiveSqlParser.RULE_columnNamePath,
23
26
  HiveSqlParser.RULE_columnNameCreate,
24
27
  ]);
25
28
  }
@@ -39,13 +42,15 @@ export class HiveSQL extends BasicSQL {
39
42
  createEntityCollector(input, allTokens, caretTokenIndex) {
40
43
  return new HiveEntityCollector(input, allTokens, caretTokenIndex);
41
44
  }
42
- processCandidates(candidates, allTokens, caretTokenIndex, tokenIndexOffset) {
45
+ createSemanticContextCollector(input, caretPosition, allTokens, options) {
46
+ return new HiveSemanticContextCollector(input, caretPosition, allTokens, options);
47
+ }
48
+ processCandidates(candidates, allTokens, caretTokenIndex) {
43
49
  const originalSyntaxSuggestions = [];
44
50
  const keywords = [];
45
51
  for (let candidate of candidates.rules) {
46
52
  const [ruleType, candidateRule] = candidate;
47
- const startTokenIndex = candidateRule.startTokenIndex + tokenIndexOffset;
48
- const tokenRanges = allTokens.slice(startTokenIndex, caretTokenIndex + tokenIndexOffset + 1);
53
+ const tokenRanges = allTokens.slice(candidateRule.startTokenIndex, caretTokenIndex + 1);
49
54
  let syntaxContextType = void 0;
50
55
  switch (ruleType) {
51
56
  case HiveSqlParser.RULE_dbSchemaName: {
@@ -89,6 +94,24 @@ export class HiveSQL extends BasicSQL {
89
94
  syntaxContextType = EntityContextType.COLUMN_CREATE;
90
95
  break;
91
96
  }
97
+ case HiveSqlParser.RULE_columnNamePath: {
98
+ if (candidateRule.ruleList.includes(HiveSqlParser.RULE_orderByClause) ||
99
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_havingClause) ||
100
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_groupByClause) ||
101
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_sortByClause) ||
102
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_whereClause) ||
103
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_qualifyClause) ||
104
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_clusterByClause) ||
105
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_distributeByClause) ||
106
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_selectClause) ||
107
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_joinSource) ||
108
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_caseExpression) ||
109
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_whenExpression) ||
110
+ candidateRule.ruleList.includes(HiveSqlParser.RULE_castExpression)) {
111
+ syntaxContextType = EntityContextType.COLUMN;
112
+ }
113
+ break;
114
+ }
92
115
  default:
93
116
  break;
94
117
  }
@@ -99,16 +122,8 @@ export class HiveSQL extends BasicSQL {
99
122
  });
100
123
  }
101
124
  }
102
- for (let candidate of candidates.tokens) {
103
- const symbolicName = this._parser.vocabulary.getSymbolicName(candidate[0]);
104
- const displayName = this._parser.vocabulary.getDisplayName(candidate[0]);
105
- if (displayName && symbolicName && symbolicName.startsWith('KW_')) {
106
- const keyword = displayName.startsWith("'") && displayName.endsWith("'")
107
- ? displayName.slice(1, -1)
108
- : displayName;
109
- keywords.push(keyword);
110
- }
111
- }
125
+ const processedKeywords = processTokenCandidates(this._parser, candidates.tokens);
126
+ keywords.push(...processedKeywords);
112
127
  return {
113
128
  syntax: originalSyntaxSuggestions,
114
129
  keywords,