parseSelectWithSetOperations parses SELECT statements that may have set operations. It supports UNION, UNION ALL, EXCEPT, and INTERSECT operations with proper left-associative parsing. Examples: SELECT name FROM users UNION SELECT name FROM customers SELECT id FROM orders UNION ALL SELECT id FRO
()
| 37 | // SELECT product FROM inventory EXCEPT SELECT product FROM discontinued |
| 38 | // SELECT a FROM t1 UNION SELECT b FROM t2 INTERSECT SELECT c FROM t3 |
| 39 | func (p *Parser) parseSelectWithSetOperations() (ast.Statement, error) { |
| 40 | // Parse the first SELECT statement |
| 41 | leftStmt, err := p.parseSelectStatement() |
| 42 | if err != nil { |
| 43 | return nil, err |
| 44 | } |
| 45 | |
| 46 | // Check for set operations (UNION, EXCEPT, INTERSECT). MINUS is a |
| 47 | // Snowflake / Oracle synonym for EXCEPT; it tokenizes as a plain |
| 48 | // identifier, so match it by value. |
| 49 | for p.isAnyType(models.TokenTypeUnion, models.TokenTypeExcept, models.TokenTypeIntersect) || |
| 50 | p.isMinusSetOp() { |
| 51 | // Parse the set operation type |
| 52 | operationLiteral := p.currentToken.Token.Value |
| 53 | if p.isMinusSetOp() { |
| 54 | operationLiteral = "EXCEPT" // normalize to canonical name |
| 55 | } |
| 56 | p.advance() |
| 57 | |
| 58 | // Check for ALL keyword |
| 59 | all := false |
| 60 | if p.isType(models.TokenTypeAll) { |
| 61 | all = true |
| 62 | p.advance() |
| 63 | } |
| 64 | |
| 65 | // Parse the right-hand SELECT statement |
| 66 | if !p.isType(models.TokenTypeSelect) { |
| 67 | return nil, p.expectedError("SELECT after set operation") |
| 68 | } |
| 69 | p.advance() // Consume SELECT |
| 70 | |
| 71 | rightStmt, err := p.parseSelectStatement() |
| 72 | if err != nil { |
| 73 | return nil, goerrors.InvalidSetOperationError( |
| 74 | operationLiteral, |
| 75 | fmt.Sprintf("error parsing right SELECT: %v", err), |
| 76 | p.currentLocation(), |
| 77 | "", |
| 78 | ) |
| 79 | } |
| 80 | |
| 81 | // Create the set operation with left as the accumulated result |
| 82 | setOp := &ast.SetOperation{ |
| 83 | Left: leftStmt, |
| 84 | Operator: operationLiteral, |
| 85 | All: all, |
| 86 | Right: rightStmt, |
| 87 | } |
| 88 | |
| 89 | leftStmt = setOp // The result becomes the left side for any subsequent operations |
| 90 | } |
| 91 | |
| 92 | return leftStmt, nil |
| 93 | } |
| 94 | |
| 95 | // isMinusSetOp returns true if the current token is the Snowflake / Oracle |
| 96 | // MINUS keyword used as a set operator (synonym for EXCEPT). MINUS tokenizes |
no test coverage detected