Skip to content

Commit 6e7b955

Browse files
committed
Add CAST/CONVERT parsing and enable VECTOR/function tests
- Add CastCall, ConvertCall, TryCastCall, TryConvertCall AST types - Implement special parsing for CAST/CONVERT syntax in expressions - Add VECTOR to known SQL data types - Fix CreateFunctionStatement JSON marshaling to include Parameters - Enable 6 previously failing tests: - Baselines160_CreateFunctionStatementTests160 - CreateFunctionStatementTests160 - Baselines160_ExpressionTests160 - ExpressionTests160 - Baselines160_VectorFunctionTests160 - VectorFunctionTests160
1 parent 6538173 commit 6e7b955

10 files changed

Lines changed: 336 additions & 6 deletions

File tree

ast/function_call.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,45 @@ type FunctionCall struct {
4343

4444
func (*FunctionCall) node() {}
4545
func (*FunctionCall) scalarExpression() {}
46+
47+
// CastCall represents a CAST expression: CAST(expression AS data_type)
48+
type CastCall struct {
49+
DataType DataTypeReference `json:"DataType,omitempty"`
50+
Parameter ScalarExpression `json:"Parameter,omitempty"`
51+
Collation *Identifier `json:"Collation,omitempty"`
52+
}
53+
54+
func (*CastCall) node() {}
55+
func (*CastCall) scalarExpression() {}
56+
57+
// ConvertCall represents a CONVERT expression: CONVERT(data_type, expression [, style])
58+
type ConvertCall struct {
59+
DataType DataTypeReference `json:"DataType,omitempty"`
60+
Parameter ScalarExpression `json:"Parameter,omitempty"`
61+
Style ScalarExpression `json:"Style,omitempty"`
62+
Collation *Identifier `json:"Collation,omitempty"`
63+
}
64+
65+
func (*ConvertCall) node() {}
66+
func (*ConvertCall) scalarExpression() {}
67+
68+
// TryCastCall represents a TRY_CAST expression
69+
type TryCastCall struct {
70+
DataType DataTypeReference `json:"DataType,omitempty"`
71+
Parameter ScalarExpression `json:"Parameter,omitempty"`
72+
Collation *Identifier `json:"Collation,omitempty"`
73+
}
74+
75+
func (*TryCastCall) node() {}
76+
func (*TryCastCall) scalarExpression() {}
77+
78+
// TryConvertCall represents a TRY_CONVERT expression
79+
type TryConvertCall struct {
80+
DataType DataTypeReference `json:"DataType,omitempty"`
81+
Parameter ScalarExpression `json:"Parameter,omitempty"`
82+
Style ScalarExpression `json:"Style,omitempty"`
83+
Collation *Identifier `json:"Collation,omitempty"`
84+
}
85+
86+
func (*TryConvertCall) node() {}
87+
func (*TryConvertCall) scalarExpression() {}

parser/marshal.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,68 @@ func scalarExpressionToJSON(expr ast.ScalarExpression) jsonNode {
12851285
node["Collation"] = identifierToJSON(e.Collation)
12861286
}
12871287
return node
1288+
case *ast.CastCall:
1289+
node := jsonNode{
1290+
"$type": "CastCall",
1291+
}
1292+
if e.DataType != nil {
1293+
node["DataType"] = dataTypeReferenceToJSON(e.DataType)
1294+
}
1295+
if e.Parameter != nil {
1296+
node["Parameter"] = scalarExpressionToJSON(e.Parameter)
1297+
}
1298+
if e.Collation != nil {
1299+
node["Collation"] = identifierToJSON(e.Collation)
1300+
}
1301+
return node
1302+
case *ast.ConvertCall:
1303+
node := jsonNode{
1304+
"$type": "ConvertCall",
1305+
}
1306+
if e.DataType != nil {
1307+
node["DataType"] = dataTypeReferenceToJSON(e.DataType)
1308+
}
1309+
if e.Parameter != nil {
1310+
node["Parameter"] = scalarExpressionToJSON(e.Parameter)
1311+
}
1312+
if e.Style != nil {
1313+
node["Style"] = scalarExpressionToJSON(e.Style)
1314+
}
1315+
if e.Collation != nil {
1316+
node["Collation"] = identifierToJSON(e.Collation)
1317+
}
1318+
return node
1319+
case *ast.TryCastCall:
1320+
node := jsonNode{
1321+
"$type": "TryCastCall",
1322+
}
1323+
if e.DataType != nil {
1324+
node["DataType"] = dataTypeReferenceToJSON(e.DataType)
1325+
}
1326+
if e.Parameter != nil {
1327+
node["Parameter"] = scalarExpressionToJSON(e.Parameter)
1328+
}
1329+
if e.Collation != nil {
1330+
node["Collation"] = identifierToJSON(e.Collation)
1331+
}
1332+
return node
1333+
case *ast.TryConvertCall:
1334+
node := jsonNode{
1335+
"$type": "TryConvertCall",
1336+
}
1337+
if e.DataType != nil {
1338+
node["DataType"] = dataTypeReferenceToJSON(e.DataType)
1339+
}
1340+
if e.Parameter != nil {
1341+
node["Parameter"] = scalarExpressionToJSON(e.Parameter)
1342+
}
1343+
if e.Style != nil {
1344+
node["Style"] = scalarExpressionToJSON(e.Style)
1345+
}
1346+
if e.Collation != nil {
1347+
node["Collation"] = identifierToJSON(e.Collation)
1348+
}
1349+
return node
12881350
case *ast.BinaryExpression:
12891351
node := jsonNode{
12901352
"$type": "BinaryExpression",
@@ -6760,6 +6822,13 @@ func createFunctionStatementToJSON(s *ast.CreateFunctionStatement) jsonNode {
67606822
if s.Name != nil {
67616823
node["Name"] = schemaObjectNameToJSON(s.Name)
67626824
}
6825+
if len(s.Parameters) > 0 {
6826+
params := make([]jsonNode, len(s.Parameters))
6827+
for i, p := range s.Parameters {
6828+
params[i] = procedureParameterToJSON(p)
6829+
}
6830+
node["Parameters"] = params
6831+
}
67636832
if s.ReturnType != nil {
67646833
node["ReturnType"] = functionReturnTypeToJSON(s.ReturnType)
67656834
}

parser/parse_select.go

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,20 @@ func (p *Parser) parsePrimaryExpression() (ast.ScalarExpression, error) {
643643
p.nextToken() // consume N
644644
return p.parseNationalStringLiteral()
645645
}
646+
// Check for CAST/CONVERT special functions
647+
upper := strings.ToUpper(p.curTok.Literal)
648+
if upper == "CAST" && p.peekTok.Type == TokenLParen {
649+
return p.parseCastCall()
650+
}
651+
if upper == "CONVERT" && p.peekTok.Type == TokenLParen {
652+
return p.parseConvertCall()
653+
}
654+
if upper == "TRY_CAST" && p.peekTok.Type == TokenLParen {
655+
return p.parseTryCastCall()
656+
}
657+
if upper == "TRY_CONVERT" && p.peekTok.Type == TokenLParen {
658+
return p.parseTryConvertCall()
659+
}
646660
return p.parseColumnReferenceOrFunctionCall()
647661
case TokenNumber:
648662
val := p.curTok.Literal
@@ -2169,3 +2183,207 @@ func identifiersToSchemaObjectName(identifiers []*ast.Identifier) *ast.SchemaObj
21692183

21702184
// ======================= New Statement Parsing Functions =======================
21712185

2186+
2187+
// parseCastCall parses a CAST expression: CAST(expression AS data_type)
2188+
func (p *Parser) parseCastCall() (ast.ScalarExpression, error) {
2189+
p.nextToken() // consume CAST
2190+
if p.curTok.Type != TokenLParen {
2191+
return nil, fmt.Errorf("expected ( after CAST, got %s", p.curTok.Literal)
2192+
}
2193+
p.nextToken() // consume (
2194+
2195+
// Parse the expression
2196+
expr, err := p.parseScalarExpression()
2197+
if err != nil {
2198+
return nil, err
2199+
}
2200+
2201+
// Expect AS
2202+
if p.curTok.Type != TokenAs {
2203+
return nil, fmt.Errorf("expected AS in CAST, got %s", p.curTok.Literal)
2204+
}
2205+
p.nextToken() // consume AS
2206+
2207+
// Parse the data type
2208+
dt, err := p.parseDataTypeReference()
2209+
if err != nil {
2210+
return nil, err
2211+
}
2212+
2213+
// Expect )
2214+
if p.curTok.Type != TokenRParen {
2215+
return nil, fmt.Errorf("expected ) in CAST, got %s", p.curTok.Literal)
2216+
}
2217+
p.nextToken() // consume )
2218+
2219+
cast := &ast.CastCall{
2220+
DataType: dt,
2221+
Parameter: expr,
2222+
}
2223+
2224+
// Check for COLLATE clause
2225+
if strings.ToUpper(p.curTok.Literal) == "COLLATE" {
2226+
p.nextToken() // consume COLLATE
2227+
cast.Collation = p.parseIdentifier()
2228+
}
2229+
2230+
return cast, nil
2231+
}
2232+
2233+
// parseConvertCall parses a CONVERT expression: CONVERT(data_type, expression [, style])
2234+
func (p *Parser) parseConvertCall() (ast.ScalarExpression, error) {
2235+
p.nextToken() // consume CONVERT
2236+
if p.curTok.Type != TokenLParen {
2237+
return nil, fmt.Errorf("expected ( after CONVERT, got %s", p.curTok.Literal)
2238+
}
2239+
p.nextToken() // consume (
2240+
2241+
// Parse the data type first
2242+
dt, err := p.parseDataTypeReference()
2243+
if err != nil {
2244+
return nil, err
2245+
}
2246+
2247+
// Expect comma
2248+
if p.curTok.Type != TokenComma {
2249+
return nil, fmt.Errorf("expected , in CONVERT, got %s", p.curTok.Literal)
2250+
}
2251+
p.nextToken() // consume ,
2252+
2253+
// Parse the expression
2254+
expr, err := p.parseScalarExpression()
2255+
if err != nil {
2256+
return nil, err
2257+
}
2258+
2259+
convert := &ast.ConvertCall{
2260+
DataType: dt,
2261+
Parameter: expr,
2262+
}
2263+
2264+
// Check for optional style parameter
2265+
if p.curTok.Type == TokenComma {
2266+
p.nextToken() // consume ,
2267+
style, err := p.parseScalarExpression()
2268+
if err != nil {
2269+
return nil, err
2270+
}
2271+
convert.Style = style
2272+
}
2273+
2274+
// Expect )
2275+
if p.curTok.Type != TokenRParen {
2276+
return nil, fmt.Errorf("expected ) in CONVERT, got %s", p.curTok.Literal)
2277+
}
2278+
p.nextToken() // consume )
2279+
2280+
// Check for COLLATE clause
2281+
if strings.ToUpper(p.curTok.Literal) == "COLLATE" {
2282+
p.nextToken() // consume COLLATE
2283+
convert.Collation = p.parseIdentifier()
2284+
}
2285+
2286+
return convert, nil
2287+
}
2288+
2289+
// parseTryCastCall parses a TRY_CAST expression
2290+
func (p *Parser) parseTryCastCall() (ast.ScalarExpression, error) {
2291+
p.nextToken() // consume TRY_CAST
2292+
if p.curTok.Type != TokenLParen {
2293+
return nil, fmt.Errorf("expected ( after TRY_CAST, got %s", p.curTok.Literal)
2294+
}
2295+
p.nextToken() // consume (
2296+
2297+
// Parse the expression
2298+
expr, err := p.parseScalarExpression()
2299+
if err != nil {
2300+
return nil, err
2301+
}
2302+
2303+
// Expect AS
2304+
if p.curTok.Type != TokenAs {
2305+
return nil, fmt.Errorf("expected AS in TRY_CAST, got %s", p.curTok.Literal)
2306+
}
2307+
p.nextToken() // consume AS
2308+
2309+
// Parse the data type
2310+
dt, err := p.parseDataTypeReference()
2311+
if err != nil {
2312+
return nil, err
2313+
}
2314+
2315+
// Expect )
2316+
if p.curTok.Type != TokenRParen {
2317+
return nil, fmt.Errorf("expected ) in TRY_CAST, got %s", p.curTok.Literal)
2318+
}
2319+
p.nextToken() // consume )
2320+
2321+
cast := &ast.TryCastCall{
2322+
DataType: dt,
2323+
Parameter: expr,
2324+
}
2325+
2326+
// Check for COLLATE clause
2327+
if strings.ToUpper(p.curTok.Literal) == "COLLATE" {
2328+
p.nextToken() // consume COLLATE
2329+
cast.Collation = p.parseIdentifier()
2330+
}
2331+
2332+
return cast, nil
2333+
}
2334+
2335+
// parseTryConvertCall parses a TRY_CONVERT expression
2336+
func (p *Parser) parseTryConvertCall() (ast.ScalarExpression, error) {
2337+
p.nextToken() // consume TRY_CONVERT
2338+
if p.curTok.Type != TokenLParen {
2339+
return nil, fmt.Errorf("expected ( after TRY_CONVERT, got %s", p.curTok.Literal)
2340+
}
2341+
p.nextToken() // consume (
2342+
2343+
// Parse the data type first
2344+
dt, err := p.parseDataTypeReference()
2345+
if err != nil {
2346+
return nil, err
2347+
}
2348+
2349+
// Expect comma
2350+
if p.curTok.Type != TokenComma {
2351+
return nil, fmt.Errorf("expected , in TRY_CONVERT, got %s", p.curTok.Literal)
2352+
}
2353+
p.nextToken() // consume ,
2354+
2355+
// Parse the expression
2356+
expr, err := p.parseScalarExpression()
2357+
if err != nil {
2358+
return nil, err
2359+
}
2360+
2361+
convert := &ast.TryConvertCall{
2362+
DataType: dt,
2363+
Parameter: expr,
2364+
}
2365+
2366+
// Check for optional style parameter
2367+
if p.curTok.Type == TokenComma {
2368+
p.nextToken() // consume ,
2369+
style, err := p.parseScalarExpression()
2370+
if err != nil {
2371+
return nil, err
2372+
}
2373+
convert.Style = style
2374+
}
2375+
2376+
// Expect )
2377+
if p.curTok.Type != TokenRParen {
2378+
return nil, fmt.Errorf("expected ) in TRY_CONVERT, got %s", p.curTok.Literal)
2379+
}
2380+
p.nextToken() // consume )
2381+
2382+
// Check for COLLATE clause
2383+
if strings.ToUpper(p.curTok.Literal) == "COLLATE" {
2384+
p.nextToken() // consume COLLATE
2385+
convert.Collation = p.parseIdentifier()
2386+
}
2387+
2388+
return convert, nil
2389+
}

parser/parse_statements.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ func getSqlDataTypeOption(typeName string) (string, bool) {
629629
"ROWVERSION": "Rowversion",
630630
"TIMESTAMP": "Timestamp",
631631
"CONNECTION": "Connection",
632+
"VECTOR": "Vector",
632633
}
633634
if mapped, ok := typeMap[strings.ToUpper(typeName)]; ok {
634635
return mapped, true
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)