blob: 2a18f2b7047c175a62c326d612ae3668c129c172 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.planner;
import org.apache.doris.analysis.AggregateInfo;
import org.apache.doris.analysis.Analyzer;
import org.apache.doris.analysis.Expr;
import org.apache.doris.analysis.FunctionCallExpr;
import org.apache.doris.analysis.SelectStmt;
import org.apache.doris.analysis.SlotDescriptor;
import org.apache.doris.analysis.SlotRef;
import org.apache.doris.analysis.TableName;
import org.apache.doris.analysis.TupleDescriptor;
import org.apache.doris.catalog.AggregateType;
import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.KeysType;
import org.apache.doris.catalog.MaterializedIndexMeta;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Table;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.jmockit.Deencapsulation;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.doris.thrift.TStorageType;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.Set;
import mockit.Expectations;
import mockit.Injectable;
public class MaterializedViewSelectorTest {
@Test
public void initTest(@Injectable SelectStmt selectStmt,
@Injectable SlotDescriptor tableAColumn1Desc,
@Injectable AggregateInfo aggregateInfo,
@Injectable Table tableA,
@Injectable TupleDescriptor tableADesc,
@Injectable SlotDescriptor tableAColumn2Desc,
@Injectable SlotDescriptor tableBColumn1Desc,
@Injectable TupleDescriptor tableBDesc,
@Injectable Table tableB,
@Injectable Analyzer analyzer) {
TableName tableAName = new TableName("test", "tableA");
TableName tableBName = new TableName("test", "tableB");
SlotRef tableAColumn1 = new SlotRef(tableAName, "c1");
Deencapsulation.setField(tableAColumn1, "isAnalyzed", true);
SlotRef tableAColumn2 = new SlotRef(tableAName, "c2");
Deencapsulation.setField(tableAColumn2, "isAnalyzed", true);
SlotRef tableBColumn1 = new SlotRef(tableBName, "c1");
Deencapsulation.setField(tableBColumn1, "isAnalyzed", true);
Deencapsulation.setField(tableAColumn1, "desc", tableAColumn1Desc);
Deencapsulation.setField(tableAColumn2, "desc", tableAColumn2Desc);
Deencapsulation.setField(tableBColumn1, "desc", tableBColumn1Desc);
FunctionCallExpr tableAColumn2Sum = new FunctionCallExpr("SUM", Lists.newArrayList(tableAColumn2));
FunctionCallExpr tableBColumn1Max = new FunctionCallExpr("MAX", Lists.newArrayList(tableBColumn1));
new Expectations() {
{
selectStmt.getAggInfo();
result = aggregateInfo;
aggregateInfo.getGroupingExprs();
result = Lists.newArrayList(tableAColumn1);
tableAColumn1Desc.isMaterialized();
result = true;
tableAColumn1Desc.getColumn().getName();
result = "c1";
tableAColumn1Desc.getParent();
result = tableADesc;
tableADesc.getTable();
result = tableA;
tableA.getId();
result = 1;
aggregateInfo.getAggregateExprs();
result = Lists.newArrayList(tableAColumn2Sum, tableBColumn1Max);
tableAColumn2Sum.getChildren();
result = Lists.newArrayList(tableAColumn2);
tableBColumn1Max.getChildren();
result = Lists.newArrayList(tableBColumn1);
tableAColumn2.getColumnName();
result = "c2";
tableBColumn1.getColumnName();
result = "c1";
tableAColumn2.getTableName().getTbl();
result = "tableA";
tableBColumn1.getTableName().getTbl();
result = "tableB";
tableAColumn2Desc.getParent();
result = tableADesc;
tableBColumn1Desc.getParent();
result = tableBDesc;
tableBDesc.getTable();
result = tableB;
tableB.getId();
result = 2;
tableAColumn2Desc.isMaterialized();
result = true;
tableBColumn1Desc.isMaterialized();
result = true;
tableAColumn2Desc.getColumn().getName();
result = "c2";
tableBColumn1Desc.getColumn().getName();
result = "c1";
}
};
MaterializedViewSelector materializedViewSelector = new MaterializedViewSelector(selectStmt, analyzer);
Map<Long, Set<String>> columnNamesInPredicates =
Deencapsulation.getField(materializedViewSelector, "columnNamesInPredicates");
Assert.assertEquals(0, columnNamesInPredicates.size());
Assert.assertFalse(Deencapsulation.getField(materializedViewSelector, "isSPJQuery"));
Map<Long, Set<String>> columnNamesInGrouping =
Deencapsulation.getField(materializedViewSelector, "columnNamesInGrouping");
Assert.assertEquals(1, columnNamesInGrouping.size());
Set<String> tableAColumnNamesInGrouping = columnNamesInGrouping.get(new Long(1));
Assert.assertNotEquals(tableAColumnNamesInGrouping, null);
Assert.assertEquals(1, tableAColumnNamesInGrouping.size());
Assert.assertTrue(tableAColumnNamesInGrouping.contains("c1"));
Map<Long, Set<FunctionCallExpr>> aggregateColumnsInQuery =
Deencapsulation.getField(materializedViewSelector, "aggColumnsInQuery");
Assert.assertEquals(2, aggregateColumnsInQuery.size());
Set<FunctionCallExpr> tableAAgggregatedColumns = aggregateColumnsInQuery.get(new Long(1));
Assert.assertEquals(1, tableAAgggregatedColumns.size());
FunctionCallExpr aggregatedColumn1 = tableAAgggregatedColumns.iterator().next();
List<Expr> aggColumn1Params = aggregatedColumn1.getParams().exprs();
Assert.assertEquals(1, aggColumn1Params.size());
Assert.assertTrue(aggColumn1Params.get(0) instanceof SlotRef);
Assert.assertEquals("c2", ((SlotRef) aggColumn1Params.get(0)).getColumnName());
Assert.assertTrue("SUM".equalsIgnoreCase(aggregatedColumn1.getFnName().getFunction()));
Set<FunctionCallExpr> tableBAgggregatedColumns = aggregateColumnsInQuery.get(new Long(2));
Assert.assertEquals(1, tableBAgggregatedColumns.size());
FunctionCallExpr aggregatedColumn2 = tableBAgggregatedColumns.iterator().next();
List<Expr> aggColumn2Params = aggregatedColumn2.getParams().exprs();
Assert.assertEquals(1, aggColumn2Params.size());
Assert.assertTrue(aggColumn2Params.get(0) instanceof SlotRef);
Assert.assertEquals("c1", ((SlotRef) aggColumn2Params.get(0)).getColumnName());
Assert.assertTrue("MAX".equalsIgnoreCase(aggregatedColumn2.getFnName().getFunction()));
}
@Test
public void testCheckCompensatingPredicates(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer,
@Injectable MaterializedIndexMeta indexMeta1,
@Injectable MaterializedIndexMeta indexMeta2,
@Injectable MaterializedIndexMeta indexMeta3,
@Injectable MaterializedIndexMeta indexMeta4) {
Set<String> tableAColumnNames = Sets.newHashSet();
tableAColumnNames.add("C1");
Map<Long, MaterializedIndexMeta> candidateIndexIdToSchema = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index1Columns.add(index1Column1);
candidateIndexIdToSchema.put(new Long(1), indexMeta1);
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c1", Type.INT, false, AggregateType.NONE, true, "", "");
index2Columns.add(index2Column1);
candidateIndexIdToSchema.put(new Long(2), indexMeta2);
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("c1", Type.INT, false, AggregateType.SUM, true, "", "");
index3Columns.add(index3Column1);
candidateIndexIdToSchema.put(new Long(3), indexMeta3);
List<Column> index4Columns = Lists.newArrayList();
Column index4Column2 = new Column("c2", Type.INT, true, null, true, "", "");
index4Columns.add(index4Column2);
candidateIndexIdToSchema.put(new Long(4), indexMeta4);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
indexMeta1.getSchema();
result = index1Columns;
indexMeta2.getSchema();
result = index2Columns;
indexMeta3.getSchema();
result = index3Columns;
indexMeta4.getSchema();
result = index4Columns;
}
};
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
Deencapsulation.invoke(selector, "checkCompensatingPredicates", tableAColumnNames, candidateIndexIdToSchema);
Assert.assertEquals(2, candidateIndexIdToSchema.size());
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(1)));
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(2)));
}
@Test
public void testCheckGrouping(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer,
@Injectable MaterializedIndexMeta indexMeta1,
@Injectable MaterializedIndexMeta indexMeta2,
@Injectable MaterializedIndexMeta indexMeta3) {
Set<String> tableAColumnNames = Sets.newHashSet();
tableAColumnNames.add("C1");
Map<Long, MaterializedIndexMeta> candidateIndexIdToSchema = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index1Columns.add(index1Column1);
candidateIndexIdToSchema.put(new Long(1), indexMeta1);
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index2Columns.add(index2Column1);
Column index2Column2 = new Column("c2", Type.INT, false, AggregateType.SUM, true, "", "");
index2Columns.add(index2Column2);
candidateIndexIdToSchema.put(new Long(2), indexMeta2);
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index3Columns.add(index3Column1);
Column index3Column2 = new Column("c1", Type.INT, false, AggregateType.SUM, true, "", "");
index3Columns.add(index3Column2);
candidateIndexIdToSchema.put(new Long(3), indexMeta3);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
indexMeta1.getSchema();
result = index1Columns;
indexMeta1.getKeysType();
result = KeysType.DUP_KEYS;
indexMeta2.getSchema();
result = index2Columns;
indexMeta3.getSchema();
result = index3Columns;
}
};
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
Deencapsulation.setField(selector, "isSPJQuery", false);
Deencapsulation.invoke(selector, "checkGrouping", tableAColumnNames, candidateIndexIdToSchema);
Assert.assertEquals(2, candidateIndexIdToSchema.size());
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(1)));
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(2)));
}
@Test
public void testCheckAggregationFunction(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer,
@Injectable MaterializedIndexMeta indexMeta1,
@Injectable MaterializedIndexMeta indexMeta2,
@Injectable MaterializedIndexMeta indexMeta3) {
Map<Long, MaterializedIndexMeta> candidateIndexIdToSchema = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index1Columns.add(index1Column1);
candidateIndexIdToSchema.put(new Long(1), indexMeta1);
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index2Columns.add(index2Column1);
Column index2Column2 = new Column("c2", Type.INT, false, AggregateType.SUM, true, "", "");
index2Columns.add(index2Column2);
candidateIndexIdToSchema.put(new Long(2), indexMeta2);
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index3Columns.add(index3Column1);
Column index3Column2 = new Column("c1", Type.INT, false, AggregateType.SUM, true, "", "");
index3Columns.add(index3Column2);
candidateIndexIdToSchema.put(new Long(3), indexMeta3);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
indexMeta1.getSchema();
result = index1Columns;
indexMeta1.getKeysType();
result = KeysType.DUP_KEYS;
indexMeta2.getSchema();
result = index2Columns;
indexMeta3.getSchema();
result = index3Columns;
}
};
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
TableName tableName = new TableName("db1", "table1");
SlotRef slotRef = new SlotRef(tableName, "C1");
FunctionCallExpr functionCallExpr = new FunctionCallExpr("sum", Lists.newArrayList(slotRef));
Set<FunctionCallExpr> aggregatedColumnsInQueryOutput = Sets.newHashSet();
aggregatedColumnsInQueryOutput.add(functionCallExpr);
Deencapsulation.setField(selector, "isSPJQuery", false);
Deencapsulation.invoke(selector, "checkAggregationFunction", aggregatedColumnsInQueryOutput,
candidateIndexIdToSchema);
Assert.assertEquals(2, candidateIndexIdToSchema.size());
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(1)));
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(3)));
}
@Test
public void testCheckOutputColumns(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer,
@Injectable MaterializedIndexMeta indexMeta1,
@Injectable MaterializedIndexMeta indexMeta2,
@Injectable MaterializedIndexMeta indexMeta3) {
Map<Long, MaterializedIndexMeta> candidateIndexIdToSchema = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index1Columns.add(index1Column1);
candidateIndexIdToSchema.put(new Long(1), indexMeta1);
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index2Columns.add(index2Column1);
Column index2Column2 = new Column("c2", Type.INT, false, AggregateType.NONE, true, "", "");
index2Columns.add(index2Column2);
candidateIndexIdToSchema.put(new Long(2), indexMeta2);
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("C2", Type.INT, true, null, true, "", "");
index3Columns.add(index3Column1);
Column index3Column2 = new Column("c1", Type.INT, false, AggregateType.SUM, true, "", "");
index3Columns.add(index3Column2);
candidateIndexIdToSchema.put(new Long(3), indexMeta3);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
indexMeta1.getSchema();
result = index1Columns;
indexMeta2.getSchema();
result = index2Columns;
indexMeta3.getSchema();
result = index3Columns;
}
};
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
Set<String> columnNamesInQueryOutput = Sets.newHashSet();
columnNamesInQueryOutput.add("c1");
columnNamesInQueryOutput.add("c2");
Deencapsulation.invoke(selector, "checkOutputColumns", columnNamesInQueryOutput,
candidateIndexIdToSchema);
Assert.assertEquals(2, candidateIndexIdToSchema.size());
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(2)));
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(3)));
}
@Test
public void testCompensateIndex(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer,
@Injectable OlapTable table) {
Map<Long, MaterializedIndexMeta> candidateIndexIdToSchema = Maps.newHashMap();
Map<Long, MaterializedIndexMeta> allVisibleIndexes = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c2", Type.INT, true, AggregateType.SUM, true, "", "");
index1Columns.add(index1Column1);
allVisibleIndexes.put(new Long(1), new MaterializedIndexMeta(
0, index1Columns, 0, 0, (short) 0, TStorageType.COLUMN, KeysType.AGG_KEYS, null));
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index2Columns.add(index2Column1);
Column index2Column2 = new Column("c2", Type.INT, false, AggregateType.SUM, true, "", "");
index2Columns.add(index2Column2);
allVisibleIndexes.put(new Long(2), new MaterializedIndexMeta(
0, index2Columns, 0, 0, (short) 0, TStorageType.COLUMN, KeysType.AGG_KEYS, null));
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index3Columns.add(index3Column1);
Column index3Column2 = new Column("c3", Type.INT, false, AggregateType.SUM, true, "", "");
index3Columns.add(index3Column2);
allVisibleIndexes.put(new Long(3), new MaterializedIndexMeta(
0, index3Columns, 0, 0, (short) 0, TStorageType.COLUMN, KeysType.AGG_KEYS, null));
List<Column> keyColumns = Lists.newArrayList();
keyColumns.add(index2Column1);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
table.getBaseIndexId();
result = -1L;
table.getKeyColumnsByIndexId(-1L);
result = keyColumns;
table.getKeyColumnsByIndexId(1L);
result = Lists.newArrayList();
table.getKeyColumnsByIndexId(2L);
result = keyColumns;
table.getKeyColumnsByIndexId(3L);
result = keyColumns;
}
};
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
Deencapsulation.invoke(selector, "compensateCandidateIndex", candidateIndexIdToSchema,
allVisibleIndexes, table);
Assert.assertEquals(2, candidateIndexIdToSchema.size());
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(2)));
Assert.assertTrue(candidateIndexIdToSchema.keySet().contains(new Long(3)));
}
@Test
public void testSelectBestRowCountIndex(@Injectable SelectStmt selectStmt, @Injectable Analyzer analyzer) {
Map<Long, List<Column>> candidateIndexIdToSchema = Maps.newHashMap();
List<Column> index1Columns = Lists.newArrayList();
Column index1Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index1Columns.add(index1Column1);
Column index1Column2 = new Column("c2", Type.INT, false, AggregateType.NONE, true, "", "");
index1Columns.add(index1Column2);
Column index1Column3 = new Column("c3", Type.INT, false, AggregateType.NONE, true, "", "");
index1Columns.add(index1Column3);
candidateIndexIdToSchema.put(new Long(1), index1Columns);
List<Column> index2Columns = Lists.newArrayList();
Column index2Column1 = new Column("c2", Type.INT, true, null, true, "", "");
index2Columns.add(index2Column1);
Column index2Column2 = new Column("c1", Type.INT, false, AggregateType.NONE, true, "", "");
index2Columns.add(index2Column2);
Column index2Column3 = new Column("c3", Type.INT, false, AggregateType.NONE, true, "", "");
index2Columns.add(index2Column3);
candidateIndexIdToSchema.put(new Long(2), index2Columns);
List<Column> index3Columns = Lists.newArrayList();
Column index3Column1 = new Column("c1", Type.INT, true, null, true, "", "");
index3Columns.add(index3Column1);
Column index3Column2 = new Column("c3", Type.INT, false, AggregateType.NONE, true, "", "");
index3Columns.add(index3Column2);
Column index3Column3 = new Column("c2", Type.INT, false, AggregateType.NONE, true, "", "");
index3Columns.add(index3Column3);
candidateIndexIdToSchema.put(new Long(3), index3Columns);
new Expectations() {
{
selectStmt.getAggInfo();
result = null;
}
};
Set<String> equivalenceColumns = Sets.newHashSet();
equivalenceColumns.add("c1");
equivalenceColumns.add("c2");
Set<String> unequivalenceColumns = Sets.newHashSet();
unequivalenceColumns.add("c3");
MaterializedViewSelector selector = new MaterializedViewSelector(selectStmt, analyzer);
Set<Long> result = Deencapsulation.invoke(selector, "matchBestPrefixIndex", candidateIndexIdToSchema,
equivalenceColumns, unequivalenceColumns);
Assert.assertEquals(2, result.size());
Assert.assertTrue(result.contains(new Long(1)));
Assert.assertTrue(result.contains(new Long(2)));
}
}