IGNITE-4340: Fixed implicit type conversion in DML. This closes #1303.
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 c2c8726..4030758 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
@@ -17,11 +17,13 @@
 
 package org.apache.ignite.internal.processors.query.h2;
 
+import java.lang.reflect.Array;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -73,6 +75,8 @@
 import org.h2.command.Prepared;
 import org.h2.jdbc.JdbcPreparedStatement;
 import org.h2.table.Column;
+import org.h2.value.DataType;
+import org.h2.value.Value;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.jsr166.ConcurrentHashMap8;
@@ -487,7 +491,8 @@
                 if (hasNewVal && i == valColIdx - 2)
                     continue;
 
-                newColVals.put(plan.colNames[i], e.get(i + 2));
+                newColVals.put(plan.colNames[i], convert(e.get(i + 2), plan.colNames[i],
+                    plan.tbl.rowDescriptor(), plan.colTypes[i]));
             }
 
             newVal = plan.valSupplier.apply(e);
@@ -575,6 +580,64 @@
     }
 
     /**
+     * Convert value to column's expected type by means of H2.
+     *
+     * @param val Source value.
+     * @param colName Column name to search for property.
+     * @param desc Row descriptor.
+     * @param type Expected column type to convert to.
+     * @return Converted object.
+     * @throws IgniteCheckedException if failed.
+     */
+    @SuppressWarnings({"ConstantConditions", "SuspiciousSystemArraycopy"})
+    private static Object convert(Object val, String colName, GridH2RowDescriptor desc, int type)
+        throws IgniteCheckedException {
+        if (val == null)
+            return null;
+
+        GridQueryProperty prop = desc.type().property(colName);
+
+        assert prop != null;
+
+        Class<?> expCls = prop.type();
+
+        Class<?> currCls = val.getClass();
+
+        if (val instanceof Date && currCls != Date.class && expCls == Date.class) {
+            // H2 thinks that java.util.Date is always a Timestamp, while binary marshaller expects
+            // precise Date instance. Let's satisfy it.
+            return new Date(((Date) val).getTime());
+        }
+
+        // We have to convert arrays of reference types manually - see https://issues.apache.org/jira/browse/IGNITE-4327
+        // Still, we only can convert from Object[] to something more precise.
+        if (type == Value.ARRAY && currCls != expCls) {
+            if (currCls != Object[].class)
+                throw new IgniteCheckedException("Unexpected array type - only conversion from Object[] is assumed");
+
+            // Why would otherwise type be Value.ARRAY?
+            assert expCls.isArray();
+
+            Object[] curr = (Object[]) val;
+
+            Object newArr = Array.newInstance(expCls.getComponentType(), curr.length);
+
+            System.arraycopy(curr, 0, newArr, 0, curr.length);
+
+            return newArr;
+        }
+
+        int objType = DataType.getTypeFromClass(val.getClass());
+
+        if (objType == type)
+            return val;
+
+        Value h2Val = desc.wrap(val, objType);
+
+        return h2Val.convertTo(type).getObject();
+    }
+
+    /**
      * Process errors of entry processor - split the keys into duplicated/concurrently modified and those whose
      * processing yielded an exception.
      *
@@ -633,8 +696,8 @@
 
         // If we have just one item to put, just do so
         if (plan.rowsNum == 1) {
-            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.keySupplier,
-                plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc.type());
+            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.colTypes, plan.keySupplier,
+                plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc);
 
             cctx.cache().put(t.getKey(), t.getValue());
             return 1;
@@ -646,8 +709,8 @@
             for (Iterator<List<?>> it = cursor.iterator(); it.hasNext();) {
                 List<?> row = it.next();
 
-                IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.keySupplier, plan.valSupplier,
-                    plan.keyColIdx, plan.valColIdx, desc.type());
+                IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.colTypes, plan.keySupplier, plan.valSupplier,
+                    plan.keyColIdx, plan.valColIdx, desc);
 
                 rows.put(t.getKey(), t.getValue());
 
@@ -679,8 +742,8 @@
 
         // If we have just one item to put, just do so
         if (plan.rowsNum == 1) {
-            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.keySupplier,
-                plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc.type());
+            IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.colTypes,
+                plan.keySupplier, plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc);
 
             if (cctx.cache().putIfAbsent(t.getKey(), t.getValue()))
                 return 1;
@@ -705,8 +768,8 @@
             while (it.hasNext()) {
                 List<?> row = it.next();
 
-                final IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.keySupplier,
-                    plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc.type());
+                final IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.colTypes, plan.keySupplier,
+                    plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc);
 
                 rows.put(t.getKey(), new InsertEntryProcessor(t.getValue()));
 
@@ -772,22 +835,21 @@
 
     /**
      * Convert row presented as an array of Objects into key-value pair to be inserted to cache.
-     *
      * @param cctx Cache context.
      * @param row Row to process.
      * @param cols Query cols.
+     * @param colTypes Column types to convert data from {@code row} to.
      * @param keySupplier Key instantiation method.
      * @param valSupplier Key instantiation method.
      * @param keyColIdx Key column index, or {@code -1} if no key column is mentioned in {@code cols}.
      * @param valColIdx Value column index, or {@code -1} if no value column is mentioned in {@code cols}.
-     * @param desc Table descriptor.
-     * @return Key-value pair.
+     * @param rowDesc Row descriptor.
      * @throws IgniteCheckedException if failed.
      */
     @SuppressWarnings({"unchecked", "ConstantConditions", "ResultOfMethodCallIgnored"})
     private IgniteBiTuple<?, ?> rowToKeyValue(GridCacheContext cctx, Object[] row, String[] cols,
-        KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx,
-        GridQueryTypeDescriptor desc) throws IgniteCheckedException {
+        int[] colTypes, KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx,
+        GridH2RowDescriptor rowDesc) throws IgniteCheckedException {
         Object key = keySupplier.apply(F.asList(row));
         Object val = valSupplier.apply(F.asList(row));
 
@@ -797,11 +859,13 @@
         if (val == null)
             throw new IgniteSQLException("Value for INSERT or MERGE must not be null", IgniteQueryErrorCode.NULL_VALUE);
 
+        GridQueryTypeDescriptor desc = rowDesc.type();
+
         for (int i = 0; i < cols.length; i++) {
             if (i == keyColIdx || i == valColIdx)
                 continue;
 
-            desc.setValue(cols[i], key, val, row[i]);
+            desc.setValue(cols[i], key, val, convert(row[i], cols[i], rowDesc, colTypes[i]));
         }
 
         if (cctx.binaryMarshaller()) {
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 f4ffbc7..5df44db 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
@@ -212,10 +212,10 @@
     private static final int TWO_STEP_QRY_CACHE_SIZE = 1024;
 
     /** Field name for key. */
-    public static final String KEY_FIELD_NAME = "_key";
+    public static final String KEY_FIELD_NAME = "_KEY";
 
     /** Field name for value. */
-    public static final String VAL_FIELD_NAME = "_val";
+    public static final String VAL_FIELD_NAME = "_VAL";
 
     /** */
     private static final Field COMMAND_FIELD;
@@ -1246,8 +1246,8 @@
                             cachesCreated = true;
                         }
                         else
-                            throw new IgniteSQLException("Failed to parse query: " + sqlQry, e.getSQLState(),
-                                IgniteQueryErrorCode.PARSING);
+                            throw new IgniteSQLException("Failed to parse query: " + sqlQry,
+                                IgniteQueryErrorCode.PARSING, e);
                     }
                 }
             }
