This is an automated email from the ASF dual-hosted git repository. tanner pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/calcite.git
commit f1d03166a7fce96535b0d20973260a3516c3e3f6 Author: macroguo <[email protected]> AuthorDate: Mon Jul 3 19:45:39 2023 +0800 [CALCITE-5816] Only return left-hand table columns when validate LEFT SEMI JOIN query --- .../src/test/java/org/apache/calcite/test/BabelTest.java | 16 ++++++++++++++++ babel/src/test/resources/sql/select.iq | 15 ++++++++++++++- .../org/apache/calcite/sql/validate/JoinNamespace.java | 3 +++ .../java/org/apache/calcite/sql/validate/JoinScope.java | 11 +++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/babel/src/test/java/org/apache/calcite/test/BabelTest.java b/babel/src/test/java/org/apache/calcite/test/BabelTest.java index 088d3e3a01..da463df30c 100644 --- a/babel/src/test/java/org/apache/calcite/test/BabelTest.java +++ b/babel/src/test/java/org/apache/calcite/test/BabelTest.java @@ -24,6 +24,7 @@ import org.apache.calcite.sql.fun.SqlLibrary; import org.apache.calcite.sql.fun.SqlLibraryOperatorTableFactory; import org.apache.calcite.sql.parser.SqlParserFixture; import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl; +import org.apache.calcite.sql.validate.SqlConformanceEnum; import org.junit.jupiter.api.Test; @@ -231,6 +232,21 @@ class BabelTest { "EXPR$0=false\n"); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-5816">[CALCITE-5816] + * Query with LEFT SEMI JOIN should not refer to RHS columns</a>. */ + @Test public void testLeftSemiJoin() { + final SqlValidatorFixture v = Fixtures.forValidator() + .withParserConfig(c -> c.withParserFactory(SqlBabelParserImpl.FACTORY)) + .withConformance(SqlConformanceEnum.BABEL); + + v.withSql("SELECT * FROM dept LEFT SEMI JOIN emp ON emp.deptno = dept.deptno") + .type("RecordType(INTEGER NOT NULL DEPTNO, VARCHAR(10) NOT NULL NAME) NOT NULL"); + + v.withSql("SELECT deptno FROM dept LEFT SEMI JOIN emp ON emp.deptno = dept.deptno") + .type("RecordType(INTEGER NOT NULL DEPTNO) NOT NULL"); + } + private void checkSqlResult(String funLibrary, String query, String result) { CalciteAssert.that() .with(CalciteConnectionProperty.PARSER_FACTORY, diff --git a/babel/src/test/resources/sql/select.iq b/babel/src/test/resources/sql/select.iq index e82f21d7d4..0d665540c8 100755 --- a/babel/src/test/resources/sql/select.iq +++ b/babel/src/test/resources/sql/select.iq @@ -30,14 +30,27 @@ ORDER BY "EMP"."DEPTNO" !explain-validated-on all # LEFT SEMI JOIN (Hive only) +# Only LHS columns are referenced in SELECT clause SELECT * FROM emp LEFT SEMI JOIN dept ON emp.deptno = dept.deptno; -SELECT "EMP"."EMPNO", "EMP"."ENAME", "EMP"."JOB", "EMP"."MGR", "EMP"."HIREDATE", "EMP"."SAL", "EMP"."COMM", "EMP"."DEPTNO", "DEPT"."DEPTNO" AS "DEPTNO0", "DEPT"."DNAME", "DEPT"."LOC" +SELECT "EMP"."EMPNO", "EMP"."ENAME", "EMP"."JOB", "EMP"."MGR", "EMP"."HIREDATE", "EMP"."SAL", "EMP"."COMM", "EMP"."DEPTNO" FROM "scott"."EMP" AS "EMP" LEFT SEMI JOIN "scott"."DEPT" AS "DEPT" ON "EMP"."DEPTNO" = "DEPT"."DEPTNO" !explain-validated-on hive +# Can not reference RHS columns in SELECT clause +SELECT dept.dname +FROM emp LEFT SEMI JOIN dept ON emp.deptno = dept.deptno; +Table 'DEPT' not found +!error + +# Can not reference RHS columns in WHERE clause +SELECT * +FROM emp LEFT SEMI JOIN dept ON emp.deptno = dept.deptno where dept.deptno = 100; +Table 'DEPT' not found +!error + # Test CONNECT BY (Oracle only) !if (false) { SELECT * diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java index 43564fca16..a869f1167c 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java @@ -57,6 +57,9 @@ class JoinNamespace extends AbstractNamespace { leftType = typeFactory.createTypeWithNullability(leftType, true); rightType = typeFactory.createTypeWithNullability(rightType, true); break; + // LEFT SEMI JOIN can only come from Babel. + case LEFT_SEMI_JOIN: + return typeFactory.createJoinType(leftType); default: break; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java index bbf3da9d99..deabdbfcbb 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinScope.java @@ -22,6 +22,9 @@ import org.apache.calcite.sql.SqlWindow; import org.checkerframework.checker.nullness.qual.Nullable; +import static org.apache.calcite.sql.JoinType.LEFT_SEMI_JOIN; +import static org.apache.calcite.sql.SqlUtil.stripAs; + import static java.util.Objects.requireNonNull; /** @@ -66,6 +69,14 @@ public class JoinScope extends ListScope { @Override public void addChild(SqlValidatorNamespace ns, String alias, boolean nullable) { super.addChild(ns, alias, nullable); + + // LEFT SEMI JOIN can only come from Babel. + if (join.getJoinType() == LEFT_SEMI_JOIN + && stripAs(join.getRight()) == ns.getNode()) { + // Ignore the right hand side. + return; + } + if ((usingScope != null) && (usingScope != parent)) { // We're looking at a join within a join. Recursively add this // child to its parent scope too. Example:
