Skip to content

Commit f6a4e57

Browse files
authored
Add full CREATE EXTERNAL DATA SOURCE support with options (#34)
1 parent 7123ce3 commit f6a4e57

File tree

10 files changed

+115
-48
lines changed

10 files changed

+115
-48
lines changed

ast/external_statements.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@ package ast
22

33
// CreateExternalDataSourceStatement represents CREATE EXTERNAL DATA SOURCE statement
44
type CreateExternalDataSourceStatement struct {
5-
Name *Identifier
6-
Options []*ExternalDataSourceOption
5+
Name *Identifier
6+
DataSourceType string // HADOOP, RDBMS, SHARD_MAP_MANAGER, BLOB_STORAGE, EXTERNAL_GENERICS
7+
Location *StringLiteral
8+
ExternalDataSourceOptions []*ExternalDataSourceLiteralOrIdentifierOption
79
}
810

911
func (s *CreateExternalDataSourceStatement) node() {}
1012
func (s *CreateExternalDataSourceStatement) statement() {}
1113

12-
// ExternalDataSourceOption represents an option for external data source
13-
type ExternalDataSourceOption struct {
14-
OptionKind string
15-
Value ScalarExpression
14+
// ExternalDataSourceLiteralOrIdentifierOption represents an option for external data source
15+
type ExternalDataSourceLiteralOrIdentifierOption struct {
16+
OptionKind string // Credential, ResourceManagerLocation, DatabaseName, ShardMapName
17+
Value *IdentifierOrValueExpression
1618
}
1719

1820
// CreateExternalFileFormatStatement represents CREATE EXTERNAL FILE FORMAT statement
@@ -97,8 +99,8 @@ type ExternalLibraryOption struct {
9799

98100
// AlterExternalDataSourceStatement represents ALTER EXTERNAL DATA SOURCE statement
99101
type AlterExternalDataSourceStatement struct {
100-
Name *Identifier
101-
Options []*ExternalDataSourceOption
102+
Name *Identifier
103+
ExternalDataSourceOptions []*ExternalDataSourceLiteralOrIdentifierOption
102104
}
103105

104106
func (s *AlterExternalDataSourceStatement) node() {}

parser/marshal.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6057,6 +6057,32 @@ func createExternalDataSourceStatementToJSON(s *ast.CreateExternalDataSourceStat
60576057
if s.Name != nil {
60586058
node["Name"] = identifierToJSON(s.Name)
60596059
}
6060+
if s.DataSourceType != "" {
6061+
node["DataSourceType"] = s.DataSourceType
6062+
}
6063+
if s.Location != nil {
6064+
node["Location"] = stringLiteralToJSON(s.Location)
6065+
}
6066+
if len(s.ExternalDataSourceOptions) > 0 {
6067+
var options []jsonNode
6068+
for _, opt := range s.ExternalDataSourceOptions {
6069+
options = append(options, externalDataSourceOptionToJSON(opt))
6070+
}
6071+
node["ExternalDataSourceOptions"] = options
6072+
}
6073+
return node
6074+
}
6075+
6076+
func externalDataSourceOptionToJSON(opt *ast.ExternalDataSourceLiteralOrIdentifierOption) jsonNode {
6077+
node := jsonNode{
6078+
"$type": "ExternalDataSourceLiteralOrIdentifierOption",
6079+
}
6080+
if opt.Value != nil {
6081+
node["Value"] = identifierOrValueExpressionToJSON(opt.Value)
6082+
}
6083+
if opt.OptionKind != "" {
6084+
node["OptionKind"] = opt.OptionKind
6085+
}
60606086
return node
60616087
}
60626088

@@ -7124,29 +7150,16 @@ func alterExternalDataSourceStatementToJSON(s *ast.AlterExternalDataSourceStatem
71247150
if s.Name != nil {
71257151
node["Name"] = identifierToJSON(s.Name)
71267152
}
7127-
if len(s.Options) > 0 {
7128-
opts := make([]jsonNode, len(s.Options))
7129-
for i, o := range s.Options {
7153+
if len(s.ExternalDataSourceOptions) > 0 {
7154+
opts := make([]jsonNode, len(s.ExternalDataSourceOptions))
7155+
for i, o := range s.ExternalDataSourceOptions {
71307156
opts[i] = externalDataSourceOptionToJSON(o)
71317157
}
71327158
node["ExternalDataSourceOptions"] = opts
71337159
}
71347160
return node
71357161
}
71367162

7137-
func externalDataSourceOptionToJSON(o *ast.ExternalDataSourceOption) jsonNode {
7138-
node := jsonNode{
7139-
"$type": "ExternalDataSourceLiteralOrIdentifierOption",
7140-
}
7141-
if o.OptionKind != "" {
7142-
node["OptionKind"] = o.OptionKind
7143-
}
7144-
if o.Value != nil {
7145-
node["Value"] = scalarExpressionToJSON(o.Value)
7146-
}
7147-
return node
7148-
}
7149-
71507163
func alterExternalLanguageStatementToJSON(s *ast.AlterExternalLanguageStatement) jsonNode {
71517164
node := jsonNode{
71527165
"$type": "AlterExternalLanguageStatement",

parser/parse_ddl.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3819,32 +3819,33 @@ func (p *Parser) parseAlterExternalDataSourceStatement() (*ast.AlterExternalData
38193819
break
38203820
}
38213821

3822-
opt := &ast.ExternalDataSourceOption{}
3823-
opt.OptionKind = strings.ToUpper(p.curTok.Literal)
3822+
optName := strings.ToUpper(p.curTok.Literal)
38243823
p.nextToken()
38253824

38263825
// Expect =
38273826
if p.curTok.Type == TokenEquals {
38283827
p.nextToken()
38293828
}
38303829

3830+
opt := &ast.ExternalDataSourceLiteralOrIdentifierOption{
3831+
OptionKind: externalDataSourceOptionKindToPascalCase(optName),
3832+
Value: &ast.IdentifierOrValueExpression{},
3833+
}
3834+
38313835
// Parse value
38323836
if p.curTok.Type == TokenString {
3833-
val, _ := p.parseStringLiteral()
3834-
opt.Value = val
3837+
strLit, _ := p.parseStringLiteral()
3838+
opt.Value.Value = strLit.Value
3839+
opt.Value.ValueExpression = strLit
38353840
} else if p.curTok.Type == TokenIdent {
3836-
opt.Value = &ast.ColumnReferenceExpression{
3837-
ColumnType: "Regular",
3838-
MultiPartIdentifier: &ast.MultiPartIdentifier{
3839-
Count: 1,
3840-
Identifiers: []*ast.Identifier{p.parseIdentifier()},
3841-
},
3842-
}
3841+
ident := p.parseIdentifier()
3842+
opt.Value.Value = ident.Value
3843+
opt.Value.Identifier = ident
38433844
} else {
38443845
p.nextToken()
38453846
}
38463847

3847-
stmt.Options = append(stmt.Options, opt)
3848+
stmt.ExternalDataSourceOptions = append(stmt.ExternalDataSourceOptions, opt)
38483849

38493850
if p.curTok.Type == TokenComma {
38503851
p.nextToken()

parser/parse_statements.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4192,26 +4192,57 @@ func (p *Parser) parseCreateExternalDataSourceStatement() (*ast.CreateExternalDa
41924192

41934193
// Parse WITH clause
41944194
if p.curTok.Type == TokenWith {
4195+
// Default to EXTERNAL_GENERICS if WITH clause exists but no TYPE specified
4196+
stmt.DataSourceType = "EXTERNAL_GENERICS"
41954197
p.nextToken() // consume WITH
41964198
if p.curTok.Type != TokenLParen {
41974199
return nil, fmt.Errorf("expected ( after WITH, got %s", p.curTok.Literal)
41984200
}
41994201
p.nextToken() // consume (
42004202

42014203
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
4202-
opt := &ast.ExternalDataSourceOption{
4203-
OptionKind: p.curTok.Literal,
4204-
}
4204+
optName := strings.ToUpper(p.curTok.Literal)
42054205
p.nextToken() // consume option name
42064206
if p.curTok.Type == TokenEquals {
4207+
p.nextToken() // consume =
4208+
}
4209+
4210+
switch optName {
4211+
case "TYPE":
4212+
// TYPE sets DataSourceType
4213+
stmt.DataSourceType = strings.ToUpper(p.curTok.Literal)
42074214
p.nextToken()
4208-
val, err := p.parseScalarExpression()
4215+
case "LOCATION":
4216+
// LOCATION sets Location as StringLiteral
4217+
strLit, err := p.parseStringLiteral()
42094218
if err != nil {
42104219
return nil, err
42114220
}
4212-
opt.Value = val
4221+
stmt.Location = strLit
4222+
default:
4223+
// All other options go into ExternalDataSourceOptions
4224+
opt := &ast.ExternalDataSourceLiteralOrIdentifierOption{
4225+
OptionKind: externalDataSourceOptionKindToPascalCase(optName),
4226+
Value: &ast.IdentifierOrValueExpression{},
4227+
}
4228+
4229+
// Determine if value is identifier or string literal
4230+
if p.curTok.Type == TokenString {
4231+
strLit, err := p.parseStringLiteral()
4232+
if err != nil {
4233+
return nil, err
4234+
}
4235+
opt.Value.Value = strLit.Value
4236+
opt.Value.ValueExpression = strLit
4237+
} else {
4238+
// It's an identifier
4239+
ident := p.parseIdentifier()
4240+
opt.Value.Value = ident.Value
4241+
opt.Value.Identifier = ident
4242+
}
4243+
stmt.ExternalDataSourceOptions = append(stmt.ExternalDataSourceOptions, opt)
42134244
}
4214-
stmt.Options = append(stmt.Options, opt)
4245+
42154246
if p.curTok.Type == TokenComma {
42164247
p.nextToken()
42174248
}
@@ -4227,6 +4258,26 @@ func (p *Parser) parseCreateExternalDataSourceStatement() (*ast.CreateExternalDa
42274258
return stmt, nil
42284259
}
42294260

4261+
// externalDataSourceOptionKindToPascalCase converts option names to PascalCase
4262+
func externalDataSourceOptionKindToPascalCase(optName string) string {
4263+
switch strings.ToUpper(optName) {
4264+
case "CREDENTIAL":
4265+
return "Credential"
4266+
case "RESOURCE_MANAGER_LOCATION":
4267+
return "ResourceManagerLocation"
4268+
case "DATABASE_NAME":
4269+
return "DatabaseName"
4270+
case "SHARD_MAP_NAME":
4271+
return "ShardMapName"
4272+
case "CONNECTION_OPTIONS":
4273+
return "ConnectionOptions"
4274+
case "PUSHDOWN":
4275+
return "Pushdown"
4276+
default:
4277+
return optName
4278+
}
4279+
}
4280+
42304281
func (p *Parser) parseCreateExternalFileFormatStatement() (*ast.CreateExternalFileFormatStatement, error) {
42314282
// FILE FORMAT name WITH (options)
42324283
p.nextToken() // consume FILE
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)