Repository: ignite Updated Branches: refs/heads/master bc41ab03f -> dc38a2562
IGNITE-4750: SQL: support for GROUP_CONCAT function. This closes #3185. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/dc38a256 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/dc38a256 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/dc38a256 Branch: refs/heads/master Commit: dc38a2562aab54a32aa6290cf93a2a19c4cd3950 Parents: bc41ab0 Author: tledkov-gridgain <tled...@gridgain.com> Authored: Thu Sep 20 17:58:13 2018 +0300 Committer: devozerov <voze...@gridgain.com> Committed: Thu Sep 20 17:58:13 2018 +0300 ---------------------------------------------------------------------- .../query/h2/sql/GridSqlAggregateFunction.java | 84 ++++++- .../query/h2/sql/GridSqlQueryParser.java | 42 +++- .../query/h2/sql/GridSqlQuerySplitter.java | 21 ++ .../processors/query/h2/sql/GridSqlType.java | 4 + .../IgniteSqlGroupConcatCollocatedTest.java | 250 +++++++++++++++++++ .../IgniteSqlGroupConcatNotCollocatedTest.java | 210 ++++++++++++++++ .../IgniteCacheQuerySelfTestSuite.java | 6 + 7 files changed, 603 insertions(+), 14 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java index edda030..2978b15 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java @@ -17,7 +17,8 @@ package org.apache.ignite.internal.processors.query.h2.sql; -import org.h2.util.StringUtils; +import org.apache.ignite.internal.util.typedef.F; +import org.h2.util.StatementBuilder; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.COUNT; @@ -40,6 +41,15 @@ public class GridSqlAggregateFunction extends GridSqlFunction { /** */ private final boolean distinct; + /** */ + private GridSqlElement groupConcatSeparator; + + /** */ + private GridSqlElement[] groupConcatOrderExpression; + + /** */ + private boolean[] groupConcatOrderDesc; + /** * @param distinct Distinct. * @param type Type. @@ -75,26 +85,74 @@ public class GridSqlAggregateFunction extends GridSqlFunction { return distinct; } + /** + * @param orderExpression Order expression. + * @param orderDesc Order descending flag. + * @return {@code this} for chaining. + */ + public GridSqlAggregateFunction setGroupConcatOrder(GridSqlElement[] orderExpression, boolean[] orderDesc) { + groupConcatOrderExpression = orderExpression; + groupConcatOrderDesc = orderDesc; + + return this; + } + + /** + * @return {@code true} in case GROUP_CONCAT function contains ORDER BY expressions. + */ + public boolean hasGroupConcatOrder() { + return ! F.isEmpty(groupConcatOrderExpression); + } + + /** + * @param separator Separator expression. + * @return {@code this} for chaining. + */ + public GridSqlAggregateFunction setGroupConcatSeparator(GridSqlElement separator) { + groupConcatSeparator = separator; + + return this; + } + + /** + * @return Separator expression. + */ + public GridSqlElement getGroupConcatSeparator() { + return groupConcatSeparator; + } + /** {@inheritDoc} */ @Override public String getSQL() { - String text; + if (type == COUNT_ALL) + return "COUNT(*)"; - switch (type) { - case GROUP_CONCAT: - throw new UnsupportedOperationException(); + StatementBuilder buff = new StatementBuilder(name()).append('('); - case COUNT_ALL: - return "COUNT(*)"; + if (distinct) + buff.append("DISTINCT "); + + buff.append(child().getSQL()); + + if (!F.isEmpty(groupConcatOrderExpression)) { + buff.append(" ORDER BY "); + + buff.resetCount(); - default: - text = type.name(); + for (int i = 0; i < groupConcatOrderExpression.length; ++i) { + buff.appendExceptFirst(", "); - break; + buff.append(groupConcatOrderExpression[i].getSQL()); + + if (groupConcatOrderDesc[i]) + buff.append(" DESC"); + } } - if (distinct) - return text + "(DISTINCT " + child().getSQL() + ")"; + if (groupConcatSeparator != null) + buff.append(" SEPARATOR ").append(groupConcatSeparator.getSQL()); + + buff.append(')'); - return text + StringUtils.enclose(child().getSQL()); + return buff.toString(); } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java index 18c068a..cca1dfc 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java @@ -63,6 +63,7 @@ import org.h2.command.dml.Insert; import org.h2.command.dml.Merge; import org.h2.command.dml.Query; import org.h2.command.dml.Select; +import org.h2.command.dml.SelectOrderBy; import org.h2.command.dml.SelectUnion; import org.h2.command.dml.Update; import org.h2.engine.Constants; @@ -254,6 +255,14 @@ public class GridSqlQueryParser { private static final Getter<Aggregate, Expression> ON = getter(Aggregate.class, "on"); /** */ + private static final Getter<Aggregate, Expression> GROUP_CONCAT_SEPARATOR = getter(Aggregate.class, + "groupConcatSeparator"); + + /** */ + private static final Getter<Aggregate, ArrayList<SelectOrderBy>> GROUP_CONCAT_ORDER_LIST = getter(Aggregate.class, + "groupConcatOrderList"); + + /** */ private static final Getter<RangeTable, Expression> RANGE_MIN = getter(RangeTable.class, "min"); /** */ @@ -2112,13 +2121,24 @@ public class GridSqlQueryParser { int typeId = TYPE.get((Aggregate)expression); if (GridSqlAggregateFunction.isValidType(typeId)) { - GridSqlAggregateFunction res = new GridSqlAggregateFunction(DISTINCT.get((Aggregate)expression), typeId); + GridSqlAggregateFunction res = new GridSqlAggregateFunction( + DISTINCT.get((Aggregate)expression), typeId); Expression on = ON.get((Aggregate)expression); if (on != null) res.addChild(parseExpression(on, calcTypes)); + ArrayList<SelectOrderBy> orders = GROUP_CONCAT_ORDER_LIST.get((Aggregate)expression); + + if (!F.isEmpty(orders)) + parseGroupConcatOrder(res, orders, calcTypes); + + Expression separator = GROUP_CONCAT_SEPARATOR.get((Aggregate)expression); + + if (separator!= null) + res.setGroupConcatSeparator(parseExpression(separator, calcTypes)); + return res; } } @@ -2161,6 +2181,26 @@ public class GridSqlQueryParser { } /** + * @param f Aggregate function. + * @param orders Orders. + * @param calcTypes Calculate types for all the expressions. + */ + private void parseGroupConcatOrder(GridSqlAggregateFunction f, ArrayList<SelectOrderBy> orders, + boolean calcTypes) { + GridSqlElement[] grpConcatOrderExpression = new GridSqlElement[orders.size()]; + boolean[] grpConcatOrderDesc = new boolean[orders.size()]; + + for (int i = 0; i < orders.size(); ++i) { + SelectOrderBy o = orders.get(i); + + grpConcatOrderExpression[i] = parseExpression(o.expression, calcTypes); + grpConcatOrderDesc[i] = o.descending; + } + + f.setGroupConcatOrder(grpConcatOrderExpression, grpConcatOrderDesc); + } + + /** * @param cond Condition. * @param o Object. */ http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java index 3e3b449..6143dc4 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java @@ -38,7 +38,9 @@ import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.processors.cache.query.CacheQueryPartitionInfo; import org.apache.ignite.internal.processors.cache.query.GridCacheSqlQuery; import org.apache.ignite.internal.processors.cache.query.GridCacheTwoStepQuery; +import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; import org.apache.ignite.internal.processors.cache.query.QueryTable; +import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.h2.H2Utils; import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor; @@ -60,6 +62,7 @@ import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst.TR import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.AVG; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.CAST; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.COUNT; +import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.GROUP_CONCAT; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.MAX; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.MIN; import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.SUM; @@ -2173,6 +2176,24 @@ public class GridSqlQuerySplitter { break; + case GROUP_CONCAT: + if (agg.distinct() || agg.hasGroupConcatOrder()) { + throw new IgniteSQLException("Clauses DISTINCT and ORDER BY are unsupported for GROUP_CONCAT " + + "for not collocated data.", IgniteQueryErrorCode.UNSUPPORTED_OPERATION); + } + + if (hasDistinctAggregate) + mapAgg = agg.child(); + else + mapAgg = aggregate(agg.distinct(), agg.type()).resultType(GridSqlType.STRING).addChild(agg.child()); + + rdcAgg = aggregate(false, GROUP_CONCAT) + .setGroupConcatSeparator(agg.getGroupConcatSeparator()) + .resultType(GridSqlType.STRING) + .addChild(column(mapAggAlias.alias())); + + break; + default: throw new IgniteException("Unsupported aggregate: " + agg.type()); } http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlType.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlType.java index b4a610c..ca16520 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlType.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlType.java @@ -49,6 +49,10 @@ public final class GridSqlType { ValueBoolean.DISPLAY_SIZE, "BOOLEAN"); /** */ + public static final GridSqlType STRING = new GridSqlType(Value.STRING, 0, 0, + -1, "VARCHAR"); + + /** */ public static final GridSqlType RESULT_SET = new GridSqlType(Value.RESULT_SET, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, ""); http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatCollocatedTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatCollocatedTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatCollocatedTest.java new file mode 100644 index 0000000..05d29bf --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatCollocatedTest.java @@ -0,0 +1,250 @@ +/* + * 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; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.CacheKeyConfiguration; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.affinity.AffinityKeyMapped; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.cache.query.annotations.QuerySqlField; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests for GROUP_CONCAT aggregate function in collocated mode. + */ +@SuppressWarnings("unchecked") +public class IgniteSqlGroupConcatCollocatedTest extends GridCommonAbstractTest { + /** */ + private static final int CLIENT = 7; + + /** */ + private static final int KEY_BASE_FOR_DUPLICATES = 100; + + /** */ + private static final String CACHE_NAME = "cache"; + + /** */ + private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + TcpDiscoverySpi disco = new TcpDiscoverySpi(); + + disco.setIpFinder(ipFinder); + + cfg.setDiscoverySpi(disco); + + cfg.setCacheConfiguration( + new CacheConfiguration(CACHE_NAME) + .setAffinity(new RendezvousAffinityFunction().setPartitions(8)) + .setQueryEntities(Collections.singletonList(new QueryEntity(Key.class, Value.class)))) + .setCacheKeyConfiguration(new CacheKeyConfiguration(Key.class)); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGridsMultiThreaded(3, false); + + Ignition.setClientMode(true); + try { + startGrid(CLIENT); + } + finally { + Ignition.setClientMode(false); + } + + IgniteCache c = grid(CLIENT).cache(CACHE_NAME); + + int k = 0; + + for (int grp = 1; grp < 7; ++grp) { + for (int i = 0; i < grp; ++i) { + c.put(new Key(k, grp), new Value(k, Character.toString((char)('A' + k)))); + + k++; + } + } + + // Add duplicates + k = 0; + for (int grp = 1; grp < 7; ++grp) { + for (int i = 0; i < grp; ++i) { + c.put(new Key(k + KEY_BASE_FOR_DUPLICATES, grp), + new Value(k + KEY_BASE_FOR_DUPLICATES, Character.toString((char)('A' + k)))); + + k++; + } + } + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + stopAllGrids(); + } + + /** + * + */ + public void testGroupConcatSimple() { + IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + List<List<Object>> res = c.query( + new SqlFieldsQuery("select grp, GROUP_CONCAT(str0) from Value WHERE id < ? group by grp ") + .setCollocated(true).setArgs(KEY_BASE_FOR_DUPLICATES)).getAll(); + + for (List<Object> row : res) { + int grp = (int)row.get(0); + + String str = (String)row.get(1); + + for (int i = 0; i < grp; ++i) { + String s = "" + (char)('A' + i + (grp - 1) * grp / 2); + + assertTrue("Invalid group_concat result: string doesn't contain value: " + + "[str=" + str + ", val=" + s , str.contains(s)); + } + } + } + + /** + * + */ + public void testGroupConcatOrderBy() { + IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + HashMap<Integer, String> exp = new HashMap<>(); + + exp.put(1, "id#0=A"); + exp.put(2, "id#1=B; id#2=C"); + exp.put(3, "id#3=D; id#4=E; id#5=F"); + exp.put(4, "id#6=G; id#7=H; id#8=I; id#9=J"); + exp.put(5, "id#10=K; id#11=L; id#12=M; id#13=N; id#14=O"); + exp.put(6, "id#15=P; id#16=Q; id#17=R; id#18=S; id#19=T; id#20=U"); + + List<List<Object>> res = c.query( + new SqlFieldsQuery("select grp, GROUP_CONCAT(strId || '=' || str0 ORDER BY id SEPARATOR '; ')" + + " from Value WHERE id < ? group by grp") + .setCollocated(true).setArgs(KEY_BASE_FOR_DUPLICATES)).getAll(); + + HashMap<Integer, String> resMap = resultMap(res); + + assertEquals(exp, resMap); + } + + /** + * + */ + public void testGroupConcatWithDistinct() { + IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + HashMap<Integer, String> exp = new HashMap<>(); + + exp.put(1, "A"); + exp.put(2, "B,C"); + exp.put(3, "D,E,F"); + exp.put(4, "G,H,I,J"); + exp.put(5, "K,L,M,N,O"); + exp.put(6, "P,Q,R,S,T,U"); + + List<List<Object>> res = c.query( + new SqlFieldsQuery("select grp, GROUP_CONCAT(DISTINCT str0 ORDER BY str0) from Value group by grp") + .setCollocated(true)).getAll(); + + HashMap<Integer, String> resMap = resultMap(res); + + assertEquals(exp, resMap); + } + + /** + * @param res Result collection. + * @return Map ro result compare. + */ + private HashMap<Integer, String> resultMap(List<List<Object>> res) { + HashMap<Integer, String> map = new HashMap<>(); + + for (List<Object> row : res) + map.put((Integer)row.get(0), (String)row.get(1)); + + return map; + } + + /** + * + */ + public static class Key { + /** */ + @QuerySqlField + private int id; + + /** */ + @QuerySqlField + @AffinityKeyMapped + private int grp; + + /** + * @param id Id. + * @param grp Group. + */ + public Key(int id, int grp) { + this.id = id; + this.grp = grp; + } + } + + /** + * + */ + public static class Value { + /** */ + @QuerySqlField + private String str0; + + /** */ + @QuerySqlField + private String str1; + + /** */ + @QuerySqlField + private String strId; + + /** + * @param id Id. + * @param str String value. + */ + public Value(int id, String str) { + str0 = str; + str1 = str + "_1"; + strId = "id#" + id; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatNotCollocatedTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatNotCollocatedTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatNotCollocatedTest.java new file mode 100644 index 0000000..fbd38f7 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/IgniteSqlGroupConcatNotCollocatedTest.java @@ -0,0 +1,210 @@ +/* + * 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; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.cache.query.annotations.QuerySqlField; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; +import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +/** + * Tests for GROUP_CONCAT aggregate function in not collocated mode. + */ +@SuppressWarnings("unchecked") +public class IgniteSqlGroupConcatNotCollocatedTest extends GridCommonAbstractTest { + /** */ + private static final int CLIENT = 7; + + /** */ + private static final String CACHE_NAME = "cache"; + + /** */ + private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true); + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + TcpDiscoverySpi disco = new TcpDiscoverySpi(); + + disco.setIpFinder(ipFinder); + + cfg.setDiscoverySpi(disco); + + cfg.setCacheConfiguration( + new CacheConfiguration(CACHE_NAME) + .setAffinity(new RendezvousAffinityFunction().setPartitions(8)) + .setQueryEntities(Collections.singletonList(new QueryEntity(Key.class, Value.class)))); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGridsMultiThreaded(3, false); + + Ignition.setClientMode(true); + try { + startGrid(CLIENT); + } + finally { + Ignition.setClientMode(false); + } + + IgniteCache c = grid(CLIENT).cache(CACHE_NAME); + + int k = 0; + + for (int grp = 1; grp < 7; ++grp) { + for (int i = 0; i < grp; ++i) { + c.put(new Key(k, grp), new Value(k, Character.toString((char)('A' + k)))); + + k++; + } + } + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + stopAllGrids(); + } + + /** + * + */ + public void testGroupConcatSimple() { + IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + List<List<Object>> res = c.query( + new SqlFieldsQuery("select grp, GROUP_CONCAT(str0) from Value group by grp")).getAll(); + + for (List<Object> row : res) { + int grp = (int)row.get(0); + + String str = (String)row.get(1); + + for (int i = 0; i < grp; ++i) { + String s = "" + (char)('A' + i + (grp - 1) * grp / 2); + + assertTrue("Invalid group_concat result: string doesn't contain value: " + + "[str=" + str + ", val=" + s , str.contains(s)); + } + } + } + + /** + * + */ + public void testGroupConcatCountDistinct() { + IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + List<List<Object>> res = c.query( + new SqlFieldsQuery("select count(distinct str0), group_concat(str0) from Value group by grp")).getAll(); + + for (List<Object> row : res) { + long cnt = (long)row.get(0); + + String str = (String)row.get(1); + + for (int i = 0; i < cnt; ++i) { + String s = "" + (char)('A' + i + (cnt - 1) * cnt / 2); + + assertTrue("Invalid group_concat result: string doesn't contain value: " + + "[str=" + str + ", val=" + s , str.contains(s)); + } + } + } + + /** + * + */ + public void testGroupConcatDistributedException() { + final IgniteCache c = ignite(CLIENT).cache(CACHE_NAME); + + GridTestUtils.assertThrowsAnyCause(log, new Callable<Object>() { + @Override public Object call() { + c.query(new SqlFieldsQuery("select grp, GROUP_CONCAT(str0 ORDER BY str0) " + + "from Value group by grp")).getAll(); + + return null; + } + }, IgniteSQLException.class, "Clauses DISTINCT and ORDER BY are unsupported for GROUP_CONCAT " + + "for not collocated data"); + } + + /** + * + */ + public static class Key { + /** */ + @QuerySqlField + private int id; + + /** */ + @QuerySqlField + private int grp; + + /** + * @param id Id. + * @param grp Group. + */ + public Key(int id, int grp) { + this.id = id; + this.grp = grp; + } + } + + /** + * + */ + public static class Value { + /** */ + @QuerySqlField + private String str0; + + /** */ + @QuerySqlField + private String str1; + + /** */ + @QuerySqlField + private String strId; + + /** + * @param id Id. + * @param str String value. + */ + public Value(int id, String str) { + str0 = str; + str1 = str + "_1"; + strId = "id#" + id; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/dc38a256/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java index 1036eed..6645099 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java @@ -153,6 +153,8 @@ import org.apache.ignite.internal.processors.query.IgniteQueryDedicatedPoolTest; import org.apache.ignite.internal.processors.query.IgniteSqlDefaultValueTest; import org.apache.ignite.internal.processors.query.IgniteSqlDistributedJoinSelfTest; import org.apache.ignite.internal.processors.query.IgniteSqlEntryCacheModeAgnosticTest; +import org.apache.ignite.internal.processors.query.IgniteSqlGroupConcatCollocatedTest; +import org.apache.ignite.internal.processors.query.IgniteSqlGroupConcatNotCollocatedTest; import org.apache.ignite.internal.processors.query.IgniteSqlKeyValueFieldsTest; import org.apache.ignite.internal.processors.query.IgniteSqlNotNullConstraintTest; import org.apache.ignite.internal.processors.query.IgniteSqlParameterizedQueryTest; @@ -476,6 +478,10 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite { // Partition loss. suite.addTestSuite(IndexingCachePartitionLossPolicySelfTest.class); + // GROUP_CONCAT + suite.addTestSuite(IgniteSqlGroupConcatCollocatedTest.class); + suite.addTestSuite(IgniteSqlGroupConcatNotCollocatedTest.class); + return suite; } }