User: sgrimstad Date: 27 Sep 18 17:23 Revision: 1a41a15edf6ad9f394fc09fc91052abbf7004d7f Summary: IGNITE-9632 implementation TeamCity URL: https://ci.ignite.apache.org/viewModification.html?tab=vcsModificationFiles&modId=832933&personal=false Index: modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java =================================================================== --- modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java (revision d5b1bbae684d401b40e1731296807b3429526bcf) +++ modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java (revision 1a41a15edf6ad9f394fc09fc91052abbf7004d7f) @@ -53,6 +53,7 @@ import org.h2.command.Prepared; import org.h2.command.dml.Query; import org.h2.command.dml.SelectUnion; +import org.h2.table.Column; import org.h2.table.IndexColumn; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; @@ -2342,6 +2343,29 @@ return null; } + case IN: { + if (op.size() == 0) //Key expected + return null; + else if (op.size() == 1) //Empty selection + return null; + else if (!GridSqlColumn.class.isAssignableFrom(op.child().getClass()) + && !GridH2Table.class.isAssignableFrom( + (GridSqlColumn.class.cast(op.child()).column().getTable().getClass()))) + return null; + else { + List res = new ArrayList<>(op.size() - 1); + for (int i = 1; i < op.size(); i++) { + CacheQueryPartitionInfo cur = getCacheQueryPartitionInfo( + GridSqlColumn.class.cast(op.child()).column(), op.child(i), ctx); + + if (cur != null) + res.add(cur); + } + + return res.size() > 0 ? res.toArray(new CacheQueryPartitionInfo[res.size()]) : null; + } + } + default: return null; } @@ -2368,34 +2392,42 @@ if (!(right instanceof GridSqlConst) && !(right instanceof GridSqlParameter)) return null; - GridSqlColumn column = (GridSqlColumn)left; + return getCacheQueryPartitionInfo(GridSqlColumn.class.cast(left).column(), right, ctx); + } - if (!(column.column().getTable() instanceof GridH2Table)) - return null; + /** + * Extracts the partition if possible + * @param col H2 column + * @param cnstOrPar Constant or parameter element + * @param ctx Kernal Context. + * @return partition info, or {@code null} if none identified + * @throws IgniteCheckedException + */ + @Nullable private static CacheQueryPartitionInfo getCacheQueryPartitionInfo(Column col, GridSqlElement cnstOrPar, + GridKernalContext ctx) throws IgniteCheckedException { + assert col!=null && col.getTable()!=null && GridH2Table.class.isAssignableFrom(col.getTable().getClass()); - GridH2Table tbl = (GridH2Table) column.column().getTable(); + GridH2Table tbl = (GridH2Table)col.getTable(); GridH2RowDescriptor desc = tbl.rowDescriptor(); IndexColumn affKeyCol = tbl.getAffinityKeyColumn(); - int colId = column.column().getColumnId(); + int colId = col.getColumnId(); if ((affKeyCol == null || colId != affKeyCol.column.getColumnId()) && !desc.isKeyColumn(colId)) return null; - if (right instanceof GridSqlConst) { - GridSqlConst constant = (GridSqlConst)right; - + if (GridSqlConst.class.isAssignableFrom(cnstOrPar.getClass())) { return new CacheQueryPartitionInfo(ctx.affinity().partition(tbl.cacheName(), - constant.value().getObject()), null, null, -1, -1); + GridSqlConst.class.cast(cnstOrPar).value().getObject()), null, null, -1, -1); } - - GridSqlParameter param = (GridSqlParameter) right; - + else if (GridSqlParameter.class.isAssignableFrom(cnstOrPar.getClass())) { - return new CacheQueryPartitionInfo(-1, tbl.cacheName(), tbl.getName(), + return new CacheQueryPartitionInfo(-1, tbl.cacheName(), tbl.getName(), - column.column().getType(), param.index()); + col.getType(), GridSqlParameter.class.cast(cnstOrPar).index()); - } + } + return null; + } /** * Merges two partition info arrays, removing duplicates Index: modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/InOperationExtractPartitionSelfTest.java =================================================================== --- modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/InOperationExtractPartitionSelfTest.java (revision 1a41a15edf6ad9f394fc09fc91052abbf7004d7f) +++ modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/InOperationExtractPartitionSelfTest.java (revision 1a41a15edf6ad9f394fc09fc91052abbf7004d7f) @@ -0,0 +1,199 @@ +/* + * 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.ignite.internal.processors.query.h2.twostep; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.LongAdder; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteException; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.query.FieldsQueryCursor; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.managers.communication.GridIoMessage; +import org.apache.ignite.internal.processors.query.h2.twostep.msg.GridH2QueryRequest; +import org.apache.ignite.lang.IgniteInClosure; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.communication.tcp.TcpCommunicationSpi; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +import static org.apache.ignite.internal.processors.query.h2.twostep.JoinSqlTestHelper.ORG; +import static org.apache.ignite.internal.processors.query.h2.twostep.JoinSqlTestHelper.ORG_COUNT; + +/** */ +public class InOperationExtractPartitionSelfTest extends GridCommonAbstractTest { + /** */ + private static final int NODES_COUNT = 8; + + /** */ + private static IgniteCache orgCache; + + /** */ + private static LongAdder cnt = new LongAdder(); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setCommunicationSpi(new TcpCommunicationSpi() { + /** {@inheritDoc} */ + @Override public void sendMessage(ClusterNode node, Message msg, IgniteInClosure ackC) { + assert msg != null; + + if (GridIoMessage.class.isAssignableFrom(msg.getClass())) { + GridIoMessage gridMsg = (GridIoMessage)msg; + + if (GridH2QueryRequest.class.isAssignableFrom(gridMsg.message().getClass())) + cnt.increment(); + } + + super.sendMessage(node, msg, ackC); + } + }); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGridsMultiThreaded(NODES_COUNT, false); + + orgCache = ignite(0).getOrCreateCache(new CacheConfiguration(ORG) + .setCacheMode(CacheMode.PARTITIONED) + .setIndexedTypes(String.class, JoinSqlTestHelper.Organization.class) + ); + + awaitPartitionMapExchange(); + + JoinSqlTestHelper.populateDataIntoOrg(orgCache); + + try (FieldsQueryCursor> cur = orgCache.query(new SqlFieldsQuery( + "SELECT * FROM Organization org WHERE org.id = '" + ORG + 0 + "'"))) { + + assert cur != null; + + List> rows = cur.getAll(); + + assert rows.size() == 1; + } + + try (FieldsQueryCursor> cur = orgCache.query(new SqlFieldsQuery( + "SELECT * FROM Organization org WHERE org.id = ?").setArgs(ORG + 0))) { + + assert cur != null; + + List> rows = cur.getAll(); + + assert rows.size() == 1; + } + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + stopAllGrids(); + } + + /** */ + public void testAlternativeUsageOfIn(){ + try (FieldsQueryCursor> cur = orgCache.query(new SqlFieldsQuery( + "SELECT * FROM Organization org WHERE org._KEY IN (SELECT subOrg._KEY FROM Organization subOrg)"))) { + + assertNotNull(cur); + + List> rows = cur.getAll(); + + assertEquals(ORG_COUNT, rows.size()); + } + } + + /** */ + public void testEmptyList() { + testInOperator(Collections.emptyList(), null, 0L, NODES_COUNT - 1); + } + + /** */ + public void testSingleValueList() { + testInOperator(Collections.singletonList(ORG + 0), null, 1L, 1); + testInOperator(Collections.singletonList(ORG + 1), null, 1L, 1); + testInOperator(Collections.singletonList(ORG + String.valueOf(ORG_COUNT - 1)), null, 1L, 1); + testInOperator(Collections.singletonList("ORG"), null, 0L, 1); + testInOperator(Collections.singletonList("?"), new String[] {ORG + 0}, 1L, 1); + testInOperator(Collections.singletonList("?"), new String[] {ORG + 2}, 1L, 1); + testInOperator(Collections.singletonList("?"), new String[] {ORG + String.valueOf(ORG_COUNT - 1)}, 1L, 1); + testInOperator(Collections.singletonList("?"), new String[] {"ORG"}, 0L, 1); + } + + /** */ + public void testMultipleValueList() { + testInOperator(Arrays.asList(ORG + 0, ORG + 3, ORG + String.valueOf(ORG_COUNT - 1)), null, 3, 3); + testInOperator(Arrays.asList("ORG", ORG + 0, ORG + 4, ORG + String.valueOf(ORG_COUNT - 1)), null, 3, 4); + testInOperator(Arrays.asList(ORG + 0, ORG + 5, ORG + String.valueOf(ORG_COUNT - 1), "ORG"), null, 3, 4); + testInOperator(Arrays.asList(ORG + 0, ORG + 6, "MID", ORG + String.valueOf(ORG_COUNT - 1), "ORG"), null, 3, 5); + + final List allArgs3 = Arrays.asList("?", "?", "?"); + final List allArgs4 = Arrays.asList("?", "?", "?", "?"); + + testInOperator(allArgs3, new String[] {ORG + 0, ORG + 7, ORG + String.valueOf(ORG_COUNT - 1)}, 3, 3); + testInOperator(allArgs4, new String[] {"ORG", ORG + 0, ORG + 8, ORG + String.valueOf(ORG_COUNT - 1)}, 3, 4); + testInOperator(allArgs4, new String[] {ORG + 0, ORG + 9, ORG + String.valueOf(ORG_COUNT - 1), "ORG"}, 3, 4); + testInOperator(allArgs4, new String[] {ORG + 0, "MID", ORG + String.valueOf(ORG_COUNT - 1), "ORG"}, 2, 4); + + testInOperator( + Arrays.asList("?", ORG + 9, ORG + String.valueOf(ORG_COUNT - 1), "?"), + new String[] {ORG + 0, "ORG"}, + 3, + 4 + ); + testInOperator( + Arrays.asList("?", "?", ORG + String.valueOf(ORG_COUNT - 1), "ORG"), + new String[] {ORG + 0, "MID"}, + 2, + 4 + ); + } + + /** + * + * @param cnst Constants and parameters('?'). + * @param args Values of parameters. + * @param expRes Expected result. + * @param maxReq Maximum number of requests to process query. + */ + private void testInOperator(List cnst, Object[] args, long expRes, int maxReq) { + int curIdx = cnt.intValue(); + + String toIn = cnst.size() == 0 ? "" : String.valueOf("'" + String.join("','", cnst) + "'") + .replace("'?'", "?"); + + try (FieldsQueryCursor> cur = orgCache.query(new SqlFieldsQuery( + "SELECT * FROM Organization org WHERE org._KEY IN (" + toIn + ")").setArgs(args))) { + + assertNotNull(cur); + + List> rows = cur.getAll(); + + assertEquals(expRes, rows.size()); + + assertTrue(cnt.intValue() - curIdx <= maxReq); + } + } +} Index: modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/JoinSqlTestHelper.java =================================================================== --- modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/JoinSqlTestHelper.java (revision d5b1bbae684d401b40e1731296807b3429526bcf) +++ modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/JoinSqlTestHelper.java (revision 1a41a15edf6ad9f394fc09fc91052abbf7004d7f) @@ -25,9 +25,12 @@ */ public class JoinSqlTestHelper { /** */ - private static final int ORG_COUNT = 100; + static final String ORG = "org"; /** */ + static final int ORG_COUNT = 100; + + /** */ private static final int PERSON_PER_ORG_COUNT = 10; /** */ @@ -43,7 +46,7 @@ for (int i = 0; i < ORG_COUNT; i++) { Organization org = new Organization(); - org.setId("org" + i); + org.setId(ORG + i); org.setName("Organization #" + i); @@ -61,7 +64,7 @@ for (int i = 0; i < ORG_COUNT; i++) { Organization org = new Organization(); - org.setId("org" + i); + org.setId(ORG + i); org.setName("Organization #" + i); Index: modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java =================================================================== --- modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java (revision d5b1bbae684d401b40e1731296807b3429526bcf) +++ modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite2.java (revision 1a41a15edf6ad9f394fc09fc91052abbf7004d7f) @@ -53,6 +53,7 @@ import org.apache.ignite.internal.processors.query.h2.twostep.CacheQueryMemoryLeakTest; import org.apache.ignite.internal.processors.query.h2.twostep.DisappearedCacheCauseRetryMessageSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.DisappearedCacheWasNotFoundMessageSelfTest; +import org.apache.ignite.internal.processors.query.h2.twostep.InOperationExtractPartitionSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.NonCollocatedRetryMessageSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.RetryCauseMessageSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.TableViewSubquerySelfTest; @@ -120,6 +121,8 @@ suite.addTestSuite(DisappearedCacheCauseRetryMessageSelfTest.class); suite.addTestSuite(DisappearedCacheWasNotFoundMessageSelfTest.class); + suite.addTestSuite(InOperationExtractPartitionSelfTest.class); + suite.addTestSuite(TableViewSubquerySelfTest.class); return suite;