ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From agoncha...@apache.org
Subject [38/50] [abbrv] ignite git commit: IGNITE-6055: SQL. Add string length constraint - Fixes #3973.
Date Wed, 29 Aug 2018 16:51:18 GMT
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");


Mime
View raw message