dt-sql-parser 4.5.1-beta.3 → 4.5.2-beta.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 (51) hide show
  1. package/dist/lib/SQLParserBase.d.ts +9 -0
  2. package/dist/lib/SQLParserBase.js +15 -0
  3. package/dist/lib/generic/GenericSqlListener.d.ts +11 -0
  4. package/dist/lib/generic/GenericSqlParser.d.ts +61 -47
  5. package/dist/lib/generic/GenericSqlParser.js +1047 -949
  6. package/dist/lib/generic/GenericSqlVisitor.d.ts +7 -0
  7. package/dist/lib/hive/HiveSqlParser.d.ts +54 -30
  8. package/dist/lib/hive/HiveSqlParser.js +5633 -5467
  9. package/dist/lib/hive/HiveSqlParserListener.d.ts +22 -0
  10. package/dist/lib/hive/HiveSqlParserVisitor.d.ts +14 -0
  11. package/dist/lib/mysql/MySqlParser.d.ts +34 -9
  12. package/dist/lib/mysql/MySqlParser.js +5697 -5550
  13. package/dist/lib/mysql/MySqlParserListener.d.ts +33 -5
  14. package/dist/lib/mysql/MySqlParserVisitor.d.ts +20 -3
  15. package/dist/lib/postgresql/PostgreSqlParser.d.ts +200 -166
  16. package/dist/lib/postgresql/PostgreSqlParser.js +10389 -10136
  17. package/dist/lib/postgresql/PostgreSqlParserListener.d.ts +31 -5
  18. package/dist/lib/postgresql/PostgreSqlParserVisitor.d.ts +19 -3
  19. package/dist/lib/trino/TrinoSqlListener.d.ts +33 -0
  20. package/dist/lib/trino/TrinoSqlParser.d.ts +157 -115
  21. package/dist/lib/trino/TrinoSqlParser.js +4775 -4494
  22. package/dist/lib/trino/TrinoSqlVisitor.d.ts +21 -0
  23. package/dist/parser/common/basicSQL.d.ts +3 -0
  24. package/dist/parser/common/basicSQL.js +47 -1
  25. package/dist/parser/common/entityCollector.d.ts +37 -2
  26. package/dist/parser/common/entityCollector.js +303 -2
  27. package/dist/parser/flink/flinkEntityCollector.d.ts +2 -1
  28. package/dist/parser/flink/flinkEntityCollector.js +4 -0
  29. package/dist/parser/flink/index.js +1 -1
  30. package/dist/parser/generic/genericEntityCollector.d.ts +7 -2
  31. package/dist/parser/generic/genericEntityCollector.js +65 -15
  32. package/dist/parser/generic/index.js +13 -1
  33. package/dist/parser/hive/hiveEntityCollector.d.ts +2 -1
  34. package/dist/parser/hive/hiveEntityCollector.js +5 -0
  35. package/dist/parser/hive/index.js +1 -1
  36. package/dist/parser/impala/impalaEntityCollector.d.ts +2 -1
  37. package/dist/parser/impala/impalaEntityCollector.js +4 -0
  38. package/dist/parser/impala/index.js +1 -1
  39. package/dist/parser/mysql/index.js +15 -1
  40. package/dist/parser/mysql/mysqlEntityCollector.d.ts +3 -1
  41. package/dist/parser/mysql/mysqlEntityCollector.js +24 -0
  42. package/dist/parser/postgresql/index.js +3 -2
  43. package/dist/parser/postgresql/postgreEntityCollector.d.ts +3 -1
  44. package/dist/parser/postgresql/postgreEntityCollector.js +10 -0
  45. package/dist/parser/spark/index.js +1 -1
  46. package/dist/parser/spark/sparkEntityCollector.d.ts +2 -1
  47. package/dist/parser/spark/sparkEntityCollector.js +5 -0
  48. package/dist/parser/trino/index.js +4 -2
  49. package/dist/parser/trino/trinoEntityCollector.d.ts +2 -1
  50. package/dist/parser/trino/trinoEntityCollector.js +5 -0
  51. package/package.json +1 -1
@@ -138,6 +138,8 @@ import { RelationDefaultContext } from "./TrinoSqlParser.js";
138
138
  import { JoinRelationContext } from "./TrinoSqlParser.js";
139
139
  import { JoinTypeContext } from "./TrinoSqlParser.js";
140
140
  import { JoinCriteriaContext } from "./TrinoSqlParser.js";
141
+ import { JoinColumnEqualityContext } from "./TrinoSqlParser.js";
142
+ import { JoinColumnReferenceContext } from "./TrinoSqlParser.js";
141
143
  import { SampledRelationContext } from "./TrinoSqlParser.js";
142
144
  import { SampleTypeContext } from "./TrinoSqlParser.js";
143
145
  import { TrimsSpecificationContext } from "./TrinoSqlParser.js";
@@ -356,6 +358,7 @@ import { CatalogNameCreateContext } from "./TrinoSqlParser.js";
356
358
  import { FunctionNameContext } from "./TrinoSqlParser.js";
