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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dt-sql-parser",
"version": "4.5.0-beta.0",
"version": "4.5.0-beta.1",
"authors": "DTStack Corporation",
"description": "SQL Parsers for BigData, built with antlr4",
"keywords": [
Expand Down
1 change: 1 addition & 0 deletions src/grammar/flink/FlinkSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ columnProjectItem
| selectLiteralColumnName (columnAlias | KW_AS? expression)?
| tableAllColumns columnAlias?
| selectExpressionColumnName (columnAlias | KW_AS? columnName)?
| {this.shouldMatchEmpty()}? emptyColumn
;

selectWindowItemColumnName
Expand Down
1 change: 1 addition & 0 deletions src/grammar/hive/HiveSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,7 @@ selectItem
| KW_AS LPAREN alias=id_ (COMMA alias=id_)* RPAREN
)?
)
| {this.shouldMatchEmpty()}? emptyColumn
;

selectLiteralColumnName
Expand Down
1 change: 1 addition & 0 deletions src/grammar/impala/ImpalaSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ selectItem
: selectLiteralColumnName columnAlias?
| selectExpressionColumnName columnAlias?
| tableAllColumns
| {this.shouldMatchEmpty()}? emptyColumn
;

columnAlias
Expand Down
17 changes: 6 additions & 11 deletions src/grammar/mysql/MySqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -1205,9 +1205,10 @@ selectElements
;

selectElement
: tableAllColumns
| selectLiteralColumnName (KW_AS? alias=uid)?
| selectExpressionColumnName (KW_AS? alias=uid)?
: tableAllColumns # selectElement_star
| selectLiteralColumnName (KW_AS? alias=uid)? # selectElement_label
| selectExpressionColumnName (KW_AS? alias=uid)? # selectElement_expr
| uid DOT {this.shouldMatchEmpty()}? emptyColumn # selectElement_dot_empty
;

tableAllColumns
Expand Down Expand Up @@ -2424,7 +2425,7 @@ emptyColumn
;

columnName
: uid (dottedIdAllowEmpty dottedIdAllowEmpty?)?
: uid (dottedId dottedId?)?
| .? dottedId dottedId?
| {this.shouldMatchEmpty()}? emptyColumn
;
Expand All @@ -2436,7 +2437,7 @@ columnNamePath

columnNamePathAllowEmpty
: {this.shouldMatchEmpty()}? emptyColumn
| uid (dottedIdAllowEmpty dottedIdAllowEmpty?)?
| uid (dottedId dottedId?)?
;

tableSpaceNameCreate
Expand Down Expand Up @@ -2574,12 +2575,6 @@ dottedId
| '.' uid
;

dottedIdAllowEmpty
: DOT ID
| '.' uid
| {this.shouldMatchEmpty()}? DOT emptyColumn
;

decimalLiteral
: DECIMAL_LITERAL
| ZERO_DECIMAL
Expand Down
14 changes: 11 additions & 3 deletions src/grammar/postgresql/PostgreSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -2615,7 +2615,8 @@ when_clause
;

indirectionEl
: DOT (colLabel | STAR)
: DOT indirectionLabel
| DOT STAR
| OPEN_BRACKET (expression | expression? COLON expression?) CLOSE_BRACKET
;

Expand All @@ -2634,6 +2635,8 @@ targetList
targetEl
: tableAllColumns # target_star
| (selectLiteralColumnName | selectExpressionColumnName) (KW_AS? alias=identifier |) # target_label
| colId DOT {this.entityCollecting}? emptyColumn # target_dot_empty
| {this.entityCollecting}? emptyColumn # target_empty
;

tableAllColumns
Expand Down Expand Up @@ -2722,18 +2725,17 @@ procedureNameCreate
| colId indirection
;

// Empty column rule for entity collection
emptyColumn
:
;

columnName
: colId optIndirection
| {this.shouldMatchEmpty()}? (colId DOT emptyColumn | emptyColumn)
;

columnNamePath
: colId optIndirection
| {this.shouldMatchEmpty()}? (colId DOT emptyColumn | emptyColumn)
;

columnNameCreate
Expand Down Expand Up @@ -2800,6 +2802,12 @@ colLabel
| reservedKeyword
;

indirectionLabel
: identifier
| colNameKeyword
| typeFuncNameKeyword
;

identifier
: Identifier (KW_UESCAPE anysconst)?
| stringConst
Expand Down
1 change: 1 addition & 0 deletions src/grammar/spark/SparkSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ namedExpression
: (tableAllColumns | selectLiteralColumnName | selectExpressionColumnName) (
KW_AS? (alias=errorCapturingIdentifier | identifierList)
)?
| {this.shouldMatchEmpty()}? emptyColumn
;

namedExpressionSeq
Expand Down
51 changes: 46 additions & 5 deletions src/lib/SQLParserBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,50 @@ export abstract class SQLParserBase<T = antlr.ParserRuleContext> extends antlr.P

public entityCollecting = false;

public shouldMatchEmpty () {
return this.entityCollecting
&& (this.tokenStream.LT(-1)?.tokenIndex ?? Infinity) <= this.caretTokenIndex
&& (this.tokenStream.LT(1)?.tokenIndex ?? -Infinity) >= this.caretTokenIndex
/**
* Semantic predicate to determine whether to match empty column.
*
* Key design:
* 1. Only match empty column in entityCollecting mode
* 2. Check if caret position is at the empty column position
* 3. In validate mode (entityCollecting=false), this predicate returns false
* and reports an error to ensure incomplete SQL is caught
*
* IMPORTANT: This predicate should be used carefully to avoid affecting
* prediction in non-entity-collecting contexts.
*/
public shouldMatchEmpty (ruleName?: string) {
// Only match in entityCollecting mode or when caret position is specified (suggestion mode)
if (this.entityCollecting || this.caretTokenIndex >= 0) {
// If no caret position specified, match all empty columns
if (this.caretTokenIndex < 0) {
return true;
}

// Check if caret is at the position where empty column would be
const prevTokenIndex = this.tokenStream.LT(-1)?.tokenIndex;
const nextTokenIndex = this.tokenStream.LT(1)?.tokenIndex;

// Match if caret is between previous and next token
if (prevTokenIndex !== undefined && nextTokenIndex !== undefined) {
return prevTokenIndex <= this.caretTokenIndex && nextTokenIndex >= this.caretTokenIndex;
}

// If only previous token exists, match if caret is after it
if (prevTokenIndex !== undefined) {
return prevTokenIndex <= this.caretTokenIndex;
}

// If only next token exists, match if caret is before it
if (nextTokenIndex !== undefined) {
return nextTokenIndex >= this.caretTokenIndex;
}

return false;
}

// In pure validate mode, don't match empty columns
// This allows ANTLR to report errors naturally
return false;
}
}
}
2 changes: 1 addition & 1 deletion src/lib/flink/FlinkSqlParser.interp

Large diffs are not rendered by default.

Loading