[Lateral View] Multi lateral views map one TableFunctionNode (#7000)

1. Forbidden non-string column as params of explode_view.
The first param of explode_view must be string column(VARCHAR/CHAR/STRING)

2. N-1 n lateral views map one TableFunctionNode
The TableFunctionNode include all of fnExprs which belongs to one table.
For example:
select pageid,mycol1, mycol2 from pageAds
    lateral view explode_string(col1) myTable1 as mycol1
    lateral view explode_string(col2) myTable2 as mycol2;
TableFunctionNode
|----
|- fnExprList: explode_string(col1), explode_string(col2)
This commit is contained in:
EmmyMiao87
2021-11-17 11:13:08 +08:00
committed by GitHub
parent 6c6380969b
commit 7b712925fc
5 changed files with 44 additions and 22 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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<LateralViewRef> lateralViewRefs;
private List<FunctionCallExpr> fnCallExprList;
private List<TupleId> lateralViewTupleIds;
private FunctionCallExpr fnCallExpr;
protected TableFunctionNode(PlanNodeId id, PlanNode inputNode, LateralViewRef lateralViewRef) {
protected TableFunctionNode(PlanNodeId id, PlanNode inputNode, List<LateralViewRef> 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));
}
}

View File

@ -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(*)}"));

View File

@ -657,7 +657,7 @@ struct TOlapRewriteNode {
}
struct TTableFunctionNode {
1: required Exprs.TExpr fnCallExpr
1: required list<Exprs.TExpr> fnCallExprList
}
// This contains all of the information computed by the plan as part of the resource