Skip to content

Commit

Permalink
ignite-2913 - SQL: EXISTS support added
Browse files Browse the repository at this point in the history
  • Loading branch information
svladykin authored and symbicator committed Apr 5, 2017
1 parent 2e6bc44 commit ae435fb
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 44 deletions.
Expand Up @@ -31,7 +31,7 @@ public enum GridSqlOperationType {
MULTIPLY(2, new BiExpressionSqlGenerator("*")),
DIVIDE(2, new BiExpressionSqlGenerator("/")),
MODULUS(2, new BiExpressionSqlGenerator("%")),
NEGATE(1, new PrefixSqlGenerator("-")),
NEGATE(1, new PrefixSqlGenerator("-", true)),

// from org.h2.expression.Comparison
EQUAL(2, new BiExpressionSqlGenerator("=")),
Expand All @@ -47,7 +47,7 @@ public enum GridSqlOperationType {
IS_NULL(1, new SuffixSqlGenerator("IS NULL")),
IS_NOT_NULL(1, new SuffixSqlGenerator("IS NOT NULL")),

NOT(1, new PrefixSqlGenerator("NOT")),
NOT(1, new PrefixSqlGenerator("NOT", true)),

// from org.h2.expression.ConditionAndOr
AND(2, new BiExpressionSqlGenerator("AND")),
Expand All @@ -58,6 +58,7 @@ public enum GridSqlOperationType {
LIKE(2, new BiExpressionSqlGenerator("LIKE")),

IN(-1, new ConditionInSqlGenerator()),
EXISTS(1, new PrefixSqlGenerator("EXISTS", false)),

;
/** */
Expand Down Expand Up @@ -145,18 +146,32 @@ private static class PrefixSqlGenerator implements SqlGenerator {
/** */
private final String text;

/** */
private final boolean addSpace;

/**
* @param text Text.
* @param addSpace Add space char after the prefix.
*/
private PrefixSqlGenerator(String text) {
private PrefixSqlGenerator(String text, boolean addSpace) {
this.text = text;
this.addSpace = addSpace;
}

/** {@inheritDoc} */
@Override public String getSql(GridSqlOperation operation) {
assert operation.operationType().childrenCnt == 1;

return '(' + text + ' ' + operation.child().getSQL() + ')';
StringBuilder b = new StringBuilder();

b.append('(').append(text);

if (addSpace)
b.append(' ');

b.append(operation.child(0).getSQL()).append(')');

return b.toString();
}
}

Expand Down
Expand Up @@ -46,6 +46,7 @@
import org.h2.expression.CompareLike;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.ConditionExists;
import org.h2.expression.ConditionIn;
import org.h2.expression.ConditionInConstantSet;
import org.h2.expression.ConditionInSelect;
Expand Down Expand Up @@ -80,6 +81,7 @@
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.DIVIDE;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.EQUAL;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.EQUAL_NULL_SAFE;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.EXISTS;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.IN;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.IS_NOT_NULL;
import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.IS_NULL;
Expand Down Expand Up @@ -186,7 +188,10 @@ public class GridSqlQueryParser {
"compareType");

/** */
private static final Getter<ConditionInSelect, Query> QUERY = getter(ConditionInSelect.class, "query");
private static final Getter<ConditionInSelect, Query> QUERY_IN = getter(ConditionInSelect.class, "query");

/** */
private static final Getter<ConditionExists, Query> QUERY_EXISTS = getter(ConditionExists.class, "query");

/** */
private static final Getter<CompareLike, Expression> LEFT = getter(CompareLike.class, "left");
Expand Down Expand Up @@ -854,7 +859,7 @@ private GridSqlElement parseExpression0(Expression expression, boolean calcTypes

res.addChild(parseExpression(LEFT_CIS.get((ConditionInSelect)expression), calcTypes));

Query qry = QUERY.get((ConditionInSelect)expression);
Query qry = QUERY_IN.get((ConditionInSelect)expression);

assert0(qry instanceof Select, qry);

Expand Down Expand Up @@ -959,6 +964,16 @@ private GridSqlElement parseExpression0(Expression expression, boolean calcTypes
return res;
}

if (expression instanceof ConditionExists) {
Query qry = QUERY_EXISTS.get((ConditionExists)expression);

GridSqlOperation res = new GridSqlOperation(EXISTS);

res.addChild(new GridSqlSubquery(parse(qry, null)));

return res;
}

throw new IgniteException("Unsupported expression: " + expression + " [type=" +
expression.getClass().getSimpleName() + ']');
}
Expand Down
Expand Up @@ -21,9 +21,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import javax.cache.CacheException;
Expand All @@ -33,10 +35,8 @@
import org.apache.ignite.cache.CacheKeyConfiguration;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.CachePeekMode;
import org.apache.ignite.cache.affinity.AffinityKeyMapped;
import org.apache.ignite.cache.affinity.Affinity;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.affinity.Affinity;
import org.apache.ignite.cache.affinity.AffinityKeyMapped;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
Expand All @@ -63,8 +63,8 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
private static final TcpDiscoveryIpFinder ipFinder = new TcpDiscoveryVmIpFinder(true);

/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(gridName);
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);

CacheKeyConfiguration keyCfg = new CacheKeyConfiguration(TestKey.class.getName(), "affKey");

Expand All @@ -81,6 +81,11 @@ public class IgniteSqlSplitterSelfTest extends GridCommonAbstractTest {
return cfg;
}

@Override
protected long getTestTimeout() {
return 100_000_000;
}

/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
startGridsMultiThreaded(3, false);
Expand Down Expand Up @@ -148,6 +153,47 @@ public void testOffsetLimit() throws Exception {
}
}

@SuppressWarnings("SuspiciousMethodCalls")
public void testExists() {
IgniteCache<Integer,Person2> x = ignite(0).getOrCreateCache(cacheConfig("x", true,
Integer.class, Person2.class));
IgniteCache<Integer,Person2> y = ignite(0).getOrCreateCache(cacheConfig("y", true,
Integer.class, Person2.class));

try {
GridRandom rnd = new GridRandom();

Set<Integer> intersects = new HashSet<>();

for (int i = 0; i < 3000; i++) {
int r = rnd.nextInt(3);

if (r != 0)
x.put(i, new Person2(i, "pers_x_" + i));

if (r != 1)
y.put(i, new Person2(i, "pers_y_" + i));

if (r == 2)
intersects.add(i);
}

assertFalse(intersects.isEmpty());

List<List<?>> res = x.query(new SqlFieldsQuery("select _key from \"x\".Person2 px " +
"where exists(select 1 from \"y\".Person2 py where px._key = py._key)")).getAll();

assertEquals(intersects.size(), res.size());

for (List<?> row : res)
assertTrue(intersects.contains(row.get(0)));
}
finally {
x.destroy();
y.destroy();
}
}

/**
* @throws Exception If failed.
*/
Expand Down Expand Up @@ -550,15 +596,15 @@ public void testDistributedJoinsPlan() throws Exception {
"\"orgRepl\".Organization o",
"where p.affKey = o._key", true);

checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
"(select * from \"persPart\".Person2) p",
"\"orgPart\".Organization o",
"where p._key = o._key", false);

checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
"\"persPart\".Person2 p",
"(select * from \"orgPart\".Organization) o",
"where p._key = o._key", false);
// TODO Now we can not analyze subqueries to decide if we are collocated or not.
// checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
// "(select * from \"persPart\".Person2) p",
// "\"orgPart\".Organization o",
// "where p._key = o._key", false);
// checkNoBatchedJoin(persPart, "select p._key k1, o._key k2 ",
// "\"persPart\".Person2 p",
// "(select * from \"orgPart\".Organization) o",
// "where p._key = o._key", false);