357
359
  import { FunctionNameCreateContext } from "./TrinoSqlParser.js";
358
360
  import { ColumnRefContext } from "./TrinoSqlParser.js";
361
+ import { EmptyColumnContext } from "./TrinoSqlParser.js";
359
362
  import { ColumnNameContext } from "./TrinoSqlParser.js";
360
363
  import { ColumnNameCreateContext } from "./TrinoSqlParser.js";
361
364
  import { QualifiedNameContext } from "./TrinoSqlParser.js";
@@ -1320,6 +1323,18 @@ export declare class TrinoSqlVisitor<Result> extends AbstractParseTreeVisitor<Re
1320
1323
  * @return the visitor result
1321
1324
  */
1322
1325
  visitJoinCriteria?: (ctx: JoinCriteriaContext) => Result;
1326
+ /**
1327
+ * Visit a parse tree produced by `TrinoSqlParser.joinColumnEquality`.
1328
+ * @param ctx the parse tree
1329
+ * @return the visitor result
1330
+ */
1331
+ visitJoinColumnEquality?: (ctx: JoinColumnEqualityContext) => Result;
1332
+ /**
1333
+ * Visit a parse tree produced by `TrinoSqlParser.joinColumnReference`.
1334
+ * @param ctx the parse tree
1335
+ * @return the visitor result
1336
+ */
1337
+ visitJoinColumnReference?: (ctx: JoinColumnReferenceContext) => Result;
1323
1338
  /**
1324
1339
  * Visit a parse tree produced by `TrinoSqlParser.sampledRelation`.
1325
1340
  * @param ctx the parse tree
@@ -2769,6 +2784,12 @@ export declare class TrinoSqlVisitor<Result> extends AbstractParseTreeVisitor<Re
2769
2784
  * @return the visitor result
2770
2785
  */
2771
2786
  visitColumnRef?: (ctx: ColumnRefContext) => Result;
2787
+ /**
2788
+ * Visit a parse tree produced by `TrinoSqlParser.emptyColumn`.
2789
+ * @param ctx the parse tree
2790
+ * @return the visitor result
2791
+ */
2792
+ visitEmptyColumn?: (ctx: EmptyColumnContext) => Result;
2772
2793
  /**
2773
2794
  * Visit a parse tree produced by `TrinoSqlParser.columnName`.
2774
2795
  * @param ctx the parse tree
@@ -45,6 +45,7 @@ export declare abstract class BasicSQL<L extends Lexer = Lexer, PRC extends Pars
45
45
  * @param caretTokenIndex tokenIndex of caretPosition
46
46
  */
47
47
  protected abstract processCandidates(candidates: CandidatesCollection, allTokens: Token[], caretTokenIndex: number): Suggestions<Token>;
48
+ protected getCandidateTokenRanges(candidates: CandidatesCollection, candidateStartTokenIndex: number, allTokens: Token[], caretTokenIndex: number): Token[];
48
49
  /**
49
50
  * Get a new splitListener instance.
50
51
  */
@@ -169,6 +170,8 @@ export declare abstract class BasicSQL<L extends Lexer = Lexer, PRC extends Pars
169
170
  } | null;
170
171
  /**
171
172
  * Get the minimum statement tree for collecting completion candidates
173
+ * Each supported grammar exposes one top-level statement per direct program child
174
+ * Keep the lookup shallow to avoid selecting nested statements or subqueries
172
175
  */
173
176
  private getSuggestionParseTree;
