@@ -2856,6 +2856,15 @@ func (p *Parser) parseNamedTableReferenceWithName(son *ast.SchemaObjectName) (*a
28562856 ForPath : false ,
28572857 }
28582858
2859+ // Parse FOR SYSTEM_TIME clause (temporal tables)
2860+ if p .curTok .Type == TokenIdent && strings .ToUpper (p .curTok .Literal ) == "FOR" && strings .ToUpper (p .peekTok .Literal ) == "SYSTEM_TIME" {
2861+ temporal , err := p .parseTemporalClause ()
2862+ if err != nil {
2863+ return nil , err
2864+ }
2865+ ref .TemporalClause = temporal
2866+ }
2867+
28592868 // Check for TABLESAMPLE before alias
28602869 if strings .ToUpper (p .curTok .Literal ) == "TABLESAMPLE" {
28612870 tableSample , err := p .parseTableSampleClause ()
@@ -2978,6 +2987,127 @@ func (p *Parser) parseNamedTableReferenceWithName(son *ast.SchemaObjectName) (*a
29782987 return ref , nil
29792988}
29802989
2990+ // parseTemporalClause parses a FOR SYSTEM_TIME clause for temporal tables
2991+ func (p * Parser ) parseTemporalClause () (* ast.TemporalClause , error ) {
2992+ clause := & ast.TemporalClause {}
2993+
2994+ p .nextToken () // consume FOR
2995+ p .nextToken () // consume SYSTEM_TIME
2996+
2997+ upper := strings .ToUpper (p .curTok .Literal )
2998+ switch upper {
2999+ case "AS" :
3000+ // AS OF <time>
3001+ p .nextToken () // consume AS
3002+ if strings .ToUpper (p .curTok .Literal ) != "OF" {
3003+ return nil , fmt .Errorf ("expected OF after AS, got %s" , p .curTok .Literal )
3004+ }
3005+ p .nextToken () // consume OF
3006+ clause .TemporalClauseType = "AsOf"
3007+ startTime , err := p .parseTemporalTimeValue ()
3008+ if err != nil {
3009+ return nil , err
3010+ }
3011+ clause .StartTime = startTime
3012+
3013+ case "BETWEEN" :
3014+ // BETWEEN <start> AND <end>
3015+ p .nextToken () // consume BETWEEN
3016+ clause .TemporalClauseType = "Between"
3017+ startTime , err := p .parseTemporalTimeValue ()
3018+ if err != nil {
3019+ return nil , err
3020+ }
3021+ clause .StartTime = startTime
3022+ if p .curTok .Type != TokenAnd {
3023+ return nil , fmt .Errorf ("expected AND, got %s" , p .curTok .Literal )
3024+ }
3025+ p .nextToken () // consume AND
3026+ endTime , err := p .parseTemporalTimeValue ()
3027+ if err != nil {
3028+ return nil , err
3029+ }
3030+ clause .EndTime = endTime
3031+
3032+ case "FROM" :
3033+ // FROM <start> TO <end>
3034+ p .nextToken () // consume FROM
3035+ clause .TemporalClauseType = "FromTo"
3036+ startTime , err := p .parseTemporalTimeValue ()
3037+ if err != nil {
3038+ return nil , err
3039+ }
3040+ clause .StartTime = startTime
3041+ if strings .ToUpper (p .curTok .Literal ) != "TO" {
3042+ return nil , fmt .Errorf ("expected TO, got %s" , p .curTok .Literal )
3043+ }
3044+ p .nextToken () // consume TO
3045+ endTime , err := p .parseTemporalTimeValue ()
3046+ if err != nil {
3047+ return nil , err
3048+ }
3049+ clause .EndTime = endTime
3050+
3051+ case "CONTAINED" :
3052+ // CONTAINED IN (<start>, <end>)
3053+ p .nextToken () // consume CONTAINED
3054+ if strings .ToUpper (p .curTok .Literal ) != "IN" {
3055+ return nil , fmt .Errorf ("expected IN after CONTAINED, got %s" , p .curTok .Literal )
3056+ }
3057+ p .nextToken () // consume IN
3058+ if p .curTok .Type != TokenLParen {
3059+ return nil , fmt .Errorf ("expected ( after CONTAINED IN, got %s" , p .curTok .Literal )
3060+ }
3061+ p .nextToken () // consume (
3062+ clause .TemporalClauseType = "ContainedIn"
3063+ startTime , err := p .parseTemporalTimeValue ()
3064+ if err != nil {
3065+ return nil , err
3066+ }
3067+ clause .StartTime = startTime
3068+ if p .curTok .Type != TokenComma {
3069+ return nil , fmt .Errorf ("expected comma, got %s" , p .curTok .Literal )
3070+ }
3071+ p .nextToken () // consume ,
3072+ endTime , err := p .parseTemporalTimeValue ()
3073+ if err != nil {
3074+ return nil , err
3075+ }
3076+ clause .EndTime = endTime
3077+ if p .curTok .Type != TokenRParen {
3078+ return nil , fmt .Errorf ("expected ), got %s" , p .curTok .Literal )
3079+ }
3080+ p .nextToken () // consume )
3081+
3082+ case "ALL" :
3083+ // ALL
3084+ p .nextToken () // consume ALL
3085+ clause .TemporalClauseType = "TemporalAll"
3086+
3087+ default :
3088+ return nil , fmt .Errorf ("unexpected temporal clause type: %s" , p .curTok .Literal )
3089+ }
3090+
3091+ return clause , nil
3092+ }
3093+
3094+ // parseTemporalTimeValue parses a time value in a temporal clause (string literal or variable)
3095+ func (p * Parser ) parseTemporalTimeValue () (ast.ScalarExpression , error ) {
3096+ if p .curTok .Type == TokenString || p .curTok .Type == TokenNationalString {
3097+ lit , err := p .parseStringLiteral ()
3098+ if err != nil {
3099+ return nil , err
3100+ }
3101+ return lit , nil
3102+ }
3103+ if p .curTok .Type == TokenIdent && strings .HasPrefix (p .curTok .Literal , "@" ) {
3104+ varRef := & ast.VariableReference {Name : p .curTok .Literal }
3105+ p .nextToken ()
3106+ return varRef , nil
3107+ }
3108+ return nil , fmt .Errorf ("expected string literal or variable for temporal time, got %s" , p .curTok .Literal )
3109+ }
3110+
29813111// parseFullTextTableReference parses CONTAINSTABLE or FREETEXTTABLE
29823112func (p * Parser ) parseFullTextTableReference (funcType string ) (* ast.FullTextTableReference , error ) {
29833113 ref := & ast.FullTextTableReference {
0 commit comments