diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
index 0c662acb7206c..ea46fb4f2193a 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java
@@ -70,6 +70,7 @@
 import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable;
 import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral;
 import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
+import org.apache.ignite.internal.processors.query.calcite.type.OtherType;
 import org.apache.ignite.internal.processors.query.calcite.util.IgniteResource;
 import org.apache.ignite.internal.util.typedef.F;
 import org.jetbrains.annotations.Nullable;
@@ -102,7 +103,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
     }
 
     /** Dynamic parameters. */
-    Object[] parameters;
+    @Nullable private final Object[] parameters;
 
     /**
      * Creates a validator.
@@ -110,12 +111,17 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
      * @param opTab         Operator table
      * @param catalogReader Catalog reader
      * @param typeFactory   Type factory
-     * @param config        Config
+     * @param cfg           Config
      * @param parameters    Dynamic parameters
      */
-    public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogReader,
-        IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters) {
-        super(opTab, catalogReader, typeFactory, config);
+    public IgniteSqlValidator(
+        SqlOperatorTable opTab,
+        CalciteCatalogReader catalogReader,
+        IgniteTypeFactory typeFactory,
+        SqlValidator.Config cfg,
+        @Nullable Object[] parameters
+    ) {
+        super(opTab, catalogReader, typeFactory, cfg);
 
         this.parameters = parameters;
     }
@@ -534,9 +540,45 @@ private boolean isSystemFieldName(String alias) {
             || QueryUtils.VAL_FIELD_NAME.equalsIgnoreCase(alias);
     }
 
+    /** {@inheritDoc} */
+    @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) {
+        if (expr instanceof SqlDynamicParam) {
+            RelDataType type = deriveDynamicParameterType((SqlDynamicParam)expr);
+
+            if (type != null)
+                return type;
+        }
+
+        return super.deriveType(scope, expr);
+    }
+
+    /** @return A derived type or {@code null} if unable to determine. */
+    @Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node) {
+        RelDataType type = getValidatedNodeTypeIfKnown(node);
+
+        // Do not clarify the widest type for any value.
+        if (type instanceof OtherType)
+            return type;
+
+        if (parameters == null || node.getIndex() >= parameters.length)
+            return null;
+
+        Object val = parameters[node.getIndex()];
+
+        if (val == null)
+            return null;
+
+        type = typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true);
+
+        setValidatedNodeType(node, type);
+
+        return type;
+    }
+
     /** {@inheritDoc} */
     @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) {
-        if (inferDynamicParamType(inferredType, node))
+        if (node instanceof SqlDynamicParam && unknownType.equals(inferredType)
+            && deriveDynamicParameterType((SqlDynamicParam)node) != null)
             return;
 
         if (node instanceof SqlCall) {
@@ -583,40 +625,6 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) {
             super.inferUnknownTypes(inferredType, scope, node);
     }
 
-    /**
-     * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index
-     * is actual to {@link #parameters}.
-     *
-     * @return {@code True} if a new type was set. {@code False} otherwise.
-     */
-    private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) {
-        if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length)
-            return false;
-
-        Object val = parameters[((SqlDynamicParam)node).getIndex()];
-
-        if (val == null) {
-            if (inferredType.equals(unknownType)) {
-                setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL));
-
-                return true;
-            }
-
-            return false;
-        }
-
-        RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass()));
-
-        assert !unknownType.equals(valType);
-
-        if (unknownType.equals(inferredType) || valType.getFamily().equals(inferredType.getFamily()))
-            setValidatedNodeType(node, valType);
-        else
-            setValidatedNodeType(node, inferredType);
-
-        return true;
-    }
-
     /** {@inheritDoc} */
     @Override public SqlLiteral resolveLiteral(SqlLiteral literal) {
         if (literal instanceof SqlNumericLiteral && literal.createSqlType(typeFactory).getSqlTypeName() == SqlTypeName.BIGINT) {
diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
index 2f222083c6a61..d50c2f227604f 100644
--- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
+++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java
@@ -33,6 +33,7 @@
 import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Planning context.
@@ -45,7 +46,7 @@ public final class PlanningContext implements Context {
     private final String qry;
 
     /** */
-    private final Object[] parameters;
+    @Nullable private final Object[] parameters;
 
     /** */
     private final CancelFlag cancelFlag = new CancelFlag(new AtomicBoolean());
@@ -68,7 +69,7 @@ public final class PlanningContext implements Context {
     private PlanningContext(
         Context parentCtx,
         String qry,
-        Object[] parameters,
+        @Nullable Object[] parameters,
         long plannerTimeout
     ) {
         this.qry = qry;
@@ -90,7 +91,7 @@ public String query() {
      * @return Query parameters.
      */
     @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
-    public Object[] parameters() {
+    @Nullable public Object[] parameters() {
         return parameters;
     }
 
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
index b3eea80c0f32f..78d825a9e3dcc 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java
@@ -26,6 +26,8 @@
 import java.time.Period;
 import java.util.List;
 import java.util.UUID;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.internal.processors.query.IgniteSQLException;
 import org.apache.ignite.internal.util.typedef.F;
 import org.junit.Test;
 
@@ -60,6 +62,44 @@ public void testMetadataTypesForDynamicParameters() {
         }
     }
 
+    /** */
+    @Test
+    public void testMissedValue() {
+        assertThrows("SELECT ?", IgniteSQLException.class, "Illegal use of dynamic parameter");
+
+        assertThrows("SELECT ?, ?", IgniteSQLException.class, "Illegal use of dynamic parameter", "arg0");
+    }
+
+    /** */
+    @Test
+    public void testCasts() {
+        assertQuery("SELECT CAST(? as INTEGER)").withParams('1').returns(1).check();
+        assertQuery("SELECT ?::INTEGER").withParams('1').returns(1).check();
+        assertQuery("SELECT ?::VARCHAR").withParams(1).returns("1").check();
+        assertQuery("SELECT CAST(? as VARCHAR)").withParams(1).returns("1").check();
+
+        IgniteCache<Integer, Employer> cache = createAndPopulateTable();
+
+        cache.put(cache.size(), new Employer("15", 15d));
+
+        assertQuery("SELECT name FROM Person WHERE id=?::INTEGER").withParams("2").returns("Ilya").check();
+        assertQuery("SELECT name FROM Person WHERE id=CAST(? as INTEGER)").withParams("2").returns("Ilya").check();
+
+        assertQuery("SELECT id FROM Person WHERE name=CAST(? as VARCHAR)").withParams(15).returns(5).check();
+        assertQuery("SELECT id FROM Person WHERE name IN (?::VARCHAR)").withParams(15).returns(5).check();
+        assertQuery("SELECT name FROM Person WHERE id IN (?::INTEGER)").withParams("2").returns("Ilya").check();
+        assertQuery("SELECT name FROM Person WHERE id IN (?::INTEGER, ?::INTEGER)").withParams("2", "3")
+            .returns("Ilya").returns("Roma").check();
+
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams(1).returns(6L).check();
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams("abc").returns(6L).check();
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams(new Object[] { null }).returns(0L).check();
+
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams(1).returns(0L).check();
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams("abc").returns(0L).check();
+        assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams(new Object[] {null}).returns(6L).check();
+    }
+
     /** */
     @Test
     public void testDynamicParameters() {
diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java
index 2370eea664f0c..928ca43049e5f 100644
--- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java
+++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java
@@ -49,7 +49,7 @@ public void testResourceCleanup() throws Exception {
         // AbstractBasicIntegrationTest.afterTest method.
         GridTestUtils.runMultiThreaded(() -> {
             for (int i = 0; i < 100; i++)
-                sql(sql, i % 10);
+                sql(sql, "region" + (i % 10));
         }, 10, "query_starter");
     }