174
177
  /**
@@ -1,5 +1,5 @@
1
1
  import { CodeCompletionCore } from 'antlr4-c3';
2
- import { CharStreams, CommonTokenStream, ParserRuleContext, ParseTreeWalker, PredictionMode, } from 'antlr4ng';
2
+ import { CharStreams, CommonTokenStream, ParserRuleContext, ParseTreeWalker, PredictionMode, Token, } from 'antlr4ng';
3
3
  import { ErrorStrategy } from './errorStrategy';
4
4
  import { findCaretTokenIndex } from './findCaretTokenIndex';
5
5
  import { ctxToText, tokenToWord } from './textAndWord';
@@ -17,6 +17,49 @@ export class BasicSQL {
17
17
  };
18
18
  this.locale = 'en_US';
19
19
  }
20
+ getCandidateTokenRanges(candidates, candidateStartTokenIndex, allTokens, caretTokenIndex) {
21
+ var _a;
22
+ // antlr4-c3 may return both entity and alias candidates; use the nearest candidate's start index as boundary
23
+ const endTokenIndex = Array.from(candidates.rules.values()).reduce((nearestStartTokenIndex, candidateRule) => {
24
+ if (candidateRule.startTokenIndex <= candidateStartTokenIndex) {
25
+ return nearestStartTokenIndex;
26
+ }
27
+ return Math.min(nearestStartTokenIndex, candidateRule.startTokenIndex);
28
+ }, caretTokenIndex + 1);
29
+ const previousVisibleToken = allTokens
30
+ .slice(candidateStartTokenIndex, endTokenIndex)
31
+ .reverse()
32
+ .find((token) => token.channel === Token.DEFAULT_CHANNEL);
33
+ const visibleTokenIndexes = allTokens
34
+ .slice(endTokenIndex, caretTokenIndex + 1)
35
+ .reduce((indexes, token, offset) => {
36
+ if (token.channel === Token.DEFAULT_CHANNEL) {
37
+ indexes.push(endTokenIndex + offset);
38
+ }
39
+ return indexes;
40
+ }, []);
41
+ const firstVisibleToken = allTokens[visibleTokenIndexes[0]];
42
+ let rangeEndTokenIndex = endTokenIndex;
43
+ // candidate boundary may fall inside a multi-level qualified name; extend along the identifier and dot chain
44
+ if ((previousVisibleToken === null || previousVisibleToken === void 0 ? void 0 : previousVisibleToken.text) === '.' || (firstVisibleToken === null || firstVisibleToken === void 0 ? void 0 : firstVisibleToken.text) === '.') {
45
+ let visibleTokenOffset = 0;
46
+ if ((previousVisibleToken === null || previousVisibleToken === void 0 ? void 0 : previousVisibleToken.text) === '.' && firstVisibleToken) {
47
+ rangeEndTokenIndex = visibleTokenIndexes[visibleTokenOffset] + 1;
48
+ visibleTokenOffset += 1;
49
+ }
50
+ while (((_a = allTokens[visibleTokenIndexes[visibleTokenOffset]]) === null || _a === void 0 ? void 0 : _a.text) === '.') {
51
+ rangeEndTokenIndex = visibleTokenIndexes[visibleTokenOffset] + 1;
52
+ visibleTokenOffset += 1;
53
+ if (visibleTokenOffset < visibleTokenIndexes.length) {
54
+ rangeEndTokenIndex = visibleTokenIndexes[visibleTokenOffset] + 1;
55
+ visibleTokenOffset += 1;
56
+ }
57
+ }
58
+ }
59
+ return allTokens
60
+ .slice(candidateStartTokenIndex, rangeEndTokenIndex)
61
+ .filter((token) => token.channel === Token.DEFAULT_CHANNEL);
62
+ }
20
63
  /**
21
64
  * Create an antlr4 lexer from input.
22
65
  * @param input string
@@ -387,6 +430,8 @@ export class BasicSQL {
387
430
  }
388
431
  /**
389
432
  * Get the minimum statement tree for collecting completion candidates
433
+ * Each supported grammar exposes one top-level statement per direct program child
434
+ * Keep the lookup shallow to avoid selecting nested statements or subqueries
390
435
  */
