/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.supportregression.epl;
import com.espertech.esper.epl.expression.core.ExprNodeUtility;
import com.espertech.esper.epl.join.plan.*;
import org.junit.Assert;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
public class SupportQueryPlanIndexHelper {
public static String getIndexedExpressions(Map<TableLookupIndexReqKey, QueryPlanIndexItem> entries) {
StringWriter buf = new StringWriter();
for (Map.Entry<TableLookupIndexReqKey, QueryPlanIndexItem> entry : entries.entrySet()) {
buf.append(Arrays.toString(entry.getValue().getIndexProps()));
}
return buf.toString();
}
public static void compareQueryPlans(QueryPlan expectedPlan, QueryPlan actualPlan) {
Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping = new HashMap<TableLookupIndexReqKey, TableLookupIndexReqKey>();
compareIndexes(expectedPlan.getIndexSpecs(), actualPlan.getIndexSpecs(), indexNameMapping);
compareExecNodeSpecs(expectedPlan.getExecNodeSpecs(), actualPlan.getExecNodeSpecs(), indexNameMapping);
}
private static void compareIndexes(QueryPlanIndex[] expected, QueryPlanIndex[] actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
Assert.assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
compareIndex(i, expected[i], actual[i], indexNameMapping);
}
}
private static void compareIndex(int streamNum, QueryPlanIndex expected, QueryPlanIndex actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
Map<TableLookupIndexReqKey, QueryPlanIndexItem> actualItems = actual.getItems();
Map<TableLookupIndexReqKey, QueryPlanIndexItem> expectedItems = expected.getItems();
Assert.assertEquals("Number of indexes mismatch for stream " + streamNum, expectedItems.size(), actualItems.size());
Iterator<Map.Entry<TableLookupIndexReqKey, QueryPlanIndexItem>> itActual = actualItems.entrySet().iterator();
Iterator<Map.Entry<TableLookupIndexReqKey, QueryPlanIndexItem>> itExpected = expectedItems.entrySet().iterator();
int count = 0;
for (;itActual.hasNext();) {
Map.Entry<TableLookupIndexReqKey, QueryPlanIndexItem> actualItem = itActual.next();
Map.Entry<TableLookupIndexReqKey, QueryPlanIndexItem> expectedItem = itExpected.next();
SupportQueryPlanIndexHelper.compareIndexItem(streamNum, count, expectedItem.getValue(), actualItem.getValue());
count++;
indexNameMapping.put(actualItem.getKey(), expectedItem.getKey());
}
}
private static void compareExecNodeSpecs(QueryPlanNode[] expected, QueryPlanNode[] actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
Assert.assertEquals(expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
compareExecNodeSpec(i, expected[i], actual[i], indexNameMapping);
}
}
private static void compareExecNodeSpec(int streamNum, QueryPlanNode expected, QueryPlanNode actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
if (actual instanceof QueryPlanNodeNoOp && expected == null) {
}
else if (actual instanceof TableLookupNode && expected instanceof TableLookupNode) {
compareTableLookup(streamNum, (TableLookupNode) expected, (TableLookupNode) actual, indexNameMapping);
}
else if (actual instanceof TableOuterLookupNode && expected instanceof TableOuterLookupNode) {
compareTableLookupOuter(streamNum, (TableOuterLookupNode) expected, (TableOuterLookupNode) actual, indexNameMapping);
}
else if (actual instanceof LookupInstructionQueryPlanNode && expected instanceof LookupInstructionQueryPlanNode) {
compareInstruction(streamNum, (LookupInstructionQueryPlanNode) expected, (LookupInstructionQueryPlanNode) actual, indexNameMapping);
}
else {
Assert.fail("Failed to compare plan node for stream " + streamNum + ", unhandled plan " + actual.getClass().getName());
}
}
private static void compareInstruction(int streamNum, LookupInstructionQueryPlanNode expected, LookupInstructionQueryPlanNode actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
assertEquals(expected.getRootStream(), actual.getRootStream());
assertEquals(expected.getRootStreamName(), actual.getRootStreamName());
assertEquals(expected.getLookupInstructions().size(), actual.getLookupInstructions().size());
for (int i = 0; i < expected.getLookupInstructions().size(); i++) {
compareInstructionDetail(streamNum, i, expected.getLookupInstructions().get(i), actual.getLookupInstructions().get(i), indexNameMapping);
}
}
private static void compareInstructionDetail(int streamNum, int numInstruction, LookupInstructionPlan expected, LookupInstructionPlan actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
assertEquals(expected.getLookupPlans().length, actual.getLookupPlans().length);
for (int i = 0; i < expected.getLookupPlans().length; i++) {
compareTableLookupPlan(streamNum, numInstruction, expected.getLookupPlans()[i], actual.getLookupPlans()[i], indexNameMapping);
}
}
private static void compareTableLookupOuter(int streamNum, TableOuterLookupNode expected, TableOuterLookupNode actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
compareTableLookupPlan(streamNum, 0, expected.getLookupStrategySpec(), actual.getLookupStrategySpec(), indexNameMapping);
}
private static void compareTableLookup(int streamNum, TableLookupNode expected, TableLookupNode actual, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
compareTableLookupPlan(streamNum, 0, expected.getTableLookupPlan(), actual.getTableLookupPlan(), indexNameMapping);
}
private static void compareTableLookupPlan(int streamNum, int numInstruction, TableLookupPlan expectedPlan, TableLookupPlan actualPlan, Map<TableLookupIndexReqKey, TableLookupIndexReqKey> indexNameMapping) {
String message = "Failed at stream " + streamNum + " and instruction " + numInstruction;
Assert.assertEquals(message, expectedPlan.getIndexedStream(), actualPlan.getIndexedStream());
Assert.assertEquals(message, expectedPlan.getLookupStream(), actualPlan.getLookupStream());
Assert.assertEquals(message, expectedPlan.getClass().getSimpleName(), actualPlan.getClass().getSimpleName());
// assert index mapping
Assert.assertEquals(message, expectedPlan.getIndexNum().length, actualPlan.getIndexNum().length);
for (int i = 0; i < expectedPlan.getIndexNum().length; i++) {
TableLookupIndexReqKey expectedIndexKey = expectedPlan.getIndexNum()[i];
TableLookupIndexReqKey actualIndexKey = actualPlan.getIndexNum()[i];
Assert.assertEquals(message, expectedIndexKey, indexNameMapping.get(actualIndexKey));
}
if (expectedPlan instanceof FullTableScanLookupPlan && actualPlan instanceof FullTableScanLookupPlan) {
}
else if (expectedPlan instanceof IndexedTableLookupPlanSingle && actualPlan instanceof IndexedTableLookupPlanSingle) {
IndexedTableLookupPlanSingle singleActual = (IndexedTableLookupPlanSingle) actualPlan;
IndexedTableLookupPlanSingle singleExpected = (IndexedTableLookupPlanSingle) expectedPlan;
compareIndexDesc(singleExpected.getKeyDescriptor(), singleActual.getKeyDescriptor());
}
else if (expectedPlan instanceof InKeywordTableLookupPlanMultiIdx && actualPlan instanceof InKeywordTableLookupPlanMultiIdx) {
InKeywordTableLookupPlanMultiIdx inExpected = (InKeywordTableLookupPlanMultiIdx) expectedPlan;
InKeywordTableLookupPlanMultiIdx inActual = (InKeywordTableLookupPlanMultiIdx) actualPlan;
assertTrue(ExprNodeUtility.deepEquals(inExpected.getKeyExpr(), inActual.getKeyExpr(), false));
}
else if (expectedPlan instanceof InKeywordTableLookupPlanSingleIdx && actualPlan instanceof InKeywordTableLookupPlanSingleIdx) {
InKeywordTableLookupPlanSingleIdx inExpected = (InKeywordTableLookupPlanSingleIdx) expectedPlan;
InKeywordTableLookupPlanSingleIdx inActual = (InKeywordTableLookupPlanSingleIdx) actualPlan;
assertTrue(ExprNodeUtility.deepEquals(inExpected.getExpressions(), inActual.getExpressions(), false));
}
else if (expectedPlan instanceof SortedTableLookupPlan && actualPlan instanceof SortedTableLookupPlan) {
SortedTableLookupPlan inExpected = (SortedTableLookupPlan) expectedPlan;
SortedTableLookupPlan inActual = (SortedTableLookupPlan) actualPlan;
assertEquals(inExpected.getLookupStream(), inActual.getLookupStream());
assertTrue(ExprNodeUtility.deepEquals(inExpected.getRangeKeyPair().getExpressions(), inActual.getRangeKeyPair().getExpressions(), false));
}
else {
Assert.fail("Failed to compare plan for stream " + streamNum + ", found type " + actualPlan.getClass());
}
}
private static void compareIndexDesc(TableLookupKeyDesc expected, TableLookupKeyDesc actual) {
assertEquals(expected.getHashes().size(), actual.getHashes().size());
for (int i = 0; i < expected.getHashes().size(); i++) {
compareIndexDescHash(expected.getHashes().get(i), actual.getHashes().get(i));
}
assertEquals(expected.getRanges().size(), actual.getRanges().size());
for (int i = 0; i < expected.getRanges().size(); i++) {
compareIndexDescRange(expected.getRanges().get(i), actual.getRanges().get(i));
}
}
private static void compareIndexDescRange(QueryGraphValueEntryRange expected, QueryGraphValueEntryRange actual) {
assertEquals(expected.toQueryPlan(), actual.toQueryPlan());
}
private static void compareIndexDescHash(QueryGraphValueEntryHashKeyed expected, QueryGraphValueEntryHashKeyed actual) {
assertEquals(expected.toQueryPlan(), actual.toQueryPlan());
}
private static void compareIndexItem(int stream, int num, QueryPlanIndexItem expectedIndex, QueryPlanIndexItem actualIndex) {
if (!expectedIndex.equalsCompareSortedProps(actualIndex)) {
Assert.fail("At stream " + stream + " index " + num + "\nExpected:\n" + expectedIndex + "\n" +
"Received:\n" + actualIndex + "\n");
}
}
}