ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sboi...@apache.org
Subject [07/12] ignite git commit: IGNITE-5648: SQL: Added support for NOT NULL constraint. This closes #2341.
Date Wed, 13 Sep 2017 08:06:51 GMT
IGNITE-5648: SQL: Added support for NOT NULL constraint. This closes #2341.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/43be051c
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/43be051c
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/43be051c

Branch: refs/heads/ignite-6149
Commit: 43be051cd33f0e35a5bf05fa3dbe73660d2dcdd2
Parents: 8c23805
Author: Sergey Kalashnikov <skalashnikov@gridgain.com>
Authored: Tue Sep 12 16:53:50 2017 +0300
Committer: devozerov <vozerov@gridgain.com>
Committed: Tue Sep 12 16:53:50 2017 +0300

----------------------------------------------------------------------
 .../org/apache/ignite/cache/QueryEntity.java    |   32 +-
 .../processors/cache/GridCacheContext.java      |   26 +
 .../processors/cache/GridCacheMapEntry.java     |   23 +
 .../distributed/dht/GridDhtTxPrepareFuture.java |    3 +
 .../dht/atomic/GridDhtAtomicCache.java          |   43 +-
 .../GridNearAtomicSingleUpdateFuture.java       |    9 +-
 .../dht/atomic/GridNearAtomicUpdateFuture.java  |    9 +-
 .../near/GridNearOptimisticTxPrepareFuture.java |    2 +-
 .../cache/distributed/near/GridNearTxLocal.java |   14 +-
 .../local/atomic/GridLocalAtomicCache.java      |   21 +-
 .../transactions/IgniteTxLocalAdapter.java      |    2 +
 .../processors/query/GridQueryProcessor.java    |   21 +-
 .../processors/query/GridQueryProperty.java     |    7 +
 .../query/GridQueryTypeDescriptor.java          |   10 +
 .../internal/processors/query/QueryField.java   |   14 +-
 .../internal/processors/query/QuerySchema.java  |   11 +-
 .../query/QueryTypeDescriptorImpl.java          |   30 +
 .../internal/processors/query/QueryUtils.java   |   42 +-
 .../query/property/QueryBinaryProperty.java     |   12 +-
 .../query/property/QueryClassProperty.java      |   16 +-
 .../query/h2/DmlStatementsProcessor.java        |   10 +-
 .../processors/query/h2/IgniteH2Indexing.java   |    3 +-
 .../query/h2/ddl/DdlStatementsProcessor.java    |   15 +-
 .../processors/query/h2/opt/GridH2Table.java    |    6 +-
 .../query/h2/sql/GridSqlQueryParser.java        |    4 -
 .../cache/index/DynamicColumnsAbstractTest.java |   14 +-
 .../H2DynamicColumnsAbstractBasicSelfTest.java  |   28 +
 .../query/IgniteSqlNotNullConstraintTest.java   | 1060 ++++++++++++++++++
 .../h2/GridIndexingSpiAbstractSelfTest.java     |   10 +
 .../query/h2/sql/GridQueryParsingTest.java      |   25 +-
 .../IgniteCacheQuerySelfTestSuite.java          |    2 +
 31 files changed, 1475 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
