parseJoinedTableRef parses the table reference on the right-hand side of a JOIN.
(joinType string)
| 259 | |
| 260 | // parseJoinedTableRef parses the table reference on the right-hand side of a JOIN. |
| 261 | func (p *Parser) parseJoinedTableRef(joinType string) (ast.TableReference, error) { |
| 262 | var ref ast.TableReference |
| 263 | |
| 264 | // Optional LATERAL (PostgreSQL) |
| 265 | isLateral := false |
| 266 | if p.isType(models.TokenTypeLateral) { |
| 267 | isLateral = true |
| 268 | p.advance() |
| 269 | } |
| 270 | |
| 271 | if p.isType(models.TokenTypeLParen) { |
| 272 | // Derived table (subquery) |
| 273 | p.advance() // Consume ( |
| 274 | |
| 275 | if !p.isType(models.TokenTypeSelect) && !p.isType(models.TokenTypeWith) { |
| 276 | return ref, p.expectedError("SELECT in derived table") |
| 277 | } |
| 278 | p.advance() // Consume SELECT |
| 279 | |
| 280 | subquery, err := p.parseSelectStatement() |
| 281 | if err != nil { |
| 282 | return ref, err |
| 283 | } |
| 284 | selectStmt, ok := subquery.(*ast.SelectStatement) |
| 285 | if !ok { |
| 286 | return ref, p.expectedError("SELECT statement in derived table") |
| 287 | } |
| 288 | |
| 289 | if !p.isType(models.TokenTypeRParen) { |
| 290 | return ref, p.expectedError(")") |
| 291 | } |
| 292 | p.advance() // Consume ) |
| 293 | |
| 294 | ref = ast.TableReference{Subquery: selectStmt, Lateral: isLateral} |
| 295 | } else { |
| 296 | joinedName, err := p.parseQualifiedName() |
| 297 | if err != nil { |
| 298 | return ref, goerrors.ExpectedTokenError( |
| 299 | "table name after "+joinType+" JOIN", |
| 300 | p.currentToken.Token.Type.String(), |
| 301 | p.currentLocation(), |
| 302 | "", |
| 303 | ) |
| 304 | } |
| 305 | ref = ast.TableReference{Name: joinedName, Lateral: isLateral} |
| 306 | } |
| 307 | |
| 308 | // Optional alias. |
| 309 | // Guard: in MariaDB, CONNECT followed by BY is a hierarchical query clause, not an alias. |
| 310 | // Similarly, START followed by WITH is a hierarchical query seed, not an alias. |
| 311 | // Don't consume PIVOT/UNPIVOT as a table alias — they are contextual |
| 312 | // keywords in SQL Server/Oracle and must reach the pivot-clause parser below. |
| 313 | if (p.isIdentifier() || p.isType(models.TokenTypeAs)) && !p.isMariaDBClauseStart() && !p.isPivotKeyword() && !p.isUnpivotKeyword() && !p.isQualifyKeyword() && !p.isMinusSetOp() && !p.isSnowflakeTimeTravelStart() && !p.isSampleKeyword() && !p.isMatchRecognizeKeyword() { |
| 314 | if p.isType(models.TokenTypeAs) { |
| 315 | p.advance() |
| 316 | if !p.isIdentifier() { |
| 317 | return ref, p.expectedError("alias after AS") |
| 318 | } |
no test coverage detected