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 e6576ecc29..b056b4ba1e 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 @@ -98,6 +98,9 @@ public class LateralViewRef extends TableRef { if (originColumn == null) { throw new AnalysisException("The explode column must be a real column in table"); } + if (!originColumn.getType().isStringType()) { + throw new AnalysisException("The explode column must be VARCHAR/CHAR/STRING type"); + } // analyze lateral view desc = analyzer.registerTableRef(this); explodeSlotRef = new SlotRef(new TableName(null, viewName), columnName); diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/SingleNodePlanner.java b/fe/fe-core/src/main/java/org/apache/doris/planner/SingleNodePlanner.java index 31c0b3fe2c..57b88d854a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/SingleNodePlanner.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/SingleNodePlanner.java @@ -1882,12 +1882,10 @@ public class SingleNodePlanner { throws UserException { Preconditions.checkNotNull(lateralViewRefs); Preconditions.checkState(lateralViewRefs.size() > 0); - for (LateralViewRef lateralViewRef: lateralViewRefs) { - TableFunctionNode tableFunctionNode = new TableFunctionNode(ctx_.getNextNodeId(), inputNode, - lateralViewRef); - tableFunctionNode.init(analyzer); - inputNode = tableFunctionNode; - } + TableFunctionNode tableFunctionNode = new TableFunctionNode(ctx_.getNextNodeId(), inputNode, + lateralViewRefs); + tableFunctionNode.init(analyzer); + inputNode = tableFunctionNode; return inputNode; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/TableFunctionNode.java b/fe/fe-core/src/main/java/org/apache/doris/planner/TableFunctionNode.java index 4601f8a9dc..3bb9d8ce1d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/planner/TableFunctionNode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/planner/TableFunctionNode.java @@ -18,34 +18,41 @@ package org.apache.doris.planner; import org.apache.doris.analysis.Analyzer; +import org.apache.doris.analysis.Expr; import org.apache.doris.analysis.FunctionCallExpr; import org.apache.doris.analysis.LateralViewRef; +import org.apache.doris.analysis.TupleId; import org.apache.doris.common.UserException; import org.apache.doris.thrift.TExplainLevel; import org.apache.doris.thrift.TPlanNode; import org.apache.doris.thrift.TPlanNodeType; import org.apache.doris.thrift.TTableFunctionNode; +import java.util.List; +import java.util.stream.Collectors; + public class TableFunctionNode extends PlanNode { - private LateralViewRef lateralViewRef; + private List lateralViewRefs; + private List fnCallExprList; + private List lateralViewTupleIds; - private FunctionCallExpr fnCallExpr; - - protected TableFunctionNode(PlanNodeId id, PlanNode inputNode, LateralViewRef lateralViewRef) { + protected TableFunctionNode(PlanNodeId id, PlanNode inputNode, List lateralViewRefs) { super(id, "TABLE FUNCTION NODE"); tupleIds.addAll(inputNode.getTupleIds()); tblRefIds.addAll(inputNode.getTupleIds()); - tupleIds.add(lateralViewRef.getDesc().getId()); - tblRefIds.add(lateralViewRef.getDesc().getId()); + lateralViewTupleIds = lateralViewRefs.stream().map(e -> e.getDesc().getId()) + .collect(Collectors.toList()); + tupleIds.addAll(lateralViewTupleIds); + tblRefIds.addAll(lateralViewTupleIds); children.add(inputNode); - this.lateralViewRef = lateralViewRef; + this.lateralViewRefs = lateralViewRefs; } @Override public void init(Analyzer analyzer) throws UserException { super.init(analyzer); - fnCallExpr = lateralViewRef.getFnExpr(); + fnCallExprList = lateralViewRefs.stream().map(e -> e.getFnExpr()).collect(Collectors.toList()); computeStats(analyzer); } @@ -59,11 +66,28 @@ public class TableFunctionNode extends PlanNode { @Override public String getNodeExplainString(String prefix, TExplainLevel detailLevel) { StringBuilder output = new StringBuilder(); - output.append(prefix + "table function: ").append(fnCallExpr.toSql() + "\n"); + output.append(prefix + "table function: "); + for (FunctionCallExpr fnExpr : fnCallExprList) { + output.append(fnExpr.toSqlImpl() + " "); + } + output.append("\n"); + + output.append(prefix + "lateral view tuple id: "); + for (TupleId tupleId : lateralViewTupleIds) { + output.append(tupleId.asInt() + " "); + } + output.append("\n"); + if (detailLevel == TExplainLevel.BRIEF) { return output.toString(); } + output.append(prefix + "tuple id: "); + for (TupleId tupleId : tupleIds) { + output.append(tupleId.asInt() + " "); + } + output.append("\n"); + if (!conjuncts.isEmpty()) { output.append(prefix).append("PREDICATES: ").append( getExplainString(conjuncts)).append("\n"); @@ -76,6 +100,6 @@ public class TableFunctionNode extends PlanNode { protected void toThrift(TPlanNode msg) { msg.node_type = TPlanNodeType.TABLE_FUNCTION_NODE; msg.table_function_node = new TTableFunctionNode(); - msg.table_function_node.setFnCallExpr(fnCallExpr.treeToThrift()); + msg.table_function_node.setFnCallExprList(Expr.treesToThrift(fnCallExprList)); } } 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 1d59d15f4d..274628670b 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 @@ -150,12 +150,9 @@ public class TableFunctionPlanTest { String sql = "desc verbose select k1, e1, e2 from db1.tbl1 lateral view explode_split(k2, \",\") tmp1 as e1" + " lateral view explode_split(k2, \",\") tmp2 as e2;"; String explainString = UtFrameUtils.getSQLPlanOrErrorMsg(ctx, sql, true); - Assert.assertTrue(explainString.contains("2:TABLE FUNCTION NODE")); - Assert.assertTrue(explainString.contains("table function: explode_split(`k2`, ',')")); - Assert.assertTrue(explainString.contains("tuple ids: 0 1 2")); Assert.assertTrue(explainString.contains("1:TABLE FUNCTION NODE")); - Assert.assertTrue(explainString.contains("table function: explode_split(`k2`, ',')")); - Assert.assertTrue(explainString.contains("tuple ids: 0 1")); + Assert.assertTrue(explainString.contains("table function: explode_split(`k2`, ',') explode_split(`k2`, ',')")); + Assert.assertTrue(explainString.contains("lateral view tuple id: 1 2")); // lateral view 2 tuple Assert.assertTrue(explainString.contains("TupleDescriptor{id=1, tbl=tmp2, byteSize=32, materialized=true}")); Assert.assertTrue(explainString.contains("SlotDescriptor{id=1, col=e2, type=VARCHAR(*)}")); diff --git a/gensrc/thrift/PlanNodes.thrift b/gensrc/thrift/PlanNodes.thrift index 6a862ef84f..a217157751 100644 --- a/gensrc/thrift/PlanNodes.thrift +++ b/gensrc/thrift/PlanNodes.thrift @@ -657,7 +657,7 @@ struct TOlapRewriteNode { } struct TTableFunctionNode { - 1: required Exprs.TExpr fnCallExpr + 1: required list fnCallExprList } // This contains all of the information computed by the plan as part of the resource