diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java old mode 100644 new mode 100755 index 10c82a906c4930..501871f01a603b --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/Analyzer.java @@ -656,6 +656,10 @@ private TupleDescriptor resolveColumnRef(TableName tblName, String colName) thro for (TupleDescriptor desc : tupleByAlias.get(tblName.toString())) { //result = desc; if (!isVisible(desc.getId())) { + if (colName.equalsIgnoreCase(Column.DELETE_SIGN)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_UNIUQUE_KEY_USE_SEMI_JOIN, + Joiner.on(".").join(tblName.getTbl(),colName)); + } ErrorReport.reportAnalysisException(ErrorCode.ERR_ILLEGAL_COLUMN_REFERENCE_ERROR, Joiner.on(".").join(tblName.getTbl(),colName)); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java old mode 100644 new mode 100755 index 186899ccf4a0fa..514055ebbba5b8 --- a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java @@ -38,6 +38,10 @@ public enum ErrorCode { ERR_ILLEGAL_COLUMN_REFERENCE_ERROR(1053, new byte[] {'2', '3', '0', '0', '1'}, "Illegal column/field reference '%s' of semi-/anti-join"), ERR_BAD_FIELD_ERROR(1054, new byte[] {'4', '2', 'S', '2', '2'}, "Unknown column '%s' in '%s'"), + ERR_UNIUQUE_KEY_USE_SEMI_JOIN(1055, new byte[] {'2', '3', '0', '0', '1'}, + "%s is a hidden column to mark whether a row deleted when unique key existed, " + + "'__DORIS_DELETE_SIGN__ = 0' will appear in where clause, that will cause semi join syntax error, " + + "please use '[not] in/existed' as replacement."), ERR_WRONG_VALUE_COUNT(1058, new byte[] {'2', '1', 'S', '0', '1'}, "Column count doesn't match value count"), ERR_DUP_FIELDNAME(1060, new byte[] {'4', '2', 'S', '2', '1'}, "Duplicate column name '%s'"), ERR_NONUNIQ_TABLE(1066, new byte[] {'4', '2', '0', '0', '0'}, "Not unique table/alias: '%s'"), diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/UniqueKeySemiJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/UniqueKeySemiJoinTest.java new file mode 100755 index 00000000000000..251eb26d256942 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/UniqueKeySemiJoinTest.java @@ -0,0 +1,117 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.utframe.DorisAssert; +import org.apache.doris.utframe.UtFrameUtils; +import org.junit.*; +import org.junit.rules.ExpectedException; + +import java.util.UUID; + +public class UniqueKeySemiJoinTest { + private static String runningDir = "fe/mocked/DemoTest/" + UUID.randomUUID().toString() + "/"; + private static DorisAssert dorisAssert; + + @Rule + public ExpectedException expectedEx = ExpectedException.none(); + + @AfterClass + public static void tearDown() throws Exception { + UtFrameUtils.cleanDorisFeDir(runningDir); + } + + @BeforeClass + public static void setUp() throws Exception { + UtFrameUtils.createMinDorisCluster(runningDir); + String createTbl1Str = "CREATE TABLE `t0` (\n" + + " `c0` tinyint NOT NULL,\n" + + " `c1` tinyint NOT NULL\n" + + ") ENGINE=OLAP\n" + + "UNIQUE KEY(`c0`)\n" + + "COMMENT \"OLAP\"\n" + + "DISTRIBUTED BY HASH(`c0`) BUCKETS 10\n" + + "PROPERTIES (\n" + + "\"replication_num\" = \"1\",\n" + + "\"in_memory\" = \"false\",\n" + + "\"storage_format\" = \"DEFAULT\"\n" + + ");"; + String createTbl2Str = "CREATE TABLE `t1` (\n" + + " `c0` tinyint NOT NULL,\n" + + " `c1` tinyint NOT NULL\n" + + ") ENGINE=OLAP\n" + + "UNIQUE KEY(`c0`)\n" + + "COMMENT \"OLAP\"\n" + + "DISTRIBUTED BY HASH(`c0`) BUCKETS 10\n" + + "PROPERTIES (\n" + + "\"replication_num\" = \"1\",\n" + + "\"in_memory\" = \"false\",\n" + + "\"storage_format\" = \"DEFAULT\"\n" + + ");"; + dorisAssert = new DorisAssert(); + dorisAssert.withDatabase("db1").useDatabase("db1"); + dorisAssert.withTable(createTbl1Str) + .withTable(createTbl2Str); + } + + @Test + public void testSemiJoin() throws Exception { + String sql = " SELECT * FROM t1 LEFT SEMI JOIN t0 ON t1.c0 = t0.c0"; + try { + dorisAssert.query(sql).explainQuery(); + Assert.fail("t0.__DORIS_DELETE_SIGN__ is a hidden column to mark whether a row deleted when unique key existed, '__DORIS_DELETE_SIGN__ = 0' will appear in where clause, that will cause semi join syntax error, please use not in/existed as replacement."); + } catch (AnalysisException e) { + System.out.println(e.getMessage()); + } + + sql = " SELECT * FROM t1 RIGHT SEMI JOIN t0 ON t1.c0 = t0.c0"; + try { + dorisAssert.query(sql).explainQuery(); + Assert.fail("t0.__DORIS_DELETE_SIGN__ is a hidden column to mark whether a row deleted when unique key existed, '__DORIS_DELETE_SIGN__ = 0' will appear in where clause, that will cause semi join syntax error, please use not in/existed as replacement."); + } catch (AnalysisException e) { + System.out.println(e.getMessage()); + } + + sql = " SELECT * FROM t1 LEFT ANTI JOIN t0 ON t1.c0 = t0.c0"; + try { + dorisAssert.query(sql).explainQuery(); + Assert.fail("t0.__DORIS_DELETE_SIGN__ is a hidden column to mark whether a row deleted when unique key existed, '__DORIS_DELETE_SIGN__ = 0' will appear in where clause, that will cause semi join syntax error, please use not in/existed as replacement."); + } catch (AnalysisException e) { + System.out.println(e.getMessage()); + } + + sql = " SELECT * FROM t1 RIGHT ANTI JOIN t0 ON t1.c0 = t0.c0"; + try { + dorisAssert.query(sql).explainQuery(); + Assert.fail("t0.__DORIS_DELETE_SIGN__ is a hidden column to mark whether a row deleted when unique key existed, '__DORIS_DELETE_SIGN__ = 0' will appear in where clause, that will cause semi join syntax error, please use not in/existed as replacement."); + } catch (AnalysisException e) { + System.out.println(e.getMessage()); + } + + sql = " SELECT * FROM t1 LEFT JOIN t0 ON t1.c0 = t0.c0"; + dorisAssert.query(sql).explainQuery(); + + sql = " SELECT * FROM t1 RIGHT JOIN t0 ON t1.c0 = t0.c0"; + dorisAssert.query(sql).explainQuery(); + + sql = " SELECT * FROM t1 where c0 NOT IN ( select c0 from t0)"; + dorisAssert.query(sql).explainQuery(); + } +}