@@ -865,20 +865,77 @@ func (p *Parser) parseExecuteSpecification() (*ast.ExecuteSpecification, error)
865865 // Parse procedure reference
866866 procRef := & ast.ExecutableProcedureReference {}
867867
868+ // Check for OPENDATASOURCE or OPENROWSET
869+ upperLit := strings .ToUpper (p .curTok .Literal )
870+ if upperLit == "OPENDATASOURCE" || upperLit == "OPENROWSET" {
871+ p .nextToken () // consume OPENDATASOURCE/OPENROWSET
872+ if p .curTok .Type == TokenLParen {
873+ p .nextToken () // consume (
874+
875+ // Parse provider name
876+ var providerName * ast.StringLiteral
877+ if p .curTok .Type == TokenString {
878+ providerName = p .parseStringLiteralValue ()
879+ p .nextToken ()
880+ }
881+
882+ // Expect comma
883+ if p .curTok .Type == TokenComma {
884+ p .nextToken ()
885+ }
886+
887+ // Parse init string
888+ var initString * ast.StringLiteral
889+ if p .curTok .Type == TokenString {
890+ initString = p .parseStringLiteralValue ()
891+ p .nextToken ()
892+ }
893+
894+ // Expect )
895+ if p .curTok .Type == TokenRParen {
896+ p .nextToken ()
897+ }
898+
899+ procRef .AdHocDataSource = & ast.AdHocDataSource {
900+ ProviderName : providerName ,
901+ InitString : initString ,
902+ }
903+
904+ // Expect . and then schema.object.procedure name
905+ if p .curTok .Type == TokenDot {
906+ p .nextToken () // consume .
907+ }
908+ }
909+ }
910+
868911 if p .curTok .Type == TokenIdent && strings .HasPrefix (p .curTok .Literal , "@" ) {
869912 // Procedure variable
870913 procRef .ProcedureReference = & ast.ProcedureReferenceName {
871914 ProcedureVariable : & ast.VariableReference {Name : p .curTok .Literal },
872915 }
873916 p .nextToken ()
874- } else {
917+ } else if p . curTok . Type != TokenEOF && p . curTok . Type != TokenSemicolon {
875918 // Procedure name
876919 son , err := p .parseSchemaObjectName ()
877920 if err != nil {
878921 return nil , err
879922 }
923+ pr := & ast.ProcedureReference {Name : son }
924+
925+ // Check for procedure number: ;number
926+ if p .curTok .Type == TokenSemicolon {
927+ p .nextToken () // consume ;
928+ if p .curTok .Type == TokenNumber {
929+ pr .Number = & ast.IntegerLiteral {
930+ LiteralType : "Integer" ,
931+ Value : p .curTok .Literal ,
932+ }
933+ p .nextToken ()
934+ }
935+ }
936+
880937 procRef .ProcedureReference = & ast.ProcedureReferenceName {
881- ProcedureReference : & ast. ProcedureReference { Name : son } ,
938+ ProcedureReference : pr ,
882939 }
883940 }
884941
@@ -1015,11 +1072,83 @@ func (p *Parser) parseExecuteContextForSpec() (*ast.ExecuteContext, error) {
10151072func (p * Parser ) parseExecuteParameter () (* ast.ExecuteParameter , error ) {
10161073 param := & ast.ExecuteParameter {IsOutput : false }
10171074
1018- expr , err := p .parseScalarExpression ()
1019- if err != nil {
1020- return nil , err
1075+ // Check for DEFAULT keyword
1076+ if strings .ToUpper (p .curTok .Literal ) == "DEFAULT" {
1077+ param .ParameterValue = & ast.DefaultLiteral {LiteralType : "Default" , Value : "DEFAULT" }
1078+ p .nextToken ()
1079+ return param , nil
1080+ }
1081+
1082+ // Check for named parameter: @name = value
1083+ if p .curTok .Type == TokenIdent && strings .HasPrefix (p .curTok .Literal , "@" ) {
1084+ varName := p .curTok .Literal
1085+ p .nextToken ()
1086+
1087+ if p .curTok .Type == TokenEquals {
1088+ // Named parameter
1089+ p .nextToken () // consume =
1090+ param .Variable = & ast.VariableReference {Name : varName }
1091+
1092+ // Check for DEFAULT keyword as value
1093+ if strings .ToUpper (p .curTok .Literal ) == "DEFAULT" {
1094+ param .ParameterValue = & ast.DefaultLiteral {LiteralType : "Default" , Value : "DEFAULT" }
1095+ p .nextToken ()
1096+ } else {
1097+ // Parse the parameter value
1098+ expr , err := p .parseScalarExpression ()
1099+ if err != nil {
1100+ return nil , err
1101+ }
1102+ param .ParameterValue = expr
1103+ }
1104+ } else {
1105+ // Just a variable as value (not a named parameter)
1106+ param .ParameterValue = & ast.VariableReference {Name : varName }
1107+ }
1108+ } else {
1109+ // Check for bare identifier as IdentifierLiteral (e.g., EXEC sp_addtype birthday, datetime)
1110+ // Only if it's not followed by . or ( which would indicate a column/function reference
1111+ if p .curTok .Type == TokenIdent && ! strings .HasPrefix (p .curTok .Literal , "@" ) {
1112+ upper := strings .ToUpper (p .curTok .Literal )
1113+ // Skip keywords that are expression starters
1114+ isKeyword := upper == "NULL" || upper == "DEFAULT" || upper == "NOT" ||
1115+ upper == "CASE" || upper == "EXISTS" || upper == "CAST" ||
1116+ upper == "CONVERT" || upper == "COALESCE" || upper == "NULLIF"
1117+ if ! isKeyword && p .peekTok .Type != TokenDot && p .peekTok .Type != TokenLParen {
1118+ // Plain identifier - treat as IdentifierLiteral
1119+ quoteType := "NotQuoted"
1120+ if strings .HasPrefix (p .curTok .Literal , "[" ) {
1121+ quoteType = "SquareBracket"
1122+ }
1123+ param .ParameterValue = & ast.IdentifierLiteral {
1124+ LiteralType : "Identifier" ,
1125+ QuoteType : quoteType ,
1126+ Value : p .curTok .Literal ,
1127+ }
1128+ p .nextToken ()
1129+ } else {
1130+ // Regular value expression
1131+ expr , err := p .parseScalarExpression ()
1132+ if err != nil {
1133+ return nil , err
1134+ }
1135+ param .ParameterValue = expr
1136+ }
1137+ } else {
1138+ // Regular value expression
1139+ expr , err := p .parseScalarExpression ()
1140+ if err != nil {
1141+ return nil , err
1142+ }
1143+ param .ParameterValue = expr
1144+ }
1145+ }
1146+
1147+ // Check for OUTPUT modifier
1148+ if strings .ToUpper (p .curTok .Literal ) == "OUTPUT" || strings .ToUpper (p .curTok .Literal ) == "OUT" {
1149+ param .IsOutput = true
1150+ p .nextToken ()
10211151 }
1022- param .ParameterValue = expr
10231152
10241153 return param , nil
10251154}
0 commit comments