@@ -2512,6 +2512,10 @@ func (p *Parser) parseSingleTableReference() (ast.TableReference, error) {
25122512 ForPath : false ,
25132513 }, nil
25142514 }
2515+ // Handle OPENJSON specially
2516+ if upper == "OPENJSON" {
2517+ return p .parseOpenJsonTableReference (params , alias )
2518+ }
25152519 }
25162520
25172521 ref := & ast.SchemaObjectFunctionTableReference {
@@ -2528,6 +2532,113 @@ func (p *Parser) parseSingleTableReference() (ast.TableReference, error) {
25282532 return p .parseNamedTableReferenceWithName (son )
25292533}
25302534
2535+ // parseOpenJsonTableReference parses OPENJSON function with optional WITH clause
2536+ func (p * Parser ) parseOpenJsonTableReference (params []ast.ScalarExpression , alias * ast.Identifier ) (ast.TableReference , error ) {
2537+ ref := & ast.OpenJsonTableReference {
2538+ ForPath : false ,
2539+ Alias : alias ,
2540+ }
2541+
2542+ // First parameter is the Variable (JSON expression)
2543+ if len (params ) > 0 {
2544+ ref .Variable = params [0 ]
2545+ }
2546+
2547+ // Second parameter is the RowPattern (optional path expression)
2548+ if len (params ) > 1 {
2549+ ref .RowPattern = params [1 ]
2550+ }
2551+
2552+ // Check for WITH clause (schema declaration)
2553+ if p .curTok .Type == TokenWith {
2554+ p .nextToken () // consume WITH
2555+ if p .curTok .Type != TokenLParen {
2556+ return nil , fmt .Errorf ("expected ( after OPENJSON WITH, got %s" , p .curTok .Literal )
2557+ }
2558+ p .nextToken () // consume (
2559+
2560+ // Parse schema declaration items
2561+ for p .curTok .Type != TokenRParen && p .curTok .Type != TokenEOF {
2562+ item , err := p .parseSchemaDeclarationItemOpenjson ()
2563+ if err != nil {
2564+ return nil , err
2565+ }
2566+ ref .SchemaDeclarationItems = append (ref .SchemaDeclarationItems , item )
2567+
2568+ if p .curTok .Type == TokenComma {
2569+ p .nextToken ()
2570+ } else {
2571+ break
2572+ }
2573+ }
2574+
2575+ if p .curTok .Type == TokenRParen {
2576+ p .nextToken () // consume )
2577+ }
2578+ }
2579+
2580+ // Parse optional alias after WITH clause
2581+ if ref .Alias == nil {
2582+ if p .curTok .Type == TokenAs {
2583+ p .nextToken ()
2584+ ref .Alias = p .parseIdentifier ()
2585+ } else if p .curTok .Type == TokenIdent {
2586+ upper := strings .ToUpper (p .curTok .Literal )
2587+ if upper != "WHERE" && upper != "GROUP" && upper != "HAVING" && upper != "WINDOW" && upper != "ORDER" &&
2588+ upper != "OPTION" && upper != "GO" && upper != "WITH" && upper != "ON" &&
2589+ upper != "JOIN" && upper != "INNER" && upper != "LEFT" && upper != "RIGHT" &&
2590+ upper != "FULL" && upper != "CROSS" && upper != "OUTER" && upper != "FOR" {
2591+ ref .Alias = p .parseIdentifier ()
2592+ }
2593+ }
2594+ }
2595+
2596+ return ref , nil
2597+ }
2598+
2599+ // parseSchemaDeclarationItemOpenjson parses a column definition in OPENJSON WITH clause
2600+ func (p * Parser ) parseSchemaDeclarationItemOpenjson () (* ast.SchemaDeclarationItemOpenjson , error ) {
2601+ item := & ast.SchemaDeclarationItemOpenjson {
2602+ ColumnDefinition : & ast.ColumnDefinitionBase {},
2603+ }
2604+
2605+ // Parse column name
2606+ item .ColumnDefinition .ColumnIdentifier = p .parseIdentifier ()
2607+
2608+ // Parse data type
2609+ dataType , err := p .parseDataTypeReference ()
2610+ if err != nil {
2611+ return nil , err
2612+ }
2613+ item .ColumnDefinition .DataType = dataType
2614+
2615+ // Parse optional COLLATE
2616+ if strings .ToUpper (p .curTok .Literal ) == "COLLATE" {
2617+ p .nextToken () // consume COLLATE
2618+ item .ColumnDefinition .Collation = p .parseIdentifier ()
2619+ }
2620+
2621+ // Parse optional path mapping (string literal) or AS JSON
2622+ if p .curTok .Type == TokenString || p .curTok .Type == TokenNationalString {
2623+ mapping , err := p .parseScalarExpression ()
2624+ if err != nil {
2625+ return nil , err
2626+ }
2627+ item .Mapping = mapping
2628+ }
2629+
2630+ // Parse optional AS JSON
2631+ if p .curTok .Type == TokenAs {
2632+ p .nextToken () // consume AS
2633+ if strings .ToUpper (p .curTok .Literal ) == "JSON" {
2634+ item .AsJson = true
2635+ p .nextToken () // consume JSON
2636+ }
2637+ }
2638+
2639+ return item , nil
2640+ }
2641+
25312642// parseDerivedTableReference parses a derived table (parenthesized query) like (SELECT ...) AS alias
25322643// or an inline derived table (VALUES clause) like (VALUES (...), (...)) AS alias(cols)
25332644// or a data modification table reference (DML with OUTPUT) like (INSERT ... OUTPUT ...) AS alias
0 commit comments