()
| 300 | } |
| 301 | |
| 302 | func (p *Parser) parseOneTimeTravelClause() (*ast.TimeTravelClause, error) { |
| 303 | pos := p.currentLocation() |
| 304 | kind := strings.ToUpper(p.currentToken.Token.Value) |
| 305 | p.advance() // Consume AT / BEFORE / CHANGES |
| 306 | if !p.isType(models.TokenTypeLParen) { |
| 307 | return nil, p.expectedError("( after " + kind) |
| 308 | } |
| 309 | p.advance() // Consume ( |
| 310 | |
| 311 | clause := &ast.TimeTravelClause{ |
| 312 | Kind: kind, |
| 313 | Named: map[string]ast.Expression{}, |
| 314 | Pos: pos, |
| 315 | } |
| 316 | |
| 317 | // Parse comma-separated named arguments: name => expr [, name => expr]... |
| 318 | // Snowflake uses TIMESTAMP, OFFSET, STATEMENT, INFORMATION as argument |
| 319 | // names; these tokenize as dedicated keyword types, not identifiers. |
| 320 | // Accept any non-punctuation token with a non-empty value as the name. |
| 321 | for { |
| 322 | argName := strings.ToUpper(p.currentToken.Token.Value) |
| 323 | if argName == "" || p.isType(models.TokenTypeRParen) || |
| 324 | p.isType(models.TokenTypeComma) || p.isType(models.TokenTypeLParen) { |
| 325 | return nil, p.expectedError("argument name in " + kind) |
| 326 | } |
| 327 | p.advance() |
| 328 | if p.currentToken.Token.Type != models.TokenTypeRArrow { |
| 329 | return nil, p.expectedError("=> after " + argName) |
| 330 | } |
| 331 | p.advance() // => |
| 332 | // Values are typically literal expressions, but may also be bare |
| 333 | // keywords like DEFAULT or APPEND_ONLY for CHANGES (INFORMATION => …). |
| 334 | var value ast.Expression |
| 335 | if v, err := p.parseExpression(); err == nil { |
| 336 | value = v |
| 337 | } else if p.currentToken.Token.Value != "" && |
| 338 | !p.isType(models.TokenTypeRParen) && !p.isType(models.TokenTypeComma) { |
| 339 | value = &ast.Identifier{Name: p.currentToken.Token.Value} |
| 340 | p.advance() |
| 341 | } else { |
| 342 | return nil, err |
| 343 | } |
| 344 | clause.Named[argName] = value |
| 345 | if p.isType(models.TokenTypeComma) { |
| 346 | p.advance() |
| 347 | continue |
| 348 | } |
| 349 | break |
| 350 | } |
| 351 | |
| 352 | if !p.isType(models.TokenTypeRParen) { |
| 353 | return nil, p.expectedError(")") |
| 354 | } |
| 355 | p.advance() // Consume ) |
| 356 | return clause, nil |
| 357 | } |
| 358 | |
| 359 | // isSnowflakeTimeTravelStart returns true when the current token begins an |
no test coverage detected