Skip to content

next merge main #397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Feb 13, 2025
Merged
30 changes: 29 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [4.1.0-beta.0](https://github.com/DTStack/dt-sql-parser/compare/v4.0.1...v4.1.0-beta.0) (2024-08-27)


### Features

* add alter table stmt ([#312](https://github.com/DTStack/dt-sql-parser/issues/312)) ([5aade9e](https://github.com/DTStack/dt-sql-parser/commit/5aade9e6daafc2c6e70c5202d7ef06572ec37f6e))
* add benchmark test suite ([#273](https://github.com/DTStack/dt-sql-parser/issues/273)) ([de1bd9d](https://github.com/DTStack/dt-sql-parser/commit/de1bd9de4cb7c3b42d51bedd79635eb91afba9ed))
* **basicSql:** remove judge splitListener/collectListener, all sqlParser implements it ([#316](https://github.com/DTStack/dt-sql-parser/issues/316)) ([eb2e920](https://github.com/DTStack/dt-sql-parser/commit/eb2e920e345aef98285ba261c2060db61d1d56b8))
* collect entity's attribute([#333](https://github.com/DTStack/dt-sql-parser/issues/333)) ([a3b6b7e](https://github.com/DTStack/dt-sql-parser/commit/a3b6b7eb8bad2444b16481985278461c35360570))
* **flinksql:** collect comment, type attribute for entity ([#319](https://github.com/DTStack/dt-sql-parser/issues/319)) ([ae52ebd](https://github.com/DTStack/dt-sql-parser/commit/ae52ebdd6b6d1511cf92eb09521b06bdec66ba0d)), closes [#305](https://github.com/DTStack/dt-sql-parser/issues/305)
* improve errorListener msg ([#281](https://github.com/DTStack/dt-sql-parser/issues/281)) ([deef123](https://github.com/DTStack/dt-sql-parser/commit/deef1238bb25d5bfee80ddaf1fea5ad48178d17b))
* sync some useful syntax from antlr/grammars-v4 ([95a1087](https://github.com/DTStack/dt-sql-parser/commit/95a108744bb40e418056faaf86bd97b85dd191f8))
* upgrade trino to 450 ([#323](https://github.com/DTStack/dt-sql-parser/issues/323)) ([2b0de6a](https://github.com/DTStack/dt-sql-parser/commit/2b0de6a3da16561ec52b0c69d4e052226d54a553))
* use common sql to run benchmark ([#326](https://github.com/DTStack/dt-sql-parser/issues/326)) ([76d0900](https://github.com/DTStack/dt-sql-parser/commit/76d090040e7af26227727673a82f77cda08b3f9e))


### Bug Fixes

* alert to alterView ([#346](https://github.com/DTStack/dt-sql-parser/issues/346)) ([9ba5100](https://github.com/DTStack/dt-sql-parser/commit/9ba51007e2f21ab8bc42623596ee281801904cfa))
* **benchmark:** add reports dir judge and remove plsql and include pgsql ([9c534c2](https://github.com/DTStack/dt-sql-parser/commit/9c534c25cacba3cfba6bd234c68e8f27bd90b2e2))
* build mysql ([5d6ff46](https://github.com/DTStack/dt-sql-parser/commit/5d6ff4662a11acf9f16b1f18c41c204922890df9))
* createFunction and createFunctionLoadable ([e83449a](https://github.com/DTStack/dt-sql-parser/commit/e83449a0cc0a50be510c7b4a3337597b1890fc92))
* flinksql function params add more time functions ([#347](https://github.com/DTStack/dt-sql-parser/issues/347)) ([b835c4b](https://github.com/DTStack/dt-sql-parser/commit/b835c4b5b506c8e4bf0bd9c99fe66c15e53a179b))
* **impala:** fix alter table change statement ([#332](https://github.com/DTStack/dt-sql-parser/issues/332)) ([4a9681e](https://github.com/DTStack/dt-sql-parser/commit/4a9681ed3bd188e41c30a6d7be39d6e77df7f61b))
* mysql case when ([#317](https://github.com/DTStack/dt-sql-parser/issues/317)) ([fea1ad1](https://github.com/DTStack/dt-sql-parser/commit/fea1ad1a357b70291a240eca6d2058bab9b49469))
* **postgresql:** combine plsql_unreserved_keyword to unreserved_keyword and remove unused rules ([7884cbe](https://github.com/DTStack/dt-sql-parser/commit/7884cbe37844c057fa41fde4d0716af43c4023af))
* spell check ([#337](https://github.com/DTStack/dt-sql-parser/issues/337)) ([694b0cd](https://github.com/DTStack/dt-sql-parser/commit/694b0cdf15943d02a9402a748155a1b06508af95))

### [4.0.2](https://github.com/DTStack/dt-sql-parser/compare/v4.0.1...v4.0.2) (2024-06-19)


Expand Down Expand Up @@ -61,7 +89,7 @@ All notable changes to this project will be documented in this file. See [standa

### Features

* add toMatchUnorderedArrary matcher and apply it ([#271](https://github.com/DTStack/dt-sql-parser/issues/271)) ([a05f099](https://github.com/DTStack/dt-sql-parser/commit/a05f099aa1ad555c408bc2018240fb4611ec09b8))
* add toMatchUnorderedArray matcher and apply it ([#271](https://github.com/DTStack/dt-sql-parser/issues/271)) ([a05f099](https://github.com/DTStack/dt-sql-parser/commit/a05f099aa1ad555c408bc2018240fb4611ec09b8))
* collect entity ([#265](https://github.com/DTStack/dt-sql-parser/issues/265)) ([a997211](https://github.com/DTStack/dt-sql-parser/commit/a99721162be0d463b513f53bb13ada6d10168548)), closes [#256](https://github.com/DTStack/dt-sql-parser/issues/256) [#263](https://github.com/DTStack/dt-sql-parser/issues/263) [#268](https://github.com/DTStack/dt-sql-parser/issues/268)
* migrate to antlr4ng ([#267](https://github.com/DTStack/dt-sql-parser/issues/267)) ([195878d](https://github.com/DTStack/dt-sql-parser/commit/195878da9bb1ff8011b5d60c02389fa66d2bc0b8))
* **spark:** support materialized view for spark sql ([#262](https://github.com/DTStack/dt-sql-parser/issues/262)) ([5ce89cb](https://github.com/DTStack/dt-sql-parser/commit/5ce89cb421de18330d56e23a4ab5b658b2130a0b))
Expand Down
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.0.2",
"version": "4.1.0-beta.0",
"authors": "DTStack Corporation",
"description": "SQL Parsers for BigData, built with antlr4",
"keywords": [
Expand Down
12 changes: 9 additions & 3 deletions scripts/benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,19 @@ function checkVersion() {
if (semver.lt(currentVersion, MIN_VERSION)) {
console.error(
chalk.bold.red(
`Current Node.js version (v${currentVersion}) is lower than required version (v${semver.major(MIN_VERSION)}.x)`
`Current Node.js version (v${currentVersion}) is lower than required version (v${semver.major(
MIN_VERSION
)}.x)`
)
);
return false;
} else {
if (isRelease && semver.lt(currentVersion, RELEASE_VERSION)) {
console.error(
chalk.bold.red(
`Node.js version v${semver.major(RELEASE_VERSION)}.x+ is required for release benchmark!`
`Node.js version v${semver.major(
RELEASE_VERSION
)}.x+ is required for release benchmark!`
)
);
return false;
Expand Down Expand Up @@ -81,7 +85,9 @@ function prompt() {
'Cold start' +
(isNodeVersionOk
? ''
: ` (Only supported on Node.js v${semver.major(RECOMMENDED_VERSION)}.x+)`),
: ` (Only supported on Node.js v${semver.major(
RECOMMENDED_VERSION
)}.x+)`),
value: 'cold',
disabled: !isNodeVersionOk,
},
Expand Down
46 changes: 23 additions & 23 deletions src/grammar/flink/FlinkSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ createTable
simpleCreateTable
: KW_CREATE KW_TEMPORARY? KW_TABLE ifNotExists? tablePathCreate LR_BRACKET columnOptionDefinition (
COMMA columnOptionDefinition
)* (COMMA watermarkDefinition)? (COMMA tableConstraint)? (COMMA selfDefinitionClause)? RR_BRACKET commentSpec? partitionDefinition? withOption
likeDefinition?
)* (COMMA watermarkDefinition)? (COMMA tableConstraint)? (COMMA selfDefinitionClause)? RR_BRACKET (
KW_COMMENT comment=STRING_LITERAL
)? partitionDefinition? withOption likeDefinition?
;

/*
Expand All @@ -175,7 +176,7 @@ columnOptionDefinition
;

physicalColumnDefinition
: columnNameCreate columnType columnConstraint? commentSpec?
: columnNameCreate columnType columnConstraint? (KW_COMMENT comment=STRING_LITERAL)?
;

columnNameCreate
Expand All @@ -193,8 +194,8 @@ columnNameList
;

columnType
: typeName=(KW_DATE | KW_BOOLEAN | KW_NULL)
| typeName=(
: colType=(KW_DATE | KW_BOOLEAN | KW_NULL)
| colType=(
KW_CHAR
| KW_VARCHAR
| KW_STRING
Expand All @@ -210,12 +211,12 @@ columnType
| KW_TIMESTAMP_LTZ
| KW_DATETIME
) lengthOneDimension?
| typeName=KW_TIMESTAMP lengthOneDimension? ((KW_WITHOUT | KW_WITH) KW_LOCAL? KW_TIME KW_ZONE)?
| typeName=(KW_DECIMAL | KW_DEC | KW_NUMERIC | KW_FLOAT | KW_DOUBLE) lengthTwoOptionalDimension?
| type=(KW_ARRAY | KW_MULTISET) lengthOneTypeDimension?
| type=KW_MAP mapTypeDimension?
| type=KW_ROW rowTypeDimension?
| type=KW_RAW lengthTwoStringDimension?
| colType=KW_TIMESTAMP lengthOneDimension? ((KW_WITHOUT | KW_WITH) KW_LOCAL? KW_TIME KW_ZONE)?
| colType=(KW_DECIMAL | KW_DEC | KW_NUMERIC | KW_FLOAT | KW_DOUBLE) lengthTwoOptionalDimension?
| colType=(KW_ARRAY | KW_MULTISET) lengthOneTypeDimension?
| colType=KW_MAP mapTypeDimension?
| colType=KW_ROW rowTypeDimension?
| colType=KW_RAW lengthTwoStringDimension?
;

lengthOneDimension
Expand All @@ -240,17 +241,14 @@ mapTypeDimension

rowTypeDimension
: LESS_SYMBOL columnName columnType (COMMA columnName columnType)* GREATER_SYMBOL
| LR_BRACKET columnName columnType (COMMA columnName columnType)* RR_BRACKET
;

columnConstraint
: (KW_CONSTRAINT constraintName)? KW_PRIMARY KW_KEY (KW_NOT KW_ENFORCED)?
| KW_NOT? KW_NULL
;

commentSpec
: KW_COMMENT STRING_LITERAL
;

metadataColumnDefinition
: columnNameCreate columnType KW_METADATA (KW_FROM metadataKey)? KW_VIRTUAL?
;
Expand All @@ -260,7 +258,7 @@ metadataKey
;

computedColumnDefinition
: columnNameCreate KW_AS computedColumnExpression commentSpec?
: columnNameCreate KW_AS computedColumnExpression (KW_COMMENT comment=STRING_LITERAL)?
;

// 计算表达式
Expand Down Expand Up @@ -316,11 +314,13 @@ createCatalog
;

createDatabase
: KW_CREATE KW_DATABASE ifNotExists? databasePathCreate commentSpec? withOption
: KW_CREATE KW_DATABASE ifNotExists? databasePathCreate (KW_COMMENT comment=STRING_LITERAL)? withOption
;

createView
: KW_CREATE KW_TEMPORARY? KW_VIEW ifNotExists? viewPathCreate columnNameList? commentSpec? KW_AS queryStatement
: KW_CREATE KW_TEMPORARY? KW_VIEW ifNotExists? viewPathCreate columnNameList? (
KW_COMMENT comment=STRING_LITERAL
)? KW_AS queryStatement
;

createFunction
Expand Down Expand Up @@ -513,8 +513,8 @@ tableReference
;

tablePrimary
: KW_TABLE? tablePath systemTimePeriod? (KW_AS? correlationName)?
| viewPath systemTimePeriod? (KW_AS? correlationName)?
: KW_TABLE? tablePath systemTimePeriod?
| viewPath systemTimePeriod?
| KW_LATERAL KW_TABLE LR_BRACKET functionName LR_BRACKET functionParam (COMMA functionParam)* RR_BRACKET RR_BRACKET
| KW_LATERAL? LR_BRACKET queryStatement RR_BRACKET
| KW_UNNEST LR_BRACKET expression RR_BRACKET
Expand Down Expand Up @@ -648,7 +648,7 @@ limitClause
;

partitionByClause
: KW_PARTITION KW_BY columnName (COMMA columnName)*
: KW_PARTITION KW_BY (columnName | primaryExpression) (COMMA (columnName | primaryExpression))*
;

quantifiers
Expand Down Expand Up @@ -834,7 +834,7 @@ intervalValue
;

tableAlias
: KW_AS? identifier identifierList?
: KW_AS? alias=identifier identifierList?
;

errorCapturingIdentifier
Expand Down Expand Up @@ -1217,4 +1217,4 @@ nonReservedKeywords
| KW_WEEK
| KW_YEARS
| KW_ZONE
;
;
14 changes: 9 additions & 5 deletions src/grammar/hive/HiveSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ execStatement
| KW_MERGE QUERY_HINT? KW_INTO tableName (KW_AS? id_)? KW_USING joinSourcePart KW_ON expression whenClauses
| KW_PREPARE id_ KW_FROM queryStatementExpression
| KW_EXECUTE id_ KW_USING constantList
| KW_SET configPropertiesItem (DOT configPropertiesItem)* EQUAL .*?
| KW_SET configPropertiesItem ((DOT | COLON) configPropertiesItem)* EQUAL .*?
;

loadStatement
Expand Down Expand Up @@ -887,7 +887,9 @@ tableConstraint
;

columnNameTypeConstraint
: colName=columnNameCreate columnType columnConstraint? (KW_COMMENT comment=StringLiteral)?
: colName=columnNameCreate colType=columnType columnConstraint? (
KW_COMMENT comment=StringLiteral
)?
;

columnConstraint
Expand Down Expand Up @@ -1363,10 +1365,12 @@ joinToken
;

lateralView
: KW_LATERAL KW_VIEW KW_OUTER function_ tableAlias (KW_AS id_ (COMMA id_)*)?
: KW_LATERAL KW_VIEW KW_OUTER function_ alias=tableAlias (KW_AS id_ (COMMA id_)*)?
| COMMA? KW_LATERAL (
KW_VIEW function_ tableAlias (KW_AS id_ (COMMA id_)*)?
| KW_TABLE LPAREN valuesClause RPAREN KW_AS? tableAlias (LPAREN id_ (COMMA id_)* RPAREN)?
KW_VIEW function_ alias=tableAlias (KW_AS id_ (COMMA id_)*)?
| KW_TABLE LPAREN valuesClause RPAREN KW_AS? alias=tableAlias (
LPAREN id_ (COMMA id_)* RPAREN
)?
)
;

Expand Down
40 changes: 22 additions & 18 deletions src/grammar/impala/ImpalaSqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -96,20 +96,20 @@ createKuduTableAsSelect
: KW_CREATE KW_EXTERNAL? KW_TABLE ifNotExists? tableNameCreate (
LPAREN kuduTableElement (COMMA kuduTableElement)* (COMMA KW_PRIMARY KW_KEY columnAliases)? RPAREN
)? (KW_PRIMARY KW_KEY columnAliases?)? (KW_PARTITION KW_BY kuduPartitionClause)? (
KW_COMMENT stringLiteral
commentClause
)? KW_STORED KW_AS KW_KUDU (KW_TBLPROPERTIES tblProp=properties)? (KW_AS queryStatement)?
;

createView
: KW_CREATE KW_VIEW ifNotExists? viewNameCreate viewColumns? (KW_COMMENT stringLiteral)? (
: KW_CREATE KW_VIEW ifNotExists? viewNameCreate viewColumns? commentClause? (
KW_TBLPROPERTIES tblProp=properties
)? KW_AS queryStatement
;

createSchema
: KW_CREATE (KW_SCHEMA | KW_DATABASE) ifNotExists? databaseNameCreate (
KW_COMMENT comment=stringLiteral
)? (KW_LOCATION location=stringLiteral)?
: KW_CREATE (KW_SCHEMA | KW_DATABASE) ifNotExists? databaseNameCreate (commentClause)? (
KW_LOCATION location=stringLiteral
)?
;

createRole
Expand All @@ -119,14 +119,14 @@ createRole
createAggregateFunction
: KW_CREATE KW_AGGREGATE? KW_FUNCTION ifNotExists? functionNameCreate (
LPAREN (type (COMMA type)*)? RPAREN
)? KW_RETURNS type (KW_INTERMEDIATE type)? KW_LOCATION STRING (KW_INIT_FN EQ STRING)? KW_UPDATE_FN EQ STRING KW_MERGE_FN EQ STRING (
)? KW_RETURNS returnType=type (KW_INTERMEDIATE type)? KW_LOCATION STRING (KW_INIT_FN EQ STRING)? KW_UPDATE_FN EQ STRING KW_MERGE_FN EQ STRING (
KW_PREPARE_FN EQ STRING
)? (KW_CLOSEFN EQ STRING)? (KW_SERIALIZE_FN EQ STRING)? (KW_FINALIZE_FN EQ STRING)?
;

createFunction
: KW_CREATE KW_FUNCTION ifNotExists? functionNameCreate (LPAREN (type (COMMA type)*)? RPAREN)? (
KW_RETURNS type
KW_RETURNS returnType=type
)? KW_LOCATION STRING KW_SYMBOL EQ symbol=stringLiteral
;

Expand Down Expand Up @@ -569,11 +569,9 @@ tableOrViewPath
;

createCommonItem
: (KW_SORT KW_BY columnAliases)? (KW_COMMENT comment=stringLiteral)? (
KW_ROW KW_FORMAT rowFormat
)? (KW_WITH KW_SERDEPROPERTIES serdProp=properties)? (KW_STORED KW_AS fileFormat)? (
KW_LOCATION location=stringLiteral
)? (
: (KW_SORT KW_BY columnAliases)? commentClause? (KW_ROW KW_FORMAT rowFormat)? (
KW_WITH KW_SERDEPROPERTIES serdProp=properties
)? (KW_STORED KW_AS fileFormat)? (KW_LOCATION location=stringLiteral)? (
KW_CACHED KW_IN cacheName=qualifiedName (KW_WITH KW_REPLICATION EQ INTEGER_VALUE)?
| KW_UNCACHED
)? (KW_TBLPROPERTIES tblProp=properties)?
Expand All @@ -588,9 +586,11 @@ assignmentItem
;

viewColumns
: LPAREN columnNamePathCreate (KW_COMMENT stringLiteral)? (
COMMA columnNamePathCreate (KW_COMMENT stringLiteral)?
)* RPAREN
: LPAREN viewColumnItem? (COMMA viewColumnItem?)* RPAREN
;

viewColumnItem
: columnNamePathCreate commentClause?
;

queryStatement
Expand Down Expand Up @@ -621,19 +621,23 @@ columnSpec
;

columnDefinition
: columnNamePathCreate type (KW_COMMENT stringLiteral)?
: columnNamePathCreate colType=type commentClause?
;

kuduTableElement
: kuduColumnDefinition
;

kuduColumnDefinition
: columnNamePathCreate type (kuduAttributes kuduAttributes*?)? (KW_COMMENT stringLiteral)? (
: columnNamePathCreate colType=type (kuduAttributes kuduAttributes*?)? commentClause? (
KW_PRIMARY KW_KEY
)?
;

commentClause
: KW_COMMENT comment=stringLiteral
;

columnSpecWithKudu
: columnSpec (kuduAttributes kuduAttributes*?)?
;
Expand Down Expand Up @@ -838,7 +842,7 @@ sampleType
;

aliasedRelation
: relationPrimary (KW_AS? identifier columnAliases?)?
: relationPrimary (KW_AS? alias=identifier columnAliases?)?
;

columnAliases
Expand Down
8 changes: 4 additions & 4 deletions src/grammar/mysql/MySqlParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ functionParameter
;

routineOption
: KW_COMMENT STRING_LITERAL # routineComment
: KW_COMMENT comment=STRING_LITERAL # routineComment
| KW_LANGUAGE KW_SQL # routineLanguage
| KW_NOT? KW_DETERMINISTIC # routineBehavior
| (KW_CONTAINS KW_SQL | KW_NO KW_SQL | KW_READS KW_SQL KW_DATA | KW_MODIFIES KW_SQL KW_DATA) # routineData
Expand Down Expand Up @@ -483,7 +483,7 @@ constraintSymbol
;

columnDefinition
: dataType columnConstraint*
: colType=dataType columnConstraint*
;

columnConstraint
Expand All @@ -494,7 +494,7 @@ columnConstraint
| (KW_AUTO_INCREMENT | KW_ON KW_UPDATE currentTimestamp) # autoIncrementColumnConstraint
| KW_PRIMARY? KW_KEY # primaryKeyColumnConstraint
| KW_UNIQUE KW_KEY? # uniqueKeyColumnConstraint
| KW_COMMENT STRING_LITERAL # commentColumnConstraint
| KW_COMMENT comment=STRING_LITERAL # commentColumnConstraint
| KW_COLUMN_FORMAT colformat=(KW_FIXED | KW_DYNAMIC | KW_DEFAULT) # formatColumnConstraint
| KW_STORAGE storageval=(KW_DISK | KW_MEMORY | KW_DEFAULT) # storageColumnConstraint
| referenceDefinition # referenceColumnConstraint
Expand Down Expand Up @@ -536,7 +536,7 @@ tableOption
| KW_DEFAULT? charSet '='? (charsetName | KW_DEFAULT) # tableOptionCharset
| (KW_CHECKSUM | KW_PAGE_CHECKSUM) '='? boolValue=('0' | '1') # tableOptionChecksum
| KW_DEFAULT? KW_COLLATE '='? collationName # tableOptionCollate
| KW_COMMENT '='? STRING_LITERAL # tableOptionComment
| KW_COMMENT '='? comment=STRING_LITERAL # tableOptionComment
| KW_COMPRESSION '='? (STRING_LITERAL | ID) # tableOptionCompression
| KW_CONNECTION '='? STRING_LITERAL # tableOptionConnection
| (KW_DATA | KW_INDEX) KW_DIRECTORY '='? STRING_LITERAL # tableOptionDataDirectory
Expand Down
Loading