ignite-sql - moved packages
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/2851b52b Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/2851b52b Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/2851b52b Branch: refs/heads/ignite-6 Commit: 2851b52b9921988980051130129e5c043898e724 Parents: 0b1f629 Author: S.Vladykin <svlady...@gridgain.com> Authored: Mon Jan 26 21:43:49 2015 +0300 Committer: S.Vladykin <svlady...@gridgain.com> Committed: Mon Jan 26 21:43:49 2015 +0300 ---------------------------------------------------------------------- .../processors/query/h2/IgniteH2Indexing.java | 6 +- .../query/h2/sql/GridSqlAggregateFunction.java | 76 +++ .../processors/query/h2/sql/GridSqlAlias.java | 55 +++ .../processors/query/h2/sql/GridSqlColumn.java | 57 +++ .../processors/query/h2/sql/GridSqlConst.java | 39 ++ .../processors/query/h2/sql/GridSqlElement.java | 73 +++ .../query/h2/sql/GridSqlFunction.java | 142 ++++++ .../query/h2/sql/GridSqlFunctionType.java | 78 +++ .../processors/query/h2/sql/GridSqlJoin.java | 68 +++ .../query/h2/sql/GridSqlOperation.java | 73 +++ .../query/h2/sql/GridSqlOperationType.java | 202 ++++++++ .../query/h2/sql/GridSqlParameter.java | 44 ++ .../query/h2/sql/GridSqlQueryParser.java | 495 +++++++++++++++++++ .../query/h2/sql/GridSqlQuerySplitter.java | 252 ++++++++++ .../processors/query/h2/sql/GridSqlSelect.java | 287 +++++++++++ .../query/h2/sql/GridSqlSubquery.java | 44 ++ .../processors/query/h2/sql/GridSqlTable.java | 55 +++ .../processors/query/h2/sql/GridSqlValue.java | 17 + .../query/h2/twostep/GridMapQueryExecutor.java | 270 ++++++++++ .../query/h2/twostep/GridMergeIndex.java | 290 +++++++++++ .../h2/twostep/GridMergeIndexUnsorted.java | 85 ++++ .../query/h2/twostep/GridMergeTable.java | 178 +++++++ .../h2/twostep/GridReduceQueryExecutor.java | 242 +++++++++ .../query/h2/twostep/GridResultPage.java | 59 +++ .../twostep/messages/GridNextPageRequest.java | 59 +++ .../twostep/messages/GridNextPageResponse.java | 180 +++++++ .../query/h2/twostep/messages/GridQueryAck.java | 34 ++ .../twostep/messages/GridQueryFailResponse.java | 46 ++ .../h2/twostep/messages/GridQueryRequest.java | 61 +++ .../query/h2/sql/GridSqlAggregateFunction.java | 76 --- .../processors/query/h2/sql/GridSqlAlias.java | 55 --- .../processors/query/h2/sql/GridSqlColumn.java | 57 --- .../processors/query/h2/sql/GridSqlConst.java | 39 -- .../processors/query/h2/sql/GridSqlElement.java | 73 --- .../query/h2/sql/GridSqlFunction.java | 142 ------ .../query/h2/sql/GridSqlFunctionType.java | 78 --- .../processors/query/h2/sql/GridSqlJoin.java | 68 --- .../query/h2/sql/GridSqlOperation.java | 73 --- .../query/h2/sql/GridSqlOperationType.java | 202 -------- .../query/h2/sql/GridSqlParameter.java | 44 -- .../query/h2/sql/GridSqlQueryParser.java | 495 ------------------- .../query/h2/sql/GridSqlQuerySplitter.java | 252 ---------- .../processors/query/h2/sql/GridSqlSelect.java | 287 ----------- .../query/h2/sql/GridSqlSubquery.java | 44 -- .../processors/query/h2/sql/GridSqlTable.java | 55 --- .../processors/query/h2/sql/GridSqlValue.java | 17 - .../query/h2/twostep/GridMapQueryExecutor.java | 270 ---------- .../query/h2/twostep/GridMergeIndex.java | 290 ----------- .../h2/twostep/GridMergeIndexUnsorted.java | 85 ---- .../query/h2/twostep/GridMergeTable.java | 178 ------- .../h2/twostep/GridReduceQueryExecutor.java | 242 --------- .../query/h2/twostep/GridResultPage.java | 59 --- .../twostep/messages/GridNextPageRequest.java | 59 --- .../twostep/messages/GridNextPageResponse.java | 180 ------- .../query/h2/twostep/messages/GridQueryAck.java | 34 -- .../twostep/messages/GridQueryFailResponse.java | 46 -- .../h2/twostep/messages/GridQueryRequest.java | 61 --- .../query/h2/sql/GridQueryParsingTest.java | 1 - 58 files changed, 3564 insertions(+), 3565 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java index 53721b1..baa4382 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java @@ -39,9 +39,9 @@ import org.apache.ignite.internal.util.typedef.*; import org.apache.ignite.internal.util.typedef.internal.*; import org.gridgain.grid.kernal.processors.cache.query.GridCacheSqlResult; import org.gridgain.grid.kernal.processors.cache.query.GridCacheTwoStepQuery; -import org.gridgain.grid.kernal.processors.query.h2.sql.GridSqlQuerySplitter; -import org.gridgain.grid.kernal.processors.query.h2.twostep.GridMapQueryExecutor; -import org.gridgain.grid.kernal.processors.query.h2.twostep.GridReduceQueryExecutor; +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter; +import org.apache.ignite.internal.processors.query.h2.twostep.GridMapQueryExecutor; +import org.apache.ignite.internal.processors.query.h2.twostep.GridReduceQueryExecutor; import org.h2.api.*; import org.h2.command.*; import org.h2.constant.*; http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/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 new file mode 100644 index 0000000..376d1e1 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAggregateFunction.java @@ -0,0 +1,76 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.util.*; + +import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.*; + +/** + * Aggregate function. + */ +public class GridSqlAggregateFunction extends GridSqlFunction { + /** */ + private static final GridSqlFunctionType[] TYPE_INDEX = new GridSqlFunctionType[]{ + COUNT_ALL, COUNT, GROUP_CONCAT, SUM, MIN, MAX, AVG, +// STDDEV_POP, STDDEV_SAMP, VAR_POP, VAR_SAMP, BOOL_OR, BOOL_AND, SELECTIVITY, HISTOGRAM, + }; + + /** */ + private final boolean distinct; + + /** + * @param distinct Distinct. + * @param type Type. + */ + public GridSqlAggregateFunction(boolean distinct, GridSqlFunctionType type) { + super(type); + + this.distinct = distinct; + } + + /** + * @param distinct Distinct. + * @param typeId Type. + */ + public GridSqlAggregateFunction(boolean distinct, int typeId) { + this(distinct, TYPE_INDEX[typeId]); + } + + /** + * @return Distinct. + */ + public boolean distinct() { + return distinct; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + String text; + + switch (type) { + case GROUP_CONCAT: + throw new UnsupportedOperationException(); + + case COUNT_ALL: + return "COUNT(*)"; + + default: + text = type.name(); + + break; + } + + if (distinct) + return text + "(DISTINCT " + child().getSQL() + ")"; + + return text + StringUtils.enclose(child().getSQL()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java new file mode 100644 index 0000000..f1a87d1 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlAlias.java @@ -0,0 +1,55 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.command.*; + +/** + * Alias for column or table. + */ +public class GridSqlAlias extends GridSqlElement { + /** */ + private final String alias; + + /** */ + private final boolean useAs; + + /** + * @param alias Alias. + * @param expr Expr. + */ + public GridSqlAlias(String alias, GridSqlElement expr) { + this(alias, expr, false); + } + + /** + * @param alias Alias. + * @param expr Expr. + * @param useAs Use 'AS' keyword. + */ + public GridSqlAlias(String alias, GridSqlElement expr, boolean useAs) { + addChild(expr); + + this.useAs = useAs; + this.alias = alias; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return child().getSQL() + (useAs ? " AS " : " ") + Parser.quoteIdentifier(alias); + } + + /** + * @return Alias. + */ + public String alias() { + return alias; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java new file mode 100644 index 0000000..31acbe0 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlColumn.java @@ -0,0 +1,57 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +/** + * Column. + */ +public class GridSqlColumn extends GridSqlElement implements GridSqlValue { + /** */ + private final GridSqlElement expressionInFrom; + + /** */ + private final String colName; + + /** SQL from original query. May be qualified or unqualified column name. */ + private final String sqlText; + + /** + * @param from From. + * @param name Name. + * @param sqlText Text. + */ + public GridSqlColumn(GridSqlElement from, String name, String sqlText) { + assert sqlText != null; + + expressionInFrom = from; + colName = name; + this.sqlText = sqlText; + } + + + /** + * @return Column name. + */ + public String columnName() { + return colName; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return sqlText; + } + + /** + * @return Expression in from. + */ + public GridSqlElement expressionInFrom() { + return expressionInFrom; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlConst.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlConst.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlConst.java new file mode 100644 index 0000000..fd0e7d0 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlConst.java @@ -0,0 +1,39 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.value.*; + +/** + * Constant value. + */ +public class GridSqlConst extends GridSqlElement implements GridSqlValue { + /** */ + private final Value val; + + /** + * @param val Value. + */ + public GridSqlConst(Value val) { + this.val = val; + } + + /** + * @return Value. + */ + public Value value() { + return val; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return val.getSQL(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java new file mode 100644 index 0000000..35140b5 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlElement.java @@ -0,0 +1,73 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import java.util.*; + +/** + * Abstract SQL element. + */ +public abstract class GridSqlElement implements Cloneable { + /** */ + protected List<GridSqlElement> children = new ArrayList<>(); + + /** {@inheritDoc} */ + public abstract String getSQL(); + + /** + * @return Children. + */ + public List<GridSqlElement> children() { + return children; + } + + /** + * @param expr Expr. + * @return {@code this}. + */ + public GridSqlElement addChild(GridSqlElement expr) { + if (expr == null) + throw new NullPointerException(); + + children.add(expr); + + return this; + } + + /** + * @return First child. + */ + public GridSqlElement child() { + return children.get(0); + } + + /** + * @param idx Index. + * @return Child. + */ + public GridSqlElement child(int idx) { + return children.get(idx); + } + + /** {@inheritDoc} */ + @SuppressWarnings({"CloneCallsConstructors", "CloneDoesntDeclareCloneNotSupportedException"}) + @Override protected GridSqlElement clone() { + try { + GridSqlElement res = (GridSqlElement)super.clone(); + + res.children = new ArrayList<>(children); + + return res; + } + catch (CloneNotSupportedException e) { + throw new IllegalStateException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunction.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunction.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunction.java new file mode 100644 index 0000000..06b4235 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunction.java @@ -0,0 +1,142 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.apache.ignite.internal.util.typedef.*; +import org.h2.util.*; +import org.h2.value.*; + +import java.util.*; + +import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.*; + +/** + * Function. + */ +public class GridSqlFunction extends GridSqlElement { + /** */ + private static final Map<String, GridSqlFunctionType> TYPE_MAP = new HashMap<>(); + + /** + * + */ + static { + for (GridSqlFunctionType type : GridSqlFunctionType.values()) + TYPE_MAP.put(type.name(), type); + } + + /** */ + private final String name; + + /** */ + protected final GridSqlFunctionType type; + + /** */ + private String castType; + + /** + * @param type Function type. + */ + public GridSqlFunction(GridSqlFunctionType type) { + this(type, type.functionName()); + } + + /** + * @param type Type. + * @param name Name. + */ + private GridSqlFunction(GridSqlFunctionType type, String name) { + if (name == null) + throw new NullPointerException(); + + if (type == null) + type = UNKNOWN_FUNCTION; + + this.name = name; + this.type = type; + } + + /** + * @param name Name. + */ + public GridSqlFunction(String name) { + this(TYPE_MAP.get(name), name); + } + + /** + * @param castType Type for {@link GridSqlFunctionType#CAST} function. + * @return {@code this}. + */ + public GridSqlFunction setCastType(String castType) { + this.castType = castType; + + return this; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + StatementBuilder buff = new StatementBuilder(name); + + if (type == CASE) { + if (!children.isEmpty()) + buff.append(" ").append(child().getSQL()); + + for (int i = 1, len = children.size() - 1; i < len; i += 2) { + buff.append(" WHEN ").append(child(i).getSQL()); + buff.append(" THEN ").append(child(i + 1).getSQL()); + } + if (children.size() % 2 == 0) + buff.append(" ELSE ").append(child(children.size() - 1).getSQL()); + + return buff.append(" END").toString(); + } + + buff.append('('); + + if (type == CAST) { + assert !F.isEmpty(castType) : castType; + assert children().size() == 1; + + buff.append(child().getSQL()).append(" AS ").append(castType); + } + else if (type == CONVERT) { + assert !F.isEmpty(castType) : castType; + assert children().size() == 1; + + buff.append(child().getSQL()).append(',').append(castType); + } + else if (type == GridSqlFunctionType.EXTRACT) { + ValueString v = (ValueString)((GridSqlConst)child(0)).value(); + buff.append(v.getString()).append(" FROM ").append(child(1).getSQL()); + } + else { + for (GridSqlElement e : children) { + buff.appendExceptFirst(", "); + buff.append(e.getSQL()); + } + } + + return buff.append(')').toString(); + } + + /** + * @return Name. + */ + public String name() { + return name; + } + + /** + * @return Type. + */ + public GridSqlFunctionType type() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunctionType.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunctionType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunctionType.java new file mode 100644 index 0000000..ffad44e --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlFunctionType.java @@ -0,0 +1,78 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.expression.*; + +/** + * Full list of available functions see at {@link Function} + */ +public enum GridSqlFunctionType { + // Aggregate functions. + /** */ + COUNT_ALL("COUNT(*)"), + + /** */ + COUNT, + + /** */ + SUM, + + /** */ + MIN, + + /** */ + MAX, + + /** */ + AVG, + + /** */ + GROUP_CONCAT, + + // Functions with special handling. + /** */ + CASE, + + /** */ + CAST, + + /** */ + CONVERT, + + /** */ + EXTRACT, + + /** Constant for all other functions. */ + UNKNOWN_FUNCTION; + + /** */ + private final String name; + + /** + */ + GridSqlFunctionType() { + name = name(); + } + + /** + * @param name Name. + */ + GridSqlFunctionType(String name) { + this.name = name; + } + + /** + * @return Function name. + */ + public String functionName() { + return name; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlJoin.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlJoin.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlJoin.java new file mode 100644 index 0000000..400fc68 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlJoin.java @@ -0,0 +1,68 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.util.*; +import org.jetbrains.annotations.*; + +/** + * Join of two tables or subqueries. + */ +public class GridSqlJoin extends GridSqlElement { + /** + * @param leftTbl Left table. + * @param rightTbl Right table. + */ + public GridSqlJoin(GridSqlElement leftTbl, GridSqlElement rightTbl) { + addChild(leftTbl); + addChild(rightTbl); + } + + /** + * @return Table 1. + */ + public GridSqlElement leftTable() { + return child(0); + } + + /** + * @return Table 2. + */ + public GridSqlElement rightTable() { + return child(1); + } + + /** + * @return {@code ON} Condition. + */ + @Nullable public GridSqlElement on() { + return child(2); + } + + /** + * @return {@code true} If it is a {@code LEFT JOIN}. + */ + public boolean leftJoin() { + return false; // TODO + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + StatementBuilder buff = new StatementBuilder(); + + buff.append(leftTable().getSQL()); + + buff.append(leftJoin() ? " \n LEFT JOIN " : " \n INNER JOIN "); + + buff.append(rightTable().getSQL()); + + return buff.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java new file mode 100644 index 0000000..ea8abd1 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperation.java @@ -0,0 +1,73 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +/** + * Unary or binary operation. + */ +public class GridSqlOperation extends GridSqlElement { + /** */ + private final GridSqlOperationType opType; + + /** + * @param opType Operation type. + */ + public GridSqlOperation(GridSqlOperationType opType) { + this.opType = opType; + } + + /** + * @param opType Op type. + * @param arg argument. + */ + public GridSqlOperation(GridSqlOperationType opType, GridSqlElement arg) { + this(opType); + + addChild(arg); + } + + /** + * @param opType Op type. + * @param left Left. + * @param right Right. + */ + public GridSqlOperation(GridSqlOperationType opType, GridSqlElement left, GridSqlElement right) { + this(opType); + + addChild(left); + addChild(right); + } + + /** + * @return Left. + */ + public GridSqlElement left() { + return child(0); + } + + /** + * @return Right. + */ + public GridSqlElement right() { + return child(1); + } + + /** + * @return Operation type. + */ + public GridSqlOperationType opType() { + return opType; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return opType.toSql(this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java new file mode 100644 index 0000000..8d7c611 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlOperationType.java @@ -0,0 +1,202 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.util.*; + +/** + * Operation type. + */ +public enum GridSqlOperationType { + // from org.h2.expression.Operation + CONCAT(2, new BiExpressionSqlGenerator("||")), + PLUS(2, new BiExpressionSqlGenerator("+")), + MINUS(2, new BiExpressionSqlGenerator("-")), + MULTIPLY(2, new BiExpressionSqlGenerator("*")), + DIVIDE(2, new BiExpressionSqlGenerator("/")), + MODULUS(2, new BiExpressionSqlGenerator("%")), + NEGATE(1, new PrefixSqlGenerator("-")), + + // from org.h2.expression.Comparison + EQUAL(2, new BiExpressionSqlGenerator("=")), + EQUAL_NULL_SAFE(2, new BiExpressionSqlGenerator("IS")), + BIGGER_EQUAL(2, new BiExpressionSqlGenerator(">=")), + BIGGER(2, new BiExpressionSqlGenerator(">")), + SMALLER_EQUAL(2, new BiExpressionSqlGenerator("<=")), + SMALLER(2, new BiExpressionSqlGenerator("<")), + NOT_EQUAL(2, new BiExpressionSqlGenerator("<>")), + NOT_EQUAL_NULL_SAFE(2, new BiExpressionSqlGenerator("IS NOT")), + + SPATIAL_INTERSECTS(2, new IntersectsSqlGenerator()), + IS_NULL(1, new SuffixSqlGenerator("IS NULL")), + IS_NOT_NULL(1, new SuffixSqlGenerator("IS NOT NULL")), + + NOT(1, new PrefixSqlGenerator("NOT")), + + // from org.h2.expression.ConditionAndOr + AND(2, new BiExpressionSqlGenerator("AND")), + OR(2, new BiExpressionSqlGenerator("OR")), + + // from + REGEXP(2, new BiExpressionSqlGenerator("REGEXP")), + LIKE(2, new BiExpressionSqlGenerator("LIKE")), + + IN(-1, new ConditionInSqlGenerator()), + + ; + /** */ + private final SqlGenerator sqlGenerator; + + /** */ + private final int childrenCnt; + + /** + * @param childrenCnt Children count. + * @param sqlGenerator sqlGenerator. + */ + GridSqlOperationType(int childrenCnt, SqlGenerator sqlGenerator) { + this.childrenCnt = childrenCnt; + this.sqlGenerator = sqlGenerator; + } + + /** + * @param operation Operation. + */ + public String toSql(GridSqlOperation operation) { + return sqlGenerator.getSql(operation); + } + + /** + * @return Children count. + */ + public int childrenCount() { + return childrenCnt; + } + + /** + * + */ + private static interface SqlGenerator { + + /** + * @param operation Operation expression. + */ + public String getSql(GridSqlOperation operation); + } + + /** + * + */ + private static class BiExpressionSqlGenerator implements SqlGenerator { + + /** */ + private final String delim; + + /** + * @param delim Delimiter. + */ + private BiExpressionSqlGenerator(String delim) { + this.delim = delim; + } + + /** {@inheritDoc} */ + @Override public String getSql(GridSqlOperation operation) { + assert operation.opType().childrenCnt == 2; + + return '(' + operation.child(0).getSQL() + " " + delim + " " + operation.child(1).getSQL() + ')'; + } + } + + /** + * + */ + private static class IntersectsSqlGenerator implements SqlGenerator { + + /** {@inheritDoc} */ + @Override public String getSql(GridSqlOperation operation) { + assert operation.opType().childrenCnt == 2; + + return "(INTERSECTS(" + operation.child(0) + ", " + operation.child(1) + "))"; + } + } + + /** + * + */ + private static class PrefixSqlGenerator implements SqlGenerator { + /** */ + private final String text; + + /** + * @param text Text. + */ + private PrefixSqlGenerator(String text) { + this.text = text; + } + + /** {@inheritDoc} */ + @Override public String getSql(GridSqlOperation operation) { + assert operation.opType().childrenCnt == 1; + + return '(' + text + ' ' + operation.child().getSQL() + ')'; + } + } + + /** + * + */ + private static class SuffixSqlGenerator implements SqlGenerator { + /** */ + private final String text; + + /** + * @param text Text. + */ + private SuffixSqlGenerator(String text) { + this.text = text; + } + + /** {@inheritDoc} */ + @Override public String getSql(GridSqlOperation operation) { + assert operation.opType().childrenCnt == 1; + + return '(' + operation.child().getSQL() + ' ' + text + ')'; + } + } + + /** + * + */ + private static class ConditionInSqlGenerator implements SqlGenerator { + + /** {@inheritDoc} */ + @Override public String getSql(GridSqlOperation operation) { + StatementBuilder buff = new StatementBuilder("("); + + buff.append(operation.child(0).getSQL()).append(" IN("); + + assert operation.children().size() > 1; + + if (operation.children().size() == 2) { + String child = operation.child(1).getSQL(); + + buff.append(' ').append(StringUtils.unEnclose(child)).append(' '); + } + else { + for (int i = 1; i < operation.children().size(); i++) { + buff.appendExceptFirst(", "); + buff.append(operation.child(i).getSQL()); + } + } + + return buff.append("))").toString(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlParameter.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlParameter.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlParameter.java new file mode 100644 index 0000000..3e11aab --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlParameter.java @@ -0,0 +1,44 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +/** + * Query parameter. + */ +public class GridSqlParameter extends GridSqlElement implements GridSqlValue { + /** Index. */ + private int idx; + + /** + * @param idx Index. + */ + public GridSqlParameter(int idx) { + this.idx = idx; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return "?" + (idx + 1); + } + + /** + * @return Index. + */ + public int index() { + return idx; + } + + /** + * @param idx New index. + */ + public void index(int idx) { + this.idx = idx; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/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 new file mode 100644 index 0000000..7a7c0e6 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java @@ -0,0 +1,495 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.apache.ignite.*; +import org.h2.command.dml.*; +import org.h2.engine.*; +import org.h2.expression.*; +import org.h2.jdbc.*; +import org.h2.result.*; +import org.h2.table.*; +import org.jetbrains.annotations.*; + +import java.lang.reflect.*; +import java.sql.*; +import java.util.*; +import java.util.Set; + +import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlOperationType.*; + +/** + * H2 Query parser. + */ +@SuppressWarnings("TypeMayBeWeakened") +public class GridSqlQueryParser { + /** */ + private static final GridSqlOperationType[] OPERATION_OP_TYPES = new GridSqlOperationType[]{CONCAT, PLUS, MINUS, MULTIPLY, DIVIDE, null, MODULUS}; + + /** */ + private static final GridSqlOperationType[] COMPARISON_TYPES = new GridSqlOperationType[]{EQUAL, BIGGER_EQUAL, BIGGER, SMALLER_EQUAL, + SMALLER, NOT_EQUAL, IS_NULL, IS_NOT_NULL, + null, null, null, SPATIAL_INTERSECTS /* 11 */, null, null, null, null, EQUAL_NULL_SAFE /* 16 */, null, null, null, null, + NOT_EQUAL_NULL_SAFE /* 21 */}; + + /** */ + private static final Getter<Select, Expression> CONDITION = getter(Select.class, "condition"); + + /** */ + private static final Getter<Select, int[]> GROUP_INDEXES = getter(Select.class, "groupIndex"); + + /** */ + private static final Getter<Operation, Integer> OPERATION_TYPE = getter(Operation.class, "opType"); + + /** */ + private static final Getter<Operation, Expression> OPERATION_LEFT = getter(Operation.class, "left"); + + /** */ + private static final Getter<Operation, Expression> OPERATION_RIGHT = getter(Operation.class, "right"); + + /** */ + private static final Getter<Comparison, Integer> COMPARISON_TYPE = getter(Comparison.class, "compareType"); + + /** */ + private static final Getter<Comparison, Expression> COMPARISON_LEFT = getter(Comparison.class, "left"); + + /** */ + private static final Getter<Comparison, Expression> COMPARISON_RIGHT = getter(Comparison.class, "right"); + + /** */ + private static final Getter<ConditionAndOr, Integer> ANDOR_TYPE = getter(ConditionAndOr.class, "andOrType"); + + /** */ + private static final Getter<ConditionAndOr, Expression> ANDOR_LEFT = getter(ConditionAndOr.class, "left"); + + /** */ + private static final Getter<ConditionAndOr, Expression> ANDOR_RIGHT = getter(ConditionAndOr.class, "right"); + + /** */ + private static final Getter<TableView, Query> VIEW_QUERY = getter(TableView.class, "viewQuery"); + + /** */ + private static final Getter<TableFilter, String> ALIAS = getter(TableFilter.class, "alias"); + + /** */ + private static final Getter<Select, Integer> HAVING_INDEX = getter(Select.class, "havingIndex"); + + /** */ + private static final Getter<ConditionIn, Expression> LEFT_CI = getter(ConditionIn.class, "left"); + + /** */ + private static final Getter<ConditionIn, List<Expression>> VALUE_LIST_CI = getter(ConditionIn.class, "valueList"); + + /** */ + private static final Getter<ConditionInConstantSet, Expression> LEFT_CICS = + getter(ConditionInConstantSet.class, "left"); + + /** */ + private static final Getter<ConditionInConstantSet, List<Expression>> VALUE_LIST_CICS = + getter(ConditionInConstantSet.class, "valueList"); + + /** */ + private static final Getter<ConditionInSelect, Expression> LEFT_CIS = getter(ConditionInSelect.class, "left"); + + /** */ + private static final Getter<ConditionInSelect, Boolean> ALL = getter(ConditionInSelect.class, "all"); + + /** */ + private static final Getter<ConditionInSelect, Integer> COMPARE_TYPE = getter(ConditionInSelect.class, + "compareType"); + + /** */ + private static final Getter<ConditionInSelect, Query> QUERY = getter(ConditionInSelect.class, "query"); + + /** */ + private static final Getter<CompareLike, Expression> LEFT = getter(CompareLike.class, "left"); + + /** */ + private static final Getter<CompareLike, Expression> RIGHT = getter(CompareLike.class, "right"); + + /** */ + private static final Getter<CompareLike, Expression> ESCAPE = getter(CompareLike.class, "escape"); + + /** */ + private static final Getter<CompareLike, Boolean> REGEXP_CL = getter(CompareLike.class, "regexp"); + + /** */ + private static final Getter<Aggregate, Boolean> DISTINCT = getter(Aggregate.class, "distinct"); + + /** */ + private static final Getter<Aggregate, Integer> TYPE = getter(Aggregate.class, "type"); + + /** */ + private static final Getter<Aggregate, Expression> ON = getter(Aggregate.class, "on"); + + /** */ + private final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>(); + + /** + * @param conn Connection. + * @param select Select query. + * @return Parsed select query. + */ + public static GridSqlSelect parse(Connection conn, String select) { + Session ses = (Session)((JdbcConnection)conn).getSession(); + + return new GridSqlQueryParser().parse((Select)ses.prepare(select)); + } + + /** + * @param filter Filter. + */ + private GridSqlElement parse(TableFilter filter) { + GridSqlElement res = (GridSqlElement)h2ObjToGridObj.get(filter); + + if (res == null) { + Table tbl = filter.getTable(); + + if (tbl instanceof TableBase) + res = new GridSqlTable(tbl.getSchema().getName(), tbl.getName()); + else if (tbl instanceof TableView) { + Query qry = VIEW_QUERY.get((TableView)tbl); + + assert0(qry instanceof Select, qry); + + res = new GridSqlSubquery(parse((Select)qry)); + } + else + throw new IgniteException("Unsupported query: " + filter); + + String alias = ALIAS.get(filter); + + if (alias != null) + res = new GridSqlAlias(alias, res, false); + + h2ObjToGridObj.put(filter, res); + } + + return res; + } + + /** + * @param select Select. + */ + public GridSqlSelect parse(Select select) { + GridSqlSelect res = (GridSqlSelect)h2ObjToGridObj.get(select); + + if (res != null) + return res; + + res = new GridSqlSelect(); + + h2ObjToGridObj.put(select, res); + + res.distinct(select.isDistinct()); + + Expression where = CONDITION.get(select); + res.where(parseExpression(where)); + + Set<TableFilter> allFilers = new HashSet<>(select.getTopFilters()); + + GridSqlElement from = null; + + TableFilter filter = select.getTopTableFilter(); + do { + assert0(filter != null, select); + assert0(!filter.isJoinOuter(), select); + assert0(filter.getNestedJoin() == null, select); + assert0(filter.getJoinCondition() == null, select); + assert0(filter.getFilterCondition() == null, select); + + GridSqlElement gridFilter = parse(filter); + + from = from == null ? gridFilter : new GridSqlJoin(from, gridFilter); + + allFilers.remove(filter); + + filter = filter.getJoin(); + } + while (filter != null); + + res.from(from); + + assert allFilers.isEmpty(); + + ArrayList<Expression> expressions = select.getExpressions(); + + for (Expression exp : expressions) + res.addExpression(parseExpression(exp)); + + int[] grpIdx = GROUP_INDEXES.get(select); + + if (grpIdx != null) { + res.groupColumns(grpIdx); + + for (int idx : grpIdx) + res.addGroupExpression(parseExpression(expressions.get(idx))); + } + + assert0(select.getHaving() == null, select); + + int havingIdx = HAVING_INDEX.get(select); + + if (havingIdx >= 0) + res.having(parseExpression(expressions.get(havingIdx))); + + for (int i = 0; i < select.getColumnCount(); i++) + res.addSelectExpression(parseExpression(expressions.get(i))); + + SortOrder sortOrder = select.getSortOrder(); + + if (sortOrder != null) { + int[] indexes = sortOrder.getQueryColumnIndexes(); + int[] sortTypes = sortOrder.getSortTypes(); + + for (int i = 0; i < indexes.length; i++) + res.addSort(parseExpression(expressions.get(indexes[i])), sortTypes[i]); + } + + return res; + } + + /** + * @param expression Expression. + */ + private GridSqlElement parseExpression(@Nullable Expression expression) { + if (expression == null) + return null; + + GridSqlElement res = (GridSqlElement)h2ObjToGridObj.get(expression); + + if (res == null) { + res = parseExpression0(expression); + + h2ObjToGridObj.put(expression, res); + } + + return res; + } + + /** + * @param expression Expression. + */ + private GridSqlElement parseExpression0(Expression expression) { + if (expression instanceof ExpressionColumn) { + TableFilter tblFilter = ((ExpressionColumn)expression).getTableFilter(); + + GridSqlElement gridTblFilter = parse(tblFilter); + + return new GridSqlColumn(gridTblFilter, expression.getColumnName(), expression.getSQL()); + } + + if (expression instanceof Alias) + return new GridSqlAlias(expression.getAlias(), parseExpression(expression.getNonAliasExpression()), true); + + if (expression instanceof ValueExpression) + return new GridSqlConst(expression.getValue(null)); + + if (expression instanceof Operation) { + Operation operation = (Operation)expression; + + Integer type = OPERATION_TYPE.get(operation); + + if (type == Operation.NEGATE) { + assert OPERATION_RIGHT.get(operation) == null; + + return new GridSqlOperation(GridSqlOperationType.NEGATE, parseExpression(OPERATION_LEFT.get(operation))); + } + + return new GridSqlOperation(OPERATION_OP_TYPES[type], + parseExpression(OPERATION_LEFT.get(operation)), + parseExpression(OPERATION_RIGHT.get(operation))); + } + + if (expression instanceof Comparison) { + Comparison cmp = (Comparison)expression; + + GridSqlOperationType opType = COMPARISON_TYPES[COMPARISON_TYPE.get(cmp)]; + + assert opType != null : COMPARISON_TYPE.get(cmp); + + GridSqlElement left = parseExpression(COMPARISON_LEFT.get(cmp)); + + if (opType.childrenCount() == 1) + return new GridSqlOperation(opType, left); + + GridSqlElement right = parseExpression(COMPARISON_RIGHT.get(cmp)); + + return new GridSqlOperation(opType, left, right); + } + + if (expression instanceof ConditionNot) + return new GridSqlOperation(NOT, parseExpression(expression.getNotIfPossible(null))); + + if (expression instanceof ConditionAndOr) { + ConditionAndOr andOr = (ConditionAndOr)expression; + + int type = ANDOR_TYPE.get(andOr); + + assert type == ConditionAndOr.AND || type == ConditionAndOr.OR; + + return new GridSqlOperation(type == ConditionAndOr.AND ? AND : OR, + parseExpression(ANDOR_LEFT.get(andOr)), parseExpression(ANDOR_RIGHT.get(andOr))); + } + + if (expression instanceof Subquery) { + Query qry = ((Subquery)expression).getQuery(); + + assert0(qry instanceof Select, expression); + + return new GridSqlSubquery(parse((Select) qry)); + } + + if (expression instanceof ConditionIn) { + GridSqlOperation res = new GridSqlOperation(IN); + + res.addChild(parseExpression(LEFT_CI.get((ConditionIn) expression))); + + List<Expression> vals = VALUE_LIST_CI.get((ConditionIn)expression); + + for (Expression val : vals) + res.addChild(parseExpression(val)); + + return res; + } + + if (expression instanceof ConditionInConstantSet) { + GridSqlOperation res = new GridSqlOperation(IN); + + res.addChild(parseExpression(LEFT_CICS.get((ConditionInConstantSet) expression))); + + List<Expression> vals = VALUE_LIST_CICS.get((ConditionInConstantSet)expression); + + for (Expression val : vals) + res.addChild(parseExpression(val)); + + return res; + } + + if (expression instanceof ConditionInSelect) { + GridSqlOperation res = new GridSqlOperation(IN); + + boolean all = ALL.get((ConditionInSelect)expression); + int compareType = COMPARE_TYPE.get((ConditionInSelect)expression); + + assert0(!all, expression); + assert0(compareType == Comparison.EQUAL, expression); + + res.addChild(parseExpression(LEFT_CIS.get((ConditionInSelect) expression))); + + Query qry = QUERY.get((ConditionInSelect)expression); + + assert0(qry instanceof Select, qry); + + res.addChild(new GridSqlSubquery(parse((Select) qry))); + + return res; + } + + if (expression instanceof CompareLike) { + assert0(ESCAPE.get((CompareLike)expression) == null, expression); + + boolean regexp = REGEXP_CL.get((CompareLike)expression); + + return new GridSqlOperation(regexp ? REGEXP : LIKE, parseExpression(LEFT.get((CompareLike) expression)), + parseExpression(RIGHT.get((CompareLike) expression))); + } + + if (expression instanceof Function) { + Function f = (Function)expression; + + GridSqlFunction res = new GridSqlFunction(f.getName()); + + for (Expression arg : f.getArgs()) + res.addChild(parseExpression(arg)); + + if (f.getFunctionType() == Function.CAST || f.getFunctionType() == Function.CONVERT) + res.setCastType(new Column(null, f.getType(), f.getPrecision(), f.getScale(), f.getDisplaySize()) + .getCreateSQL()); + + return res; + } + + if (expression instanceof Parameter) + return new GridSqlParameter(((Parameter)expression).getIndex()); + + if (expression instanceof Aggregate) { + GridSqlAggregateFunction res = new GridSqlAggregateFunction(DISTINCT.get((Aggregate)expression), + TYPE.get((Aggregate)expression)); + + Expression on = ON.get((Aggregate)expression); + + if (on != null) + res.addChild(parseExpression(on)); + + return res; + } + + throw new IgniteException("Unsupported expression: " + expression + " [type=" + + expression.getClass().getSimpleName() + ']'); + } + + /** + * @param cond Condition. + * @param o Object. + */ + private static void assert0(boolean cond, Object o) { + if (!cond) + throw new IgniteException("Unsupported query: " + o); + } + + /** + * @param cls Class. + * @param fldName Fld name. + */ + private static <T, R> Getter<T, R> getter(Class<T> cls, String fldName) { + Field field; + + try { + field = cls.getDeclaredField(fldName); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + + field.setAccessible(true); + + return new Getter<>(field); + } + + /** + * Field getter. + */ + @SuppressWarnings("unchecked") + private static class Getter<T, R> { + /** */ + private final Field fld; + + /** + * @param fld Fld. + */ + private Getter(Field fld) { + this.fld = fld; + } + + /** + * @param obj Object. + * @return Result. + */ + public R get(T obj) { + try { + return (R)fld.get(obj); + } + catch (IllegalAccessException e) { + throw new IgniteException(e); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/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 new file mode 100644 index 0000000..db67ee7 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQuerySplitter.java @@ -0,0 +1,252 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.apache.ignite.*; +import org.gridgain.grid.kernal.processors.cache.query.*; + +import java.sql.*; +import java.util.*; + +import static org.apache.ignite.internal.processors.query.h2.sql.GridSqlFunctionType.*; + +/** + * Splits a single SQL query into two step map-reduce query. + */ +public class GridSqlQuerySplitter { + /** */ + private static final String TABLE_PREFIX = "__T"; + + /** */ + private static final String COLUMN_PREFIX = "__C"; + + /** + * @param idx Index of table. + * @return Table name. + */ + private static String table(int idx) { + return TABLE_PREFIX + idx; + } + + /** + * @param idx Index of column. + * @return Generated by index column alias. + */ + private static String columnName(int idx) { + return COLUMN_PREFIX + idx; + } + + /** + * @param conn Connection. + * @param query Query. + * @param params Parameters. + * @return Two step query. + */ + public static GridCacheTwoStepQuery split(Connection conn, String query, Object[] params) { + // TODO possibly get column types from query. + GridSqlSelect srcQry = GridSqlQueryParser.parse(conn, query); + + if (srcQry.groups().isEmpty()) { // Simple case. + String tbl0 = table(0); + + GridCacheTwoStepQuery res = new GridCacheTwoStepQuery("select * from " + tbl0); + + res.addMapQuery(tbl0, srcQry.getSQL(), params); + + return res; + } + + // Split all select expressions into map-reduce parts. + List<GridSqlElement> mapExps = new ArrayList<>(srcQry.allExpressions()); + + GridSqlElement[] rdcExps = new GridSqlElement[srcQry.select().size()]; + + for (int i = 0, len = mapExps.size(); i < len; i++) + splitSelectExpression(mapExps, rdcExps, i); + + // Build map query. + GridSqlSelect mapQry = srcQry.clone(); + + mapQry.clearSelect(); + + for (GridSqlElement exp : mapExps) + mapQry.addSelectExpression(exp); + + mapQry.clearGroups(); + + for (int col : srcQry.groupColumns()) + mapQry.addGroupExpression(column(((GridSqlAlias)mapExps.get(col)).alias())); + + // TODO sort support + + // Reduce query. + GridSqlSelect rdcQry = new GridSqlSelect(); + + for (GridSqlElement rdcExp : rdcExps) + rdcQry.addSelectExpression(rdcExp); + + rdcQry.from(new GridSqlTable(null, table(0))); + + for (int col : srcQry.groupColumns()) + rdcQry.addGroupExpression(column(((GridSqlAlias)mapExps.get(col)).alias())); + + GridCacheTwoStepQuery res = new GridCacheTwoStepQuery(rdcQry.getSQL()); + + res.addMapQuery(table(0), mapQry.getSQL(), params); + + return res; + } + + /** + * @param mapSelect Selects for map query. + * @param rdcSelect Selects for reduce query. + * @param idx Index. + */ + private static void splitSelectExpression(List<GridSqlElement> mapSelect, GridSqlElement[] rdcSelect, int idx) { + GridSqlElement el = mapSelect.get(idx); + + GridSqlAlias alias = null; + + if (el instanceof GridSqlAlias) { // Unwrap from alias. + alias = (GridSqlAlias)el; + el = alias.child(); + } + + if (el instanceof GridSqlAggregateFunction) { + GridSqlAggregateFunction agg = (GridSqlAggregateFunction)el; + + GridSqlElement mapAgg, rdcAgg; + + String mapAggAlias = columnName(idx); + + switch (agg.type()) { + case AVG: // SUM( AVG(CAST(x AS DECIMAL))*COUNT(x) )/SUM( COUNT(x) ). + //-- COUNT(x) map + GridSqlElement cntMapAgg = aggregate(agg.distinct(), COUNT).addChild(agg.child()); + + // Add generated alias to COUNT(x). + // Using size as index since COUNT will be added as the last select element to the map query. + String cntMapAggAlias = columnName(mapSelect.size()); + + cntMapAgg = alias(cntMapAggAlias, cntMapAgg); + + mapSelect.add(cntMapAgg); + + //-- AVG(CAST(x AS DECIMAL)) map + mapAgg = aggregate(agg.distinct(), AVG).addChild( // Add function argument. + function(CAST).setCastType("DECIMAL").addChild(agg.child())); + + //-- SUM( AVG(x)*COUNT(x) )/SUM( COUNT(x) ) reduce + GridSqlElement sumUpRdc = aggregate(false, SUM).addChild( + op(GridSqlOperationType.MULTIPLY, + column(mapAggAlias), + column(cntMapAggAlias))); + + GridSqlElement sumDownRdc = aggregate(false, SUM).addChild(column(cntMapAggAlias)); + + rdcAgg = op(GridSqlOperationType.DIVIDE, sumUpRdc, sumDownRdc); + + break; + + case SUM: // SUM( SUM(x) ) + case MAX: // MAX( MAX(x) ) + case MIN: // MIN( MIN(x) ) + mapAgg = aggregate(agg.distinct(), agg.type()).addChild(agg.child()); + + rdcAgg = aggregate(agg.distinct(), agg.type()).addChild(column(mapAggAlias)); + + break; + + case COUNT_ALL: // CAST(SUM( COUNT(*) ) AS BIGINT) + case COUNT: // CAST(SUM( COUNT(x) ) AS BIGINT) + mapAgg = aggregate(agg.distinct(), agg.type()); + + if (agg.type() == COUNT) + mapAgg.addChild(agg.child()); + + rdcAgg = aggregate(false, SUM).addChild(column(mapAggAlias)); + + rdcAgg = function(CAST).setCastType("BIGINT").addChild(rdcAgg); + + break; + + default: + throw new IgniteException("Unsupported aggregate: " + agg.type()); + } + + assert !(mapAgg instanceof GridSqlAlias); + + // Add generated alias to map aggregate. + mapAgg = alias(mapAggAlias, mapAgg); + + if (alias != null) // Add initial alias if it was set. + rdcAgg = alias(alias.alias(), rdcAgg); + + // Set map and reduce aggregates to their places in selects. + mapSelect.set(idx, mapAgg); + + rdcSelect[idx] = rdcAgg; + } + else { + if (alias == null) { // Generate alias if none. + alias = alias(columnName(idx), mapSelect.get(idx)); + + mapSelect.set(idx, alias); + } + + if (idx < rdcSelect.length) + rdcSelect[idx] = column(alias.alias()); + } + } + + /** + * @param distinct Distinct. + * @param type Type. + * @return Aggregate function. + */ + private static GridSqlAggregateFunction aggregate(boolean distinct, GridSqlFunctionType type) { + return new GridSqlAggregateFunction(distinct, type); + } + + /** + * @param name Column name. + * @return Column. + */ + private static GridSqlColumn column(String name) { + return new GridSqlColumn(null, name, name); + } + + /** + * @param alias Alias. + * @param child Child. + * @return Alias. + */ + private static GridSqlAlias alias(String alias, GridSqlElement child) { + return new GridSqlAlias(alias, child); + } + + /** + * @param type Type. + * @param left Left expression. + * @param right Right expression. + * @return Binary operator. + */ + private static GridSqlOperation op(GridSqlOperationType type, GridSqlElement left, GridSqlElement right) { + return new GridSqlOperation(type, left, right); + } + + /** + * @param type Type. + * @return Function. + */ + private static GridSqlFunction function(GridSqlFunctionType type) { + return new GridSqlFunction(type); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java new file mode 100644 index 0000000..ee4d6c8 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSelect.java @@ -0,0 +1,287 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.result.*; +import org.h2.util.*; + +import java.util.*; + +/** + * Select query. + */ +public class GridSqlSelect implements Cloneable { + /** */ + private boolean distinct; + + /** */ + private List<GridSqlElement> allExprs; + + /** */ + private List<GridSqlElement> select = new ArrayList<>(); + + /** */ + private List<GridSqlElement> groups = new ArrayList<>(); + + /** */ + private int[] grpCols; + + /** */ + private GridSqlElement from; + + /** */ + private GridSqlElement where; + + /** */ + private GridSqlElement having; + + /** */ + private Map<GridSqlElement, Integer> sort = new LinkedHashMap<>(); + + /** + * @return Distinct. + */ + public boolean distinct() { + return distinct; + } + + /** + * @param distinct New distinct. + */ + public void distinct(boolean distinct) { + this.distinct = distinct; + } + + /** + * @return Generate sql. + */ + public String getSQL() { + StatementBuilder buff = new StatementBuilder("SELECT"); + + if (distinct) + buff.append(" DISTINCT"); + + for (GridSqlElement expression : select) { + buff.appendExceptFirst(","); + buff.append('\n'); + buff.append(StringUtils.indent(expression.getSQL(), 4, false)); + } + + buff.append("\nFROM ").append(from.getSQL()); + + if (where != null) + buff.append("\nWHERE ").append(StringUtils.unEnclose(where.getSQL())); + + if (!groups.isEmpty()) { + buff.append("\nGROUP BY "); + + buff.resetCount(); + + for (GridSqlElement expression : groups) { + buff.appendExceptFirst(", "); + + if (expression instanceof GridSqlAlias) + buff.append(StringUtils.unEnclose((expression.child().getSQL()))); + else + buff.append(StringUtils.unEnclose(expression.getSQL())); + } + } + + if (having != null) + buff.append("\nHAVING ").append(StringUtils.unEnclose(having.getSQL())); + + if (!sort.isEmpty()) { + buff.append("\nORDER BY "); + + buff.resetCount(); + + for (Map.Entry<GridSqlElement, Integer> entry : sort.entrySet()) { + buff.appendExceptFirst(", "); + + GridSqlElement expression = entry.getKey(); + + int idx = select.indexOf(expression); + + if (idx >= 0) + buff.append(idx + 1); + else + buff.append('=').append(StringUtils.unEnclose(expression.getSQL())); + + int type = entry.getValue(); + + if ((type & SortOrder.DESCENDING) != 0) + buff.append(" DESC"); + + if ((type & SortOrder.NULLS_FIRST) != 0) + buff.append(" NULLS FIRST"); + else if ((type & SortOrder.NULLS_LAST) != 0) + buff.append(" NULLS LAST"); + } + } + + return buff.toString(); + } + + /** + * @param expression Expression. + */ + public void addExpression(GridSqlElement expression) { + if (allExprs == null) + allExprs = new ArrayList<>(); + + allExprs.add(expression); + } + + /** + * @return All expressions in select, group by, order by. + */ + public List<GridSqlElement> allExpressions() { + return allExprs; + } + + /** + * @return Expressions. + */ + public List<GridSqlElement> select() { + return select; + } + + /** + * Clears select list. + */ + public void clearSelect() { + select = new ArrayList<>(); + } + + /** + * @param expression Expression. + */ + public void addSelectExpression(GridSqlElement expression) { + select.add(expression); + } + + /** + * @return Expressions. + */ + public List<GridSqlElement> groups() { + return groups; + } + + /** + * + */ + public void clearGroups() { + groups = new ArrayList<>(); + grpCols = null; + } + + /** + * @param expression Expression. + */ + public void addGroupExpression(GridSqlElement expression) { + groups.add(expression); + } + + /** + * @return Group columns. + */ + public int[] groupColumns() { + return grpCols; + } + + /** + * @param grpCols Group columns. + */ + public void groupColumns(int[] grpCols) { + this.grpCols = grpCols; + } + + /** + * @return Tables. + */ + public GridSqlElement from() { + return from; + } + + /** + * @param from From element. + */ + public void from(GridSqlElement from) { + this.from = from; + } + + /** + * @return Where. + */ + public GridSqlElement where() { + return where; + } + + /** + * @param where New where. + */ + public void where(GridSqlElement where) { + this.where = where; + } + + /** + * @return Having. + */ + public GridSqlElement having() { + return having; + } + + /** + * @param having New having. + */ + public void having(GridSqlElement having) { + this.having = having; + } + + /** + * @return Sort. + */ + public Map<GridSqlElement, Integer> sort() { + return sort; + } + + /** + * + */ + public void clearSort() { + sort = new LinkedHashMap<>(); + } + + /** + * @param expression Expression. + * @param sortType The sort type bit mask (SortOrder.DESCENDING, SortOrder.NULLS_FIRST, SortOrder.NULLS_LAST). + */ + public void addSort(GridSqlElement expression, int sortType) { + sort.put(expression, sortType); + } + + /** {@inheritDoc} */ + @SuppressWarnings({"CloneCallsConstructors", "CloneDoesntDeclareCloneNotSupportedException"}) + @Override public GridSqlSelect clone() { + try { + GridSqlSelect res = (GridSqlSelect)super.clone(); + + res.select = new ArrayList<>(select); + res.groups = new ArrayList<>(groups); + res.sort = new LinkedHashMap<>(sort); + res.allExprs = null; + + return res; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Never thrown. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSubquery.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSubquery.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSubquery.java new file mode 100644 index 0000000..7a558f0 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlSubquery.java @@ -0,0 +1,44 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +/** + * Subquery. + */ +public class GridSqlSubquery extends GridSqlElement { + /** */ + private GridSqlSelect select; + + /** + * @param select Select. + */ + public GridSqlSubquery(GridSqlSelect select) { + this.select = select; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return "(" + select.getSQL() + ")"; + } + + /** + * @return Select. + */ + public GridSqlSelect select() { + return select; + } + + /** + * @param select New select. + */ + public void select(GridSqlSelect select) { + this.select = select; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java new file mode 100644 index 0000000..c9c551f --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlTable.java @@ -0,0 +1,55 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +import org.h2.command.*; +import org.jetbrains.annotations.*; + +/** + * Table with optional schema. + */ +public class GridSqlTable extends GridSqlElement { + /** */ + private final String schema; + + /** */ + private final String tblName; + + /** + * @param schema Schema. + * @param tblName Table name. + */ + public GridSqlTable(@Nullable String schema, String tblName) { + this.schema = schema; + this.tblName = tblName; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + if (schema == null) + return Parser.quoteIdentifier(tblName); + + return Parser.quoteIdentifier(schema) + '.' + Parser.quoteIdentifier(tblName); + } + + /** + * @return Schema. + */ + public String schema() { + return schema; + } + + /** + * @return Table name. + */ + public String tableName() { + return tblName; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlValue.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlValue.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlValue.java new file mode 100644 index 0000000..41c8275 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlValue.java @@ -0,0 +1,17 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.sql; + +/** + * Marker interface for a simple value. + */ +public interface GridSqlValue { + // No-op. +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/2851b52b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java new file mode 100644 index 0000000..c572d5d --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/twostep/GridMapQueryExecutor.java @@ -0,0 +1,270 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.apache.ignite.internal.processors.query.h2.twostep; + +import org.apache.ignite.*; +import org.apache.ignite.cluster.*; +import org.apache.ignite.internal.*; +import org.apache.ignite.internal.processors.cache.*; +import org.apache.ignite.internal.processors.query.h2.*; +import org.apache.ignite.internal.util.typedef.*; +import org.apache.ignite.lang.*; +import org.apache.ignite.spi.indexing.*; +import org.gridgain.grid.kernal.processors.cache.query.*; +import org.apache.ignite.internal.processors.query.h2.twostep.messages.*; +import org.h2.jdbc.*; +import org.h2.result.*; +import org.h2.value.*; +import org.jdk8.backport.*; +import org.jetbrains.annotations.*; + +import java.lang.reflect.*; +import java.sql.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * Map query executor. + */ +public class GridMapQueryExecutor { + /** */ + private static final Field RESULT_FIELD; + + /** + * Initialize. + */ + static { + try { + RESULT_FIELD = JdbcResultSet.class.getDeclaredField("result"); + + RESULT_FIELD.setAccessible(true); + } + catch (NoSuchFieldException e) { + throw new IllegalStateException("Check H2 version in classpath.", e); + } + } + + /** */ + private IgniteLogger log; + + /** */ + private GridKernalContext ctx; + + /** */ + private IgniteH2Indexing h2; + + /** */ + private ConcurrentMap<UUID, ConcurrentMap<Long, QueryResults>> qryRess = new ConcurrentHashMap8<>(); + + /** + * @param ctx Context. + * @param h2 H2 Indexing. + * @throws IgniteCheckedException If failed. + */ + public void start(final GridKernalContext ctx, IgniteH2Indexing h2) throws IgniteCheckedException { + this.ctx = ctx; + this.h2 = h2; + + log = ctx.log(GridMapQueryExecutor.class); + + // TODO handle node failures. + + ctx.io().addUserMessageListener(GridTopic.TOPIC_QUERY, new IgniteBiPredicate<UUID, Object>() { + @Override public boolean apply(UUID nodeId, Object msg) { + assert msg != null; + + ClusterNode node = ctx.discovery().node(nodeId); + + if (msg instanceof GridQueryRequest) + executeLocalQuery(node, (GridQueryRequest)msg); + else if (msg instanceof GridNextPageRequest) + sendNextPage(node, (GridNextPageRequest)msg); + + return true; + } + }); + } + + /** + * @param node Node. + * @param req Query request. + */ + private void executeLocalQuery(ClusterNode node, GridQueryRequest req) { + h2.setFilters(new GridIndexingQueryFilter() { + @Nullable @Override public <K, V> IgniteBiPredicate<K, V> forSpace(String spaceName) { + final GridCacheAdapter<Object, Object> cache = ctx.cache().internalCache(spaceName); + + if (cache.context().isReplicated() || cache.configuration().getBackups() == 0) + return null; + + return new IgniteBiPredicate<K, V>() { + @Override public boolean apply(K k, V v) { + return cache.context().affinity().primary(ctx.discovery().localNode(), k, -1); + } + }; + } + }); + + try { + QueryResults qr = new QueryResults(req.requestId(), req.queries().size()); + + ConcurrentMap<Long, QueryResults> nodeRess = qryRess.get(node.id()); + + if (nodeRess == null) { + nodeRess = new ConcurrentHashMap8<>(); + + ConcurrentMap<Long, QueryResults> old = qryRess.putIfAbsent(node.id(), nodeRess); + + if (old != null) + nodeRess = old; + } + + QueryResults old = nodeRess.putIfAbsent(req.requestId(), qr); + + assert old == null; + + // Prepare snapshots for all the needed tables before actual run. + for (GridCacheSqlQuery qry : req.queries()) { + // TODO + } + + // Run queries. + int i = 0; + + for (GridCacheSqlQuery qry : req.queries()) { + ResultSet rs = h2.executeSqlQueryWithTimer(h2.connectionForSpace(null), qry.query(), + F.asList(qry.parameters())); + + assert rs instanceof JdbcResultSet : rs.getClass(); + + ResultInterface res = (ResultInterface)RESULT_FIELD.get(rs); + + qr.results[i] = res; + qr.resultSets[i] = rs; + + // Send the first page. + sendNextPage(node, qr, i, req.pageSize(), res.getRowCount()); + + i++; + } + } + catch (Throwable e) { + sendError(node, req.requestId(), e); + } + finally { + h2.setFilters(null); + } + } + + /** + * @param node Node. + * @param qryReqId Query request ID. + * @param err Error. + */ + private void sendError(ClusterNode node, long qryReqId, Throwable err) { + try { + ctx.io().sendUserMessage(F.asList(node), new GridQueryFailResponse(qryReqId, err)); + } + catch (IgniteCheckedException e) { + e.addSuppressed(err); + + log.error("Failed to send error message.", e); + } + } + + /** + * @param node Node. + * @param req Request. + */ + private void sendNextPage(ClusterNode node, GridNextPageRequest req) { + ConcurrentMap<Long, QueryResults> nodeRess = qryRess.get(node.id()); + + QueryResults qr = nodeRess == null ? null : nodeRess.get(req.queryRequestId()); + + if (qr == null) + sendError(node, req.queryRequestId(), + new IllegalStateException("No query result found for request: " + req)); + else + sendNextPage(node, qr, req.query(), req.pageSize(), -1); + } + + /** + * @param node Node. + * @param qr Query results. + * @param qry Query. + * @param pageSize Page size. + * @param allRows All rows count. + */ + private void sendNextPage(ClusterNode node, QueryResults qr, int qry, int pageSize, int allRows) { + int page; + + List<Value[]> rows = new ArrayList<>(Math.min(64, pageSize)); + + ResultInterface res = qr.results[qry]; + + assert res != null; + + boolean last = false; + + synchronized (res) { + page = qr.pages[qry]++; + + for (int i = 0 ; i < pageSize; i++) { + if (!res.next()) { + last = true; + + break; + } + + rows.add(res.currentRow()); + } + } + + try { + ctx.io().sendUserMessage(F.asList(node), + new GridNextPageResponse(qr.qryReqId, qry, page, allRows, last, rows), + GridTopic.TOPIC_QUERY, false, 0); + } + catch (IgniteCheckedException e) { + log.error("Failed to send message.", e); + + throw new IgniteException(e); + } + } + + /** + * + */ + private static class QueryResults { + /** */ + private long qryReqId; + + /** */ + private ResultInterface[] results; + + /** */ + private ResultSet[] resultSets; + + /** */ + private int[] pages; + + /** + * @param qryReqId Query request ID. + * @param qrys Queries. + */ + private QueryResults(long qryReqId, int qrys) { + this.qryReqId = qryReqId; + + results = new ResultInterface[qrys]; + resultSets = new ResultSet[qrys]; + pages = new int[qrys]; + } + } +}