// Join multiple.

Expand Down Expand Up @@ -703,6 +749,7 @@ public void testDistributedJoinsPlan() throws Exception {
ignite(0).destroyCache(cache.getName());
}
}

/**
* @throws Exception If failed.
*/
Expand Down Expand Up @@ -791,26 +838,26 @@ private void checkNoBatchedJoin(IgniteCache<Object, Object> cache,
false,
0,
select +
"from " + cache1 + "," + cache2 + " "+ where);
"from " + cache1 + "," + cache2 + " " + where);

checkQueryPlan(cache,
false,
0,
select +
"from " + cache2 + "," + cache1 + " "+ where);
"from " + cache2 + "," + cache1 + " " + where);

if (testEnforceJoinOrder) {
checkQueryPlan(cache,
true,
0,
select +
"from " + cache1 + "," + cache2 + " "+ where);
"from " + cache1 + "," + cache2 + " " + where);

checkQueryPlan(cache,
true,
0,
select +
"from " + cache2 + "," + cache1 + " "+ where);
"from " + cache2 + "," + cache1 + " " + where);
}
}

Expand All @@ -825,7 +872,8 @@ private void checkQueryPlan(IgniteCache<Object, Object> cache,
boolean enforceJoinOrder,
int expBatchedJoins,
String sql,
String...expText) {
String...expText
) {
checkQueryPlan(cache,
enforceJoinOrder,
expBatchedJoins,
Expand All @@ -850,13 +898,13 @@ private void checkQueryPlan(IgniteCache<Object, Object> cache,
boolean enforceJoinOrder,
int expBatchedJoins,
SqlFieldsQuery qry,
String...expText) {
String... expText) {
qry.setEnforceJoinOrder(enforceJoinOrder);
qry.setDistributedJoins(true);

String plan = queryPlan(cache, qry);

log.info("Plan: " + plan);
log.info("\n Plan:\n" + plan);

assertEquals("Unexpected number of batched joins in plan [plan=" + plan + ", qry=" + qry + ']',
expBatchedJoins,
Expand Down Expand Up @@ -986,7 +1034,7 @@ private void doTestDistributedJoins(IgniteCache<Integer, Object> c, int orgs, in
* @param args Arguments.
* @return Column as list.
*/
private static <X> List<X> columnQuery(IgniteCache<?,?> c, String qry, Object... args) {
private static <X> List<X> columnQuery(IgniteCache<?, ?> c, String qry, Object... args) {
return column(0, c.query(new SqlFieldsQuery(qry).setArgs(args)).getAll());
}

Expand Down Expand Up @@ -1584,4 +1632,4 @@ private static class OrderGood implements Serializable {
@QuerySqlField
private int goodId;
}
}
}

0 comments on commit ae435fb

Please sign in to comment.