Skip to content

Commit b07e1ae

Browse files
committed
Fix table hint and alias parsing order in FROM clause
Support both T-SQL table hint syntaxes: 1. Old-style: table_name (nolock) AS alias - hints before alias 2. New-style: table_name AS alias WITH (hints) - alias before hints The parser now correctly handles both patterns by checking for old-style hints first (parentheses without WITH), then parsing alias, then checking for new-style hints (WITH keyword followed by parentheses).
1 parent ca81ad1 commit b07e1ae

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

parser/parse_select.go

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,36 @@ func (p *Parser) parseNamedTableReference() (*ast.NamedTableReference, error) {
25462546
}
25472547
ref.SchemaObject = son
25482548

2549+
// T-SQL supports two syntaxes for table hints:
2550+
// 1. Old-style: table_name (nolock) AS alias - hints before alias, no WITH
2551+
// 2. New-style: table_name AS alias WITH (hints) - alias before hints, WITH required
2552+
2553+
// Check for old-style hints (without WITH keyword): table (nolock) as alias
2554+
if p.curTok.Type == TokenLParen && p.peekIsTableHint() {
2555+
p.nextToken() // consume (
2556+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
2557+
hint, err := p.parseTableHint()
2558+
if err != nil {
2559+
return nil, err
2560+
}
2561+
if hint != nil {
2562+
ref.TableHints = append(ref.TableHints, hint)
2563+
}
2564+
if p.curTok.Type == TokenComma {
2565+
p.nextToken()
2566+
} else if p.curTok.Type != TokenRParen {
2567+
// Check if the next token is a valid table hint (space-separated hints)
2568+
if p.isTableHintToken() {
2569+
continue // Continue parsing space-separated hints
2570+
}
2571+
break
2572+
}
2573+
}
2574+
if p.curTok.Type == TokenRParen {
2575+
p.nextToken()
2576+
}
2577+
}
2578+
25492579
// Parse optional alias (AS alias or just alias)
25502580
if p.curTok.Type == TokenAs {
25512581
p.nextToken()
@@ -2563,14 +2593,10 @@ func (p *Parser) parseNamedTableReference() (*ast.NamedTableReference, error) {
25632593
}
25642594
}
25652595

2566-
// Parse optional table hints WITH (hint, hint, ...) or old-style (hint, hint, ...)
2596+
// Check for new-style hints (with WITH keyword): alias WITH (hints)
25672597
if p.curTok.Type == TokenWith && p.peekTok.Type == TokenLParen {
25682598
p.nextToken() // consume WITH
2569-
}
2570-
if p.curTok.Type == TokenLParen {
2571-
// Check if this looks like hints (first token is a hint keyword)
2572-
// Save position to peek
2573-
if p.peekIsTableHint() {
2599+
if p.curTok.Type == TokenLParen && p.peekIsTableHint() {
25742600
p.nextToken() // consume (
25752601
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
25762602
hint, err := p.parseTableHint()
@@ -2583,9 +2609,8 @@ func (p *Parser) parseNamedTableReference() (*ast.NamedTableReference, error) {
25832609
if p.curTok.Type == TokenComma {
25842610
p.nextToken()
25852611
} else if p.curTok.Type != TokenRParen {
2586-
// Check if the next token is a valid table hint (space-separated hints)
25872612
if p.isTableHintToken() {
2588-
continue // Continue parsing space-separated hints
2613+
continue
25892614
}
25902615
break
25912616
}
@@ -2606,6 +2631,35 @@ func (p *Parser) parseNamedTableReferenceWithName(son *ast.SchemaObjectName) (*a
26062631
ForPath: false,
26072632
}
26082633

2634+
// T-SQL supports two syntaxes for table hints:
2635+
// 1. Old-style: table_name (nolock) AS alias - hints before alias, no WITH
2636+
// 2. New-style: table_name AS alias WITH (hints) - alias before hints, WITH required
2637+
2638+
// Check for old-style hints (without WITH keyword): table (nolock) as alias
2639+
if p.curTok.Type == TokenLParen && p.peekIsTableHint() {
2640+
p.nextToken() // consume (
2641+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
2642+
hint, err := p.parseTableHint()
2643+
if err != nil {
2644+
return nil, err
2645+
}
2646+
if hint != nil {
2647+
ref.TableHints = append(ref.TableHints, hint)
2648+
}
2649+
if p.curTok.Type == TokenComma {
2650+
p.nextToken()
2651+
} else if p.curTok.Type != TokenRParen {
2652+
if p.isTableHintToken() {
2653+
continue
2654+
}
2655+
break
2656+
}
2657+
}
2658+
if p.curTok.Type == TokenRParen {
2659+
p.nextToken()
2660+
}
2661+
}
2662+
26092663
// Parse optional alias (AS alias or just alias)
26102664
if p.curTok.Type == TokenAs {
26112665
p.nextToken()
@@ -2625,13 +2679,10 @@ func (p *Parser) parseNamedTableReferenceWithName(son *ast.SchemaObjectName) (*a
26252679
}
26262680
}
26272681

2628-
// Parse optional table hints WITH (hint, hint, ...) or old-style (hint, hint, ...)
2682+
// Check for new-style hints (with WITH keyword): alias WITH (hints)
26292683
if p.curTok.Type == TokenWith && p.peekTok.Type == TokenLParen {
26302684
p.nextToken() // consume WITH
2631-
}
2632-
if p.curTok.Type == TokenLParen {
2633-
// Check if this looks like hints (first token is a hint keyword)
2634-
if p.peekIsTableHint() {
2685+
if p.curTok.Type == TokenLParen && p.peekIsTableHint() {
26352686
p.nextToken() // consume (
26362687
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
26372688
hint, err := p.parseTableHint()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)