391
436
  getSuggestionParseTree(parseTree, caretTokenIndex) {
392
437
  var _a, _b, _c;
@@ -421,6 +466,7 @@ export class BasicSQL {
421
466
  const suggestionParseTree = this.getSuggestionParseTree(parseTree, caretTokenIndex);
422
467
  if (suggestionParseTree === parseTree)
423
468
  return candidates;
469
+ // Keep the program candidates for outer rule paths and use statement candidates for isolation
424
470
  const statementCore = new CodeCompletionCore(parser);
425
471
  statementCore.preferredRules = this.preferredRules;
426
472
  const statementCandidates = statementCore.collectCandidates(caretTokenIndex, suggestionParseTree);
@@ -1,4 +1,4 @@
1
- import { ParserRuleContext, Token } from 'antlr4ng';
1
+ import { ErrorNode, ParserRuleContext, Token } from 'antlr4ng';
2
2
  import { TextPosition, WordPosition, WordRange } from './textAndWord';
3
3
  import { EntityContextType } from './types';
4
4
  /**
@@ -144,10 +144,21 @@ export declare abstract class EntityCollector {
144
144
  private readonly _allTokens;
145
145
  private readonly _caretTokenIndex;
146
146
  private readonly _entitiesSet;
147
+ /**
148
+ * Contexts whose source slice contains a parse error.
149
+ * Error recovery can stretch a rule context across a whole clause, so `ctxToText`
150
+ * would return e.g. `JOIN myOwn\nORDER BY 1` where a name is expected.
151
+ */
152
+ private readonly _errorContexts;
147
153
  /** Staging statements that have already entered. */
148
154
  private readonly _stmtStack;
149
155
  /** Staging entities inside a single statement or clause. */
150
156
  private readonly _entityStack;
157
+ /**
158
+ * CTE definitions collected while walking.
159
+ * Visibility is sequential inside the WITH owner's range, and references bind to the nearest preceding CTE.
160
+ */
161
+ private _cteDefinitions;
151
162
  /**
152
163
  * The scope depth of the statement containing the caret.
153
164
  * This is used to determine the accessibility of entities.
@@ -164,7 +175,7 @@ export declare abstract class EntityCollector {
164
175
  * */
165
176
  private _rootStmt;
166
177
  visitTerminal(): void;
167
- visitErrorNode(): void;
178
+ visitErrorNode(node: ErrorNode): void;
168
179
  enterEveryRule(): void;
169
180
  exitEveryRule(): void;
170
181
  getRootStmt(): StmtContext | null;
@@ -215,5 +226,29 @@ export declare abstract class EntityCollector {
215
226
  protected combineQueryResultStmtEntities(stmtContext: StmtContext, entitiesInsideStmt: EntityContext[]): EntityContext[];
216
227
  protected combineFromTableSource(stmtContext: StmtContext, entitiesInsideStmt: EntityContext[]): EntityContext[];
217
228
  protected combineCreateTableOrViewStmtEntities(stmtContext: StmtContext, entitiesInsideStmt: EntityContext[]): EntityContext[];
229
+ /**
230
+ * Collect one CTE definition.
231
+ * `endTokenIndex` is the closing token of this item only (not a following CTE).
232
+ * Column names, when present, become the outward schema and do not replace the body query result.
233
+ */
234
+ protected collectCte(cteCtx: ParserRuleContext, nameCtx: ParserRuleContext | null, bodyCtx: ParserRuleContext | null, endTokenIndex: number, columnNameCtxs?: ParserRuleContext[], columnListCtx?: ParserRuleContext | null): void;
235
+ private cteScopeEndTokenIndex;
236
+ private extendUnclosedScope;
237
+ private finalizeCteDefinitions;
238
+ /**
239
+ * CTE outward schema defaults to the body query result itself.
240
+ * Override when that result's text covers more than the select list.
241
+ */
242
+ protected adaptOutwardQueryResult(entity: CommonEntityContext): CommonEntityContext;
243
+ protected sourceSlice(startIndex: number, endIndex: number): string;
244
+ private findOutwardQueryResult;
245
+ private columnTokens;
246
+ private createColumnListQueryResultFromTokens;
247
+ private createColumnListQueryResult;
248
+ private wrapOutwardColumns;
249
+ private bindCteReferences;
250
+ private markCteAccessibility;
251
+ /** Nearest preceding CTE whose scope covers `tokenIndex`. */
252
+ private resolveCte;
218
253
  }
219
254
  export {};
@@ -9,7 +9,7 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { isToken, Token } from 'antlr4ng';
12
+ import { isToken, ParserRuleContext, TerminalNode, Token } from 'antlr4ng';
13
13
  import { SimpleStack } from './simpleStack';
14
14
  import { ctxToText, isWordRange, tokenToWord, } from './textAndWord';
15
15
  import { EntityContextType } from './types';
@@ -224,24 +224,63 @@ function isEntityInScope(entity, rangeEntity) {
224
224
  rangeStart <= entityStart &&
225
225
  rangeStop >= entityStop);
226
226
  }
227
+ /**
228
+ * Parents of a single CTE item. The scope of that CTE is the query that owns this clause.
229
+ */
230
+ const CTE_CLAUSE_CONTEXT_NAMES = new Set([
231
+ 'WithClauseContext',
232
+ 'WithStatementContext',
233
+ 'CtesContext',
234
+ 'WithContext',
235
+ ]);
236
+ function normalizeIdent(text) {
237
+ let value = text.trim();
238
+ if ((value.startsWith('`') && value.endsWith('`') && value.length >= 2) ||
239
+ (value.startsWith('"') && value.endsWith('"') && value.length >= 2) ||
240
+ (value.startsWith('[') && value.endsWith(']') && value.length >= 2)) {
241
+ value = value.slice(1, -1);
242
+ }
243
+ return value.toLowerCase();
244
+ }
245
+ function sameIdent(left, right) {
246
+ return normalizeIdent(left) === normalizeIdent(right);
247
+ }
227
248
  /**
228
249
  * @todo: Handle alias, includes column alias, table alias, query as alias and so on.
229
250
  * @todo: [may be need] Combine the entities in each clause.
230
251
  */
231
252
  export class EntityCollector {
232
253
  constructor(input, allTokens, caretTokenIndex) {
254
+ /**
255
+ * Contexts whose source slice contains a parse error.
256
+ * Error recovery can stretch a rule context across a whole clause, so `ctxToText`
257
+ * would return e.g. `JOIN myOwn\nORDER BY 1` where a name is expected.
258
+ */
259
+ this._errorContexts = new Set();
233
260
  this._input = input;
234
261
  this._allTokens = allTokens || [];
235
262
  this._caretTokenIndex = caretTokenIndex !== null && caretTokenIndex !== void 0 ? caretTokenIndex : -1;
236
263
  this._entitiesSet = new Set();
237
264
  this._stmtStack = new SimpleStack();
238
265
  this._entityStack = new SimpleStack();
266
+ this._cteDefinitions = [];
239
267
  this._rootStmt = null;
240
268
  this._caretStmtScopeDepth = 0;
241
269
  this._caretStmt = null;
242
270
  }
243
271
  visitTerminal() { }
244
- visitErrorNode() { }
272
+ visitErrorNode(node) {
273
+ /**
274
+ * Mark every ancestor. An entity pushed on one of them would carry the
275
+ * error-tainted source slice as its `text`. Entities are always pushed from
276
+ * `exit<Rule>` callbacks, so this runs before the context is used.
277
+ */
278
+ let parent = node.parent;
279
+ while (parent) {
280
+ this._errorContexts.add(parent);
281
+ parent = parent.parent;
282
+ }
283
+ }
245
284
  enterEveryRule() { }
246
285
  exitEveryRule() { }
247
286
  getRootStmt() {
@@ -254,11 +293,16 @@ export class EntityCollector {
254
293
  this._entitiesSet.clear();
255
294
  this._stmtStack.clear();
256
295
  this._entityStack.clear();
296
+ this._cteDefinitions = [];
257
297
  this._rootStmt = null;
298
+ this._errorContexts.clear();
258
299
  }
259
300
  exitProgram() {
301
+ this.finalizeCteDefinitions();
302
+ this.bindCteReferences();
260
303
  const entities = Array.from(this._entitiesSet);
261
304
  if (this._caretTokenIndex !== -1) {
305
+ this.markCteAccessibility();
262
306
  this.attachAccessibleToEntities(entities);
263
307
  }
264
308
  this._entityStack.clear();
@@ -376,6 +420,11 @@ export class EntityCollector {
376
420
  if (!this._entityStack.isEmpty()) {
377
421
  this.combineEntitiesAndAdd(stmtContext);
378
422
  }
423
+ // Statement-scoped walks never exit program. Finalize once the outermost statement pops.
424
+ if (this._stmtStack.isEmpty()) {
425
+ this.finalizeCteDefinitions();
426
+ this.bindCteReferences();
427
+ }
379
428
  // If the current statement contains the caret and its scope depth equals the caret statement's scope depth
380
429
  // then set it as the nearest statement containing the caret
381
430
  if (stmtContext.isContainCaret &&
@@ -386,6 +435,9 @@ export class EntityCollector {
386
435
  return stmtContext;
387
436
  }
388
437
  pushEntity(ctx, type, attrInfo, metaInfo) {
438
+ // A context containing a parse error has no usable `text`.
439
+ if (this._errorContexts.has(ctx))
440
+ return null;
389
441
  const entityContext = toEntityContext(ctx, type, this._input, this._stmtStack.peek(), attrInfo, metaInfo);
390
442
  if (entityContext) {
391
443
  if (this._stmtStack.isEmpty()) {
@@ -545,4 +597,253 @@ export class EntityCollector {
545
597
  }
546
598
  return finalEntities;
547
599
  }
600
+ /**
601
+ * Collect one CTE definition.
602
+ * `endTokenIndex` is the closing token of this item only (not a following CTE).
603
+ * Column names, when present, become the outward schema and do not replace the body query result.
604
+ */
605
+ collectCte(cteCtx, nameCtx, bodyCtx, endTokenIndex, columnNameCtxs = [], columnListCtx = null) {
606
+ if (!nameCtx || endTokenIndex < 0)
607
+ return;
608
+ const entity = this.pushEntity(nameCtx, EntityContextType.TABLE, undefined, {
609
+ declareType: TableDeclareType.EXPRESSION,
610
+ });
611
+ if (!entity || !isCommonEntityContext(entity))
612
+ return;
613
+ this._cteDefinitions.push({
614
+ entity,
615
+ name: entity.text,
616
+ body: bodyCtx,
617
+ columnNameCtxs,
618
+ columnListCtx,
619
+ endTokenIndex,
620
+ scopeEndTokenIndex: this.cteScopeEndTokenIndex(cteCtx, endTokenIndex),
621
+ outward: null,
622
+ });
623
+ }
624
+ cteScopeEndTokenIndex(cteCtx, endTokenIndex) {
625
+ var _a, _b, _c, _d, _e;
626
+ let scopeEnd = endTokenIndex;
627
+ let current = cteCtx.parent;
628
+ while (current) {
629
+ if (CTE_CLAUSE_CONTEXT_NAMES.has(current.constructor.name)) {
630
+ scopeEnd = (_c = (_b = (_a = current.parent) === null || _a === void 0 ? void 0 : _a.stop) === null || _b === void 0 ? void 0 : _b.tokenIndex) !== null && _c !== void 0 ? _c : endTokenIndex;
631
+ break;
632
+ }
633
+ current = current.parent;
634
+ }
635
+ if (!current) {
636
+ scopeEnd = (_e = (_d = this._stmtStack.peek()) === null || _d === void 0 ? void 0 : _d.position.endTokenIndex) !== null && _e !== void 0 ? _e : endTokenIndex;
637
+ }
638
+ // An incomplete FROM/JOIN stops the parsed query before the caret.
639
+ // Keep the CTE visible until a semicolon or a closing paren leaves this query.
640
+ return this.extendUnclosedScope(scopeEnd);
641
+ }
642
+ extendUnclosedScope(scopeEnd) {
643
+ var _a;
644
+ let depth = 0;
645
+ let end = scopeEnd;
646
+ for (let i = scopeEnd + 1; i < this._allTokens.length; i++) {
647
+ const token = this._allTokens[i];
648
+ if (token.channel === Token.HIDDEN_CHANNEL || token.type === Token.EOF)
649
+ continue;
650
+ const text = (_a = token.text) !== null && _a !== void 0 ? _a : '';
651
+ if (text === ';')
652
+ break;
653
+ if (text === '(') {
654
+ depth++;
655
+ }
656
+ else if (text === ')') {
657
+ if (depth === 0)
658
+ break;
659
+ depth--;
660
+ }
661
+ end = token.tokenIndex;
662
+ }
663
+ return end;
664
+ }
665
+ finalizeCteDefinitions() {
666
+ this._cteDefinitions.forEach((cte) => {
667
+ if (cte.outward) {
668
+ cte.entity.relatedEntities = [cte.outward];
669
+ return;
670
+ }
671
+ const fromColumnList = cte.columnListCtx
672
+ ? this.createColumnListQueryResultFromTokens(cte.entity.belongStmt, this.columnTokens(cte.columnListCtx))
673
+ : cte.columnNameCtxs.length
674
+ ? this.createColumnListQueryResult(cte.entity.belongStmt, cte.columnNameCtxs)
675
+ : null;
676
+ const found = fromColumnList !== null && fromColumnList !== void 0 ? fromColumnList : this.findOutwardQueryResult(cte.body);
677
+ // Column-list results are already a dedicated object. A body query
678
+ // result is shared with the entity set, so dialects may copy it.
679
+ const outward = fromColumnList !== null && fromColumnList !== void 0 ? fromColumnList : (found ? this.adaptOutwardQueryResult(found) : null);
680
+ cte.outward = outward;
681
+ if (outward) {
682
+ cte.entity.relatedEntities = [outward];
683
+ }
684
+ });
685
+ }
686
+ /**
687
+ * CTE outward schema defaults to the body query result itself.
688
+ * Override when that result's text covers more than the select list.
689
+ */
690
+ adaptOutwardQueryResult(entity) {
691
+ return entity;
692
+ }
693
+ sourceSlice(startIndex, endIndex) {
694
+ return this._input.slice(startIndex, endIndex + 1);
695
+ }
696
+ findOutwardQueryResult(body) {
697
+ var _a, _b, _c, _d;
698
+ if (!(body === null || body === void 0 ? void 0 : body.start) || !body.stop)
699
+ return null;
700
+ const start = body.start.tokenIndex;
701
+ const stop = body.stop.tokenIndex;
702
+ let best = null;
703
+ for (const entity of this._entitiesSet) {
704
+ if (entity.entityContextType !== EntityContextType.QUERY_RESULT ||
705
+ !isCommonEntityContext(entity)) {
706
+ continue;
707
+ }
708
+ const tokenStart = entity.position.startTokenIndex;
709
+ const tokenStop = entity.position.endTokenIndex;
710
+ if (tokenStart < start || tokenStop > stop)
711
+ continue;
712
+ if (!best) {
713
+ best = entity;
714
+ continue;
715
+ }
716
+ const bestDepth = (_b = (_a = best.belongStmt) === null || _a === void 0 ? void 0 : _a.scopeDepth) !== null && _b !== void 0 ? _b : 0;
717
+ const depth = (_d = (_c = entity.belongStmt) === null || _c === void 0 ? void 0 : _c.scopeDepth) !== null && _d !== void 0 ? _d : 0;
718
+ if (depth < bestDepth ||
719
+ (depth === bestDepth && tokenStart < best.position.startTokenIndex)) {
720
+ best = entity;
721
+ }
722
+ }
723
+ return best;
724
+ }
725
+ columnTokens(listCtx) {
726
+ const tokens = [];
727
+ const skip = new Set(['(', ')', ',', '.', '[', ']']);
728
+ const visit = (ctx) => {
729
+ var _a;
730
+ for (let i = 0; i < ctx.getChildCount(); i++) {
731
+ const child = ctx.getChild(i);
732
+ if (child instanceof ParserRuleContext) {
733
+ visit(child);
734
+ continue;
735
+ }
736
+ if (child instanceof TerminalNode) {
737
+ const text = (_a = child.symbol.text) !== null && _a !== void 0 ? _a : '';
738
+ if (text && !skip.has(text))
739
+ tokens.push(child.symbol);
740
+ }
741
+ }
742
+ };
743
+ visit(listCtx);
744
+ return tokens;
745
+ }
746
+ createColumnListQueryResultFromTokens(belongStmt, tokens) {
747
+ const columns = [];
748
+ tokens.forEach((token) => {
749
+ const word = tokenToWord(token, this._input);
750
+ if (!word.text.trim())
751
+ return;
752
+ columns.push({
753
+ entityContextType: EntityContextType.COLUMN,
754
+ text: word.text,
755
+ position: {
756
+ startIndex: word.startIndex,
757
+ endIndex: word.endIndex,
758
+ line: word.line,
759
+ startColumn: word.startColumn,
760
+ endColumn: word.endColumn,
761
+ startTokenIndex: token.tokenIndex,
762
+ endTokenIndex: token.tokenIndex,
763
+ },
764
+ belongStmt,
765
+ declareType: ColumnDeclareType.LITERAL,
766
+ relatedEntities: null,
767
+ [AttrName.colType]: null,
768
+ [AttrName.comment]: null,
769
+ isAccessible: null,
770
+ });
771
+ });
772
+ return this.wrapOutwardColumns(belongStmt, columns);
773
+ }
774
+ createColumnListQueryResult(belongStmt, columnNameCtxs) {
775
+ const columns = [];
776
+ columnNameCtxs.forEach((nameCtx) => {
777
+ const column = toEntityContext(nameCtx, EntityContextType.COLUMN, this._input, belongStmt, undefined, { declareType: ColumnDeclareType.LITERAL });
778
+ if (column && isColumnEntityContext(column) && column.text.trim()) {
779
+ columns.push(column);
780
+ }
781
+ });
782
+ return this.wrapOutwardColumns(belongStmt, columns);
783
+ }
784
+ wrapOutwardColumns(belongStmt, columns) {
785
+ if (!columns.length)
786
+ return null;
787
+ const first = columns[0].position;
788
+ const last = columns[columns.length - 1].position;
789
+ return {
790
+ entityContextType: EntityContextType.QUERY_RESULT,
791
+ text: columns.map((column) => column.text).join(', '),
792
+ position: {
793
+ startIndex: first.startIndex,
794
+ endIndex: last.endIndex,
795
+ line: first.line,
796
+ startColumn: first.startColumn,
797
+ endColumn: last.endColumn,
798
+ startTokenIndex: first.startTokenIndex,
799
+ endTokenIndex: last.endTokenIndex,
800
+ },
801
+ belongStmt,
802
+ relatedEntities: null,
803
+ columns,
804
+ [AttrName.comment]: null,
805
+ isAccessible: null,
806
+ };
807
+ }
808
+ bindCteReferences() {
809
+ this._entitiesSet.forEach((entity) => {
810
+ if (entity.entityContextType !== EntityContextType.TABLE ||
811
+ !isCommonEntityContext(entity) ||
812
+ entity.declareType !== TableDeclareType.LITERAL) {
813
+ return;
814
+ }
815
+ const cte = this.resolveCte(entity.text, entity.position.startTokenIndex);
816
+ if (!cte)
817
+ return;
818
+ entity.declareType = TableDeclareType.EXPRESSION;
819
+ entity.relatedEntities = cte.outward ? [cte.outward] : null;
820
+ });
821
+ }
822
+ markCteAccessibility() {
823
+ const caretTokenIndex = this.getPrevNonHiddenTokenIndex(this._caretTokenIndex);
824
+ if (!Number.isFinite(caretTokenIndex)) {
825
+ this._cteDefinitions.forEach((cte) => {
826
+ cte.entity.isAccessible = false;
827
+ });
828
+ return;
829
+ }
830
+ this._cteDefinitions.forEach((cte) => {
831
+ cte.entity.isAccessible = this.resolveCte(cte.name, caretTokenIndex) === cte;
832
+ });
833
+ }
834
+ /** Nearest preceding CTE whose scope covers `tokenIndex`. */
835
+ resolveCte(name, tokenIndex) {
836
+ let winner = null;
837
+ this._cteDefinitions.forEach((cte) => {
838
+ if (!sameIdent(cte.name, name))
839
+ return;
840
+ if (tokenIndex <= cte.endTokenIndex)
841
+ return;
842
+ if (tokenIndex > cte.scopeEndTokenIndex)
843
+ return;
844
+ if (!winner || cte.endTokenIndex > winner.endTokenIndex)
845
+ winner = cte;
846
+ });
847
+ return winner;
848
+ }
548
849
  }
@@ -1,4 +1,4 @@
1
- import { AtomExpressionTableContext, AtomFunctionTableContext, CatalogPathContext, CatalogPathCreateContext, ColumnNameCreateContext, CreateCatalogContext, CreateDatabaseContext, CreateFunctionContext, CreateTableContext, CreateViewContext, DatabasePathContext, DatabasePathCreateContext, FunctionNameCreateContext, InsertStatementContext, QueryStatementContext, SelectExpressionColumnNameContext, SelectListContext, SelectLiteralColumnNameContext, SelectWindowItemColumnNameContext, SqlStatementContext, TableAllColumnsContext, TablePathContext, TablePathCreateContext, ViewPathContext, ViewPathCreateContext } from '../../lib/flink/FlinkSqlParser';
1
+ import { AtomExpressionTableContext, AtomFunctionTableContext, CatalogPathContext, CatalogPathCreateContext, ColumnNameCreateContext, CreateCatalogContext, CreateDatabaseContext, CreateFunctionContext, CreateTableContext, CreateViewContext, DatabasePathContext, DatabasePathCreateContext, FunctionNameCreateContext, InsertStatementContext, QueryStatementContext, SelectExpressionColumnNameContext, SelectListContext, SelectLiteralColumnNameContext, SelectWindowItemColumnNameContext, SqlStatementContext, TableAllColumnsContext, TablePathContext, TablePathCreateContext, ViewPathContext, ViewPathCreateContext, WithItemContext } from '../../lib/flink/FlinkSqlParser';
2
2
  import { FlinkSqlParserListener } from '../../lib/flink/FlinkSqlParserListener';
3
3
  import { EntityCollector } from '../common/entityCollector';
4
4
  export declare class FlinkEntityCollector extends EntityCollector implements FlinkSqlParserListener {
@@ -11,6 +11,7 @@ export declare class FlinkEntityCollector extends EntityCollector implements Fli
11
11
  exitAtomFunctionTable(ctx: AtomFunctionTableContext): void;
12
12
  exitAtomExpressionTable(ctx: AtomExpressionTableContext): void;
13
13
  exitTablePathCreate(ctx: TablePathCreateContext): void;
14
+ exitWithItem(ctx: WithItemContext): void;
14
15
  exitViewPath(ctx: ViewPathContext): void;
15
16
  exitViewPathCreate(ctx: ViewPathCreateContext): void;
16
17
  exitFunctionNameCreate(ctx: FunctionNameCreateContext): void;
@@ -58,6 +58,10 @@ export class FlinkEntityCollector extends EntityCollector {
58
58
  },
59
59
  ]);
60
60
  }
61
+ exitWithItem(ctx) {
62
+ var _a, _b;
63
+ this.collectCte(ctx, ctx.withItemName(), ctx.queryStatement(), (_b = (_a = ctx.stop) === null || _a === void 0 ? void 0 : _a.tokenIndex) !== null && _b !== void 0 ? _b : -1, ctx.columnName());
64
+ }
61
65
  exitViewPath(ctx) {
62
66
  this.pushEntity(ctx, EntityContextType.VIEW);
63
67
  }
@@ -67,7 +67,7 @@ export class FlinkSQL extends BasicSQL {
67
67
  const keywords = [];
68
68
  for (let candidate of candidates.rules) {
69
69
  const [ruleType, candidateRule] = candidate;
70
- const tokenRanges = allTokens.slice(candidateRule.startTokenIndex, caretTokenIndex + 1);
70
+ const tokenRanges = this.getCandidateTokenRanges(candidates, candidateRule.startTokenIndex, allTokens, caretTokenIndex);
71
71
  let syntaxContextType = void 0;
72
72
  switch (ruleType) {
73
73
  case FlinkSqlParser.RULE_catalogPath: {
@@ -1,13 +1,18 @@
1
1
  import type { GenericSqlListener } from '../../lib/generic/GenericSqlListener';
2
- import { AlterTableContext, ColumnDefinitionContext, ColumnRefContext, CreateTableContext, DeleteContext, DropTableContext, InsertContext, QuerySpecificationContext, QueryStatementContext, SelectItemContext, SingleStatementContext, TableNameContext, TableNameCreateContext, UpdateContext } from '../../lib/generic/GenericSqlParser';
3
- import { EntityCollector } from '../common/entityCollector';
2
+ import { AlterTableContext, ColumnDefinitionContext, ColumnRefContext, CreateTableContext, DeleteContext, DropTableContext, InsertContext, NamedQueryContext, QuerySpecificationContext, QueryStatementContext, SelectItemContext, SingleStatementContext, TableNameContext, TableNameCreateContext, UpdateContext } from '../../lib/generic/GenericSqlParser';
3
+ import { CommonEntityContext, EntityCollector } from '../common/entityCollector';
4
4
  export declare class GenericEntityCollector extends EntityCollector implements GenericSqlListener {
5
+ /** Select-list source text for each query result. The result itself keeps the full statement. */
6
+ private readonly selectListText;
7
+ enterProgram(): void;
5
8
  /** ====== Entity Begin */
6
9
  exitTableName(ctx: TableNameContext): void;
10
+ exitNamedQuery(ctx: NamedQueryContext): void;
7
11
  exitTableNameCreate(ctx: TableNameCreateContext): void;
8
12
  exitColumnRef(ctx: ColumnRefContext): void;
9
13
  exitColumnDefinition(ctx: ColumnDefinitionContext): void;
10
14
  exitQuerySpecification(ctx: QuerySpecificationContext): void;
15
+ protected adaptOutwardQueryResult(entity: CommonEntityContext): CommonEntityContext;
11
16
  exitSelectItem(ctx: SelectItemContext): void;
12
17
  private isSimpleColumnReference;
13
18
  /** ===== Statement begin */