This is an automated email from the ASF dual-hosted git repository. vozerov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 3524eb9 IGNITE-11118: SQL: Type conversions for thin client partition pruning. This closes #6016. 3524eb9 is described below commit 3524eb9d42bd865d28326c4f82070928c5e5fd75 Author: sanpwc <lapin1...@gmail.com> AuthorDate: Thu Feb 7 16:46:27 2019 +0300 IGNITE-11118: SQL: Type conversions for thin client partition pruning. This closes #6016. --- .../sql/optimizer/affinity/PartitionAllNode.java | 2 +- ...onResolver.java => PartitionClientContext.java} | 19 +- .../optimizer/affinity/PartitionCompositeNode.java | 27 +- .../optimizer/affinity/PartitionConstantNode.java | 2 +- .../optimizer/affinity/PartitionDataTypeUtils.java | 528 +++++++++++++++++++++ .../sql/optimizer/affinity/PartitionGroupNode.java | 13 +- .../sql/optimizer/affinity/PartitionNode.java | 4 +- .../sql/optimizer/affinity/PartitionNoneNode.java | 2 +- .../optimizer/affinity/PartitionParameterNode.java | 36 +- ...onResolver.java => PartitionParameterType.java} | 45 +- .../sql/optimizer/affinity/PartitionResolver.java | 1 + .../optimizer/affinity/PartitionSingleNode.java | 14 +- .../processors/query/h2/IgniteH2Indexing.java | 6 +- .../query/h2/affinity/PartitionExtractor.java | 69 ++- .../h2/twostep/SqlDataTypeConversionTest.java | 372 +++++++++++++++ .../IgniteBinaryCacheQueryTestSuite.java | 5 +- 16 files changed, 1079 insertions(+), 66 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionAllNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionAllNode.java index c625089..cb9500d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionAllNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionAllNode.java @@ -36,7 +36,7 @@ public class PartitionAllNode implements PartitionNode { } /** {@inheritDoc} */ - @Override public Collection<Integer> apply(Object... args) { + @Override public Collection<Integer> apply(PartitionClientContext cliCtx, Object... args) { return null; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionClientContext.java similarity index 66% copy from modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java copy to modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionClientContext.java index e606200..1d7594f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionClientContext.java @@ -14,23 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.ignite.internal.sql.optimizer.affinity; -import org.apache.ignite.IgniteCheckedException; +import org.jetbrains.annotations.Nullable; /** - * Partition resolver interface. Takes argument, data type and cache name, returns partition. + * Client context. Passed to partition resolver on thin clients. */ -public interface PartitionResolver { +public class PartitionClientContext { /** * Resolve partition. * * @param arg Argument. - * @param dataType Data type. + * @param typ Type. * @param cacheName Cache name. - * @return Partition. - * @throws IgniteCheckedException If failed. + * @return Partition or {@code null} if cannot be resolved. */ - int partition(Object arg, int dataType, String cacheName) throws IgniteCheckedException; + @Nullable public Integer partition(Object arg, @Nullable PartitionParameterType typ, String cacheName) { + PartitionDataTypeUtils.convert(arg, typ); + + // TODO: IGNITE-10308: Implement partition resolution logic. + return null; + } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionCompositeNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionCompositeNode.java index d724cf8..1235e6a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionCompositeNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionCompositeNode.java @@ -56,14 +56,21 @@ public class PartitionCompositeNode implements PartitionNode { } /** {@inheritDoc} */ - @Override public Collection<Integer> apply(Object... args) throws IgniteCheckedException { - Collection<Integer> leftParts = left.apply(args); - Collection<Integer> rightParts = right.apply(args); + @Override public Collection<Integer> apply(PartitionClientContext cliCtx, Object... args) + throws IgniteCheckedException { + Collection<Integer> leftParts = left.apply(cliCtx, args); + Collection<Integer> rightParts = right.apply(cliCtx, args); + + // Failed to resolve partitions on both sides, return. + if (leftParts == null && rightParts == null) + return null; if (op == PartitionCompositeNodeOperator.AND) { - // () and (...) -> () - if (leftParts == null || rightParts == null) - return null; + // (ALL) and (...) -> (...) + if (leftParts == null) + return rightParts; + else if (rightParts == null) + return leftParts; // (A, B) and (B, C) -> (B) leftParts = new HashSet<>(leftParts); @@ -73,11 +80,9 @@ public class PartitionCompositeNode implements PartitionNode { else { assert op == PartitionCompositeNodeOperator.OR; - // () or (...) -> (...) - if (leftParts == null) - return rightParts; - else if (rightParts == null) - return leftParts; + // (ALL) or (...) -> (ALL) + if (leftParts == null || rightParts == null) + return null; // (A, B) or (B, C) -> (A, B, C) leftParts = new HashSet<>(leftParts); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionConstantNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionConstantNode.java index da9dca2..24037bd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionConstantNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionConstantNode.java @@ -39,7 +39,7 @@ public class PartitionConstantNode extends PartitionSingleNode { } /** {@inheritDoc} */ - @Override public int applySingle(Object... args) { + @Override public Integer applySingle(PartitionClientContext cliCtx, Object... args) { return part; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionDataTypeUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionDataTypeUtils.java new file mode 100644 index 0000000..cbb9810 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionDataTypeUtils.java @@ -0,0 +1,528 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.sql.optimizer.affinity; + +import java.math.BigDecimal; +import java.util.UUID; +import org.jetbrains.annotations.NotNull; + +/** + * Utility methods for partition extractor. + */ +public class PartitionDataTypeUtils { + /** Decimal representation of maximum long value. */ + private static final BigDecimal MAX_LONG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE); + + /** Decimal representation of minimum long value. */ + private static final BigDecimal MIN_LONG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE); + + /** Decimal representation of maximum int value. */ + private static final BigDecimal MAX_INTEGER_DECIMAL = new BigDecimal(Integer.MAX_VALUE); + + /** Decimal representation of minimum int value. */ + private static final BigDecimal MIN_INTEGER_DECIMAL = new BigDecimal(Integer.MIN_VALUE); + + /** Decimal representation of maximum short value. */ + private static final BigDecimal MAX_SHORT_DECIMAL = new BigDecimal(Short.MAX_VALUE); + + /** Decimal representation of minimum short value. */ + private static final BigDecimal MIN_SHORT_DECIMAL = new BigDecimal(Short.MIN_VALUE); + + /** Decimal representation of maximum byte value. */ + private static final BigDecimal MAX_BYTE_DECIMAL = new BigDecimal(Byte.MAX_VALUE); + + /** Decimal representation of minimum byte value. */ + private static final BigDecimal MIN_BYTE_DECIMAL = new BigDecimal(Byte.MIN_VALUE); + + /** Convertation failure marker. */ + public static final Object CONVERTATION_FAILURE = new Object(); + + /** + * Convert argument to the given type. + * + * @param arg Argument. + * @param targetType Type. + * @return Converted argument or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + public static Object convert(Object arg, PartitionParameterType targetType) { + assert targetType != null; + + if (arg == null) + return null; + + PartitionParameterType argType = typeFromClass(arg.getClass()); + + if (argType == null) + return CONVERTATION_FAILURE; + + if (argType == targetType) + return arg; + + try { + switch (targetType) { + case BOOLEAN: + return getBoolean(arg, argType); + + case BYTE: + return getIntegerNumeric( + arg, + argType, + Byte.MIN_VALUE, + Byte.MAX_VALUE, + MIN_BYTE_DECIMAL, + MAX_BYTE_DECIMAL, + PartitionParameterType.BYTE + ); + + case SHORT: + return getIntegerNumeric( + arg, + argType, + Short.MIN_VALUE, + Short.MAX_VALUE, + MIN_SHORT_DECIMAL, + MAX_SHORT_DECIMAL, + PartitionParameterType.SHORT + ); + + case INT: + return getIntegerNumeric( + arg, + argType, + Integer.MIN_VALUE, + Integer.MAX_VALUE, + MIN_INTEGER_DECIMAL, + MAX_INTEGER_DECIMAL, + PartitionParameterType.INT + ); + + case LONG: + return getIntegerNumeric( + arg, + argType, + Long.MIN_VALUE, + Long.MAX_VALUE, MIN_LONG_DECIMAL, + MAX_LONG_DECIMAL, + PartitionParameterType.LONG + ); + + case FLOAT: + return getFloat(arg, argType); + + case DOUBLE: + return getDouble(arg, argType); + + case DECIMAL: + return getDecimal(arg, argType); + + case STRING: + return getString(arg, argType); + + case UUID: + return getUUID(arg, argType); + + default: + return CONVERTATION_FAILURE; + } + } + catch (NumberFormatException e) { + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to <code>UUID</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @NotNull private static Object getUUID(Object arg, PartitionParameterType argType) { + switch (argType) { + case STRING: + return stringToUUID((String)arg); + + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to <code>String</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + private static Object getString(Object arg, PartitionParameterType argType) { + switch (argType) { + case BOOLEAN: + return (Boolean)arg ? "TRUE" : "FALSE"; + + case BYTE: + case SHORT: + case INT: + case LONG: + case DOUBLE: + case FLOAT: + return String.valueOf(arg); + + case DECIMAL: { + // We had to use such kind of convertation instead of common arg.toString() in order to match + // H2 convertation results. In case of using arg.toString() we will have inconsistant convertation + // results for values similar to BigDecimal.valueOf(12334535345456700.12345634534534578901). + // Main difference between toPlainString() and toString() is that toPlainString() + // returns a string representation of a {@code BigDecimal} without an exponent field. + String p = ((BigDecimal)arg).toPlainString(); + + return p.length() < 40 ? p : arg.toString(); + } + + case UUID: + return arg.toString(); + + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to <code>Float</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @NotNull private static Object getFloat(Object arg, PartitionParameterType argType) { + switch (argType) { + case BOOLEAN: + return arg.equals(Boolean.TRUE) ? (float)1 : (float)0; + + case BYTE: + case SHORT: + case INT: + case LONG: + case DECIMAL: + case DOUBLE: + return ((Number)arg).floatValue(); + + case STRING: + return Float.parseFloat(((String)arg).trim()); + + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to <code>Double</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @NotNull private static Object getDouble(Object arg, PartitionParameterType argType) { + switch (argType) { + case BOOLEAN: + return arg.equals(Boolean.TRUE) ? (double)1 : (double)0; + + case BYTE: + case SHORT: + case INT: + case LONG: + case DECIMAL: + case FLOAT: + return ((Number)arg).doubleValue(); + + case STRING: + return Double.parseDouble(((String)arg).trim()); + + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to <code>Decimal</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @NotNull private static Object getDecimal(Object arg, PartitionParameterType argType) { + switch (argType) { + case BOOLEAN: + return arg.equals(Boolean.TRUE) ? BigDecimal.ONE : BigDecimal.ZERO; + + case BYTE: + case SHORT: + case INT: + case LONG: + return BigDecimal.valueOf(((Number)arg).longValue()); + + case DOUBLE: { + double d = (double)arg; + + if (Double.isInfinite(d) || Double.isNaN(d)) + return CONVERTATION_FAILURE; + + return BigDecimal.valueOf(d); + } + case FLOAT: { + float f = (float)arg; + + if (Float.isInfinite(f) || Float.isNaN(f)) + return CONVERTATION_FAILURE; + + return new BigDecimal(Float.toString(f)); + } + + case STRING: + return new BigDecimal(((String)arg).trim()); + + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Convert argument to integer numeric: byte, short, int, long. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @SuppressWarnings("EnumSwitchStatementWhichMissesCases") + @NotNull private static Object getIntegerNumeric( + Object arg, + PartitionParameterType argType, + long minVal, + long maxVal, + BigDecimal minValDecimal, + BigDecimal maxValDecimal, + PartitionParameterType targetType + ) { + assert targetType == PartitionParameterType.BYTE || targetType == PartitionParameterType.SHORT || + targetType == PartitionParameterType.INT || targetType == PartitionParameterType.LONG; + + long res; + + switch (argType) { + case BOOLEAN: + res = arg.equals(Boolean.TRUE) ? 1L : 0L; + + break; + + case BYTE: + case SHORT: + case INT: + case LONG: { + res = ((Number)arg).longValue(); + + if (res > maxVal || res < minVal) + return CONVERTATION_FAILURE; + + break; + } + case DECIMAL: { + BigDecimal d = (BigDecimal)arg; + + if (d.compareTo(maxValDecimal) > 0 || d.compareTo(minValDecimal) < 0) + return CONVERTATION_FAILURE; + else + res = d.setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); + + break; + } + + case DOUBLE: { + Double d = (Double)arg; + + if (d > maxVal || d < minVal) + return CONVERTATION_FAILURE; + else + res = Math.round(d); + + break; + } + + case FLOAT: { + Float f = (Float)arg; + + if (f > maxVal || f < minVal) + return CONVERTATION_FAILURE; + else + res = Math.round(f); + + break; + } + + case STRING: { + res = Long.parseLong(((String)arg).trim()); + + if (res > maxVal || res < minVal) + return CONVERTATION_FAILURE; + + break; + } + + default: + return CONVERTATION_FAILURE; + } + + switch (targetType) { + case BYTE: + return (byte)res; + case SHORT: + return (short)res; + case INT: + return (int)res; + case LONG: + return res; + } + + return CONVERTATION_FAILURE; + } + + /** + * Convert argument to <code>Boolean</code>. + * + * @param arg Argument to convert. + * @param argType Argument type. + * @return Converted value or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + @NotNull private static Object getBoolean(Object arg, PartitionParameterType argType) { + switch (argType) { + case BYTE: + return (Byte)arg != 0; + + case SHORT: + return (Short)arg != 0; + + case INT: + return (Integer)arg != 0; + + case LONG: + return (Long)arg != 0; + + case DECIMAL: + return !arg.equals(BigDecimal.ZERO); + + case DOUBLE: + return Math.signum((Double)arg) != 0; + + case FLOAT: + return Math.signum((Float)arg) != 0; + + case STRING: { + String sVal = (String)arg; + + if ("true".equalsIgnoreCase(sVal) || + "t".equalsIgnoreCase(sVal) || + "yes".equalsIgnoreCase(sVal) || + "y".equalsIgnoreCase(sVal) || + "1".equals(sVal)) + return Boolean.TRUE; + else if ("false".equalsIgnoreCase(sVal) || + "f".equalsIgnoreCase(sVal) || + "no".equalsIgnoreCase(sVal) || + "n".equalsIgnoreCase(sVal) || + "0".equals(sVal)) + return Boolean.FALSE; + } + default: + return CONVERTATION_FAILURE; + } + } + + /** + * Get the <code>PartitionParameterType</code> type for the given Java class. + * + * @param c The Java class. + * @return The <code>PartitionParameterType</code> type. + */ + private static PartitionParameterType typeFromClass(Class<?> c) { + assert c != null; + + if (String.class == c) + return PartitionParameterType.STRING; + else if (Integer.class == c) + return PartitionParameterType.INT; + else if (Long.class == c) + return PartitionParameterType.LONG; + else if (Boolean.class == c) + return PartitionParameterType.BOOLEAN; + else if (Double.class == c) + return PartitionParameterType.DOUBLE; + else if (Byte.class == c) + return PartitionParameterType.BYTE; + else if (Short.class == c) + return PartitionParameterType.SHORT; + else if (Float.class == c) + return PartitionParameterType.FLOAT; + else if (UUID.class == c) + return PartitionParameterType.UUID; + else if (BigDecimal.class == c) + return PartitionParameterType.DECIMAL; + else + return null; + } + + /** + * Utility method that helps to convert String to UUID. Given method is more fault tolerant than more common + * <code>UUID.fromString()</code>. For example it supports String represendation of UUID-without-hyphens + * conversion, that is not supported by mentioned above <code>UUID.fromString()</code>. + * + * @param s String to + * @return UUID or <code>CONVERTATION_FAILURE</code> if convertation failed. + */ + public static Object stringToUUID(String s) { + long low = 0, high = 0; + for (int i = 0, j = 0, len = s.length(); i < len; i++) { + char c = s.charAt(i); + + if (c >= '0' && c <= '9') + low = (low << 4) | (c - '0'); + else if (c >= 'a' && c <= 'f') + low = (low << 4) | (c - 'a' + 0xa); + else if (c == '-') + continue; + else if (c >= 'A' && c <= 'F') + low = (low << 4) | (c - 'A' + 0xa); + else if (c <= ' ') + continue; + else + return CONVERTATION_FAILURE; + + if (j++ == 15) { + high = low; + + low = 0; + } + } + + return new UUID(high, low); + } + + /** + * Private constructor. + */ + private PartitionDataTypeUtils() { + // No-op. + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionGroupNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionGroupNode.java index b7106a7..8a3c5d1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionGroupNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionGroupNode.java @@ -46,12 +46,19 @@ public class PartitionGroupNode implements PartitionNode { } /** {@inheritDoc} */ - @Override public Collection<Integer> apply(Object... args) throws IgniteCheckedException { + @Override public Collection<Integer> apply(PartitionClientContext ctx, Object... args) + throws IgniteCheckedException { // Deduplicate same partitions which may appear during resolution. HashSet<Integer> res = new HashSet<>(siblings.size()); - for (PartitionSingleNode sibling : siblings) - res.add(sibling.applySingle(args)); + for (PartitionSingleNode sibling : siblings) { + Integer part = sibling.applySingle(ctx, args); + + if (part == null) + return null; + + res.add(part); + } return res; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNode.java index c8c354c..57936b1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNode.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.sql.optimizer.affinity; import org.apache.ignite.IgniteCheckedException; +import org.jetbrains.annotations.Nullable; import java.util.Collection; @@ -28,11 +29,12 @@ public interface PartitionNode { /** * Get partitions. * + * @param cliCtx Thin client context (optional). * @param args Query arguments. * @return Partitions. * @throws IgniteCheckedException If failed. */ - Collection<Integer> apply(Object... args) throws IgniteCheckedException; + Collection<Integer> apply(@Nullable PartitionClientContext cliCtx, Object... args) throws IgniteCheckedException; /** * @return Join group for the given node. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNoneNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNoneNode.java index f136fc9..361b3e0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNoneNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionNoneNode.java @@ -37,7 +37,7 @@ public class PartitionNoneNode implements PartitionNode { } /** {@inheritDoc} */ - @Override public Collection<Integer> apply(Object... args) { + @Override public Collection<Integer> apply(PartitionClientContext cliCtx, Object... args) { return Collections.emptySet(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterNode.java index 6219c30..c909a23 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterNode.java @@ -32,8 +32,11 @@ public class PartitionParameterNode extends PartitionSingleNode { /** Index. */ private final int idx; - /** Data type. */ - private final int dataType; + /** Parameter data type. */ + private final int type; + + /** Mapped parameter type. */ + private final PartitionParameterType mappedType; /** * Constructor. @@ -41,26 +44,37 @@ public class PartitionParameterNode extends PartitionSingleNode { * @param tbl Table descriptor. * @param partResolver Partition resolver. * @param idx Parameter index. - * @param dataType Parameter data type. + * @param type Parameter data type. + * @param mappedType Mapped parameter type to be used by thin clients. */ - public PartitionParameterNode(PartitionTable tbl, PartitionResolver partResolver, int idx, int dataType) { + public PartitionParameterNode(PartitionTable tbl, PartitionResolver partResolver, int idx, int type, + PartitionParameterType mappedType) { super(tbl); this.partResolver = partResolver; this.idx = idx; - this.dataType = dataType; + this.type = type; + this.mappedType = mappedType; } /** {@inheritDoc} */ - @Override public int applySingle(Object... args) throws IgniteCheckedException { + @Override public Integer applySingle(PartitionClientContext cliCtx, Object... args) throws IgniteCheckedException { assert args != null; assert idx < args.length; - return partResolver.partition( - args[idx], - dataType, - tbl.cacheName() - ); + Object arg = args[idx]; + + if (cliCtx != null) + return cliCtx.partition(arg, mappedType, tbl.cacheName()); + else { + assert partResolver != null; + + return partResolver.partition( + arg, + type, + tbl.cacheName() + ); + } } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterType.java similarity index 63% copy from modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java copy to modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterType.java index e606200..ff0943b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionParameterType.java @@ -17,20 +17,37 @@ package org.apache.ignite.internal.sql.optimizer.affinity; -import org.apache.ignite.IgniteCheckedException; - /** - * Partition resolver interface. Takes argument, data type and cache name, returns partition. + * Partition argument type. */ -public interface PartitionResolver { - /** - * Resolve partition. - * - * @param arg Argument. - * @param dataType Data type. - * @param cacheName Cache name. - * @return Partition. - * @throws IgniteCheckedException If failed. - */ - int partition(Object arg, int dataType, String cacheName) throws IgniteCheckedException; +public enum PartitionParameterType { + /** Boolean. */ + BOOLEAN, + + /** Byte. */ + BYTE, + + /** Short. */ + SHORT, + + /** Int. */ + INT, + + /** Long. */ + LONG, + + /** Float. */ + FLOAT, + + /** Double. */ + DOUBLE, + + /** String. */ + STRING, + + /** Decimal. */ + DECIMAL, + + /** UUID. */ + UUID } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java index e606200..4d25bef 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionResolver.java @@ -21,6 +21,7 @@ import org.apache.ignite.IgniteCheckedException; /** * Partition resolver interface. Takes argument, data type and cache name, returns partition. + * The only purpose of this methods is to allow partition pruning classes to be located in core module. */ public interface PartitionResolver { /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionSingleNode.java b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionSingleNode.java index 466e1c6..33f61d0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionSingleNode.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/sql/optimizer/affinity/PartitionSingleNode.java @@ -20,6 +20,7 @@ package org.apache.ignite.internal.sql.optimizer.affinity; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.F; +import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; @@ -42,17 +43,22 @@ public abstract class PartitionSingleNode implements PartitionNode { } /** {@inheritDoc} */ - @Override public Collection<Integer> apply(Object... args) throws IgniteCheckedException { - return Collections.singletonList(applySingle(args)); + @Override public Collection<Integer> apply(PartitionClientContext cliCtx, Object... args) + throws IgniteCheckedException { + Integer part = applySingle(cliCtx, args); + + return part != null ? Collections.singletonList(part) : null; } /** * Apply arguments and get single partition. * + * @param cliCtx Client context. * @param args Arguments. - * @return Partition. + * @return Partition or {@code null} if failed. */ - public abstract int applySingle(Object... args) throws IgniteCheckedException; + public abstract Integer applySingle(@Nullable PartitionClientContext cliCtx, Object... args) + throws IgniteCheckedException; /** * @return {@code True} if constant, {@code false} if argument. 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 9ee056f..f5b013f 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 @@ -2103,9 +2103,11 @@ public class IgniteH2Indexing implements GridQueryIndexing { return explicitParts; else if (derivedParts != null) { try { - Collection<Integer> realParts = derivedParts.tree().apply(args); + Collection<Integer> realParts = derivedParts.tree().apply(null, args); - if (F.isEmpty(realParts)) + if (realParts == null) + return null; + else if (realParts.isEmpty()) return IgniteUtils.EMPTY_INTS; else { int[] realParts0 = new int[realParts.size()]; diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java index f82be53..996a3b2 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/affinity/PartitionExtractor.java @@ -17,6 +17,11 @@ package org.apache.ignite.internal.processors.query.h2.affinity; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteSystemProperties; import org.apache.ignite.cache.CacheMode; @@ -46,6 +51,7 @@ import org.apache.ignite.internal.sql.optimizer.affinity.PartitionJoinCondition; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionNode; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionNoneNode; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionParameterNode; +import org.apache.ignite.internal.sql.optimizer.affinity.PartitionParameterType; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionResult; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionSingleNode; import org.apache.ignite.internal.sql.optimizer.affinity.PartitionTable; @@ -56,12 +62,6 @@ import org.h2.table.Column; import org.h2.value.Value; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - /** * Partition tree extractor. */ @@ -623,13 +623,66 @@ public class PartitionExtractor { return new PartitionConstantNode(tbl0, part); } - else if (rightParam != null) - return new PartitionParameterNode(tbl0, partResolver, rightParam.index(), leftCol0.getType()); + else if (rightParam != null) { + int colType = leftCol0.getType(); + + return new PartitionParameterNode( + tbl0, + partResolver, + rightParam.index(), + leftCol0.getType(), + mappedType(colType) + ); + } else return null; } /** + * Mapped Ignite type for H2 type. + * + * @param type H2 type. + * @return ignite type. + */ + @Nullable private static PartitionParameterType mappedType(int type) { + // Try map if possible. + switch (type) { + case Value.BOOLEAN: + return PartitionParameterType.BOOLEAN; + + case Value.BYTE: + return PartitionParameterType.BYTE; + + case Value.SHORT: + return PartitionParameterType.SHORT; + + case Value.INT: + return PartitionParameterType.INT; + + case Value.LONG: + return PartitionParameterType.LONG; + + case Value.FLOAT: + return PartitionParameterType.FLOAT; + + case Value.DOUBLE: + return PartitionParameterType.DOUBLE; + + case Value.STRING: + return PartitionParameterType.STRING; + + case Value.DECIMAL: + return PartitionParameterType.DECIMAL; + + case Value.UUID: + return PartitionParameterType.UUID; + } + + // Otherwise we do not support it. + return null; + } + + /** * Unwrap constant if possible. * * @param ast AST. diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/SqlDataTypeConversionTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/SqlDataTypeConversionTest.java new file mode 100644 index 0000000..3e62d5d --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/query/h2/twostep/SqlDataTypeConversionTest.java @@ -0,0 +1,372 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.query.h2.twostep; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Map; +import java.util.UUID; +import org.apache.ignite.internal.processors.query.h2.H2Utils; +import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; +import org.apache.ignite.internal.sql.optimizer.affinity.PartitionDataTypeUtils; +import org.apache.ignite.internal.sql.optimizer.affinity.PartitionParameterType; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.h2.value.Value; +import org.junit.Test; + +/** + * Data conversion tests. + */ +public class SqlDataTypeConversionTest extends GridCommonAbstractTest { + /** Map to convert <code>PartitionParameterType</code> instances to correspondig java classes. */ + private static final Map<PartitionParameterType, Class<?>> PARAMETER_TYPE_TO_JAVA_CLASS; + + /** Map to convert <code>PartitionParameterType</code> instances to correspondig H2 data types. */ + private static final Map<PartitionParameterType, Integer> IGNITE_PARAMETER_TYPE_TO_H2_PARAMETER_TYPE; + + /** Ignite H2 Indexing. */ + private static IgniteH2Indexing idx; + + static { + Map<PartitionParameterType, Class<?>> paramTypeToJavaCls = new EnumMap<>(PartitionParameterType.class); + + paramTypeToJavaCls.put(PartitionParameterType.BOOLEAN, Boolean.class); + paramTypeToJavaCls.put(PartitionParameterType.BYTE, Byte.class); + paramTypeToJavaCls.put(PartitionParameterType.SHORT, Short.class); + paramTypeToJavaCls.put(PartitionParameterType.INT, Integer.class); + paramTypeToJavaCls.put(PartitionParameterType.LONG, Long.class); + paramTypeToJavaCls.put(PartitionParameterType.FLOAT, Float.class); + paramTypeToJavaCls.put(PartitionParameterType.DOUBLE, Double.class); + paramTypeToJavaCls.put(PartitionParameterType.STRING, String.class); + paramTypeToJavaCls.put(PartitionParameterType.DECIMAL, BigDecimal.class); + paramTypeToJavaCls.put(PartitionParameterType.UUID, UUID.class); + + PARAMETER_TYPE_TO_JAVA_CLASS = Collections.unmodifiableMap(paramTypeToJavaCls); + + Map<PartitionParameterType, Integer> igniteParamTypeToH2ParamType = new EnumMap<>(PartitionParameterType.class); + + igniteParamTypeToH2ParamType.put(PartitionParameterType.BOOLEAN, Value.BOOLEAN); + igniteParamTypeToH2ParamType.put(PartitionParameterType.BYTE, Value.BYTE); + igniteParamTypeToH2ParamType.put(PartitionParameterType.SHORT, Value.SHORT); + igniteParamTypeToH2ParamType.put(PartitionParameterType.INT, Value.INT); + igniteParamTypeToH2ParamType.put(PartitionParameterType.LONG, Value.LONG); + igniteParamTypeToH2ParamType.put(PartitionParameterType.FLOAT, Value.FLOAT); + igniteParamTypeToH2ParamType.put(PartitionParameterType.DOUBLE, Value.DOUBLE); + igniteParamTypeToH2ParamType.put(PartitionParameterType.STRING, Value.STRING); + igniteParamTypeToH2ParamType.put(PartitionParameterType.DECIMAL, Value.DECIMAL); + igniteParamTypeToH2ParamType.put(PartitionParameterType.UUID, Value.UUID); + + IGNITE_PARAMETER_TYPE_TO_H2_PARAMETER_TYPE = Collections.unmodifiableMap(igniteParamTypeToH2ParamType); + } + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + idx = (IgniteH2Indexing)startGrid(0).context().query().getIndexing(); + } + + /** + * Test null value conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertNull() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(null); + } + + /** + * Test boolean conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertBoolean() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(Boolean.TRUE); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Boolean.FALSE); + } + + /** + * Test byte conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertByte() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2((byte)42, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2((byte)0, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Byte.MIN_VALUE, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Byte.MAX_VALUE, PartitionParameterType.UUID); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert((byte)42, PartitionParameterType.UUID)); + } + + /** + * Test short conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertShort() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2((short)42, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2((short)0, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Short.MIN_VALUE, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Short.MAX_VALUE, PartitionParameterType.UUID); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert((short)42, PartitionParameterType.UUID)); + } + + /** + * Test int conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertInteger() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(42, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Integer.MIN_VALUE, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Integer.MAX_VALUE, PartitionParameterType.UUID); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert(42, PartitionParameterType.UUID)); + } + + /** + * Test long conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertLong() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(42L, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0L, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Long.MIN_VALUE, PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Long.MAX_VALUE, PartitionParameterType.UUID); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert(42L, PartitionParameterType.UUID)); + } + + /** + * Test float conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertFloat() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(42.1f); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0.1f); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0f); + makeSureThatConvertationResultsExactTheSameAsWithinH2(1.2345678E7f); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(Float.POSITIVE_INFINITY); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Float.NEGATIVE_INFINITY); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Float.NaN); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(Float.MIN_VALUE); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Float.MAX_VALUE); + } + + /** + * Test double conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertDouble() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(42.2d); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0.2d); + makeSureThatConvertationResultsExactTheSameAsWithinH2(0d); + makeSureThatConvertationResultsExactTheSameAsWithinH2(1.2345678E7d); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(Double.POSITIVE_INFINITY); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Double.NEGATIVE_INFINITY); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Double.NaN); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(Double.MIN_VALUE); + makeSureThatConvertationResultsExactTheSameAsWithinH2(Double.MAX_VALUE); + } + + /** + * Test string conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertString() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2("42", PartitionParameterType.BOOLEAN); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert("42", PartitionParameterType.BOOLEAN)); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("0"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("1"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("42.3", PartitionParameterType.BOOLEAN); + makeSureThatConvertationResultsExactTheSameAsWithinH2("0.3", PartitionParameterType.BOOLEAN); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert("0.3", PartitionParameterType.BOOLEAN)); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("42.4f"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("0.4d"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("12345678901234567890.123456789012345678901d"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("04d17cf3-bc20-4e3d-9ff7-72437cdae227"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("04d17cf3bc204e3d9ff772437cdae227"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("a"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("aaa", + PartitionParameterType.BOOLEAN); + makeSureThatConvertationResultsExactTheSameAsWithinH2(" aaa "); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("true"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("t"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("yes"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("y"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("false"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("f"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("no"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("n"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(" true "); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("null"); + makeSureThatConvertationResultsExactTheSameAsWithinH2("NULL"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("2000-01-02"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("10:00:00"); + + makeSureThatConvertationResultsExactTheSameAsWithinH2("2001-01-01 23:59:59.123456"); + } + + /** + * Test decimal conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertDecimal() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(42.5d), PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(0.5d), PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(0), PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(1.2345678E7), + PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(BigDecimal.valueOf(12334535345456700.12345634534534578901), + PartitionParameterType.UUID); + + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(Double.MIN_VALUE), + PartitionParameterType.UUID); + makeSureThatConvertationResultsExactTheSameAsWithinH2(new BigDecimal(Double.MAX_VALUE), + PartitionParameterType.UUID); + + assertEquals(PartitionDataTypeUtils.CONVERTATION_FAILURE, + PartitionDataTypeUtils.convert(new BigDecimal(42.5d), PartitionParameterType.UUID)); + } + + /** + * Test uuid conversion. + * + * @throws Exception If failed. + */ + @Test + public void convertUUID() throws Exception { + makeSureThatConvertationResultsExactTheSameAsWithinH2(UUID.randomUUID()); + makeSureThatConvertationResultsExactTheSameAsWithinH2(UUID.fromString("00000000-0000-0000-0000-00000000000a")); + makeSureThatConvertationResultsExactTheSameAsWithinH2(new UUID(0L, 1L)); + } + + /** + * Test string to uuid conversion with different combinations of upper and lower case letters and with/without + * hyphens. + */ + @Test + public void stringToUUIDConvertation() { + UUID expUuid = UUID.fromString("273ded0d-86de-432e-b252-54c06ec22927"); + + // Lower case and hyphens. + assertEquals(expUuid, PartitionDataTypeUtils.stringToUUID("273ded0d-86de-432e-b252-54c06ec22927")); + + // Lower case without hyphens. + assertEquals(expUuid, PartitionDataTypeUtils.stringToUUID("273ded0d86de432eb25254c06ec22927")); + + // Lower case without few hyphens. + assertEquals(expUuid, PartitionDataTypeUtils.stringToUUID("273ded0d86de432e-b25254c06ec22927")); + + // Upper case and hyphens. + assertEquals(expUuid, PartitionDataTypeUtils.stringToUUID("273dED0D-86DE-432E-B25254C06EC22927")); + + // Upper case without few hyphens. + assertEquals(expUuid, PartitionDataTypeUtils.stringToUUID("273dED0D86DE432Eb252-54c06ec22927")); + } + + /** + * Actual conversial check logic. + * + * @param arg Argument to convert. + * @param excludedTargetTypesFromCheck <@code>PartitionParameterType</code> instances to exclude from check. + * @throws Exception If failed. + */ + private void makeSureThatConvertationResultsExactTheSameAsWithinH2(Object arg, + PartitionParameterType ... excludedTargetTypesFromCheck) throws Exception { + + Iterable<PartitionParameterType> targetTypes = excludedTargetTypesFromCheck.length > 0 ? + EnumSet.complementOf(EnumSet.of(excludedTargetTypesFromCheck[0], excludedTargetTypesFromCheck)): + EnumSet.allOf(PartitionParameterType.class); + + for (PartitionParameterType targetType : targetTypes) { + Object convertationRes = PartitionDataTypeUtils.convert(arg, targetType); + + if (PartitionDataTypeUtils.CONVERTATION_FAILURE == convertationRes) { + try { + H2Utils.convert(arg, idx, IGNITE_PARAMETER_TYPE_TO_H2_PARAMETER_TYPE.get(targetType)); + + fail("Data conversion failed in Ignite but not in H2."); + } + catch (org.h2.message.DbException h2Exception) { + assertTrue(h2Exception.getMessage().contains("Numeric value out of range") || + h2Exception.getMessage().contains("Data conversion error")); + } + } + else { + Object convertationH2Res = H2Utils.convert(arg, idx, + IGNITE_PARAMETER_TYPE_TO_H2_PARAMETER_TYPE.get(targetType)); + + if (convertationRes == null) + assertNull(convertationH2Res); + else { + assertEquals(PARAMETER_TYPE_TO_JAVA_CLASS.get(targetType), convertationRes.getClass()); + assertEquals(convertationH2Res.getClass(), convertationRes.getClass()); + assertEquals(convertationH2Res, convertationRes); + } + } + } + } +} diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java index 4482b612c..a275ac6 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteBinaryCacheQueryTestSuite.java @@ -82,8 +82,8 @@ import org.apache.ignite.internal.processors.cache.IgniteCachePrimitiveFieldsQue import org.apache.ignite.internal.processors.cache.IgniteCacheQueryH2IndexingLeakTest; import org.apache.ignite.internal.processors.cache.IgniteCacheQueryIndexSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheQueryLoadSelfTest; -import org.apache.ignite.internal.processors.cache.IgniteCacheSqlInsertValidationSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheSqlDmlErrorSelfTest; +import org.apache.ignite.internal.processors.cache.IgniteCacheSqlInsertValidationSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheSqlQueryErrorSelfTest; import org.apache.ignite.internal.processors.cache.IgniteCacheUnionDuplicatesTest; import org.apache.ignite.internal.processors.cache.IgniteCacheUpdateSqlQuerySelfTest; @@ -223,6 +223,7 @@ import org.apache.ignite.internal.processors.query.h2.twostep.BetweenOperationEx import org.apache.ignite.internal.processors.query.h2.twostep.DmlSelectPartitionPruningSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.InOperationExtractPartitionSelfTest; import org.apache.ignite.internal.processors.query.h2.twostep.JoinPartitionPruningSelfTest; +import org.apache.ignite.internal.processors.query.h2.twostep.SqlDataTypeConversionTest; import org.apache.ignite.internal.processors.sql.IgniteCachePartitionedAtomicColumnConstraintsTest; import org.apache.ignite.internal.processors.sql.IgniteCachePartitionedTransactionalColumnConstraintsTest; import org.apache.ignite.internal.processors.sql.IgniteCachePartitionedTransactionalSnapshotColumnConstraintTest; @@ -545,6 +546,8 @@ import org.junit.runners.Suite; GridCacheDynamicLoadOnClientTest.class, GridCacheDynamicLoadOnClientPersistentTest.class, + SqlDataTypeConversionTest.class, + //Query history. SqlQueryHistorySelfTest.class, SqlQueryHistoryFromClientSelfTest.class,