index 6440b48..fe54670 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/QueryEntity.java
@@ -83,6 +83,9 @@ public class QueryEntity implements Serializable {
     /** Table name. */
     private String tableName;
 
+    /** Fields that must have non-null value. */
+    private Set<String> notNullFields;
+
     /**
      * Creates an empty query entity.
      */
@@ -109,6 +112,8 @@ public class QueryEntity implements Serializable {
         idxs = other.idxs != null ? new ArrayList<>(other.idxs) : null;
 
         tableName = other.tableName;
+
+        notNullFields = other.notNullFields != null ? new HashSet<>(other.notNullFields) : null;
     }
 
     /**
@@ -355,6 +360,27 @@ public class QueryEntity implements Serializable {
     }
 
     /**
+     * Gets names of fields that must be checked for null.
+     *
+     * @return Set of names of fields that must have non-null values.
+     */
+    @Nullable public Set<String> getNotNullFields() {
+        return notNullFields;
+    }
+
+    /**
+     * Sets names of fields that must checked for null.
+     *
+     * @param notNullFields Set of names of fields that must have non-null values.
+     * @return {@code this} for chaining.
+     */
+    public QueryEntity setNotNullFields(@Nullable Set<String> notNullFields) {
+        this.notNullFields = notNullFields;
+
+        return this;
+    }
+
+    /**
      * Utility method for building query entities programmatically.
      * @param fullName Full name of the field.
      * @param type Type of the field.
@@ -604,12 +630,14 @@ public class QueryEntity implements Serializable {
             F.eq(keyFields, entity.keyFields) &&
             F.eq(aliases, entity.aliases) &&
             F.eqNotOrdered(idxs, entity.idxs) &&
-            F.eq(tableName, entity.tableName);
+            F.eq(tableName, entity.tableName) &&
+            F.eq(notNullFields, entity.notNullFields);
     }
 
     /** {@inheritDoc} */
     @Override public int hashCode() {
-        return Objects.hash(keyType, valType, keyFieldName, valueFieldName, fields, keyFields, aliases, idxs, tableName);
+        return Objects.hash(keyType, valType, keyFieldName, valueFieldName, fields, keyFields, aliases, idxs,
+            tableName, notNullFields);
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
index b6faf47..120007f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheContext.java
@@ -630,6 +630,13 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * @return {@code True} in case cache supports query.
+     */
+    public boolean isQueryEnabled() {
+        return !F.isEmpty(cacheCfg.getQueryEntities());
+    }
+
+    /**
      * @return {@code True} if entries should not be deleted from cache immediately.
      */
     public boolean deferredDelete() {
@@ -1804,6 +1811,25 @@ public class GridCacheContext<K, V> implements Externalizable {
     }
 
     /**
+     * Performs validation of provided key and value against configured constraints.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @throws IgniteCheckedException, If validation fails.
+     */
+    public void validateKeyAndValue(KeyCacheObject key, CacheObject val) throws IgniteCheckedException {
+        if (!isQueryEnabled())
+            return;
+
+        try {
+            ctx.query().validateKeyAndValue(cacheObjCtx, key, val);
+        }
+        catch (RuntimeException e) {
+            throw U.cast(e);
+        }
+    }
+
+    /**
      * @param map Map.
      * @param key Key.
      * @param val Value.

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
index 61f6fb4..958f156 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java
@@ -1419,6 +1419,9 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
                         updated0 = cctx.unwrapTemporary(entry.getValue());
 
                         updated = cctx.toCacheObject(updated0);
+
+                        if (updated != null) // no validation for remove case
+                            cctx.validateKeyAndValue(key, updated);
                     }
                     else
                         updated = old;
@@ -4165,6 +4168,26 @@ public abstract class GridCacheMapEntry extends GridMetadataAwareAdapter impleme
 
                     return;
                 }
+                else if ((invokeRes == null || invokeRes.getValue() == null) && writeObj != null) {
+                    try {
+                        cctx.validateKeyAndValue(entry.key, (CacheObject)writeObj);
+                    }
+                    catch (Exception e) {
+                        initResultOnCancelUpdate(null, true);
+
+                        updateRes = new GridCacheUpdateAtomicResult(UpdateOutcome.INVOKE_NO_OP,
+                            oldVal,
+                            null,
+                            new IgniteBiTuple<>(null, e),
+                            CU.TTL_ETERNAL,
+                            CU.EXPIRE_TIME_ETERNAL,
+                            null,
+                            null,
+                            0);
+
+                        return;
+                    }
+                }
 
                 op = writeObj == null ? DELETE : UPDATE;
             }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
index 47dcefc..e048696 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/GridDhtTxPrepareFuture.java
@@ -419,6 +419,9 @@ public final class GridDhtTxPrepareFuture extends GridCacheCompoundFuture<Ignite
                                     procRes = processor.process(invokeEntry, t.get2());
 
                                     val = cacheCtx.toCacheObject(invokeEntry.getValue(true));
+
+                                    if (val != null) // no validation for remove case
+                                        cacheCtx.validateKeyAndValue(key, val);
                                 }
                                 catch (Exception e) {
                                     err = e;

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
index 7d0f747..a50a707 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicCache.java
@@ -2105,35 +2105,52 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
                     CacheInvokeEntry<Object, Object> invokeEntry = new CacheInvokeEntry(entry.key(), old,
                         entry.version(), req.keepBinary(), entry);
 
-                    CacheObject updated;
+                    CacheObject updated = null;
+
+                    if (invokeRes == null)
+                        invokeRes = new GridCacheReturn(node.isLocal());
+
+                    CacheInvokeResult curInvokeRes = null;
+
+                    boolean validation = false;
 
                     try {
                         Object computed = entryProcessor.process(invokeEntry, req.invokeArguments());
 
                         if (computed != null) {
-                            if (invokeRes == null)
-                                invokeRes = new GridCacheReturn(node.isLocal());
-
                             computed = ctx.unwrapTemporary(computed);
 
-                            invokeRes.addEntryProcessResult(ctx, entry.key(), invokeEntry.key(), computed, null,
-                                req.keepBinary());
+                            curInvokeRes = CacheInvokeResult.fromResult(computed);
                         }
 
                         if (!invokeEntry.modified())
                             continue;
+                        else {
+                            updatedVal = ctx.unwrapTemporary(invokeEntry.getValue());
+
+                            updated = ctx.toCacheObject(updatedVal);
 
-                        updatedVal = ctx.unwrapTemporary(invokeEntry.getValue());
+                            validation = true;
 
-                        updated = ctx.toCacheObject(updatedVal);
+                            ctx.validateKeyAndValue(entry.key(), updated);
+                        }
                     }
                     catch (Exception e) {
-                        if (invokeRes == null)
-                            invokeRes = new GridCacheReturn(node.isLocal());
-
-                        invokeRes.addEntryProcessResult(ctx, entry.key(), invokeEntry.key(), null, e, req.keepBinary());
+                        curInvokeRes = CacheInvokeResult.fromError(e);
 
                         updated = old;
+
+                        if (validation) {
+                            res.addSkippedIndex(i);
+
+                            continue;
+                        }
+                    }
+                    finally {
+                        if (curInvokeRes != null) {
+                            invokeRes.addEntryProcessResult(ctx, entry.key(), invokeEntry.key(), curInvokeRes.result(),
+                                curInvokeRes.error(), req.keepBinary());
+                        }
                     }
 
                     if (updated == null) {
@@ -2272,6 +2289,8 @@ public class GridDhtAtomicCache<K, V> extends GridDhtCacheAdapter<K, V> {
 
                     assert updated != null;
 
+                    ctx.validateKeyAndValue(entry.key(), updated);
+
                     if (putMap == null) {
                         putMap = new LinkedHashMap<>(size, 1.0f);
                         writeVals = new ArrayList<>(size);

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java
index 67542ca..b2f9218 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicSingleUpdateFuture.java
@@ -30,6 +30,7 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryPredicate;
+import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CachePartialUpdateCheckedException;
 import org.apache.ignite.internal.processors.cache.CacheStoppedException;
 import org.apache.ignite.internal.processors.cache.EntryProcessorResourceInjectorProxy;
@@ -48,7 +49,9 @@ import org.apache.ignite.internal.util.typedef.internal.S;
 import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC;
+import static org.apache.ignite.internal.processors.cache.GridCacheOperation.CREATE;
 import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
+import static org.apache.ignite.internal.processors.cache.GridCacheOperation.UPDATE;
 
 /**
  * DHT atomic cache near update future.
@@ -542,8 +545,12 @@ public class GridNearAtomicSingleUpdateFuture extends GridNearAtomicAbstractUpda
 
         KeyCacheObject cacheKey = cctx.toCacheKeyObject(key);
 
-        if (op != TRANSFORM)
+        if (op != TRANSFORM) {
             val = cctx.toCacheObject(val);
+
+            if (op == CREATE || op == UPDATE)
+                cctx.validateKeyAndValue(cacheKey, (CacheObject)val);
+        }
         else
             val = EntryProcessorResourceInjectorProxy.wrap(cctx.kernalContext(), (EntryProcessor)val);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
index 930012a..e516a99 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridNearAtomicUpdateFuture.java
@@ -34,6 +34,7 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException;
 import org.apache.ignite.internal.cluster.ClusterTopologyServerNotFoundException;
 import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
 import org.apache.ignite.internal.processors.cache.CacheEntryPredicate;
+import org.apache.ignite.internal.processors.cache.CacheObject;
 import org.apache.ignite.internal.processors.cache.CachePartialUpdateCheckedException;
 import org.apache.ignite.internal.processors.cache.CacheStoppedException;
 import org.apache.ignite.internal.processors.cache.EntryProcessorResourceInjectorProxy;
@@ -57,7 +58,9 @@ import org.jetbrains.annotations.Nullable;
 
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_ASYNC;
 import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC;
+import static org.apache.ignite.internal.processors.cache.GridCacheOperation.CREATE;
 import static org.apache.ignite.internal.processors.cache.GridCacheOperation.TRANSFORM;
+import static org.apache.ignite.internal.processors.cache.GridCacheOperation.UPDATE;
 
 /**
  * DHT atomic cache near update future.
@@ -983,8 +986,12 @@ public class GridNearAtomicUpdateFuture extends GridNearAtomicAbstractUpdateFutu
             if (remapKeys != null && !remapKeys.contains(cacheKey))
                 continue;
 
-            if (op != TRANSFORM)
+            if (op != TRANSFORM) {
                 val = cctx.toCacheObject(val);
+
+                if (op == CREATE || op == UPDATE)
+                    cctx.validateKeyAndValue(cacheKey, (CacheObject)val);
+            }
             else
                 val = EntryProcessorResourceInjectorProxy.wrap(cctx.kernalContext(), (EntryProcessor)val);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
index c978848..77c68bd 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearOptimisticTxPrepareFuture.java
@@ -445,7 +445,7 @@ public class GridNearOptimisticTxPrepareFuture extends GridNearOptimisticTxPrepa
 
                 ClusterNode primary = updated.primary();
 
-                assert !primary.isLocal() || !cctx.kernalContext().clientNode();
+                assert !primary.isLocal() || !cctx.kernalContext().clientNode() || write.context().isLocal();
 
                 // Minor optimization to not create MappingKey: on client node can not have mapping for local node.
                 Object key =  cctx.kernalContext().clientNode() ? primary.id() :

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
index a894097..c55276c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearTxLocal.java
@@ -1252,8 +1252,13 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea
                         break; // While.
                     }
 
+                    CacheObject cVal = cacheCtx.toCacheObject(val);
+
+                    if (op == CREATE || op == UPDATE)
+                        cacheCtx.validateKeyAndValue(cacheKey, cVal);
+
                     txEntry = addEntry(op,
-                        cacheCtx.toCacheObject(val),
+                        cVal,
                         entryProcessor,
                         invokeArgs,
                         entry,
@@ -1356,8 +1361,13 @@ public class GridNearTxLocal extends GridDhtTxLocalAdapter implements AutoClosea
                 GridCacheOperation op = rmv ? DELETE : entryProcessor != null ? TRANSFORM :
                     v != null ? UPDATE : CREATE;
 
+                CacheObject cVal = cacheCtx.toCacheObject(val);
+
+                if (op == CREATE || op == UPDATE)
+                    cacheCtx.validateKeyAndValue(cacheKey, cVal);
+
                 txEntry = addEntry(op,
-                    cacheCtx.toCacheObject(val),
+                    cVal,
                     entryProcessor,
                     invokeArgs,
                     entry,

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
index e99c9e8..1be264d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/local/atomic/GridLocalAtomicCache.java
@@ -866,8 +866,11 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
 
             KeyCacheObject cacheKey = ctx.toCacheKeyObject(key);
 
-            if (op == UPDATE)
+            if (op == UPDATE) {
                 val = ctx.toCacheObject(val);
+
+                ctx.validateKeyAndValue(cacheKey, (CacheObject)val);
+            }
             else if (op == TRANSFORM)
                 ctx.kernalContext().resource().inject(val, GridResourceIoc.AnnotationSet.ENTRY_PROCESSOR, ctx.name());
 
@@ -1055,6 +1058,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
                         Object updatedVal = null;
                         CacheInvokeResult invokeRes = null;
 
+                        boolean validation = false;
+
                         try {
                             Object computed = entryProcessor.process(invokeEntry, invokeArgs);
 
@@ -1064,11 +1069,23 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
 
                             if (computed != null)
                                 invokeRes = CacheInvokeResult.fromResult(ctx.unwrapTemporary(computed));
+
+                            if (invokeEntry.modified()) {
+                                validation = true;
+
+                                ctx.validateKeyAndValue(entry.key(), updated);
+                            }
                         }
                         catch (Exception e) {
                             invokeRes = CacheInvokeResult.fromError(e);
 
                             updated = old;
+
+                            if (validation) {
+                                invokeResMap.put((K)entry.key().value(ctx.cacheObjectContext(), false), invokeRes);
+
+                                continue;
+                            }
                         }
 
                         if (invokeRes != null)
@@ -1175,6 +1192,8 @@ public class GridLocalAtomicCache<K, V> extends GridLocalCache<K, V> {
                             cacheVal = ctx.toCacheObject(ctx.unwrapTemporary(interceptorVal));
                         }
 
+                        ctx.validateKeyAndValue(entry.key(), cacheVal);
+
                         if (putMap == null) {
                             putMap = new LinkedHashMap<>(size, 1.0f);
                             writeVals = new ArrayList<>(size);

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
index e7ebaae..7ab921c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/transactions/IgniteTxLocalAdapter.java
@@ -1203,6 +1203,8 @@ public abstract class IgniteTxLocalAdapter extends IgniteTxAdapter implements Ig
                 key0 = invokeEntry.key();
             }
 
+            ctx.validateKeyAndValue(txEntry.key(), ctx.toCacheObject(val0));
+
             if (res != null)
                 ret.addEntryProcessResult(ctx, txEntry.key(), key0, res, null, txEntry.keepBinary());
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 323bae6..593a3b2 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
@@ -2241,7 +2241,7 @@ private IgniteInternalFuture<Object> rebuildIndexesFromHash(@Nullable final Stri
         for (QueryField col : cols) {
             try {
                 props.add(new QueryBinaryProperty(ctx, col.name(), null, Class.forName(col.typeName()),
-                    false, null));
+                    false, null, !col.isNullable()));
             }
             catch (ClassNotFoundException e) {
                 throw new SchemaOperationException("Class not found for new property: " + col.typeName());
@@ -2593,6 +2593,25 @@ private IgniteInternalFuture<Object> rebuildIndexesFromHash(@Nullable final Stri
     }
 
     /**
+     * Performs validation of provided key and value against configured constraints.
+     * Throws runtime exception if validation fails.
+     *
+     * @param coctx Cache object context.
+     * @param key Key.
+     * @param val Value.
+     * @throws IgniteCheckedException, If error happens.
+     */
+    public void validateKeyAndValue(CacheObjectContext coctx, KeyCacheObject key, CacheObject val)
+        throws IgniteCheckedException {
+        QueryTypeDescriptorImpl desc = typeByValue(coctx.cacheName(), coctx, key, val, false);
+
+        if (desc == null)
+            return;
+
+        desc.validateKeyAndValue(key, val);
+    }
+
+    /**
      * @param ver Version.
      */
     public static void setRequestAffinityTopologyVersion(AffinityTopologyVersion ver) {

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java
index fb4c037..c8ae212 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryProperty.java
@@ -63,4 +63,11 @@ public interface GridQueryProperty {
      * @return Parent property or {@code null} if this property is not nested.
      */
     public GridQueryProperty parent();
+
+    /**
+     * Gets the flag restricting {@code null} value for this property.
+     *
+     * @return {@code true} if property does not allow {@code null} value.
+     */
+    public boolean notNull();
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
index c149335..dcf850c 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/GridQueryTypeDescriptor.java
@@ -167,4 +167,14 @@ public interface GridQueryTypeDescriptor {
      * @return value field alias.
      */
     @Nullable public String valueFieldAlias();
+
+    /**
+     * Performs validation of given key and value against configured constraints.
+     * Throws runtime exception if validation fails.
+     *
+     * @param key Key.
+     * @param val Value.
+     * @throws IgniteCheckedException, If failure happens.
+     */
+    public void validateKeyAndValue(Object key, Object val) throws IgniteCheckedException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 968c287..8c7d367 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
@@ -34,13 +34,18 @@ public class QueryField implements Serializable {
     /** Class name for this field's values. */
     private final String typeName;
 
+    /** Nullable flag. */
+    private final boolean nullable;
+
     /**
      * @param name Field name.
      * @param typeName Class name for this field's values.
+     * @param nullable Nullable flag.
      */
-    public QueryField(String name, String typeName) {
+    public QueryField(String name, String typeName, boolean nullable) {
         this.name = name;
         this.typeName = typeName;
+        this.nullable = nullable;
     }
 
     /**
@@ -57,6 +62,13 @@ public class QueryField implements Serializable {
         return typeName;
     }
 
+    /**
+     * @return {@code true}, if field is nullable.
+     */
+    public boolean isNullable() {
+        return nullable;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(QueryField.class, this);

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
index ccbba93..40da780 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/QuerySchema.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.processors.query;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import org.apache.ignite.cache.QueryEntity;
@@ -163,8 +164,16 @@ public class QuerySchema implements Serializable {
                 if (target == null)
                     return;
 
-                for (QueryField field : op0.columns())
+                for (QueryField field : op0.columns()) {
                     target.getFields().put(field.name(), field.typeName());
+
+                    if (!field.isNullable()) {
+                        if (target.getNotNullFields() == null)
+                            target.setNotNullFields(new HashSet<String>());
+
+                        target.getNotNullFields().add(field.name());
+                    }
+                }
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 0b77c4c..e12476a 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
@@ -22,9 +22,12 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
+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.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.A;
@@ -101,6 +104,9 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
     /** Obsolete. */
     private volatile boolean obsolete;
 
+    /** */
+    private List<GridQueryProperty> validateProps;
+
     /**
      * Constructor.
      *
@@ -367,6 +373,13 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
         if (uppercaseProps.put(name.toUpperCase(), prop) != null && failOnDuplicate)
             throw new IgniteCheckedException("Property with upper cased name '" + name + "' already exists.");
 
+        if (prop.notNull()) {
+            if (validateProps == null)
+                validateProps = new ArrayList<>();
+
+            validateProps.add(prop);
+        }
+
         fields.put(name, prop.type());
     }
 
@@ -470,4 +483,21 @@ public class QueryTypeDescriptorImpl implements GridQueryTypeDescriptor {
     @Nullable @Override public String valueFieldAlias() {
         return valFieldName != null ? aliases.get(valFieldName) : null;
     }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("ForLoopReplaceableByForEach")
+    @Override public void validateKeyAndValue(Object key, Object val) throws IgniteCheckedException {
+        if (validateProps == null)
+            return;
+
+        final int size = validateProps.size();
+
+        for (int idx = 0; idx < size; ++idx) {
+            GridQueryProperty prop = validateProps.get(idx);
+
+            if (prop.value(key, val) == null)
+                throw new IgniteSQLException("Null value is not allowed for field '" + prop.name() + "'",
+                    IgniteQueryErrorCode.NULL_VALUE);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 26fc776..76efb71 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
@@ -222,6 +222,7 @@ public class QueryUtils {
         normalEntity.setKeyFields(entity.getKeyFields());
         normalEntity.setKeyFieldName(entity.getKeyFieldName());
         normalEntity.setValueFieldName(entity.getValueFieldName());
+        normalEntity.setNotNullFields(entity.getNotNullFields());
 
         // Normalize table name.
         String normalTblName = entity.getTableName();
@@ -495,6 +496,7 @@ public class QueryUtils {
     public static void processBinaryMeta(GridKernalContext ctx, QueryEntity qryEntity, QueryTypeDescriptorImpl d)
         throws IgniteCheckedException {
         Set<String> keyFields = qryEntity.getKeyFields();
+        Set<String> notNulls = qryEntity.getNotNullFields();
 
         // 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
@@ -521,8 +523,11 @@ public class QueryUtils {
             else
                 isKeyField = (hasKeyFields ? keyFields.contains(entry.getKey()) : null);
 
+            boolean notNull = notNulls != null && notNulls.contains(entry.getKey());
+
             QueryBinaryProperty prop = buildBinaryProperty(ctx, entry.getKey(),
-                U.classForName(entry.getValue(), Object.class, true), d.aliases(), isKeyField);
+                U.classForName(entry.getValue(), Object.class, true),
+                d.aliases(), isKeyField, notNull);
 
             d.addProperty(prop, false);
         }
@@ -539,6 +544,8 @@ public class QueryUtils {
      */
     public static void processClassMeta(QueryEntity qryEntity, QueryTypeDescriptorImpl d, CacheObjectContext coCtx)
         throws IgniteCheckedException {
+        Set<String> notNulls = qryEntity.getNotNullFields();
+
         for (Map.Entry<String, String> entry : qryEntity.getFields().entrySet()) {
             GridQueryProperty prop = buildProperty(
                 d.keyClass(),
@@ -548,6 +555,7 @@ public class QueryUtils {
                 entry.getKey(),
                 U.classForName(entry.getValue(), Object.class),
                 d.aliases(),
+                notNulls != null && notNulls.contains(entry.getKey()),
                 coCtx);
 
             d.addProperty(prop, false);
@@ -661,10 +669,11 @@ public class QueryUtils {
      * @param aliases Aliases.
      * @param isKeyField Key ownership flag, as defined in {@link QueryEntity#keyFields}: {@code true} if field belongs
      *      to key, {@code false} if it belongs to value, {@code null} if QueryEntity#keyFields is null.
+     * @param notNull {@code true} if {@code null} value is not allowed.
      * @return Binary property.
      */
     public static QueryBinaryProperty buildBinaryProperty(GridKernalContext ctx, String pathStr, Class<?> resType,
-                                     Map<String, String> aliases, @Nullable Boolean isKeyField) throws IgniteCheckedException {
+        Map<String, String> aliases, @Nullable Boolean isKeyField, boolean notNull) throws IgniteCheckedException {
         String[] path = pathStr.split("\\.");
 
         QueryBinaryProperty res = null;
@@ -680,7 +689,7 @@ public class QueryUtils {
             String alias = aliases.get(fullName.toString());
 
             // The key flag that we've found out is valid for the whole path.
-            res = new QueryBinaryProperty(ctx, prop, res, resType, isKeyField, alias);
+            res = new QueryBinaryProperty(ctx, prop, res, resType, isKeyField, alias, notNull);
         }
 
         return res;
@@ -692,21 +701,25 @@ public class QueryUtils {
      * @param pathStr Path string.
      * @param resType Result type.
      * @param aliases Aliases.
+     * @param notNull {@code true} if {@code null} value is not allowed.
+     * @param coCtx Cache object context.
      * @return Class property.
      * @throws IgniteCheckedException If failed.
      */
     public static QueryClassProperty buildClassProperty(Class<?> keyCls, Class<?> valCls, String pathStr,
-        Class<?> resType, Map<String,String> aliases, CacheObjectContext coCtx) throws IgniteCheckedException {
+        Class<?> resType, Map<String,String> aliases, boolean notNull, CacheObjectContext coCtx)
+        throws IgniteCheckedException {
         QueryClassProperty res = buildClassProperty(
             true,
             keyCls,
             pathStr,
             resType,
             aliases,
+            notNull,
             coCtx);
 
         if (res == null) // We check key before value consistently with BinaryProperty.
-            res = buildClassProperty(false, valCls, pathStr, resType, aliases, coCtx);
+            res = buildClassProperty(false, valCls, pathStr, resType, aliases, notNull, coCtx);
 
         if (res == null)
             throw new IgniteCheckedException(propertyInitializationExceptionMessage(keyCls, valCls, pathStr, resType));
@@ -722,11 +735,14 @@ public class QueryUtils {
      * @param pathStr Path string.
      * @param resType Result type.
      * @param aliases Aliases.
+     * @param notNull {@code true} if {@code null} value is not allowed.
+     * @param coCtx Cache object context.
      * @return Class property.
      * @throws IgniteCheckedException If failed.
      */
-    public static GridQueryProperty buildProperty(Class<?> keyCls, Class<?> valCls, String keyFieldName, String valueFieldName, String pathStr,
-                                                  Class<?> resType, Map<String,String> aliases, CacheObjectContext coCtx) throws IgniteCheckedException {
+    public static GridQueryProperty buildProperty(Class<?> keyCls, Class<?> valCls, String keyFieldName,
+        String valueFieldName, String pathStr, Class<?> resType, Map<String,String> aliases, boolean notNull,
+        CacheObjectContext coCtx) throws IgniteCheckedException {
         if (pathStr.equals(keyFieldName))
             return new KeyOrValProperty(true, pathStr, keyCls);
 
@@ -738,6 +754,7 @@ public class QueryUtils {
                 pathStr,
                 resType,
                 aliases,
+                notNull,
                 coCtx);
     }
 
@@ -763,11 +780,13 @@ public class QueryUtils {
      * @param pathStr String representing path to the property. May contains dots '.' to identify nested fields.
      * @param resType Expected result type.
      * @param aliases Aliases.
+     * @param notNull {@code true} if {@code null} value is not allowed.
+     * @param coCtx Cache object context.
      * @return Property instance corresponding to the given path.
      */
     @SuppressWarnings("ConstantConditions")
     public static QueryClassProperty buildClassProperty(boolean key, Class<?> cls, String pathStr, Class<?> resType,
-        Map<String,String> aliases, CacheObjectContext coCtx) {
+        Map<String,String> aliases, boolean notNull, CacheObjectContext coCtx) {
         String[] path = pathStr.split("\\.");
 
         QueryClassProperty res = null;
@@ -787,7 +806,7 @@ public class QueryUtils {
             if (accessor == null)
                 return null;
 
-            QueryClassProperty tmp = new QueryClassProperty(accessor, key, alias, coCtx);
+            QueryClassProperty tmp = new QueryClassProperty(accessor, key, alias, notNull, coCtx);
 
             tmp.parent(res);
 
@@ -1221,5 +1240,10 @@ public class QueryUtils {
         @Override public GridQueryProperty parent() {
             return null;
         }
+
+        /** {@inheritDoc} */
+        @Override public boolean notNull() {
+            return true;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
index d8b907e..5d90a43 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryBinaryProperty.java
@@ -65,6 +65,9 @@ public class QueryBinaryProperty implements GridQueryProperty {
     /** Whether user was warned about missing property. */
     private volatile boolean warned;
 
+    /** */
+    private final boolean notNull;
+
     /**
      * Constructor.
      *
@@ -74,9 +77,10 @@ public class QueryBinaryProperty implements GridQueryProperty {
      * @param type Result type.
      * @param key {@code true} if key property, {@code false} otherwise, {@code null}  if unknown.
      * @param alias Field alias.
+     * @param notNull {@code true} if null value is not allowed.
      */
     public QueryBinaryProperty(GridKernalContext ctx, String propName, QueryBinaryProperty parent,
-        Class<?> type, @Nullable Boolean key, String alias) {
+        Class<?> type, @Nullable Boolean key, String alias, boolean notNull) {
 
         this.ctx = ctx;
 
@@ -86,6 +90,7 @@ public class QueryBinaryProperty implements GridQueryProperty {
         this.alias = F.isEmpty(alias) ? propName : alias;
         this.parent = parent;
         this.type = type;
+        this.notNull = notNull;
 
         if (key != null)
             this.isKeyProp = key ? 1 : -1;
@@ -266,4 +271,9 @@ public class QueryBinaryProperty implements GridQueryProperty {
     @Override public GridQueryProperty parent() {
         return parent;
     }
+
+    /** {@inheritDoc} */
+    @Override public boolean notNull() {
+        return notNull;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryClassProperty.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryClassProperty.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryClassProperty.java
index 80b9c87..076a769 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryClassProperty.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/query/property/QueryClassProperty.java
@@ -44,19 +44,28 @@ public class QueryClassProperty implements GridQueryProperty {
     /** */
     private final CacheObjectContext coCtx;
 
+    /** */
+    private final boolean notNull;
+
     /**
      * Constructor.
      *
      * @param accessor Way of accessing the property.
+     * @param key {@code true} if key property, {@code false} otherwise.
+     * @param name Property name.
+     * @param notNull {@code true} if null value is not allowed.
+     * @param coCtx Cache Object Context.
      */
     public QueryClassProperty(QueryPropertyAccessor accessor, boolean key, String name,
-        @Nullable CacheObjectContext coCtx) {
+        boolean notNull, @Nullable CacheObjectContext coCtx) {
         this.accessor = accessor;
 
         this.key = key;
 
         this.name = !F.isEmpty(name) ? name : accessor.getPropertyName();
 
+        this.notNull = notNull;
+
         this.coCtx = coCtx;
     }
 
@@ -127,4 +136,9 @@ public class QueryClassProperty implements GridQueryProperty {
     @Override public GridQueryProperty parent() {
         return parent;
     }
+
+    /** {@inheritDoc} */
+    @Override public boolean notNull() {
+        return notNull;
+    }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
index 0ff9cfe..0634a52 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java
@@ -611,6 +611,8 @@ public class DmlStatementsProcessor {
                 newVal = ((BinaryObjectBuilder) newVal).build();
             }
 
+            desc.type().validateKeyAndValue(key, newVal);
+
             Object srcVal = row.get(1);
 
             if (bin && !(srcVal instanceof BinaryObject))
@@ -764,14 +766,15 @@ public class DmlStatementsProcessor {
 
         // If we have just one item to put, just do so
         if (plan.rowsNum == 1) {
-            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next(),
-                plan);
+            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next(), plan);
 
             cctx.cache().put(t.getKey(), t.getValue());
+
             return 1;
         }
         else {
             int resCnt = 0;
+
             Map<Object, Object> rows = new LinkedHashMap<>();
 
             for (Iterator<List<?>> it = cursor.iterator(); it.hasNext();) {
@@ -783,6 +786,7 @@ public class DmlStatementsProcessor {
 
                 if ((pageSize > 0 && rows.size() == pageSize) || !it.hasNext()) {
                     cctx.cache().putAll(rows);
+
                     resCnt += rows.size();
 
                     if (it.hasNext())
@@ -952,6 +956,8 @@ public class DmlStatementsProcessor {
                 val = ((BinaryObjectBuilder) val).build();
         }
 
+        desc.validateKeyAndValue(key, val);
+
         return new IgniteBiTuple<>(key, val);
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 3d3ed84..a07d48e 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
@@ -1644,7 +1644,8 @@ public class IgniteH2Indexing implements GridQueryIndexing {
         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()));
+            sql.a(',').a(H2Utils.withQuotes(e.getKey())).a(' ').a(dbTypeFromClass(e.getValue()))
+            .a(tbl.type().property(e.getKey()).notNull()? " NOT NULL" : "");
 
         sql.a(')');
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/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 6f3c3ba..03f4e1f 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
@@ -20,9 +20,11 @@ package org.apache.ignite.internal.processors.query.h2.ddl;
 import java.sql.PreparedStatement;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cache.QueryEntity;
 import org.apache.ignite.cache.QueryIndex;
@@ -241,7 +243,8 @@ public class DdlStatementsProcessor {
                         }
 
                         cols.add(new QueryField(col.columnName(),
-                            DataType.getTypeClassName(col.column().getType())));
+                            DataType.getTypeClassName(col.column().getType()),
+                            col.column().isNullable()));
                     }
 
                     if (cols != null) {
@@ -335,12 +338,21 @@ public class DdlStatementsProcessor {
 
         res.setTableName(createTbl.tableName());
 
+        Set<String> notNullFields = null;
+
         for (Map.Entry<String, GridSqlColumn> e : createTbl.columns().entrySet()) {
             GridSqlColumn gridCol = e.getValue();
 
             Column col = gridCol.column();
 
             res.addQueryField(e.getKey(), DataType.getTypeClassName(col.getType()), null);
+
+            if (!col.isNullable()) {
+                if (notNullFields == null)
+                    notNullFields = new HashSet<>();
+
+                notNullFields.add(e.getKey());
+            }
         }
 
         String valTypeName = QueryUtils.createTableValueTypeName(createTbl.schemaName(), createTbl.tableName());
@@ -350,6 +362,7 @@ public class DdlStatementsProcessor {
         res.setKeyType(keyTypeName);
 
         res.setKeyFields(createTbl.primaryKeyColumns());
+        res.setNotNullFields(notNullFields);
 
         return res;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
index 694346c..45bb9c7 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
@@ -985,7 +985,11 @@ public class GridH2Table extends TableBase {
                 }
 
                 try {
-                    newCols[pos++] = new Column(col.name(), DataType.getTypeFromClass(Class.forName(col.typeName())));
+                    Column c = new Column(col.name(), DataType.getTypeFromClass(Class.forName(col.typeName())));
+
+                    c.setNullable(col.isNullable());
+
+                    newCols[pos++] = c;
                 }
                 catch (ClassNotFoundException e) {
                     throw new IgniteSQLException("H2 data type not found for class: " + col.typeName(), e);

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
index d19e66f..a2de62b 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/GridSqlQueryParser.java
@@ -1029,10 +1029,6 @@ public class GridSqlQueryParser {
             throw new IgniteSQLException("AUTO_INCREMENT columns are not supported [colName=" + col.getName() + ']',
                 IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
 
-        if (!col.isNullable())
-            throw new IgniteSQLException("Non nullable columns are not supported [colName=" + col.getName() + ']',
-                IgniteQueryErrorCode.UNSUPPORTED_OPERATION);
-
         if (COLUMN_IS_COMPUTED.get(col))
             throw new IgniteSQLException("Computed columns are not supported [colName=" + col.getName() + ']',
                 IgniteQueryErrorCode.UNSUPPORTED_OPERATION);

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractTest.java
index 75b9a30..978c504 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/DynamicColumnsAbstractTest.java
@@ -116,6 +116,14 @@ public abstract class DynamicColumnsAbstractTest extends GridCommonAbstractTest
                 assertEquals(col.name(), e.getKey());
 
                 assertEquals(col.typeName(), e.getValue());
+
+                if (!col.isNullable()) {
+                    assertNotNull(entity.getNotNullFields());
+
+                    assertTrue(entity.getNotNullFields().contains(col.name()));
+                }
+                else if (entity.getNotNullFields() != null)
+                    assertFalse(entity.getNotNullFields().contains(col.name()));
             }
         }
 
@@ -151,6 +159,8 @@ public abstract class DynamicColumnsAbstractTest extends GridCommonAbstractTest
                 assertEquals(col.name(), e.getKey());
 
                 assertEquals(col.typeName(), e.getValue().getName());
+
+                assertTrue(col.isNullable() || desc.property(col.name()).notNull());
             }
         }
 
@@ -183,6 +193,8 @@ public abstract class DynamicColumnsAbstractTest extends GridCommonAbstractTest
 
                 assertFalse(rowDesc.isKeyValueOrVersionColumn(i));
 
+                assertEquals(col.isNullable(), c.isNullable());
+
                 try {
                     assertEquals(DataType.getTypeFromClass(Class.forName(col.typeName())),
                         rowDesc.fieldType(i - GridH2AbstractKeyValueRow.DEFAULT_COLUMNS_COUNT));
@@ -213,7 +225,7 @@ public abstract class DynamicColumnsAbstractTest extends GridCommonAbstractTest
      * @return New column with given name and type.
      */
     protected static QueryField c(String name, String typeName) {
-        return new QueryField(name, typeName);
+        return new QueryField(name, typeName, true);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/43be051c/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java
----------------------------------------------------------------------
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java
index 651a3f7..6455cad 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicColumnsAbstractBasicSelfTest.java
@@ -266,6 +266,34 @@ public abstract class H2DynamicColumnsAbstractBasicSelfTest extends DynamicColum
     }
 
     /**
+     * Test addition of column with not null constraint.
+     */
+    public void testAddNotNullColumn() {
+        run("ALTER TABLE Person ADD COLUMN age int NOT NULL");
+
+        doSleep(500);
+
+        QueryField c = new QueryField("AGE", Integer.class.getName(), false);
+
+        for (Ignite node : Ignition.allGrids())
+            checkNodeState((IgniteEx)node, QueryUtils.DFLT_SCHEMA, "PERSON", c);
+    }
+
+    /**
+     * Test addition of column explicitly defined as nullable.
+     */
+    public void testAddNullColumn() {
+        run("ALTER TABLE Person ADD COLUMN age int NULL");
+
+        doSleep(500);
+
+        QueryField c = new QueryField("AGE", Integer.class.getName(), true);
+
+        for (Ignite node : Ignition.allGrids())
+            checkNodeState((IgniteEx)node, QueryUtils.DFLT_SCHEMA, "PERSON", c);
+    }
+
+    /**
      * @return Node index to run queries on.
      */
     protected abstract int nodeIndex();


Mime
View raw message