@@ -1459,7 +1459,7 @@
         String ptrn = "Name ''{0}'' is reserved and cannot be used as a field name [type=" + type.name() + "]";
 
         for (String name : names) {
-            if (name.equals(KEY_FIELD_NAME) || name.equals(VAL_FIELD_NAME))
+            if (name.equalsIgnoreCase(KEY_FIELD_NAME) || name.equalsIgnoreCase(VAL_FIELD_NAME))
                 throw new IgniteCheckedException(MessageFormat.format(ptrn, name));
         }
 
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
index 5976f4c..b81ac60 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java
@@ -33,6 +33,12 @@
     /** Column names to set or update. */
     public final String[] colNames;
 
+    /**
+     * Expected column types to set or insert/merge.
+     * @see org.h2.value.Value
+     */
+    public final int[] colTypes;
+
     /** Method to create key for INSERT or MERGE, ignored for UPDATE and DELETE. */
     public final KeyValueSupplier keySupplier;
 
@@ -59,10 +65,11 @@
     public final FastUpdateArguments fastUpdateArgs;
 
     /** */
-    private UpdatePlan(UpdateMode mode, GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier,
+    private UpdatePlan(UpdateMode mode, GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier,
         KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry,
         int rowsNum, FastUpdateArguments fastUpdateArgs) {
         this.colNames = colNames;
+        this.colTypes = colTypes;
         this.rowsNum = rowsNum;
         assert mode != null;
         assert tbl != null;
@@ -79,43 +86,43 @@
     }
 
     /** */
-    public static UpdatePlan forMerge(GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier,
+    public static UpdatePlan forMerge(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier,
         KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry,
         int rowsNum) {
         assert !F.isEmpty(colNames);
 
-        return new UpdatePlan(UpdateMode.MERGE, tbl, colNames, keySupplier, valSupplier, keyColIdx, valColIdx,
+        return new UpdatePlan(UpdateMode.MERGE, tbl, colNames, colTypes, keySupplier, valSupplier, keyColIdx, valColIdx,
             selectQry, isLocSubqry, rowsNum, null);
     }
 
     /** */
-    public static UpdatePlan forInsert(GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier,
+    public static UpdatePlan forInsert(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier,
         KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry, int rowsNum) {
         assert !F.isEmpty(colNames);
 
-        return new UpdatePlan(UpdateMode.INSERT, tbl, colNames, keySupplier, valSupplier, keyColIdx, valColIdx, selectQry,
-            isLocSubqry, rowsNum, null);
+        return new UpdatePlan(UpdateMode.INSERT, tbl, colNames, colTypes, keySupplier, valSupplier, keyColIdx, valColIdx,
+            selectQry, isLocSubqry, rowsNum, null);
     }
 
     /** */
-    public static UpdatePlan forUpdate(GridH2Table tbl, String[] colNames, KeyValueSupplier valSupplier, int valColIdx,
-        String selectQry) {
+    public static UpdatePlan forUpdate(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier valSupplier,
+        int valColIdx, String selectQry) {
         assert !F.isEmpty(colNames);
 
-        return new UpdatePlan(UpdateMode.UPDATE, tbl, colNames, null, valSupplier, -1, valColIdx, selectQry,
+        return new UpdatePlan(UpdateMode.UPDATE, tbl, colNames, colTypes, null, valSupplier, -1, valColIdx, selectQry,
             false, 0, null);
     }
 
     /** */
     public static UpdatePlan forDelete(GridH2Table tbl, String selectQry) {
-        return new UpdatePlan(UpdateMode.DELETE, tbl, null, null, null, -1, -1, selectQry, false, 0, null);
+        return new UpdatePlan(UpdateMode.DELETE, tbl, null, null, null, null, -1, -1, selectQry, false, 0, null);
     }
 
     /** */
     public static UpdatePlan forFastUpdate(UpdateMode mode, GridH2Table tbl, FastUpdateArguments fastUpdateArgs) {
         assert mode == UpdateMode.UPDATE || mode == UpdateMode.DELETE;
 
-        return new UpdatePlan(mode, tbl, null, null, null, -1, -1, null, false, 0, fastUpdateArgs);
+        return new UpdatePlan(mode, tbl, null, null, null, null, -1, -1, null, false, 0, fastUpdateArgs);
     }
 
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
index 549b901..fdcd164 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java
@@ -132,7 +132,7 @@
             // not for updates, and hence will allow putting new pairs only.
             // We don't quote _key and _val column names on CREATE TABLE, so they are always uppercase here.
             GridSqlColumn[] keys = merge.keys();
-            if (keys.length != 1 || IgniteH2Indexing.KEY_FIELD_NAME.equals(keys[0].columnName()))
+            if (keys.length != 1 || !IgniteH2Indexing.KEY_FIELD_NAME.equals(keys[0].columnName()))
                 throw new CacheException("SQL MERGE does not support arbitrary keys");
 
             cols = merge.columns();
@@ -161,22 +161,30 @@
 
         String[] colNames = new String[cols.length];
 
-        for (int i = 0; i < cols.length; i++) {
-            colNames[i] = cols[i].columnName();
+        int[] colTypes = new int[cols.length];
 
-            if (isKeyColumn(cols[i].columnName(), desc)) {
+        for (int i = 0; i < cols.length; i++) {
+            GridSqlColumn col = cols[i];
+
+            String colName = col.columnName();
+
+            colNames[i] = colName;
+
+            colTypes[i] = col.resultType().type();
+
+            if (KEY_FIELD_NAME.equals(colName)) {
                 keyColIdx = i;
                 continue;
             }
 
-            if (isValColumn(cols[i].columnName(), desc)) {
+            if (VAL_FIELD_NAME.equals(colName)) {
                 valColIdx = i;
                 continue;
             }
 
-            GridQueryProperty prop = desc.type().property(cols[i].columnName());
+            GridQueryProperty prop = desc.type().property(colName);
 
-            assert prop != null : "Property '" + cols[i].columnName() + "' not found.";
+            assert prop != null : "Property '" + colName + "' not found.";
 
             if (prop.key())
                 hasKeyProps = true;
@@ -188,11 +196,11 @@
         KeyValueSupplier valSupplier = createSupplier(cctx, desc.type(), valColIdx, hasValProps, false);
 
         if (stmt instanceof GridSqlMerge)
-            return UpdatePlan.forMerge(tbl.dataTable(), colNames, keySupplier, valSupplier, keyColIdx, valColIdx,
-                sel.getSQL(), !isTwoStepSubqry, rowsNum);
+            return UpdatePlan.forMerge(tbl.dataTable(), colNames, colTypes, keySupplier, valSupplier, keyColIdx,
+                valColIdx, sel.getSQL(), !isTwoStepSubqry, rowsNum);
         else
-            return UpdatePlan.forInsert(tbl.dataTable(), colNames, keySupplier, valSupplier, keyColIdx, valColIdx,
-                sel.getSQL(), !isTwoStepSubqry, rowsNum);
+            return UpdatePlan.forInsert(tbl.dataTable(), colNames, colTypes, keySupplier, valSupplier, keyColIdx,
+                valColIdx, sel.getSQL(), !isTwoStepSubqry, rowsNum);
     }
 
     /**
@@ -253,10 +261,14 @@
 
                 String[] colNames = new String[updatedCols.size()];
 
+                int[] colTypes = new int[updatedCols.size()];
+
                 for (int i = 0; i < updatedCols.size(); i++) {
                     colNames[i] = updatedCols.get(i).columnName();
 
-                    if (isValColumn(colNames[i], desc))
+                    colTypes[i] = updatedCols.get(i).resultType().type();
+
+                    if (VAL_FIELD_NAME.equals(colNames[i]))
                         valColIdx = i;
                 }
 
@@ -287,7 +299,7 @@
 
                 sel = DmlAstUtils.selectForUpdate((GridSqlUpdate) stmt, errKeysPos);
 
-                return UpdatePlan.forUpdate(gridTbl, colNames, newValSupplier, valColIdx, sel.getSQL());
+                return UpdatePlan.forUpdate(gridTbl, colNames, colTypes, newValSupplier, valColIdx, sel.getSQL());
             }
             else {
                 sel = DmlAstUtils.selectForDelete((GridSqlDelete) stmt, errKeysPos);
@@ -471,32 +483,4 @@
 
         return false;
     }
-
-    /**
-     * Check that given column corresponds to the key with respect to case sensitivity, if needed (should be considered
-     * when the schema escapes all identifiers on table creation).
-     * @param colName Column name.
-     * @param desc Row descriptor.
-     * @return {@code true} if column name corresponds to _key with respect to case sensitivity depending on schema.
-     */
-    private static boolean isKeyColumn(String colName, GridH2RowDescriptor desc) {
-        if (desc.quoteAllIdentifiers())
-            return KEY_FIELD_NAME.equals(colName);
-        else
-            return KEY_FIELD_NAME.equalsIgnoreCase(colName);
-    }
-
-    /**
-     * Check that given column corresponds to the key with respect to case sensitivity, if needed (should be considered
-     * when the schema escapes all identifiers on table creation).
-     * @param colName Column name.
-     * @param desc Row descriptor.
-     * @return {@code true} if column name corresponds to _key with respect to case sensitivity depending on schema.
-     */
-    private static boolean isValColumn(String colName, GridH2RowDescriptor desc) {
-        if (desc.quoteAllIdentifiers())
-            return VAL_FIELD_NAME.equals(colName);
-        else
-            return VAL_FIELD_NAME.equalsIgnoreCase(colName);
-    }
 }
diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java
index 5ff715e..6deb146 100644
--- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java
+++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java
@@ -280,7 +280,7 @@
         GridSqlElement right = op.child(1);
 
         return left instanceof GridSqlColumn &&
-            colName.equalsIgnoreCase(((GridSqlColumn) left).columnName()) &&
+            colName.equals(((GridSqlColumn) left).columnName()) &&
             (right instanceof GridSqlConst || right instanceof GridSqlParameter);
     }
 
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractInsertSqlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractInsertSqlQuerySelfTest.java
index 47369ee..df4259e 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractInsertSqlQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractInsertSqlQuerySelfTest.java
@@ -217,7 +217,7 @@
             flds.put("Id", Integer.class.getName());
             flds.put("id", Integer.class.getName());
             flds.put("name", String.class.getName());
-            flds.put("_Val", Integer.class.getName());
+            flds.put("IntVal", Integer.class.getName());
 
             k22p.setFields(flds);
 
@@ -336,7 +336,7 @@
         if (!isBinaryMarshaller()) {
             Person2 p = new Person2(id);
             p.name = name;
-            p._Val = valFld;
+            p.IntVal = valFld;
 
             return p;
         }
@@ -344,7 +344,7 @@
             BinaryObjectBuilder o = grid(0).binary().builder("Person2");
             o.setField("id", id);
             o.setField("name", name);
-            o.setField("_Val", valFld);
+            o.setField("IntVal", valFld);
 
             return o.build();
         }
@@ -554,6 +554,6 @@
 
         /** */
         @QuerySqlField
-        public int _Val;
+        public int IntVal;
     }
 }
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractSqlDmlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractSqlDmlQuerySelfTest.java
index 1424163..7f79ec4 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractSqlDmlQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheAbstractSqlDmlQuerySelfTest.java
@@ -166,7 +166,7 @@
      * @param escapeSql whether identifiers should be quoted - see {@link CacheConfiguration#setSqlEscapeAll}
      * @return Cache configuration.
      */
-    private static CacheConfiguration cacheConfig(String name, boolean partitioned, boolean escapeSql) {
+    protected static CacheConfiguration cacheConfig(String name, boolean partitioned, boolean escapeSql) {
         return new CacheConfiguration()
             .setName(name)
             .setCacheMode(partitioned ? CacheMode.PARTITIONED : CacheMode.REPLICATED)
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheInsertSqlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheInsertSqlQuerySelfTest.java
index 4f87740..04a352f 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheInsertSqlQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheInsertSqlQuerySelfTest.java
@@ -119,7 +119,7 @@
     public void testFieldsCaseSensitivity() {
         IgniteCache<Key2, Person> p = ignite(0).cache("K22P").withKeepBinary();
 
-        p.query(new SqlFieldsQuery("insert into \"Person2\" (\"Id\", \"id\", \"name\", \"_Val\") values (1, ?, ?, 5), " +
+        p.query(new SqlFieldsQuery("insert into \"Person2\" (\"Id\", \"id\", \"name\", \"IntVal\") values (1, ?, ?, 5), " +
             "(2, 3, 'Alex', 6)").setArgs(4, "Sergi"));
 
         assertEquals(createPerson2(4, "Sergi", 5), p.get(new Key2(1)));
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheMergeSqlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheMergeSqlQuerySelfTest.java
index e487564..0ff3fda 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheMergeSqlQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheMergeSqlQuerySelfTest.java
@@ -95,7 +95,7 @@
     public void testFieldsCaseSensitivity() {
         IgniteCache<Key2, Person> p = ignite(0).cache("K22P").withKeepBinary();
 
-        p.query(new SqlFieldsQuery("merge into \"Person2\" (\"Id\", \"id\", \"name\", \"_Val\") values (1, ?, ?, 5), " +
+        p.query(new SqlFieldsQuery("merge into \"Person2\" (\"Id\", \"id\", \"name\", \"IntVal\") values (1, ?, ?, 5), " +
             "(2, 3, 'Alex', 6)").setArgs(4, "Sergi"));
 
         assertEquals(createPerson2(4, "Sergi", 5), p.get(new Key2(1)));
diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java
index 538141f..332a082 100644
--- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java
+++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java
@@ -17,17 +17,44 @@
 
 package org.apache.ignite.internal.processors.cache;
 
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.cache.query.QueryCursor;
 import org.apache.ignite.cache.query.SqlFieldsQuery;
+import org.apache.ignite.cache.query.annotations.QuerySqlField;
+import org.apache.ignite.configuration.CacheConfiguration;
 
 /**
  *
  */
 @SuppressWarnings("unchecked")
 public class IgniteCacheUpdateSqlQuerySelfTest extends IgniteCacheAbstractSqlDmlQuerySelfTest {
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        ignite(0).createCache(createAllTypesCacheConfig());
+    }
+
+    /**
+     *
+     */
+    private static CacheConfiguration createAllTypesCacheConfig() {
+        CacheConfiguration ccfg = cacheConfig("L2AT", true, true);
+
+        ccfg.setIndexedTypes(Long.class, AllTypes.class);
+
+        return ccfg;
+    }
+
     /**
      *
      */
@@ -147,4 +174,299 @@
         assertEqualsCollections(Arrays.asList("k3", createPerson(3, "Sylvia", "Green"), 3, "Sylvia", "Green"),
             leftovers.get(3));
     }
+
+    /** */
+    public void testTypeConversions() throws ParseException {
+        IgniteCache cache = ignite(0).cache("L2AT");
+
+        cache.query(new SqlFieldsQuery("insert into \"AllTypes\"(_key, _val, \"dateCol\", \"booleanCol\"," +
+            "\"tsCol\") values(2, ?, '2016-11-30 12:00:00', false, DATE '2016-12-01')").setArgs(new AllTypes(2L)));
+
+        List<?> ll = cache.query(new SqlFieldsQuery("select \"primitiveIntsCol\" from \"AllTypes\"")).getAll();
+
+        cache.query(new SqlFieldsQuery("update \"AllTypes\" set \"doubleCol\" = CAST('50' as INT)," +
+            " \"booleanCol\" = 80, \"innerTypeCol\" = ?, \"strCol\" = PI(), \"shortCol\" = " +
+            "CAST(WEEK(PARSEDATETIME('2016-11-30', 'yyyy-MM-dd')) as VARCHAR), " +
+            "\"sqlDateCol\"=TIMESTAMP '2016-12-02 13:47:00', \"tsCol\"=TIMESTAMPADD('MI', 2, " +
+            "DATEADD('DAY', 2, \"tsCol\")), \"primitiveIntsCol\" = ?, \"bytesCol\" = ?")
+            .setArgs(new AllTypes.InnerType(80L), new int[] {2, 3}, new Byte[] {4, 5, 6}));
+
+        AllTypes res = (AllTypes) cache.get(2L);
+
+        assertEquals(new BigDecimal(301.0).doubleValue(), res.bigDecimalCol.doubleValue());
+        assertEquals(50.0, res.doubleCol);
+        assertEquals(2L, (long) res.longCol);
+        assertTrue(res.booleanCol);
+        assertEquals("3.141592653589793", res.strCol);
+        assertTrue(Arrays.equals(new byte[] {0, 1}, res.primitiveBytesCol));
+        assertTrue(Arrays.equals(new Byte[] {4, 5, 6}, res.bytesCol));
+        assertTrue(Arrays.deepEquals(new Integer[] {0, 1}, res.intsCol));
+        assertTrue(Arrays.equals(new int[] {2, 3}, res.primitiveIntsCol));
+        assertEquals(new AllTypes.InnerType(80L), res.innerTypeCol);
+        assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:SS").parse("2016-11-30 12:00:00"), res.dateCol);
+        assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:SS").parse("2016-12-03 00:02:00"), res.tsCol);
+        assertEquals(2, res.intCol);
+        assertEquals(AllTypes.EnumType.ENUMTRUE, res.enumCol);
+        assertEquals(new java.sql.Date(new SimpleDateFormat("yyyy-MM-dd").parse("2016-12-02").getTime()), res.sqlDateCol);
+
+        // 49th week, right?
+        assertEquals(49, res.shortCol);
+    }
+
+    /**
+     *
+     */
+    static final class AllTypes implements Serializable {
+        /**
+         * Data Long.
+         */
+        @QuerySqlField
+        Long longCol;
+
+        /**
+         * Data double.
+         */
+        @QuerySqlField
+        double doubleCol;
+
+        /**
+         * Data String.
+         */
+        @QuerySqlField
+        String strCol;
+
+        /**
+         * Data boolean.
+         */
+        @QuerySqlField
+        boolean booleanCol;
+
+        /**
+         * Date.
+         */
+        @QuerySqlField
+        Date dateCol;
+
+        /**
+         * SQL date (non timestamp).
+         */
+        @QuerySqlField
+        java.sql.Date sqlDateCol;
+
+        /**
+         * Timestamp.
+         */
+        @QuerySqlField
+        Timestamp tsCol;
+
+        /**
+         * Data int.
+         */
+        @QuerySqlField
+        int intCol;
+
+        /**
+         * BigDecimal
+         */
+        @QuerySqlField
+        BigDecimal bigDecimalCol;
+
+        /**
+         * Data bytes array.
+         */
+        @QuerySqlField
+        Byte[] bytesCol;
+
+        /**
+         * Data bytes primitive array.
+         */
+        @QuerySqlField
+        byte[] primitiveBytesCol;
+
+        /**
+         * Data bytes array.
+         */
+        @QuerySqlField
+        Integer[] intsCol;
+
+        /**
+         * Data bytes primitive array.
+         */
+        @QuerySqlField
+        int[] primitiveIntsCol;
+
+        /**
+         * Data bytes array.
+         */
+        @QuerySqlField
+        short shortCol;
+
+        /**
+         * Inner type object.
+         */
+        @QuerySqlField
+        InnerType innerTypeCol;
+
+        /** */
+        static final class InnerType implements Serializable {
+            /** */
+            @QuerySqlField
+            Long innerLongCol;
+
+            /** */
+            @QuerySqlField
+            String innerStrCol;
+
+            /** */
+            @QuerySqlField
+            ArrayList<Long> arrListCol = new ArrayList<>();
+
+            /** */
+            InnerType(Long key) {
+                innerLongCol = key;
+                innerStrCol = Long.toString(key);
+
+                Long m = key % 8;
+
+                for (Integer i = 0; i < m; i++)
+                    arrListCol.add(key + i);
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            @Override public String toString() {
+                return "[Long=" + Long.toString(innerLongCol) +
+                    ", String='" + innerStrCol + "'" +
+                    ", ArrayList=" + arrListCol.toString() +
+                    "]";
+            }
+
+            /** {@inheritDoc} */
+            @Override public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+
+                InnerType innerType = (InnerType) o;
+
+                if (innerLongCol != null ? !innerLongCol.equals(innerType.innerLongCol) : innerType.innerLongCol != null)
+                    return false;
+                if (innerStrCol != null ? !innerStrCol.equals(innerType.innerStrCol) : innerType.innerStrCol != null)
+                    return false;
+                return arrListCol != null ? arrListCol.equals(innerType.arrListCol) : innerType.arrListCol == null;
+
+            }
+
+            /** {@inheritDoc} */
+            @Override public int hashCode() {
+                int res = innerLongCol != null ? innerLongCol.hashCode() : 0;
+                res = 31 * res + (innerStrCol != null ? innerStrCol.hashCode() : 0);
+                res = 31 * res + (arrListCol != null ? arrListCol.hashCode() : 0);
+                return res;
+            }
+        }
+
+        /** */
+        @QuerySqlField
+        EnumType enumCol;
+
+        /** */
+        enum EnumType {
+            /** */
+            ENUMTRUE,
+
+            /** */
+            ENUMFALSE
+        }
+
+        /** */
+        private void init(Long key, String str) {
+            this.longCol = key;
+            this.doubleCol = Math.round(1000 * Math.log10(longCol.doubleValue()));
+            this.bigDecimalCol = BigDecimal.valueOf(doubleCol);
+            this.doubleCol = doubleCol / 100;
+            this.strCol = str;
+            if (key % 2 == 0) {
+                this.booleanCol = true;
+                this.enumCol = EnumType.ENUMTRUE;
+                this.innerTypeCol = new InnerType(key);
+            }
+            else {
+                this.booleanCol = false;
+                this.enumCol = EnumType.ENUMFALSE;
+                this.innerTypeCol = null;
+            }
+            this.intCol = key.intValue();
+            this.bytesCol = new Byte[(int) (key % 10)];
+            this.intsCol = new Integer[(int) (key % 10)];
+            this.primitiveBytesCol = new byte[(int) (key % 10)];
+            this.primitiveIntsCol = new int[(int) (key % 10)];
+            //this.bytesCol = new Byte[10];
+            int b = 0;
+            for (int j = 0; j < bytesCol.length; j++) {
+                if (b == 256)
+                    b = 0;
+                bytesCol[j] = (byte) b;
+                primitiveBytesCol[j] = (byte) b;
+                intsCol[j] = b;
+                primitiveIntsCol[j] = b;
+                b++;
+            }
+            this.shortCol = (short) (((1000 * key) % 50000) - 25000);
+
+            dateCol = new Date();
+        }
+
+        /** */
+        AllTypes(Long key) {
+            this.init(key, Long.toString(key));
+        }
+
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            AllTypes allTypes = (AllTypes) o;
+
+            if (Double.compare(allTypes.doubleCol, doubleCol) != 0) return false;
+            if (booleanCol != allTypes.booleanCol) return false;
+            if (intCol != allTypes.intCol) return false;
+            if (shortCol != allTypes.shortCol) return false;
+            if (longCol != null ? !longCol.equals(allTypes.longCol) : allTypes.longCol != null) return false;
+            if (strCol != null ? !strCol.equals(allTypes.strCol) : allTypes.strCol != null) return false;
+            if (dateCol != null ? !dateCol.equals(allTypes.dateCol) : allTypes.dateCol != null) return false;
+            if (sqlDateCol != null ? !sqlDateCol.equals(allTypes.sqlDateCol) : allTypes.sqlDateCol != null) return false;
+            if (tsCol != null ? !tsCol.equals(allTypes.tsCol) : allTypes.tsCol != null) return false;
+            if (bigDecimalCol != null ? !bigDecimalCol.equals(allTypes.bigDecimalCol) : allTypes.bigDecimalCol != null)
+                return false;
+            // Probably incorrect - comparing Object[] arrays with Arrays.equals
+            if (!Arrays.equals(bytesCol, allTypes.bytesCol)) return false;
+            if (innerTypeCol != null ? !innerTypeCol.equals(allTypes.innerTypeCol) : allTypes.innerTypeCol != null)
+                return false;
+            return enumCol == allTypes.enumCol;
+
+        }
+
+        /** {@inheritDoc} */
+        @Override public int hashCode() {
+            int res;
+            long temp;
+            res = longCol != null ? longCol.hashCode() : 0;
+            temp = Double.doubleToLongBits(doubleCol);
+            res = 31 * res + (int) (temp ^ (temp >>> 32));
+            res = 31 * res + (strCol != null ? strCol.hashCode() : 0);
+            res = 31 * res + (booleanCol ? 1 : 0);
+            res = 31 * res + (dateCol != null ? dateCol.hashCode() : 0);
+            res = 31 * res + (sqlDateCol != null ? sqlDateCol.hashCode() : 0);
+            res = 31 * res + (tsCol != null ? tsCol.hashCode() : 0);
+            res = 31 * res + intCol;
+            res = 31 * res + (bigDecimalCol != null ? bigDecimalCol.hashCode() : 0);
+            res = 31 * res + Arrays.hashCode(bytesCol);
+            res = 31 * res + (int) shortCol;
+            res = 31 * res + (innerTypeCol != null ? innerTypeCol.hashCode() : 0);
+            res = 31 * res + (enumCol != null ? enumCol.hashCode() : 0);
+            return res;
+        }
+    }
 }