http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java index d73e89d..79780f7 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java @@ -76,13 +76,11 @@ import org.apache.ignite.failure.StopNodeFailureHandler; import org.apache.ignite.failure.StopNodeOrHaltFailureHandler; import org.apache.ignite.internal.binary.BinaryRawReaderEx; import org.apache.ignite.internal.binary.BinaryRawWriterEx; +import org.apache.ignite.internal.processors.odbc.ClientListenerProtocolVersion; import org.apache.ignite.internal.processors.platform.cache.affinity.PlatformAffinityFunction; import org.apache.ignite.internal.processors.platform.cache.expiry.PlatformExpiryPolicyFactory; import org.apache.ignite.internal.processors.platform.events.PlatformLocalEventListener; import org.apache.ignite.internal.processors.platform.plugin.cache.PlatformCachePluginConfiguration; -import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.lang.IgniteBiTuple; import org.apache.ignite.lang.IgnitePredicate; import org.apache.ignite.platform.dotnet.PlatformDotNetAffinityFunction; import org.apache.ignite.platform.dotnet.PlatformDotNetBinaryConfiguration; @@ -108,6 +106,8 @@ import org.apache.ignite.ssl.SslContextFactory; import org.apache.ignite.transactions.TransactionConcurrency; import org.apache.ignite.transactions.TransactionIsolation; +import static org.apache.ignite.internal.processors.platform.client.ClientConnectionContext.VER_1_2_0; + /** * Configuration utils. * @@ -158,9 +158,10 @@ public class PlatformConfigurationUtils { * Reads cache configuration from a stream. * * @param in Stream. + * @param ver Client version. * @return Cache configuration. */ - public static CacheConfiguration readCacheConfiguration(BinaryRawReaderEx in) { + public static CacheConfiguration readCacheConfiguration(BinaryRawReaderEx in, ClientListenerProtocolVersion ver) { assert in != null; CacheConfiguration ccfg = new CacheConfiguration(); @@ -226,7 +227,7 @@ public class PlatformConfigurationUtils { Collection<QueryEntity> entities = new ArrayList<>(qryEntCnt); for (int i = 0; i < qryEntCnt; i++) - entities.add(readQueryEntity(in)); + entities.add(readQueryEntity(in, ver)); ccfg.setQueryEntities(entities); } @@ -483,9 +484,10 @@ public class PlatformConfigurationUtils { * Reads the query entity. * * @param in Stream. + * @param ver Client version. * @return QueryEntity. */ - public static QueryEntity readQueryEntity(BinaryRawReader in) { + public static QueryEntity readQueryEntity(BinaryRawReader in, ClientListenerProtocolVersion ver) { QueryEntity res = new QueryEntity(); res.setKeyType(in.readString()); @@ -499,7 +501,8 @@ public class PlatformConfigurationUtils { Set<String> keyFields = new HashSet<>(cnt); Set<String> notNullFields = new HashSet<>(cnt); Map<String, Object> defVals = new HashMap<>(cnt); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = new HashMap<>(cnt); + Map<String, Integer> fieldsPrecision = new HashMap<>(cnt); + Map<String, Integer> fieldsScale = new HashMap<>(cnt); if (cnt > 0) { LinkedHashMap<String, String> fields = new LinkedHashMap<>(cnt); @@ -519,13 +522,18 @@ public class PlatformConfigurationUtils { Object defVal = in.readObject(); if (defVal != null) defVals.put(fieldName, defVal); + + if (ver.compareTo(VER_1_2_0) >= 0) { + int precision = in.readInt(); - int precision = in.readInt(); + if (precision != -1) + fieldsPrecision.put(fieldName, precision); - int scale = in.readInt(); + int scale = in.readInt(); - if (precision != -1 || scale != -1) - decimalInfo.put(fieldName, F.t(precision, scale)); + if (scale != -1) + fieldsScale.put(fieldName, scale); + } } res.setFields(fields); @@ -539,8 +547,11 @@ public class PlatformConfigurationUtils { if (!defVals.isEmpty()) res.setDefaultFieldValues(defVals); - if (!decimalInfo.isEmpty()) - res.setDecimalInfo(decimalInfo); + if (!fieldsPrecision.isEmpty()) + res.setFieldsPrecision(fieldsPrecision); + + if (!fieldsScale.isEmpty()) + res.setFieldsScale(fieldsScale); } // Aliases @@ -601,9 +612,11 @@ public class PlatformConfigurationUtils { * Reads Ignite configuration. * @param in Reader. * @param cfg Configuration. + * @param ver Client version. */ @SuppressWarnings("deprecation") - public static void readIgniteConfiguration(BinaryRawReaderEx in, IgniteConfiguration cfg) { + public static void readIgniteConfiguration(BinaryRawReaderEx in, IgniteConfiguration cfg, + ClientListenerProtocolVersion ver) { if (in.readBoolean()) cfg.setClientMode(in.readBoolean()); int[] evtTypes = in.readIntArray(); @@ -683,7 +696,7 @@ public class PlatformConfigurationUtils { if (in.readBoolean()) cfg.setQueryThreadPoolSize(in.readInt()); - readCacheConfigurations(in, cfg); + readCacheConfigurations(in, cfg, ver); readDiscoveryConfiguration(in, cfg); if (in.readBoolean()) { @@ -819,8 +832,10 @@ public class PlatformConfigurationUtils { * * @param cfg IgniteConfiguration to update. * @param in Reader. + * @param ver Client version. */ - private static void readCacheConfigurations(BinaryRawReaderEx in, IgniteConfiguration cfg) { + private static void readCacheConfigurations(BinaryRawReaderEx in, IgniteConfiguration cfg, + ClientListenerProtocolVersion ver) { int len = in.readInt(); if (len == 0) @@ -829,7 +844,7 @@ public class PlatformConfigurationUtils { List<CacheConfiguration> caches = new ArrayList<>(); for (int i = 0; i < len; i++) - caches.add(readCacheConfiguration(in)); + caches.add(readCacheConfiguration(in, ver)); CacheConfiguration[] oldCaches = cfg.getCacheConfiguration(); CacheConfiguration[] caches0 = caches.toArray(new CacheConfiguration[caches.size()]); @@ -928,8 +943,10 @@ public class PlatformConfigurationUtils { * * @param writer Writer. * @param ccfg Configuration. + * @param ver Client version. */ - public static void writeCacheConfiguration(BinaryRawWriter writer, CacheConfiguration ccfg) { + public static void writeCacheConfiguration(BinaryRawWriter writer, CacheConfiguration ccfg, + ClientListenerProtocolVersion ver) { assert writer != null; assert ccfg != null; @@ -989,7 +1006,7 @@ public class PlatformConfigurationUtils { writer.writeInt(qryEntities.size()); for (QueryEntity e : qryEntities) - writeQueryEntity(writer, e); + writeQueryEntity(writer, e, ver); } else writer.writeInt(0); @@ -1046,8 +1063,10 @@ public class PlatformConfigurationUtils { * * @param writer Writer. * @param qryEntity Query entity. + * @param ver Client version. */ - public static void writeQueryEntity(BinaryRawWriter writer, QueryEntity qryEntity) { + public static void writeQueryEntity(BinaryRawWriter writer, QueryEntity qryEntity, + ClientListenerProtocolVersion ver) { assert qryEntity != null; writer.writeString(qryEntity.getKeyType()); @@ -1063,7 +1082,8 @@ public class PlatformConfigurationUtils { Set<String> keyFields = qryEntity.getKeyFields(); Set<String> notNullFields = qryEntity.getNotNullFields(); Map<String, Object> defVals = qryEntity.getDefaultFieldValues(); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = qryEntity.getDecimalInfo(); + Map<String, Integer> fieldsPrecision = qryEntity.getFieldsPrecision(); + Map<String, Integer> fieldsScale = qryEntity.getFieldsScale(); writer.writeInt(fields.size()); @@ -1074,11 +1094,10 @@ public class PlatformConfigurationUtils { writer.writeBoolean(notNullFields != null && notNullFields.contains(field.getKey())); writer.writeObject(defVals != null ? defVals.get(field.getKey()) : null); - IgniteBiTuple<Integer, Integer> precisionAndScale = - decimalInfo == null ? null : decimalInfo.get(field.getKey()); - - writer.writeInt(precisionAndScale == null ? -1 : precisionAndScale.get1()); - writer.writeInt(precisionAndScale == null ? -1 : precisionAndScale.get2()); + if (ver.compareTo(VER_1_2_0) >= 0) { + writer.writeInt(fieldsPrecision == null ? -1 : fieldsPrecision.getOrDefault(field.getKey(), -1)); + writer.writeInt(fieldsScale == null ? -1 : fieldsScale.getOrDefault(field.getKey(), -1)); + } } } else @@ -1143,9 +1162,11 @@ public class PlatformConfigurationUtils { * * @param w Writer. * @param cfg Configuration. + * @param ver Client version. */ @SuppressWarnings("deprecation") - public static void writeIgniteConfiguration(BinaryRawWriter w, IgniteConfiguration cfg) { + public static void writeIgniteConfiguration(BinaryRawWriter w, IgniteConfiguration cfg, + ClientListenerProtocolVersion ver) { assert w != null; assert cfg != null; @@ -1218,7 +1239,7 @@ public class PlatformConfigurationUtils { w.writeInt(cacheCfg.length); for (CacheConfiguration ccfg : cacheCfg) - writeCacheConfiguration(w, ccfg); + writeCacheConfiguration(w, ccfg, ver); } else w.writeInt(0);
http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryFieldMetadata.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryFieldMetadata.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryFieldMetadata.java index 9b08d77..f9df499 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryFieldMetadata.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryFieldMetadata.java @@ -51,4 +51,18 @@ public interface GridQueryFieldMetadata extends Externalizable { * @return Field type name. */ public String fieldTypeName(); -} \ No newline at end of file + + /** + * Gets field precision. + * + * @return Field precision. + */ + public int precision(); + + /** + * Gets field scale. + * + * @return Field scale. + */ + public int scale(); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java index 04d1aa9..57eaa4a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProcessor.java @@ -2471,8 +2471,17 @@ public class GridQueryProcessor extends GridProcessorAdapter { for (QueryField col : cols) { try { - props.add(new QueryBinaryProperty(ctx, col.name(), null, Class.forName(col.typeName()), - false, null, !col.isNullable(), null, -1, -1)); + props.add(new QueryBinaryProperty( + ctx, + col.name(), + null, + Class.forName(col.typeName()), + false, + null, + !col.isNullable(), + null, + col.precision(), + col.scale())); } catch (ClassNotFoundException e) { throw new SchemaOperationException("Class not found for new property: " + col.typeName()); http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryField.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryField.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryField.java index d68a6cb..b74e3df 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryField.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryField.java @@ -70,6 +70,8 @@ public class QueryField implements Serializable { * @param typeName Class name for this field's values. * @param nullable Nullable flag. * @param dfltValue Default value. + * @param precision Precision. + * @param scale Scale. */ public QueryField(String name, String typeName, boolean nullable, Object dfltValue, int precision, int scale) { this.name = name; http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java index a7710f9..2eaeb1f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryTypeDescriptorImpl.java @@ -26,7 +26,9 @@ import java.util.List; import java.util.Map; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cache.QueryIndexType; -import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; +import org.apache.ignite.internal.processors.cache.CacheObject; +import org.apache.ignite.internal.processors.cache.CacheObjectContext; +import org.apache.ignite.internal.processors.cache.KeyCacheObject; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.tostring.GridToStringInclude; import org.apache.ignite.internal.util.typedef.F; @@ -34,6 +36,13 @@ import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.S; import org.jetbrains.annotations.Nullable; +import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.NULL_KEY; +import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.NULL_VALUE; +import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.TOO_LONG_KEY; +import static org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode.TOO_LONG_VALUE; +import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME; +import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME; + /** * Descriptor of type. */ @@ -110,13 +119,18 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { /** */ private List<GridQueryProperty> propsWithDefaultValue; + /** */ + @Nullable private CacheObjectContext coCtx; + /** * Constructor. * * @param cacheName Cache name. + * @param coCtx Cache object context. */ - public QueryTypeDescriptorImpl(String cacheName) { + public QueryTypeDescriptorImpl(String cacheName, @Nullable CacheObjectContext coCtx) { this.cacheName = cacheName; + this.coCtx = coCtx; } /** @@ -368,6 +382,19 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { * @throws IgniteCheckedException In case of error. */ public void addProperty(GridQueryProperty prop, boolean failOnDuplicate) throws IgniteCheckedException { + addProperty(prop, failOnDuplicate, true); + } + + /** + * Adds property to the type descriptor. + * + * @param prop Property. + * @param failOnDuplicate Fail on duplicate flag. + * @param isField {@code True} if {@code prop} if field, {@code False} if prop is "_KEY" or "_VAL". + * @throws IgniteCheckedException In case of error. + */ + public void addProperty(GridQueryProperty prop, boolean failOnDuplicate, boolean isField) + throws IgniteCheckedException { String name = prop.name(); if (props.put(name, prop) != null && failOnDuplicate) @@ -382,6 +409,12 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { validateProps.add(prop); } + else if (prop.precision() != -1) { + if (validateProps == null) + validateProps = new ArrayList<>(); + + validateProps.add(prop); + } if (prop.defaultValue() != null) { if (propsWithDefaultValue == null) @@ -390,7 +423,8 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { propsWithDefaultValue.add(prop); } - fields.put(name, prop.type()); + if (isField) + fields.put(name, prop.type()); } /** @@ -525,26 +559,35 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor { Object propVal; - int errCode; + boolean isKey = false; - if (F.eq(prop.name(), keyFieldName)) { - propVal = key; + if (F.eq(prop.name(), keyFieldName) || (keyFieldName == null && F.eq(prop.name(), KEY_FIELD_NAME))) { + propVal = key instanceof KeyCacheObject && coCtx != null ? + ((KeyCacheObject)key).value(coCtx, true) : key; - errCode = IgniteQueryErrorCode.NULL_KEY; + isKey = true; } - else if (F.eq(prop.name(), valFieldName)) { - propVal = val; - - errCode = IgniteQueryErrorCode.NULL_VALUE; + else if (F.eq(prop.name(), valFieldName) || (valFieldName == null && F.eq(prop.name(), VAL_FIELD_NAME))) { + propVal = val instanceof CacheObject && coCtx != null ? + ((CacheObject)val).value(coCtx, true) : val; } else { propVal = prop.value(key, val); + } - errCode = IgniteQueryErrorCode.NULL_VALUE; + if (propVal == null && prop.notNull()) { + throw new IgniteSQLException("Null value is not allowed for column '" + prop.name() + "'", + isKey ? NULL_KEY : NULL_VALUE); } - if (propVal == null) - throw new IgniteSQLException("Null value is not allowed for column '" + prop.name() + "'", errCode); + if (prop.precision() != -1 && + propVal != null && + String.class == propVal.getClass() && + ((String)propVal).length() > prop.precision()) { + throw new IgniteSQLException("Value for a column '" + prop.name() + "' is too long. " + + "Maximum length: " + prop.precision() + ", actual length: " + ((CharSequence)propVal).length(), + isKey ? TOO_LONG_KEY : TOO_LONG_VALUE); + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java index 3f40990..42de312 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QueryUtils.java @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -61,7 +62,6 @@ import org.apache.ignite.internal.util.Jsr310Java8DateTimeApiUtils; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.A; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.lang.IgniteBiTuple; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -268,7 +268,8 @@ public class QueryUtils { normalEntity.setValueFieldName(entity.getValueFieldName()); normalEntity.setNotNullFields(entity.getNotNullFields()); normalEntity.setDefaultFieldValues(entity.getDefaultFieldValues()); - normalEntity.setDecimalInfo(entity.getDecimalInfo()); + normalEntity.setFieldsPrecision(entity.getFieldsPrecision()); + normalEntity.setFieldsScale(entity.getFieldsScale()); // Normalize table name. String normalTblName = entity.getTableName(); @@ -407,7 +408,7 @@ public class QueryUtils { CacheObjectContext coCtx = binaryEnabled ? ctx.cacheObjects().contextForCache(ccfg) : null; - QueryTypeDescriptorImpl desc = new QueryTypeDescriptorImpl(cacheName); + QueryTypeDescriptorImpl desc = new QueryTypeDescriptorImpl(cacheName, coCtx); desc.schemaName(schemaName); @@ -543,10 +544,12 @@ public class QueryUtils { */ public static void processBinaryMeta(GridKernalContext ctx, QueryEntity qryEntity, QueryTypeDescriptorImpl d) throws IgniteCheckedException { + LinkedHashMap<String, String> fields = qryEntity.getFields(); Set<String> keyFields = qryEntity.getKeyFields(); Set<String> notNulls = qryEntity.getNotNullFields(); Map<String, Object> dlftVals = qryEntity.getDefaultFieldValues(); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = qryEntity.getDecimalInfo(); + Map<String, Integer> precision = qryEntity.getFieldsPrecision(); + Map<String, Integer> scale = qryEntity.getFieldsScale(); // We have to distinguish between empty and null keyFields when the key is not of SQL type - // when a key is not of SQL type, absence of a field in nonnull keyFields tell us that this field @@ -559,13 +562,13 @@ public class QueryUtils { if (hasKeyFields && !isKeyClsSqlType) { //ensure that 'keyFields' is case sensitive subset of 'fields' for (String keyField : keyFields) { - if (!qryEntity.getFields().containsKey(keyField)) + if (!fields.containsKey(keyField)) throw new IgniteCheckedException("QueryEntity 'keyFields' property must be a subset of keys " + "from 'fields' property (case sensitive): " + keyField); } } - for (Map.Entry<String, String> entry : qryEntity.getFields().entrySet()) { + for (Map.Entry<String, String> entry : fields.entrySet()) { Boolean isKeyField; if (isKeyClsSqlType) // We don't care about keyFields in this case - it might be null, or empty, or anything @@ -577,22 +580,73 @@ public class QueryUtils { Object dfltVal = dlftVals != null ? dlftVals.get(entry.getKey()) : null; - IgniteBiTuple<Integer, Integer> precisionAndScale = - decimalInfo != null ? decimalInfo.get(entry.getKey()) : null; - QueryBinaryProperty prop = buildBinaryProperty(ctx, entry.getKey(), U.classForName(entry.getValue(), Object.class, true), d.aliases(), isKeyField, notNull, dfltVal, - precisionAndScale != null ? precisionAndScale.get1() : -1, - precisionAndScale != null ? precisionAndScale.get2() : -1); + precision == null ? -1 : precision.getOrDefault(entry.getKey(), -1), + scale == null ? -1 : scale.getOrDefault(entry.getKey(), -1)); d.addProperty(prop, false); } + String keyFieldName = qryEntity.getKeyFieldName(); + + if (keyFieldName == null) + keyFieldName = KEY_FIELD_NAME; + + if (!F.isEmpty(precision) && precision.containsKey(keyFieldName) && + !fields.containsKey(keyFieldName)) { + addKeyValueValidationProperty(ctx, qryEntity, d, keyFieldName, true); + } + + String valFieldName = qryEntity.getValueFieldName(); + + if (valFieldName == null) + valFieldName = VAL_FIELD_NAME; + + if (!F.isEmpty(precision) && precision.containsKey(valFieldName) && + !fields.containsKey(valFieldName)) { + addKeyValueValidationProperty(ctx, qryEntity, d, valFieldName, false); + } + processIndexes(qryEntity, d); } /** + * Add validate property to QueryTypeDescriptor. + * + * @param ctx Kernel context. + * @param qryEntity Query entity. + * @param d Descriptor. + * @param name Field name. + * @throws IgniteCheckedException + */ + private static void addKeyValueValidationProperty(GridKernalContext ctx, QueryEntity qryEntity, QueryTypeDescriptorImpl d, + String name, boolean isKey) throws IgniteCheckedException { + + Map<String, Object> dfltVals = qryEntity.getDefaultFieldValues(); + Map<String, Integer> precision = qryEntity.getFieldsPrecision(); + Map<String, Integer> scale = qryEntity.getFieldsScale(); + + String typeName = isKey ? qryEntity.getKeyType() : qryEntity.getValueType(); + + Object dfltVal = dfltVals.get(name); + + QueryBinaryProperty prop = buildBinaryProperty( + ctx, + name, + U.classForName(typeName, Object.class, true), + d.aliases(), + isKey, + true, + dfltVal, + precision == null ? -1 : precision.getOrDefault(name, -1), + scale == null ? -1 : scale.getOrDefault(name, -1)); + + d.addProperty(prop, true, false); + } + + /** * Processes declarative metadata for binary object. * * @param qryEntity Declared metadata. @@ -731,11 +785,10 @@ public class QueryUtils { * @param precision Precision. * @param scale Scale. * @return Binary property. - * @throws IgniteCheckedException On error. */ - public static QueryBinaryProperty buildBinaryProperty(GridKernalContext ctx, String pathStr, Class<?> resType, - Map<String, String> aliases, @Nullable Boolean isKeyField, boolean notNull, Object dlftVal, - int precision, int scale) throws IgniteCheckedException { + public static QueryBinaryProperty buildBinaryProperty(GridKernalContext ctx, String pathStr, + Class<?> resType, Map<String, String> aliases, @Nullable Boolean isKeyField, boolean notNull, Object dlftVal, + int precision, int scale) { String[] path = pathStr.split("\\."); QueryBinaryProperty res = null; @@ -1215,6 +1268,26 @@ public class QueryUtils { ", queryIdx=" + idx + ']'); } } + + Map<String, Object> dfltVals = entity.getDefaultFieldValues(); + Map<String, Integer> precision = entity.getFieldsPrecision(); + + if (!F.isEmpty(precision)) { + for (String fld : precision.keySet()) { + if (!dfltVals.containsKey(fld)) + continue; + + Object dfltVal = dfltVals.get(fld); + + if (dfltVal == null) + continue; + + if (dfltVal.toString().length() > precision.get(fld)) { + throw new IgniteSQLException("Default value '" + dfltVal + + "' is longer than maximum length " + precision.get(fld)); + } + } + } } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java ---------------------------------------------------------------------- diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java index 786d0e0..cb3d8f2 100644 --- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java @@ -407,6 +407,24 @@ public final class GridTestUtils { * Checks whether callable throws exception, which is itself of a specified * class, or has a cause of the specified class. * + * @param runnable Runnable. + * @param cls Expected class. + * @return Thrown throwable. + */ + @Nullable public static Throwable assertThrowsWithCause(Runnable runnable, Class<? extends Throwable> cls) { + return assertThrowsWithCause(new Callable<Integer>() { + @Override public Integer call() throws Exception { + runnable.run(); + + return 0; + } + }, cls); + } + + /** + * Checks whether callable throws exception, which is itself of a specified + * class, or has a cause of the specified class. + * * @param call Callable. * @param cls Expected class. * @return Thrown throwable. http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java index 46aa1fc..de4c6c6 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2SqlFieldMetadata.java @@ -46,6 +46,12 @@ public class H2SqlFieldMetadata implements GridQueryFieldMetadata { /** Type. */ private String type; + /** Precision. */ + private int precision; + + /** Scale. */ + private int scale; + /** * Required by {@link Externalizable}. */ @@ -58,14 +64,19 @@ public class H2SqlFieldMetadata implements GridQueryFieldMetadata { * @param typeName Type name. * @param name Name. * @param type Type. + * @param precision Precision. + * @param scale Scale. */ - H2SqlFieldMetadata(@Nullable String schemaName, @Nullable String typeName, String name, String type) { + H2SqlFieldMetadata(@Nullable String schemaName, @Nullable String typeName, String name, String type, + int precision, int scale) { assert name != null && type != null : schemaName + " | " + typeName + " | " + name + " | " + type; this.schemaName = schemaName; this.typeName = typeName; this.name = name; this.type = type; + this.precision = precision; + this.scale = scale; } /** {@inheritDoc} */ @@ -89,11 +100,24 @@ public class H2SqlFieldMetadata implements GridQueryFieldMetadata { } /** {@inheritDoc} */ + @Override public int precision() { + return precision; + } + + /** {@inheritDoc} */ + @Override public int scale() { + return scale; + } + + /** {@inheritDoc} */ @Override public void writeExternal(ObjectOutput out) throws IOException { U.writeString(out, schemaName); U.writeString(out, typeName); U.writeString(out, name); U.writeString(out, type); + out.write(precision); + out.write(scale); + } /** {@inheritDoc} */ @@ -102,6 +126,8 @@ public class H2SqlFieldMetadata implements GridQueryFieldMetadata { typeName = U.readString(in); name = U.readString(in); type = U.readString(in); + precision = in.read(); + scale = in.read(); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java index e9d9f90..b9d9d8e 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/H2Utils.java @@ -211,11 +211,13 @@ public class H2Utils { String typeName = rsMeta.getTableName(i); String name = rsMeta.getColumnLabel(i); String type = rsMeta.getColumnClassName(i); + int precision = rsMeta.getPrecision(i); + int scale = rsMeta.getScale(i); if (type == null) // Expression always returns NULL. type = Void.class.getName(); - meta.add(new H2SqlFieldMetadata(schemaName, typeName, name, type)); + meta.add(new H2SqlFieldMetadata(schemaName, typeName, name, type, precision, scale)); } return meta; http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/IgniteH2Indexing.java index 5859ecb..6b7eddf 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 @@ -85,6 +85,7 @@ import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata; import org.apache.ignite.internal.processors.query.GridQueryFieldsResult; import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter; import org.apache.ignite.internal.processors.query.GridQueryIndexing; +import org.apache.ignite.internal.processors.query.GridQueryProperty; import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner; import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor; import org.apache.ignite.internal.processors.query.GridRunningQueryInfo; @@ -231,7 +232,7 @@ public class IgniteH2Indexing implements GridQueryIndexing { /** Dummy metadata for update result. */ public static final List<GridQueryFieldMetadata> UPDATE_RESULT_META = Collections.<GridQueryFieldMetadata> - singletonList(new H2SqlFieldMetadata(null, null, "UPDATED", Long.class.getName())); + singletonList(new H2SqlFieldMetadata(null, null, "UPDATED", Long.class.getName(), -1, -1)); /** */ private static final int PREPARED_STMT_CACHE_SIZE = 256; @@ -2304,8 +2305,16 @@ public class IgniteH2Indexing implements GridQueryIndexing { assert schema != null; assert tbl != null; - String keyType = dbTypeFromClass(tbl.type().keyClass()); - String valTypeStr = dbTypeFromClass(tbl.type().valueClass()); + GridQueryProperty keyProp = tbl.type().property(KEY_FIELD_NAME); + GridQueryProperty valProp = tbl.type().property(VAL_FIELD_NAME); + + String keyType = dbTypeFromClass(tbl.type().keyClass(), + keyProp == null ? -1 : keyProp.precision(), + keyProp == null ? -1 : keyProp.scale()); + + String valTypeStr = dbTypeFromClass(tbl.type().valueClass(), + valProp == null ? -1 : valProp.precision(), + valProp == null ? -1 : valProp.scale()); SB sql = new SB(); @@ -2317,9 +2326,15 @@ public class IgniteH2Indexing implements GridQueryIndexing { sql.a(',').a(VAL_FIELD_NAME).a(' ').a(valTypeStr).a(keyValVisibility); sql.a(',').a(VER_FIELD_NAME).a(" OTHER INVISIBLE"); - for (Map.Entry<String, Class<?>> e : tbl.type().fields().entrySet()) - sql.a(',').a(H2Utils.withQuotes(e.getKey())).a(' ').a(dbTypeFromClass(e.getValue())) - .a(tbl.type().property(e.getKey()).notNull()? " NOT NULL" : ""); + for (Map.Entry<String, Class<?>> e : tbl.type().fields().entrySet()) { + GridQueryProperty prop = tbl.type().property(e.getKey()); + + sql.a(',') + .a(H2Utils.withQuotes(e.getKey())) + .a(' ') + .a(dbTypeFromClass(e.getValue(), prop.precision(), prop.scale())) + .a(prop.notNull() ? " NOT NULL" : ""); + } sql.a(')'); @@ -2391,10 +2406,17 @@ public class IgniteH2Indexing implements GridQueryIndexing { * Gets corresponding DB type from java class. * * @param cls Java class. + * @param precision Field precision. + * @param scale Field scale. * @return DB type name. */ - private String dbTypeFromClass(Class<?> cls) { - return H2DatabaseType.fromClass(cls).dBTypeAsString(); + private String dbTypeFromClass(Class<?> cls, int precision, int scale) { + String dbType = H2DatabaseType.fromClass(cls).dBTypeAsString(); + + if (precision != -1 && dbType.equalsIgnoreCase(H2DatabaseType.VARCHAR.dBTypeAsString())) + return dbType + "(" + precision + ")"; + + return dbType; } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java index 0a0398b..9e88f16 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/ddl/DdlStatementsProcessor.java @@ -631,7 +631,8 @@ public class DdlStatementsProcessor { HashMap<String, Object> dfltValues = new HashMap<>(); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = new HashMap<>(); + Map<String, Integer> precision = new HashMap<>(); + Map<String, Integer> scale = new HashMap<>(); for (Map.Entry<String, GridSqlColumn> e : createTbl.columns().entrySet()) { GridSqlColumn gridCol = e.getValue(); @@ -652,15 +653,27 @@ public class DdlStatementsProcessor { if (dfltVal != null) dfltValues.put(e.getKey(), dfltVal); - if (col.getType() == Value.DECIMAL) - decimalInfo.put(e.getKey(), F.t((int)col.getPrecision(), col.getScale())); + if (col.getType() == Value.DECIMAL) { + precision.put(e.getKey(), (int)col.getPrecision()); + + scale.put(e.getKey(), col.getScale()); + } + + if (col.getType() == Value.STRING || + col.getType() == Value.STRING_FIXED || + col.getType() == Value.STRING_IGNORECASE) { + precision.put(e.getKey(), (int)col.getPrecision()); + } } if (!F.isEmpty(dfltValues)) res.setDefaultFieldValues(dfltValues); - if (!F.isEmpty(decimalInfo)) - res.setDecimalInfo(decimalInfo); + if (!F.isEmpty(precision)) + res.setFieldsPrecision(precision); + + if (!F.isEmpty(scale)) + res.setFieldsScale(scale); String valTypeName = QueryUtils.createTableValueTypeName(createTbl.schemaName(), createTbl.tableName()); String keyTypeName = QueryUtils.createTableKeyTypeName(valTypeName); http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java index 361f8b4..ce5c95e 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractFieldsQuerySelfTest.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache; +import com.google.common.collect.ImmutableMap; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -35,8 +36,8 @@ import org.apache.ignite.cache.CacheAtomicityMode; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cache.CacheWriteSynchronizationMode; +import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.cache.affinity.AffinityKey; -import org.apache.ignite.cache.query.FieldsQueryCursor; import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.cache.query.SqlFieldsQuery; import org.apache.ignite.cache.query.annotations.QuerySqlField; @@ -44,14 +45,13 @@ import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.binary.BinaryMarshaller; -import org.apache.ignite.internal.processors.cache.index.AbstractSchemaSelfTest; import org.apache.ignite.internal.processors.cache.query.GridCacheSqlIndexMetadata; import org.apache.ignite.internal.processors.cache.query.GridCacheSqlMetadata; import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree; -import org.apache.ignite.internal.processors.cache.query.SqlFieldsQueryEx; import org.apache.ignite.internal.processors.datastructures.GridCacheAtomicLongValue; import org.apache.ignite.internal.processors.datastructures.GridCacheInternalKeyImpl; import org.apache.ignite.internal.processors.query.GridQueryFieldMetadata; +import org.apache.ignite.internal.processors.query.GridQueryProcessor; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuerySplitter; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; @@ -352,7 +352,7 @@ public abstract class IgniteCacheAbstractFieldsQuerySelfTest extends GridCommonA } else if (DEFAULT_CACHE_NAME.equals(meta.cacheName()) || noOpCache.getName().equals(meta.cacheName())) assertTrue("Invalid types size", types.isEmpty()); - else + else if (!"cacheWithCustomKeyPrecision".equalsIgnoreCase(meta.cacheName())) fail("Unknown cache: " + meta.cacheName()); } } @@ -383,6 +383,102 @@ public abstract class IgniteCacheAbstractFieldsQuerySelfTest extends GridCommonA } /** @throws Exception If failed. */ + @SuppressWarnings("unchecked") + public void testExecuteWithMetaDataAndPrecision() throws Exception { + QueryEntity qeWithPrecision = new QueryEntity() + .setKeyType("java.lang.Long") + .setValueType("TestType") + .addQueryField("strField", "java.lang.String", "strField") + .setFieldsPrecision(ImmutableMap.of("strField", 999)); + + grid(0).getOrCreateCache(cacheConfiguration() + .setName("cacheWithPrecision") + .setQueryEntities(Collections.singleton(qeWithPrecision))); + + GridQueryProcessor qryProc = grid(0).context().query(); + + qryProc.querySqlFields( + new SqlFieldsQuery("INSERT INTO TestType(_KEY, strField) VALUES(?, ?)") + .setSchema("cacheWithPrecision") + .setArgs(1, "ABC"), true); + + qryProc.querySqlFields( + new SqlFieldsQuery("INSERT INTO TestType(_KEY, strField) VALUES(?, ?)") + .setSchema("cacheWithPrecision") + .setArgs(2, "DEF"), true); + + + QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)qryProc.querySqlFields( + new SqlFieldsQuery("SELECT _KEY, strField FROM TestType") + .setSchema("cacheWithPrecision"), true); + + List<GridQueryFieldMetadata> fieldsMeta = cursor.fieldsMeta(); + + for (GridQueryFieldMetadata meta : fieldsMeta) { + if (!meta.fieldName().equalsIgnoreCase("strField")) + continue; + + assertEquals(999, meta.precision()); + } + } + + public void testExecuteWithMetaDataAndCustomKeyPrecision() throws Exception { + QueryEntity qeWithPrecision = new QueryEntity() + .setKeyType("java.lang.String") + .setKeyFieldName("my_key") + .setValueType("CustomKeyType") + .addQueryField("my_key", "java.lang.String", "my_key") + .addQueryField("strField", "java.lang.String", "strField") + .setFieldsPrecision(ImmutableMap.of("strField", 999, "my_key", 777)); + + grid(0).getOrCreateCache(cacheConfiguration() + .setName("cacheWithCustomKeyPrecision") + .setQueryEntities(Collections.singleton(qeWithPrecision))); + + GridQueryProcessor qryProc = grid(0).context().query(); + + qryProc.querySqlFields( + new SqlFieldsQuery("INSERT INTO CustomKeyType(my_key, strField) VALUES(?, ?)") + .setSchema("cacheWithCustomKeyPrecision") + .setArgs("1", "ABC"), true); + + qryProc.querySqlFields( + new SqlFieldsQuery("INSERT INTO CustomKeyType(my_key, strField) VALUES(?, ?)") + .setSchema("cacheWithCustomKeyPrecision") + .setArgs("2", "DEF"), true); + + QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)qryProc.querySqlFields( + new SqlFieldsQuery("SELECT my_key, strField FROM CustomKeyType") + .setSchema("cacheWithCustomKeyPrecision"), true); + + List<GridQueryFieldMetadata> fieldsMeta = cursor.fieldsMeta(); + + int fldCnt = 0; + + for (GridQueryFieldMetadata meta : fieldsMeta) { + switch (meta.fieldName()) { + case "STRFIELD": + assertEquals(999, meta.precision()); + + fldCnt++; + + break; + + case "MY_KEY": + assertEquals(777, meta.precision()); + + fldCnt++; + + break; + default: + fail("Unknown field - " + meta.fieldName()); + } + } + + assertEquals("Metadata for all fields should be returned.", 2, fldCnt); + } + + /** @throws Exception If failed. */ public void testExecuteWithMetaData() throws Exception { QueryCursorImpl<List<?>> cursor = (QueryCursorImpl<List<?>>)personCache.query(sqlFieldsQuery( String.format("select p._KEY, p.name, p.age, o.name " + http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IgniteDecimalSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IgniteDecimalSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IgniteDecimalSelfTest.java index 9e65276..96926ea 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IgniteDecimalSelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/IgniteDecimalSelfTest.java @@ -32,8 +32,6 @@ import org.apache.ignite.cache.query.annotations.QuerySqlField; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.IgniteEx; -import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.lang.IgniteBiTuple; import org.jetbrains.annotations.NotNull; import static java.math.RoundingMode.HALF_UP; @@ -108,11 +106,14 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { queryEntity.addQueryField("id", Integer.class.getName(), null); queryEntity.addQueryField("amount", BigDecimal.class.getName(), null); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = new HashMap<>(); + Map<String, Integer> precision = new HashMap<>(); + Map<String, Integer> scale = new HashMap<>(); - decimalInfo.put("amount", F.t(PRECISION, SCALE)); + precision.put("amount",PRECISION); + scale.put("amount", SCALE); - queryEntity.setDecimalInfo(decimalInfo); + queryEntity.setFieldsPrecision(precision); + queryEntity.setFieldsScale(scale); ccfg.setQueryEntities(Collections.singletonList(queryEntity)); @@ -123,14 +124,14 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { * @throws Exception If failed. */ public void testConfiguredFromDdl() throws Exception { - checkDecimalInfo(DEC_TAB_NAME, VALUE, PRECISION, SCALE); + checkPrecisionAndScale(DEC_TAB_NAME, VALUE, PRECISION, SCALE); } /** * @throws Exception If failed. */ public void testConfiguredFromQueryEntity() throws Exception { - checkDecimalInfo(SALARY_TAB_NAME, "amount", PRECISION, SCALE); + checkPrecisionAndScale(SALARY_TAB_NAME, "amount", PRECISION, SCALE); } /** @@ -145,7 +146,7 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { IgniteCache<Integer, Salary> cache = grid.createCache(ccfg); - checkDecimalInfo(tabName, "amount", PRECISION, SCALE); + checkPrecisionAndScale(tabName, "amount", PRECISION, SCALE); } /** @@ -160,7 +161,7 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { grid.createCache(ccfg); - checkDecimalInfo(SalaryWithAnnotations.class.getSimpleName().toUpperCase(), "amount", PRECISION, SCALE); + checkPrecisionAndScale(SalaryWithAnnotations.class.getSimpleName().toUpperCase(), "amount", PRECISION, SCALE); } /** */ @@ -177,21 +178,22 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { } /** */ - private void checkDecimalInfo(String tabName, String colName, Integer precision, Integer scale) { + private void checkPrecisionAndScale(String tabName, String colName, Integer precision, Integer scale) { QueryEntity queryEntity = findTableInfo(tabName); assertNotNull(queryEntity); - Map<String, IgniteBiTuple<Integer, Integer>> decimalInfo = queryEntity.getDecimalInfo(); + Map<String, Integer> fieldsPrecision = queryEntity.getFieldsPrecision(); - assertNotNull(decimalInfo); + assertNotNull(precision); - IgniteBiTuple<Integer, Integer> columnInfo = decimalInfo.get(colName); + assertEquals(fieldsPrecision.get(colName), precision); - assertNotNull(columnInfo); + Map<String, Integer> fieldsScale = queryEntity.getFieldsScale(); - assertEquals(columnInfo.get1(), precision); - assertEquals(columnInfo.get2(), scale); + assertEquals(fieldsScale.get(colName), scale); + + assertNotNull(scale); } /** @@ -202,7 +204,6 @@ public class IgniteDecimalSelfTest extends AbstractSchemaSelfTest { IgniteEx ignite = grid(0); Collection<String> cacheNames = ignite.cacheNames(); - for (String cacheName : cacheNames) { CacheConfiguration ccfg = ignite.cache(cacheName).getConfiguration(CacheConfiguration.class); http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedAtomicColumnConstraintsTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedAtomicColumnConstraintsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedAtomicColumnConstraintsTest.java new file mode 100644 index 0000000..601090f --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedAtomicColumnConstraintsTest.java @@ -0,0 +1,398 @@ +/* + * 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.sql; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.cache.processor.EntryProcessorResult; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteException; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheEntryProcessor; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.QueryEntity; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.jetbrains.annotations.NotNull; + +import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; +import static org.apache.ignite.cache.CacheMode.PARTITIONED; +import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.apache.ignite.internal.processors.query.QueryUtils.KEY_FIELD_NAME; +import static org.apache.ignite.internal.processors.query.QueryUtils.VAL_FIELD_NAME; +import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause; + +/** */ +public class IgniteCachePartitionedAtomicColumnConstraintsTest extends GridCommonAbstractTest { + /** */ + private static final long FUT_TIMEOUT = 10_000L; + + /** */ + private static final String STR_CACHE_NAME = "STR_STR"; + + /** */ + private static final String STR_ORG_CACHE_NAME = "STR_ORG"; + + private static final String STR_ORG_WITH_FIELDS_CACHE_NAME = "STR_ORG_WITH_FIELDS"; + + /** */ + private static final String OBJ_CACHE_NAME = "ORG_ADDRESS"; + + /** */ + private Consumer<Runnable> shouldFail = (op) -> assertThrowsWithCause(op, IgniteException.class); + + /** */ + private Consumer<Runnable> shouldSucceed = Runnable::run; + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGrid(0); + + Map<String, Integer> strStrPrecision = new HashMap<>(); + + strStrPrecision.put(KEY_FIELD_NAME, 5); + strStrPrecision.put(VAL_FIELD_NAME, 5); + + jcache(grid(0), cacheConfiguration(new QueryEntity(String.class.getName(), String.class.getName()) + .setFieldsPrecision(strStrPrecision)), STR_CACHE_NAME); + + Map<String, Integer> orgAddressPrecision = new HashMap<>(); + + orgAddressPrecision.put("name", 5); + orgAddressPrecision.put("address", 5); + + jcache(grid(0), cacheConfiguration(new QueryEntity(Organization.class.getName(), Address.class.getName()) + .addQueryField("name", "java.lang.String", "name") + .addQueryField("address", "java.lang.String", "address") + .setFieldsPrecision(orgAddressPrecision)), OBJ_CACHE_NAME); + + Map<String, Integer> strOrgPrecision = new HashMap<>(); + + strOrgPrecision.put(KEY_FIELD_NAME, 5); + + jcache(grid(0), cacheConfiguration(new QueryEntity(String.class.getName(), Organization.class.getName()) + .setFieldsPrecision(strOrgPrecision)), STR_ORG_CACHE_NAME); + + jcache(grid(0), cacheConfiguration(new QueryEntity(String.class.getName(), Organization.class.getName()) + .addQueryField("name", "java.lang.String", "name") + .addQueryField("address", "java.lang.String", "address") + .setFieldsPrecision(strOrgPrecision)), STR_ORG_WITH_FIELDS_CACHE_NAME); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongValueFail() throws Exception { + IgniteCache<String, String> cache = jcache(0, STR_CACHE_NAME); + + T2<String, String> val = new T2<>("3", "123456"); + + checkPutAll(shouldFail, cache, new T2<>("1", "1"), val); + + checkPutOps(shouldFail, cache, val); + + checkReplaceOps(shouldFail, cache, val, "1"); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongKeyFail() throws Exception { + IgniteCache<String, String> cache = jcache(0, STR_CACHE_NAME); + + T2<String, String> val = new T2<>("123456", "2"); + + checkPutAll(shouldFail, cache, new T2<>("1", "1"), val); + + checkPutOps(shouldFail, cache, val); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongValueFieldFail() throws Exception { + IgniteCache<Organization, Address> cache = jcache(0, OBJ_CACHE_NAME); + + T2<Organization, Address> val = new T2<>(new Organization("3"), new Address("123456")); + + checkPutAll(shouldFail, cache, new T2<>(new Organization("1"), new Address("1")), val); + + checkPutOps(shouldFail, cache, val); + + checkReplaceOps(shouldFail, cache, val, new Address("1")); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongKeyFieldFail() throws Exception { + IgniteCache<Organization, Address> cache = jcache(0, OBJ_CACHE_NAME); + + T2<Organization, Address> val = new T2<>(new Organization("123456"), new Address("2")); + + checkPutAll(shouldFail, cache, new T2<>(new Organization("1"), new Address("1")), val); + + checkPutOps(shouldFail, cache, val); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongKeyFail2() throws Exception { + doCheckPutTooLongKeyFail2(STR_ORG_CACHE_NAME); + } + + /** + * @throws Exception If failed. + */ + public void testPutTooLongKeyFail3() throws Exception { + doCheckPutTooLongKeyFail2(STR_ORG_WITH_FIELDS_CACHE_NAME); + } + + + private void doCheckPutTooLongKeyFail2(String cacheName) { + IgniteCache<String, Organization> cache = jcache(0, cacheName); + + T2<String, Organization> val = new T2<>("123456", new Organization("1")); + + checkPutAll(shouldFail, cache, new T2<>("1", new Organization("1")), val); + + checkPutOps(shouldFail, cache, val); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongValue() throws Exception { + IgniteCache<String, String> cache = jcache(0, STR_CACHE_NAME); + + T2<String, String> val = new T2<>("3", "12345"); + + checkPutAll(shouldSucceed, cache, new T2<>("1", "1"), val); + + checkPutOps(shouldSucceed, cache, val); + + checkReplaceOps(shouldSucceed, cache, val, "1"); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongKey() throws Exception { + IgniteCache<String, String> cache = jcache(0, STR_CACHE_NAME); + + T2<String, String> val = new T2<>("12345", "2"); + + checkPutAll(shouldSucceed, cache, new T2<>("1", "1"), val); + + checkPutOps(shouldSucceed, cache, val); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongValueField() throws Exception { + IgniteCache<Organization, Address> cache = jcache(0, OBJ_CACHE_NAME); + + T2<Organization, Address> val = new T2<>(new Organization("3"), new Address("12345")); + + checkPutAll(shouldSucceed, cache, new T2<>(new Organization("1"), new Address("1")), val); + + checkPutOps(shouldSucceed, cache, val); + + checkReplaceOps(shouldSucceed, cache, val, new Address("1")); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongKeyField() throws Exception { + IgniteCache<Organization, Address> cache = jcache(0, OBJ_CACHE_NAME); + + T2<Organization, Address> val = new T2<>(new Organization("12345"), new Address("2")); + + checkPutAll(shouldSucceed, cache, new T2<>(new Organization("1"), new Address("1")), val); + + checkPutOps(shouldSucceed, cache, val); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongKey2() throws Exception { + doCheckPutLongKey2(STR_ORG_CACHE_NAME); + } + + /** + * @throws Exception If failed. + */ + public void testPutLongKey3() throws Exception { + doCheckPutLongKey2(STR_ORG_WITH_FIELDS_CACHE_NAME); + } + + private void doCheckPutLongKey2(String cacheName) { + IgniteCache<String, Organization> cache = jcache(0, cacheName); + + T2<String, Organization> key2 = new T2<>("12345", new Organization("1")); + + checkPutAll(shouldSucceed, cache, new T2<>("1", new Organization("1")), key2); + + checkPutOps(shouldSucceed, cache, key2); + } + + /** */ + private <K, V> void checkReplaceOps(Consumer<Runnable> checker, IgniteCache<K, V> cache, T2<K, V> val, V okVal) { + K k = val.get1(); + V v = val.get2(); + + cache.put(k, okVal); + + CacheEntryProcessor<K, V, ?> entryProcessor = (e, arguments) -> { + e.setValue((V)arguments[0]); + + return null; + }; + + Stream<Runnable> ops = Stream.of( + () -> cache.replace(k, v), + () -> cache.getAndReplace(k, v), + () -> cache.replace(k, okVal, v), + () -> cache.invoke(k, entryProcessor, v), + () -> cache.replaceAsync(k, v).get(FUT_TIMEOUT), + () -> cache.getAndReplaceAsync(k, v).get(FUT_TIMEOUT), + () -> cache.replaceAsync(k, okVal, v).get(FUT_TIMEOUT), + () -> cache.invokeAsync(k, entryProcessor, v).get(FUT_TIMEOUT) + ); + + ops.forEach(checker); + } + + /** */ + private <K, V> void checkPutOps(Consumer<Runnable> checker, IgniteCache<K, V> cache, T2<K, V> val) { + K k = val.get1(); + V v = val.get2(); + + Stream<Runnable> ops = Stream.of( + () -> cache.put(k, v), + () -> cache.putIfAbsent(k, v), + () -> cache.getAndPut(k, v), + () -> cache.getAndPutIfAbsent(k, v), + () -> cache.putAsync(k, v).get(FUT_TIMEOUT), + () -> cache.putIfAbsentAsync(k, v).get(FUT_TIMEOUT), + () -> cache.getAndPutAsync(k, v).get(FUT_TIMEOUT), + () -> cache.getAndPutIfAbsentAsync(k, v).get(FUT_TIMEOUT) + ); + + ops.forEach(checker); + } + + /** */ + private <K, V> void checkPutAll(Consumer<Runnable> checker, IgniteCache<K, V> cache, T2<K, V>... entries) { + CacheEntryProcessor<K, V, ?> entryProcessor = (e, arguments) -> { + e.setValue(((Iterator<V>)arguments[0]).next()); + + return null; + }; + + Map<K, V> vals = Arrays.stream(entries).collect(Collectors.toMap(T2::get1, T2::get2)); + + Stream<Runnable> ops = Stream.of( + () -> cache.putAll(vals), + () -> cache.putAllAsync(vals).get(FUT_TIMEOUT), + () -> { + Map<K, ? extends EntryProcessorResult<?>> map = + cache.invokeAll(vals.keySet(), entryProcessor, vals.values().iterator()); + + for (EntryProcessorResult<?> result : map.values()) + log.info(">>> " + result.get()); + }, + () -> { + Map<K, ? extends EntryProcessorResult<?>> map = + cache.invokeAllAsync(vals.keySet(), entryProcessor, vals.values().iterator()).get(FUT_TIMEOUT); + + for (EntryProcessorResult<?> result : map.values()) + log.info(">>> " + result.get()); + } + ); + + ops.forEach(checker); + } + + /** + * @param qryEntity Query entity. + * @return Cache configuration. + */ + protected CacheConfiguration cacheConfiguration(QueryEntity qryEntity) { + CacheConfiguration<?, ?> cache = defaultCacheConfiguration(); + + cache.setCacheMode(cacheMode()); + cache.setAtomicityMode(atomicityMode()); + cache.setBackups(1); + cache.setWriteSynchronizationMode(FULL_SYNC); + + cache.setQueryEntities(Collections.singletonList(qryEntity)); + + return cache; + } + + /** */ + @NotNull protected CacheAtomicityMode atomicityMode() { + return ATOMIC; + } + + /** */ + @NotNull protected CacheMode cacheMode() { + return PARTITIONED; + } + + /** */ + @SuppressWarnings("UnusedDeclaration") + private static class Organization implements Serializable { + /** Name. */ + private final String name; + + /** + * @param name Name. + */ + private Organization(String name) { + this.name = name; + } + } + + /** */ + @SuppressWarnings("UnusedDeclaration") + private static class Address implements Serializable { + /** Name. */ + private final String address; + + /** + * @param address Address. + */ + private Address(String address) { + this.address = address; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedTransactionalColumnConstraintsTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedTransactionalColumnConstraintsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedTransactionalColumnConstraintsTest.java new file mode 100644 index 0000000..cd5c979 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCachePartitionedTransactionalColumnConstraintsTest.java @@ -0,0 +1,30 @@ +/* + * 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.sql; + +import org.apache.ignite.cache.CacheAtomicityMode; +import org.jetbrains.annotations.NotNull; + +/** */ +public class IgniteCachePartitionedTransactionalColumnConstraintsTest + extends IgniteCachePartitionedAtomicColumnConstraintsTest { + /** {@inheritDoc} */ + @NotNull protected CacheAtomicityMode atomicityMode() { + return CacheAtomicityMode.TRANSACTIONAL; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedAtomicColumnConstraintsTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedAtomicColumnConstraintsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedAtomicColumnConstraintsTest.java new file mode 100644 index 0000000..eba9f64 --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedAtomicColumnConstraintsTest.java @@ -0,0 +1,32 @@ +/* + * 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.sql; + +import org.apache.ignite.cache.CacheMode; +import org.jetbrains.annotations.NotNull; + +import static org.apache.ignite.cache.CacheMode.PARTITIONED; + +/** */ +public class IgniteCacheReplicatedAtomicColumnConstraintsTest + extends IgniteCachePartitionedAtomicColumnConstraintsTest { + /** {@inheritDoc} */ + @NotNull @Override protected CacheMode cacheMode() { + return PARTITIONED; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedTransactionalColumnConstraintsTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedTransactionalColumnConstraintsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedTransactionalColumnConstraintsTest.java new file mode 100644 index 0000000..bddd69a --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteCacheReplicatedTransactionalColumnConstraintsTest.java @@ -0,0 +1,30 @@ +/* + * 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.sql; + +import org.apache.ignite.cache.CacheAtomicityMode; +import org.jetbrains.annotations.NotNull; + +/** */ +public class IgniteCacheReplicatedTransactionalColumnConstraintsTest + extends IgniteCacheReplicatedAtomicColumnConstraintsTest { + /** {@inheritDoc} */ + @NotNull @Override protected CacheAtomicityMode atomicityMode() { + return CacheAtomicityMode.TRANSACTIONAL; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteSQLColumnConstraintsTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteSQLColumnConstraintsTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteSQLColumnConstraintsTest.java new file mode 100644 index 0000000..762743b --- /dev/null +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/sql/IgniteSQLColumnConstraintsTest.java @@ -0,0 +1,143 @@ +/* + * 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.sql; + +import java.util.List; +import org.apache.ignite.cache.query.SqlFieldsQuery; +import org.apache.ignite.internal.processors.odbc.SqlStateCode; +import org.apache.ignite.internal.processors.query.IgniteSQLException; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; + +import static org.apache.ignite.internal.processors.odbc.SqlStateCode.CONSTRAINT_VIOLATION; + +/** + */ +public class IgniteSQLColumnConstraintsTest extends GridCommonAbstractTest { + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + startGrid(0); + + execSQL("CREATE TABLE varchar_table(id INT PRIMARY KEY, str VARCHAR(5))"); + + execSQL("INSERT INTO varchar_table VALUES(?, ?)", 1, "12345"); + + execSQL("CREATE TABLE char_table(id INT PRIMARY KEY, str CHAR(5))"); + + execSQL("INSERT INTO char_table VALUES(?, ?)", 1, "12345"); + } + + /** + * @throws Exception If failed. + */ + public void testCreateTableWithTooLongDefault() throws Exception { + checkSQLThrows("CREATE TABLE too_long_default(id INT PRIMARY KEY, str CHAR(5) DEFAULT '123456')"); + } + + /** + * @throws Exception If failed. + */ + public void testInsertTooLongVarchar() throws Exception { + checkSQLThrows("INSERT INTO varchar_table VALUES(?, ?)", 2, "123456"); + + checkSQLThrows("UPDATE varchar_table SET str = ? WHERE id = ?", "123456", 1); + + checkSQLThrows("MERGE INTO varchar_table(id, str) VALUES(?, ?)", 1, "123456"); + } + + /** + * @throws Exception If failed. + */ + public void testInsertTooLongChar() throws Exception { + checkSQLThrows("INSERT INTO char_table VALUES(?, ?)", 2, "123456"); + + checkSQLThrows("UPDATE char_table SET str = ? WHERE id = ?", "123456", 1); + + checkSQLThrows("MERGE INTO char_table(id, str) VALUES(?, ?)", 1, "123456"); + } + + /** + * @throws Exception If failed. + */ + public void testConstraintsAfterAlterTable() throws Exception { + execSQL("CREATE TABLE char_table_2(id INT PRIMARY KEY, field INTEGER)"); + + execSQL("ALTER TABLE char_table_2 ADD COLUMN str CHAR(5) NOT NULL"); + + execSQL("INSERT INTO char_table_2(id, str) VALUES(?, ?)", 1, "1"); + + checkSQLThrows("INSERT INTO char_table_2(id, str) VALUES(?, ?)", 2, "123456"); + + checkSQLThrows("UPDATE char_table_2 SET str = ? WHERE id = ?", "123456", 1); + + checkSQLThrows("MERGE INTO char_table_2(id, str) VALUES(?, ?)", 1, "123456"); + } + + /** + * @throws Exception If failed. + */ + public void testDropColumnWithConstraint() throws Exception { + execSQL("CREATE TABLE char_table_3(id INT PRIMARY KEY, field CHAR(5), field2 INTEGER)"); + + execSQL("INSERT INTO char_table_3(id, field, field2) VALUES(?, ?, ?)", 1, "12345", 1); + + checkSQLThrows("INSERT INTO char_table_3(id, field, field2) VALUES(?, ?, ?)", 2, "123456", 1); + + execSQL("ALTER TABLE char_table_3 DROP COLUMN field"); + + execSQL("INSERT INTO char_table_3(id, field2) VALUES(?, ?)", 3, 3); + } + + public void testSqlState() throws Exception { + execSQL("CREATE TABLE char_table_4(id INT PRIMARY KEY, field CHAR(5))"); + + IgniteSQLException err = (IgniteSQLException) + checkSQLThrows("INSERT INTO char_table_4(id, field) VALUES(?, ?)", 1, "123456"); + + assertEquals(err.sqlState(), CONSTRAINT_VIOLATION); + + execSQL("INSERT INTO char_table_4(id, field) VALUES(?, ?)", 2, "12345"); + + err = (IgniteSQLException) + checkSQLThrows("UPDATE char_table_4 SET field = ? WHERE id = ?", "123456", 2); + + assertEquals(err.sqlState(), CONSTRAINT_VIOLATION); + + err = (IgniteSQLException) + checkSQLThrows("MERGE INTO char_table_4(id, field) VALUES(?, ?)", 2, "123456"); + + assertEquals(err.sqlState(), CONSTRAINT_VIOLATION); + } + + /** */ + private Throwable checkSQLThrows(String sql, Object... args) { + return GridTestUtils.assertThrowsWithCause(() -> { + execSQL(sql, args); + + return 0; + }, IgniteSQLException.class); + } + + /** */ + private List<?> execSQL(String sql, Object... args) { + SqlFieldsQuery qry = new SqlFieldsQuery(sql) + .setArgs(args); + + return grid(0).context().query().querySqlFields(qry, true).getAll(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java index a658caf..6b3cace 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java +++ b/modules/indexing/src/test/java/org/apache/ignite/testsuites/IgniteCacheQuerySelfTestSuite.java @@ -188,6 +188,11 @@ import org.apache.ignite.internal.processors.query.h2.sql.BaseH2CompareQueryTest import org.apache.ignite.internal.processors.query.h2.sql.GridQueryParsingTest; import org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryDistributedJoinsTest; import org.apache.ignite.internal.processors.query.h2.sql.H2CompareBigQueryTest; +import org.apache.ignite.internal.processors.sql.IgniteCachePartitionedAtomicColumnConstraintsTest; +import org.apache.ignite.internal.processors.sql.IgniteCachePartitionedTransactionalColumnConstraintsTest; +import org.apache.ignite.internal.processors.sql.IgniteCacheReplicatedAtomicColumnConstraintsTest; +import org.apache.ignite.internal.processors.sql.IgniteCacheReplicatedTransactionalColumnConstraintsTest; +import org.apache.ignite.internal.processors.sql.IgniteSQLColumnConstraintsTest; import org.apache.ignite.internal.processors.sql.SqlConnectorConfigurationValidationSelfTest; import org.apache.ignite.internal.sql.SqlParserBulkLoadSelfTest; import org.apache.ignite.internal.sql.SqlParserCreateIndexSelfTest; @@ -444,6 +449,12 @@ public class IgniteCacheQuerySelfTestSuite extends TestSuite { suite.addTestSuite(IgniteSqlDefaultValueTest.class); suite.addTestSuite(IgniteDecimalSelfTest.class); + suite.addTestSuite(IgniteSQLColumnConstraintsTest.class); + + suite.addTestSuite(IgniteCachePartitionedAtomicColumnConstraintsTest.class); + suite.addTestSuite(IgniteCachePartitionedTransactionalColumnConstraintsTest.class); + suite.addTestSuite(IgniteCacheReplicatedAtomicColumnConstraintsTest.class); + suite.addTestSuite(IgniteCacheReplicatedTransactionalColumnConstraintsTest.class); // H2 Rows on-heap cache suite.addTestSuite(H2RowCacheSelfTest.class); http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/platforms/cpp/common/include/ignite/common/utils.h ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/common/include/ignite/common/utils.h b/modules/platforms/cpp/common/include/ignite/common/utils.h index 81b5432..4edbf4b 100644 --- a/modules/platforms/cpp/common/include/ignite/common/utils.h +++ b/modules/platforms/cpp/common/include/ignite/common/utils.h @@ -548,4 +548,4 @@ namespace ignite } } -#endif //_IGNITE_COMMON_UTILS \ No newline at end of file +#endif //_IGNITE_COMMON_UTILS http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/platforms/cpp/odbc-test/config/queries-default.xml ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/odbc-test/config/queries-default.xml b/modules/platforms/cpp/odbc-test/config/queries-default.xml index dbe3a10..659ad14 100644 --- a/modules/platforms/cpp/odbc-test/config/queries-default.xml +++ b/modules/platforms/cpp/odbc-test/config/queries-default.xml @@ -85,6 +85,12 @@ </bean> </list> </property> + + <property name="fieldsPrecision"> + <map> + <entry key="strField" value="60" /> + </map> + </property> </bean> </list> </property> http://git-wip-us.apache.org/repos/asf/ignite/blob/baab0a6d/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp ---------------------------------------------------------------------- diff --git a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp index 8e22e3d..5cacb96 100644 --- a/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp +++ b/modules/platforms/cpp/odbc-test/src/meta_queries_test.cpp @@ -180,6 +180,8 @@ BOOST_AUTO_TEST_CASE(TestColAttributesColumnLength) if (!SQL_SUCCEEDED(ret)) BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + BOOST_CHECK_EQUAL(intVal, 60); } BOOST_AUTO_TEST_CASE(TestColAttributesColumnPresicion) @@ -197,6 +199,8 @@ BOOST_AUTO_TEST_CASE(TestColAttributesColumnPresicion) if (!SQL_SUCCEEDED(ret)) BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); + + BOOST_CHECK_EQUAL(intVal, 60); } BOOST_AUTO_TEST_CASE(TestColAttributesColumnScale) @@ -278,6 +282,19 @@ BOOST_AUTO_TEST_CASE(TestGetDataWithSelectQuery) CheckSingleRowResultSetWithGetData(stmt); } +BOOST_AUTO_TEST_CASE(TestInsertTooLongValueFail) +{ + Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache"); + + SQLCHAR insertReq[] = + "insert into TestType(_key, strField) VALUES(42, '0123456789012345678901234567890123456789012345678901234567891')"; + + SQLRETURN ret = SQLExecDirect(stmt, insertReq, SQL_NTS); + + if (SQL_SUCCEEDED(ret)) + BOOST_FAIL(GetOdbcErrorMessage(SQL_HANDLE_STMT, stmt)); +} + BOOST_AUTO_TEST_CASE(TestGetInfoScrollOptions) { Connect("DRIVER={Apache Ignite};ADDRESS=127.0.0.1:11110;SCHEMA=cache");