This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new f93ac89  [fix](lateral-view) fix bugs of lateral view with CTE or 
where clause (#7865)
f93ac89 is described below

commit f93ac89a67765b7ced743ecaa1c511003ca4d1b6
Author: Mingyu Chen <morningman....@gmail.com>
AuthorDate: Fri Jan 28 22:24:23 2022 +0800

    [fix](lateral-view) fix bugs of lateral view with CTE or where clause 
(#7865)
    
    fix bugs of lateral view with CTE or where clause.
    The error case can be found in newly added tests in 
`TableFunctionPlanTest.java`
    But there are still some bugs not being fixed, so the unit test is 
annotated with @Ignore
    
    This PR contains the change is #7824 :
    
    > Issue Number: close #7823
    >
    > After the subquery is rewritten, the rewritten stmt needs to be reset
    > (that is, the content of the first analyze semantic analysis is cleared),
    > and then the rewritten stmt can be reAnalyzed.
    >
    > The lateral view ref in the previous implementation forgot to implement 
the reset function.
    > This caused him to keep the first error message in the second analyze.
    > Eventually, two duplicate tupleIds appear in the new stmt and are marked 
with different tuple.
    > From the explain string, the following syntax will have an additional 
wrong join predicate.
    > ```
    > Query: explain select k1 from test_explode lateral view explode_split(k2, 
",") tmp as e1  where k1 in (select k3 from tbl1);
    > Error equal join conjunct: `k3` = `k3`
    > ```
    >
    > This pr mainly adds the reset function of the lateral view
    > to avoid possible errors in the second analyze
    > when the lateral view and subquery rewrite occur at the same time.
---
 .../org/apache/doris/analysis/LateralViewRef.java  | 29 ++++++++++
 .../java/org/apache/doris/analysis/SelectStmt.java | 11 +++-
 .../java/org/apache/doris/analysis/TableRef.java   | 65 +++++++++++++++++-----
 .../doris/planner/TableFunctionPlanTest.java       | 59 ++++++++++++++++++++
 4 files changed, 147 insertions(+), 17 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
index 5736d23..bee7032 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/LateralViewRef.java
@@ -85,6 +85,11 @@ public class LateralViewRef extends TableRef {
         isAnalyzed = true;  // true now that we have assigned desc
     }
 
+    @Override
+    public TableRef clone() {
+        return new LateralViewRef(this.expr.clone(), this.viewName, 
this.columnName);
+    }
+
     private void analyzeFunctionExpr(Analyzer analyzer) throws 
AnalysisException {
         fnExpr = (FunctionCallExpr) expr;
         fnExpr.setTableFnCall(true);
@@ -178,5 +183,29 @@ public class LateralViewRef extends TableRef {
             throw new AnalysisException("Subquery is not allowed in lateral 
view");
         }
     }
+
+    @Override
+    public String toSql() {
+        return "lateral view " + fnExpr.toSql() + " " + viewName + " as " + 
columnName;
+    }
+
+    @Override
+    public String toString() {
+        return toSql();
+    }
+
+    @Override
+    public void reset() {
+        isAnalyzed = false;
+        expr.reset();
+        fnExpr = null;
+        originSlotRefList = Lists.newArrayList();
+        view = null;
+        explodeSlotRef = null;
+        // There is no need to call the reset function of @relatedTableRef 
here.
+        // The main reason is that @lateralViewRef itself is an attribute of 
@relatedTableRef
+        // The reset of @lateralViewRef happens in the reset() of 
@relatedTableRef.
+    }
 }
 
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
index 22db67b..1c52f12 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java
@@ -438,7 +438,7 @@ public class SelectStmt extends QueryStmt {
         if (groupByClause != null && groupByClause.isGroupByExtension()) {
             for (SelectListItem item : selectList.getItems()) {
                 if (item.getExpr() instanceof FunctionCallExpr && 
item.getExpr().fn instanceof AggregateFunction) {
-                    for (Expr expr: groupByClause.getGroupingExprs()) {
+                    for (Expr expr : groupByClause.getGroupingExprs()) {
                         if (item.getExpr().contains(expr)) {
                             throw new AnalysisException("column: " + 
expr.toSql() + " cannot both in select list and "
                                     + "aggregate functions when using GROUPING 
SETS/CUBE/ROLLUP, please use union"
@@ -877,6 +877,12 @@ public class SelectStmt extends QueryStmt {
             expandStar(new TableName(tableRef.getAliasAsName().getDb(),
                             tableRef.getAliasAsName().getTbl()),
                     tableRef.getDesc());
+
+            if (tableRef.lateralViewRefs != null) {
+                for (LateralViewRef lateralViewRef : tableRef.lateralViewRefs) 
{
+                    expandStar(lateralViewRef.getName(), 
lateralViewRef.getDesc());
+                }
+            }
         }
     }
 
@@ -1049,7 +1055,7 @@ public class SelectStmt extends QueryStmt {
             groupByClause.analyze(analyzer);
             createAggInfo(groupByClause.getGroupingExprs(), aggExprs, 
analyzer);
         } else {
-            createAggInfo( new ArrayList<>(), aggExprs, analyzer);
+            createAggInfo(new ArrayList<>(), aggExprs, analyzer);
         }
 
         // combine avg smap with the one that produces the final agg output
@@ -1874,3 +1880,4 @@ public class SelectStmt extends QueryStmt {
         return this.id.equals(((SelectStmt) obj).id);
     }
 }
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/TableRef.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/TableRef.java
index 95f90f2..c0c39ed 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/TableRef.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/TableRef.java
@@ -99,9 +99,9 @@ public class TableRef implements ParseNode, Writable {
     private boolean isForcePreAggOpened;
     // ///////////////////////////////////////
     // BEGIN: Members that need to be reset()
-    
+
     protected Expr onClause;
-    
+
     // the ref to the left of us, if we're part of a JOIN clause
     protected TableRef leftTblRef;
 
@@ -133,7 +133,7 @@ public class TableRef implements ParseNode, Writable {
 
     // END: Members that need to be reset()
     // ///////////////////////////////////////
-    
+
     public TableRef() {
         // for persist
     }
@@ -161,6 +161,7 @@ public class TableRef implements ParseNode, Writable {
         this.commonHints = commonHints;
         isAnalyzed = false;
     }
+
     // Only used to clone
     // this will reset all the 'analyzed' stuff
     protected TableRef(TableRef other) {
@@ -187,7 +188,13 @@ public class TableRef implements ParseNode, Writable {
         allMaterializedTupleIds_ = 
Lists.newArrayList(other.allMaterializedTupleIds_);
         correlatedTupleIds_ = Lists.newArrayList(other.correlatedTupleIds_);
         desc = other.desc;
-        lateralViewRefs = other.lateralViewRefs;
+        lateralViewRefs = null;
+        if (other.lateralViewRefs != null) {
+            lateralViewRefs = Lists.newArrayList();
+            for (LateralViewRef viewRef : other.lateralViewRefs) {
+                lateralViewRefs.add((LateralViewRef) viewRef.clone());
+            }
+        }
     }
 
     public PartitionNames getPartitionNames() {
@@ -279,13 +286,17 @@ public class TableRef implements ParseNode, Writable {
      * Returns true if this table ref has a resolved path that is rooted at a 
registered
      * tuple descriptor, false otherwise.
      */
-    public boolean isRelative() { return false; }
+    public boolean isRelative() {
+        return false;
+    }
 
     /**
      * Indicates if this TableRef directly or indirectly references another 
TableRef from
      * an outer query block.
      */
-    public boolean isCorrelated() { return !correlatedTupleIds_.isEmpty(); }
+    public boolean isCorrelated() {
+        return !correlatedTupleIds_.isEmpty();
+    }
 
     public Table getTable() {
         return desc.getTable();
@@ -417,7 +428,7 @@ public class TableRef implements ParseNode, Writable {
      * The join clause can only be analyzed after the left table has been 
analyzed
      * and the TupleDescriptor (desc) of this table has been created.
      */
-    public void analyzeJoin(Analyzer analyzer)  throws AnalysisException {
+    public void analyzeJoin(Analyzer analyzer) throws AnalysisException {
         Preconditions.checkState(leftTblRef == null || leftTblRef.isAnalyzed);
         Preconditions.checkState(desc != null);
         analyzeJoinHints();
@@ -540,7 +551,7 @@ public class TableRef implements ParseNode, Writable {
             // of the outer join; those can be evaluated directly when 
materializing tuples
             // without violating outer join semantics.
             analyzer.registerOnClauseConjuncts(conjuncts, this);
-            for (Expr e: conjuncts) {
+            for (Expr e : conjuncts) {
                 List<TupleId> tupleIds = Lists.newArrayList();
                 e.getIds(tupleIds, null);
                 onClauseTupleIds.addAll(tupleIds);
@@ -611,7 +622,16 @@ public class TableRef implements ParseNode, Writable {
         // if (resolvedPath_ != null) path = 
resolvedPath_.getFullyQualifiedRawPath();
         // return ToSqlUtils.getPathSql(path) + ((aliasSql != null) ? " " + 
aliasSql : "");
 
-        return name.toSql() + ((aliasSql != null) ? " " + aliasSql : "");
+        // tbl1
+        // tbl1 alias_tbl1
+        // tbl1 alias_tbl1 lateral view explode_split(k1, ",") tmp1 as e1
+        String tblName = name.toSql() + ((aliasSql != null) ? " " + aliasSql : 
"");
+        if (lateralViewRefs != null) {
+            for (LateralViewRef viewRef : lateralViewRefs) {
+                tblName += " " + viewRef.toSql();
+            }
+        }
+        return tblName;
     }
 
     @Override
@@ -652,21 +672,27 @@ public class TableRef implements ParseNode, Writable {
     /**
      * Returns all legal aliases of this table ref.
      */
-    public String[] getAliases() { return aliases_; }
+    public String[] getAliases() {
+        return aliases_;
+    }
 
     /**
      * Returns the explicit alias or the fully-qualified implicit alias. The 
returned alias
      * is guaranteed to be unique (i.e., column/field references against the 
alias cannot
      * be ambiguous).
      */
-    public String getUniqueAlias() { return aliases_[0]; }
+    public String getUniqueAlias() {
+        return aliases_[0];
+    }
 
     /**
      * Returns true if this table ref has an explicit alias.
      * Note that getAliases().length() == 1 does not imply an explicit alias 
because
      * nested collection refs have only a single implicit alias.
      */
-    public boolean hasExplicitAlias() { return hasExplicitAlias_; }
+    public boolean hasExplicitAlias() {
+        return hasExplicitAlias_;
+    }
 
     /**
      * Returns the explicit alias if this table ref has one, null otherwise.
@@ -676,7 +702,10 @@ public class TableRef implements ParseNode, Writable {
         return null;
     }
 
-    public boolean isAnalyzed() { return isAnalyzed; }
+    public boolean isAnalyzed() {
+        return isAnalyzed;
+    }
+
     public boolean isResolved() {
         return !getClass().equals(TableRef.class);
     }
@@ -722,6 +751,11 @@ public class TableRef implements ParseNode, Writable {
         allMaterializedTupleIds_.clear();
         correlatedTupleIds_.clear();
         desc = null;
+        if (lateralViewRefs != null) {
+            for (LateralViewRef lateralViewRef : lateralViewRefs) {
+                lateralViewRef.reset();
+            }
+        }
     }
 
     /**
@@ -755,7 +789,7 @@ public class TableRef implements ParseNode, Writable {
             out.writeBoolean(true);
             partitionNames.write(out);
         }
-        
+
         if (hasExplicitAlias()) {
             out.writeBoolean(true);
             Text.writeString(out, getExplicitAlias());
@@ -783,7 +817,8 @@ public class TableRef implements ParseNode, Writable {
 
         if (in.readBoolean()) {
             String alias = Text.readString(in);
-            aliases_ = new String[] { alias };
+            aliases_ = new String[]{alias};
         }
     }
 }
+
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
index 7e610dc..a2b26e4 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/planner/TableFunctionPlanTest.java
@@ -19,6 +19,7 @@ package org.apache.doris.planner;
 
 import org.apache.doris.analysis.CreateDbStmt;
 import org.apache.doris.analysis.CreateTableStmt;
+import org.apache.doris.analysis.CreateViewStmt;
 import org.apache.doris.analysis.FunctionCallExpr;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.qe.ConnectContext;
@@ -28,6 +29,7 @@ import org.apache.commons.io.FileUtils;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.io.File;
@@ -60,6 +62,11 @@ public class TableFunctionPlanTest {
                 + "distributed by hash(k1) buckets 3 
properties('replication_num' = '1');";
         createTableStmt = (CreateTableStmt) 
UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, ctx);
         Catalog.getCurrentCatalog().createTable(createTableStmt);
+
+        createTblStmtStr = "create table db1.table_for_view (k1 int, k2 int, 
k3 varchar(100)) distributed by hash(k1)" +
+                "properties('replication_num' = '1');";
+        createTableStmt = (CreateTableStmt) 
UtFrameUtils.parseAndAnalyzeStmt(createTblStmtStr, ctx);
+        Catalog.getCurrentCatalog().createTable(createTableStmt);
     }
 
     // test planner
@@ -441,6 +448,7 @@ public class TableFunctionPlanTest {
         Assert.assertTrue(explainString.contains("table function: 
explode_json_array_double('[1.1, 2.2, 3.3]')"));
         Assert.assertTrue(explainString.contains("output slot id: 0 1"));
     }
+
     /*
     Case4 agg and order column in the same stmt with lateral view
     select min(c1) from (select k1 as c1, min(k2) as c2 from tbl1 group by k1) 
tmp1
@@ -470,4 +478,55 @@ public class TableFunctionPlanTest {
         Assert.assertTrue(explainString.contains("output slot id: 2"));
         Assert.assertTrue(explainString.contains("tuple ids: 1 3"));
     }
+
+    @Test
+    public void testLaterViewWithView() throws Exception {
+        // test 1
+        String createViewStr = "create view db1.v1 (k1,e1) as select k1,e1 
from db1.table_for_view lateral view explode_split(k3,',') tmp as e1;";
+        CreateViewStmt createViewStmt = (CreateViewStmt) 
UtFrameUtils.parseAndAnalyzeStmt(createViewStr, ctx);
+        Catalog.getCurrentCatalog().createView(createViewStmt);
+
+        String sql = "select * from db1.v1;";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, 
true);
+        Assert.assertTrue(explainString.contains("output slot id: 1 2"));
+        // query again to see if it has error
+        explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true);
+        Assert.assertTrue(explainString.contains("output slot id: 1 2"));
+    }
+
+    @Test
+    public void testLaterViewWithWhere() throws Exception {
+        String sql = "select k1,e1 from db1.table_for_view lateral view 
explode_split(k3,',') tmp as e1 where k1 in (select k2 from 
db1.table_for_view);";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, 
true);
+        Assert.assertTrue(explainString.contains("join op: LEFT SEMI JOIN 
(BROADCAST)"));
+        Assert.assertTrue(explainString.contains("equal join conjunct: `k1` = 
`k2`"));
+        Assert.assertTrue(!explainString.contains("equal join conjunct: `k2` = 
`k2`"));
+    }
+
+    @Test
+    public void testLaterViewWithCTE() throws Exception {
+        String sql = "with tmp as (select k1,e1 from db1.table_for_view 
lateral view explode_split(k3,',') tmp2 as e1) select * from tmp;";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, 
true);
+        Assert.assertTrue(explainString.contains("table function: 
explode_split(`default_cluster:db1`.`table_for_view`.`k3`, ',') "));
+    }
+
+    @Ignore
+    // errCode = 2, detailMessage = Unknown column 'e1' in 'table list'
+    public void testLaterViewWithCTEBug() throws Exception {
+        String sql = "with tmp as (select * from db1.table_for_view where 
k2=1) select k1,e1 from tmp lateral view explode_split(k3,',') tmp2 as e1;";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, 
true);
+        Assert.assertTrue(!explainString.contains("Unknown column 'e1' in 
'table list'"));
+    }
+
+    @Ignore
+    // errCode = 2, detailMessage = Unknown column 'e1' in 'table list'
+    public void testLaterViewUnknowColumnBug() throws Exception {
+        // test2
+        String createViewStr = "create view db1.v2 (k1,k3) as select k1,k3 
from db1.table_for_view;";
+        CreateViewStmt createViewStmt = (CreateViewStmt) 
UtFrameUtils.parseAndAnalyzeStmt(createViewStr, ctx);
+        Catalog.getCurrentCatalog().createView(createViewStmt);
+        String sql = "select k1,e1 from db1.v2 lateral view 
explode_split(k3,',') tmp as e1;";
+        String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, 
true);
+        Assert.assertTrue(!explainString.contains("Unknown column 'e1' in 
'table list'"));
+    }
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to