(pos Pos)
| 414 | } |
| 415 | |
| 416 | func (p *Parser) parseColumnExpr(pos Pos) (Expr, error) { //nolint:funlen |
| 417 | // Should parse the keyword as an identifier if the keyword is followed by one of comma, `AS`. |
| 418 | // For example: `SELECT 1 as interval GROUP BY interval` is a valid syntax in ClickHouse. |
| 419 | if p.matchTokenKind(TokenKindKeyword) && (p.peekTokenKind(TokenKindComma) || p.peekKeyword(KeywordAs)) { |
| 420 | return p.parseIdent() |
| 421 | } |
| 422 | switch { |
| 423 | case p.matchKeyword(KeywordInterval): |
| 424 | return p.parseInterval(true) |
| 425 | case p.matchKeyword(KeywordDate), p.matchKeyword(KeywordTimestamp): |
| 426 | nextToken, err := p.lexer.peekToken() |
| 427 | if err != nil { |
| 428 | return nil, err |
| 429 | } |
| 430 | if nextToken != nil && nextToken.Kind == TokenKindString { |
| 431 | return p.parseString(p.Pos()) |
| 432 | } |
| 433 | return p.parseIdentOrFunction(pos) |
| 434 | case p.matchKeyword(KeywordCast): |
| 435 | return p.parseColumnCastExpr(pos) |
| 436 | case p.matchKeyword(KeywordCase): |
| 437 | return p.parseColumnCaseExpr(pos) |
| 438 | case p.matchKeyword(KeywordSelect): |
| 439 | return p.parseSelectQuery(pos) |
| 440 | case p.matchKeyword(KeywordExtract): |
| 441 | return p.parseColumnExtractExpr(pos) |
| 442 | case p.matchTokenKind(TokenKindIdent): |
| 443 | return p.parseIdentOrFunction(pos) |
| 444 | case p.matchTokenKind(TokenKindString): // string literal |
| 445 | return p.parseString(pos) |
| 446 | case p.matchTokenKind(TokenKindInt), |
| 447 | p.matchTokenKind(TokenKindFloat): // number literal |
| 448 | return p.parseNumber(pos) |
| 449 | case p.matchTokenKind(TokenKindLParen): |
| 450 | if peek, _ := p.lexer.peekToken(); peek != nil { |
| 451 | if peek.Kind == TokenKindKeyword && strings.EqualFold(peek.String, KeywordSelect) { |
| 452 | return p.parseSubQuery(pos) |
| 453 | } |
| 454 | } |
| 455 | return p.parseFunctionParams(p.Pos()) |
| 456 | case p.matchTokenKind("*"): |
| 457 | return p.parseColumnStar(p.Pos()) |
| 458 | case p.matchTokenKind(TokenKindLBracket): |
| 459 | return p.parseArrayParams(p.Pos()) |
| 460 | case p.matchTokenKind(TokenKindLBrace): |
| 461 | // The map literal string also starts with '{', so we need to check the next token |
| 462 | // to determine if it is a map literal or a query param. |
| 463 | // Treat both identifiers and keywords as identifier-like for placeholders. |
| 464 | // parseIdent accepts keywords-as-ident, so this is safe. |
| 465 | if p.peekTokenKind(TokenKindIdent) || p.peekTokenKind(TokenKindKeyword) { |
| 466 | return p.parseQueryParam(p.Pos()) |
| 467 | } |
| 468 | return p.parseMapLiteral(p.Pos()) |
| 469 | case p.matchTokenKind(TokenKindDot): |
| 470 | return p.parseNumber(p.Pos()) |
| 471 | case p.matchTokenKind(TokenKindQuestionMark): |
| 472 | // Placeholder `?` |
| 473 | _ = p.lexer.consumeToken() |
no test coverage detected