/* * 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. */ /* * StructSetOrResultsSet.java Utlity Class : Can be used to compare the results (StructSet OR * ResultsSet) under the scenario without/with Index Usage. Created on June 13, 2005, 11:16 AM */ package org.apache.geode.cache.query.functional; import static org.junit.Assert.*; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.apache.geode.LogWriter; import org.apache.geode.cache.query.CacheUtils; import org.apache.geode.cache.query.FunctionDomainException; import org.apache.geode.cache.query.NameResolutionException; import org.apache.geode.cache.query.Query; import org.apache.geode.cache.query.QueryInvocationTargetException; import org.apache.geode.cache.query.SelectResults; import org.apache.geode.cache.query.Struct; import org.apache.geode.cache.query.TypeMismatchException; import org.apache.geode.cache.query.internal.CompiledGroupBySelect; import org.apache.geode.cache.query.internal.CompiledSelect; import org.apache.geode.cache.query.internal.CompiledSortCriterion; import org.apache.geode.cache.query.internal.CompiledValue; import org.apache.geode.cache.query.internal.DefaultQuery; import org.apache.geode.cache.query.internal.ExecutionContext; import org.apache.geode.cache.query.internal.OrderByComparator; import org.apache.geode.cache.query.internal.QueryObserverAdapter; import org.apache.geode.cache.query.internal.QueryObserverHolder; import org.apache.geode.cache.query.types.ObjectType; import org.apache.geode.internal.util.ArrayUtils; /** * Used by these tests: * * <li/>EquiJoinIntegrationTest * <li/>IUMRCompositeIteratorJUnitTest * <li/>IUMRMultiIndexesMultiRegionJUnitTest * <li/>IUMRShuffleIteratorsJUnitTest * <li/>IUMRSingleRegionJUnitTest * <li/>IndexCreationJUnitTest * <li/>IndexHintJUnitTest * <li/>IndexMaintainceJUnitTest * <li/>IndexUseJUnitTest * <li/>IndexedMergeEquiJoinScenariosJUnitTest * <li/>MultiRegionIndexUsageJUnitTest * <li/>NonDistinctOrderByPartitionedJUnitTest * <li/>NonDistinctOrderByReplicatedJUnitTest * <li/>NonDistinctOrderByTestImplementation * <li/>OrderByPartitionedJUnitTest * <li/>OrderByReplicatedJUnitTest * <li/>OrderByTestImplementation * <li/>QueryIndexUsingXMLDUnitTest * <li/>QueryREUpdateInProgressJUnitTest * <li/>QueryUsingFunctionContextDUnitTest * <li/>QueryUsingPoolDUnitTest * <li/>TestNewFunctionSSorRSIntegrationTest * * Also used by: * * <li/>GroupByTestImpl * <li/>PdxGroupByTestImpl * <li/>PRQueryDUnitHelper */ public class StructSetOrResultsSet { public void CompareQueryResultsWithoutAndWithIndexes(Object[][] r, int len, String queries[]) { CompareQueryResultsWithoutAndWithIndexes(r, len, false, queries); } /** Creates a new instance of StructSetOrResultsSet */ public void CompareQueryResultsWithoutAndWithIndexes(Object[][] r, int len, boolean checkOrder, String queries[]) { Collection coll1 = null; Collection coll2 = null; for (int j = 0; j < len; j++) { checkSelectResultTypes((SelectResults) r[j][0], (SelectResults) r[j][1], queries[j]); checkResultSizes((SelectResults) r[j][0], (SelectResults) r[j][1], queries[j]); if (checkOrder) { coll2 = (((SelectResults) r[j][1]).asList()); coll1 = (((SelectResults) r[j][0]).asList()); } else { coll2 = (((SelectResults) r[j][1]).asSet()); coll1 = (((SelectResults) r[j][0]).asSet()); } compareResults(coll1, coll2, queries[j], checkOrder); } } public void compareExternallySortedQueriesWithOrderBy(String[] queries, Object[][] baseResults) throws Exception { for (int i = 0; i < queries.length; i++) { Query q = null; try { String query = queries[i]; int indexOfOrderBy = query.indexOf("order "); query = query.substring(0, indexOfOrderBy); q = CacheUtils.getQueryService().newQuery(query); CacheUtils.getLogger().info("Executing query: " + query); baseResults[i][1] = q.execute(); int unorderedResultsSize = ((SelectResults) baseResults[i][1]).size(); if (unorderedResultsSize == 0) { fail( "The test results size is 0 , it possibly is not validating anything. rewrite the test"); } Wrapper wrapper = getOrderByComparatorAndLimitForQuery(queries[i], unorderedResultsSize); if (wrapper.validationLevel != ValidationLevel.NONE) { Object[] externallySorted = ((SelectResults) baseResults[i][1]).toArray(); if (wrapper.validationLevel != ValidationLevel.MATCH_ONLY) { Arrays.sort(externallySorted, wrapper.obc); } if (wrapper.limit != -1) { if (externallySorted.length > wrapper.limit) { Object[] newExternallySorted = new Object[wrapper.limit]; System.arraycopy(externallySorted, 0, newExternallySorted, 0, wrapper.limit); externallySorted = newExternallySorted; } } StructSetOrResultsSet ssOrrs1 = new StructSetOrResultsSet(); ssOrrs1.compareQueryResultsWithExternallySortedResults((SelectResults) baseResults[i][0], externallySorted, queries[i], wrapper); } } catch (Exception e) { e.printStackTrace(); throw new AssertionError( "query with index=" + i + " has failed. failed query=" + queries[i], e); } } } private void compareQueryResultsWithExternallySortedResults(SelectResults sr, Object[] externallySorted, String query, Wrapper wrapper) { if (sr.size() == externallySorted.length) { CacheUtils.log("Both SelectResults are of Same Size i.e. Size= " + sr.size()); } else { fail("FAILED:SelectResults size is different in both the cases. Size1=" + sr.size() + " Size2 = " + externallySorted.length + "; failed query=" + query); } Collection coll1 = null; coll1 = sr.asList(); int j = 0; if (wrapper.validationLevel != ValidationLevel.ORDER_BY_ONLY) { for (Object o : externallySorted) { boolean removed = coll1.remove(o); if (!removed) { LogWriter logger = CacheUtils.getLogger(); logger.error("order by inconsistency at element index = " + j); logger.error(" query result =****"); logger.error(" query result =" + coll1.toString()); logger.error(" query result elementType=" + sr.getCollectionType().getElementType()); logger.error(" externally sorted result =****"); logger.error(ArrayUtils.toString(externallySorted)); fail("failed query due to element mismatch=" + query); } ++j; } assertTrue(coll1.isEmpty()); } if (wrapper.validationLevel != ValidationLevel.MATCH_ONLY) { coll1 = sr.asList(); Iterator itert1 = coll1.iterator(); int i = 0; for (Object o : externallySorted) { if (wrapper.obc.compare(o, itert1.next()) != 0) { LogWriter logger = CacheUtils.getLogger(); logger.error("order by inconsistency at element index = " + i); logger.error(" query result =****"); logger.error(" query result =" + coll1.toString()); logger.error(" externally sorted result =****"); logger.error(ArrayUtils.toString(externallySorted)); fail("failed query due to order mismatch=" + query); } ++i; } } } public Wrapper getOrderByComparatorAndLimitForQuery(String orderByQuery, int unorderedResultSize) throws FunctionDomainException, TypeMismatchException, NameResolutionException, QueryInvocationTargetException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { DefaultQuery q = (DefaultQuery) CacheUtils.getQueryService().newQuery(orderByQuery); CompiledSelect cs = q.getSimpleSelect(); List<CompiledSortCriterion> orderByAttribs = null; if (cs.getType() == CompiledValue.GROUP_BY_SELECT) { Field originalOrderByMethod = CompiledGroupBySelect.class.getDeclaredField("originalOrderByClause"); originalOrderByMethod.setAccessible(true); orderByAttribs = (List<CompiledSortCriterion>) originalOrderByMethod.get(cs); } else { orderByAttribs = cs.getOrderByAttrs(); } ObjectType resultType = cs.getElementTypeForOrderByQueries(); ExecutionContext context = new ExecutionContext(null, CacheUtils.getCache()); final OrderByComparator obc = new OrderByComparator(orderByAttribs, resultType, context); Comparator baseComparator = obc; if (resultType.isStructType()) { baseComparator = new Comparator<Struct>() { @Override public int compare(Struct o1, Struct o2) { return obc.compare(o1.getFieldValues(), o2.getFieldValues()); } }; } final Comparator secondLevelComparator = baseComparator; final Comparator finalComparator = new Comparator() { @Override public int compare(Object o1, Object o2) { final boolean[] orderByColsEqual = new boolean[] {false}; QueryObserverHolder.setInstance(new QueryObserverAdapter() { @Override public void orderByColumnsEqual() { orderByColsEqual[0] = true; } }); int result = secondLevelComparator.compare(o1, o2); if (result != 0 && orderByColsEqual[0]) { result = 0; } return result; } }; Field hasUnmappedOrderByColsField = CompiledSelect.class.getDeclaredField("hasUnmappedOrderByCols"); hasUnmappedOrderByColsField.setAccessible(true); boolean skip = ((Boolean) hasUnmappedOrderByColsField.get(cs)).booleanValue(); ValidationLevel validationLevel = ValidationLevel.ALL; int limit; if (cs.getType() == CompiledValue.GROUP_BY_SELECT) { Field limitCVField = CompiledGroupBySelect.class.getDeclaredField("limit"); limitCVField.setAccessible(true); CompiledValue limitCV = (CompiledValue) limitCVField.get(cs); Method evaluateLimitMethod = CompiledSelect.class.getDeclaredMethod("evaluateLimitValue", ExecutionContext.class, CompiledValue.class); evaluateLimitMethod.setAccessible(true); limit = ((Integer) evaluateLimitMethod.invoke(null, context, limitCV)).intValue(); } else { limit = cs.getLimitValue(null); } if (limit != -1 && limit < unorderedResultSize) { // chances are that results will not match if (skip) { validationLevel = ValidationLevel.NONE; } else { validationLevel = ValidationLevel.ORDER_BY_ONLY; } } else { if (skip) { validationLevel = ValidationLevel.MATCH_ONLY; } } return new Wrapper(finalComparator, limit, validationLevel); } enum ValidationLevel { ALL, ORDER_BY_ONLY, MATCH_ONLY, NONE } private static class Wrapper { final Comparator obc; final int limit; final ValidationLevel validationLevel; Wrapper(Comparator obc, int limit, ValidationLevel validationLevel) { this.obc = obc; this.limit = limit; this.validationLevel = validationLevel; } } /** Creates a new instance of StructSetOrResultsSet */ public void CompareCountStarQueryResultsWithoutAndWithIndexes(Object[][] r, int len, boolean checkOrder, String queries[]) { Integer count1, count2; Iterator<Integer> itert1, itert2; SelectResults result1, result2; boolean exactMatch = true; for (int j = 0; j < len; j++) { result1 = ((SelectResults) r[j][0]); result2 = ((SelectResults) r[j][1]); assertEquals(queries[j], 1, result1.size()); assertEquals(queries[j], 1, result2.size()); checkSelectResultTypes((SelectResults) r[j][0], (SelectResults) r[j][1], queries[j]); checkResultSizes((SelectResults) r[j][0], (SelectResults) r[j][1], queries[j]); compareResults(result1, result2, queries[j], true); } } /** * Compares two ArrayLists containing query results with/without order. */ public void CompareQueryResultsAsListWithoutAndWithIndexes(Object[][] r, int len, boolean checkOrder, String queries[]) { CompareQueryResultsAsListWithoutAndWithIndexes(r, len, checkOrder, true, queries); } public void CompareQueryResultsAsListWithoutAndWithIndexes(Object[][] r, int len, boolean checkOrder, boolean checkClass, String queries[]) { Integer count1, count2; Iterator<Integer> itert1, itert2; ArrayList result1, result2; for (int j = 0; j < len; j++) { result1 = ((ArrayList) r[j][0]); result2 = ((ArrayList) r[j][1]); result1.trimToSize(); result2.trimToSize(); compareQueryResultLists(result1, result2, len, checkOrder, checkClass, queries[j]); } } public void compareQueryResultLists(List r1, List r2, int len, boolean checkOrder, boolean checkClass, String query) { if (checkClass) { if ((r1.get(0).getClass().getName()).equals(r2.get(0).getClass().getName())) { CacheUtils.log( "Both SelectResults are of the same Type i.e.--> " + r1.get(0).getClass().getName()); } else { fail("FAILED:Select result Type is different in both the cases." + r1.get(0).getClass().getName() + "and" + r1.get(0).getClass().getName() + "; failed query=" + query); } } checkResultSizes(r1, r2, query); compareResults(r1, r2, query, checkOrder); } private void checkSelectResultTypes(SelectResults r1, SelectResults r2, String query) { ObjectType type1, type2; type1 = r1.getCollectionType().getElementType(); type2 = r2.getCollectionType().getElementType(); if (!(type1.getClass().getName()).equals(type2.getClass().getName())) { CacheUtils .log("Classes are : " + type1.getClass().getName() + " " + type2.getClass().getName()); fail("FAILED:Select result Type is different in both the cases." + "; failed query=" + query); } } private void checkResultSizes(Collection r1, Collection r2, String query) { if (r1.size() != r2.size()) { fail("FAILED:SelectResults size is different in both the cases. Size1=" + r1.size() + " Size2 = " + r2.size() + "; failed query=" + query); } } private void compareResults(Collection result1, Collection result2, String query, boolean checkOrder) { Iterator itert1 = result1.iterator(); Iterator itert2 = result2.iterator(); int currentIndex = 0; while (itert1.hasNext()) { Object p1 = itert1.next(); Object p2 = null; if (!checkOrder) { if (!collectionContains(result2, p1)) { fail("Atleast one element in the pair of SelectResults " + "supposedly identical, is not equal " + "Match not found for :" + p1 + " compared with:" + p2 + "; failed query=" + query + "; element unmatched =" + p1 + ";p1 class=" + p1.getClass() + " ; other set has =" + result2); } } else { boolean matched = false; if (itert2.hasNext()) { p2 = itert2.next(); matched = objectsEqual(p1, p2); if (!matched) { fail("Order of results was not the same, match not found for :" + p1 + " compared with:" + p2 + "; failed query=" + query + "; element unmatched =" + p1 + ";p1 class=" + p1.getClass() + " compared with " + p2 + ";p2 class=" + p2.getClass() + "currentIndex:" + currentIndex + "\nr1:" + result1 + "\n\nr2:" + result2); } } } currentIndex++; } } private boolean collectionContains(Collection collection, Object object) { Iterator iterator = collection.iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (objectsEqual(object, o)) { return true; } } return false; } private boolean objectsEqual(Object o1, Object o2) { // Assumed that o1 and o2 are the same object type as both are from collections created by // executing the same query if (o1 instanceof Struct) { // if o2 is null, an NPE will be thrown. Object[] values1 = ((Struct) o1).getFieldValues(); Object[] values2 = ((Struct) o2).getFieldValues(); assertEquals(values1.length, values2.length); boolean elementEqual = true; for (int i = 0; i < values1.length; ++i) { elementEqual = elementEqual && ((values1[i] == values2[i]) || values1[i].equals(values2[i])); } if (elementEqual) { return true; } } else { // if o1 is null and o2 is not, an NPE will be thrown if (o1 == o2 || o1.equals(o2)) { return true; } } return false; } }