@@ -685,7 +685,8 @@ func (p *Parser) parsePrimaryExpression() (ast.ScalarExpression, error) {
685685 return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
686686 }
687687 p .nextToken ()
688- return & ast.ParenthesisExpression {Expression : expr }, nil
688+ // Check for property access after parenthesized expression: (c1).SomeProperty
689+ return p .parsePostExpressionAccess (& ast.ParenthesisExpression {Expression : expr })
689690 case TokenCase :
690691 return p .parseCaseExpression ()
691692 default :
@@ -966,58 +967,75 @@ func (p *Parser) parseColumnReferenceOrFunctionCall() (ast.ScalarExpression, err
966967 p .nextToken () // consume dot
967968 }
968969
969- // Check for :: (user-defined type method call): a.b::func()
970+ // Check for :: (user-defined type method call or property access ): a.b::func() or a::prop
970971 if p .curTok .Type == TokenColonColon && len (identifiers ) > 0 {
971972 p .nextToken () // consume ::
972973
973- // Parse function name
974+ // Parse function/property name
974975 if p .curTok .Type != TokenIdent {
975- return nil , fmt .Errorf ("expected function name after ::, got %s" , p .curTok .Literal )
976+ return nil , fmt .Errorf ("expected identifier after ::, got %s" , p .curTok .Literal )
976977 }
977- funcName := & ast.Identifier {Value : p .curTok .Literal , QuoteType : "NotQuoted" }
978+ name := & ast.Identifier {Value : p .curTok .Literal , QuoteType : "NotQuoted" }
978979 p .nextToken ()
979980
980- // Expect (
981- if p .curTok .Type != TokenLParen {
982- return nil , fmt .Errorf ("expected ( after function name, got %s" , p .curTok .Literal )
983- }
984- p .nextToken () // consume (
985-
986981 // Build SchemaObjectName from identifiers
987982 schemaObjName := identifiersToSchemaObjectName (identifiers )
988983
989- fc := & ast.FunctionCall {
990- CallTarget : & ast.UserDefinedTypeCallTarget {
991- SchemaObjectName : schemaObjName ,
992- },
993- FunctionName : funcName ,
994- UniqueRowFilter : "NotSpecified" ,
995- WithArrayWrapper : false ,
996- }
984+ // If followed by ( it's a method call, otherwise property access
985+ if p .curTok .Type == TokenLParen {
986+ p .nextToken () // consume (
987+
988+ fc := & ast.FunctionCall {
989+ CallTarget : & ast.UserDefinedTypeCallTarget {
990+ SchemaObjectName : schemaObjName ,
991+ },
992+ FunctionName : name ,
993+ UniqueRowFilter : "NotSpecified" ,
994+ WithArrayWrapper : false ,
995+ }
997996
998- // Parse parameters
999- if p .curTok .Type != TokenRParen {
1000- for {
1001- param , err := p .parseScalarExpression ()
1002- if err != nil {
1003- return nil , err
1004- }
1005- fc .Parameters = append (fc .Parameters , param )
997+ // Parse parameters
998+ if p .curTok .Type != TokenRParen {
999+ for {
1000+ param , err := p .parseScalarExpression ()
1001+ if err != nil {
1002+ return nil , err
1003+ }
1004+ fc .Parameters = append (fc .Parameters , param )
10061005
1007- if p .curTok .Type != TokenComma {
1008- break
1006+ if p .curTok .Type != TokenComma {
1007+ break
1008+ }
1009+ p .nextToken () // consume comma
10091010 }
1010- p .nextToken () // consume comma
10111011 }
1012+
1013+ // Expect )
1014+ if p .curTok .Type != TokenRParen {
1015+ return nil , fmt .Errorf ("expected ) in function call, got %s" , p .curTok .Literal )
1016+ }
1017+ p .nextToken ()
1018+
1019+ // Check for OVER clause or property access after method call
1020+ return p .parsePostExpressionAccess (fc )
10121021 }
10131022
1014- // Expect )
1015- if p .curTok .Type != TokenRParen {
1016- return nil , fmt .Errorf ("expected ) in function call, got %s" , p .curTok .Literal )
1023+ // Property access: t::a
1024+ propAccess := & ast.UserDefinedTypePropertyAccess {
1025+ CallTarget : & ast.UserDefinedTypeCallTarget {
1026+ SchemaObjectName : schemaObjName ,
1027+ },
1028+ PropertyName : name ,
1029+ }
1030+
1031+ // Check for COLLATE clause
1032+ if strings .ToUpper (p .curTok .Literal ) == "COLLATE" {
1033+ p .nextToken () // consume COLLATE
1034+ propAccess .Collation = p .parseIdentifier ()
10171035 }
1018- p .nextToken ()
10191036
1020- return fc , nil
1037+ // Check for chained property access
1038+ return p .parsePostExpressionAccess (propAccess )
10211039 }
10221040
10231041 // If followed by ( it's a function call
@@ -1046,7 +1064,7 @@ func (p *Parser) parseColumnReference() (*ast.ColumnReferenceExpression, error)
10461064 return nil , fmt .Errorf ("expected column reference, got function call" )
10471065}
10481066
1049- func (p * Parser ) parseFunctionCallFromIdentifiers (identifiers []* ast.Identifier ) (* ast.FunctionCall , error ) {
1067+ func (p * Parser ) parseFunctionCallFromIdentifiers (identifiers []* ast.Identifier ) (ast.ScalarExpression , error ) {
10501068 fc := & ast.FunctionCall {
10511069 UniqueRowFilter : "NotSpecified" ,
10521070 WithArrayWrapper : false ,
@@ -1070,6 +1088,15 @@ func (p *Parser) parseFunctionCallFromIdentifiers(identifiers []*ast.Identifier)
10701088 // Consume (
10711089 p .nextToken ()
10721090
1091+ // Check for ALL or DISTINCT
1092+ if strings .ToUpper (p .curTok .Literal ) == "ALL" {
1093+ fc .UniqueRowFilter = "All"
1094+ p .nextToken ()
1095+ } else if strings .ToUpper (p .curTok .Literal ) == "DISTINCT" {
1096+ fc .UniqueRowFilter = "Distinct"
1097+ p .nextToken ()
1098+ }
1099+
10731100 // Parse parameters
10741101 if p .curTok .Type != TokenRParen {
10751102 for {
@@ -1092,7 +1119,105 @@ func (p *Parser) parseFunctionCallFromIdentifiers(identifiers []*ast.Identifier)
10921119 }
10931120 p .nextToken ()
10941121
1095- return fc , nil
1122+ // Check for OVER clause or property access after function call
1123+ return p .parsePostExpressionAccess (fc )
1124+ }
1125+
1126+ // parsePostExpressionAccess handles chained property access (.PropertyName), COLLATE clauses, and OVER clauses
1127+ // after an expression (function call, parenthesized expression, or property access).
1128+ func (p * Parser ) parsePostExpressionAccess (expr ast.ScalarExpression ) (ast.ScalarExpression , error ) {
1129+ // Loop to handle chained property access like .SomeProperty.AnotherProperty
1130+ for {
1131+ // Check for .PropertyName pattern (property access)
1132+ if p .curTok .Type == TokenDot {
1133+ p .nextToken () // consume .
1134+
1135+ if p .curTok .Type != TokenIdent {
1136+ return nil , fmt .Errorf ("expected property name after ., got %s" , p .curTok .Literal )
1137+ }
1138+ propName := & ast.Identifier {Value : p .curTok .Literal , QuoteType : "NotQuoted" }
1139+ p .nextToken ()
1140+
1141+ // Check if it's a method call: .method()
1142+ if p .curTok .Type == TokenLParen {
1143+ p .nextToken () // consume (
1144+
1145+ fc := & ast.FunctionCall {
1146+ CallTarget : & ast.ExpressionCallTarget {
1147+ Expression : expr ,
1148+ },
1149+ FunctionName : propName ,
1150+ UniqueRowFilter : "NotSpecified" ,
1151+ WithArrayWrapper : false ,
1152+ }
1153+
1154+ // Parse parameters
1155+ if p .curTok .Type != TokenRParen {
1156+ for {
1157+ param , err := p .parseScalarExpression ()
1158+ if err != nil {
1159+ return nil , err
1160+ }
1161+ fc .Parameters = append (fc .Parameters , param )
1162+
1163+ if p .curTok .Type != TokenComma {
1164+ break
1165+ }
1166+ p .nextToken () // consume comma
1167+ }
1168+ }
1169+
1170+ // Expect )
1171+ if p .curTok .Type != TokenRParen {
1172+ return nil , fmt .Errorf ("expected ) in method call, got %s" , p .curTok .Literal )
1173+ }
1174+ p .nextToken ()
1175+
1176+ expr = fc
1177+ continue
1178+ }
1179+
1180+ // Property access: .PropertyName
1181+ propAccess := & ast.UserDefinedTypePropertyAccess {
1182+ CallTarget : & ast.ExpressionCallTarget {
1183+ Expression : expr ,
1184+ },
1185+ PropertyName : propName ,
1186+ }
1187+
1188+ // Check for COLLATE clause
1189+ if strings .ToUpper (p .curTok .Literal ) == "COLLATE" {
1190+ p .nextToken () // consume COLLATE
1191+ propAccess .Collation = p .parseIdentifier ()
1192+ }
1193+
1194+ expr = propAccess
1195+ continue
1196+ }
1197+
1198+ // Check for OVER clause for function calls
1199+ if fc , ok := expr .(* ast.FunctionCall ); ok && strings .ToUpper (p .curTok .Literal ) == "OVER" {
1200+ p .nextToken () // consume OVER
1201+
1202+ if p .curTok .Type != TokenLParen {
1203+ return nil , fmt .Errorf ("expected ( after OVER, got %s" , p .curTok .Literal )
1204+ }
1205+ p .nextToken () // consume (
1206+
1207+ // For now, just skip to closing paren (basic OVER() support)
1208+ // TODO: Parse partition by, order by, and window frame
1209+ if p .curTok .Type != TokenRParen {
1210+ return nil , fmt .Errorf ("expected ) in OVER clause, got %s" , p .curTok .Literal )
1211+ }
1212+ p .nextToken () // consume )
1213+
1214+ fc .OverClause = & ast.OverClause {}
1215+ }
1216+
1217+ break
1218+ }
1219+
1220+ return expr , nil
10961221}
10971222
10981223func (p * Parser ) parseFromClause () (* ast.FromClause , error ) {
0 commit comments