@@ -471,10 +471,15 @@ func (p *Parser) parseSelectElement() (ast.SelectElement, error) {
471471 }
472472
473473 // Not an assignment, treat as regular scalar expression starting with variable
474- varRef := & ast.VariableReference {Name : varName }
474+ var varExpr ast.ScalarExpression
475+ if strings .HasPrefix (varName , "@@" ) {
476+ varExpr = & ast.GlobalVariableExpression {Name : varName }
477+ } else {
478+ varExpr = & ast.VariableReference {Name : varName }
479+ }
475480
476481 // Handle postfix operations (method calls, property access)
477- expr , err := p .handlePostfixOperations (varRef )
482+ expr , err := p .handlePostfixOperations (varExpr )
478483 if err != nil {
479484 return nil , err
480485 }
@@ -1899,6 +1904,10 @@ func (p *Parser) parseFunctionCallFromIdentifiers(identifiers []*ast.Identifier)
18991904 return p .parseParseCall (false )
19001905 case "TRY_PARSE" :
19011906 return p .parseParseCall (true )
1907+ case "JSON_OBJECT" :
1908+ return p .parseJsonObjectCall ()
1909+ case "JSON_ARRAY" :
1910+ return p .parseJsonArrayCall ()
19021911 }
19031912 }
19041913
@@ -5866,6 +5875,149 @@ func (p *Parser) parseParseCall(isTry bool) (ast.ScalarExpression, error) {
58665875 }, nil
58675876}
58685877
5878+ // parseJsonObjectCall parses JSON_OBJECT('key':value, 'key2':value2, ... [NULL|ABSENT ON NULL])
5879+ func (p * Parser ) parseJsonObjectCall () (* ast.FunctionCall , error ) {
5880+ fc := & ast.FunctionCall {
5881+ FunctionName : & ast.Identifier {Value : "JSON_OBJECT" , QuoteType : "NotQuoted" },
5882+ UniqueRowFilter : "NotSpecified" ,
5883+ WithArrayWrapper : false ,
5884+ }
5885+
5886+ p .nextToken () // consume (
5887+
5888+ // Parse key-value pairs
5889+ for p .curTok .Type != TokenRParen && p .curTok .Type != TokenEOF {
5890+ // Check for NULL ON NULL or ABSENT ON NULL at start of loop
5891+ upperLit := strings .ToUpper (p .curTok .Literal )
5892+ if upperLit == "NULL" || upperLit == "ABSENT" {
5893+ // Look ahead to see if this is "NULL ON NULL" or "ABSENT ON NULL"
5894+ if p .peekIsOnNull () {
5895+ fc .AbsentOrNullOnNull = append (fc .AbsentOrNullOnNull , & ast.Identifier {Value : upperLit , QuoteType : "NotQuoted" })
5896+ p .nextToken () // consume NULL or ABSENT
5897+ p .nextToken () // consume ON
5898+ p .nextToken () // consume NULL
5899+ continue
5900+ }
5901+ }
5902+
5903+ // Parse key expression
5904+ keyExpr , err := p .parseScalarExpression ()
5905+ if err != nil {
5906+ return nil , err
5907+ }
5908+
5909+ // Check for : (JSON key-value separator)
5910+ if p .curTok .Type == TokenColon {
5911+ p .nextToken () // consume :
5912+
5913+ // Parse value expression
5914+ valueExpr , err := p .parseScalarExpression ()
5915+ if err != nil {
5916+ return nil , err
5917+ }
5918+
5919+ fc .JsonParameters = append (fc .JsonParameters , & ast.JsonKeyValue {
5920+ JsonKeyName : keyExpr ,
5921+ JsonValue : valueExpr ,
5922+ })
5923+ } else {
5924+ // Just a regular parameter without colon (shouldn't happen for JSON_OBJECT)
5925+ fc .Parameters = append (fc .Parameters , keyExpr )
5926+ }
5927+
5928+ // After parsing a value, check for NULL ON NULL or ABSENT ON NULL
5929+ postValueLit := strings .ToUpper (p .curTok .Literal )
5930+ if postValueLit == "NULL" || postValueLit == "ABSENT" {
5931+ if p .peekIsOnNull () {
5932+ fc .AbsentOrNullOnNull = append (fc .AbsentOrNullOnNull , & ast.Identifier {Value : postValueLit , QuoteType : "NotQuoted" })
5933+ p .nextToken () // consume NULL or ABSENT
5934+ p .nextToken () // consume ON
5935+ p .nextToken () // consume NULL
5936+ // Continue to check for ) or comma
5937+ }
5938+ }
5939+
5940+ if p .curTok .Type == TokenComma {
5941+ p .nextToken () // consume ,
5942+ } else {
5943+ break
5944+ }
5945+ }
5946+
5947+ if p .curTok .Type != TokenRParen {
5948+ return nil , fmt .Errorf ("expected ) in JSON_OBJECT, got %s" , p .curTok .Literal )
5949+ }
5950+ p .nextToken () // consume )
5951+
5952+ return fc , nil
5953+ }
5954+
5955+ // parseJsonArrayCall parses JSON_ARRAY(value1, value2, ... [NULL|ABSENT ON NULL])
5956+ func (p * Parser ) parseJsonArrayCall () (* ast.FunctionCall , error ) {
5957+ fc := & ast.FunctionCall {
5958+ FunctionName : & ast.Identifier {Value : "JSON_ARRAY" , QuoteType : "NotQuoted" },
5959+ UniqueRowFilter : "NotSpecified" ,
5960+ WithArrayWrapper : false ,
5961+ }
5962+
5963+ p .nextToken () // consume (
5964+
5965+ // Parse array elements
5966+ for p .curTok .Type != TokenRParen && p .curTok .Type != TokenEOF {
5967+ // Check for NULL ON NULL or ABSENT ON NULL at start of loop
5968+ upperLit := strings .ToUpper (p .curTok .Literal )
5969+ if upperLit == "NULL" || upperLit == "ABSENT" {
5970+ // Look ahead to see if this is "NULL ON NULL" or "ABSENT ON NULL"
5971+ if p .peekIsOnNull () {
5972+ fc .AbsentOrNullOnNull = append (fc .AbsentOrNullOnNull , & ast.Identifier {Value : upperLit , QuoteType : "NotQuoted" })
5973+ p .nextToken () // consume NULL or ABSENT
5974+ p .nextToken () // consume ON
5975+ p .nextToken () // consume NULL
5976+ continue
5977+ }
5978+ }
5979+
5980+ // Parse value expression
5981+ valueExpr , err := p .parseScalarExpression ()
5982+ if err != nil {
5983+ return nil , err
5984+ }
5985+ fc .Parameters = append (fc .Parameters , valueExpr )
5986+
5987+ // After parsing a value, check for NULL ON NULL or ABSENT ON NULL
5988+ postValueLit := strings .ToUpper (p .curTok .Literal )
5989+ if postValueLit == "NULL" || postValueLit == "ABSENT" {
5990+ if p .peekIsOnNull () {
5991+ fc .AbsentOrNullOnNull = append (fc .AbsentOrNullOnNull , & ast.Identifier {Value : postValueLit , QuoteType : "NotQuoted" })
5992+ p .nextToken () // consume NULL or ABSENT
5993+ p .nextToken () // consume ON
5994+ p .nextToken () // consume NULL
5995+ // Continue to check for ) or comma
5996+ }
5997+ }
5998+
5999+ if p .curTok .Type == TokenComma {
6000+ p .nextToken () // consume ,
6001+ } else {
6002+ break
6003+ }
6004+ }
6005+
6006+ if p .curTok .Type != TokenRParen {
6007+ return nil , fmt .Errorf ("expected ) in JSON_ARRAY, got %s" , p .curTok .Literal )
6008+ }
6009+ p .nextToken () // consume )
6010+
6011+ return fc , nil
6012+ }
6013+
6014+ // peekIsOnNull checks if the next tokens are "ON NULL"
6015+ func (p * Parser ) peekIsOnNull () bool {
6016+ // Just check if the next token is ON
6017+ // The caller will verify the NULL after ON when consuming
6018+ return p .peekTok .Type == TokenOn
6019+ }
6020+
58696021// parseChangeTableReference parses CHANGETABLE(CHANGES ...) or CHANGETABLE(VERSION ...)
58706022func (p * Parser ) parseChangeTableReference () (ast.TableReference , error ) {
58716023 p .nextToken () // consume CHANGETABLE
0 commit comments