diff --git a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/BaseHandlerBuilder.java b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/BaseHandlerBuilder.java index 8db4c345c..4ecdb0cab 100644 --- a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/BaseHandlerBuilder.java +++ b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/BaseHandlerBuilder.java @@ -40,6 +40,7 @@ import com.actiontech.dble.route.RouteResultsetNode; import com.actiontech.dble.route.util.RouterUtil; import com.actiontech.dble.server.NonBlockingSession; import com.actiontech.dble.server.parser.ServerParse; +import com.actiontech.dble.util.StringUtil; import com.alibaba.druid.sql.ast.SQLOrderingSpecification; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; @@ -374,6 +375,21 @@ public abstract class BaseHandlerBuilder { addDelayTableHandler(sh, (TableNode) node); } else if (node instanceof JoinNode && canDoAsMerge()) { nestLoopAddHandler(sh, node); + } else if (node.haveDependOnNode()) { + List nestLoopDependOnNodeList = node.getNestLoopDependOnNodeList(); + for (PlanNode planNode : nestLoopDependOnNodeList) { + TableNode tableNode = (TableNode) planNode.getNestLoopDependNode(); + HintNestLoopHelper hintNestLoopHelper = tableNode.getHintNestLoopHelper(); + List delayTableHandlers = hintNestLoopHelper.getDelayTableHandlers(tableNode); + Map sendMakeHandlerHashMap = hintNestLoopHelper.getSendMakeHandlerHashMap(); + Set tableHandlers = sh.getTableHandlers(); + for (DelayTableHandler delayTableHandler : delayTableHandlers) { + if (StringUtil.equals(planNode.getAlias(), delayTableHandler.getTableAlias())) { + tableHandlers.add(delayTableHandler); + } + } + sendMakeHandlerHashMap.put(tableNode, sh); + } } } diff --git a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/HintNestLoopHelper.java b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/HintNestLoopHelper.java index 48917a52e..04ed8fe66 100644 --- a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/HintNestLoopHelper.java +++ b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/HintNestLoopHelper.java @@ -12,6 +12,8 @@ public class HintNestLoopHelper { Map> delayTableHandlerMap = new HashMap<>(); Map sendMakeHandlerHashMap = new HashMap<>(); + //hint plan that the node is a dependent node, when in fact the node is not + Set fakeDependSet = new HashSet<>(); Map> itemMap = new HashMap<>(); public Map getSendMakeHandlerHashMap() { @@ -33,4 +35,8 @@ public class HintNestLoopHelper { public Map> getItemMap() { return itemMap; } + + public Set getFakeDependSet() { + return fakeDependSet; + } } diff --git a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/JoinNodeHandlerBuilder.java b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/JoinNodeHandlerBuilder.java index 965c1aaa8..c0562728e 100644 --- a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/JoinNodeHandlerBuilder.java +++ b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/builder/JoinNodeHandlerBuilder.java @@ -234,12 +234,15 @@ class JoinNodeHandlerBuilder extends BaseHandlerBuilder { if (Objects.nonNull(dependNode)) { Pair itemPair = ((TableNode) currentNode).getHintNestLoopHelper().getItemMap().get(currentNode); DelayTableHandler delayTableHandler = buildDelayHandler(isLeft, currentNode, itemPair.getKey(), itemPair.getValue()); - Map> delayTableHandlerMap = dependNode.getHintNestLoopHelper().getDelayTableHandlerMap(); + HintNestLoopHelper hintNestLoopHelper = dependNode.getHintNestLoopHelper(); + Map> delayTableHandlerMap = hintNestLoopHelper.getDelayTableHandlerMap(); List delayTableHandlerList = Optional.ofNullable(delayTableHandlerMap.get(dependNode)).orElse(new ArrayList<>()); - delayTableHandlerList.add(delayTableHandler); + if (!hintNestLoopHelper.getFakeDependSet().contains(currentNode)) { + delayTableHandlerList.add(delayTableHandler); + } delayTableHandlerMap.put(dependNode, delayTableHandlerList); pres.add(delayTableHandler); - SendMakeHandler sendMakeHandler = dependNode.getHintNestLoopHelper().getSendMakeHandlerHashMap().get(dependNode); + SendMakeHandler sendMakeHandler = hintNestLoopHelper.getSendMakeHandlerHashMap().get(dependNode); if (Objects.nonNull(sendMakeHandler)) { sendMakeHandler.getTableHandlers().add(delayTableHandler); } @@ -250,7 +253,7 @@ class JoinNodeHandlerBuilder extends BaseHandlerBuilder { } else if (currentNode instanceof TableNode) { DMLResponseHandler rh = buildJoinChild(currentNode, isLeft); HintNestLoopHelper hintNestLoopHelper = ((TableNode) currentNode).getHintNestLoopHelper(); - List delayTableHandlerList = Optional.ofNullable(hintNestLoopHelper.getDelayTableHandlerMap().get(currentNode)).orElse(new ArrayList<>()); + List delayTableHandlerList = hintNestLoopHelper.getDelayTableHandlers(node); SendMakeHandler sendMakeHandler = ((TableNode) currentNode).getHintNestLoopHelper().getSendMakeHandlerHashMap().get(currentNode); for (DelayTableHandler handler : delayTableHandlerList) { sendMakeHandler.getTableHandlers().add(handler); @@ -314,6 +317,7 @@ class JoinNodeHandlerBuilder extends BaseHandlerBuilder { private DelayTableHandler buildDelayHandler(boolean isLeft, PlanNode tnBig, Item keySource, Item keyToPass) { final DelayTableHandler delayTableHandler = new DelayTableHandler(getSequenceId(), session, keySource); + delayTableHandler.setTableAlias(tnBig.getAlias()); delayTableHandler.setLeft(isLeft); CallBackHandler tempDone = () -> { Set valueSet = delayTableHandler.getValueSet(); diff --git a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/query/impl/DelayTableHandler.java b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/query/impl/DelayTableHandler.java index f89924e49..a7e67e4a1 100644 --- a/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/query/impl/DelayTableHandler.java +++ b/src/main/java/com/actiontech/dble/backend/mysql/nio/handler/query/impl/DelayTableHandler.java @@ -51,6 +51,7 @@ public class DelayTableHandler extends BaseDMLHandler { private final Item sourceSel; private Field sourceField; private Set valueSet; + private String tableAlias; public DelayTableHandler(long id, Session session, Item sourceSel) { super(id, session); @@ -183,4 +184,11 @@ public class DelayTableHandler extends BaseDMLHandler { return HandlerType.TEMPTABLE; } + public String getTableAlias() { + return tableAlias; + } + + public void setTableAlias(String tableAlias) { + this.tableAlias = tableAlias; + } } diff --git a/src/main/java/com/actiontech/dble/plan/optimizer/JoinNestLoopChooser.java b/src/main/java/com/actiontech/dble/plan/optimizer/JoinNestLoopChooser.java index 781bbb74a..0c437ca20 100644 --- a/src/main/java/com/actiontech/dble/plan/optimizer/JoinNestLoopChooser.java +++ b/src/main/java/com/actiontech/dble/plan/optimizer/JoinNestLoopChooser.java @@ -10,10 +10,11 @@ import com.actiontech.dble.plan.node.PlanNode; import com.actiontech.dble.plan.node.TableNode; import com.actiontech.dble.plan.util.PlanUtil; import com.actiontech.dble.route.parser.util.Pair; +import com.actiontech.dble.server.parser.HintPlanParse; import com.actiontech.dble.util.StringUtil; +import com.google.common.collect.Lists; import java.util.*; -import java.util.stream.Collectors; public class JoinNestLoopChooser { private Map nodeMap; @@ -27,17 +28,9 @@ public class JoinNestLoopChooser { this.hintPlanInfo = hintPlanInfo; nodeMap = new HashMap<>(); nodeDependMap = new HashMap<>(); - conversionNodeDependMap(hintPlanInfo.getDependMap()); hintNestLoopHelper = new HintNestLoopHelper(); } - private void conversionNodeDependMap(HashMap> dependMap) { - dependMap.forEach((k, v) -> { - List dependList = v.stream().map(HintPlanNode::getName).collect(Collectors.toList()); - nodeDependMap.put(k, dependList); - }); - } - public void tryNestLoop() throws MySQLOutPutException { buildNode(jn); checkHintDependency(); @@ -72,7 +65,21 @@ public class JoinNestLoopChooser { private void buildNestLoop() { nodeDependMap.forEach((alias, v) -> { if (!v.isEmpty()) { - String dependName = v.get(0); + HashMap> dependMap = hintPlanInfo.getDependMap(); + Set hintPlanNodes = dependMap.get(alias); + LinkedHashMap hintPlanNodeMap = hintPlanInfo.getHintPlanNodeMap(); + String dependName = hintPlanNodes.iterator().next().getName(); + if (hintPlanNodes.size() > 1 && !StringUtil.equals(dependName, v.get(0)) && hintPlanNodeMap.get(dependName) == HintPlanParse.Type.AND) { + PlanNode currentNode = nodeMap.get(alias); + PlanNode dependNode = nodeMap.get(dependName).getParent(); + TableNode fakeDependNode = (TableNode) nodeMap.get(v.get(0)); + List nodeList = Optional.ofNullable(dependNode.getNestLoopDependOnNodeList()).orElse(new ArrayList<>()); + nodeList.add(nodeList.size(), currentNode); + dependNode.setNestLoopDependOnNodeList(nodeList); + fakeDependNode.getHintNestLoopHelper().getFakeDependSet().add(currentNode); + } + + dependName = v.get(0); PlanNode currentNode = nodeMap.get(alias); PlanNode dependNode = nodeMap.get(dependName); List nodeList = Optional.ofNullable(dependNode.getNestLoopDependOnNodeList()).orElse(new ArrayList<>()); @@ -109,13 +116,17 @@ public class JoinNestLoopChooser { } private void checkHintDependency() { - HashMap> dependMap = hintPlanInfo.getDependMap(); + LinkedHashMap hintPlanNodeMap = hintPlanInfo.getHintPlanNodeMap(); HashMap> erMap = hintPlanInfo.getErMap(); checkErCondition(erMap); if (hintPlanInfo.nodeSize() != nodeMap.size()) { throw new MySQLOutPutException(ErrorCode.ER_OPTIMIZER, "", "the number of tables in the hint plan and the actual SQL varies"); } - dependMap.forEach((k, v) -> checkAndOrCondition(k)); + hintPlanNodeMap.forEach((k, v) -> { + if (v != HintPlanParse.Type.ER) { + checkAndOrCondition(k); + } + }); hintAndCheck(); } @@ -128,7 +139,11 @@ public class JoinNestLoopChooser { PlanNode dependNode = nodeMap.get(node.getName()); boolean result = dependencyHelper(dependNode, currentNode, currentNode); if (result) { - nodeDependMap.get(currentNode.getAlias()).add(dependNode.getAlias()); + //the verified dependency need to be put into the nodeDependMap + String alias = currentNode.getAlias(); + List dependList = Optional.ofNullable(nodeDependMap.get(alias)).orElse(Lists.newArrayList()); + dependList.add(dependNode.getAlias()); + nodeDependMap.put(k, dependList); return; } } diff --git a/src/main/java/com/actiontech/dble/server/parser/HintPlanParse.java b/src/main/java/com/actiontech/dble/server/parser/HintPlanParse.java index 6ff2fecce..116a980d5 100644 --- a/src/main/java/com/actiontech/dble/server/parser/HintPlanParse.java +++ b/src/main/java/com/actiontech/dble/server/parser/HintPlanParse.java @@ -58,7 +58,8 @@ public class HintPlanParse { nodeStack = buildChildNode(nodeStack, and); nodeStack = buildChildNode(nodeStack, or); } - if (nodeStack.size() > 1 || !nodeStack.peek().isTable()) { + Node peek = nodeStack.peek(); + if (nodeStack.size() > 1 || !(peek.isTable() || peek.getType() == Type.ER)) { throw new ConfigException("hint parse failure"); } return nodeStack.pop(); @@ -161,13 +162,13 @@ public class HintPlanParse { er.append(c); break; case '|': - addNode(nodeName.toString(), nodeList, erRelation); + addNode(nodeName.toString(), nodeList, erRelation, Type.OR); nodeList.add(new Node(String.valueOf(c), Type.OR)); nodeName = new StringBuilder(); er = new StringBuilder(); break; case '&': - addNode(nodeName.toString(), nodeList, erRelation); + addNode(nodeName.toString(), nodeList, erRelation, Type.AND); nodeList.add(new Node(String.valueOf(c), Type.AND)); nodeName = new StringBuilder(); er = new StringBuilder(); @@ -198,7 +199,7 @@ public class HintPlanParse { } @NotNull - private void addNode(String nodeName, List nodeList, boolean erRelation) throws ConfigException { + private void addNode(String nodeName, List nodeList, boolean erRelation, Type type) throws ConfigException { if (erRelation) { throw new ConfigException("er Relation need like (a,b,c)"); } @@ -208,7 +209,7 @@ public class HintPlanParse { } nodeMap.putIfAbsent(nodeName, HintPlanNode.of(nodeName)); //table has dependencies will be added to the end - hintPlanNodeMap.put(nodeName, Type.OR); + hintPlanNodeMap.put(nodeName, type); nodeList.add(new Node(nodeName)); }