@@ -2814,6 +2814,54 @@ func (p *Parser) parseBooleanPrimaryExpression() (ast.BooleanExpression, error)
28142814 return nil , err
28152815 }
28162816
2817+ // Check if we got a placeholder for a scalar expression without comparison
2818+ // This happens when parsing something like (XACT_STATE()) in: IF (XACT_STATE()) = -1
2819+ if placeholder , ok := inner .(* ast.BooleanScalarPlaceholder ); ok {
2820+ // The inner content was a bare scalar expression
2821+ // curTok should still be ) since we didn't consume it
2822+ if p .curTok .Type != TokenRParen {
2823+ return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
2824+ }
2825+ p .nextToken () // consume )
2826+
2827+ // Wrap the scalar in a ParenthesisExpression
2828+ parenExpr := & ast.ParenthesisExpression {Expression : placeholder .Scalar }
2829+
2830+ // Check for comparison operators after the parenthesized expression
2831+ if p .isComparisonOperator () {
2832+ return p .parseComparisonAfterLeft (parenExpr )
2833+ }
2834+
2835+ // Check for IS NULL / IS NOT NULL
2836+ if p .curTok .Type == TokenIs {
2837+ return p .parseIsNullAfterLeft (parenExpr )
2838+ }
2839+
2840+ // Check for NOT before IN/LIKE/BETWEEN
2841+ notDefined := false
2842+ if p .curTok .Type == TokenNot {
2843+ notDefined = true
2844+ p .nextToken ()
2845+ }
2846+
2847+ if p .curTok .Type == TokenIn {
2848+ return p .parseInExpressionAfterLeft (parenExpr , notDefined )
2849+ }
2850+ if p .curTok .Type == TokenLike {
2851+ return p .parseLikeExpressionAfterLeft (parenExpr , notDefined )
2852+ }
2853+ if p .curTok .Type == TokenBetween {
2854+ return p .parseBetweenExpressionAfterLeft (parenExpr , notDefined )
2855+ }
2856+
2857+ if notDefined {
2858+ return nil , fmt .Errorf ("expected IN, LIKE, or BETWEEN after NOT, got %s" , p .curTok .Literal )
2859+ }
2860+
2861+ // If no comparison follows, return error
2862+ return nil , fmt .Errorf ("expected comparison operator after parenthesized expression, got %s" , p .curTok .Literal )
2863+ }
2864+
28172865 if p .curTok .Type != TokenRParen {
28182866 return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
28192867 }
@@ -2983,6 +3031,11 @@ func (p *Parser) parseBooleanPrimaryExpression() (ast.BooleanExpression, error)
29833031 compType = "LessThanOrEqualTo"
29843032 case TokenGreaterOrEqual :
29853033 compType = "GreaterThanOrEqualTo"
3034+ case TokenRParen :
3035+ // We're at ) without a comparison operator - this happens when parsing
3036+ // a parenthesized scalar expression like (XACT_STATE()) in a boolean context.
3037+ // Return a special marker that the caller can handle.
3038+ return & ast.BooleanScalarPlaceholder {Scalar : left }, nil
29863039 default :
29873040 return nil , fmt .Errorf ("expected comparison operator, got %s" , p .curTok .Literal )
29883041 }
@@ -3046,6 +3099,171 @@ func (p *Parser) parseComparisonAfterLeft(left ast.ScalarExpression) (ast.Boolea
30463099 }, nil
30473100}
30483101
3102+ // parseInExpressionAfterLeft parses an IN expression after the left operand is already parsed
3103+ func (p * Parser ) parseInExpressionAfterLeft (left ast.ScalarExpression , notDefined bool ) (ast.BooleanExpression , error ) {
3104+ p .nextToken () // consume IN
3105+
3106+ if p .curTok .Type != TokenLParen {
3107+ return nil , fmt .Errorf ("expected ( after IN, got %s" , p .curTok .Literal )
3108+ }
3109+ p .nextToken () // consume (
3110+
3111+ // Check if it's a subquery or value list
3112+ if p .curTok .Type == TokenSelect {
3113+ subquery , err := p .parseQueryExpression ()
3114+ if err != nil {
3115+ return nil , err
3116+ }
3117+ if p .curTok .Type != TokenRParen {
3118+ return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
3119+ }
3120+ p .nextToken () // consume )
3121+ return & ast.BooleanInExpression {
3122+ Expression : left ,
3123+ NotDefined : notDefined ,
3124+ Subquery : subquery ,
3125+ }, nil
3126+ }
3127+
3128+ // Parse value list
3129+ var values []ast.ScalarExpression
3130+ for {
3131+ val , err := p .parseScalarExpression ()
3132+ if err != nil {
3133+ return nil , err
3134+ }
3135+ values = append (values , val )
3136+ if p .curTok .Type != TokenComma {
3137+ break
3138+ }
3139+ p .nextToken () // consume ,
3140+ }
3141+ if p .curTok .Type != TokenRParen {
3142+ return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
3143+ }
3144+ p .nextToken () // consume )
3145+ return & ast.BooleanInExpression {
3146+ Expression : left ,
3147+ NotDefined : notDefined ,
3148+ Values : values ,
3149+ }, nil
3150+ }
3151+
3152+ // parseLikeExpressionAfterLeft parses a LIKE expression after the left operand is already parsed
3153+ func (p * Parser ) parseLikeExpressionAfterLeft (left ast.ScalarExpression , notDefined bool ) (ast.BooleanExpression , error ) {
3154+ p .nextToken () // consume LIKE
3155+
3156+ pattern , err := p .parseScalarExpression ()
3157+ if err != nil {
3158+ return nil , err
3159+ }
3160+
3161+ var escapeExpr ast.ScalarExpression
3162+ if p .curTok .Type == TokenEscape {
3163+ p .nextToken () // consume ESCAPE
3164+ escapeExpr , err = p .parseScalarExpression ()
3165+ if err != nil {
3166+ return nil , err
3167+ }
3168+ }
3169+
3170+ return & ast.BooleanLikeExpression {
3171+ FirstExpression : left ,
3172+ SecondExpression : pattern ,
3173+ EscapeExpression : escapeExpr ,
3174+ NotDefined : notDefined ,
3175+ }, nil
3176+ }
3177+
3178+ // parseBetweenExpressionAfterLeft parses a BETWEEN expression after the left operand is already parsed
3179+ func (p * Parser ) parseBetweenExpressionAfterLeft (left ast.ScalarExpression , notDefined bool ) (ast.BooleanExpression , error ) {
3180+ p .nextToken () // consume BETWEEN
3181+
3182+ low , err := p .parseScalarExpression ()
3183+ if err != nil {
3184+ return nil , err
3185+ }
3186+
3187+ if p .curTok .Type != TokenAnd {
3188+ return nil , fmt .Errorf ("expected AND in BETWEEN, got %s" , p .curTok .Literal )
3189+ }
3190+ p .nextToken () // consume AND
3191+
3192+ high , err := p .parseScalarExpression ()
3193+ if err != nil {
3194+ return nil , err
3195+ }
3196+
3197+ ternaryType := "Between"
3198+ if notDefined {
3199+ ternaryType = "NotBetween"
3200+ }
3201+ return & ast.BooleanTernaryExpression {
3202+ TernaryExpressionType : ternaryType ,
3203+ FirstExpression : left ,
3204+ SecondExpression : low ,
3205+ ThirdExpression : high ,
3206+ }, nil
3207+ }
3208+
3209+ // finishParenthesizedBooleanExpression finishes parsing a parenthesized boolean expression
3210+ // after the initial comparison/expression has been parsed
3211+ func (p * Parser ) finishParenthesizedBooleanExpression (inner ast.BooleanExpression ) (ast.BooleanExpression , error ) {
3212+ // Check for AND/OR continuation
3213+ for p .curTok .Type == TokenAnd || p .curTok .Type == TokenOr {
3214+ op := p .curTok .Type
3215+ p .nextToken ()
3216+
3217+ right , err := p .parseBooleanPrimaryExpression ()
3218+ if err != nil {
3219+ return nil , err
3220+ }
3221+
3222+ if op == TokenAnd {
3223+ inner = & ast.BooleanBinaryExpression {
3224+ BinaryExpressionType : "And" ,
3225+ FirstExpression : inner ,
3226+ SecondExpression : right ,
3227+ }
3228+ } else {
3229+ inner = & ast.BooleanBinaryExpression {
3230+ BinaryExpressionType : "Or" ,
3231+ FirstExpression : inner ,
3232+ SecondExpression : right ,
3233+ }
3234+ }
3235+ }
3236+
3237+ // Expect closing parenthesis
3238+ if p .curTok .Type != TokenRParen {
3239+ return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
3240+ }
3241+ p .nextToken () // consume )
3242+
3243+ return & ast.BooleanParenthesisExpression {Expression : inner }, nil
3244+ }
3245+
3246+ // parseIsNullAfterLeft parses IS NULL / IS NOT NULL after the left operand is already parsed
3247+ func (p * Parser ) parseIsNullAfterLeft (left ast.ScalarExpression ) (ast.BooleanExpression , error ) {
3248+ p .nextToken () // consume IS
3249+
3250+ isNot := false
3251+ if p .curTok .Type == TokenNot {
3252+ isNot = true
3253+ p .nextToken () // consume NOT
3254+ }
3255+
3256+ if p .curTok .Type != TokenNull {
3257+ return nil , fmt .Errorf ("expected NULL after IS/IS NOT, got %s" , p .curTok .Literal )
3258+ }
3259+ p .nextToken () // consume NULL
3260+
3261+ return & ast.BooleanIsNullExpression {
3262+ IsNot : isNot ,
3263+ Expression : left ,
3264+ }, nil
3265+ }
3266+
30493267// identifiersToSchemaObjectName converts a slice of identifiers to a SchemaObjectName.
30503268// For 1 identifier: BaseIdentifier
30513269// For 2 identifiers: SchemaIdentifier.BaseIdentifier
0 commit comments