package com.taobao.tddl.optimizer.costbased;
import java.util.HashMap;
import java.util.Map;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import com.taobao.tddl.common.model.ExtraCmd;
import com.taobao.tddl.optimizer.BaseOptimizerTest;
import com.taobao.tddl.optimizer.core.ast.QueryTreeNode;
import com.taobao.tddl.optimizer.core.ast.query.JoinNode;
import com.taobao.tddl.optimizer.core.ast.query.TableNode;
import com.taobao.tddl.optimizer.core.plan.IDataNodeExecutor;
import com.taobao.tddl.optimizer.core.plan.IQueryTree;
import com.taobao.tddl.optimizer.core.plan.query.IJoin;
import com.taobao.tddl.optimizer.core.plan.query.IJoin.JoinStrategy;
import com.taobao.tddl.optimizer.core.plan.query.IMerge;
import com.taobao.tddl.optimizer.core.plan.query.IParallelizableQueryTree.QUERY_CONCURRENCY;
import com.taobao.tddl.optimizer.core.plan.query.IQuery;
import com.taobao.tddl.optimizer.utils.OptimizerUtils;
/**
* 整个优化器的集成测试,主要是一些query
*/
public class OptimizerTest extends BaseOptimizerTest {
private static Map<String, Object> extraCmd = new HashMap<String, Object>();
@BeforeClass
public static void setUp() {
extraCmd.put(ExtraCmd.CHOOSE_INDEX, true);
extraCmd.put(ExtraCmd.CHOOSE_JOIN, false);
extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, false);
extraCmd.put(ExtraCmd.MERGE_EXPAND, false);
extraCmd.put(ExtraCmd.JOIN_MERGE_JOIN_JUDGE_BY_RULE, true);
}
@Test
public void test_单表查询_无条件() {
TableNode table = new TableNode("TABLE1");
QueryTreeNode qn = table;
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(qn, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.SEQUENTIAL, ((IMerge) qc).getQueryConcurrency());// 串行
IDataNodeExecutor dne = ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(dne instanceof IQuery);
IQuery query = (IQuery) dne;
Assert.assertEquals(null, query.getKeyFilter());
Assert.assertEquals(null, query.getValueFilter());
}
// 单表主键查询
// ID为主键,同时在ID上存在索引
// 直接查询KV ID->data
// keyFilter为ID=1
@Test
public void test_单表查询_主键条件() {
TableNode table = new TableNode("TABLE1");
table.query("ID=1 AND ID<40");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
Assert.assertTrue(qc instanceof IQuery);
Assert.assertEquals("TABLE1.ID = 1", ((IQuery) qc).getKeyFilter().toString());
Assert.assertEquals(null, ((IQuery) qc).getValueFilter());
}
// 单表主键查询
// ID为主键,同时在ID上存在索引
// 因为!=不能使用主键索引
// valueFilter为ID!=1
@Test
public void test_单表查询_主键条件_不等于只能是valueFilter() {
TableNode table = new TableNode("TABLE1");
table.query("ID != 1");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.CONCURRENT, ((IMerge) qc).getQueryConcurrency());// 并行
IDataNodeExecutor dne = ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(dne instanceof IQuery);
IQuery query = (IQuery) dne;
Assert.assertEquals(null, query.getKeyFilter());
Assert.assertEquals("TABLE1.ID != 1", query.getValueFilter().toString());
}
// 单表非主键索引查询
// NAME上存在索引
// 会生成一个Join节点
// 左边通过NAME索引找到满足条件的PK,keyFilter应该为NAME=1
// 与pk->data Join
// Join类型为IndexNestLoop
@Test
public void test_单表查询_value条件() {
TableNode table = new TableNode("TABLE1");
table.query("NAME = 1");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.CONCURRENT, ((IMerge) qc).getQueryConcurrency());// 并行
IDataNodeExecutor dne = ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(dne instanceof IJoin);
IJoin join = (IJoin) dne;
IQuery left = (IQuery) join.getLeftNode();
Assert.assertEquals("TABLE1._NAME.NAME = 1", left.getKeyFilter().toString());
}
// 单表非主键无索引查询
// SCHOOL上不存在索引
// 所以会执行全表扫描
// 只会生成一个IQuery
// SCHOOL=1作为valueFilter
@Test
public void test_单表查询_非任何索引条件() {
TableNode table = new TableNode("TABLE1");
table.query("SCHOOL = 1");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.CONCURRENT, ((IMerge) qc).getQueryConcurrency());// 并行
IDataNodeExecutor dne = ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(dne instanceof IQuery);
IQuery query = (IQuery) dne;
Assert.assertEquals("TABLE1.SCHOOL = 1", query.getValueFilter().toString());
}
// 单表or查询
// 查询条件由or连接,
// 由于NAME和ID上存在索引,所以会生成两个子查询
// or的两边分别作为子查询的keyFilter
// 由于NAME=2323的子查询为非主键索引查询
// 所以此处会生成一个join节点
// 最后一个merge节点用于合并子查询的结果
@Test
public void test_单表查询_OR条件_1() {
TableNode table = new TableNode("TABLE1");
table.query("NAME = 2323 OR ID=1");
extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, true);
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, false);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).isUnion());// 是union查询
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IQuery);
IQuery query = (IQuery) ((IMerge) qc).getSubNode().get(0);
Assert.assertEquals("TABLE1.ID = 1", query.getKeyFilter().toString());
Assert.assertTrue(((IMerge) qc).getSubNode().get(1) instanceof IMerge);
Assert.assertTrue(((IMerge) ((IMerge) qc).getSubNode().get(1)).getSubNode().get(0) instanceof IJoin);
IJoin join = (IJoin) ((IMerge) ((IMerge) qc).getSubNode().get(1)).getSubNode().get(0);
Assert.assertEquals("TABLE1._NAME.NAME = 2323", ((IQuery) join.getLeftNode()).getKeyFilter().toString());
}
// 单表复杂查询条件
// SCHOOL=1 AND (ID=4 OR ID=3)
// 应该展开为
// (SCHOOL=1 AND ID=4) OR (SCHOOL=1 AND ID=3)
@Test
public void test_单表查询_复杂条件展开() {
TableNode table = new TableNode("TABLE1");
table.query("SCHOOL=1 AND (ID=4 OR ID=3)");
extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, true);
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(table, null, extraCmd);
extraCmd.put(ExtraCmd.CHOOSE_INDEX_MERGE, false);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).isUnion());// 是union查询
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IQuery);
Assert.assertTrue(((IMerge) qc).getSubNode().get(1) instanceof IQuery);
IQuery query1 = (IQuery) ((IMerge) qc).getSubNode().get(0);
Assert.assertEquals("TABLE1.ID = 4", query1.getKeyFilter().toString());
Assert.assertEquals("TABLE1.SCHOOL = 1", query1.getValueFilter().toString());
IQuery query2 = (IQuery) ((IMerge) qc).getSubNode().get(1);
Assert.assertEquals("TABLE1.ID = 3", query2.getKeyFilter().toString());
Assert.assertEquals("TABLE1.SCHOOL = 1", query2.getValueFilter().toString());
}
// 两表Join查询,右表连接键为主键,右表为主键查询
// 开启了join merge join
// 右表为主键查询的情况下,Join策略应该选择IndexNestLoop
@Test
public void test_两表Join_主键() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "ID", "ID");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
// 应该是join merge join
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IJoin);
IJoin subJoin = (IJoin) ((IMerge) qc).getSubNode().get(0);
Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, subJoin.getJoinStrategy());
}
// 两表Join查询,右表连接键为主键,右表为主键查询
// 开启了join merge join
// 右表虽然为二级索引的查询,但Join列不是索引列,应该选择NestLoop
// 会是一个table1 join ( table2 index join table2 key )的多级join
@Test
public void test_两表Join_主键_存在二级索引条件() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "ID", "ID");
join.query("TABLE2.NAME = 1");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IJoin);
IJoin subJoin = (IJoin) ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(subJoin.getRightNode() instanceof IJoin);
Assert.assertEquals(JoinStrategy.NEST_LOOP_JOIN, subJoin.getJoinStrategy());
}
// 两表Join查询,右表连接键为主键,右表为主键查询
// 开启了join merge join
// 右表主键索引的查询,Join列也索引列,应该选择IndexNestLoop
// 会是一个(table1 join table2 index ) join table2 key 的多级join
@Test
public void test_两表Join_主键索引_存在主键索引条件() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "ID", "ID");
join.query("TABLE2.ID IN (1,2)");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IJoin);
IJoin subJoin = (IJoin) ((IMerge) qc).getSubNode().get(0);
Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, subJoin.getJoinStrategy());
}
// 两表Join查询,右表连接键为主键,右表为二级索引查询
// 开启了join merge join
// 右表二级索引的查询,Join列也是二级索引索引,应该选择NestLoop
// 会是一个(table1 index join table1 index ) join (table2 index join table2
// key)的多级join
@Test
public void test_两表Join_二级索引_存在二级索引条件() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "NAME", "NAME");
join.query("TABLE2.NAME = 1");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IJoin);
Assert.assertTrue(((IJoin) qc).getLeftNode() instanceof IJoin);
Assert.assertTrue(((IJoin) qc).getRightNode() instanceof IMerge);
Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, ((IJoin) qc).getJoinStrategy());
IJoin subJoin = (IJoin) ((IJoin) qc).getLeftNode();
Assert.assertTrue(((IJoin) subJoin).getLeftNode() instanceof IMerge);
Assert.assertTrue(((IJoin) subJoin).getRightNode() instanceof IMerge);
Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, subJoin.getJoinStrategy());
}
@Test
public void test_三表Join_主键索引_存在主键索引条件() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "TABLE1.ID", "TABLE2.ID");
join = join.join("TABLE3", "TABLE1.ID", "TABLE3.ID");
join.query("TABLE3.ID IN (1,2)");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertTrue(((IMerge) qc).getSubNode().get(0) instanceof IJoin);
IJoin subJoin = (IJoin) ((IMerge) qc).getSubNode().get(0);
Assert.assertTrue(subJoin.getLeftNode() instanceof IJoin);
Assert.assertEquals(JoinStrategy.INDEX_NEST_LOOP, subJoin.getJoinStrategy());
}
@Test
public void test_两表join_orderby_groupby_limit条件() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "ID", "ID");
join.select(OptimizerUtils.createColumnFromString("TABLE1.ID AS JID"),
OptimizerUtils.createColumnFromString("CONCAT(TABLE1.NAME,TABLE1.SCHOOL) AS JNAME"));
join.orderBy("JID");
join.groupBy("JNAME");
join.having("COUNT(JID) > 0");
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.CONCURRENT, ((IMerge) qc).getQueryConcurrency());// 串行
}
@Test
public void test_两表join_单独limit条件_不做并行() {
TableNode table = new TableNode("TABLE1");
JoinNode join = table.join("TABLE2", "ID", "ID");
join.select(OptimizerUtils.createColumnFromString("TABLE1.ID AS JID"),
OptimizerUtils.createColumnFromString("CONCAT(TABLE1.NAME,TABLE1.SCHOOL) AS JNAME"));
join.limit(10, 20);
IQueryTree qc = (IQueryTree) optimizer.optimizeAndAssignment(join, null, extraCmd);
Assert.assertTrue(qc instanceof IMerge);
Assert.assertEquals(QUERY_CONCURRENCY.SEQUENTIAL, ((IMerge) qc).getQueryConcurrency());// 串行
IJoin jn = (IJoin) ((IMerge) qc).getSubNode().get(0);
Assert.assertEquals("0", jn.getLimitFrom().toString());
Assert.assertEquals("30", jn.getLimitTo().toString());
}
}