package com.taobao.tddl.optimizer.costbased; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.junit.Assert; import org.junit.Test; import com.taobao.tddl.common.model.ExtraCmd; import com.taobao.tddl.optimizer.BaseOptimizerTest; import com.taobao.tddl.optimizer.config.table.IndexMeta; import com.taobao.tddl.optimizer.core.ast.QueryTreeNode; import com.taobao.tddl.optimizer.core.ast.QueryTreeNode.FilterType; import com.taobao.tddl.optimizer.core.ast.query.JoinNode; import com.taobao.tddl.optimizer.core.ast.query.MergeNode; import com.taobao.tddl.optimizer.core.ast.query.QueryNode; import com.taobao.tddl.optimizer.core.ast.query.TableNode; import com.taobao.tddl.optimizer.core.expression.IFilter; import com.taobao.tddl.optimizer.core.expression.ISelectable; import com.taobao.tddl.optimizer.core.plan.query.IJoin.JoinStrategy; import com.taobao.tddl.optimizer.costbased.chooser.IndexChooser; import com.taobao.tddl.optimizer.costbased.chooser.JoinChooser; import com.taobao.tddl.optimizer.costbased.pusher.FilterPusher; import com.taobao.tddl.optimizer.utils.FilterUtils; public class JoinChooserTest extends BaseOptimizerTest { @Test public void test_TableNode转化为index_主键索引() { TableNode table = new TableNode("TABLE1"); table.query("ID = 1"); build(table); QueryTreeNode qn = table.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof TableNode); Assert.assertEquals("TABLE1.ID = 1", ((TableNode) qn).getKeyFilter().toString()); } @Test public void test_TableNode转化为index_查询字段都在索引上() { TableNode table = new TableNode("TABLE1"); table.select("ID,NAME"); table.query("NAME = 1"); build(table); QueryTreeNode qn = table.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof TableNode); Assert.assertEquals("TABLE1._NAME.NAME = 1", ((TableNode) qn).getKeyFilter().toString()); } @Test public void test_TableNode转化为index到kv的join() { // table1存在id的主键索引和name的二级索引,期望构造为index name join id TableNode table = new TableNode("TABLE1"); table.query("NAME = 1"); build(table); QueryTreeNode qn = table.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof JoinNode); Assert.assertEquals("TABLE1._NAME.NAME = 1", ((JoinNode) qn).getLeftNode().getKeyFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID = TABLE1.ID", ((JoinNode) qn).getJoinFilter().get(0).toString()); } @Test public void test_TableNode转化为index到kv_复杂条件查询() { TableNode table = new TableNode("TABLE1"); table.select("(ID + NAME) AS NEWNAME"); // 设置为函数 table.query("NAME = 1 AND ID > 3 AND SCHOOL = 1"); table.orderBy("SCHOOL", false);// 增加一个隐藏列 table.groupBy("NEWNAME"); build(table); QueryTreeNode qn = table.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof JoinNode); Assert.assertEquals("TABLE1._NAME.NAME = 1", ((JoinNode) qn).getLeftNode().getKeyFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID > 3", ((JoinNode) qn).getLeftNode().getResultFilter().toString()); Assert.assertEquals("TABLE1.SCHOOL = 1", ((JoinNode) qn).getRightNode().getResultFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID = TABLE1.ID", ((JoinNode) qn).getJoinFilter().get(0).toString()); } @Test public void test_子查询套TableNode转化为index到kv() { TableNode table = new TableNode("TABLE1"); table.query("NAME = 1"); build(table); QueryNode query = new QueryNode(table); query.build(); QueryTreeNode qn = query.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof QueryNode); Assert.assertTrue(qn.getChild() instanceof JoinNode); Assert.assertEquals("TABLE1._NAME.NAME = 1", ((JoinNode) qn.getChild()).getLeftNode().getKeyFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID = TABLE1.ID", ((JoinNode) qn.getChild()).getJoinFilter().get(0).toString()); } @Test public void test_Join左是子查询_右是TableNode_转化为最左树() { TableNode table1 = new TableNode("TABLE1"); QueryNode query = new QueryNode(table1); query.build(); TableNode table2 = new TableNode("TABLE2"); JoinNode join = table1.join(table2, "NAME", "NAME"); join.setJoinStrategy(JoinStrategy.INDEX_NEST_LOOP); join.query("TABLE1.NAME = 1 AND TABLE1.ID > 3 AND TABLE1.SCHOOL = 1");// 原本条件应该是加在join下的,这里省区推导的过程 join.select("(TABLE2.ID + TABLE2.NAME) AS NEWNAME"); // 设置为函数 join.orderBy("TABLE1.SCHOOL", false);// 增加一个隐藏列 join.groupBy("NEWNAME"); join.build(); QueryTreeNode qn = FilterPusher.optimize(join);// 先把条件推导子节点上,构建子节点join build(table1); build(table2); qn = qn.convertToJoinIfNeed(); Assert.assertTrue(qn instanceof JoinNode); Assert.assertEquals("TABLE2$_NAME.ID = TABLE2.ID", ((JoinNode) qn).getJoinFilter().get(0).toString()); Assert.assertTrue(((JoinNode) qn).getLeftNode() instanceof JoinNode); Assert.assertEquals("TABLE1.NAME = TABLE2$_NAME.NAME", ((JoinNode) ((JoinNode) qn).getLeftNode()).getJoinFilter().get(0).toString()); Assert.assertTrue(((JoinNode) ((JoinNode) qn).getLeftNode()).getLeftNode() instanceof JoinNode); JoinNode jn = (JoinNode) ((JoinNode) ((JoinNode) qn).getLeftNode()).getLeftNode(); Assert.assertEquals("TABLE1._NAME.NAME = 1", jn.getLeftNode().getKeyFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID > 3", jn.getLeftNode().getResultFilter().toString()); Assert.assertEquals("TABLE1.SCHOOL = 1", jn.getRightNode().getResultFilter().toString()); Assert.assertEquals("TABLE1._NAME.ID = TABLE1.ID", jn.getJoinFilter().get(0).toString()); } /** * c1有索引,c2无索引 */ @Test public void test_JoinStrategy选择_调整join顺序() { // table11.c1为二级索引,table10.c2不存在索引 TableNode table1 = new TableNode("TABLE11"); TableNode table2 = new TableNode("TABLE10"); QueryTreeNode qn = table1.join(table2).addJoinKeys("C1", "C2"); qn.build(); qn = FilterPusher.optimize(qn);// 先把条件推导子节点上,构建子节点join build(table1); build(table2); qn = optimize(qn, true, true, true); Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, ((JoinNode) qn).getJoinStrategy()); Assert.assertTrue(((JoinNode) qn).getLeftNode() instanceof JoinNode); Assert.assertEquals("TABLE10", ((JoinNode) ((JoinNode) qn).getLeftNode()).getLeftNode().getName()); Assert.assertEquals("TABLE11", ((JoinNode) qn).getRightNode().getName()); Assert.assertEquals("TABLE11", ((TableNode) ((JoinNode) qn).getRightNode()).getIndexUsed().getName()); } @Test public void test_JoinStrategy选择_存在条件() { // table11虽然是C2非主键列为join,但存在主键条件,使用NESTLOOP TableNode table1 = new TableNode("TABLE10"); TableNode table2 = new TableNode("TABLE11"); QueryTreeNode qn = table1.join(table2).addJoinKeys("C1", "C2"); qn.query("TABLE11.ID = 1"); qn.build(); qn = FilterPusher.optimize(qn);// 先把条件推导子节点上,构建子节点join build(table1); build(table2); qn = optimize(qn, true, true, true); Assert.assertEquals(JoinStrategy.NEST_LOOP_JOIN, ((JoinNode) qn).getJoinStrategy()); Assert.assertEquals("TABLE10", ((JoinNode) qn).getLeftNode().getName()); Assert.assertEquals("TABLE11", ((JoinNode) qn).getRightNode().getName()); Assert.assertEquals("TABLE11", ((TableNode) ((JoinNode) qn).getRightNode()).getIndexUsed().getName()); } @Test public void test_JoinStrategy选择_存在子查询_存在条件() { // table11虽然是C2非主键列为join,但存在主键条件,使用NESTLOOP TableNode table1 = new TableNode("TABLE10"); QueryNode query = new QueryNode(table1); TableNode table2 = new TableNode("TABLE11"); QueryTreeNode qn = query.join(table2).addJoinKeys("C1", "C2"); qn.query("TABLE11.ID = 1"); qn.build(); qn = FilterPusher.optimize(qn);// 先把条件推导子节点上,构建子节点join build(table1); build(table2); qn = optimize(qn, true, true, true); Assert.assertEquals(JoinStrategy.NEST_LOOP_JOIN, ((JoinNode) qn).getJoinStrategy()); Assert.assertEquals("TABLE10", ((JoinNode) qn).getLeftNode().getName()); Assert.assertEquals("TABLE11", ((JoinNode) qn).getRightNode().getName()); Assert.assertEquals("TABLE11", ((TableNode) ((JoinNode) qn).getRightNode()).getIndexUsed().getName()); } @Test public void test_存在OR条件_所有字段均有索引_构建IndexMerge() { TableNode table1 = new TableNode("TABLE10"); table1.query("ID = 1 OR C1 = 2"); table1.build(); QueryTreeNode qn = optimize(table1, true, true, true); Assert.assertTrue(qn instanceof MergeNode); Assert.assertTrue(qn.getChildren().get(0) instanceof TableNode); Assert.assertTrue(qn.getChildren().get(1) instanceof JoinNode); } @Test public void test_存在OR条件_有字段无索引_构建全表扫描() { TableNode table1 = new TableNode("TABLE10"); table1.query("ID = 1 OR C1 = 2 OR C2 = 1"); table1.build(); QueryTreeNode qn = optimize(table1, true, true, false); Assert.assertTrue(qn instanceof TableNode); // Assert.assertTrue(((TableNode) qn).isFullTableScan()); } @Test public void test_JoinStrategy选择sortmerge_outterJoin() { TableNode table1 = new TableNode("TABLE10"); TableNode table2 = new TableNode("TABLE11"); JoinNode join = table1.join(table2).setOuterJoin(); join.build(); QueryTreeNode qn = optimize(join, true, true, true); Assert.assertEquals(JoinStrategy.SORT_MERGE_JOIN, ((JoinNode) qn).getJoinStrategy()); } @Test public void test_JoinStrategy选择sortmerge_右表存在group条件() { TableNode table1 = new TableNode("TABLE1"); TableNode table2 = new TableNode("TABLE2"); JoinNode join = table1.join(table2).setLeftOuterJoin().addJoinKeys("ID", "ID").addJoinKeys("NAME", "NAME"); join.orderBy("TABLE2.ID"); join.groupBy("TABLE2.NAME").groupBy("TABLE2.ID").groupBy("TABLE2.SCHOOL"); join.build(); QueryTreeNode qn = optimize(join, true, true, true); Assert.assertEquals(JoinStrategy.SORT_MERGE_JOIN, ((JoinNode) qn).getJoinStrategy()); } @Test public void test_JoinStrategy选择sortmerge_右表存在group条件和order条件不能合并() { TableNode table1 = new TableNode("TABLE1"); TableNode table2 = new TableNode("TABLE2"); JoinNode join = table1.join(table2).setLeftOuterJoin().addJoinKeys("ID", "ID").addJoinKeys("NAME", "NAME"); join.orderBy("TABLE2.SCHOOL"); join.groupBy("TABLE2.NAME").groupBy("TABLE2.ID"); join.build(); QueryTreeNode qn = optimize(join, true, true, true); Assert.assertEquals(JoinStrategy.SORT_MERGE_JOIN, ((JoinNode) qn).getJoinStrategy()); } @Test public void test_JoinStrategy选择sortmerge_右表存在order条件() { TableNode table1 = new TableNode("TABLE1"); TableNode table2 = new TableNode("TABLE2"); JoinNode join = table1.join(table2).setLeftOuterJoin().addJoinKeys("ID", "ID").addJoinKeys("NAME", "NAME"); join.orderBy("TABLE2.ID"); join.build(); QueryTreeNode qn = optimize(join, true, true, true); Assert.assertEquals(JoinStrategy.SORT_MERGE_JOIN, ((JoinNode) qn).getJoinStrategy()); } @Test public void test_JoinStrategy不选择sortmerge_右表存在order条件() { TableNode table1 = new TableNode("TABLE1"); TableNode table2 = new TableNode("TABLE2"); JoinNode join = table1.join(table2).setLeftOuterJoin().addJoinKeys("ID", "ID").addJoinKeys("NAME", "NAME"); join.orderBy("TABLE2.SCHOOL"); // join列的顺序没法推导 join.build(); QueryTreeNode qn = optimize(join, true, true, true); Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, ((JoinNode) qn).getJoinStrategy()); } private QueryTreeNode optimize(QueryTreeNode qtn, boolean chooseIndex, boolean chooseJoin, boolean chooseIndexMerge) { Map<String, Object> extraCmd = new HashMap<String, Object>(); extraCmd.put(ExtraCmd.CHOOSE_INDEX, chooseIndex); extraCmd.put(ExtraCmd.CHOOSE_JOIN, chooseJoin); extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, chooseIndexMerge); return (QueryTreeNode) JoinChooser.optimize(qtn, extraCmd); } private void build(TableNode table) { table.build(); Map<String, Object> extraCmd = new HashMap<String, Object>(); extraCmd.put(ExtraCmd.CHOOSE_INDEX, true); IndexMeta index = IndexChooser.findBestIndex(table.getTableMeta(), new ArrayList<ISelectable>(), FilterUtils.toDNFNode(table.getWhereFilter()), table.getTableName(), extraCmd); table.useIndex(index); Map<FilterType, IFilter> result = FilterSpliter.splitByIndex(FilterUtils.toDNFNode(table.getWhereFilter()), table); table.setKeyFilter(result.get(FilterType.IndexQueryKeyFilter)); table.setIndexQueryValueFilter(result.get(FilterType.IndexQueryValueFilter)); table.setResultFilter(result.get(FilterType.ResultFilter)); } }