parseJoinType parses the optional NATURAL keyword and the join-type keywords (LEFT, RIGHT, FULL, INNER, CROSS, OUTER APPLY, …) that precede the JOIN keyword. Returns (joinType string, isNatural bool, err).
()
| 173 | // (LEFT, RIGHT, FULL, INNER, CROSS, OUTER APPLY, …) that precede the JOIN keyword. |
| 174 | // Returns (joinType string, isNatural bool, err). |
| 175 | func (p *Parser) parseJoinType() (string, bool, error) { |
| 176 | joinType := "INNER" |
| 177 | isNatural := false |
| 178 | explicitType := false // tracks whether a join-type keyword was explicitly given |
| 179 | |
| 180 | // ClickHouse GLOBAL JOIN — consume the GLOBAL modifier before the join type. |
| 181 | // GLOBAL semantics (distributed join) are not preserved in the AST. |
| 182 | if p.dialect == string(keywords.DialectClickHouse) && p.isTokenMatch("GLOBAL") { |
| 183 | p.advance() // consume GLOBAL; fall through to standard join parsing |
| 184 | } |
| 185 | |
| 186 | // ClickHouse ANY/ALL join strictness prefix — e.g. ANY LEFT JOIN, ALL INNER JOIN. |
| 187 | // The strictness modifier is consumed but not modeled on the AST. |
| 188 | if p.dialect == string(keywords.DialectClickHouse) && |
| 189 | (p.isType(models.TokenTypeAny) || p.isType(models.TokenTypeAll)) { |
| 190 | p.advance() |
| 191 | } |
| 192 | |
| 193 | if p.isType(models.TokenTypeNatural) { |
| 194 | isNatural = true |
| 195 | p.advance() |
| 196 | } |
| 197 | |
| 198 | switch { |
| 199 | case p.isType(models.TokenTypeLeft): |
| 200 | joinType = "LEFT" |
| 201 | explicitType = true |
| 202 | p.advance() |
| 203 | if p.isType(models.TokenTypeOuter) { |
| 204 | p.advance() |
| 205 | } |
| 206 | case p.isType(models.TokenTypeRight): |
| 207 | joinType = "RIGHT" |
| 208 | explicitType = true |
| 209 | p.advance() |
| 210 | if p.isType(models.TokenTypeOuter) { |
| 211 | p.advance() |
| 212 | } |
| 213 | case p.isType(models.TokenTypeFull): |
| 214 | joinType = "FULL" |
| 215 | explicitType = true |
| 216 | p.advance() |
| 217 | if p.isType(models.TokenTypeOuter) { |
| 218 | p.advance() |
| 219 | } |
| 220 | case p.isType(models.TokenTypeInner): |
| 221 | joinType = "INNER" |
| 222 | explicitType = true |
| 223 | p.advance() |
| 224 | case p.isType(models.TokenTypeCross): |
| 225 | joinType = "CROSS" |
| 226 | explicitType = true |
| 227 | p.advance() |
| 228 | if p.dialect == string(keywords.DialectSQLServer) && |
| 229 | p.currentToken.Token.Type == models.TokenTypeIdentifier && |
| 230 | strings.ToUpper(p.currentToken.Token.Value) == "APPLY" { |
| 231 | joinType = "CROSS APPLY" |
| 232 | p.advance() |
no test coverage detected