diff --git a/parser/parser_column.go b/parser/parser_column.go index c45fde6..ab04870 100644 --- a/parser/parser_column.go +++ b/parser/parser_column.go @@ -79,7 +79,15 @@ func (p *Parser) getNextPrecedence() int { case p.matchKeyword(KeywordIn): return precedenceIn case p.matchKeyword(KeywordGlobal): - // GLOBAL is only valid before IN, so it binds like IN itself. + // GLOBAL is both the IN operator's prefix and a join locality. Before a + // join it belongs to the enclosing FROM clause — the locality in + // `ON a = b GLOBAL LEFT JOIN c` — so the expression has to end here + // rather than bind it. Everywhere else it binds like IN itself, which + // keeps `GLOBAL` from being mistaken for an alias in `SELECT a GLOBAL`. + if p.peekJoinAfterLocality() { + return PrecedenceUnknown + } + return precedenceIn case p.matchTokenKind(TokenKindQuestionMark): return PrecedenceQuery @@ -159,16 +167,23 @@ func (p *Parser) parseInfix(expr Expr, precedence int) (Expr, error) { return p.parseBetweenClause(expr, false) case p.matchKeyword(KeywordGlobal): _ = p.lexer.consumeToken() + negated := p.tryConsumeKeywords(KeywordNot) if p.expectKeyword(KeywordIn) != nil { return nil, fmt.Errorf("expected IN after GLOBAL, got %s", p.currentTokenKind()) } + + op := TokenKind("GLOBAL IN") + if negated { + op = "GLOBAL NOT IN" + } + rightExpr, err := p.parseSubExpr(p.Pos(), precedence) if err != nil { return nil, err } return &BinaryOperation{ LeftExpr: expr, - Operation: "GLOBAL IN", + Operation: op, RightExpr: rightExpr, }, nil case p.matchTokenKind(TokenKindDot): diff --git a/parser/parser_query.go b/parser/parser_query.go index 17c0294..188c4df 100644 --- a/parser/parser_query.go +++ b/parser/parser_query.go @@ -3,6 +3,7 @@ package parser import ( "errors" "fmt" + "strings" "slices" ) @@ -279,12 +280,60 @@ func (p *Parser) parseJoinTableExpr(_ Pos) (Expr, error) { } } +// parseJoinLocality consumes a GLOBAL or LOCAL join locality together with the +// join type that follows it, returning them as join modifiers. It returns a nil +// slice when the keyword is not followed by a join, leaving the caller to treat +// the clause as something other than a join. +func (p *Parser) parseJoinLocality(pos Pos) ([]string, error) { + locality := p.current().String + _ = p.lexer.consumeToken() + + joinOp := p.parseJoinOp(pos) + if len(joinOp) == 0 && !p.matchKeyword(KeywordJoin) { + return nil, nil + } + + // ARRAY JOIN reads a column list rather than a distributed table, so it has + // no locality. parseJoinOp keeps each modifier's source spelling, so the + // keyword has to be matched case-insensitively. + if slices.ContainsFunc(joinOp, func(modifier string) bool { + return strings.EqualFold(modifier, KeywordArray) + }) { + return nil, fmt.Errorf("%s cannot be combined with ARRAY JOIN", locality) + } + + return append([]string{locality}, joinOp...), nil +} + +// peekJoinAfterLocality reports whether the current GLOBAL/LOCAL keyword is +// followed by a join operator. It leaves the lexer where it found it, so +// expression parsing can use it to tell a join locality apart from the GLOBAL IN +// operator. +func (p *Parser) peekJoinAfterLocality() bool { + savedState := p.lexer.saveState() + defer p.lexer.restoreState(savedState) + + _ = p.lexer.consumeToken() + p.parseJoinOp(p.Pos()) + + return p.matchKeyword(KeywordJoin) +} + func (p *Parser) parseJoinRightExpr(pos Pos) (expr Expr, err error) { var rightExpr Expr var modifiers []string switch { - case p.tryConsumeKeywords(KeywordGlobal): - case p.tryConsumeKeywords(KeywordLocal): + case p.matchOneOfKeywords(KeywordGlobal, KeywordLocal): + // GLOBAL/LOCAL only says how the right-hand table is distributed, so the + // join type still follows it: `GLOBAL LEFT JOIN` is a LEFT join. + modifiers, err = p.parseJoinLocality(p.Pos()) + if err != nil { + return nil, err + } + + if modifiers == nil { + return nil, nil + } case p.tryConsumeTokenKind(TokenKindComma) != nil: return p.parseJoinExpr(p.Pos()) default: diff --git a/parser/parser_test.go b/parser/parser_test.go index 87218dd..ee9de09 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -186,6 +186,27 @@ func TestParser_InvalidSyntax(t *testing.T) { // Invalid ARRAY JOIN types (only ARRAY JOIN, LEFT ARRAY JOIN, and INNER ARRAY JOIN are valid) "SELECT * FROM t RIGHT ARRAY JOIN arr AS a", // RIGHT ARRAY JOIN not supported "SELECT * FROM t FULL ARRAY JOIN arr AS a", // FULL ARRAY JOIN not supported + // ARRAY JOIN reads a column list, so it has no GLOBAL/LOCAL locality. + // Join modifiers keep their source spelling, so the lowercase spellings + // have to be rejected too. + "SELECT * FROM t GLOBAL ARRAY JOIN arr AS a", + "SELECT * FROM t LOCAL LEFT ARRAY JOIN arr AS a", + "SELECT * FROM t GLOBAL array JOIN arr AS a", + "SELECT * FROM t LOCAL left array JOIN arr AS a", + // A join takes at most one locality, and it precedes the join type + "SELECT * FROM t1 GLOBAL LOCAL JOIN t2 ON t1.a = t2.a", + "SELECT * FROM t1 LEFT GLOBAL JOIN t2 ON t1.a = t2.a", + // GLOBAL is a keyword everywhere, never an implicit alias + "SELECT a GLOBAL FROM t", + "SELECT a GLOBAL, b FROM t", + // GLOBAL is an operator only before IN/NOT IN, and LOCAL is never one + "SELECT * FROM t WHERE a GLOBAL LIKE 'x'", + "SELECT * FROM t WHERE a GLOBAL NOT LIKE 'x'", + "SELECT * FROM t WHERE a GLOBAL BETWEEN 1 AND 2", + "SELECT * FROM t WHERE a NOT GLOBAL IN (SELECT 1)", + "SELECT * FROM t WHERE a GLOBAL GLOBAL IN (SELECT 1)", + "SELECT * FROM t WHERE a LOCAL IN (SELECT 1)", + "SELECT * FROM t WHERE a LOCAL NOT IN (SELECT 1)", "00e1d", // invalid number that leaves curToken nil "CREATE--", // trailing comment pushes p.Pos() past end of input (wrapError out-of-range) // Inputs that previously caused a nil-pointer dereference while diff --git a/parser/position_test.go b/parser/position_test.go index dab4d8b..a8de849 100644 --- a/parser/position_test.go +++ b/parser/position_test.go @@ -90,3 +90,14 @@ func TestDictionaryAttributeEnd(t *testing.T) { require.Equal(t, "IS_OBJECT_ID", sql[36:48]) require.Equal(t, Pos(48), attrs[0].End()) } + +func TestJoinLocalityIsInsideTheJoinSpan(t *testing.T) { + sql := "SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a" + stmt := parseOneStmt(t, sql).(*SelectQuery) + join := stmt.From.Expr.(*JoinExpr).Right.(*JoinExpr) + // the join starts at GLOBAL rather than at the join type, so a rewriter that + // reads the node back from the source keeps the locality + require.Equal(t, "GLOBAL", sql[17:23]) + require.Equal(t, Pos(17), join.Pos()) + require.Equal(t, Pos(len(sql)), join.End()) +} diff --git a/parser/precedence_test.go b/parser/precedence_test.go index 69e6872..87bea02 100644 --- a/parser/precedence_test.go +++ b/parser/precedence_test.go @@ -143,3 +143,75 @@ func TestIntersect(t *testing.T) { require.NotNil(t, selectQuery.Intersect) require.Equal(t, "SELECT 1 INTERSECT SELECT 2", Format(stmts[0])) } + +func TestGlobalJoinLocalityPrefixesTheJoinType(t *testing.T) { + // GLOBAL/LOCAL only says how the right-hand table is distributed, so the + // join type still follows it and both have to survive into the modifiers. + for _, tc := range []struct { + sql string + modifiers []string + }{ + {"SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a", []string{"GLOBAL", "JOIN"}}, + {"SELECT * FROM t1 GLOBAL INNER JOIN t2 ON t1.a = t2.a", []string{"GLOBAL", "INNER", "JOIN"}}, + {"SELECT * FROM t1 GLOBAL LEFT OUTER JOIN t2 ON t1.a = t2.a", []string{"GLOBAL", "LEFT", "OUTER", "JOIN"}}, + {"SELECT * FROM t1 GLOBAL ANY LEFT JOIN t2 ON t1.a = t2.a", []string{"GLOBAL", "ANY", "LEFT", "JOIN"}}, + {"SELECT * FROM t1 GLOBAL CROSS JOIN t2", []string{"GLOBAL", "CROSS", "JOIN"}}, + {"SELECT * FROM t1 AS x LOCAL FULL JOIN t2 USING a", []string{"LOCAL", "FULL", "JOIN"}}, + } { + from := parseOneStmt(t, tc.sql).(*SelectQuery).From.Expr + join, ok := from.(*JoinExpr) + require.True(t, ok, "%s: expected a *JoinExpr in FROM, got %T", tc.sql, from) + + right, ok := join.Right.(*JoinExpr) + require.True(t, ok, "%s: expected the join to carry a right side, got %T", tc.sql, join.Right) + require.Equal(t, tc.modifiers, right.Modifiers, tc.sql) + require.Equal(t, tc.sql, Format(parseOneStmt(t, tc.sql)), tc.sql) + } +} + +func TestGlobalAfterJoinConstraintStartsANewJoin(t *testing.T) { + // A GLOBAL that follows an ON/USING clause belongs to the next join, so it + // must not be read as the GLOBAL IN operator. + sql := "SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a GLOBAL LEFT JOIN t3 ON t1.a = t3.a" + stmt := parseOneStmt(t, sql) + join := stmt.(*SelectQuery).From.Expr.(*JoinExpr) + + first, ok := join.Right.(*JoinExpr) + require.True(t, ok) + require.Equal(t, []string{"GLOBAL", "JOIN"}, first.Modifiers) + + second, ok := first.Right.(*JoinExpr) + require.True(t, ok, "the second GLOBAL should start another join, got %T", first.Right) + require.Equal(t, []string{"GLOBAL", "LEFT", "JOIN"}, second.Modifiers) + require.Equal(t, sql, Format(stmt)) +} + +func TestGlobalInAndGlobalNotIn(t *testing.T) { + for _, tc := range []struct { + sql string + op TokenKind + }{ + {"SELECT a GLOBAL IN (SELECT 1)", "GLOBAL IN"}, + {"SELECT a GLOBAL NOT IN (SELECT 1)", "GLOBAL NOT IN"}, + } { + expr := parseSelectItemExpr(t, tc.sql) + in, ok := expr.(*BinaryOperation) + require.True(t, ok, "%s: expected BinaryOperation at the top, got %T", tc.sql, expr) + require.Equal(t, tc.op, in.Operation, tc.sql) + } +} + +func TestGlobalInGroupsLikeIn(t *testing.T) { + // GLOBAL IN binds with IN's precedence: `a = b GLOBAL IN (1)` groups as + // `a = (b GLOBAL IN (1))`. + for _, sql := range []string{"SELECT a = b GLOBAL IN (1)", "SELECT a = b GLOBAL NOT IN (1)"} { + expr := parseSelectItemExpr(t, sql) + eq, ok := expr.(*BinaryOperation) + require.True(t, ok, "%s: expected `=` at the top, got %T", sql, expr) + require.Equal(t, TokenKind("="), eq.Operation, sql) + + in, ok := eq.RightExpr.(*BinaryOperation) + require.True(t, ok, "%s: right side of `=` should be the GLOBAL IN operation, got %T", sql, eq.RightExpr) + require.Contains(t, string(in.Operation), "GLOBAL", sql) + } +} diff --git a/parser/testdata/query/format/beautify/select_with_global_join_locality.sql b/parser/testdata/query/format/beautify/select_with_global_join_locality.sql new file mode 100644 index 0000000..e84619f --- /dev/null +++ b/parser/testdata/query/format/beautify/select_with_global_join_locality.sql @@ -0,0 +1,112 @@ +-- Origin SQL: +SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL INNER JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT OUTER JOIN t2 USING (a); + +SELECT * FROM t1 GLOBAL ANY LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL CROSS JOIN t2; + +SELECT * FROM t1 AS x LOCAL FULL JOIN t2 ON x.a = t2.a; + +SELECT * FROM t1 AS x LOCAL RIGHT JOIN t2 USING a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a GLOBAL LEFT JOIN t3 ON t1.a = t3.a; + +SELECT * FROM t WHERE a GLOBAL IN (SELECT b FROM t2); + +SELECT * FROM t WHERE a GLOBAL NOT IN (SELECT b FROM t2); + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a GLOBAL NOT IN (SELECT b FROM t3); + + +-- Beautify SQL: +SELECT + * +FROM + t1 + GLOBAL JOIN + t2 ON t1.a = t2.a; +SELECT + * +FROM + t1 + GLOBAL INNER JOIN + t2 ON t1.a = t2.a; +SELECT + * +FROM + t1 + GLOBAL LEFT JOIN + t2 ON t1.a = t2.a; +SELECT + * +FROM + t1 + GLOBAL LEFT OUTER JOIN + t2 USING a; +SELECT + * +FROM + t1 + GLOBAL ANY LEFT JOIN + t2 ON t1.a = t2.a; +SELECT + * +FROM + t1 + GLOBAL CROSS JOIN + t2; +SELECT + * +FROM + t1 AS x + LOCAL FULL JOIN + t2 ON x.a = t2.a; +SELECT + * +FROM + t1 AS x + LOCAL RIGHT JOIN + t2 USING a; +SELECT + * +FROM + t1 + GLOBAL LEFT JOIN + t2 ON t1.a = t2.a + GLOBAL LEFT JOIN + t3 ON t1.a = t3.a; +SELECT + * +FROM + t +WHERE + a GLOBAL IN (SELECT + b + FROM + t2); +SELECT + * +FROM + t +WHERE + a GLOBAL NOT IN (SELECT + b + FROM + t2); +SELECT + * +FROM + t1 + GLOBAL LEFT JOIN + t2 ON t1.a = t2.a +WHERE + t1.a GLOBAL NOT IN (SELECT + b + FROM + t3); diff --git a/parser/testdata/query/format/select_with_global_join_locality.sql b/parser/testdata/query/format/select_with_global_join_locality.sql new file mode 100644 index 0000000..7a24d00 --- /dev/null +++ b/parser/testdata/query/format/select_with_global_join_locality.sql @@ -0,0 +1,39 @@ +-- Origin SQL: +SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL INNER JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT OUTER JOIN t2 USING (a); + +SELECT * FROM t1 GLOBAL ANY LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL CROSS JOIN t2; + +SELECT * FROM t1 AS x LOCAL FULL JOIN t2 ON x.a = t2.a; + +SELECT * FROM t1 AS x LOCAL RIGHT JOIN t2 USING a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a GLOBAL LEFT JOIN t3 ON t1.a = t3.a; + +SELECT * FROM t WHERE a GLOBAL IN (SELECT b FROM t2); + +SELECT * FROM t WHERE a GLOBAL NOT IN (SELECT b FROM t2); + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a GLOBAL NOT IN (SELECT b FROM t3); + + +-- Format SQL: +SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a; +SELECT * FROM t1 GLOBAL INNER JOIN t2 ON t1.a = t2.a; +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a; +SELECT * FROM t1 GLOBAL LEFT OUTER JOIN t2 USING a; +SELECT * FROM t1 GLOBAL ANY LEFT JOIN t2 ON t1.a = t2.a; +SELECT * FROM t1 GLOBAL CROSS JOIN t2; +SELECT * FROM t1 AS x LOCAL FULL JOIN t2 ON x.a = t2.a; +SELECT * FROM t1 AS x LOCAL RIGHT JOIN t2 USING a; +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a GLOBAL LEFT JOIN t3 ON t1.a = t3.a; +SELECT * FROM t WHERE a GLOBAL IN (SELECT b FROM t2); +SELECT * FROM t WHERE a GLOBAL NOT IN (SELECT b FROM t2); +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a GLOBAL NOT IN (SELECT b FROM t3); diff --git a/parser/testdata/query/output/select_with_global_join_locality.sql.golden.json b/parser/testdata/query/output/select_with_global_join_locality.sql.golden.json new file mode 100644 index 0000000..bb038b1 --- /dev/null +++ b/parser/testdata/query/output/select_with_global_join_locality.sql.golden.json @@ -0,0 +1,1756 @@ +[ + { + "SelectPos": 0, + "StatementEnd": 46, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 7, + "NameEnd": 7 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 9, + "Expr": { + "JoinPos": 14, + "Left": { + "Table": { + "TablePos": 14, + "TableEnd": 16, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 14, + "NameEnd": 16 + } + }, + "HasFinal": false + }, + "StatementEnd": 16, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 17, + "Left": { + "Table": { + "TablePos": 29, + "TableEnd": 31, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 29, + "NameEnd": 31 + } + }, + "HasFinal": false + }, + "StatementEnd": 31, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "JOIN" + ], + "Constraints": { + "OnPos": 32, + "On": { + "ListPos": 35, + "ListEnd": 46, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 35, + "NameEnd": 37 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 38, + "NameEnd": 39 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 42, + "NameEnd": 44 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 45, + "NameEnd": 46 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 49, + "StatementEnd": 101, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 56, + "NameEnd": 56 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 58, + "Expr": { + "JoinPos": 63, + "Left": { + "Table": { + "TablePos": 63, + "TableEnd": 65, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 63, + "NameEnd": 65 + } + }, + "HasFinal": false + }, + "StatementEnd": 65, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 66, + "Left": { + "Table": { + "TablePos": 84, + "TableEnd": 86, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 84, + "NameEnd": 86 + } + }, + "HasFinal": false + }, + "StatementEnd": 86, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "INNER", + "JOIN" + ], + "Constraints": { + "OnPos": 87, + "On": { + "ListPos": 90, + "ListEnd": 101, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 90, + "NameEnd": 92 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 93, + "NameEnd": 94 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 97, + "NameEnd": 99 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 100, + "NameEnd": 101 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 104, + "StatementEnd": 155, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 111, + "NameEnd": 111 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 113, + "Expr": { + "JoinPos": 118, + "Left": { + "Table": { + "TablePos": 118, + "TableEnd": 120, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 118, + "NameEnd": 120 + } + }, + "HasFinal": false + }, + "StatementEnd": 120, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 121, + "Left": { + "Table": { + "TablePos": 138, + "TableEnd": 140, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 138, + "NameEnd": 140 + } + }, + "HasFinal": false + }, + "StatementEnd": 140, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "LEFT", + "JOIN" + ], + "Constraints": { + "OnPos": 141, + "On": { + "ListPos": 144, + "ListEnd": 155, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 144, + "NameEnd": 146 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 147, + "NameEnd": 148 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 151, + "NameEnd": 153 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 154, + "NameEnd": 155 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 158, + "StatementEnd": 209, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 165, + "NameEnd": 165 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 167, + "Expr": { + "JoinPos": 172, + "Left": { + "Table": { + "TablePos": 172, + "TableEnd": 174, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 172, + "NameEnd": 174 + } + }, + "HasFinal": false + }, + "StatementEnd": 174, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 175, + "Left": { + "Table": { + "TablePos": 198, + "TableEnd": 200, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 198, + "NameEnd": 200 + } + }, + "HasFinal": false + }, + "StatementEnd": 200, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "LEFT", + "OUTER", + "JOIN" + ], + "Constraints": { + "UsingPos": 201, + "Using": { + "ListPos": 208, + "ListEnd": 209, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "Name": "a", + "QuoteType": 1, + "NamePos": 208, + "NameEnd": 209 + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 213, + "StatementEnd": 268, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 220, + "NameEnd": 220 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 222, + "Expr": { + "JoinPos": 227, + "Left": { + "Table": { + "TablePos": 227, + "TableEnd": 229, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 227, + "NameEnd": 229 + } + }, + "HasFinal": false + }, + "StatementEnd": 229, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 230, + "Left": { + "Table": { + "TablePos": 251, + "TableEnd": 253, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 251, + "NameEnd": 253 + } + }, + "HasFinal": false + }, + "StatementEnd": 253, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "ANY", + "LEFT", + "JOIN" + ], + "Constraints": { + "OnPos": 254, + "On": { + "ListPos": 257, + "ListEnd": 268, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 257, + "NameEnd": 259 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 260, + "NameEnd": 261 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 264, + "NameEnd": 266 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 267, + "NameEnd": 268 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 271, + "StatementEnd": 308, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 278, + "NameEnd": 278 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 280, + "Expr": { + "JoinPos": 285, + "Left": { + "Table": { + "TablePos": 285, + "TableEnd": 287, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 285, + "NameEnd": 287 + } + }, + "HasFinal": false + }, + "StatementEnd": 287, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 288, + "Left": { + "Table": { + "TablePos": 306, + "TableEnd": 308, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 306, + "NameEnd": 308 + } + }, + "HasFinal": false + }, + "StatementEnd": 308, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "CROSS", + "JOIN" + ], + "Constraints": null + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 311, + "StatementEnd": 365, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 318, + "NameEnd": 318 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 320, + "Expr": { + "JoinPos": 325, + "Left": { + "Table": { + "TablePos": 325, + "TableEnd": 332, + "Alias": null, + "Expr": { + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 325, + "NameEnd": 327 + } + }, + "AliasPos": 331, + "Alias": { + "Name": "x", + "QuoteType": 1, + "NamePos": 331, + "NameEnd": 332 + } + }, + "HasFinal": false + }, + "StatementEnd": 332, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 333, + "Left": { + "Table": { + "TablePos": 349, + "TableEnd": 351, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 349, + "NameEnd": 351 + } + }, + "HasFinal": false + }, + "StatementEnd": 351, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "LOCAL", + "FULL", + "JOIN" + ], + "Constraints": { + "OnPos": 352, + "On": { + "ListPos": 355, + "ListEnd": 365, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "x", + "QuoteType": 1, + "NamePos": 355, + "NameEnd": 356 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 357, + "NameEnd": 358 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 361, + "NameEnd": 363 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 364, + "NameEnd": 365 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 368, + "StatementEnd": 417, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 375, + "NameEnd": 375 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 377, + "Expr": { + "JoinPos": 382, + "Left": { + "Table": { + "TablePos": 382, + "TableEnd": 389, + "Alias": null, + "Expr": { + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 382, + "NameEnd": 384 + } + }, + "AliasPos": 388, + "Alias": { + "Name": "x", + "QuoteType": 1, + "NamePos": 388, + "NameEnd": 389 + } + }, + "HasFinal": false + }, + "StatementEnd": 389, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 390, + "Left": { + "Table": { + "TablePos": 407, + "TableEnd": 409, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 407, + "NameEnd": 409 + } + }, + "HasFinal": false + }, + "StatementEnd": 409, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "LOCAL", + "RIGHT", + "JOIN" + ], + "Constraints": { + "UsingPos": 410, + "Using": { + "ListPos": 416, + "ListEnd": 417, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "Name": "a", + "QuoteType": 1, + "NamePos": 416, + "NameEnd": 417 + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 420, + "StatementEnd": 506, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 427, + "NameEnd": 427 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 429, + "Expr": { + "JoinPos": 434, + "Left": { + "Table": { + "TablePos": 434, + "TableEnd": 436, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 434, + "NameEnd": 436 + } + }, + "HasFinal": false + }, + "StatementEnd": 436, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 437, + "Left": { + "Table": { + "TablePos": 454, + "TableEnd": 456, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 454, + "NameEnd": 456 + } + }, + "HasFinal": false + }, + "StatementEnd": 456, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 472, + "Left": { + "Table": { + "TablePos": 489, + "TableEnd": 491, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t3", + "QuoteType": 1, + "NamePos": 489, + "NameEnd": 491 + } + }, + "HasFinal": false + }, + "StatementEnd": 491, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "LEFT", + "JOIN" + ], + "Constraints": { + "OnPos": 492, + "On": { + "ListPos": 495, + "ListEnd": 506, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 495, + "NameEnd": 497 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 498, + "NameEnd": 499 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t3", + "QuoteType": 1, + "NamePos": 502, + "NameEnd": 504 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 505, + "NameEnd": 506 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": [ + "GLOBAL", + "LEFT", + "JOIN" + ], + "Constraints": { + "OnPos": 457, + "On": { + "ListPos": 460, + "ListEnd": 471, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 460, + "NameEnd": 462 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 463, + "NameEnd": 464 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 467, + "NameEnd": 469 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 470, + "NameEnd": 471 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 509, + "StatementEnd": 560, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 516, + "NameEnd": 516 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 518, + "Expr": { + "Table": { + "TablePos": 523, + "TableEnd": 524, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t", + "QuoteType": 1, + "NamePos": 523, + "NameEnd": 524 + } + }, + "HasFinal": false + }, + "StatementEnd": 524, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": { + "WherePos": 525, + "Expr": { + "LeftExpr": { + "Name": "a", + "QuoteType": 1, + "NamePos": 531, + "NameEnd": 532 + }, + "Operation": "GLOBAL IN", + "RightExpr": { + "HasParen": true, + "Select": { + "SelectPos": 544, + "StatementEnd": 560, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "b", + "QuoteType": 1, + "NamePos": 551, + "NameEnd": 552 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 553, + "Expr": { + "Table": { + "TablePos": 558, + "TableEnd": 560, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 558, + "NameEnd": 560 + } + }, + "HasFinal": false + }, + "StatementEnd": 560, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + } + }, + "HasGlobal": false, + "HasNot": false + } + }, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 564, + "StatementEnd": 619, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 571, + "NameEnd": 571 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 573, + "Expr": { + "Table": { + "TablePos": 578, + "TableEnd": 579, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t", + "QuoteType": 1, + "NamePos": 578, + "NameEnd": 579 + } + }, + "HasFinal": false + }, + "StatementEnd": 579, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": { + "WherePos": 580, + "Expr": { + "LeftExpr": { + "Name": "a", + "QuoteType": 1, + "NamePos": 586, + "NameEnd": 587 + }, + "Operation": "GLOBAL NOT IN", + "RightExpr": { + "HasParen": true, + "Select": { + "SelectPos": 603, + "StatementEnd": 619, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "b", + "QuoteType": 1, + "NamePos": 610, + "NameEnd": 611 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 612, + "Expr": { + "Table": { + "TablePos": 617, + "TableEnd": 619, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 617, + "NameEnd": 619 + } + }, + "HasFinal": false + }, + "StatementEnd": 619, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + } + }, + "HasGlobal": false, + "HasNot": false + } + }, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 623, + "StatementEnd": 717, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 630, + "NameEnd": 630 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 632, + "Expr": { + "JoinPos": 637, + "Left": { + "Table": { + "TablePos": 637, + "TableEnd": 639, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t1", + "QuoteType": 1, + "NamePos": 637, + "NameEnd": 639 + } + }, + "HasFinal": false + }, + "StatementEnd": 639, + "SampleRatio": null, + "HasFinal": false + }, + "Right": { + "JoinPos": 640, + "Left": { + "Table": { + "TablePos": 657, + "TableEnd": 659, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t2", + "QuoteType": 1, + "NamePos": 657, + "NameEnd": 659 + } + }, + "HasFinal": false + }, + "StatementEnd": 659, + "SampleRatio": null, + "HasFinal": false + }, + "Right": null, + "Modifiers": [ + "GLOBAL", + "LEFT", + "JOIN" + ], + "Constraints": { + "OnPos": 660, + "On": { + "ListPos": 663, + "ListEnd": 674, + "HasDistinct": false, + "Items": [ + { + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 663, + "NameEnd": 665 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 666, + "NameEnd": 667 + } + ] + }, + "Operation": "=", + "RightExpr": { + "Fields": [ + { + "Name": "t2", + "QuoteType": 1, + "NamePos": 670, + "NameEnd": 672 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 673, + "NameEnd": 674 + } + ] + }, + "HasGlobal": false, + "HasNot": false + }, + "Alias": null + } + ] + } + } + }, + "Modifiers": null, + "Constraints": null + } + }, + "Window": null, + "Prewhere": null, + "Where": { + "WherePos": 675, + "Expr": { + "LeftExpr": { + "Fields": [ + { + "Name": "t1", + "QuoteType": 1, + "NamePos": 681, + "NameEnd": 683 + }, + { + "Name": "a", + "QuoteType": 1, + "NamePos": 684, + "NameEnd": 685 + } + ] + }, + "Operation": "GLOBAL NOT IN", + "RightExpr": { + "HasParen": true, + "Select": { + "SelectPos": 701, + "StatementEnd": 717, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "b", + "QuoteType": 1, + "NamePos": 708, + "NameEnd": 709 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 710, + "Expr": { + "Table": { + "TablePos": 715, + "TableEnd": 717, + "Alias": null, + "Expr": { + "Database": null, + "Table": { + "Name": "t3", + "QuoteType": 1, + "NamePos": 715, + "NameEnd": 717 + } + }, + "HasFinal": false + }, + "StatementEnd": 717, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + } + }, + "HasGlobal": false, + "HasNot": false + } + }, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + } +] \ No newline at end of file diff --git a/parser/testdata/query/select_with_global_join_locality.sql b/parser/testdata/query/select_with_global_join_locality.sql new file mode 100644 index 0000000..d99bd6a --- /dev/null +++ b/parser/testdata/query/select_with_global_join_locality.sql @@ -0,0 +1,23 @@ +SELECT * FROM t1 GLOBAL JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL INNER JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL LEFT OUTER JOIN t2 USING (a); + +SELECT * FROM t1 GLOBAL ANY LEFT JOIN t2 ON t1.a = t2.a; + +SELECT * FROM t1 GLOBAL CROSS JOIN t2; + +SELECT * FROM t1 AS x LOCAL FULL JOIN t2 ON x.a = t2.a; + +SELECT * FROM t1 AS x LOCAL RIGHT JOIN t2 USING a; + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a GLOBAL LEFT JOIN t3 ON t1.a = t3.a; + +SELECT * FROM t WHERE a GLOBAL IN (SELECT b FROM t2); + +SELECT * FROM t WHERE a GLOBAL NOT IN (SELECT b FROM t2); + +SELECT * FROM t1 GLOBAL LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a GLOBAL NOT IN (SELECT b FROM t3);