ignite-gg9499 - sql parser
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/0497d7f0 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/0497d7f0 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/0497d7f0 Branch: refs/heads/ignite-gg9499 Commit: 0497d7f0b8bb28eee878bc6a883c0b6a58dda797 Parents: b16f120 Author: S.Vladykin <svlady...@gridgain.com> Authored: Tue Dec 23 23:09:09 2014 +0300 Committer: S.Vladykin <svlady...@gridgain.com> Committed: Tue Dec 23 23:09:09 2014 +0300 ---------------------------------------------------------------------- .../query/h2/sql/GridAggregateFunction.java | 70 ++++ .../processors/query/h2/sql/GridAlias.java | 63 ++++ .../query/h2/sql/GridFunctionType.java | 62 ++++ .../processors/query/h2/sql/GridJoin.java | 60 +++ .../processors/query/h2/sql/GridOperation.java | 75 ++++ .../query/h2/sql/GridOperationType.java | 202 ++++++++++ .../query/h2/sql/GridQueryParser.java | 370 +++++++++++++++++++ .../processors/query/h2/sql/GridQueryUtils.java | 148 ++++++++ .../processors/query/h2/sql/GridSelect.java | 241 ++++++++++++ .../processors/query/h2/sql/GridSqlColumn.java | 56 +++ .../processors/query/h2/sql/GridSqlElement.java | 68 ++++ .../query/h2/sql/GridSqlFunction.java | 117 ++++++ .../query/h2/sql/GridSqlParameter.java | 44 +++ .../processors/query/h2/sql/GridSqlValue.java | 17 + .../processors/query/h2/sql/GridSubquery.java | 44 +++ .../processors/query/h2/sql/GridTable.java | 55 +++ .../query/h2/sql/GridValueExpression.java | 39 ++ .../processors/query/h2/sql/GridQueryTest.java | 248 +++++++++++++ 18 files changed, 1979 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAggregateFunction.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAggregateFunction.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAggregateFunction.java new file mode 100644 index 0000000..dffee20 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAggregateFunction.java @@ -0,0 +1,70 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.util.*; + +import static org.gridgain.grid.kernal.processors.query.h2.sql.GridFunctionType.*; + +/** + * Aggregate function. + */ +public class GridAggregateFunction extends GridSqlFunction { + + /** */ + private static final GridFunctionType[] TYPE_INDEX = new GridFunctionType[]{ + 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 GridAggregateFunction(boolean distinct, GridFunctionType type) { + super(type); + + this.distinct = distinct; + } + + /** + * @param distinct Distinct. + * @param typeId Type. + */ + public GridAggregateFunction(boolean distinct, int typeId) { + this(distinct, TYPE_INDEX[typeId]); + } + + /** {@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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAlias.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAlias.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAlias.java new file mode 100644 index 0000000..7c9a080 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridAlias.java @@ -0,0 +1,63 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.command.*; +import org.jetbrains.annotations.*; + +/** + * Alias for column or table. + */ +public class GridAlias extends GridSqlElement { + + /** */ + private final String alias; + + /** */ + private final boolean useAs; + + /** + * @param alias Alias. + * @param expr Expr. + */ + public GridAlias(@NotNull String alias, @NotNull GridSqlElement expr) { + this(alias, expr, false); + } + + /** + * @param alias Alias. + * @param expr Expr. + * @param useAs Use 'AS' keyword. + */ + public GridAlias(@NotNull String alias, @NotNull 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. + */ + @NotNull + public String alias() { + return alias; + } + + /** {@inheritDoc} */ + public GridSqlElement getNonAliasExpression() { + return child(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridFunctionType.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridFunctionType.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridFunctionType.java new file mode 100644 index 0000000..ba01dd5 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridFunctionType.java @@ -0,0 +1,62 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.expression.*; + +/** + * Full list of available functions see at {@link Function} + */ +public enum GridFunctionType { + ABS(1), CEIL(1), FLOOR(1), COS(1), PI(0), POWER(2), RAND(-1), ROUND(1), + CASE(-1), CAST(1), CONVERT(1), EXTRACT(2), + DAY_OF_MONTH(1), DAY_OF_WEEK(1), DAY_OF_YEAR(1), + + // Aggregate functions. + COUNT_ALL("COUNT(*)", 0), COUNT(1), GROUP_CONCAT(1), SUM(1), MIN(1), MAX(1), AVG(1), STDDEV_POP(1), STDDEV_SAMP(1), + VAR_POP(1), VAR_SAMP(1), BOOL_OR(1), BOOL_AND(1), SELECTIVITY(1), HISTOGRAM(1); + + /** */ + private final String name; + + /** */ + private final int argCnt; + + /** + * @param argCnt Count. + */ + GridFunctionType(int argCnt) { + name = name(); + this.argCnt = argCnt; + } + + /** + * @param name Name. + * @param argCnt Count. + */ + GridFunctionType(String name, int argCnt) { + this.name = name; + this.argCnt = argCnt; + } + + /** + * @return Argument count. + */ + public int argumentCount() { + return argCnt; + } + + /** + * @return Function name. + */ + public String functionName() { + return name; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridJoin.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridJoin.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridJoin.java new file mode 100644 index 0000000..d703fe7 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridJoin.java @@ -0,0 +1,60 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.util.*; + +/** + * + */ +public class GridJoin extends GridSqlElement { + + /** */ + private final GridSqlElement tbl1; + + /** */ + private final GridSqlElement tbl2; + + /** + * @param tbl1 Table 1. + * @param tbl2 Table 2. + */ + public GridJoin(GridSqlElement tbl1, GridSqlElement tbl2) { + this.tbl1 = tbl1; + this.tbl2 = tbl2; + } + + /** + * @return Table 1. + */ + public GridSqlElement table1() { + return tbl1; + } + + /** + * @return Table 2. + */ + public GridSqlElement table2() { + return tbl2; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + StatementBuilder buff = new StatementBuilder(); + + buff.append(tbl1.getSQL()); + + buff.append(" \n INNER JOIN "); + + buff.append(tbl2.getSQL()); + + return buff.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperation.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperation.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperation.java new file mode 100644 index 0000000..c38ae85 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperation.java @@ -0,0 +1,75 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.jetbrains.annotations.*; + +/** + * + */ +public class GridOperation extends GridSqlElement { + /** */ + private final GridOperationType opType; + + /** + * @param opType Operation type. + */ + public GridOperation(@NotNull GridOperationType opType) { + this.opType = opType; + } + + /** + * @param opType Op type. + * @param arg argument. + */ + public GridOperation(@NotNull GridOperationType opType, @NotNull GridSqlElement arg) { + this(opType); + + addChild(arg); + } + + /** + * @param opType Op type. + * @param left Left. + * @param right Right. + */ + public GridOperation(@NotNull GridOperationType opType, @NotNull GridSqlElement left, @NotNull 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 GridOperationType opType() { + return opType; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return opType.toSql(this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperationType.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperationType.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperationType.java new file mode 100644 index 0000000..8c56731 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridOperationType.java @@ -0,0 +1,202 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.util.*; + +/** + * + */ +public enum GridOperationType { + // 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. + */ + GridOperationType(int childrenCnt, SqlGenerator sqlGenerator) { + this.childrenCnt = childrenCnt; + this.sqlGenerator = sqlGenerator; + } + + /** + * @param operation Operation. + */ + public String toSql(GridOperation operation) { + return sqlGenerator.getSql(operation); + } + + /** + * @return Children count. + */ + public int childrenCount() { + return childrenCnt; + } + + /** + * + */ + private static interface SqlGenerator { + + /** + * @param operation Operation expression. + */ + public String getSql(GridOperation 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(GridOperation 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(GridOperation 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(GridOperation 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(GridOperation operation) { + assert operation.opType().childrenCnt == 1; + + return '(' + operation.child().getSQL() + ' ' + text + ')'; + } + } + + /** + * + */ + private static class ConditionInSqlGenerator implements SqlGenerator { + + /** {@inheritDoc} */ + @Override public String getSql(GridOperation 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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryParser.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryParser.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryParser.java new file mode 100644 index 0000000..d31bee3 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryParser.java @@ -0,0 +1,370 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.apache.ignite.*; +import org.gridgain.grid.kernal.processors.query.h2.sql.GridQueryUtils.*; +import org.h2.command.dml.*; +import org.h2.expression.*; +import org.h2.result.*; +import org.h2.table.*; +import org.jetbrains.annotations.*; + +import java.lang.reflect.*; +import java.util.*; +import java.util.Set; + +import static org.gridgain.grid.kernal.processors.query.h2.sql.GridOperationType.*; +import static org.gridgain.grid.kernal.processors.query.h2.sql.GridQueryUtils.*; + +/** + * H2 Query parser. + */ +@SuppressWarnings("TypeMayBeWeakened") +public class GridQueryParser { + /** */ + private static final GridOperationType[] OPERATION_OP_TYPES = new GridOperationType[]{CONCAT, PLUS, MINUS, MULTIPLY, DIVIDE, null, MODULUS}; + + /** */ + private static final GridOperationType[] COMPARISON_TYPES = new GridOperationType[]{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 Field GROUP_INDEXES = GridQueryUtils.getField(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 final IdentityHashMap<Object, Object> h2ObjToGridObj = new IdentityHashMap<>(); + + /** + * @param filter Filter. + */ + private GridSqlElement toGridTableFilter(TableFilter filter) { + GridSqlElement res = (GridSqlElement)h2ObjToGridObj.get(filter); + + if (res == null) { + Table tbl = filter.getTable(); + + if (tbl instanceof TableBase) + res = new GridTable(tbl.getSchema().getName(), tbl.getName()); + else if (tbl instanceof TableView) { + Query qry = GridQueryUtils.getFieldValue(tbl, "viewQuery"); + + assert0(qry instanceof Select, qry); + + res = new GridSubquery(toGridSelect((Select)qry)); + } + else + throw new IgniteException("Unsupported query: " + filter); + + String alias = GridQueryUtils.getFieldValue(filter, "alias"); + + if (alias != null) + res = new GridAlias(alias, res, false); + + h2ObjToGridObj.put(filter, res); + } + + return res; + } + + /** + * @param select Select. + */ + public GridSelect toGridSelect(Select select) { + GridSelect res = (GridSelect)h2ObjToGridObj.get(select); + + if (res != null) + return res; + + res = new GridSelect(); + + h2ObjToGridObj.put(select, res); + + res.distinct(select.isDistinct()); + + Expression where = CONDITION.get(select); + res.where(toGridExpression(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 = toGridTableFilter(filter); + + from = from == null ? gridFilter : new GridJoin(from, gridFilter); + + allFilers.remove(filter); + + filter = filter.getJoin(); + } + while (filter != null); + + res.from(from); + + assert allFilers.isEmpty(); + + ArrayList<Expression> expressions = select.getExpressions(); + + int[] grpIdx = GridQueryUtils.getFieldValue(GROUP_INDEXES, select); + + if (grpIdx != null) { + for (int idx : grpIdx) + res.addGroupExpression(toGridExpression(expressions.get(idx))); + } + + assert0(select.getHaving() == null, select); + + int havingIdx = GridQueryUtils.getFieldValue(select, "havingIndex"); + + if (havingIdx >= 0) + res.having(toGridExpression(expressions.get(havingIdx))); + + for (int i = 0; i < select.getColumnCount(); i++) + res.addSelectExpression(toGridExpression(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(toGridExpression(expressions.get(indexes[i])), sortTypes[i]); + } + + return res; + } + + /** + * @param expression Expression. + */ + private GridSqlElement toGridExpression(@Nullable Expression expression) { + if (expression == null) + return null; + + GridSqlElement res = (GridSqlElement)h2ObjToGridObj.get(expression); + + if (res == null) { + res = toGridExpression0(expression); + + h2ObjToGridObj.put(expression, res); + } + + return res; + } + + /** + * @param expression Expression. + */ + @NotNull private GridSqlElement toGridExpression0(@NotNull Expression expression) { + if (expression instanceof ExpressionColumn) { + TableFilter tblFilter = ((ExpressionColumn)expression).getTableFilter(); + + GridSqlElement gridTblFilter = toGridTableFilter(tblFilter); + + return new GridSqlColumn(gridTblFilter, expression.getColumnName(), expression.getSQL()); + } + + if (expression instanceof Alias) + return new GridAlias(expression.getAlias(), toGridExpression(expression.getNonAliasExpression()), true); + + if (expression instanceof ValueExpression) + return new GridValueExpression(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 GridOperation(GridOperationType.NEGATE, toGridExpression(OPERATION_LEFT.get(operation))); + } + + return new GridOperation(OPERATION_OP_TYPES[type], + toGridExpression(OPERATION_LEFT.get(operation)), + toGridExpression(OPERATION_RIGHT.get(operation))); + } + + if (expression instanceof Comparison) { + Comparison cmp = (Comparison)expression; + + GridOperationType opType = COMPARISON_TYPES[COMPARISON_TYPE.get(cmp)]; + + assert opType != null : COMPARISON_TYPE.get(cmp); + + GridSqlElement left = toGridExpression(COMPARISON_LEFT.get(cmp)); + + if (opType.childrenCount() == 1) + return new GridOperation(opType, left); + + GridSqlElement right = toGridExpression(COMPARISON_RIGHT.get(cmp)); + + return new GridOperation(opType, left, right); + } + + if (expression instanceof ConditionNot) + return new GridOperation(NOT, toGridExpression(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 GridOperation(type == ConditionAndOr.AND ? AND : OR, + toGridExpression(ANDOR_LEFT.get(andOr)), toGridExpression(ANDOR_RIGHT.get(andOr))); + } + + if (expression instanceof Subquery) { + Query qry = ((Subquery)expression).getQuery(); + + assert0(qry instanceof Select, expression); + + return new GridSubquery(toGridSelect((Select)qry)); + } + + if (expression instanceof ConditionIn) { + GridOperation res = new GridOperation(IN); + + res.addChild(toGridExpression((Expression)GridQueryUtils.getFieldValue(expression, "left"))); + + List<Expression> vals = GridQueryUtils.getFieldValue(expression, "valueList"); + + for (Expression val : vals) + res.addChild(toGridExpression(val)); + + return res; + } + + if (expression instanceof ConditionInConstantSet) { + GridOperation res = new GridOperation(IN); + + res.addChild(toGridExpression((Expression)GridQueryUtils.getFieldValue(expression, "left"))); + + List<Expression> vals = GridQueryUtils.getFieldValue(expression, "valueList"); + + for (Expression val : vals) + res.addChild(toGridExpression(val)); + + return res; + } + + if (expression instanceof ConditionInSelect) { + GridOperation res = new GridOperation(IN); + + boolean all = GridQueryUtils.getFieldValue(expression, "all"); + int compareType = GridQueryUtils.getFieldValue(expression, "compareType"); + + assert0(!all, expression); + assert0(compareType == Comparison.EQUAL, expression); + + res.addChild(toGridExpression((Expression)GridQueryUtils.getFieldValue(expression, "left"))); + + Query qry = GridQueryUtils.getFieldValue(expression, "query"); + + assert0(qry instanceof Select, qry); + + res.addChild(new GridSubquery(toGridSelect((Select)qry))); + + return res; + } + + if (expression instanceof CompareLike) { + assert0(GridQueryUtils.getFieldValue(expression, "escape") == null, expression); + + boolean regexp = GridQueryUtils.getFieldValue(expression, "regexp"); + + return new GridOperation(regexp ? REGEXP : LIKE, + toGridExpression((Expression)GridQueryUtils.getFieldValue(expression, "left")), + toGridExpression((Expression)GridQueryUtils.getFieldValue(expression, "right"))); + } + + if (expression instanceof Function) { + Function function = (Function)expression; + + GridSqlFunction res = new GridSqlFunction(function.getName()); + + for (Expression arg : function.getArgs()) + res.addChild(toGridExpression(arg)); + + return res; + } + + if (expression instanceof Parameter) + return new GridSqlParameter(((Parameter)expression).getIndex()); + + if (expression instanceof Aggregate) { + GridAggregateFunction res = new GridAggregateFunction(GridQueryUtils.<Boolean>getFieldValue(expression, + "distinct"), GridQueryUtils.<Integer>getFieldValue(expression, "type")); + + Expression on = GridQueryUtils.getFieldValue(expression, "on"); + + if (on != null) + res.addChild(toGridExpression(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); + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryUtils.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryUtils.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryUtils.java new file mode 100644 index 0000000..0ffac88 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryUtils.java @@ -0,0 +1,148 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.table.*; + +import java.lang.reflect.*; +import java.util.*; + +/** + * + */ +public class GridQueryUtils { + + /** + * @param res + * @param tbl + */ + public static void extractTables(Set<TableBase> res, Table tbl) { + if (tbl instanceof TableBase) { + res.add((TableBase)tbl); + + return; + } + + if (tbl instanceof TableView) { + List<Table> tableList = getTables((TableView)tbl); + + for (Table table : tableList) { + extractTables(res, table); + } + } + else + throw new IllegalArgumentException("Unknown table type: " + tbl); + } + + /** + * @param view Table view. + * @return List of tables. + */ + public static List<Table> getTables(TableView view) { + try { + Field field = TableView.class.getDeclaredField("tables"); + + field.setAccessible(true); + + return (List<Table>)field.get(view); + } + catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + /** + * + * @param fld + * @param obj + * @param <T> + * @return + */ + public static <T> T getFieldValue(Field fld, Object obj) { + try { + return (T)fld.get(obj); + } + catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + /** + * @param cls + * @param fieldName + * @return + */ + public static Field getField(Class<?> cls, String fieldName) { + Field field; + try { + field = cls.getDeclaredField(fieldName); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + + field.setAccessible(true); + + return field; + } + + /** + * @param cls Class. + * @param obj Object. + * @param fldName Fld name. + */ + public static <C> Object getFieldValue(Class<? super C> cls, C obj, String fldName) { + Field field = getField(cls, fldName); + + return getFieldValue(field, obj); + } + + /** + * @param obj Object. + * @param fldName Fld name. + */ + public static <T> T getFieldValue(Object obj, String fldName) { + return (T)getFieldValue((Class<? super Object>)obj.getClass(), obj, fldName); + } + + /** + * @param cls Class. + * @param fldName Fld name. + */ + public static <T, R> Getter<T, R> getter(Class<T> cls, String fldName) { + Field field = getField(cls, fldName); + + return new Getter<>(field); + } + + /** + * + */ + @SuppressWarnings("PublicInnerClass") + public 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) { + return getFieldValue(fld, obj); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSelect.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSelect.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSelect.java new file mode 100644 index 0000000..30a50e8 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSelect.java @@ -0,0 +1,241 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.result.*; +import org.h2.util.*; + +import java.util.*; + +/** + * + */ +public class GridSelect implements Cloneable { + /** */ + private boolean distinct; + + /** */ + private List<GridSqlElement> select = new ArrayList<>(); + + /** */ + private List<GridSqlElement> groups = new ArrayList<>(); + + /** */ + 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 GridAlias) + buff.append(StringUtils.unEnclose(((GridAlias)expression).getNonAliasExpression().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(); + } + + /** + * @return Expressions. + */ + public List<GridSqlElement> select() { + return select; + } + + /** + * @param expression Expression. + */ + public void addSelectExpression(GridSqlElement expression) { + select.add(expression); + } + + /** + * @return Expressions. + */ + public List<GridSqlElement> groups() { + return groups; + } + + /** + * + */ + public void clearGroups() { + groups.clear(); + } + + /** + * @param expression Expression. + */ + public void addGroupExpression(GridSqlElement expression) { + groups.add(expression); + } + + /** + * @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.clear(); + } + + /** + * @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 GridSelect clone() { + try { + GridSelect res = (GridSelect)super.clone(); + + res.select = new ArrayList<>(select); + res.groups = new ArrayList<>(groups); + res.sort = new LinkedHashMap<>(sort); + + return res; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Never thrown. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlColumn.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlColumn.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlColumn.java new file mode 100644 index 0000000..046d9ca --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlColumn.java @@ -0,0 +1,56 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +/** + * + */ +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) { + 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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlElement.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlElement.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlElement.java new file mode 100644 index 0000000..f569261 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlElement.java @@ -0,0 +1,68 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.jetbrains.annotations.*; + +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. + */ + public void addChild(@NotNull GridSqlElement expr) { + children.add(expr); + } + + /** + * + */ + @NotNull public GridSqlElement child() { + return children.get(0); + } + + /** + * + */ + @NotNull public GridSqlElement child(int idx) { + return children.get(idx); + } + + /** {@inheritDoc} */ + @SuppressWarnings("CloneCallsConstructors") + @Override protected GridSqlElement clone() { + try { + GridSqlElement res = (GridSqlElement)super.clone(); + + res.children = new ArrayList<>(children); + + return res; + } + catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlFunction.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlFunction.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlFunction.java new file mode 100644 index 0000000..5e9e112 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlFunction.java @@ -0,0 +1,117 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.util.*; +import org.h2.value.*; + +import java.util.*; + +/** + * Function. + */ +public class GridSqlFunction extends GridSqlElement { + /** */ + private static final Map<String, GridFunctionType> TYPE_MAP = new HashMap<>(); + + /** + * + */ + static { + for (GridFunctionType type : GridFunctionType.values()) + TYPE_MAP.put(type.name(), type); + } + + /** */ + private final String name; + + /** */ + protected final GridFunctionType type; + + /** + * @param type Function type. + */ + public GridSqlFunction(GridFunctionType type) { + name = type.functionName(); + + this.type = type; + + if (type == GridFunctionType.CAST || type == GridFunctionType.CONVERT) + throw new UnsupportedOperationException(); + } + + /** + * @param name Name. + */ + public GridSqlFunction(String name) { + this(TYPE_MAP.get(name)); + + if (type == GridFunctionType.CAST || type == GridFunctionType.CONVERT) + throw new UnsupportedOperationException(); + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + StatementBuilder buff = new StatementBuilder(name); + + if (type == GridFunctionType.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 == GridFunctionType.CAST) { + throw new UnsupportedOperationException("CAST"); +// buff.append(child().getSQL()).append(" AS "). +// append(new Column(null, dataType, precision, scale, displaySize).getCreateSQL()); + } + else if (type == GridFunctionType.CONVERT) { + throw new UnsupportedOperationException("CONVERT"); +// buff.append(args[0].getSQL()).append(','). +// append(new Column(null, dataType, precision, scale, displaySize).getCreateSQL()); + } + else if (type == GridFunctionType.EXTRACT) { + ValueString v = (ValueString) ((GridValueExpression)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 GridFunctionType type() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlParameter.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlParameter.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlParameter.java new file mode 100644 index 0000000..0b7da9e --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlParameter.java @@ -0,0 +1,44 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlValue.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlValue.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlValue.java new file mode 100644 index 0000000..62765e7 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSqlValue.java @@ -0,0 +1,17 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSubquery.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSubquery.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSubquery.java new file mode 100644 index 0000000..79dc7c0 --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridSubquery.java @@ -0,0 +1,44 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +/** + * Subquery. + */ +public class GridSubquery extends GridSqlElement { + /** */ + private GridSelect select; + + /** + * @param select Select. + */ + public GridSubquery(GridSelect select) { + this.select = select; + } + + /** {@inheritDoc} */ + @Override public String getSQL() { + return "(" + select.getSQL() + ")"; + } + + /** + * @return Select. + */ + public GridSelect select() { + return select; + } + + /** + * @param select New select. + */ + public void select(GridSelect select) { + this.select = select; + } +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridTable.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridTable.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridTable.java new file mode 100644 index 0000000..53b3b3f --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridTable.java @@ -0,0 +1,55 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.command.*; +import org.jetbrains.annotations.*; + +/** + * Table with optional schema. + */ +public class GridTable extends GridSqlElement { + /** */ + private final String schema; + + /** */ + private final String tblName; + + /** + * @param schema Schema. + * @param tblName Table name. + */ + public GridTable(@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/0497d7f0/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridValueExpression.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridValueExpression.java b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridValueExpression.java new file mode 100644 index 0000000..05c5c6c --- /dev/null +++ b/modules/indexing/src/main/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridValueExpression.java @@ -0,0 +1,39 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.h2.value.*; + +/** + * Constant value. + */ +public class GridValueExpression extends GridSqlElement implements GridSqlValue { + /** */ + private final Value val; + + /** + * @param val Value. + */ + public GridValueExpression(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/0497d7f0/modules/indexing/src/test/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryTest.java b/modules/indexing/src/test/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryTest.java new file mode 100644 index 0000000..4feaba6 --- /dev/null +++ b/modules/indexing/src/test/java/org/gridgain/grid/kernal/processors/query/h2/sql/GridQueryTest.java @@ -0,0 +1,248 @@ +/* @java.file.header */ + +/* _________ _____ __________________ _____ + * __ ____/___________(_)______ /__ ____/______ ____(_)_______ + * _ / __ __ ___/__ / _ __ / _ / __ _ __ `/__ / __ __ \ + * / /_/ / _ / _ / / /_/ / / /_/ / / /_/ / _ / _ / / / + * \____/ /_/ /_/ \_,__/ \____/ \__,_/ /_/ /_/ /_/ + */ + +package org.gridgain.grid.kernal.processors.query.h2.sql; + +import org.gridgain.grid.cache.*; +import org.gridgain.grid.cache.query.*; +import org.gridgain.grid.kernal.*; +import org.gridgain.grid.kernal.processors.cache.*; +import org.gridgain.grid.kernal.processors.query.*; +import org.gridgain.grid.kernal.processors.query.h2.*; +import org.h2.command.*; +import org.h2.command.dml.*; +import org.h2.engine.*; +import org.h2.jdbc.*; + +import java.io.*; +import java.util.*; + +/** + * + */ +public class GridQueryTest extends GridCacheAbstractQuerySelfTest { + /** {@inheritDoc} */ + @Override protected int gridCount() { + return 1; + } + + /** {@inheritDoc} */ + @Override protected GridCacheMode cacheMode() { + return GridCacheMode.REPLICATED; + } + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + GridCache cache = ignite.cache(null); + + cache.putx("testAddr", new Address()); + cache.putx("testPerson", new Person()); + } + + /** + * + */ + public void testAllExampless() throws Exception { + checkQuery("select name, date from Person"); + checkQuery("select distinct name, date from Person"); + checkQuery("select * from Person p"); + checkQuery("select * from Person"); + checkQuery("select distinct * from Person"); + checkQuery("select p.name, date from Person p"); + + checkQuery("select * from Person p, Address a"); + checkQuery("select * from Person, Address"); + checkQuery("select p.* from Person p, Address a"); + checkQuery("select person.* from Person, Address a"); + checkQuery("select p.*, street from Person p, Address a"); + checkQuery("select p.name, a.street from Person p, Address a"); + checkQuery("select distinct p.name, a.street from Person p, Address a"); + checkQuery("select distinct name, street from Person, Address"); + checkQuery("select p1.name, a2.street from Person p1, Address a1, Person p2, Address a2"); + + checkQuery("select p.name n, a.street s from Person p, Address a"); + checkQuery("select p.name, 1 as i, 'aaa' s from Person p"); + + checkQuery("select p.name + 'a', 1 * 3 as i, 'aaa' s, -p.old, -p.old as old from Person p"); + checkQuery("select p.name || 'a' + p.name, (p.old * 3) % p.old - p.old / p.old, p.name = 'aaa', " + + " p.name is p.name, p.old > 0, p.old >= 0, p.old < 0, p.old <= 0, p.old <> 0, p.old is not p.old, " + + " p.old is null, p.old is not null " + + " from Person p"); + + checkQuery("select p.name from Person p where name <> 'ivan'"); + checkQuery("select p.name from Person p where name like 'i%'"); + checkQuery("select p.name from Person p where name regexp 'i%'"); + checkQuery("select p.name from Person p, Address a where p.name <> 'ivan' and a.id > 10 or not (a.id = 100)"); + + checkQuery("select case p.name when 'a' then 1 when 'a' then 2 end as a from Person p"); + checkQuery("select case p.name when 'a' then 1 when 'a' then 2 else -1 end as a from Person p"); + + checkQuery("select abs(p.old) from Person p"); + checkQuery("select * from Person p where p.name in ('a', 'b', '_' + RAND())"); // test ConditionIn + checkQuery("select * from Person p where p.name in ('a', 'b', 'c')"); // test ConditionInConstantSet + checkQuery("select * from Person p where p.name in (select a.street from Address a)"); // test ConditionInConstantSet + + checkQuery("select (select a.street from Address a where a.id = p.addrId) from Person p"); // test ConditionInConstantSet + + checkQuery("select p.name, ? from Person p where name regexp ? and p.old < ?"); + + checkQuery("select count(*) as a from Person"); + checkQuery("select count(*) as a, count(p.*), count(p.name) from Person p"); + checkQuery("select count(distinct p.name) from Person p"); + + checkQuery("select p.name, avg(p.old), max(p.old) from Person p group by p.name"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by n"); + + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.addrId, p.name"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name, p.addrId"); + checkQuery("select p.name n, max(p.old) + min(p.old) / count(distinct p.old) from Person p group by p.name"); + checkQuery("select p.name n, max(p.old) maxOld, min(p.old) minOld from Person p group by p.name having maxOld > 10 and min(p.old) < 1"); + + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by n"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by p.name"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by p.name, m"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by p.name, max(p.old) desc"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by p.name nulls first"); + checkQuery("select p.name n, avg(p.old) a, max(p.old) m from Person p group by p.name order by p.name nulls last"); + checkQuery("select p.name n from Person p order by p.old + 10"); + checkQuery("select p.name n from Person p order by p.old + 10, p.name"); + checkQuery("select p.name n from Person p order by p.old + 10, p.name desc"); + + checkQuery("select p.name n from Person p, (select a.street from Address a where a.street is not null) "); + checkQuery("select street from Person p, (select a.street from Address a where a.street is not null) "); + checkQuery("select addr.street from Person p, (select a.street from Address a where a.street is not null) addr"); + } + + /** + * + */ + public void testExample1() throws Exception { + Select select = parse("select p.name n, max(p.old) maxOld, min(p.old) minOld from Person p group by p.name having maxOld > 10 and min(p.old) < 1"); + + GridQueryParser ses = new GridQueryParser(); + + GridSelect gridSelect = ses.toGridSelect(select); + + //System.out.println(select.getPlanSQL()); + System.out.println(gridSelect.getSQL()); + } + + /** + * + */ + private JdbcConnection connection() throws Exception { + GridKernalContext ctx = ((GridKernal)ignite).context(); + + GridQueryProcessor qryProcessor = ctx.query(); + + GridH2Indexing idx = (GridH2Indexing)GridQueryUtils.getFieldValue(GridQueryProcessor.class, qryProcessor, + "idx"); + + return (JdbcConnection)idx.connectionForSpace(null); + } + + /** + * @param sql Sql. + */ + private GridSelect toGridSelect(String sql) throws Exception { + Session ses = (Session)connection().getSession(); + + Select select = (Select)ses.prepare(sql); + + return new GridQueryParser().toGridSelect(select); + } + + /** + * @param sql Sql. + */ + private <T extends Prepared> T parse(String sql) throws Exception { + Session ses = (Session)connection().getSession(); + + return (T)ses.prepare(sql); + } + + /** + * @param sql1 Sql 1. + * @param sql2 Sql 2. + */ + private void assertSqlEquals(String sql1, String sql2) { + assertEquals(normalizeSql(sql1), normalizeSql(sql2)); + } + + /** + * @param sql Sql. + */ + private static String normalizeSql(String sql) { + return sql.toLowerCase() + .replaceAll("/\\*(?:.|\r|\n)*?\\*/", " ") + .replaceAll("\\s*on\\s+1\\s*=\\s*1\\s*", " ") + .replaceAll("\\s+", " ") + .replaceAll("\\( +", "(") + .replaceAll(" +\\)", ")") + .trim(); + } + + /** + * @param qry Query. + */ + private void checkQuery(String qry) throws Exception { + Prepared prepared = parse(qry); + + GridQueryParser ses = new GridQueryParser(); + + String res; + + if (prepared instanceof Select) + res = ses.toGridSelect((Select)prepared).getSQL(); + else + throw new UnsupportedOperationException(); + + assertSqlEquals(prepared.getPlanSQL(), res); + + System.out.println(normalizeSql(res)); + } + + /** + * + */ + public static class Person implements Serializable { + @GridCacheQuerySqlField(index = true) + public Date date = new Date(); + + @GridCacheQuerySqlField(index = true) + public String name = "Ivan"; + + @GridCacheQuerySqlField(index = true) + public String parentName; + + @GridCacheQuerySqlField(index = true) + public int addrId; + + @GridCacheQuerySqlField(index = true) + public int old; + } + + /** + * + */ + public static class Address implements Serializable { + @GridCacheQuerySqlField(index = true) + public int id; + + @GridCacheQuerySqlField(index = true) + public int streetNumber; + + @GridCacheQuerySqlField(index = true) + public String street = "Nevskiy"; + } + +}