| /* |
| * 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. |
| */ |
| /* |
| * NestedQueryJUnitTest.java JUnit based test |
| * |
| * Created on March 28, 2005, 11:35 AM |
| */ |
| package org.apache.geode.cache.query.functional; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| import org.junit.rules.ErrorCollector; |
| |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.query.CacheUtils; |
| import org.apache.geode.cache.query.Index; |
| import org.apache.geode.cache.query.IndexType; |
| import org.apache.geode.cache.query.Query; |
| import org.apache.geode.cache.query.QueryService; |
| import org.apache.geode.cache.query.SelectResults; |
| import org.apache.geode.cache.query.data.Portfolio; |
| import org.apache.geode.cache.query.internal.QueryObserverAdapter; |
| import org.apache.geode.cache.query.internal.QueryObserverHolder; |
| import org.apache.geode.cache.query.internal.StructImpl; |
| import org.apache.geode.cache.query.types.ObjectType; |
| import org.apache.geode.test.junit.categories.OQLQueryTest; |
| |
| @Category({OQLQueryTest.class}) |
| public class NestedQueryJUnitTest { |
| |
| private ObjectType resType1 = null; |
| private ObjectType resType2 = null; |
| |
| private int resSize1 = 0; |
| private int resSize2 = 0; |
| |
| private Iterator itert1 = null; |
| private Iterator itert2 = null; |
| |
| private Set set1 = null; |
| private Set set2 = null; |
| |
| private String s1; |
| private String s2; |
| |
| @Rule |
| public ErrorCollector errorCollector = new ErrorCollector(); // used by testQueries |
| |
| @Before |
| public void setUp() throws java.lang.Exception { |
| CacheUtils.startCache(); |
| Region r = CacheUtils.createRegion("Portfolios", Portfolio.class); |
| for (int i = 0; i < 4; i++) |
| r.put(i + "", new Portfolio(i)); |
| } |
| |
| @After |
| public void tearDown() throws java.lang.Exception { |
| CacheUtils.closeCache(); |
| } |
| |
| @Ignore("TODO: this test was disabled") |
| @Test |
| public void testQueries2() throws Exception { |
| String queryString; |
| Query query; |
| Object result; |
| // Executes successfully |
| queryString = |
| "SELECT DISTINCT * FROM /Portfolios WHERE NOT(SELECT DISTINCT * FROM positions.values p WHERE p.secId = 'IBM').isEmpty"; |
| query = CacheUtils.getQueryService().newQuery(queryString); |
| result = query.execute(); |
| // Fails |
| queryString = |
| "SELECT DISTINCT * FROM /Portfolios where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status"; |
| query = CacheUtils.getQueryService().newQuery(queryString); |
| result = query.execute(); |
| // Executes successfully |
| queryString = |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where x.ID = p.ID).status"; |
| query = CacheUtils.getQueryService().newQuery(queryString); |
| result = query.execute(); |
| // Fails |
| queryString = |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = x.ID).status"; |
| query = CacheUtils.getQueryService().newQuery(queryString); |
| result = query.execute(); |
| |
| queryString = |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status"; |
| query = CacheUtils.getQueryService().newQuery(queryString); |
| result = query.execute(); |
| } |
| |
| @Test |
| public void testQueries() throws Exception { |
| String queries[] = { |
| "SELECT DISTINCT * FROM /Portfolios WHERE NOT(SELECT DISTINCT * FROM positions.values p WHERE p.secId = 'IBM').isEmpty", |
| "SELECT DISTINCT * FROM /Portfolios where NOT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).isEmpty", |
| "SELECT DISTINCT * FROM /Portfolios where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status", |
| "SELECT DISTINCT * FROM /Portfolios where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status", |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where x.ID = p.ID).status", |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = x.ID).status", |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status"}; |
| for (int i = 0; i < queries.length; i++) { |
| try { |
| Query query = CacheUtils.getQueryService().newQuery(queries[i]); |
| query.execute(); |
| } catch (Exception e) { |
| errorCollector.addError(e); |
| } |
| } |
| } |
| |
| @Test |
| public void testNestedQueriesEvaluation() throws Exception { |
| QueryService qs; |
| qs = CacheUtils.getQueryService(); |
| String queries[] = { |
| "SELECT DISTINCT * FROM /Portfolios where NOT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).isEmpty", |
| // NQIU 1: PASS |
| // "SELECT DISTINCT * FROM /Portfolios where status = ELEMENT(SELECT DISTINCT * FROM |
| // /Portfolios p where ID = 0).status", |
| // NQIU 2 : Failed: 16 May'05 |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = x.ID).status", |
| // NQIU 3:PASS |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where p.ID = 0).status", |
| // NQIU 4:PASS |
| "SELECT DISTINCT * FROM /Portfolios WHERE NOT(SELECT DISTINCT * FROM positions.values p WHERE p.secId = 'IBM').isEmpty", |
| // NQIU 5: PASS |
| "SELECT DISTINCT * FROM /Portfolios x where status = ELEMENT(SELECT DISTINCT * FROM /Portfolios p where x.ID = p.ID).status", |
| // NQIU 6: PASS |
| }; |
| SelectResults r[][] = new SelectResults[queries.length][2]; |
| |
| for (int i = 0; i < queries.length; i++) { |
| Query q = null; |
| try { |
| q = CacheUtils.getQueryService().newQuery(queries[i]); |
| |
| r[i][0] = (SelectResults) q.execute(); |
| QueryObserverImpl observer = new QueryObserverImpl(); |
| QueryObserverHolder.setInstance(observer); |
| if (!observer.isIndexesUsed) { |
| CacheUtils.log("NO INDEX USED"); |
| } |
| resType1 = (r[i][0]).getCollectionType().getElementType(); |
| resSize1 = ((r[i][0]).size()); |
| set1 = ((r[i][0]).asSet()); |
| // Iterator iter=set1.iterator(); |
| |
| } catch (Exception e) { |
| throw new AssertionError(q.getQueryString(), e); |
| } |
| } |
| |
| // Create an Index on status and execute the same query again. |
| |
| qs = CacheUtils.getQueryService(); |
| qs.createIndex("IDIndex", IndexType.FUNCTIONAL, "ID", "/Portfolios pf"); |
| qs.createIndex("secIdIndex", IndexType.FUNCTIONAL, "b.secId", |
| "/Portfolios pf, pf.positions.values b"); |
| qs.createIndex("r1Index", IndexType.FUNCTIONAL, "secId", |
| "/Portfolios.values['0'].positions.values"); |
| |
| for (int i = 0; i < queries.length; i++) { |
| Query q = null; |
| try { |
| q = CacheUtils.getQueryService().newQuery(queries[i]); |
| QueryObserverImpl observer2 = new QueryObserverImpl(); |
| QueryObserverHolder.setInstance(observer2); |
| r[i][1] = (SelectResults) q.execute(); |
| |
| if (observer2.isIndexesUsed == true) { |
| } else if (i != 3) { |
| fail("Index not used"); |
| } |
| resType2 = (r[i][1]).getCollectionType().getElementType(); |
| resSize2 = ((r[i][1]).size()); |
| set2 = ((r[i][1]).asSet()); |
| |
| } catch (Exception e) { |
| throw new AssertionError(q.getQueryString(), e); |
| } |
| } |
| for (int j = 0; j <= 1; j++) { |
| // Increase the value of j as you go on adding the Queries in queries[] |
| if (((r[j][0]).getCollectionType().getElementType()) |
| .equals((r[j][1]).getCollectionType().getElementType())) { |
| CacheUtils.log("Both Search Results are of the same Type i.e.--> " |
| + (r[j][0]).getCollectionType().getElementType()); |
| } else { |
| fail("FAILED:Search result Type is different in both the cases"); |
| } |
| if ((r[j][0]).size() == (r[j][1]).size()) { |
| CacheUtils.log("Both Search Results are of Same Size i.e. Size= " + (r[j][1]).size()); |
| } else { |
| fail("FAILED:Search result Type is different in both the cases"); |
| } |
| } |
| itert2 = set2.iterator(); |
| itert1 = set1.iterator(); |
| while (itert1.hasNext()) { |
| Portfolio p1 = (Portfolio) itert1.next(); |
| Portfolio p2 = (Portfolio) itert2.next(); |
| if (!set1.contains(p2) || !set2.contains(p1)) |
| fail("FAILED: In both the Cases the members of ResultsSet are different."); |
| } |
| CacheUtils.compareResultsOfWithAndWithoutIndex(r, this); |
| } |
| |
| @Test |
| public void testNestedQueriesResultsAsStructSet() throws Exception { |
| QueryService qs; |
| qs = CacheUtils.getQueryService(); |
| String queries[] = { |
| |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios ptf, positions pos)" |
| + " WHERE pos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios AS ptf, positions AS pos)" |
| + " WHERE pos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM ptf IN /Portfolios, pos IN positions)" |
| + " WHERE pos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" |
| + " (SELECT DISTINCT pos AS myPos FROM /Portfolios ptf, positions pos)" |
| + " WHERE myPos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios ptf, positions pos) p" |
| + " WHERE p.pos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios ptf, positions pos) p" |
| + " WHERE pos.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios, positions) p" |
| + " WHERE p.positions.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios, positions)" |
| + " WHERE positions.value.secId = 'IBM'", |
| "SELECT DISTINCT * FROM" + " (SELECT DISTINCT * FROM /Portfolios ptf, positions pos) p" |
| + " WHERE p.get('pos').value.secId = 'IBM'", |
| // NQIU 7: PASS |
| }; |
| SelectResults r[][] = new SelectResults[queries.length][2]; |
| |
| |
| for (int i = 0; i < queries.length; i++) { |
| Query q = null; |
| try { |
| q = CacheUtils.getQueryService().newQuery(queries[i]); |
| CacheUtils.getLogger().info("Executing query: " + queries[i]); |
| r[i][0] = (SelectResults) q.execute(); |
| QueryObserverImpl observer = new QueryObserverImpl(); |
| QueryObserverHolder.setInstance(observer); |
| |
| resType1 = (r[i][0]).getCollectionType().getElementType(); |
| resSize1 = ((r[i][0]).size()); |
| set1 = ((r[i][0]).asSet()); |
| |
| } catch (Exception e) { |
| throw new AssertionError(e); |
| } |
| } |
| |
| // Create an Index on status and execute the same query again. |
| |
| qs = CacheUtils.getQueryService(); |
| qs.createIndex("secIdIndex", IndexType.FUNCTIONAL, "b.secId", |
| "/Portfolios pf, pf.positions.values b"); |
| for (int i = 0; i < queries.length; i++) { |
| Query q = null; |
| try { |
| q = CacheUtils.getQueryService().newQuery(queries[i]); |
| r[i][1] = (SelectResults) q.execute(); |
| |
| QueryObserverImpl observer2 = new QueryObserverImpl(); |
| QueryObserverHolder.setInstance(observer2); |
| resType2 = (r[i][1]).getCollectionType().getElementType(); |
| resSize2 = ((r[i][1]).size()); |
| set2 = ((r[i][1]).asSet()); |
| |
| } catch (Exception e) { |
| throw new AssertionError(q.getQueryString(), e); |
| } |
| } |
| for (int j = 0; j < queries.length; j++) { |
| |
| if (((r[j][0]).getCollectionType().getElementType()) |
| .equals((r[j][1]).getCollectionType().getElementType())) { |
| CacheUtils.log("Both Search Results are of the same Type i.e.--> " |
| + (r[j][0]).getCollectionType().getElementType()); |
| } else { |
| fail("FAILED:Search result Type is different in both the cases"); |
| } |
| if ((r[j][0]).size() == (r[j][1]).size()) { |
| CacheUtils.log("Both Search Results are of Same Size i.e. Size= " + (r[j][1]).size()); |
| } else { |
| fail("FAILED:Search result Type is different in both the cases"); |
| } |
| } |
| boolean pass = true; |
| itert1 = set1.iterator(); |
| while (itert1.hasNext()) { |
| StructImpl p1 = (StructImpl) itert1.next(); |
| itert2 = set2.iterator(); |
| boolean found = false; |
| while (itert2.hasNext()) { |
| StructImpl p2 = (StructImpl) itert2.next(); |
| if ((p1).equals(p2)) { |
| found = true; |
| } |
| } |
| if (!found) |
| pass = false; |
| } |
| if (!pass) |
| fail("Test failed"); |
| |
| CacheUtils.compareResultsOfWithAndWithoutIndex(r, this); |
| } |
| |
| @Test |
| public void testNestedQueryWithProjectionDoesNotReturnUndefinedForBug45131() throws Exception { |
| QueryService qs = CacheUtils.getQueryService(); |
| Region region1 = CacheUtils.createRegion("portfolios1", Portfolio.class); |
| Region region2 = CacheUtils.createRegion("portfolios2", Portfolio.class); |
| for (int i = 0; i <= 1000; i++) { |
| Portfolio p = new Portfolio(i); |
| p.createTime = (long) (i); |
| region1.put(i, p); |
| region2.put(i, p); |
| } |
| |
| Index p1IdIndex = qs.createIndex("P1IDIndex", IndexType.FUNCTIONAL, "P.ID", "/portfolios1 P"); |
| Index p2IdIndex = qs.createIndex("P2IDIndex", IndexType.FUNCTIONAL, "P2.ID", "/portfolios2 P2"); |
| Index createTimeIndex = |
| qs.createIndex("createTimeIndex", IndexType.FUNCTIONAL, "P.createTime", "/portfolios1 P"); |
| |
| SelectResults results = (SelectResults) qs |
| .newQuery( |
| "SELECT P2.ID FROM /portfolios2 P2 where P2.ID in (SELECT P.ID from /portfolios1 P where P.createTime >= 500L and P.createTime < 1000L)") |
| .execute(); |
| for (Object o : results) { |
| assertNotSame(o, QueryService.UNDEFINED); |
| } |
| } |
| |
| @Test |
| public void testExecCacheForNestedQueries() throws Exception { |
| QueryService qs = CacheUtils.getQueryService(); |
| Region region1 = CacheUtils.createRegion("portfolios1", Portfolio.class); |
| Region region2 = CacheUtils.createRegion("portfolios2", Portfolio.class); |
| for (int i = 0; i <= 1000; i++) { |
| Portfolio p = new Portfolio(i); |
| p.createTime = (long) (i); |
| region1.put(i, p); |
| region2.put(i, p); |
| } |
| |
| Index p1IdIndex = qs.createIndex("P1IDIndex", IndexType.FUNCTIONAL, "P.ID", "/portfolios1 P"); |
| Index p2IdIndex = qs.createIndex("P2IDIndex", IndexType.FUNCTIONAL, "P2.ID", "/portfolios2 P2"); |
| Index createTimeIndex = |
| qs.createIndex("createTimeIndex", IndexType.FUNCTIONAL, "P.createTime", "/portfolios1 P"); |
| |
| String rangeQueryString = |
| "SELECT P.ID FROM /portfolios1 P WHERE P.createTime >= 500L AND P.createTime < 1000L"; |
| // Retrieve location ids that are within range |
| String multiInnerQueryString = |
| "SELECT P FROM /portfolios1 P WHERE P.ID IN(SELECT P2.ID FROM /portfolios2 P2 where P2.ID in ($1)) and P.createTime >=500L and P.createTime < 1000L"; |
| |
| Query rangeQuery = qs.newQuery(rangeQueryString); |
| SelectResults rangeResults = (SelectResults) rangeQuery.execute(); |
| |
| Query multiInnerQuery = qs.newQuery(multiInnerQueryString); |
| Object[] params = new Object[1]; |
| params[0] = rangeResults; |
| |
| SelectResults results = (SelectResults) multiInnerQuery.execute(params); |
| // By now we would have hit the ClassCastException, instead we just check |
| // size and make sure we got results. |
| assertEquals(500, results.size()); |
| } |
| |
| /** |
| * Tests a nested query with shorts converted to integer types in the result set of the inner |
| * query. The short field in the outer query should be evaluated against the integer types and |
| * match. |
| */ |
| @Test |
| public void testNestedQueryWithShortTypesFromInnerQuery() throws Exception { |
| QueryService qs = CacheUtils.getQueryService(); |
| Region region1 = CacheUtils.createRegion("portfolios1", Portfolio.class); |
| int numEntries = 1000; |
| int numIds = 100; |
| for (int i = 0; i < numEntries; i++) { |
| Portfolio p = new Portfolio(i % (numIds)); |
| p.shortID = (short) p.ID; |
| region1.put("" + i, p); |
| } |
| |
| helpTestIndexForQuery( |
| "<trace>SELECT * FROM /portfolios1 p where p.shortID in (SELECT p.shortID FROM /portfolios1 p WHERE p.shortID = 1)", |
| "p.shortID", "/portfolios1 p"); |
| } |
| |
| /** |
| * Tests a nested query that has duplicate results in the inner query Results should not be |
| * duplicated in the final result set |
| */ |
| @Test |
| public void testNestedQueryWithMultipleMatchingResultsWithIn() throws Exception { |
| QueryService qs = CacheUtils.getQueryService(); |
| Region region1 = CacheUtils.createRegion("portfolios1", Portfolio.class); |
| int numEntries = 1000; |
| int numIds = 100; |
| for (int i = 0; i < numEntries; i++) { |
| Portfolio p = new Portfolio(i % (numIds)); |
| p.shortID = (short) p.ID; |
| region1.put("" + i, p); |
| } |
| |
| helpTestIndexForQuery( |
| "<trace>SELECT * FROM /portfolios1 p where p.ID in (SELECT p.ID FROM /portfolios1 p WHERE p.ID = 1)", |
| "p.ID", "/portfolios1 p"); |
| } |
| |
| /** |
| * helper method to test against a compact range index |
| */ |
| private void helpTestIndexForQuery(String query, String indexedExpression, String regionPath) |
| throws Exception { |
| QueryService qs = CacheUtils.getQueryService(); |
| QueryObserverImpl observer = new QueryObserverImpl(); |
| QueryObserverHolder.setInstance(observer); |
| SelectResults nonIndexedResults = (SelectResults) qs.newQuery(query).execute(); |
| assertFalse(observer.isIndexesUsed); |
| |
| qs.createIndex("newIndex", indexedExpression, regionPath); |
| SelectResults indexedResults = (SelectResults) qs.newQuery(query).execute(); |
| assertEquals(nonIndexedResults.size(), indexedResults.size()); |
| assertTrue(observer.isIndexesUsed); |
| } |
| |
| private static class QueryObserverImpl extends QueryObserverAdapter { |
| |
| private boolean isIndexesUsed = false; |
| private ArrayList indexesUsed = new ArrayList(); |
| |
| @Override |
| public void beforeIndexLookup(Index index, int oper, Object key) { |
| indexesUsed.add(index.getName()); |
| } |
| |
| @Override |
| public void afterIndexLookup(Collection results) { |
| if (results != null) { |
| isIndexesUsed = true; |
| } |
| } |
| } |
| } |